1 /*
2 | Copyright (C) 2002-2005 Jorg Schuler <jcsjcs at users sourceforge net>
3 | Part of the gtkpod project.
4 |
5 | URL: http://www.gtkpod.org/
6 | URL: http://gtkpod.sourceforge.net/
7 |
8 | The code contained in this file is free software; you can redistribute
9 | it and/or modify it under the terms of the GNU Lesser General Public
10 | License as published by the Free Software Foundation; either version
11 | 2.1 of the License, or (at your option) any later version.
12 |
13 | This file is distributed in the hope that it will be useful,
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 | Lesser General Public License for more details.
17 |
18 | You should have received a copy of the GNU Lesser General Public
19 | License along with this code; if not, write to the Free Software
20 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 |
22 | iTunes and iPod are trademarks of Apple
23 |
24 | This product is not supported/written/published by Apple!
25 |
26 | $Id$
27 */
28
29 #include "itdb_private.h"
30 #include <glib/gi18n-lib.h>
31 #include <string.h>
32
33 /* spl_action_known(), itb_splr_get_field_type(),
34 * itb_splr_get_action_type() are adapted from source provided by
35 * Samuel "Otto" Wood (sam dot wood at gmail dot com). These part can
36 * also be used under a FreeBSD license. You may also contact Samuel
37 * for a complete copy of his original C++-classes.
38 * */
39
40 /**
41 * itdb_spl_action_known:
42 * @action: an #ItdbSPLAction
43 *
44 * Checks if @action is a known (to libgpod) smart playlist action.
45 *
46 * Returns: TRUE if @action is known. Otherwise a warning is
47 * displayed and FALSE is returned.
48 */
itdb_spl_action_known(ItdbSPLAction action)49 gboolean itdb_spl_action_known (ItdbSPLAction action)
50 {
51 gboolean result = FALSE;
52
53 switch (action)
54 {
55 case ITDB_SPLACTION_IS_INT:
56 case ITDB_SPLACTION_IS_GREATER_THAN:
57 case ITDB_SPLACTION_IS_NOT_GREATER_THAN:
58 case ITDB_SPLACTION_IS_LESS_THAN:
59 case ITDB_SPLACTION_IS_NOT_LESS_THAN:
60 case ITDB_SPLACTION_IS_IN_THE_RANGE:
61 case ITDB_SPLACTION_IS_NOT_IN_THE_RANGE:
62 case ITDB_SPLACTION_IS_IN_THE_LAST:
63 case ITDB_SPLACTION_IS_STRING:
64 case ITDB_SPLACTION_CONTAINS:
65 case ITDB_SPLACTION_STARTS_WITH:
66 case ITDB_SPLACTION_DOES_NOT_START_WITH:
67 case ITDB_SPLACTION_ENDS_WITH:
68 case ITDB_SPLACTION_DOES_NOT_END_WITH:
69 case ITDB_SPLACTION_IS_NOT_INT:
70 case ITDB_SPLACTION_IS_NOT_IN_THE_LAST:
71 case ITDB_SPLACTION_IS_NOT:
72 case ITDB_SPLACTION_DOES_NOT_CONTAIN:
73 case ITDB_SPLACTION_BINARY_AND:
74 case ITDB_SPLACTION_NOT_BINARY_AND:
75 case ITDB_SPLACTION_BINARY_UNKNOWN1:
76 case ITDB_SPLACTION_BINARY_UNKNOWN2:
77 result = TRUE;
78 }
79 if (result == FALSE)
80 { /* New action! */
81 g_warning (_("Unknown action (0x%x) in smart playlist will be ignored.\n"), action);
82 }
83 return result;
84 }
85
86 /**
87 * itdb_splr_get_field_type:
88 * @splr: an #Itdb_SPLRule
89 *
90 * Gets the type of the field of the @splr rule
91 *
92 * Returns: an #Itdb_SPLFieldType corresponding to @splr field
93 * type (string, int, date, ...)
94 */
itdb_splr_get_field_type(const Itdb_SPLRule * splr)95 ItdbSPLFieldType itdb_splr_get_field_type (const Itdb_SPLRule *splr)
96 {
97 g_return_val_if_fail (splr != NULL, ITDB_SPLFT_UNKNOWN);
98
99 switch((ItdbSPLField)splr->field)
100 {
101 case ITDB_SPLFIELD_SONG_NAME:
102 case ITDB_SPLFIELD_ALBUM:
103 case ITDB_SPLFIELD_ALBUMARTIST:
104 case ITDB_SPLFIELD_ARTIST:
105 case ITDB_SPLFIELD_GENRE:
106 case ITDB_SPLFIELD_KIND:
107 case ITDB_SPLFIELD_COMMENT:
108 case ITDB_SPLFIELD_COMPOSER:
109 case ITDB_SPLFIELD_GROUPING:
110 case ITDB_SPLFIELD_TVSHOW:
111 case ITDB_SPLFIELD_CATEGORY:
112 case ITDB_SPLFIELD_DESCRIPTION:
113 case ITDB_SPLFIELD_SORT_SONG_NAME:
114 case ITDB_SPLFIELD_SORT_ALBUM:
115 case ITDB_SPLFIELD_SORT_ALBUMARTIST:
116 case ITDB_SPLFIELD_SORT_ARTIST:
117 case ITDB_SPLFIELD_SORT_COMPOSER:
118 case ITDB_SPLFIELD_SORT_TVSHOW:
119 return ITDB_SPLFT_STRING;
120 case ITDB_SPLFIELD_BITRATE:
121 case ITDB_SPLFIELD_SAMPLE_RATE:
122 case ITDB_SPLFIELD_YEAR:
123 case ITDB_SPLFIELD_TRACKNUMBER:
124 case ITDB_SPLFIELD_SIZE:
125 case ITDB_SPLFIELD_PLAYCOUNT:
126 case ITDB_SPLFIELD_DISC_NUMBER:
127 case ITDB_SPLFIELD_BPM:
128 case ITDB_SPLFIELD_RATING:
129 case ITDB_SPLFIELD_ALBUM_RATING:
130 case ITDB_SPLFIELD_TIME: /* time is the length of the track in
131 milliseconds */
132 case ITDB_SPLFIELD_SEASON_NR:
133 case ITDB_SPLFIELD_SKIPCOUNT:
134 case ITDB_SPLFIELD_PODCAST:
135 return ITDB_SPLFT_INT;
136 case ITDB_SPLFIELD_COMPILATION:
137 case ITDB_SPLFIELD_PURCHASE:
138 return ITDB_SPLFT_BOOLEAN;
139 case ITDB_SPLFIELD_DATE_MODIFIED:
140 case ITDB_SPLFIELD_DATE_ADDED:
141 case ITDB_SPLFIELD_LAST_PLAYED:
142 case ITDB_SPLFIELD_LAST_SKIPPED:
143 return ITDB_SPLFT_DATE;
144 case ITDB_SPLFIELD_PLAYLIST:
145 return ITDB_SPLFT_PLAYLIST;
146 case ITDB_SPLFIELD_VIDEO_KIND:
147 return ITDB_SPLFT_BINARY_AND;
148 }
149 return(ITDB_SPLFT_UNKNOWN);
150 }
151
152 /**
153 * itdb_splr_get_action_type:
154 * @splr: an #Itdb_SPLRule
155 *
156 * Gets the type of the action associated with @splr.
157 *
158 * Returns: type (range, date, string...) of the action field
159 */
itdb_splr_get_action_type(const Itdb_SPLRule * splr)160 ItdbSPLActionType itdb_splr_get_action_type (const Itdb_SPLRule *splr)
161 {
162 ItdbSPLFieldType fieldType;
163
164 g_return_val_if_fail (splr != NULL, ITDB_SPLAT_INVALID);
165
166 fieldType = itdb_splr_get_field_type (splr);
167
168 switch(fieldType)
169 {
170 case ITDB_SPLFT_STRING:
171 switch ((ItdbSPLAction)splr->action)
172 {
173 case ITDB_SPLACTION_IS_STRING:
174 case ITDB_SPLACTION_IS_NOT:
175 case ITDB_SPLACTION_CONTAINS:
176 case ITDB_SPLACTION_DOES_NOT_CONTAIN:
177 case ITDB_SPLACTION_STARTS_WITH:
178 case ITDB_SPLACTION_DOES_NOT_START_WITH:
179 case ITDB_SPLACTION_ENDS_WITH:
180 case ITDB_SPLACTION_DOES_NOT_END_WITH:
181 return ITDB_SPLAT_STRING;
182 case ITDB_SPLACTION_IS_NOT_IN_THE_RANGE:
183 case ITDB_SPLACTION_IS_INT:
184 case ITDB_SPLACTION_IS_NOT_INT:
185 case ITDB_SPLACTION_IS_GREATER_THAN:
186 case ITDB_SPLACTION_IS_NOT_GREATER_THAN:
187 case ITDB_SPLACTION_IS_LESS_THAN:
188 case ITDB_SPLACTION_IS_NOT_LESS_THAN:
189 case ITDB_SPLACTION_IS_IN_THE_RANGE:
190 case ITDB_SPLACTION_IS_IN_THE_LAST:
191 case ITDB_SPLACTION_IS_NOT_IN_THE_LAST:
192 case ITDB_SPLACTION_BINARY_AND:
193 case ITDB_SPLACTION_NOT_BINARY_AND:
194 case ITDB_SPLACTION_BINARY_UNKNOWN1:
195 case ITDB_SPLACTION_BINARY_UNKNOWN2:
196 return ITDB_SPLAT_INVALID;
197 }
198 /* Unknown action type */
199 g_warning ("Unknown action type %d\n\n", splr->action);
200 return ITDB_SPLAT_UNKNOWN;
201
202 case ITDB_SPLFT_INT:
203 switch ((ItdbSPLAction)splr->action)
204 {
205 case ITDB_SPLACTION_IS_INT:
206 case ITDB_SPLACTION_IS_NOT_INT:
207 case ITDB_SPLACTION_IS_GREATER_THAN:
208 case ITDB_SPLACTION_IS_NOT_GREATER_THAN:
209 case ITDB_SPLACTION_IS_LESS_THAN:
210 case ITDB_SPLACTION_IS_NOT_LESS_THAN:
211 return ITDB_SPLAT_INT;
212 case ITDB_SPLACTION_IS_NOT_IN_THE_RANGE:
213 case ITDB_SPLACTION_IS_IN_THE_RANGE:
214 return ITDB_SPLAT_RANGE_INT;
215 case ITDB_SPLACTION_BINARY_AND:
216 case ITDB_SPLACTION_NOT_BINARY_AND:
217 case ITDB_SPLACTION_BINARY_UNKNOWN1:
218 case ITDB_SPLACTION_BINARY_UNKNOWN2:
219 case ITDB_SPLACTION_IS_STRING:
220 case ITDB_SPLACTION_CONTAINS:
221 case ITDB_SPLACTION_STARTS_WITH:
222 case ITDB_SPLACTION_DOES_NOT_START_WITH:
223 case ITDB_SPLACTION_ENDS_WITH:
224 case ITDB_SPLACTION_DOES_NOT_END_WITH:
225 case ITDB_SPLACTION_IS_IN_THE_LAST:
226 case ITDB_SPLACTION_IS_NOT_IN_THE_LAST:
227 case ITDB_SPLACTION_IS_NOT:
228 case ITDB_SPLACTION_DOES_NOT_CONTAIN:
229 return ITDB_SPLAT_INVALID;
230 }
231 /* Unknown action type */
232 g_warning ("Unknown action type %d\n\n", splr->action);
233 return ITDB_SPLAT_UNKNOWN;
234
235 case ITDB_SPLFT_BOOLEAN:
236 return ITDB_SPLAT_NONE;
237
238 case ITDB_SPLFT_DATE:
239 switch ((ItdbSPLAction)splr->action)
240 {
241 case ITDB_SPLACTION_IS_INT:
242 case ITDB_SPLACTION_IS_NOT_INT:
243 case ITDB_SPLACTION_IS_GREATER_THAN:
244 case ITDB_SPLACTION_IS_NOT_GREATER_THAN:
245 case ITDB_SPLACTION_IS_LESS_THAN:
246 case ITDB_SPLACTION_IS_NOT_LESS_THAN:
247 return ITDB_SPLAT_DATE;
248 case ITDB_SPLACTION_IS_IN_THE_LAST:
249 case ITDB_SPLACTION_IS_NOT_IN_THE_LAST:
250 return ITDB_SPLAT_INTHELAST;
251 case ITDB_SPLACTION_IS_IN_THE_RANGE:
252 case ITDB_SPLACTION_IS_NOT_IN_THE_RANGE:
253 return ITDB_SPLAT_RANGE_DATE;
254 case ITDB_SPLACTION_IS_STRING:
255 case ITDB_SPLACTION_CONTAINS:
256 case ITDB_SPLACTION_STARTS_WITH:
257 case ITDB_SPLACTION_DOES_NOT_START_WITH:
258 case ITDB_SPLACTION_ENDS_WITH:
259 case ITDB_SPLACTION_DOES_NOT_END_WITH:
260 case ITDB_SPLACTION_IS_NOT:
261 case ITDB_SPLACTION_DOES_NOT_CONTAIN:
262 case ITDB_SPLACTION_BINARY_AND:
263 case ITDB_SPLACTION_NOT_BINARY_AND:
264 case ITDB_SPLACTION_BINARY_UNKNOWN1:
265 case ITDB_SPLACTION_BINARY_UNKNOWN2:
266 return ITDB_SPLAT_INVALID;
267 }
268 case ITDB_SPLFT_BINARY_AND:
269 switch ((ItdbSPLAction)splr->action)
270 {
271 case ITDB_SPLACTION_BINARY_UNKNOWN1:
272 case ITDB_SPLACTION_BINARY_UNKNOWN2:
273 return ITDB_SPLAT_UNKNOWN;
274 case ITDB_SPLACTION_BINARY_AND:
275 case ITDB_SPLACTION_NOT_BINARY_AND:
276 return ITDB_SPLAT_BINARY_AND;
277 case ITDB_SPLACTION_IS_INT:
278 case ITDB_SPLACTION_IS_NOT_INT:
279 case ITDB_SPLACTION_IS_GREATER_THAN:
280 case ITDB_SPLACTION_IS_NOT_GREATER_THAN:
281 case ITDB_SPLACTION_IS_LESS_THAN:
282 case ITDB_SPLACTION_IS_NOT_LESS_THAN:
283 case ITDB_SPLACTION_IS_IN_THE_LAST:
284 case ITDB_SPLACTION_IS_NOT_IN_THE_LAST:
285 case ITDB_SPLACTION_IS_IN_THE_RANGE:
286 case ITDB_SPLACTION_IS_NOT_IN_THE_RANGE:
287 case ITDB_SPLACTION_IS_STRING:
288 case ITDB_SPLACTION_CONTAINS:
289 case ITDB_SPLACTION_STARTS_WITH:
290 case ITDB_SPLACTION_DOES_NOT_START_WITH:
291 case ITDB_SPLACTION_ENDS_WITH:
292 case ITDB_SPLACTION_DOES_NOT_END_WITH:
293 case ITDB_SPLACTION_IS_NOT:
294 case ITDB_SPLACTION_DOES_NOT_CONTAIN:
295 return ITDB_SPLAT_INVALID;
296 }
297
298 /* Unknown action type */
299 g_warning ("Unknown action type %d\n\n", splr->action);
300 return ITDB_SPLAT_UNKNOWN;
301
302 case ITDB_SPLFT_PLAYLIST:
303 switch ((ItdbSPLAction)splr->action)
304 {
305 case ITDB_SPLACTION_IS_INT:
306 case ITDB_SPLACTION_IS_NOT_INT:
307 return ITDB_SPLAT_PLAYLIST;
308 case ITDB_SPLACTION_IS_GREATER_THAN:
309 case ITDB_SPLACTION_IS_NOT_GREATER_THAN:
310 case ITDB_SPLACTION_IS_LESS_THAN:
311 case ITDB_SPLACTION_IS_NOT_LESS_THAN:
312 case ITDB_SPLACTION_IS_IN_THE_LAST:
313 case ITDB_SPLACTION_IS_NOT_IN_THE_LAST:
314 case ITDB_SPLACTION_IS_IN_THE_RANGE:
315 case ITDB_SPLACTION_IS_NOT_IN_THE_RANGE:
316 case ITDB_SPLACTION_IS_STRING:
317 case ITDB_SPLACTION_CONTAINS:
318 case ITDB_SPLACTION_STARTS_WITH:
319 case ITDB_SPLACTION_DOES_NOT_START_WITH:
320 case ITDB_SPLACTION_ENDS_WITH:
321 case ITDB_SPLACTION_DOES_NOT_END_WITH:
322 case ITDB_SPLACTION_IS_NOT:
323 case ITDB_SPLACTION_DOES_NOT_CONTAIN:
324 case ITDB_SPLACTION_BINARY_AND:
325 case ITDB_SPLACTION_NOT_BINARY_AND:
326 case ITDB_SPLACTION_BINARY_UNKNOWN1:
327 case ITDB_SPLACTION_BINARY_UNKNOWN2:
328 return ITDB_SPLAT_INVALID;
329 }
330 /* Unknown action type */
331 g_warning ("Unknown action type %d\n\n", splr->action);
332 return ITDB_SPLAT_UNKNOWN;
333
334 case ITDB_SPLFT_UNKNOWN:
335 /* Unknown action type */
336 g_warning ("Unknown action type %d\n\n", splr->action);
337 return ITDB_SPLAT_UNKNOWN;
338 }
339 return ITDB_SPLAT_UNKNOWN;
340 }
341
342 /* -------------------------------------------------------------------
343 *
344 * smart playlist stuff, adapted from source provided by Samuel "Otto"
345 * Wood (sam dot wood at gmail dot com). This part can also be used
346 * under a FreeBSD license. You can also contact Samuel for a complete
347 * copy of his original C++-classes.
348 *
349 */
350
351 /**
352 * itdb_splr_eval:
353 * @splr: an #Itdb_SPLRule
354 * @track: an #Itdb_Track
355 *
356 * Evaluates @splr's truth against @track. @track->itdb must be set.
357 *
358 * Returns: TRUE if @track matches @splr, FALSE otherwise.
359 */
itdb_splr_eval(Itdb_SPLRule * splr,Itdb_Track * track)360 gboolean itdb_splr_eval (Itdb_SPLRule *splr, Itdb_Track *track)
361 {
362 ItdbSPLFieldType ft;
363 ItdbSPLActionType at;
364 gchar *strcomp = NULL;
365 gint64 intcomp = 0;
366 gboolean boolcomp = FALSE;
367 gboolean handled = FALSE;
368 guint32 datecomp = 0;
369 Itdb_Playlist *playcomp = NULL;
370 time_t t;
371
372 g_return_val_if_fail (splr, FALSE);
373 g_return_val_if_fail (track, FALSE);
374 g_return_val_if_fail (track->itdb, FALSE);
375
376 ft = itdb_splr_get_field_type (splr);
377 at = itdb_splr_get_action_type (splr);
378
379 g_return_val_if_fail (at != ITDB_SPLAT_INVALID, FALSE);
380
381 /* find what we need to compare in the track */
382 switch (splr->field)
383 {
384 case ITDB_SPLFIELD_SONG_NAME:
385 strcomp = g_utf8_casefold(track->title, -1);
386 handled = TRUE;
387 break;
388 case ITDB_SPLFIELD_ALBUM:
389 strcomp = g_utf8_casefold(track->album, -1);
390 handled = TRUE;
391 break;
392 case ITDB_SPLFIELD_ARTIST:
393 strcomp = g_utf8_casefold(track->artist, -1);
394 handled = TRUE;
395 break;
396 case ITDB_SPLFIELD_GENRE:
397 strcomp = g_utf8_casefold(track->genre, -1);
398 handled = TRUE;
399 break;
400 case ITDB_SPLFIELD_KIND:
401 strcomp = g_utf8_casefold(track->filetype, -1);
402 handled = TRUE;
403 break;
404 case ITDB_SPLFIELD_COMMENT:
405 strcomp = g_utf8_casefold(track->comment, -1);
406 handled = TRUE;
407 break;
408 case ITDB_SPLFIELD_COMPOSER:
409 strcomp = g_utf8_casefold(track->composer, -1);
410 handled = TRUE;
411 break;
412 case ITDB_SPLFIELD_GROUPING:
413 strcomp = g_utf8_casefold(track->grouping, -1);
414 handled = TRUE;
415 break;
416 case ITDB_SPLFIELD_BITRATE:
417 intcomp = track->bitrate;
418 handled = TRUE;
419 break;
420 case ITDB_SPLFIELD_SAMPLE_RATE:
421 intcomp = track->samplerate;
422 handled = TRUE;
423 break;
424 case ITDB_SPLFIELD_YEAR:
425 intcomp = track->year;
426 handled = TRUE;
427 break;
428 case ITDB_SPLFIELD_TRACKNUMBER:
429 intcomp = track->track_nr;
430 handled = TRUE;
431 break;
432 case ITDB_SPLFIELD_SIZE:
433 intcomp = track->size;
434 handled = TRUE;
435 break;
436 case ITDB_SPLFIELD_PLAYCOUNT:
437 intcomp = track->playcount;
438 handled = TRUE;
439 break;
440 case ITDB_SPLFIELD_DISC_NUMBER:
441 intcomp = track->cd_nr;
442 handled = TRUE;
443 break;
444 case ITDB_SPLFIELD_BPM:
445 intcomp = track->BPM;
446 handled = TRUE;
447 break;
448 case ITDB_SPLFIELD_RATING:
449 intcomp = track->rating;
450 handled = TRUE;
451 break;
452 case ITDB_SPLFIELD_TIME:
453 intcomp = track->tracklen;
454 handled = TRUE;
455 break;
456 case ITDB_SPLFIELD_COMPILATION:
457 boolcomp = track->compilation;
458 handled = TRUE;
459 break;
460 case ITDB_SPLFIELD_DATE_MODIFIED:
461 datecomp = track->time_modified;
462 handled = TRUE;
463 break;
464 case ITDB_SPLFIELD_DATE_ADDED:
465 datecomp = track->time_added;
466 handled = TRUE;
467 break;
468 case ITDB_SPLFIELD_LAST_PLAYED:
469 datecomp = track->time_played;
470 handled = TRUE;
471 break;
472 case ITDB_SPLFIELD_PLAYLIST:
473 playcomp = itdb_playlist_by_id (track->itdb, splr->fromvalue);
474 handled = TRUE;
475 break;
476 case ITDB_SPLFIELD_ALBUMARTIST:
477 strcomp = g_utf8_casefold(track->albumartist, -1);
478 handled = TRUE;
479 break;
480 case ITDB_SPLFIELD_TVSHOW:
481 strcomp = g_utf8_casefold(track->tvshow, -1);
482 handled = TRUE;
483 break;
484 case ITDB_SPLFIELD_LAST_SKIPPED:
485 datecomp = track->last_skipped;
486 handled = TRUE;
487 break;
488 case ITDB_SPLFIELD_SEASON_NR:
489 intcomp = track->season_nr;
490 handled = TRUE;
491 break;
492 case ITDB_SPLFIELD_SKIPCOUNT:
493 intcomp = track->skipcount;
494 handled = TRUE;
495 break;
496 case ITDB_SPLFIELD_VIDEO_KIND:
497 intcomp = track->mediatype;
498 handled = TRUE;
499 break;
500 }
501 if (!handled)
502 { /* unknown field type -- default to FALSE */
503 g_return_val_if_reached (FALSE);
504 }
505
506 /* actually do the comparison to our rule */
507 switch (ft)
508 {
509 case ITDB_SPLFT_STRING:
510 if(strcomp && splr->string)
511 {
512 gchar *casefolded_str;
513 gboolean cmp_result;
514
515 casefolded_str = g_utf8_casefold(splr->string, -1);
516 cmp_result = FALSE;
517 switch (splr->action)
518 {
519 case ITDB_SPLACTION_IS_STRING:
520 cmp_result = (strcmp (strcomp, casefolded_str) == 0);
521 break;
522 case ITDB_SPLACTION_IS_NOT:
523 cmp_result = (strcmp (strcomp, casefolded_str) != 0);
524 break;
525 case ITDB_SPLACTION_CONTAINS:
526 cmp_result = (strstr (strcomp, casefolded_str) != NULL);
527 break;
528 case ITDB_SPLACTION_DOES_NOT_CONTAIN:
529 cmp_result = (strstr (strcomp, casefolded_str) == NULL);
530 break;
531 case ITDB_SPLACTION_STARTS_WITH:
532 cmp_result = g_str_has_prefix(strcomp, casefolded_str);
533 break;
534 case ITDB_SPLACTION_ENDS_WITH:
535 cmp_result = g_str_has_suffix(strcomp, casefolded_str);
536 break;
537 case ITDB_SPLACTION_DOES_NOT_START_WITH:
538 cmp_result = !g_str_has_prefix(strcomp, casefolded_str);
539 break;
540 case ITDB_SPLACTION_DOES_NOT_END_WITH:
541 cmp_result = !g_str_has_suffix(strcomp, casefolded_str);
542 break;
543 }
544 g_free(casefolded_str);
545 g_free(strcomp);
546 return cmp_result;
547 }
548 g_free(strcomp);
549 return FALSE;
550 case ITDB_SPLFT_INT:
551 switch(splr->action)
552 {
553 case ITDB_SPLACTION_IS_INT:
554 return (intcomp == splr->fromvalue);
555 case ITDB_SPLACTION_IS_NOT_INT:
556 return (intcomp != splr->fromvalue);
557 case ITDB_SPLACTION_IS_GREATER_THAN:
558 return (intcomp > splr->fromvalue);
559 case ITDB_SPLACTION_IS_LESS_THAN:
560 return (intcomp < splr->fromvalue);
561 case ITDB_SPLACTION_IS_IN_THE_RANGE:
562 return ((intcomp <= splr->fromvalue &&
563 intcomp >= splr->tovalue) ||
564 (intcomp >= splr->fromvalue &&
565 intcomp <= splr->tovalue));
566 case ITDB_SPLACTION_IS_NOT_IN_THE_RANGE:
567 return ((intcomp < splr->fromvalue &&
568 intcomp < splr->tovalue) ||
569 (intcomp > splr->fromvalue &&
570 intcomp > splr->tovalue));
571 }
572 return FALSE;
573 case ITDB_SPLFT_BINARY_AND:
574 switch(splr->action)
575 {
576 case ITDB_SPLACTION_BINARY_AND:
577 return (intcomp & splr->fromvalue)? TRUE:FALSE;
578 case ITDB_SPLACTION_NOT_BINARY_AND:
579 return (intcomp & splr->fromvalue)? FALSE:TRUE;
580 }
581 return FALSE;
582 case ITDB_SPLFT_BOOLEAN:
583 switch (splr->action)
584 {
585 case ITDB_SPLACTION_IS_INT: /* aka "is set" */
586 return (boolcomp != 0);
587 case ITDB_SPLACTION_IS_NOT_INT: /* aka "is not set" */
588 return (boolcomp == 0);
589 }
590 return FALSE;
591 case ITDB_SPLFT_DATE:
592 switch (splr->action)
593 {
594 case ITDB_SPLACTION_IS_INT:
595 return (datecomp == splr->fromvalue);
596 case ITDB_SPLACTION_IS_NOT_INT:
597 return (datecomp != splr->fromvalue);
598 case ITDB_SPLACTION_IS_GREATER_THAN:
599 return (datecomp > splr->fromvalue);
600 case ITDB_SPLACTION_IS_LESS_THAN:
601 return (datecomp < splr->fromvalue);
602 case ITDB_SPLACTION_IS_NOT_GREATER_THAN:
603 return (datecomp <= splr->fromvalue);
604 case ITDB_SPLACTION_IS_NOT_LESS_THAN:
605 return (datecomp >= splr->fromvalue);
606 case ITDB_SPLACTION_IS_IN_THE_LAST:
607 time (&t);
608 t += (splr->fromdate * splr->fromunits);
609 return (datecomp > t);
610 case ITDB_SPLACTION_IS_NOT_IN_THE_LAST:
611 time (&t);
612 t += (splr->fromdate * splr->fromunits);
613 return (datecomp <= t);
614 case ITDB_SPLACTION_IS_IN_THE_RANGE:
615 return ((datecomp <= splr->fromvalue &&
616 datecomp >= splr->tovalue) ||
617 (datecomp >= splr->fromvalue &&
618 datecomp <= splr->tovalue));
619 case ITDB_SPLACTION_IS_NOT_IN_THE_RANGE:
620 return ((datecomp < splr->fromvalue &&
621 datecomp < splr->tovalue) ||
622 (datecomp > splr->fromvalue &&
623 datecomp > splr->tovalue));
624 }
625 return FALSE;
626 case ITDB_SPLFT_PLAYLIST:
627 /* if we didn't find the playlist, just exit instead of
628 dealing with it */
629 if (playcomp == NULL) return FALSE;
630
631 switch(splr->action)
632 {
633 case ITDB_SPLACTION_IS_INT: /* is this track in this playlist? */
634 return (itdb_playlist_contains_track (playcomp, track));
635 case ITDB_SPLACTION_IS_NOT_INT:/* NOT in this playlist? */
636 return (!itdb_playlist_contains_track (playcomp, track));
637 }
638 return FALSE;
639 case ITDB_SPLFT_UNKNOWN:
640 g_return_val_if_fail (ft != ITDB_SPLFT_UNKNOWN, FALSE);
641 return FALSE;
642 default: /* new type: warning to change this code */
643 g_return_val_if_fail (FALSE, FALSE);
644 return FALSE;
645 }
646 /* we should never make it out of the above switches alive */
647 g_return_val_if_fail (FALSE, FALSE);
648 return FALSE;
649 }
650
651 /* local functions to help with the sorting of the list of tracks so
652 * that we can do limits */
compTitle(Itdb_Track * a,Itdb_Track * b)653 static gint compTitle (Itdb_Track *a, Itdb_Track *b)
654 {
655 return strcmp (a->title, b->title);
656 }
compAlbum(Itdb_Track * a,Itdb_Track * b)657 static gint compAlbum (Itdb_Track *a, Itdb_Track *b)
658 {
659 return strcmp (a->album, b->album);
660 }
compArtist(Itdb_Track * a,Itdb_Track * b)661 static gint compArtist (Itdb_Track *a, Itdb_Track *b)
662 {
663 return strcmp (a->artist, b->artist);
664 }
compGenre(Itdb_Track * a,Itdb_Track * b)665 static gint compGenre (Itdb_Track *a, Itdb_Track *b)
666 {
667 return strcmp (a->genre, b->genre);
668 }
compMostRecentlyAdded(Itdb_Track * a,Itdb_Track * b)669 static gint compMostRecentlyAdded (Itdb_Track *a, Itdb_Track *b)
670 {
671 return b->time_added - a->time_added;
672 }
compLeastRecentlyAdded(Itdb_Track * a,Itdb_Track * b)673 static gint compLeastRecentlyAdded (Itdb_Track *a, Itdb_Track *b)
674 {
675 return a->time_added - b->time_added;
676 }
compMostOftenPlayed(Itdb_Track * a,Itdb_Track * b)677 static gint compMostOftenPlayed (Itdb_Track *a, Itdb_Track *b)
678 {
679 return b->playcount - a->playcount;
680 }
compLeastOftenPlayed(Itdb_Track * a,Itdb_Track * b)681 static gint compLeastOftenPlayed (Itdb_Track *a, Itdb_Track *b)
682 {
683 return a->playcount - b->playcount;
684 }
compMostRecentlyPlayed(Itdb_Track * a,Itdb_Track * b)685 static gint compMostRecentlyPlayed (Itdb_Track *a, Itdb_Track *b)
686 {
687 return b->time_played - a->time_played;
688 }
compLeastRecentlyPlayed(Itdb_Track * a,Itdb_Track * b)689 static gint compLeastRecentlyPlayed (Itdb_Track *a, Itdb_Track *b)
690 {
691 return a->time_played - b->time_played;
692 }
compHighestRating(Itdb_Track * a,Itdb_Track * b)693 static gint compHighestRating (Itdb_Track *a, Itdb_Track *b)
694 {
695 return b->rating - a->rating;
696 }
compLowestRating(Itdb_Track * a,Itdb_Track * b)697 static gint compLowestRating (Itdb_Track *a, Itdb_Track *b)
698 {
699 return a->rating - b->rating;
700 }
701
702 /* Randomize the order of the members of the GList @list */
703 /* Returns a pointer to the new start of the list */
randomize_glist(GList * list)704 static GList *randomize_glist (GList *list)
705 {
706 gint32 nr = g_list_length (list);
707
708 while (nr > 1)
709 {
710 /* get random element among the first nr members */
711 gint32 rand = g_random_int_range (0, nr);
712 GList *gl = g_list_nth (list, rand);
713 /* remove it and add it at the end */
714 list = g_list_remove_link (list, gl);
715 list = g_list_concat (list, gl);
716 --nr;
717 }
718 return list;
719 }
720
721 /**
722 * itdb_playlist_randomize:
723 * @pl: an #Itdb_Playlist to randomize
724 *
725 * Randomizes @pl
726 */
itdb_playlist_randomize(Itdb_Playlist * pl)727 void itdb_playlist_randomize (Itdb_Playlist *pl)
728 {
729 g_return_if_fail (pl);
730
731 pl->members = randomize_glist (pl->members);
732 }
733
734 /**
735 * itdb_spl_update:
736 * @spl: an #Itdb_Playlist
737 *
738 * Updates the content of the smart playlist @spl (meant to be called
739 * if the tracks stored in the #Itdb_iTunesDB associated with @spl
740 * have changed somehow and you want @spl->members to be accurate
741 * with regards to those changes. Does nothing if @spl isn't a smart
742 * playlist.
743 */
itdb_spl_update(Itdb_Playlist * spl)744 void itdb_spl_update (Itdb_Playlist *spl)
745 {
746 GList *gl;
747 Itdb_iTunesDB *itdb;
748 GList *sel_tracks = NULL;
749
750 g_return_if_fail (spl);
751 g_return_if_fail (spl->itdb);
752
753 itdb = spl->itdb;
754
755 /* we only can populate smart playlists */
756 if (!spl->is_spl) return;
757
758 /* clear this playlist */
759 g_list_free (spl->members);
760 spl->members = NULL;
761 spl->num = 0;
762
763 for (gl=itdb->tracks; gl ; gl=gl->next)
764 {
765 Itdb_Track *t = gl->data;
766 g_return_if_fail (t);
767 /* skip non-checked songs if we have to do so (this takes care
768 of *all* the match_checked functionality) */
769 if (spl->splpref.matchcheckedonly && (t->checked != 0))
770 continue;
771 /* first, match the rules */
772 if (spl->splpref.checkrules)
773 { /* if we are set to check the rules */
774 /* start with true for "match all",
775 start with false for "match any" */
776 gboolean matchrules;
777 GList *gl;
778
779 if (spl->splrules.match_operator == ITDB_SPLMATCH_AND)
780 matchrules = TRUE;
781 else matchrules = FALSE;
782 /* assume everything matches with no rules */
783 if (spl->splrules.rules == NULL) matchrules = TRUE;
784 /* match all rules */
785 for (gl=spl->splrules.rules; gl; gl=gl->next)
786 {
787 Itdb_SPLRule* splr = gl->data;
788 gboolean ruletruth = itdb_splr_eval (splr, t);
789 if (spl->splrules.match_operator == ITDB_SPLMATCH_AND)
790 {
791 if (!ruletruth)
792 { /* one rule did not match -- we can stop */
793 matchrules = FALSE;
794 break;
795 }
796 }
797 else if (spl->splrules.match_operator == ITDB_SPLMATCH_OR)
798 {
799 if (ruletruth)
800 { /* one rule matched -- we can stop */
801 matchrules = TRUE;
802 break;
803 }
804 }
805 }
806 if (matchrules)
807 { /* we have a track that matches the ruleset, append to
808 * playlist for now*/
809 sel_tracks = g_list_append (sel_tracks, t);
810 }
811 }
812 else
813 { /* we aren't checking the rules, so just append to
814 playlist */
815 sel_tracks = g_list_append (sel_tracks, t);
816 }
817 }
818 /* no reason to go on if nothing matches so far */
819 if (g_list_length (sel_tracks) == 0) return;
820
821 /* do the limits */
822 if (spl->splpref.checklimits)
823 {
824 /* use a double because we may need to deal with fractions
825 * here */
826 gdouble runningtotal = 0;
827 guint32 trackcounter = 0;
828 guint32 tracknum = g_list_length (sel_tracks);
829
830 /* printf("limitsort: %d\n", spl->splpref.limitsort); */
831
832 /* limit to (number) (type) selected by (sort) */
833 /* first, we sort the list */
834 switch(spl->splpref.limitsort)
835 {
836 case ITDB_LIMITSORT_RANDOM:
837 sel_tracks = randomize_glist (sel_tracks);
838 break;
839 case ITDB_LIMITSORT_SONG_NAME:
840 sel_tracks = g_list_sort (sel_tracks, (GCompareFunc)compTitle);
841 break;
842 case ITDB_LIMITSORT_ALBUM:
843 sel_tracks = g_list_sort (sel_tracks, (GCompareFunc)compAlbum);
844 break;
845 case ITDB_LIMITSORT_ARTIST:
846 sel_tracks = g_list_sort (sel_tracks, (GCompareFunc)compArtist);
847 break;
848 case ITDB_LIMITSORT_GENRE:
849 sel_tracks = g_list_sort (sel_tracks, (GCompareFunc)compGenre);
850 break;
851 case ITDB_LIMITSORT_MOST_RECENTLY_ADDED:
852 sel_tracks = g_list_sort (sel_tracks,
853 (GCompareFunc)compMostRecentlyAdded);
854 break;
855 case ITDB_LIMITSORT_LEAST_RECENTLY_ADDED:
856 sel_tracks = g_list_sort (sel_tracks,
857 (GCompareFunc)compLeastRecentlyAdded);
858 break;
859 case ITDB_LIMITSORT_MOST_OFTEN_PLAYED:
860 sel_tracks = g_list_sort (sel_tracks,
861 (GCompareFunc)compMostOftenPlayed);
862 break;
863 case ITDB_LIMITSORT_LEAST_OFTEN_PLAYED:
864 sel_tracks = g_list_sort (sel_tracks,
865 (GCompareFunc)compLeastOftenPlayed);
866 break;
867 case ITDB_LIMITSORT_MOST_RECENTLY_PLAYED:
868 sel_tracks = g_list_sort (sel_tracks,
869 (GCompareFunc)compMostRecentlyPlayed);
870 break;
871 case ITDB_LIMITSORT_LEAST_RECENTLY_PLAYED:
872 sel_tracks = g_list_sort (sel_tracks,
873 (GCompareFunc)compLeastRecentlyPlayed);
874 break;
875 case ITDB_LIMITSORT_HIGHEST_RATING:
876 sel_tracks = g_list_sort (sel_tracks,
877 (GCompareFunc)compHighestRating);
878 break;
879 case ITDB_LIMITSORT_LOWEST_RATING:
880 sel_tracks = g_list_sort (sel_tracks,
881 (GCompareFunc)compLowestRating);
882 break;
883 default:
884 g_warning ("Programming error: should not reach this point (default of switch (spl->splpref.limitsort)\n");
885 break;
886 }
887 /* now that the list is sorted in the order we want, we
888 take the top X tracks off the list and insert them into
889 our playlist */
890
891 while ((runningtotal < spl->splpref.limitvalue) &&
892 (trackcounter < tracknum))
893 {
894 gdouble currentvalue=0;
895 Itdb_Track *t = g_list_nth_data (sel_tracks, trackcounter);
896
897 /* printf ("track: %d runningtotal: %lf, limitvalue: %d\n", */
898 /* trackcounter, runningtotal, spl->splpref.limitvalue); */
899
900 /* get the next song's value to add to running total */
901 switch (spl->splpref.limittype)
902 {
903 case ITDB_LIMITTYPE_MINUTES:
904 currentvalue = (double)(t->tracklen)/(60*1000);
905 break;
906 case ITDB_LIMITTYPE_HOURS:
907 currentvalue = (double)(t->tracklen)/(60*60*1000);
908 break;
909 case ITDB_LIMITTYPE_MB:
910 currentvalue = (double)(t->size)/(1024*1024);
911 break;
912 case ITDB_LIMITTYPE_GB:
913 currentvalue = (double)(t->size)/(1024*1024*1024);
914 break;
915 case ITDB_LIMITTYPE_SONGS:
916 currentvalue = 1;
917 break;
918 default:
919 g_warning ("Programming error: should not reach this point (default of switch (spl->splpref.limittype)\n");
920 break;
921 }
922 /* check to see that we won't actually exceed the
923 * limitvalue */
924 if (runningtotal + currentvalue <=
925 spl->splpref.limitvalue)
926 {
927 runningtotal += currentvalue;
928 /* Add the playlist entry */
929 itdb_playlist_add_track (spl, t, -1);
930 }
931 /* increment the track counter so we can look at the next
932 track */
933 trackcounter++;
934 /* printf (" track: %d runningtotal: %lf, limitvalue: %d\n", */
935 /* trackcounter, runningtotal, spl->splpref.limitvalue); */
936 } /* end while */
937 /* no longer needed */
938 g_list_free (sel_tracks);
939 sel_tracks = NULL;
940 } /* end if limits enabled */
941 else
942 { /* no limits, so stick everything that matched the rules into
943 the playlist */
944 spl->members = sel_tracks;
945 spl->num = g_list_length (sel_tracks);
946 sel_tracks = NULL;
947 }
948 }
949
950 /**
951 * itdb_spl_update_all:
952 * @itdb: an #Itdb_iTunesDB
953 *
954 * Updates all smart playlists contained in @itdb
955 */
itdb_spl_update_all(Itdb_iTunesDB * itdb)956 void itdb_spl_update_all (Itdb_iTunesDB *itdb)
957 {
958 g_return_if_fail (itdb);
959
960 g_list_foreach (itdb->playlists, (GFunc)itdb_spl_update, NULL);
961 }
962
963
spl_update2(Itdb_Playlist * playlist,gpointer data)964 static void spl_update2 (Itdb_Playlist *playlist, gpointer data)
965 {
966 g_return_if_fail (playlist);
967 if (playlist->is_spl && playlist->splpref.liveupdate)
968 itdb_spl_update (playlist);
969 }
970
971 /**
972 * itdb_spl_update_live:
973 * @itdb: an #Itdb_iTunesDB
974 *
975 * Updates all smart playlists contained in @itdb which have the
976 * @liveupdate flag set.
977 *
978 * Since: 0.2.0
979 */
itdb_spl_update_live(Itdb_iTunesDB * itdb)980 void itdb_spl_update_live (Itdb_iTunesDB *itdb)
981 {
982 g_return_if_fail (itdb);
983
984 g_list_foreach (itdb->playlists, (GFunc)spl_update2, NULL);
985 }
986
987
988 /* end of code based on Samuel Wood's work */
989 /* ------------------------------------------------------------------- */
990
991 /**
992 * itdb_splr_validate:
993 * @splr: an #Itdb_SPLRule
994 *
995 * Validates a smart playlist rule
996 */
itdb_splr_validate(Itdb_SPLRule * splr)997 void itdb_splr_validate (Itdb_SPLRule *splr)
998 {
999 ItdbSPLActionType at;
1000
1001 g_return_if_fail (splr != NULL);
1002
1003 at = itdb_splr_get_action_type (splr);
1004
1005 g_return_if_fail (at != ITDB_SPLAT_UNKNOWN);
1006
1007 switch (at)
1008 {
1009 case ITDB_SPLAT_INT:
1010 case ITDB_SPLAT_PLAYLIST:
1011 case ITDB_SPLAT_DATE:
1012 case ITDB_SPLAT_BINARY_AND:
1013 splr->fromdate = 0;
1014 splr->fromunits = 1;
1015 splr->tovalue = splr->fromvalue;
1016 splr->todate = 0;
1017 splr->tounits = 1;
1018 break;
1019 case ITDB_SPLAT_RANGE_INT:
1020 case ITDB_SPLAT_RANGE_DATE:
1021 splr->fromdate = 0;
1022 splr->fromunits = 1;
1023 splr->todate = 0;
1024 splr->tounits = 1;
1025 break;
1026 case ITDB_SPLAT_INTHELAST:
1027 splr->fromvalue = ITDB_SPL_DATE_IDENTIFIER;
1028 splr->tovalue = ITDB_SPL_DATE_IDENTIFIER;
1029 splr->tounits = 1;
1030 break;
1031 case ITDB_SPLAT_NONE:
1032 case ITDB_SPLAT_STRING:
1033 splr->fromvalue = 0;
1034 splr->fromdate = 0;
1035 splr->fromunits = 0;
1036 splr->tovalue = 0;
1037 splr->todate = 0;
1038 splr->tounits = 0;
1039 break;
1040 case ITDB_SPLAT_INVALID:
1041 case ITDB_SPLAT_UNKNOWN:
1042 g_return_if_fail (FALSE);
1043 break;
1044 }
1045
1046 }
1047
1048 /**
1049 * itdb_splr_free:
1050 * @splr: an #Itdb_SPLRule
1051 *
1052 * Frees the memory used by @splr
1053 */
itdb_splr_free(Itdb_SPLRule * splr)1054 void itdb_splr_free (Itdb_SPLRule *splr)
1055 {
1056 if (splr)
1057 {
1058 g_free (splr->string);
1059 g_free (splr);
1060 }
1061 }
1062
1063 /**
1064 * itdb_splr_remove:
1065 * @pl: an #Itdb_Playlist
1066 * @splr: an Itdb_SPLRule
1067 *
1068 * Removes the smart playlist rule @splr from playlist @pl. The memory
1069 * used by @splr is freed.
1070 */
itdb_splr_remove(Itdb_Playlist * pl,Itdb_SPLRule * splr)1071 void itdb_splr_remove (Itdb_Playlist *pl, Itdb_SPLRule *splr)
1072 {
1073 g_return_if_fail (pl);
1074 g_return_if_fail (splr);
1075
1076 pl->splrules.rules = g_list_remove (pl->splrules.rules, splr);
1077 itdb_splr_free (splr);
1078 }
1079
1080 /**
1081 * itdb_splr_add:
1082 * @pl: an #Itdb_Playlist
1083 * @splr: an #Itdb_SPLRule
1084 * @pos: position of the rule
1085 *
1086 * Adds the smart rule @splr to @pl at position @pos. If @pos is -1,
1087 * @splr gets appended to the end. After this call, @splr memory is
1088 * managed by @pl, so you no longer need to call itdb_splr_free()
1089 */
itdb_splr_add(Itdb_Playlist * pl,Itdb_SPLRule * splr,gint pos)1090 void itdb_splr_add (Itdb_Playlist *pl, Itdb_SPLRule *splr, gint pos)
1091 {
1092 g_return_if_fail (pl);
1093 g_return_if_fail (splr);
1094
1095 pl->splrules.rules = g_list_insert (pl->splrules.rules,
1096 splr, pos);
1097 }
1098
1099 /**
1100 * itdb_splr_new:
1101 *
1102 * Creates a new default smart rule
1103 *
1104 * Returns: a new #Itdb_SPLRule that must be freed with itdb_splr_free() when
1105 * no longer needed
1106 */
itdb_splr_new(void)1107 Itdb_SPLRule *itdb_splr_new (void)
1108 {
1109 Itdb_SPLRule *splr = g_new0 (Itdb_SPLRule, 1);
1110
1111 splr->field = ITDB_SPLFIELD_ARTIST;
1112 splr->action = ITDB_SPLACTION_CONTAINS;
1113 splr->fromvalue = 0;
1114 splr->fromdate = 0;
1115 splr->fromunits = 0;
1116 splr->tovalue = 0;
1117 splr->todate = 0;
1118 splr->tounits = 0;
1119
1120 return splr;
1121 }
1122
1123 /**
1124 * itdb_splr_add_new:
1125 * @pl: an #Itdb_Playlist
1126 * @pos: position to insert the rule at
1127 *
1128 * Creates a new smart rule and inserts it at position @pos in @pl. If
1129 * @pos is -1, the new rule gets appended to the end.
1130 *
1131 * Returns: pointer to the newly created #Itdb_SPLRule. Its
1132 * memory is handled by @pl though, so you don't need to explicitly
1133 * call itdb_splr_free() on it
1134 */
itdb_splr_add_new(Itdb_Playlist * pl,gint pos)1135 Itdb_SPLRule *itdb_splr_add_new (Itdb_Playlist *pl, gint pos)
1136 {
1137 Itdb_SPLRule *splr;
1138
1139 g_return_val_if_fail (pl, NULL);
1140
1141 splr = itdb_splr_new ();
1142 itdb_splr_add (pl, splr, pos);
1143 return splr;
1144 }
1145
1146 /* Duplicate Itdb_SPLRule @splr */
splr_duplicate(Itdb_SPLRule * splr)1147 static Itdb_SPLRule *splr_duplicate (Itdb_SPLRule *splr)
1148 {
1149 Itdb_SPLRule *dup = NULL;
1150 if (splr)
1151 {
1152 dup = g_malloc (sizeof (Itdb_SPLRule));
1153 memcpy (dup, splr, sizeof (Itdb_SPLRule));
1154
1155 /* Now copy the strings */
1156 dup->string = g_strdup (splr->string);
1157 }
1158 return dup;
1159 }
1160
1161 /**
1162 * itdb_playlist_duplicate:
1163 * @pl: an #Itdb_Playlist
1164 *
1165 * Duplicates an existing playlist. @pl_dup->id is set to zero, so
1166 * that it will be set to a unique value when adding it to an
1167 * #Itdb_iTunesDB. The returned playlist won't be associated with an
1168 * #Itdb_iTunesDB.
1169 *
1170 * Returns: a newly allocated #Itdb_Playlist that you'll have to
1171 * free with itdb_playlist_free() when you no longer need it.
1172 */
itdb_playlist_duplicate(Itdb_Playlist * pl)1173 Itdb_Playlist *itdb_playlist_duplicate (Itdb_Playlist *pl)
1174 {
1175 Itdb_Playlist *pl_dup;
1176 GList *gl;
1177
1178 g_return_val_if_fail (pl, NULL);
1179
1180 pl_dup = g_new (Itdb_Playlist, 1);
1181 memcpy (pl_dup, pl, sizeof (Itdb_Playlist));
1182 /* clear list heads */
1183 pl_dup->members = NULL;
1184 pl_dup->splrules.rules = NULL;
1185
1186 /* clear itdb pointer */
1187 pl_dup->itdb = NULL;
1188
1189 /* Now copy strings */
1190 pl_dup->name = g_strdup (pl->name);
1191
1192 /* Copy members */
1193 pl_dup->members = g_list_copy (pl->members);
1194
1195 /* Copy rules */
1196 for (gl=pl->splrules.rules; gl; gl=gl->next)
1197 {
1198 Itdb_SPLRule *splr_dup = splr_duplicate (gl->data);
1199 pl_dup->splrules.rules = g_list_append (
1200 pl_dup->splrules.rules, splr_dup);
1201 }
1202
1203 /* Set id to 0, so it will be set to a unique value when adding
1204 * this playlist to a itdb */
1205 pl_dup->id = 0;
1206
1207 /* Copy userdata */
1208 if (pl->userdata && pl->userdata_duplicate)
1209 pl_dup->userdata = pl->userdata_duplicate (pl->userdata);
1210
1211 /* Copy private data too */
1212 pl_dup->priv = g_memdup (pl->priv, sizeof (Itdb_Playlist_Private));
1213
1214 return pl_dup;
1215 }
1216
1217 /**
1218 * itdb_spl_copy_rules:
1219 * @dest: destination #Itdb_Playlist
1220 * @src: source #Itdb_Playlist
1221 *
1222 * Copy all relevant information for smart playlist from playlist @src
1223 * to playlist @dest. If @dest is already a smart playlist, the
1224 * existing data is overwritten/deleted.
1225 */
itdb_spl_copy_rules(Itdb_Playlist * dest,Itdb_Playlist * src)1226 void itdb_spl_copy_rules (Itdb_Playlist *dest, Itdb_Playlist *src)
1227 {
1228 GList *gl;
1229
1230 g_return_if_fail (dest);
1231 g_return_if_fail (src);
1232 g_return_if_fail (dest->is_spl);
1233 g_return_if_fail (src->is_spl);
1234
1235 /* remove existing rules */
1236 g_list_foreach (dest->splrules.rules, (GFunc)(itdb_splr_free), NULL);
1237 g_list_free (dest->splrules.rules);
1238
1239 /* copy general spl settings */
1240 memcpy (&dest->splpref, &src->splpref, sizeof (Itdb_SPLPref));
1241 memcpy (&dest->splrules, &src->splrules, sizeof (Itdb_SPLRules));
1242 dest->splrules.rules = NULL;
1243
1244 /* Copy rules */
1245 for (gl=src->splrules.rules; gl; gl=gl->next)
1246 {
1247 Itdb_SPLRule *splr_dup = splr_duplicate (gl->data);
1248 dest->splrules.rules = g_list_append (
1249 dest->splrules.rules, splr_dup);
1250 }
1251 }
1252
1253 /**
1254 * itdb_playlist_new:
1255 * @title: playlist title
1256 * @spl: smart playlist flag
1257 *
1258 * Creates a new playlist. If @spl is TRUE, a smart playlist is
1259 * generated. pl->id is set by itdb_playlist_add() when the playlist
1260 * is added to an #Itdb_iTunesDB
1261 *
1262 * Returns: a new #Itdb_Playlist which must be freed with
1263 * itdb_playlist_free() after use
1264 */
itdb_playlist_new(const gchar * title,gboolean spl)1265 Itdb_Playlist *itdb_playlist_new (const gchar *title, gboolean spl)
1266 {
1267 Itdb_Playlist *pl = g_new0 (Itdb_Playlist, 1);
1268
1269 pl->type = ITDB_PL_TYPE_NORM;
1270 pl->name = g_strdup (title);
1271 pl->sortorder = ITDB_PSO_MANUAL;
1272
1273 pl->timestamp = time (NULL);
1274
1275 pl->is_spl = spl;
1276 if (spl)
1277 {
1278 pl->splpref.liveupdate = TRUE;
1279 pl->splpref.checkrules = TRUE;
1280 pl->splpref.checklimits = FALSE;
1281 pl->splpref.limittype = ITDB_LIMITTYPE_HOURS;
1282 pl->splpref.limitsort = ITDB_LIMITSORT_RANDOM;
1283 pl->splpref.limitvalue = 2;
1284 pl->splpref.matchcheckedonly = FALSE;
1285 pl->splrules.match_operator = ITDB_SPLMATCH_AND;
1286 /* add at least one rule */
1287 itdb_splr_add_new (pl, 0);
1288 }
1289 pl->priv = g_new0 (Itdb_Playlist_Private, 1);
1290
1291 return pl;
1292 }
1293
1294 /**
1295 * itdb_playlist_free:
1296 * @pl: an #Itdb_Playlist
1297 *
1298 * Frees the memory used by playlist @pl.
1299 */
itdb_playlist_free(Itdb_Playlist * pl)1300 void itdb_playlist_free (Itdb_Playlist *pl)
1301 {
1302 g_return_if_fail (pl);
1303
1304 g_free (pl->name);
1305 g_list_free (pl->members);
1306 g_list_foreach (pl->splrules.rules, (GFunc)(itdb_splr_free), NULL);
1307 g_list_free (pl->splrules.rules);
1308 if (pl->userdata && pl->userdata_destroy)
1309 (*pl->userdata_destroy) (pl->userdata);
1310
1311 g_free (pl->priv);
1312 g_free (pl);
1313 }
1314
itdb_playlist_add_internal(Itdb_iTunesDB * itdb,Itdb_Playlist * pl,gint32 pos,GList ** playlists)1315 static void itdb_playlist_add_internal (Itdb_iTunesDB *itdb, Itdb_Playlist *pl,
1316 gint32 pos, GList **playlists)
1317 {
1318 g_return_if_fail (itdb);
1319 g_return_if_fail (pl);
1320 g_return_if_fail (!pl->userdata || pl->userdata_duplicate);
1321
1322 pl->itdb = itdb;
1323
1324 /* set unique ID when not yet set */
1325 if (pl->id == 0)
1326 {
1327 GList *gl;
1328 guint64 id;
1329 do
1330 {
1331 id = ((guint64)g_random_int () << 32) |
1332 ((guint64)g_random_int ());
1333 /* check if id is really unique (with 100 playlists the
1334 * chance to create a duplicate is 1 in
1335 * 184,467,440,737,095,516.16) */
1336 for (gl=*playlists; id && gl; gl=gl->next)
1337 {
1338 Itdb_Playlist *g_pl = gl->data;
1339 g_return_if_fail (g_pl);
1340 if (id == g_pl->id) id = 0;
1341 }
1342 } while (id == 0);
1343 pl->id = id;
1344 }
1345 if (pl->sortorder == 0) pl->sortorder = ITDB_PSO_MANUAL;
1346 if (pl->timestamp == 0) pl->timestamp = time (NULL);
1347
1348 /* pos == -1 appends at the end of the list */
1349 *playlists = g_list_insert (*playlists, pl, pos);
1350 }
1351
itdb_playlist_add_mhsd5_playlist(Itdb_iTunesDB * itdb,Itdb_Playlist * pl,gint32 pos)1352 void itdb_playlist_add_mhsd5_playlist (Itdb_iTunesDB *itdb, Itdb_Playlist *pl,
1353 gint32 pos)
1354 {
1355 itdb_playlist_add_internal (itdb, pl, pos, &itdb->priv->mhsd5_playlists);
1356 }
1357
1358 /**
1359 * itdb_playlist_add:
1360 * @itdb: an #Itdb_iTunesDB
1361 * @pl: an #Itdb_Playlist
1362 * @pos: position to insert @pl at
1363 *
1364 * Adds playlist @pl to the database @itdb at position @pos (-1 for
1365 * "append to end"). A unique id is created if @pl->id is equal to
1366 * zero. After calling this function, @itdb manages the memory of @pl,
1367 * which means you no longer need to explicitly call
1368 * itdb_playlist_free()
1369 */
itdb_playlist_add(Itdb_iTunesDB * itdb,Itdb_Playlist * pl,gint32 pos)1370 void itdb_playlist_add (Itdb_iTunesDB *itdb, Itdb_Playlist *pl, gint32 pos)
1371 {
1372 itdb_playlist_add_internal (itdb, pl, pos, &itdb->playlists);
1373 }
1374
1375 /**
1376 * itdb_playlist_move:
1377 * @pl: an #Itdb_Playlist
1378 * @pos: new position
1379 *
1380 * Moves playlist @pl to position @pos. -1 will append to the end of the
1381 * list.
1382 */
itdb_playlist_move(Itdb_Playlist * pl,gint32 pos)1383 void itdb_playlist_move (Itdb_Playlist *pl, gint32 pos)
1384 {
1385 Itdb_iTunesDB *itdb;
1386
1387 g_return_if_fail (pl);
1388 itdb = pl->itdb;
1389 g_return_if_fail (itdb);
1390
1391 itdb->playlists = g_list_remove (itdb->playlists, pl);
1392 itdb->playlists = g_list_insert (itdb->playlists, pl, pos);
1393 }
1394
1395 /**
1396 * itdb_playlist_remove:
1397 * @pl: an #Itdb_Playlist
1398 *
1399 * Removes @pl from the #Itdb_iTunesDB it's associated with
1400 * and frees memory
1401 */
itdb_playlist_remove(Itdb_Playlist * pl)1402 void itdb_playlist_remove (Itdb_Playlist *pl)
1403 {
1404 Itdb_iTunesDB *itdb;
1405
1406 g_return_if_fail (pl);
1407 itdb = pl->itdb;
1408 g_return_if_fail (itdb);
1409
1410 itdb->playlists = g_list_remove (itdb->playlists, pl);
1411 itdb_playlist_free (pl);
1412 }
1413
1414 /**
1415 * itdb_playlist_unlink:
1416 * @pl: an #Itdb_Playlist
1417 *
1418 * Remove @pl from the #Itdb_iTunesDB it's associated with but do not
1419 * free memory. @pl->itdb is set to NULL after this function returns
1420 */
itdb_playlist_unlink(Itdb_Playlist * pl)1421 void itdb_playlist_unlink (Itdb_Playlist *pl)
1422 {
1423 Itdb_iTunesDB *itdb;
1424
1425 g_return_if_fail (pl);
1426 itdb = pl->itdb;
1427 g_return_if_fail (itdb);
1428
1429 itdb->playlists = g_list_remove (itdb->playlists, pl);
1430 pl->itdb = NULL;
1431 }
1432
1433 /**
1434 * itdb_playlist_exists:
1435 * @itdb: an #Itdb_iTunesDB
1436 * @pl: an #Itdb_Playlist
1437 *
1438 * Checks if @pl is present in @itdb
1439 *
1440 * Returns: TRUE if @pl exists in @itdb, FALSE otherwise
1441 */
itdb_playlist_exists(Itdb_iTunesDB * itdb,Itdb_Playlist * pl)1442 gboolean itdb_playlist_exists (Itdb_iTunesDB *itdb, Itdb_Playlist *pl)
1443 {
1444 g_return_val_if_fail (itdb, FALSE);
1445 g_return_val_if_fail (pl, FALSE);
1446
1447 if (g_list_find (itdb->playlists, pl)) return TRUE;
1448 else return FALSE;
1449 }
1450
1451 /**
1452 * itdb_playlist_add_track:
1453 * @pl: an #Itdb_Playlist
1454 * @track: an #Itdb_Track
1455 * @pos: position to insert @track at
1456 *
1457 * Adds @track to @pl at position @pos (-1 to append at the end)
1458 */
itdb_playlist_add_track(Itdb_Playlist * pl,Itdb_Track * track,gint32 pos)1459 void itdb_playlist_add_track (Itdb_Playlist *pl,
1460 Itdb_Track *track, gint32 pos)
1461 {
1462 g_return_if_fail (pl);
1463 g_return_if_fail (pl->itdb);
1464 g_return_if_fail (track);
1465
1466 track->itdb = pl->itdb;
1467
1468 pl->members = g_list_insert (pl->members, track, pos);
1469 }
1470
1471 /**
1472 * itdb_playlist_remove_track:
1473 * @pl: an #Itdb_Playlist
1474 * @track: an #Itdb_Track
1475 *
1476 * Removes @track from @pl. If @pl is NULL, removes @track from the
1477 * master playlist. If @track can't be found in @pl, nothing happens.
1478 * If after removing @track, @pl is empty, it's not removed from the
1479 * database The memory used by @track isn't freed.
1480 */
itdb_playlist_remove_track(Itdb_Playlist * pl,Itdb_Track * track)1481 void itdb_playlist_remove_track (Itdb_Playlist *pl, Itdb_Track *track)
1482 {
1483 g_return_if_fail (track);
1484
1485 if (pl == NULL)
1486 pl = itdb_playlist_mpl (track->itdb);
1487
1488 g_return_if_fail (pl);
1489
1490 pl->members = g_list_remove (pl->members, track);
1491 }
1492
1493 /**
1494 * itdb_playlist_by_id:
1495 * @itdb: an #Itdb_iTunesDB
1496 * @id: ID of the playlist to look for
1497 *
1498 * Looks up a playlist whose ID is @id
1499 *
1500 * Returns: the #Itdb_Playlist with ID @id or NULL if there is no
1501 * such playlist.
1502 */
itdb_playlist_by_id(Itdb_iTunesDB * itdb,guint64 id)1503 Itdb_Playlist *itdb_playlist_by_id (Itdb_iTunesDB *itdb, guint64 id)
1504 {
1505 GList *gl;
1506
1507 g_return_val_if_fail (itdb, NULL);
1508
1509 for (gl=itdb->playlists; gl; gl=gl->next)
1510 {
1511 Itdb_Playlist *pl = gl->data;
1512 if (pl->id == id) return pl;
1513 }
1514 return NULL;
1515 }
1516
1517 /**
1518 * itdb_playlist_by_nr:
1519 * @itdb: an #Itdb_iTunesDB
1520 * @num: the position of the playlist, counting from 0
1521 *
1522 * Gets the playlist at the given position in @itdb
1523 *
1524 * Returns: the #Itdb_Playlist, or NULL if there is no playlist
1525 * at @pos
1526 */
itdb_playlist_by_nr(Itdb_iTunesDB * itdb,guint32 num)1527 Itdb_Playlist *itdb_playlist_by_nr (Itdb_iTunesDB *itdb, guint32 num)
1528 {
1529 Itdb_Playlist *pl;
1530 g_return_val_if_fail (itdb, NULL);
1531 pl = g_list_nth_data (itdb->playlists, num);
1532 g_return_val_if_fail (pl, NULL);
1533 return pl;
1534 }
1535
1536 /**
1537 * itdb_playlist_by_name:
1538 * @itdb: an #Itdb_iTunesDB
1539 * @name: name of the playlist to look for
1540 *
1541 * Searches a playlist whose name is @name in @itdb
1542 *
1543 * Returns: the first #Itdb_Playlist with name @name, NULL if
1544 * there is no such playlist
1545 */
itdb_playlist_by_name(Itdb_iTunesDB * itdb,gchar * name)1546 Itdb_Playlist *itdb_playlist_by_name (Itdb_iTunesDB *itdb, gchar *name)
1547 {
1548 GList *gl;
1549 g_return_val_if_fail (itdb, NULL);
1550 g_return_val_if_fail (name, NULL);
1551
1552 for (gl=itdb->playlists; gl; gl=gl->next)
1553 {
1554 Itdb_Playlist *pl = gl->data;
1555 g_return_val_if_fail (pl, NULL);
1556 if (pl->name && (strcmp (pl->name, name) == 0))
1557 return pl;
1558 }
1559 return NULL;
1560 }
1561
1562 /**
1563 * itdb_playlist_is_mpl:
1564 * @pl: an #Itdb_Playlist
1565 *
1566 * Checks if @pl is the master playlist
1567 *
1568 * Returns: TRUE if @pl is the master playlist, FALSE otherwise
1569 *
1570 * Since: 0.1.6
1571 */
itdb_playlist_is_mpl(Itdb_Playlist * pl)1572 gboolean itdb_playlist_is_mpl (Itdb_Playlist *pl)
1573 {
1574 g_return_val_if_fail (pl, FALSE);
1575
1576 return ((pl->type & 0xff) == ITDB_PL_TYPE_MPL);
1577 }
1578
1579 /**
1580 * itdb_playlist_is_podcasts:
1581 * @pl: an #Itdb_Playlist
1582 *
1583 * Checks if @pl is the podcasts playlist
1584 *
1585 * Returns: TRUE if @pl is the podcasts playlist, FALSE otherwise
1586 *
1587 * Since: 0.1.6
1588 */
itdb_playlist_is_podcasts(Itdb_Playlist * pl)1589 gboolean itdb_playlist_is_podcasts (Itdb_Playlist *pl)
1590 {
1591 g_return_val_if_fail (pl, FALSE);
1592
1593 return (pl->podcastflag == ITDB_PL_FLAG_PODCASTS);
1594 }
1595
1596 /**
1597 * itdb_playlist_is_audiobooks:
1598 * @pl: an #Itdb_Playlist
1599 *
1600 * Checks if @pl is an audiobook playlist
1601 *
1602 * Returns: TRUE if all tracks in @pl have a mediatype of ITDB_MEDIATYPE_AUDIOBOOK,
1603 FALSE if there are no tracks or one track is not the correct type
1604 *
1605 */
itdb_playlist_is_audiobooks(Itdb_Playlist * pl)1606 gboolean itdb_playlist_is_audiobooks (Itdb_Playlist *pl)
1607 {
1608 GList *tl;
1609 Itdb_Track *track;
1610
1611 g_return_val_if_fail (pl, FALSE);
1612 g_return_val_if_fail (pl->members, FALSE);
1613 for (tl = pl->members; tl; tl = tl->next)
1614 {
1615 track = tl->data;
1616 /* Don't check more tracks than necessary */
1617 if (!(track->mediatype & ITDB_MEDIATYPE_AUDIOBOOK))
1618 {
1619 return FALSE;
1620 }
1621 }
1622 return TRUE;
1623 }
1624
1625 /**
1626 * itdb_playlist_set_mpl:
1627 * @pl: an #Itdb_Playlist
1628 *
1629 * Sets @pl to be a master playlist
1630 *
1631 * Since: 0.2.0
1632 */
itdb_playlist_set_mpl(Itdb_Playlist * pl)1633 void itdb_playlist_set_mpl (Itdb_Playlist *pl)
1634 {
1635 g_return_if_fail (pl);
1636
1637 pl->type = ITDB_PL_TYPE_MPL;
1638 }
1639
1640 /**
1641 * itdb_playlist_set_podcasts:
1642 * @pl: an #Itdb_Playlist
1643 *
1644 * Set @pl to be a podcasts playlist
1645 *
1646 * Since: 0.2.0
1647 */
itdb_playlist_set_podcasts(Itdb_Playlist * pl)1648 void itdb_playlist_set_podcasts (Itdb_Playlist *pl)
1649 {
1650 g_return_if_fail (pl);
1651
1652 pl->podcastflag = ITDB_PL_FLAG_PODCASTS;
1653 }
1654
1655 /**
1656 * itdb_playlist_mpl:
1657 * @itdb: an #Itdb_iTunesDB
1658 *
1659 * Gets the master playlist of @itdb
1660 *
1661 * Returns: the master playlist of @itdb
1662 */
itdb_playlist_mpl(Itdb_iTunesDB * itdb)1663 Itdb_Playlist *itdb_playlist_mpl (Itdb_iTunesDB *itdb)
1664 {
1665 Itdb_Playlist *pl;
1666
1667 g_return_val_if_fail (itdb, NULL);
1668
1669 pl = g_list_nth_data (itdb->playlists, 0);
1670 g_return_val_if_fail (pl, NULL);
1671
1672 /* mpl is guaranteed to be at first position... */
1673 g_return_val_if_fail (itdb_playlist_is_mpl (pl), NULL);
1674
1675 return pl;
1676 }
1677
1678 /**
1679 * itdb_playlist_podcasts:
1680 * @itdb: an #Itdb_iTunesDB
1681 *
1682 * Gets the podcasts playlist of @itdb
1683 *
1684 * Returns: the podcasts playlist of @itdb, or NULL if there is
1685 * not one
1686 *
1687 * Since: 0.1.6
1688 */
itdb_playlist_podcasts(Itdb_iTunesDB * itdb)1689 Itdb_Playlist *itdb_playlist_podcasts (Itdb_iTunesDB *itdb)
1690 {
1691 GList *gl;
1692
1693 g_return_val_if_fail (itdb, NULL);
1694
1695 for (gl=itdb->playlists; gl; gl=gl->next)
1696 {
1697 Itdb_Playlist *pl = gl->data;
1698 g_return_val_if_fail (pl, NULL);
1699
1700 if (itdb_playlist_is_podcasts (pl)) return pl;
1701 }
1702
1703 return NULL;
1704 }
1705
1706 /**
1707 * itdb_playlist_contains_track:
1708 * @pl: an #Itdb_Playlist
1709 * @track: an #Itdb_Track
1710 *
1711 * Checks if @track is in @pl
1712 *
1713 * Returns: TRUE if @track is in @pl, FALSE otherwise
1714 */
itdb_playlist_contains_track(Itdb_Playlist * pl,Itdb_Track * tr)1715 gboolean itdb_playlist_contains_track (Itdb_Playlist *pl, Itdb_Track *tr)
1716 {
1717 g_return_val_if_fail (tr, FALSE);
1718
1719 if (pl == NULL)
1720 pl = itdb_playlist_mpl (tr->itdb);
1721
1722 g_return_val_if_fail (pl, FALSE);
1723
1724 if (g_list_find (pl->members, tr)) return TRUE;
1725 else return FALSE;
1726 }
1727
1728 /**
1729 * itdb_playlist_contain_track_number:
1730 * @tr: an #Itdb_Track
1731 *
1732 * Counts the number of playlist @track is a member of (not including
1733 * the master playlist)
1734 *
1735 * Returns: the number of playlist containing @track
1736 */
itdb_playlist_contain_track_number(Itdb_Track * tr)1737 guint32 itdb_playlist_contain_track_number (Itdb_Track *tr)
1738 {
1739 Itdb_iTunesDB *itdb;
1740 guint32 num = 0;
1741 GList *gl;
1742
1743 g_return_val_if_fail (tr, 0);
1744 itdb = tr->itdb;
1745 g_return_val_if_fail (itdb, 0);
1746
1747 /* start with 2nd playlist (skip MPL) */
1748 gl = g_list_nth (itdb->playlists, 1);
1749 while (gl)
1750 {
1751 g_return_val_if_fail (gl->data, num);
1752 if (itdb_playlist_contains_track (gl->data, tr)) ++num;
1753 gl = gl->next;
1754 }
1755 return num;
1756 }
1757
1758 /**
1759 * itdb_playlist_tracks_number:
1760 * @pl: an #Itdb_Playlist
1761 *
1762 * Counts the number of tracks in @pl
1763 *
1764 * Returns: the number of tracks in @pl
1765 */
itdb_playlist_tracks_number(Itdb_Playlist * pl)1766 guint32 itdb_playlist_tracks_number (Itdb_Playlist *pl)
1767 {
1768 g_return_val_if_fail (pl, 0);
1769
1770 return g_list_length (pl->members);
1771 }
1772