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