1 /*
2 | Copyright (C) 2002-2007 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 | This program is free software; you can redistribute it and/or modify
9 | it under the terms of the GNU General Public License as published by
10 | the Free Software Foundation; either version 2 of the License, or
11 | (at your option) any later version.
12 |
13 | This program 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
16 | GNU General Public License for more details.
17 |
18 | You should have received a copy of the GNU General Public License
19 | along with this program; 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 #ifdef HAVE_CONFIG_H
30 # include <config.h>
31 #endif
32
33 #include "display_itdb.h"
34 #include "sha1.h"
35 #include "prefs.h"
36 #include "misc.h"
37 #include "misc_track.h"
38 #include "info.h"
39 #include "charset.h"
40 #include <math.h>
41 #include <stdlib.h>
42 #include <string.h>
43
44
45 /* ------------------------------------------------------------ *\
46 | |
47 | functions for sha1 checksums |
48 | |
49 \* ------------------------------------------------------------ */
50
51 /**
52 * Register all tracks in the sha1 hash and remove duplicates (while
53 * preserving playlists)
54 */
gp_sha1_hash_tracks_itdb(iTunesDB * itdb)55 void gp_sha1_hash_tracks_itdb (iTunesDB *itdb)
56 {
57 gint ns, count;
58 GList *gl;
59
60 g_return_if_fail (itdb);
61
62 if (!prefs_get_int("sha1")) return;
63 ns = itdb_tracks_number (itdb); /* number of tracks */
64 if (ns == 0) return;
65
66 block_widgets (); /* block widgets -- this might take a while,
67 so we'll do refreshs */
68 sha1_free (itdb); /* release sha1 hash */
69 count = 0;
70 /* populate the hash table */
71 gl=itdb->tracks;
72 while (gl)
73 {
74 Track *track=gl->data;
75 Track *oldtrack = sha1_track_exists_insert (itdb, track);
76
77 /* need to get next track now because it might be a duplicate and
78 thus be removed when we call gp_duplicate_remove() */
79 gl = gl->next;
80
81 if (oldtrack)
82 {
83 gp_duplicate_remove (oldtrack, track);
84 }
85
86 ++count;
87 if (((count % 20) == 1) || (count == ns))
88 { /* update for count == 1, 21, 41 ... and for count == n */
89 gtkpod_statusbar_message (ngettext ("Hashed %d of %d track.",
90 "Hashed %d of %d tracks.", ns),
91 count, ns);
92 while (widgets_blocked && gtk_events_pending ())
93 gtk_main_iteration ();
94 }
95 }
96 gp_duplicate_remove (NULL, NULL); /* show info dialogue */
97 release_widgets (); /* release widgets again */
98 }
99
100
101 /**
102 * Call gp_hash_tracks_itdb() for each itdb.
103 *
104 */
gp_sha1_hash_tracks(void)105 void gp_sha1_hash_tracks (void)
106 {
107 GList *gl;
108 struct itdbs_head *itdbs_head;
109
110 g_return_if_fail (gtkpod_window);
111 itdbs_head = g_object_get_data (G_OBJECT (gtkpod_window),
112 "itdbs_head");
113 g_return_if_fail (itdbs_head);
114
115 block_widgets ();
116 for (gl=itdbs_head->itdbs; gl; gl=gl->next)
117 {
118 gp_sha1_hash_tracks_itdb (gl->data);
119 }
120 release_widgets ();
121 }
122
123
rm_sha1(gpointer track,gpointer user_data)124 static void rm_sha1 (gpointer track, gpointer user_data)
125 {
126 ExtraTrackData *etr;
127 g_return_if_fail (track);
128 etr = ((Track *)track)->userdata;
129 g_return_if_fail (etr);
130 C_FREE (etr->sha1_hash);
131 }
132
133 /**
134 * Call sha1_free() for each itdb and delete sha1 checksums in all tracks.
135 *
136 */
gp_sha1_free_hash(void)137 void gp_sha1_free_hash (void)
138 {
139 GList *gl;
140 struct itdbs_head *itdbs_head;
141
142 g_return_if_fail (gtkpod_window);
143 itdbs_head = g_object_get_data (G_OBJECT (gtkpod_window),
144 "itdbs_head");
145 g_return_if_fail (itdbs_head);
146
147 for (gl=itdbs_head->itdbs; gl; gl=gl->next)
148 {
149 iTunesDB *itdb = gl->data;
150 g_return_if_fail (itdb);
151 sha1_free (itdb);
152 g_list_foreach (itdb->tracks, rm_sha1, NULL);
153 }
154 }
155
156
157 /* This function removes a duplicate track "track" from memory while
158 * preserving the playlists.
159 *
160 * The sha1 hash is not modified.
161 *
162 * The playcount/recent_playcount are modified to show the cumulative
163 * playcounts for that track.
164 *
165 * The star rating is set to the average of both star ratings if both
166 * ratings are not 0, or the higher rating if one of the ratings is 0
167 * (it is assumed that a rating of "0" means that no rating has been
168 * set).
169 *
170 * The "added" timestamp is set to the older entry (unless that one
171 * is 0).
172 *
173 * The "modified" and "last played" timestamps are set to the more
174 * recent entry.
175 *
176 * You should call "gp_duplicate_remove (NULL, NULL)" to pop up the info
177 * dialogue with the list of duplicate tracks afterwards. Call with
178 * "NULL, (void *)-1" to just clean up without dialoge.
179 *
180 * If "track" does not exist in
181 * the master play list, only a message is logged (to be displayed
182 * later when called with "NULL, NULL" */
gp_duplicate_remove(Track * oldtrack,Track * track)183 void gp_duplicate_remove (Track *oldtrack, Track *track)
184 {
185 gchar *buf, *buf2;
186 static gint deltrack_nr = 0;
187 static gboolean removed = FALSE;
188 static GString *str = NULL;
189
190 /* printf ("%p, %p, '%s'\n", oldtrack, track, str?str->str:"empty");*/
191
192 if (prefs_get_int("show_duplicates") && !oldtrack && !track && str)
193 {
194 if (str->len)
195 { /* Some tracks have been deleted. Print a notice */
196 if (removed)
197 {
198 buf = g_strdup_printf (
199 ngettext ("The following duplicate track has been removed.",
200 "The following %d duplicate tracks have been removed.",
201 deltrack_nr), deltrack_nr);
202 }
203 else
204 {
205 buf = g_strdup_printf (
206 ngettext ("The following duplicate track has not been added to the master play list.",
207 "The following %d duplicate tracks have not been added to the master play list.",
208 deltrack_nr), deltrack_nr);
209 }
210 gtkpod_confirmation
211 (-1, /* gint id, */
212 FALSE, /* gboolean modal, */
213 _("Duplicate detection"),/* title */
214 buf, /* label */
215 str->str, /* scrolled text */
216 NULL, 0, NULL, /* option 1 */
217 NULL, 0, NULL, /* option 2 */
218 TRUE, /* gboolean confirm_again, */
219 "show_duplicates",
220 /* ConfHandlerCA confirm_again_handler,*/
221 CONF_NULL_HANDLER, /* ConfHandler ok_handler,*/
222 NULL, /* don't show "Apply" button */
223 NULL, /* don't show "Cancel" button */
224 NULL, /* gpointer user_data1,*/
225 NULL); /* gpointer user_data2,*/
226 g_free (buf);
227 }
228 }
229
230 if (oldtrack == NULL)
231 { /* clean up */
232 if (str) g_string_free (str, TRUE);
233 str = NULL;
234 removed = FALSE;
235 deltrack_nr = 0;
236 gtkpod_tracks_statusbar_update();
237 }
238
239 if (oldtrack && track)
240 {
241 ExtraTrackData *oldetr = oldtrack->userdata;
242 ExtraTrackData *etr = track->userdata;
243 iTunesDB *itdb = oldtrack->itdb;
244 g_return_if_fail (itdb);
245 g_return_if_fail (oldetr);
246 g_return_if_fail (etr);
247
248 if (prefs_get_int("show_duplicates"))
249 {
250 /* add info about it to str */
251 buf = get_track_info (track, TRUE);
252 buf2 = get_track_info (oldtrack, TRUE);
253 if (!str)
254 {
255 deltrack_nr = 0;
256 str = g_string_sized_new (2000); /* used to keep record
257 * of duplicate
258 * tracks */
259 }
260 g_string_append_printf (str, "'%s': identical to '%s'\n",
261 buf, buf2);
262 g_free (buf);
263 g_free (buf2);
264 }
265 /* Set playcount */
266 oldtrack->playcount += track->playcount;
267 oldtrack->recent_playcount += track->recent_playcount;
268 /* Set rating */
269 if (oldtrack->rating && track->rating)
270 oldtrack->rating =
271 floor((double)(oldtrack->rating + track->rating + ITDB_RATING_STEP) /
272 (2 * ITDB_RATING_STEP)) * ITDB_RATING_STEP;
273 else
274 oldtrack->rating = MAX (oldtrack->rating, track->rating);
275 /* Set 'modified' timestamp */
276 oldtrack->time_modified = MAX (oldtrack->time_modified,
277 track->time_modified);
278 /* Set 'played' timestamp */
279 oldtrack->time_played = MAX (oldtrack->time_played, track->time_played);
280 /* Set 'added' timestamp */
281 oldtrack->time_added = MIN (oldtrack->time_added, track->time_added);
282
283 /* Update filename if new track has filename set (should be
284 always!?) and old filename is not available or no longer
285 valid */
286 if (etr->pc_path_locale)
287 {
288 if (!oldetr->pc_path_locale ||
289 !g_file_test (oldetr->pc_path_locale, G_FILE_TEST_IS_REGULAR))
290 {
291 g_free (oldetr->pc_path_locale);
292 g_free (oldetr->pc_path_utf8);
293 oldetr->pc_path_locale = g_strdup (etr->pc_path_locale);
294 oldetr->pc_path_utf8 = g_strdup (etr->pc_path_utf8);
295 }
296 }
297 if (itdb_playlist_contains_track (itdb_playlist_mpl (itdb),
298 track))
299 { /* track is already added to memory -> replace with "oldtrack" */
300 /* check for "track" in all playlists (except for MPL) */
301 GList *gl;
302 gl = g_list_nth (itdb->playlists, 1);
303 while (gl)
304 {
305 Playlist *pl = gl->data;
306 g_return_if_fail (pl);
307 /* if "track" is in playlist pl, we remove it and add
308 the "oldtrack" instead (this way round we don't have
309 to worry about changing sha1 hash entries */
310 if (itdb_playlist_contains_track (pl, track))
311 {
312 gp_playlist_remove_track (pl, track,
313 DELETE_ACTION_PLAYLIST);
314 if (!itdb_playlist_contains_track (pl, oldtrack))
315 gp_playlist_add_track (pl, oldtrack, TRUE);
316 }
317 gl = gl->next;
318 }
319 /* remove track from MPL, i.e. from the ipod (or the local
320 * database */
321 if (itdb->usertype & GP_ITDB_TYPE_IPOD)
322 {
323 gp_playlist_remove_track (NULL, track, DELETE_ACTION_IPOD);
324 }
325 if (itdb->usertype & GP_ITDB_TYPE_LOCAL)
326 {
327 gp_playlist_remove_track (NULL, track,
328 DELETE_ACTION_DATABASE);
329 }
330 removed = TRUE;
331 }
332 ++deltrack_nr; /* count duplicate tracks */
333 data_changed (itdb);
334 }
335 }
336
337
338 /**
339 * Register all tracks in the sha1 hash and remove duplicates (while
340 * preserving playlists).
341 * Call gp_duplicate_remove (NULL, NULL); to show an info dialogue
342 */
gp_itdb_hash(iTunesDB * itdb)343 void gp_itdb_hash (iTunesDB *itdb)
344 {
345 gint ns, track_nr;
346 Track *track, *oldtrack;
347
348 g_return_if_fail (itdb);
349
350
351 if (!prefs_get_int("sha1")) return;
352
353 ns = itdb_tracks_number (itdb);
354 if (ns == 0) return;
355
356 block_widgets (); /* block widgets -- this might take a while,
357 so we'll do refreshs */
358 sha1_free (itdb); /* release sha1 hash */
359 track_nr = 0;
360 /* populate the hash table */
361 while ((track = g_list_nth_data (itdb->tracks, track_nr)))
362 {
363 oldtrack = sha1_track_exists_insert (itdb, track);
364 /* printf("%d:%d:%p:%p\n", count, track_nr, track, oldtrack); */
365 if (oldtrack)
366 {
367 gp_duplicate_remove (oldtrack, track);
368 }
369 else
370 { /* if we removed a track (above), we don't need to increment
371 the track_nr here */
372 ++track_nr;
373 }
374 }
375 release_widgets (); /* release widgets again */
376 }
377
378
379
380
381 /* ------------------------------------------------------------ *\
382 | |
383 | functions to locate tracks |
384 | |
385 \* ------------------------------------------------------------ */
386
387
388 /* Returns the track with the filename @name or NULL, if none can be
389 * found. This function also works if @filename is on the iPod. */
gp_track_by_filename(iTunesDB * itdb,gchar * filename)390 Track *gp_track_by_filename (iTunesDB *itdb, gchar *filename)
391 {
392 gchar *musicdir = NULL;
393 Track *result = NULL;
394
395 g_return_val_if_fail (itdb, NULL);
396 g_return_val_if_fail (filename, NULL);
397
398 if (itdb->usertype & GP_ITDB_TYPE_IPOD)
399 {
400 gchar *mountpoint = get_itdb_prefs_string (itdb, KEY_MOUNTPOINT);
401 g_return_val_if_fail (mountpoint, NULL);
402 musicdir = itdb_get_music_dir (mountpoint);
403 if (!musicdir)
404 {
405 /* FIXME: guess */
406 musicdir = g_build_filename (mountpoint, "iPod_Control",
407 "Music", NULL);
408 }
409 g_free (mountpoint);
410 }
411 if ((itdb->usertype & GP_ITDB_TYPE_IPOD) &&
412 (musicdir != NULL) &&
413 (strncmp (filename, musicdir, strlen (musicdir)) == 0))
414 { /* handle track on iPod (in music dir) */
415 GList *gl;
416 for (gl=itdb->tracks; gl&&!result; gl=gl->next)
417 {
418 Track *track = gl->data;
419 gchar *ipod_path;
420 g_return_val_if_fail (track, NULL);
421 ipod_path = itdb_filename_on_ipod (track);
422 if (ipod_path)
423 {
424 if (strcasecmp (ipod_path, filename) == 0)
425 {
426 result = track;
427 }
428 g_free (ipod_path);
429 }
430 }
431 }
432 else
433 { /* handle track on local filesystem */
434 GList *gl;
435 for (gl=itdb->tracks; gl&&!result; gl=gl->next)
436 {
437 Track *track = gl->data;
438 ExtraTrackData *etr;
439 g_return_val_if_fail (track, NULL);
440 etr = track->userdata;
441 g_return_val_if_fail (etr, NULL);
442 if (etr->pc_path_locale)
443 {
444 if (strcmp (etr->pc_path_locale, filename) == 0)
445 result = track;
446 }
447 }
448 }
449 g_free (musicdir);
450 return result;
451 }
452
453
454 /* Find @track in repository @itdb by the following methods and return
455 the matches:
456
457 1) DND origin data
458 2) filename matches
459 3) SHA1 match
460
461 If DND origin data is available and valid only one track is
462 returned. Otherwise all tracks matching the filename and the SHA1
463 are returned.
464
465 If DND origin data is found to be invalid it is deleted.
466
467 Return value: a GList with matching tracks. You must call
468 g_list_free() on the list when it is no longer needed.
469 */
gp_itdb_find_same_tracks(iTunesDB * itdb,Track * track)470 GList *gp_itdb_find_same_tracks (iTunesDB *itdb, Track *track)
471 {
472 ExtraTrackData *etr;
473 Track *itr;
474 GList *tracks = NULL;
475
476 g_return_val_if_fail (itdb, NULL);
477 g_return_val_if_fail (track, NULL);
478
479 etr = track->userdata;
480 g_return_val_if_fail (etr, NULL);
481
482 if (itdb->id == etr->local_itdb_id)
483 { /* we can probably find the original track from the DND data */
484 GList *gl;
485 for (gl=itdb->tracks; gl; gl=gl->next)
486 {
487 itr = gl->data;
488 g_return_val_if_fail (itr, NULL);
489 if (itr->dbid == etr->local_track_dbid)
490 { /* found track */
491 tracks = g_list_prepend (tracks, itr);
492 return tracks;
493 }
494 }
495 /* DND origin data is no longer valid */
496 etr->local_itdb_id = 0;
497 etr->local_track_dbid = 0;
498 }
499
500 /* No luck so far -- let's get filename matches */
501 tracks = gp_itdb_pc_path_hash_find_tracks (itdb, etr->pc_path_utf8);
502
503 /* And also try SHA1 match */
504 itr = sha1_sha1_exists (itdb, etr->sha1_hash);
505
506 if (itr)
507 { /* insert into tracks list if not already present */
508 if (!g_list_find (tracks, itr))
509 {
510 tracks = g_list_prepend (tracks, itr);
511 }
512 }
513
514 return tracks;
515 }
516
517
518 /* Find @track in all local repositories and return a list.
519
520 This function calls gp_itdb_find_same_tracks() for each local
521 repository and concatenates the results into one list.
522
523 Return value: a GList with matching tracks. You must call
524 g_list_free() on the list when it is no longer needed.
525 */
gp_itdb_find_same_tracks_in_local_itdbs(Track * track)526 GList *gp_itdb_find_same_tracks_in_local_itdbs (Track *track)
527 {
528 GList *gl, *tracks=NULL;
529 struct itdbs_head *ih = gp_get_itdbs_head (gtkpod_window);
530
531 g_return_val_if_fail (ih, NULL);
532 g_return_val_if_fail (track, NULL);
533
534 for (gl=ih->itdbs; gl; gl=gl->next)
535 {
536 iTunesDB *itdb = gl->data;
537 g_return_val_if_fail (itdb, tracks);
538 if (itdb->usertype & GP_ITDB_TYPE_LOCAL)
539 {
540 GList *addtracks = gp_itdb_find_same_tracks (itdb, track);
541 tracks = g_list_concat (tracks, addtracks);
542 }
543 }
544 return tracks;
545 }
546
547
548
549 /* Find @track in all repositories (local and iPod) and return a list.
550
551 This function calls gp_itdb_find_same_tracks() for each
552 repository and concatenates the results into one list.
553
554 Return value: a GList with matching tracks. You must call
555 g_list_free() on the list when it is no longer needed.
556 */
gp_itdb_find_same_tracks_in_itdbs(Track * track)557 GList *gp_itdb_find_same_tracks_in_itdbs (Track *track)
558 {
559 GList *gl, *tracks=NULL;
560 struct itdbs_head *ih = gp_get_itdbs_head (gtkpod_window);
561
562 g_return_val_if_fail (ih, NULL);
563 g_return_val_if_fail (track, NULL);
564
565 for (gl=ih->itdbs; gl; gl=gl->next)
566 {
567 GList *addtracks;
568 iTunesDB *itdb = gl->data;
569 g_return_val_if_fail (itdb, tracks);
570 addtracks = gp_itdb_find_same_tracks (itdb, track);
571 tracks = g_list_concat (tracks, addtracks);
572 }
573 return tracks;
574 }
575
576
577
578 /* ------------------------------------------------------------ *\
579 | |
580 | functions for local path hashtable |
581 | |
582 \* ------------------------------------------------------------ */
583
584 /* set up hash table for local filenames */
gp_itdb_pc_path_hash_init(ExtraiTunesDBData * eitdb)585 void gp_itdb_pc_path_hash_init (ExtraiTunesDBData *eitdb)
586 {
587 g_return_if_fail (eitdb);
588
589 if (!eitdb->pc_path_hash)
590 {
591 eitdb->pc_path_hash = g_hash_table_new_full (
592 g_str_hash, g_str_equal,
593 g_free, NULL);
594 }
595 }
596
597
598 /* function for destroying hash value, used when destroying the hash
599 * table in gp_itdb_local_path_hash_destroy() below. */
pc_path_hash_free_value(gpointer key,gpointer value,gpointer userdata)600 static void pc_path_hash_free_value (gpointer key,
601 gpointer value,
602 gpointer userdata)
603 {
604 g_list_free (value);
605 }
606
607
608 /* free all memory associated with the local_path_hash */
gp_itdb_pc_path_hash_destroy(ExtraiTunesDBData * eitdb)609 void gp_itdb_pc_path_hash_destroy (ExtraiTunesDBData *eitdb)
610 {
611 g_return_if_fail (eitdb);
612
613 if (eitdb->pc_path_hash)
614 {
615 g_hash_table_foreach (eitdb->pc_path_hash,
616 pc_path_hash_free_value,
617 NULL);
618 g_hash_table_destroy (eitdb->pc_path_hash);
619 eitdb->pc_path_hash = NULL;
620 }
621 }
622
623
624 /* Add track to filehash. This function must only be called once for
625 * each track. */
gp_itdb_pc_path_hash_add_track(Track * track)626 void gp_itdb_pc_path_hash_add_track (Track *track)
627 {
628 iTunesDB *itdb;
629 ExtraTrackData *etr;
630 ExtraiTunesDBData *eitdb;
631
632 g_return_if_fail (track);
633 etr = track->userdata;
634 g_return_if_fail (etr);
635
636 itdb = track->itdb;
637 g_return_if_fail (itdb);
638
639 eitdb = itdb->userdata;
640 g_return_if_fail (eitdb);
641 g_return_if_fail (eitdb->pc_path_hash);
642
643 g_return_if_fail (track);
644 etr = track->userdata;
645 g_return_if_fail (etr);
646
647 /* This is only to detect programming errors */
648 g_return_if_fail (!etr->pc_path_hashed);
649
650 if (etr->pc_path_utf8 && *etr->pc_path_utf8)
651 { /* add to hash table */
652 GList *tracks;
653 tracks = g_hash_table_lookup (eitdb->pc_path_hash, etr->pc_path_utf8);
654 tracks = g_list_prepend (tracks, track);
655 g_hash_table_replace (eitdb->pc_path_hash,
656 g_strdup (etr->pc_path_utf8),
657 tracks);
658 /* This is only to detect programming errors */
659 etr->pc_path_hashed = TRUE;
660 }
661 }
662
663 /* used in the next two functions */
664 struct pc_path_hash_find_track_data
665 {
666 Track *track;
667 gchar *key;
668 };
669
670
671 /* Used in the next function. Return TRUE and the current key if
672 td->track is contained in the list for key */
pc_path_hash_find_track(gpointer key,gpointer value,gpointer user_data)673 static gboolean pc_path_hash_find_track (gpointer key,
674 gpointer value,
675 gpointer user_data)
676 {
677 GList *tracks = value;
678 struct pc_path_hash_find_track_data *td = user_data;
679
680 if (g_list_find (tracks, td->track))
681 {
682 td->key = g_strdup (key);
683 return TRUE;
684 }
685 return FALSE;
686 }
687
688
689
690 /* remove track from filehash */
gp_itdb_pc_path_hash_remove_track(Track * track)691 void gp_itdb_pc_path_hash_remove_track (Track *track)
692 {
693 struct pc_path_hash_find_track_data td;
694 ExtraTrackData *etr;
695 iTunesDB *itdb;
696 ExtraiTunesDBData *eitdb;
697 GList *tracks;
698
699 g_return_if_fail (track);
700 etr = track->userdata;
701 g_return_if_fail (etr);
702
703 itdb = track->itdb;
704 g_return_if_fail (itdb);
705
706 eitdb = itdb->userdata;
707 g_return_if_fail (eitdb);
708 g_return_if_fail (eitdb->pc_path_hash);
709
710 if (!etr->pc_path_hashed) return;
711
712 if (etr->pc_path_utf8 && *etr->pc_path_utf8)
713 { /* try lookup with filename */
714 GList *tracks;
715 tracks = g_hash_table_lookup (eitdb->pc_path_hash, etr->pc_path_utf8);
716 if (tracks)
717 { /* filename exists */
718 GList *link = g_list_find (tracks, track);
719 if (link)
720 { /* track found */
721 tracks = g_list_remove_link (tracks, link);
722 if (tracks)
723 { /* still tracks left under this filename */
724 g_hash_table_replace (eitdb->pc_path_hash,
725 g_strdup (etr->pc_path_utf8),
726 tracks);
727 }
728 else
729 { /* no more tracks left under this filename */
730 g_hash_table_remove (eitdb->pc_path_hash,
731 etr->pc_path_utf8);
732 }
733 etr->pc_path_hashed = FALSE;
734 return;
735 }
736 }
737 }
738
739 /* We didn't find the track by filename, or now filename is
740 * available any more) -> search through the list */
741 td.track = track;
742 td.key = NULL;
743 tracks = g_hash_table_find (eitdb->pc_path_hash,
744 pc_path_hash_find_track,
745 &td);
746 if (tracks)
747 {
748 tracks = g_list_remove (tracks, track);
749 if (tracks)
750 { /* still tracks left under this filename */
751 g_hash_table_replace (eitdb->pc_path_hash,
752 g_strdup (td.key),
753 tracks);
754 }
755 else
756 { /* no more tracks left under this filename */
757 g_hash_table_remove (eitdb->pc_path_hash,
758 td.key);
759 }
760 etr->pc_path_hashed = FALSE;
761 g_free (td.key);
762 }
763 }
764
765
766 /* Return all tracks with @filename (@filename in UTF8).
767 You must g_list_free the returned list when it is not longer needed. */
gp_itdb_pc_path_hash_find_tracks(iTunesDB * itdb,const gchar * filename)768 GList *gp_itdb_pc_path_hash_find_tracks (iTunesDB *itdb, const gchar *filename)
769 {
770 ExtraiTunesDBData *eitdb;
771 GList *tracks = NULL;
772
773 g_return_val_if_fail (itdb, NULL);
774 eitdb = itdb->userdata;
775 g_return_val_if_fail (eitdb, NULL);
776 g_return_val_if_fail (eitdb->pc_path_hash, NULL);
777
778 if (filename && *filename)
779 tracks = g_hash_table_lookup (eitdb->pc_path_hash, filename);
780
781 return g_list_copy (tracks);
782 }
783
784
785
786 /* ------------------------------------------------------------ *\
787 | |
788 | functions to retrieve information from tracks |
789 | |
790 \* ------------------------------------------------------------ */
791
792 /* return the address of the UTF8 field @t_item. @t_item is one of
793 * (the applicable) T_* defined in track.h */
track_get_item_pointer(Track * track,T_item t_item)794 gchar **track_get_item_pointer (Track *track, T_item t_item)
795 {
796 gchar **result = NULL;
797 ExtraTrackData *etr;
798
799 g_return_val_if_fail (track, NULL);
800 etr = track->userdata;
801 g_return_val_if_fail (etr, NULL);
802
803 switch (t_item)
804 {
805 case T_ALBUM:
806 result = &track->album;
807 break;
808 case T_ARTIST:
809 result = &track->artist;
810 break;
811 case T_TITLE:
812 result = &track->title;
813 break;
814 case T_GENRE:
815 result = &track->genre;
816 break;
817 case T_COMMENT:
818 result = &track->comment;
819 break;
820 case T_COMPOSER:
821 result = &track->composer;
822 break;
823 case T_FILETYPE:
824 result = &track->filetype;
825 break;
826 case T_IPOD_PATH:
827 result = &track->ipod_path;
828 break;
829 case T_PC_PATH:
830 result = &etr->pc_path_utf8;
831 break;
832 case T_YEAR:
833 result = &etr->year_str;
834 break;
835 case T_GROUPING:
836 result = &track->grouping;
837 break;
838 case T_CATEGORY:
839 result = &track->category;
840 break;
841 case T_DESCRIPTION:
842 result = &track->description;
843 break;
844 case T_PODCASTURL:
845 result = &track->podcasturl;
846 break;
847 case T_PODCASTRSS:
848 result = &track->podcastrss;
849 break;
850 case T_SUBTITLE:
851 result = &track->subtitle;
852 break;
853 case T_THUMB_PATH:
854 result = &etr->thumb_path_utf8;
855 break;
856 case T_TV_SHOW:
857 result = &track->tvshow;
858 break;
859 case T_TV_EPISODE:
860 result = &track->tvepisode;
861 break;
862 case T_TV_NETWORK:
863 result = &track->tvnetwork;
864 break;
865 case T_ALBUMARTIST:
866 result = &track->albumartist;
867 break;
868 case T_SORT_ARTIST:
869 result = &track->sort_artist;
870 break;
871 case T_SORT_TITLE:
872 result = &track->sort_title;
873 break;
874 case T_SORT_ALBUM:
875 result = &track->sort_album;
876 break;
877 case T_SORT_ALBUMARTIST:
878 result = &track->sort_albumartist;
879 break;
880 case T_SORT_COMPOSER:
881 result = &track->sort_composer;
882 break;
883 case T_SORT_TVSHOW:
884 result = &track->sort_tvshow;
885 break;
886 case T_LYRICS:
887 result = &etr->lyrics;
888 break;
889 case T_ALL:
890 case T_IPOD_ID:
891 case T_TRACK_NR:
892 case T_TRANSFERRED:
893 case T_SIZE:
894 case T_TRACKLEN:
895 case T_STARTTIME:
896 case T_STOPTIME:
897 case T_BITRATE:
898 case T_SAMPLERATE:
899 case T_BPM:
900 case T_PLAYCOUNT:
901 case T_RATING:
902 case T_TIME_ADDED:
903 case T_TIME_PLAYED:
904 case T_TIME_MODIFIED:
905 case T_TIME_RELEASED:
906 case T_VOLUME:
907 case T_SOUNDCHECK:
908 case T_CD_NR:
909 case T_COMPILATION:
910 case T_REMEMBER_PLAYBACK_POSITION:
911 case T_SKIP_WHEN_SHUFFLING:
912 case T_CHECKED:
913 case T_MEDIA_TYPE:
914 case T_SEASON_NR:
915 case T_EPISODE_NR:
916 case T_GAPLESS_TRACK_FLAG:
917 case T_ITEM_NUM:
918 g_return_val_if_reached (NULL);
919 }
920 return result;
921 }
922
923
924
925
926
927
928
929 /* return the UTF8 item @t_item. @t_item is one of
930 (the applicable) T_* defined in track.h */
track_get_item(Track * track,T_item t_item)931 const gchar *track_get_item (Track *track, T_item t_item)
932 {
933 gchar **ptr;
934
935 g_return_val_if_fail (track, NULL);
936
937 ptr = track_get_item_pointer (track, t_item);
938
939 if (ptr) return *ptr;
940 else return NULL;
941 }
942
943
944 /* Copy item @item from @frtrack to @totrack.
945 Return value:
946 TRUE: @totrack was changed
947 FALSE: @totrack is unchanged
948 */
track_copy_item(Track * frtrack,Track * totrack,T_item item)949 gboolean track_copy_item (Track *frtrack, Track *totrack, T_item item)
950 {
951 gboolean changed = FALSE;
952 const gchar *fritem;
953 gchar **toitem_ptr;
954 ExtraTrackData *efrtr, *etotr;
955
956 g_return_val_if_fail (frtrack, FALSE);
957 g_return_val_if_fail (totrack, FALSE);
958 g_return_val_if_fail ((item > 0) && (item < T_ITEM_NUM), FALSE);
959
960 efrtr = frtrack->userdata;
961 etotr = totrack->userdata;
962 g_return_val_if_fail (efrtr, FALSE);
963 g_return_val_if_fail (etotr, FALSE);
964
965 if (frtrack == totrack) return FALSE;
966
967 switch (item)
968 {
969 case T_ALBUM:
970 case T_ARTIST:
971 case T_TITLE:
972 case T_GENRE:
973 case T_COMMENT:
974 case T_COMPOSER:
975 case T_FILETYPE:
976 case T_IPOD_PATH:
977 case T_PC_PATH:
978 case T_YEAR:
979 case T_GROUPING:
980 case T_CATEGORY:
981 case T_DESCRIPTION:
982 case T_PODCASTURL:
983 case T_PODCASTRSS:
984 case T_SUBTITLE:
985 case T_THUMB_PATH:
986 case T_TV_SHOW:
987 case T_TV_EPISODE:
988 case T_TV_NETWORK:
989 case T_ALBUMARTIST:
990 case T_SORT_ARTIST:
991 case T_SORT_TITLE:
992 case T_SORT_ALBUM:
993 case T_SORT_ALBUMARTIST:
994 case T_SORT_COMPOSER:
995 case T_SORT_TVSHOW:
996 case T_LYRICS:
997 fritem = track_get_item (frtrack, item);
998 toitem_ptr = track_get_item_pointer (totrack, item);
999 g_return_val_if_fail (fritem, FALSE);
1000 g_return_val_if_fail (toitem_ptr, FALSE);
1001 if ((*toitem_ptr == NULL) || (strcmp (fritem, *toitem_ptr) != 0))
1002 {
1003 g_free (*toitem_ptr);
1004 *toitem_ptr = g_strdup (fritem);
1005 changed = TRUE;
1006 }
1007 if (item == T_YEAR)
1008 {
1009 if (totrack->year != frtrack->year)
1010 {
1011 totrack->year = frtrack->year;
1012 changed = TRUE;
1013 }
1014 }
1015 else if ((changed) && (item == T_LYRICS))
1016 {
1017 write_lyrics_to_file (totrack);
1018 }
1019 /* handle items that have two entries */
1020 if (item == T_PC_PATH)
1021 {
1022 if ((etotr->pc_path_locale == NULL) ||
1023 (strcmp (efrtr->pc_path_locale, etotr->pc_path_locale) != 0))
1024 {
1025 g_free (etotr->pc_path_locale);
1026 etotr->pc_path_locale = g_strdup (efrtr->pc_path_locale);
1027 changed = TRUE;
1028 }
1029 }
1030 if (item == T_THUMB_PATH)
1031 {
1032 if ((etotr->thumb_path_locale == NULL) ||
1033 (strcmp (efrtr->thumb_path_locale, etotr->thumb_path_locale) != 0))
1034 {
1035 g_free (etotr->thumb_path_locale);
1036 etotr->thumb_path_locale = g_strdup (efrtr->thumb_path_locale);
1037 changed = TRUE;
1038 }
1039 }
1040 break;
1041 case T_IPOD_ID:
1042 if (frtrack->id != totrack->id)
1043 {
1044 totrack->id = frtrack->id;
1045 changed = TRUE;
1046 }
1047 break;
1048 case T_TRACK_NR:
1049 if (frtrack->track_nr != totrack->track_nr)
1050 {
1051 totrack->track_nr = frtrack->track_nr;
1052 changed = TRUE;
1053 }
1054 if (frtrack->tracks != totrack->tracks)
1055 {
1056 totrack->tracks = frtrack->tracks;
1057 changed = TRUE;
1058 }
1059 break;
1060 case T_TRANSFERRED:
1061 if (frtrack->transferred != totrack->transferred)
1062 {
1063 totrack->transferred = frtrack->transferred;
1064 changed = TRUE;
1065 }
1066 break;
1067 case T_SIZE:
1068 if (frtrack->size != totrack->size)
1069 {
1070 totrack->size = frtrack->size;
1071 changed = TRUE;
1072 }
1073 break;
1074 case T_TRACKLEN:
1075 if (frtrack->tracklen != totrack->tracklen)
1076 {
1077 totrack->tracklen = frtrack->tracklen;
1078 changed = TRUE;
1079 }
1080 break;
1081 case T_STARTTIME:
1082 if (frtrack->starttime != totrack->starttime)
1083 {
1084 totrack->starttime = frtrack->starttime;
1085 changed = TRUE;
1086 }
1087 break;
1088 case T_STOPTIME:
1089 if (frtrack->stoptime != totrack->stoptime)
1090 {
1091 totrack->stoptime = frtrack->stoptime;
1092 changed = TRUE;
1093 }
1094 break;
1095 case T_BITRATE:
1096 if (frtrack->bitrate != totrack->bitrate)
1097 {
1098 totrack->bitrate = frtrack->bitrate;
1099 changed = TRUE;
1100 }
1101 break;
1102 case T_SAMPLERATE:
1103 if (frtrack->samplerate != totrack->samplerate)
1104 {
1105 totrack->samplerate = frtrack->samplerate;
1106 changed = TRUE;
1107 }
1108 break;
1109 case T_BPM:
1110 if (frtrack->BPM != totrack->BPM)
1111 {
1112 totrack->BPM = frtrack->BPM;
1113 changed = TRUE;
1114 }
1115 break;
1116 case T_PLAYCOUNT:
1117 if (frtrack->playcount != totrack->playcount)
1118 {
1119 totrack->playcount = frtrack->playcount;
1120 changed = TRUE;
1121 }
1122 break;
1123 case T_RATING:
1124 if (frtrack->rating != totrack->rating)
1125 {
1126 totrack->rating = frtrack->rating;
1127 changed = TRUE;
1128 }
1129 break;
1130 case T_TIME_ADDED:
1131 case T_TIME_PLAYED:
1132 case T_TIME_MODIFIED:
1133 case T_TIME_RELEASED:
1134 if (time_get_time (frtrack, item) !=
1135 time_get_time (totrack, item))
1136 {
1137 time_set_time (totrack, time_get_time (frtrack, item), item);
1138 changed = TRUE;
1139 }
1140 break;
1141 case T_VOLUME:
1142 if (frtrack->volume != totrack->volume)
1143 {
1144 totrack->volume = frtrack->volume;
1145 changed = TRUE;
1146 }
1147 break;
1148 case T_SOUNDCHECK:
1149 if (frtrack->soundcheck != totrack->soundcheck)
1150 {
1151 totrack->soundcheck = frtrack->soundcheck;
1152 changed = TRUE;
1153 }
1154 break;
1155 case T_CD_NR:
1156 if (frtrack->cd_nr != totrack->cd_nr)
1157 {
1158 totrack->cd_nr = frtrack->cd_nr;
1159 changed = TRUE;
1160 }
1161 if (frtrack->cds != totrack->cds)
1162 {
1163 totrack->cds = frtrack->cds;
1164 changed = TRUE;
1165 }
1166 break;
1167 case T_COMPILATION:
1168 if (frtrack->compilation != totrack->compilation)
1169 {
1170 totrack->compilation = frtrack->compilation;
1171 changed = TRUE;
1172 }
1173 break;
1174 case T_REMEMBER_PLAYBACK_POSITION:
1175 if (frtrack->remember_playback_position != totrack->remember_playback_position)
1176 {
1177 totrack->remember_playback_position = frtrack->remember_playback_position;
1178 changed = TRUE;
1179 }
1180 break;
1181 case T_SKIP_WHEN_SHUFFLING:
1182 if (frtrack->skip_when_shuffling != totrack->skip_when_shuffling)
1183 {
1184 totrack->skip_when_shuffling = frtrack->skip_when_shuffling;
1185 changed = TRUE;
1186 }
1187 break;
1188 case T_CHECKED:
1189 if (frtrack->checked != totrack->checked)
1190 {
1191 totrack->checked = frtrack->checked;
1192 changed = TRUE;
1193 }
1194 break;
1195 case T_MEDIA_TYPE:
1196 if (frtrack->mediatype != totrack->mediatype)
1197 {
1198 totrack->mediatype = frtrack->mediatype;
1199 changed = TRUE;
1200 }
1201 break;
1202 case T_SEASON_NR:
1203 if (frtrack->season_nr != totrack->season_nr)
1204 {
1205 totrack->season_nr = frtrack->season_nr;
1206 changed = TRUE;
1207 }
1208 break;
1209 case T_EPISODE_NR:
1210 if (frtrack->episode_nr != totrack->episode_nr)
1211 {
1212 totrack->episode_nr = frtrack->episode_nr;
1213 changed = TRUE;
1214 }
1215 break;
1216 case T_GAPLESS_TRACK_FLAG:
1217 if (frtrack->gapless_track_flag != totrack->gapless_track_flag)
1218 {
1219 totrack->gapless_track_flag = frtrack->gapless_track_flag;
1220 changed = TRUE;
1221 }
1222 break;
1223 case T_ITEM_NUM:
1224 case T_ALL:
1225 g_return_val_if_reached (FALSE);
1226
1227 }
1228 return changed;
1229 }
1230
1231
1232 /* return a pointer to the specified timestamp. @t_item is one of (the
1233 applicable) T_* defined in track.h. If the parameters are illegal,
1234 "0" is returned. */
track_get_timestamp_ptr(Track * track,T_item t_item)1235 time_t *track_get_timestamp_ptr (Track *track, T_item t_item)
1236 {
1237 g_return_val_if_fail (track, NULL);
1238
1239 switch (t_item)
1240 {
1241 case T_TIME_PLAYED:
1242 return &track->time_played;
1243 case T_TIME_MODIFIED:
1244 return &track->time_modified;
1245 case T_TIME_RELEASED:
1246 return &track->time_released;
1247 case T_TIME_ADDED:
1248 return &track->time_added;
1249 default:
1250 g_return_val_if_reached (0);
1251 }
1252 }
1253
1254
1255 /* return the specified timestamp. @t_item is one of
1256 (the * applicable) T_* defined in track.h. If the parameters are
1257 illegal, "0" is returned. */
track_get_timestamp(Track * track,T_item t_item)1258 time_t track_get_timestamp (Track *track, T_item t_item)
1259 {
1260 time_t *ptr;
1261 g_return_val_if_fail (track, 0);
1262
1263 ptr = track_get_timestamp_ptr (track, t_item);
1264 if (ptr) return *ptr;
1265 else return 0;
1266 }
1267
1268
1269 /* unified format for TRACKLEN, STARTTIME, STOPTIME */
track_get_length_string(gint32 length)1270 static gchar *track_get_length_string (gint32 length)
1271 {
1272 return g_strdup_printf ("%d:%06.3f",
1273 length/60000,
1274 ((float)(length%60000)) / 1000);
1275 }
1276
1277
1278 /* Return text for display. g_free() after use. */
track_get_text(Track * track,T_item item)1279 gchar *track_get_text (Track *track, T_item item)
1280 {
1281 gchar *text = NULL;
1282 ExtraTrackData *etr;
1283 iTunesDB *itdb;
1284
1285 g_return_val_if_fail ((item > 0) && (item < T_ITEM_NUM), NULL);
1286 g_return_val_if_fail (track, NULL);
1287 etr = track->userdata;
1288 g_return_val_if_fail (etr, NULL);
1289 itdb = track->itdb;
1290 g_return_val_if_fail (itdb, NULL);
1291
1292 switch (item)
1293 {
1294 case T_TITLE:
1295 text = g_strdup (track->title);
1296 break;
1297 case T_ARTIST:
1298 text = g_strdup (track->artist);
1299 break;
1300 case T_ALBUM:
1301 text = g_strdup (track->album);
1302 break;
1303 case T_GENRE:
1304 text = g_strdup (track->genre);
1305 break;
1306 case T_COMPOSER:
1307 text = g_strdup (track->composer);
1308 break;
1309 case T_COMMENT:
1310 text = g_strdup (track->comment);
1311 break;
1312 case T_FILETYPE:
1313 text = g_strdup (track->filetype);
1314 break;
1315 case T_GROUPING:
1316 text = g_strdup (track->grouping);
1317 break;
1318 case T_CATEGORY:
1319 text = g_strdup (track->category);
1320 break;
1321 case T_DESCRIPTION:
1322 text = g_strdup (track->description);
1323 break;
1324 case T_PODCASTURL:
1325 text = g_strdup (track->podcasturl);
1326 break;
1327 case T_PODCASTRSS:
1328 text = g_strdup (track->podcastrss);
1329 break;
1330 case T_SUBTITLE:
1331 text = g_strdup (track->subtitle);
1332 break;
1333 case T_TV_SHOW:
1334 text = g_strdup (track->tvshow);
1335 break;
1336 case T_TV_EPISODE:
1337 text = g_strdup (track->tvepisode);
1338 break;
1339 case T_TV_NETWORK:
1340 text = g_strdup (track->tvnetwork);
1341 break;
1342 case T_ALBUMARTIST:
1343 text = g_strdup (track->albumartist);
1344 break;
1345 case T_SORT_ARTIST:
1346 text = g_strdup (track->sort_artist);
1347 break;
1348 case T_SORT_TITLE:
1349 text = g_strdup (track->sort_title);
1350 break;
1351 case T_SORT_ALBUM:
1352 text = g_strdup (track->sort_album);
1353 break;
1354 case T_SORT_ALBUMARTIST:
1355 text = g_strdup (track->sort_albumartist);
1356 break;
1357 case T_SORT_COMPOSER:
1358 text = g_strdup (track->sort_composer);
1359 break;
1360 case T_SORT_TVSHOW:
1361 text = g_strdup (track->sort_tvshow);
1362 break;
1363 case T_TRACK_NR:
1364 if (track->tracks == 0)
1365 text = g_strdup_printf ("%d", track->track_nr);
1366 else
1367 text = g_strdup_printf ("%d/%d",
1368 track->track_nr, track->tracks);
1369 break;
1370 case T_CD_NR:
1371 if (track->cds == 0)
1372 text = g_strdup_printf ("%d", track->cd_nr);
1373 else
1374 text = g_strdup_printf ("%d/%d", track->cd_nr, track->cds);
1375 break;
1376 case T_IPOD_ID:
1377 if (track->id != -1)
1378 text = g_strdup_printf ("%d", track->id);
1379 else
1380 text = g_strdup ("--");
1381 break;
1382 case T_PC_PATH:
1383 text = g_strdup (etr->pc_path_utf8);
1384 break;
1385 case T_IPOD_PATH:
1386 if (itdb->usertype & GP_ITDB_TYPE_IPOD)
1387 {
1388 text = g_strdup (track->ipod_path);
1389 }
1390 if (itdb->usertype & GP_ITDB_TYPE_LOCAL)
1391 {
1392 text = g_strdup (_("Local Database"));
1393 }
1394 break;
1395 case T_THUMB_PATH:
1396 text = g_strdup (etr->thumb_path_utf8);
1397 if (!text || (strlen (text) == 0))
1398 { /* no path set */
1399 g_free (text);
1400 text = NULL;
1401 if (itdb_track_has_thumbnails (track))
1402 { /* artwork is set */
1403 text = g_strdup (_("Embedded or filename was lost"));
1404 }
1405 else
1406 {
1407 text = g_strdup (_("Artwork not set"));
1408 }
1409 }
1410 break;
1411 case T_SIZE:
1412 text = g_strdup_printf ("%d", track->size);
1413 break;
1414 case T_TRACKLEN:
1415 text = track_get_length_string (track->tracklen);
1416 break;
1417 case T_STARTTIME:
1418 text = track_get_length_string (track->starttime);
1419 break;
1420 case T_STOPTIME:
1421 if (track->stoptime == 0)
1422 text = track_get_length_string (track->tracklen);
1423 else
1424 text = track_get_length_string (track->stoptime);
1425 break;
1426 case T_BITRATE:
1427 text = g_strdup_printf ("%dk", track->bitrate);
1428 break;
1429 case T_SAMPLERATE:
1430 text = g_strdup_printf ("%d", track->samplerate);
1431 break;
1432 case T_BPM:
1433 text = g_strdup_printf ("%d", track->BPM);
1434 break;
1435 case T_PLAYCOUNT:
1436 text = g_strdup_printf ("%d", track->playcount);
1437 break;
1438 case T_YEAR:
1439 text = g_strdup_printf ("%d", track->year);
1440 break;
1441 case T_RATING:
1442 text = g_strdup_printf ("%d", track->rating/ITDB_RATING_STEP);
1443 break;
1444 case T_TIME_PLAYED:
1445 case T_TIME_MODIFIED:
1446 case T_TIME_ADDED:
1447 case T_TIME_RELEASED:
1448 text = time_field_to_string (track, item);
1449 break;
1450 case T_VOLUME:
1451 text = g_strdup_printf ("%d", track->volume);
1452 break;
1453 case T_SOUNDCHECK:
1454 text = g_strdup_printf ("%0.2f", soundcheck_to_replaygain (track->soundcheck));
1455 break;
1456 case T_SEASON_NR:
1457 text = g_strdup_printf ("%d", track->season_nr);
1458 break;
1459 case T_EPISODE_NR:
1460 text = g_strdup_printf ("%d", track->episode_nr);
1461 break;
1462 case T_MEDIA_TYPE:
1463 text = g_strdup_printf ("%#.8x", track->mediatype);
1464 break;
1465 case T_TRANSFERRED:
1466 case T_COMPILATION:
1467 case T_REMEMBER_PLAYBACK_POSITION:
1468 case T_SKIP_WHEN_SHUFFLING:
1469 case T_ALL:
1470 case T_CHECKED:
1471 case T_ITEM_NUM:
1472 case T_GAPLESS_TRACK_FLAG:
1473 break;
1474 case T_LYRICS:
1475 read_lyrics_from_file (track, &text);
1476 break;
1477 }
1478 return text;
1479 }
1480
1481
1482 /* unified scanner for TRACKLEN, STARTTIME, STOPTIME */
track_scan_length(const gchar * new_text)1483 static gint32 track_scan_length (const gchar *new_text)
1484 {
1485 gint32 nr;
1486 const gchar *str;
1487
1488 g_return_val_if_fail (new_text, 0);
1489
1490 str = strrchr (new_text, ':');
1491 if (str)
1492 { /* MM:SS */
1493 /* A simple cast to gint32 can sometimes produce a number
1494 that's "1" too small (14.9999999999 -> 14 instead of 15) ->
1495 add 0.1 */
1496 nr = 1000 * (((gdouble)(60 * atoi (new_text))) + atof (str+1)) + 0.1;
1497 }
1498 else
1499 { /* SS */
1500 nr = 1000 * atof (new_text) + 0.1;
1501 }
1502
1503 return nr;
1504 }
1505
1506
1507 /* Set track data according to @new_text
1508
1509 Return value: TRUE, if the track data was modified, FALSE otherwise
1510 */
track_set_text(Track * track,const gchar * new_text,T_item item)1511 gboolean track_set_text (Track *track, const gchar *new_text, T_item item)
1512 {
1513 gboolean changed = FALSE;
1514 gchar **itemp_utf8;
1515 const gchar *str;
1516 ExtraTrackData *etr;
1517 gint32 nr;
1518 time_t t;
1519
1520 g_return_val_if_fail (track, FALSE);
1521 g_return_val_if_fail (new_text, FALSE);
1522
1523 etr = track->userdata;
1524 g_return_val_if_fail (etr, FALSE);
1525
1526
1527 switch(item)
1528 {
1529 case T_TITLE:
1530 case T_ALBUM:
1531 case T_ARTIST:
1532 case T_GENRE:
1533 case T_COMPOSER:
1534 case T_COMMENT:
1535 case T_FILETYPE:
1536 case T_GROUPING:
1537 case T_CATEGORY:
1538 case T_DESCRIPTION:
1539 case T_PODCASTURL:
1540 case T_PODCASTRSS:
1541 case T_SUBTITLE:
1542 case T_TV_SHOW:
1543 case T_TV_EPISODE:
1544 case T_TV_NETWORK:
1545 case T_ALBUMARTIST:
1546 case T_SORT_ARTIST:
1547 case T_SORT_TITLE:
1548 case T_SORT_ALBUM:
1549 case T_SORT_ALBUMARTIST:
1550 case T_SORT_COMPOSER:
1551 case T_SORT_TVSHOW:
1552 itemp_utf8 = track_get_item_pointer (track, item);
1553 if (g_utf8_collate (*itemp_utf8, new_text) != 0)
1554 {
1555 g_free (*itemp_utf8);
1556 *itemp_utf8 = g_strdup (new_text);
1557 changed = TRUE;
1558 }
1559 break;
1560 case T_LYRICS:
1561 if ((etr->lyrics == NULL) ||
1562 (strcmp(etr->lyrics,new_text) != 0))
1563 {
1564 g_free(etr->lyrics);
1565 etr->lyrics=g_strdup(new_text);
1566 changed = TRUE;
1567 }
1568 break;
1569 case T_TRACK_NR:
1570 nr = atoi (new_text);
1571 if ((nr >= 0) && (nr != track->track_nr))
1572 {
1573 track->track_nr = nr;
1574 changed = TRUE;
1575 }
1576 str = strrchr (new_text, '/');
1577 if (str)
1578 {
1579 nr = atoi (str+1);
1580 if ((nr >= 0) && (nr != track->tracks))
1581 {
1582 track->tracks = nr;
1583 changed = TRUE;
1584 }
1585 }
1586 break;
1587 case T_CD_NR:
1588 nr = atoi (new_text);
1589 if ((nr >= 0) && (nr != track->cd_nr))
1590 {
1591 track->cd_nr = nr;
1592 changed = TRUE;
1593 }
1594 str = strrchr (new_text, '/');
1595 if (str)
1596 {
1597 nr = atoi (str+1);
1598 if ((nr >= 0) && (nr != track->cds))
1599 {
1600 track->cds = nr;
1601 changed = TRUE;
1602 }
1603 }
1604 break;
1605 case T_YEAR:
1606 nr = atoi (new_text);
1607 if ((nr >= 0) && (nr != track->year))
1608 {
1609 g_free (etr->year_str);
1610 etr->year_str = g_strdup_printf ("%d", nr);
1611 track->year = nr;
1612 changed = TRUE;
1613 }
1614 break;
1615 case T_PLAYCOUNT:
1616 nr = atoi (new_text);
1617 if ((nr >= 0) && (nr != track->playcount))
1618 {
1619 track->playcount = nr;
1620 changed = TRUE;
1621 }
1622 break;
1623 case T_RATING:
1624 nr = atoi (new_text);
1625 if ((nr >= 0) && (nr <= 5) && (nr != track->rating))
1626 {
1627 track->rating = nr*ITDB_RATING_STEP;
1628 changed = TRUE;
1629 }
1630 break;
1631 case T_TIME_ADDED:
1632 case T_TIME_PLAYED:
1633 case T_TIME_MODIFIED:
1634 case T_TIME_RELEASED:
1635 t = time_string_to_time (new_text);
1636 if ((t != -1) && (t != time_get_time (track, item)))
1637 {
1638 time_set_time (track, t, item);
1639 changed = TRUE;
1640 }
1641 break;
1642 case T_VOLUME:
1643 nr = atoi (new_text);
1644 if (nr != track->volume)
1645 {
1646 track->volume = nr;
1647 changed = TRUE;
1648 }
1649 break;
1650 case T_SOUNDCHECK:
1651 nr = replaygain_to_soundcheck (atof (new_text));
1652 /* printf("%d : %f\n", nr, atof (new_text)); */
1653 if (nr != track->soundcheck)
1654 {
1655 track->soundcheck = nr;
1656 changed = TRUE;
1657 }
1658 break;
1659 case T_SIZE:
1660 nr = atoi (new_text);
1661 if (nr != track->size)
1662 {
1663 track->size = nr;
1664 changed = TRUE;
1665 }
1666 break;
1667 case T_BITRATE:
1668 nr = atoi (new_text);
1669 if (nr != track->bitrate)
1670 {
1671 track->bitrate = nr;
1672 changed = TRUE;
1673 }
1674 break;
1675 case T_SAMPLERATE:
1676 nr = atoi (new_text);
1677 if (nr != track->samplerate)
1678 {
1679 track->samplerate = nr;
1680 changed = TRUE;
1681 }
1682 break;
1683 case T_BPM:
1684 nr = atoi (new_text);
1685 if (nr != track->BPM)
1686 {
1687 track->BPM = nr;
1688 changed = TRUE;
1689 }
1690 break;
1691 case T_TRACKLEN:
1692 nr = track_scan_length (new_text);
1693 if (nr != track->tracklen)
1694 {
1695 track->tracklen = nr;
1696 changed = TRUE;
1697 }
1698 break;
1699 case T_STARTTIME:
1700 nr = track_scan_length (new_text);
1701 if (nr != track->starttime)
1702 {
1703 track->starttime = nr;
1704 changed = TRUE;
1705 /* Set stoptime to 0 if stoptime is the same as tracklen */
1706 if (track->stoptime == track->tracklen)
1707 track->stoptime = 0;
1708 }
1709 break;
1710 case T_STOPTIME:
1711 nr = track_scan_length (new_text);
1712 /* if stoptime is identical to tracklen, set stoptime to 0 if
1713 * starttime is 0 as well */
1714 if ((nr == track->tracklen) &&
1715 (track->starttime == 0))
1716 {
1717 nr = 0;
1718 }
1719 if (nr != track->stoptime)
1720 {
1721 track->stoptime = nr;
1722 changed = TRUE;
1723 }
1724 break;
1725 case T_SEASON_NR:
1726 nr = atoi (new_text);
1727 if ((nr >= 0) && (nr != track->season_nr))
1728 {
1729 track->season_nr = nr;
1730 changed = TRUE;
1731 }
1732 break;
1733 case T_EPISODE_NR:
1734 nr = atoi (new_text);
1735 if ((nr >= 0) && (nr != track->episode_nr))
1736 {
1737 track->episode_nr = nr;
1738 changed = TRUE;
1739 }
1740 break;
1741 case T_MEDIA_TYPE:
1742 case T_PC_PATH:
1743 case T_IPOD_PATH:
1744 case T_IPOD_ID:
1745 case T_TRANSFERRED:
1746 case T_COMPILATION:
1747 case T_REMEMBER_PLAYBACK_POSITION:
1748 case T_SKIP_WHEN_SHUFFLING:
1749 case T_CHECKED:
1750 case T_ALL:
1751 case T_GAPLESS_TRACK_FLAG:
1752 case T_ITEM_NUM:
1753 case T_THUMB_PATH: // TODO: this should in fact be settable
1754 gtkpod_warning ("Programming error: track_set_text() called with illegal argument (item: %d)\n", item);
1755 break;
1756 }
1757
1758 return changed;
1759 }
1760
1761
1762
1763
1764 /* Fills @size with the size and @num with the number of
1765 non-transferred tracks. The size is in Bytes, minus the space taken
1766 by tracks that will be overwritten. */
1767 /* @size and @num may be NULL */
gp_info_nontransferred_tracks(iTunesDB * itdb,gdouble * size,guint32 * num)1768 void gp_info_nontransferred_tracks (iTunesDB *itdb,
1769 gdouble *size, guint32 *num)
1770 {
1771 GList *gl;
1772
1773 if (size) *size = 0;
1774 if (num) *num = 0;
1775 g_return_if_fail (itdb);
1776
1777 for (gl = itdb->tracks; gl; gl=gl->next)
1778 {
1779 Track *tr = gl->data;
1780 ExtraTrackData *etr;
1781 g_return_if_fail (tr);
1782 etr = tr->userdata;
1783 g_return_if_fail (etr);
1784 if (!tr->transferred)
1785 {
1786 if (size) *size += tr->size;
1787 if (num) *num += 1;
1788 }
1789 }
1790 }
1791
1792
1793
intern_add_track(Playlist * pl,Track * track)1794 static void intern_add_track (Playlist *pl, Track *track)
1795 {
1796 iTunesDB *from_itdb, *to_itdb;
1797 Playlist *to_mpl;
1798 from_itdb = track->itdb;
1799 g_return_if_fail (from_itdb);
1800 to_itdb = pl->itdb;
1801 to_mpl = itdb_playlist_mpl (to_itdb);
1802
1803 /* printf ("add tr %p to pl: %p\n", track, pl); */
1804 if (from_itdb == to_itdb)
1805 { /* DND within the same itdb */
1806
1807 /* set flags to 'podcast' if adding to podcast list */
1808 if (itdb_playlist_is_podcasts (pl))
1809 gp_track_set_flags_podcast (track);
1810 #if 0 /* initially iTunes didn't add podcasts to the MPL */
1811 if (!itdb_playlist_contains_track (to_mpl, track))
1812 { /* add to MPL if not already present (will happen
1813 * if dragged from the podcasts playlist */
1814 gp_playlist_add_track (to_mpl, track, TRUE);
1815 }
1816 #endif
1817 if (!itdb_playlist_is_mpl (pl))
1818 {
1819 /* add to designated playlist -- unless adding
1820 * to podcasts list and track already exists there */
1821 if (itdb_playlist_is_podcasts (pl) &&
1822 g_list_find (pl->members, track))
1823 {
1824 gchar *buf = get_track_info (track, FALSE);
1825 gtkpod_warning (_("Podcast already present: '%s'\n\n"), buf);
1826 g_free (buf);
1827 }
1828 else
1829 {
1830 gp_playlist_add_track (pl, track, TRUE);
1831 }
1832 }
1833 }
1834 else
1835 { /* DND between different itdbs -- need to duplicate the
1836 track before inserting */
1837 Track *duptr, *addtr;
1838 ExtraTrackData *eduptr;
1839 /* duplicate track */
1840 duptr = itdb_track_duplicate (track);
1841 eduptr = duptr->userdata;
1842 g_return_if_fail (eduptr);
1843
1844 duptr->transferred = FALSE;
1845
1846 /* check if adding to iPod and track is on different iPod */
1847 if ((from_itdb->usertype & GP_ITDB_TYPE_IPOD) &&
1848 (to_itdb->usertype & GP_ITDB_TYPE_IPOD))
1849 {
1850 /* Check if track exists locally */
1851 if (!(eduptr->pc_path_locale &&
1852 g_file_test (eduptr->pc_path_locale, G_FILE_TEST_EXISTS)))
1853 { /* No. Use iPod path as source */
1854 g_free (eduptr->pc_path_locale);
1855 g_free (eduptr->pc_path_utf8);
1856 eduptr->pc_path_locale = itdb_filename_on_ipod (track);
1857 eduptr->pc_path_utf8 = charset_to_utf8 (eduptr->pc_path_locale);
1858 }
1859 /* Remove old reference to iPod path */
1860 g_free (duptr->ipod_path);
1861 duptr->ipod_path = g_strdup ("");
1862 }
1863
1864 if (!eduptr->pc_path_locale)
1865 {
1866 gchar *buf;
1867 buf = get_track_info (track, FALSE);
1868 gtkpod_warning (_("Could not find source file for '%s'. Track not copied."));
1869 g_free (buf);
1870 itdb_track_free (duptr);
1871 return;
1872 }
1873
1874 if ((from_itdb->usertype & GP_ITDB_TYPE_LOCAL) &&
1875 (to_itdb->usertype & GP_ITDB_TYPE_IPOD))
1876 { /* make sure the DND origin data is set correctly */
1877 eduptr->local_itdb_id = from_itdb->id;
1878 eduptr->local_track_dbid = track->dbid;
1879 }
1880
1881 /* add to database -- if duplicate detection is on and the
1882 same track already exists in the database, the already
1883 existing track is returned and @duptr is freed */
1884 addtr = gp_track_add (to_itdb, duptr);
1885
1886 /* set flags to 'podcast' if adding to podcast list */
1887 if (itdb_playlist_is_podcasts (pl))
1888 gp_track_set_flags_podcast (addtr);
1889
1890 if (addtr == duptr)
1891 { /* no duplicate */
1892 #if 0 /* initially iTunes didn't add podcasts to the MPL */
1893 /* we need to add to the MPL if the track is no
1894 duplicate and will not be added to the podcasts
1895 playlist */
1896 if (!itdb_playlist_is_podcasts (pl))
1897 { /* don't add to mpl if we add to the podcasts
1898 playlist */
1899 gp_playlist_add_track (to_mpl, addtr, TRUE);
1900 }
1901 #else
1902 /* we need to add to the MPL if the track is no
1903 duplicate */
1904 gp_playlist_add_track (to_mpl, addtr, TRUE);
1905 #endif
1906 }
1907 #if 0 /* initially iTunes didn't add podcasts to the MPL */
1908 else
1909 { /* duplicate */
1910 /* we also need to add to the MPL if the track is a
1911 duplicate, does not yet exist in the MPL and will
1912 not be added to a podcast list (this happens if
1913 it's already in the podcast list) */
1914 if ((!itdb_playlist_contains_track (to_mpl, addtr)) &&
1915 (!itdb_playlist_is_podcasts (pl)))
1916 {
1917 gp_playlist_add_track (to_mpl, addtr, TRUE);
1918 }
1919 }
1920 #endif
1921 /* add to designated playlist (if not mpl) -- unless
1922 * adding to podcasts list and track already * exists
1923 * there */
1924 if (!itdb_playlist_is_mpl (pl))
1925 {
1926 if (itdb_playlist_is_podcasts (pl) &&
1927 g_list_find (pl->members, addtr))
1928 {
1929 gchar *buf = get_track_info (addtr, FALSE);
1930 gtkpod_warning (_("Podcast already present: '%s'\n\n"), buf);
1931 g_free (buf);
1932 }
1933 else
1934 {
1935 gp_playlist_add_track (pl, addtr, TRUE);
1936 }
1937 }
1938 }
1939 }
1940
1941
1942 /*------------------------------------------------------------------*\
1943 * *
1944 * DND to playlists *
1945 * *
1946 \*------------------------------------------------------------------*/
1947
1948 /* DND: add either a GList of tracks or an ASCII list of tracks to
1949 * Playlist @pl */
add_tracks_to_playlist(Playlist * pl,gchar * string,GList * tracks)1950 static void add_tracks_to_playlist (Playlist *pl,
1951 gchar *string, GList *tracks)
1952 {
1953
1954
1955 g_return_if_fail (!(string && tracks));
1956 g_return_if_fail (pl);
1957 g_return_if_fail (pl->itdb);
1958 g_return_if_fail (itdb_playlist_mpl (pl->itdb));
1959 if (!(string || tracks)) return;
1960
1961 if (string)
1962 {
1963 Track *track;
1964 gchar *str = string;
1965 while(parse_tracks_from_string(&str, &track))
1966 {
1967 g_return_if_fail (track);
1968 intern_add_track (pl, track);
1969 }
1970 }
1971 if (tracks)
1972 {
1973 GList *gl;
1974 for (gl=tracks; gl; gl=gl->next)
1975 {
1976 Track *track = gl->data;
1977 g_return_if_fail (track);
1978 intern_add_track (pl, track);
1979 }
1980 }
1981 }
1982
1983
1984 /* DND: add a glist of tracks to Playlist @pl */
add_trackglist_to_playlist(Playlist * pl,GList * tracks)1985 void add_trackglist_to_playlist (Playlist *pl, GList *tracks)
1986 {
1987 add_tracks_to_playlist (pl, NULL, tracks);
1988 }
1989
1990
1991 /* DND: add a list of tracks to Playlist @pl */
add_tracklist_to_playlist(Playlist * pl,gchar * string)1992 void add_tracklist_to_playlist (Playlist *pl, gchar *string)
1993 {
1994 add_tracks_to_playlist (pl, string, NULL);
1995 }
1996
1997 /* DND: add a list of files to Playlist @pl.
1998
1999 @pl: playlist to add to or NULL. If NULL, a "New Playlist" will be
2000 created and inserted at position @pl_pos for adding tracks and when
2001 adding a playlist file, a playlist with the name of the playlist
2002 file will be added.
2003
2004 @pl_pos: position to add playlist file, ignored if @pl!=NULL.
2005
2006 @trackaddfunc: passed on to add_track_by_filename() etc. */
2007
2008 /* Return value: playlist to where the tracks were added. Note: when
2009 adding playlist files, additional playlists may have been created */
add_text_plain_to_playlist(iTunesDB * itdb,Playlist * pl,gchar * str,gint pl_pos,AddTrackFunc trackaddfunc,gpointer data)2010 Playlist *add_text_plain_to_playlist (iTunesDB *itdb, Playlist *pl,
2011 gchar *str, gint pl_pos,
2012 AddTrackFunc trackaddfunc,
2013 gpointer data)
2014 {
2015 gchar **files = NULL, **filesp = NULL;
2016 Playlist *pl_playlist = pl; /* playlist for playlist file */
2017 Playlist *pl_playlist_created = NULL;
2018
2019 g_return_val_if_fail (itdb, NULL);
2020
2021 if (!str) return NULL;
2022
2023 /* printf("pl: %x, pl_pos: %d\n%s\n", pl, pl_pos, str);*/
2024
2025 block_widgets ();
2026
2027 files = g_strsplit (str, "\n", -1);
2028 if (files)
2029 {
2030 filesp = files;
2031 while (*filesp)
2032 {
2033 gboolean added = FALSE;
2034 gint file_len = -1;
2035
2036 gchar *file = NULL;
2037 gchar *decoded_file = NULL;
2038
2039 file = *filesp;
2040 /* file is in uri form (the ones we're looking for are
2041 file:///), file can include the \n or \r\n, which isn't
2042 a valid character of the filename and will cause the
2043 uri decode / file test to fail, so we'll cut it off if
2044 its there. */
2045 file_len = strlen (file);
2046 if (file_len && (file[file_len-1] == '\n'))
2047 {
2048 file[file_len-1] = 0;
2049 --file_len;
2050 }
2051 if (file_len && (file[file_len-1] == '\r'))
2052 {
2053 file[file_len-1] = 0;
2054 --file_len;
2055 }
2056
2057 decoded_file = filename_from_uri (file, NULL, NULL);
2058 if (decoded_file != NULL)
2059 {
2060 if (g_file_test (decoded_file, G_FILE_TEST_IS_DIR))
2061 { /* directory */
2062 if (!pl)
2063 { /* no playlist yet -- create new one */
2064 pl = add_new_pl_user_name (itdb, NULL, pl_pos);
2065 if (!pl) break; /* while (*filesp) */
2066 }
2067 add_directory_by_name (itdb, decoded_file, pl,
2068 prefs_get_int("add_recursively"),
2069 trackaddfunc, data);
2070 added = TRUE;
2071 }
2072 if (g_file_test (decoded_file, G_FILE_TEST_IS_REGULAR))
2073 { /* regular file */
2074 FileType ftype = determine_file_type (decoded_file);
2075 switch (ftype)
2076 {
2077 case FILE_TYPE_MP3:
2078 case FILE_TYPE_M4A:
2079 case FILE_TYPE_M4P:
2080 case FILE_TYPE_M4B:
2081 case FILE_TYPE_WAV:
2082 case FILE_TYPE_M4V:
2083 case FILE_TYPE_MP4:
2084 case FILE_TYPE_MOV:
2085 case FILE_TYPE_MPG:
2086 case FILE_TYPE_OGG:
2087 case FILE_TYPE_FLAC:
2088 if (!pl)
2089 { /* no playlist yet -- create new one */
2090 pl = add_new_pl_user_name (itdb, NULL,
2091 pl_pos);
2092 if (!pl) break; /* while (*filesp) */
2093 }
2094 add_track_by_filename (itdb, decoded_file, pl,
2095 prefs_get_int("add_recursively"),
2096 trackaddfunc, data);
2097 added = TRUE;
2098 break;
2099 case FILE_TYPE_M3U:
2100 case FILE_TYPE_PLS:
2101 pl_playlist_created = add_playlist_by_filename (
2102 itdb, decoded_file,
2103 pl_playlist, pl_pos, trackaddfunc, data);
2104 added = TRUE;
2105 break;
2106 case FILE_TYPE_UNKNOWN:
2107 case FILE_TYPE_DIRECTORY:
2108 case FILE_TYPE_IMAGE:
2109 break;
2110 }
2111 }
2112 g_free (decoded_file);
2113 }
2114 if (!added)
2115 {
2116 if (strlen (*filesp) != 0)
2117 gtkpod_warning (_("drag and drop: ignored '%s'\n"), *filesp);
2118 }
2119 ++filesp;
2120 }
2121 g_strfreev (files);
2122 }
2123 /* display log of non-updated tracks */
2124 display_non_updated (NULL, NULL);
2125 /* display log updated tracks */
2126 display_updated (NULL, NULL);
2127 /* display log of detected duplicates */
2128 gp_duplicate_remove (NULL, NULL);
2129
2130 release_widgets ();
2131
2132 if (pl) return pl;
2133 if (pl_playlist_created) return pl_playlist_created;
2134 return NULL;
2135 }
2136
2137 /*------------------------------------------------------------------*\
2138 * *
2139 * Functions setting default values on tracks *
2140 * *
2141 \*------------------------------------------------------------------*/
2142
2143 /* set podcast-specific flags for @track */
gp_track_set_flags_podcast(Track * track)2144 void gp_track_set_flags_podcast (Track *track)
2145 {
2146 g_return_if_fail (track);
2147 track->skip_when_shuffling = 0x01; /* skip when shuffling */
2148 track->remember_playback_position = 0x01; /* remember playback
2149 * position */
2150 track->flag4 = 0x01; /* Show Title/Album on the 'Now Playing' page */
2151 track->mediatype = ITDB_MEDIATYPE_PODCAST; /* show up under Podcasts */
2152 }
2153
2154 /* set podcast-specific flags for @track */
gp_track_set_flags_default(Track * track)2155 void gp_track_set_flags_default (Track *track)
2156 {
2157 g_return_if_fail (track);
2158 track->skip_when_shuffling = 0x00; /* do not skip when shuffling */
2159 track->remember_playback_position = 0x00; /* do not remember
2160 * playback position */
2161 track->flag4 = 0x00; /* Show Title/Album/Artist on the 'Now
2162 Playing' page */
2163 }
2164
2165
2166 /*------------------------------------------------------------------*\
2167 * *
2168 * Generic functions to "do" things on selected playlist / entry *
2169 * / tracks *
2170 * *
2171 \*------------------------------------------------------------------*/
2172
2173 /* Make a list of all selected tracks and call @do_func with that list
2174 as argument */
gp_do_selected_tracks(void (* do_func)(GList * tracks))2175 void gp_do_selected_tracks (void (*do_func)(GList *tracks))
2176 {
2177 GList *selected_tracks = NULL;
2178
2179 g_return_if_fail (do_func);
2180
2181 /* I'm using ids instead of "Track *" -pointer because it would be
2182 * possible that a track gets removed during the process */
2183 selected_tracks = tm_get_selected_tracks ();
2184 do_func (selected_tracks);
2185 g_list_free (selected_tracks);
2186 }
2187
2188
2189 /* Make a list of all tracks in the currently selected entry of sort
2190 tab @inst and call @do_func with that list as argument */
gp_do_selected_entry(void (* do_func)(GList * tracks),gint inst)2191 void gp_do_selected_entry (void (*do_func)(GList *tracks), gint inst)
2192 {
2193 GList *selected_tracks = NULL;
2194 TabEntry *entry;
2195 GList *gl;
2196
2197 g_return_if_fail (do_func);
2198
2199 g_return_if_fail ((inst >= 0) && (inst <= prefs_get_int("sort_tab_num")));
2200
2201 entry = st_get_selected_entry (inst);
2202 if (entry == NULL)
2203 { /* no entry selected */
2204 gtkpod_statusbar_message (_("No entry selected."));
2205 return;
2206 }
2207 for (gl=entry->members; gl; gl=gl->next)
2208 { /* make a list with all trackids in this entry */
2209 Track *track = gl->data;
2210 g_return_if_fail (track);
2211 selected_tracks = g_list_prepend (selected_tracks, track);
2212 }
2213 selected_tracks = g_list_reverse (selected_tracks);
2214 do_func (selected_tracks);
2215 g_list_free (selected_tracks);
2216 }
2217
2218
2219 /* Make a list of the tracks in the current playlist and call @do_func
2220 with that list as argument */
gp_do_selected_playlist(void (* do_func)(GList * tracks))2221 void gp_do_selected_playlist (void (*do_func)(GList *tracks))
2222 {
2223 GList *selected_tracks = NULL;
2224 Playlist *pl;
2225 GList *gl;
2226
2227 g_return_if_fail (do_func);
2228
2229 pl = pm_get_selected_playlist();
2230 if (!pl)
2231 { /* no playlist selected */
2232 message_sb_no_playlist_selected ();
2233 return;
2234 }
2235 for (gl=pl->members; gl; gl=gl->next)
2236 { /* make a list with all trackids in this entry */
2237 Track *track = gl->data;
2238 g_return_if_fail (track);
2239 selected_tracks = g_list_prepend (selected_tracks, track);
2240 }
2241 selected_tracks = g_list_reverse (selected_tracks);
2242 do_func (selected_tracks);
2243 g_list_free (selected_tracks);
2244 }
2245
2246
2247 /* return some sensible input about the "track". You must free the
2248 * return string after use. */
get_track_info(Track * track,gboolean prefer_filename)2249 gchar *get_track_info (Track *track, gboolean prefer_filename)
2250 {
2251 ExtraTrackData *etr;
2252
2253 g_return_val_if_fail (track, NULL);
2254 etr = track->userdata;
2255 g_return_val_if_fail (etr, NULL);
2256
2257 if (prefer_filename)
2258 {
2259 if (etr->pc_path_utf8 && strlen(etr->pc_path_utf8))
2260 return g_path_get_basename (etr->pc_path_utf8);
2261 }
2262 if ((track->title && strlen(track->title)))
2263 return g_strdup (track->title);
2264 if ((track->album && strlen(track->album)))
2265 return g_strdup (track->album);
2266 if ((track->artist && strlen(track->artist)))
2267 return g_strdup (track->artist);
2268 if ((track->composer && strlen(track->composer)))
2269 return g_strdup (track->composer);
2270 if (!prefer_filename)
2271 {
2272 if (etr->pc_path_utf8 && strlen(etr->pc_path_utf8))
2273 return g_path_get_basename (etr->pc_path_utf8);
2274 }
2275
2276 return g_strdup_printf ("iPod ID: %d", track->id);
2277 }
2278