1 /* Time-stamp: <2008-07-06 10:38:05 jcs>
2 |
3 | Copyright (C) 2002-2005 Jorg Schuler <jcsjcs at users sourceforge net>
4 | Part of the gtkpod project.
5 |
6 | URL: http://www.gtkpod.org/
7 | URL: http://gtkpod.sourceforge.net/
8 |
9 | This program is free software; you can redistribute it and/or modify
10 | it under the terms of the GNU General Public License as published by
11 | the Free Software Foundation; either version 2 of the License, or
12 | (at your option) any later version.
13 |
14 | This program is distributed in the hope that it will be useful,
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 | GNU General Public License for more details.
18 |
19 | You should have received a copy of the GNU General Public License
20 | along with this program; if not, write to the Free Software
21 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 |
23 | iTunes and iPod are trademarks of Apple
24 |
25 | This product is not supported/written/published by Apple!
26 |
27 | $Id$
28 */
29
30 /* This file provides functions for syncing a directory or directories
31 * with a playlist */
32
33 #include <libintl.h>
34 #include <string.h>
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <unistd.h>
38 #include "display_itdb.h"
39 #include "file.h"
40 #include "info.h"
41 #include "misc.h"
42 #include "misc_track.h"
43 #include "prefs.h"
44 #include "syncdir.h"
45
46
47 struct add_files_data
48 {
49 Playlist *playlist;
50 GList **tracks_updated;
51 GHashTable *filepath_hash;
52 };
53
54 /* Used in the callback after adding a new track to
55 * to add to the filehash */
56 struct added_file_data
57 {
58 GHashTable *filepath_hash;
59 gchar *filepath;
60 };
61
62
63 /**
64 * confirm_sync_dirs:
65 *
66 * @dirs_hash: hash table containing the directory names
67 * @key_sync_confirm_dirs: preference key to specify whether or not
68 * the list of directories should be confirmed. The
69 * confirmation dialog may change the value of this prefs
70 * entry. If NULL confirmation takes place.
71 *
72 * Have the user confirm which directories should be included into the
73 * confirmation process.
74 *
75 * Return value: FALSE: user aborted. TRUE: otherwise. @dirs_hash will
76 * be adjusted to reflect the selected directories.
77 */
confirm_sync_dirs(GHashTable * dirs_hash,const gchar * key_sync_confirm_dirs)78 static gboolean confirm_sync_dirs (GHashTable *dirs_hash,
79 const gchar *key_sync_confirm_dirs)
80 {
81 g_return_val_if_fail (dirs_hash, FALSE);
82
83 if (key_sync_confirm_dirs && !prefs_get_int (key_sync_confirm_dirs))
84 return TRUE;
85
86 /* FIXME: implement confirmation (doesn't strike me as a major
87 * feature -- feel free to contribute)
88 *
89 * The idea would be to have the user check/uncheck each of the
90 * individual directories. @dirs_hash will be adjusted to reflect
91 * the selected directories.
92 */
93 return TRUE;
94 }
95
96
97 /**
98 * confirm_delete_tracks:
99 *
100 * @tracks: GList with tracks that are supposed to be removed from the
101 * iPod or the local repository.
102 * @key_sync_confirm_delete: preference key to specify whether or not
103 * the removal of tracks should be confirmed. The
104 * confirmation dialog may change the value of this prefs
105 * entry. If NULL confirmation takes place.
106 *
107 * Return value: TRUE: it's OK to remove the tracks. FALSE: it's not
108 * OK to remove the tracks. TRUE is also given if no
109 * tracks are present, @key_sync_confirm_delete is NULL
110 * or it's setting is 'FALSE' (0).
111 */
112
confirm_delete_tracks(GList * tracks,const gchar * key_sync_confirm_delete)113 static gboolean confirm_delete_tracks (GList *tracks,
114 const gchar *key_sync_confirm_delete)
115 {
116 GtkResponseType response;
117 struct DeleteData dd;
118 gchar *label, *title;
119 GString *string;
120 iTunesDB *itdb;
121 Track *tr;
122
123 if (tracks == NULL)
124 return TRUE;
125
126 if (key_sync_confirm_delete &&
127 !prefs_get_int (key_sync_confirm_delete))
128 return TRUE;
129
130 tr = g_list_nth_data (tracks, 0);
131 g_return_val_if_fail (tr, FALSE);
132 itdb = tr->itdb;
133 g_return_val_if_fail (itdb, FALSE);
134
135 dd.itdb = itdb;
136 dd.pl = NULL;
137 dd.tracks = tracks;
138 if (itdb->usertype & GP_ITDB_TYPE_IPOD)
139 dd.deleteaction = DELETE_ACTION_IPOD;
140 if (itdb->usertype & GP_ITDB_TYPE_LOCAL)
141 dd.deleteaction = DELETE_ACTION_DATABASE;
142
143 delete_populate_settings (&dd,
144 &label, &title,
145 NULL, NULL,
146 &string);
147
148 response = gtkpod_confirmation (
149 -1, /* gint id, */
150 TRUE, /* gboolean modal, */
151 title, /* title */
152 label, /* label */
153 string->str, /* scrolled text */
154 NULL, 0, NULL, /* option 1 */
155 NULL, 0, NULL, /* option 2 */
156 TRUE, /* gboolean confirm_again, */
157 key_sync_confirm_delete, /* ConfHandlerOpt confirm_again_key,*/
158 CONF_NULL_HANDLER, /* ConfHandler ok_handler,*/
159 NULL, /* don't show "Apply" button */
160 CONF_NULL_HANDLER, /* cancel_handler,*/
161 NULL, /* gpointer user_data1,*/
162 NULL); /* gpointer user_data2,*/
163
164
165 g_free (label);
166 g_free (title);
167 g_string_free (string, TRUE);
168
169 if (response == GTK_RESPONSE_OK)
170 {
171 /* it's OK to remove the tracks */
172 return TRUE;
173 }
174 else
175 {
176 /* better not delete the tracks */
177 return FALSE;
178 }
179 }
180
181
182 /* Used by sync_show_summary() */
sync_add_tracks(GString * str,GList * tracks,const gchar * title)183 static void sync_add_tracks (GString *str,
184 GList *tracks, const gchar *title)
185 {
186 GList *gl;
187
188 g_return_if_fail (str);
189 g_return_if_fail (title);
190
191 if (tracks)
192 {
193 g_string_append (str, title);
194
195 for (gl=tracks; gl; gl=gl->next)
196 {
197 gchar *buf;
198 Track *tr = gl->data;
199 g_return_if_fail (tr);
200
201 buf = get_track_info (tr, FALSE);
202 g_string_append_printf (str, "%s\n", buf);
203 g_free (buf);
204 }
205 g_string_append_printf (str, "\n\n");
206 }
207 }
208
209
210
211 /**
212 * sync_show_summary:
213 *
214 * @key_sync_show_summary: preference key to specify whether or not a
215 * summary should be shown or not. If NULL, the summary is
216 * shown. This key may be changed by the confirmation dialog.
217 * @playlist: playlist where are syncing with.
218 * @tracks_to_delete_from_ipod: GList with tracks to be deleted from
219 * the iPod or local repository.
220 * @tracks_to_delete_from_playlist: GList with tracks to be deleted
221 * from @playlist.
222 * @tracks_updated: GList with tracks that have been updated.
223 */
show_sync_summary(const gchar * key_sync_show_summary,Playlist * playlist,GList * tracks_to_delete_from_ipod,GList * tracks_to_delete_from_playlist,GList * tracks_updated)224 static void show_sync_summary (const gchar *key_sync_show_summary,
225 Playlist *playlist,
226 GList *tracks_to_delete_from_ipod,
227 GList *tracks_to_delete_from_playlist,
228 GList *tracks_updated)
229 {
230 GString *summary;
231 Playlist *mpl;
232 gint no_length;
233
234 g_return_if_fail (playlist);
235 g_return_if_fail (playlist->itdb);
236
237 if (key_sync_show_summary && !prefs_get_int (key_sync_show_summary))
238 return;
239
240 summary = g_string_sized_new (2000);
241
242 /* mpl->name is the repository's name */
243 mpl = itdb_playlist_mpl (playlist->itdb);
244 g_return_if_fail (mpl);
245 g_string_append_printf (summary,
246 _("Sync summary for %s/%s\n"),
247 mpl->name, playlist->name);
248
249 /* used to check whether data was added or not */
250 no_length = strlen (summary->str);
251
252 sync_add_tracks (
253 summary,
254 tracks_updated,
255 ngettext ("The following track has been added or updated:\n",
256 "The following tracks have been added or updated:\n",
257 g_list_length (tracks_updated)));
258
259 if (playlist->itdb->usertype & GP_ITDB_TYPE_IPOD)
260 {
261 sync_add_tracks (
262 summary,
263 tracks_to_delete_from_ipod,
264 ngettext ("The following track has been completely removed from the iPod:\n",
265 "The following tracks have been completely removed from the iPod:\n",
266 g_list_length (tracks_to_delete_from_ipod)));
267 }
268 else
269 {
270 sync_add_tracks (
271 summary,
272 tracks_to_delete_from_ipod,
273 ngettext ("The following track has been removed from the repository:\n",
274 "The following tracks have been removed from the repository:\n",
275 g_list_length (tracks_to_delete_from_ipod)));
276 }
277
278 sync_add_tracks (summary,
279 tracks_to_delete_from_playlist,
280 ngettext ("The following track has been removed from the playlist:\n",
281 "The following tracks have been removed from the playlist:\n",
282 g_list_length (tracks_to_delete_from_playlist)));
283
284 if (strlen (summary->str) == no_length)
285 {
286 g_string_append (summary, _("Nothing was changed.\n"));
287 }
288
289 gtkpod_confirmation (CONF_ID_SYNC_SUMMARY,
290 FALSE,
291 _("Sync summary"),
292 NULL,
293 summary->str,
294 NULL, 0, NULL,
295 NULL, 0, NULL,
296 TRUE,
297 key_sync_show_summary,
298 CONF_NULL_HANDLER, NULL, NULL,
299 NULL, NULL);
300
301 g_string_free (summary, TRUE);
302 }
303
304
305
306
307 /* Callback for adding tracks (makes sure track isn't added to playlist
308 * again if it already exists */
sync_addtrackfunc(Playlist * plitem,Track * track,gpointer data)309 static void sync_addtrackfunc (Playlist *plitem, Track *track, gpointer data)
310 {
311 struct added_file_data *afd = data;
312
313 g_return_if_fail (plitem);
314 g_return_if_fail (track);
315
316 g_return_if_fail (afd->filepath_hash);
317 g_return_if_fail (afd->filepath);
318
319 /* add the new entry to the filepath */
320 g_hash_table_insert (afd->filepath_hash, g_strdup (afd->filepath), track);
321
322 /* only add if @track isn't already a member of the current playlist */
323 if (!itdb_playlist_contains_track (plitem, track))
324 gp_playlist_add_track (plitem, track, TRUE);
325 }
326
327
328 /* Builds a hash of all the tracks in the playlists db,
329 * hashed by the file path */
get_itdb_filepath_hash(Playlist * pl)330 static GHashTable *get_itdb_filepath_hash (Playlist *pl)
331 {
332 GHashTable* filepath_hash;
333 iTunesDB *itdb = pl->itdb;
334
335 filepath_hash = g_hash_table_new_full (
336 g_str_hash, g_str_equal, g_free, NULL);
337
338 GList *gl;
339 for (gl=itdb->tracks; gl; gl=gl->next)
340 {
341 ExtraTrackData *etr;
342 Track *track = gl->data;
343 g_return_val_if_fail (track, NULL);
344
345 etr = track->userdata;
346 g_return_val_if_fail (etr, NULL);
347
348 /* track has filename info */
349 if (etr->pc_path_locale && *etr->pc_path_locale)
350 {
351 g_hash_table_insert (filepath_hash, g_strdup (etr->pc_path_locale), track);
352 }
353 }
354
355 return filepath_hash;
356 }
357
358
359 /**
360 * add_files:
361 *
362 * add all music/video files to the playlist @userdata->playlist.
363 * updated/newly added tracks are appended to @userdata->tracks_updated.
364 */
add_files(gpointer key,gpointer value,gpointer user_data)365 static void add_files (gpointer key, gpointer value, gpointer user_data)
366 {
367 struct add_files_data *afd = user_data;
368 Playlist *pl;
369 gchar *dirname;
370
371 g_return_if_fail (key);
372 g_return_if_fail (afd);
373 g_return_if_fail (afd->playlist);
374 g_return_if_fail (afd->tracks_updated);
375 g_return_if_fail (afd->filepath_hash);
376
377 dirname = key;
378 pl = afd->playlist;
379
380 if (g_file_test (dirname, G_FILE_TEST_IS_DIR))
381 {
382 GDir *dir = g_dir_open (dirname, 0, NULL);
383 if (dir != NULL)
384 {
385 G_CONST_RETURN gchar *next;
386 while ((next = g_dir_read_name (dir)))
387 {
388 gchar *filename = g_build_filename (dirname, next, NULL);
389 FileType filetype = determine_file_type (filename);
390 gboolean updated = FALSE;
391 Track *tr=NULL;
392
393 switch (filetype)
394 {
395 case FILE_TYPE_UNKNOWN:
396 case FILE_TYPE_DIRECTORY:
397 case FILE_TYPE_IMAGE:
398 case FILE_TYPE_M3U:
399 case FILE_TYPE_PLS:
400 /* ignore non-music/video files */
401 break;
402 case FILE_TYPE_MP3:
403 case FILE_TYPE_M4A:
404 case FILE_TYPE_M4P:
405 case FILE_TYPE_M4B:
406 case FILE_TYPE_WAV:
407 case FILE_TYPE_M4V:
408 case FILE_TYPE_MP4:
409 case FILE_TYPE_MOV:
410 case FILE_TYPE_MPG:
411 case FILE_TYPE_OGG:
412 case FILE_TYPE_FLAC:
413 tr = g_hash_table_lookup (afd->filepath_hash, filename);
414 if (tr)
415 { /* track is already present in playlist.
416 Update if date stamp is different. */
417 struct stat filestat;
418 ExtraTrackData *etr = tr->userdata;
419 g_return_if_fail (etr);
420
421 stat (filename, &filestat);
422 /*
423 printf ("%ld %ld (%s)\n, %ld %d\n",
424 filestat.st_mtime, etr->mtime,
425 filename,
426 filestat.st_size, tr->size);
427 */
428 if ((filestat.st_mtime != etr->mtime) ||
429 (filestat.st_size != tr->size))
430 {
431 update_track_from_file (pl->itdb, tr);
432 updated = TRUE;
433 }
434 }
435 else
436 { /* track is not known -- at least not by it's
437 * filename -> add to playlist using the
438 * standard function. Duplicate adding is
439 * avoided by an addtrack function checking
440 * for duplication */
441 struct added_file_data data;
442 data.filepath = filename;
443 data.filepath_hash = afd->filepath_hash;
444
445 add_track_by_filename (pl->itdb, filename,
446 pl, FALSE,
447 sync_addtrackfunc, &data);
448
449 tr = g_hash_table_lookup (afd->filepath_hash, filename);
450 updated = TRUE;
451 }
452 break;
453 }
454 if (tr && updated)
455 {
456 *afd->tracks_updated =
457 g_list_append (*afd->tracks_updated, tr);
458 }
459 g_free (filename);
460 }
461 }
462 g_dir_close (dir);
463 }
464 }
465
466
467 /**
468 * cache_directory:
469 *
470 * Add the given directory to the given hash table then recurse into the directory
471 * and add all its sub-directories.
472 *
473 * @dir: top-level directory to recurse into and store subdirectories
474 * @dirs_hash: pointer to a hash table that stores all directory paths
475 *
476 * Return value: none, dirs_hash stores all required paths
477 *
478 **/
cache_directory(const gchar * dir,GHashTable * dirs_hash)479 static void cache_directory (const gchar *dir, GHashTable *dirs_hash)
480 {
481 GDir *dir_handle;
482 const gchar *filename;
483 gchar *path;
484
485 if (! g_file_test (dir, G_FILE_TEST_IS_DIR))
486 return;
487
488 /* dir represents a directory so store it in the hash table */
489 g_hash_table_insert(dirs_hash, g_strdup(dir), NULL);
490
491 dir_handle = g_dir_open (dir, 0, NULL);
492 if (dir_handle == NULL)
493 return;
494
495 /* Loop through the filenames in the directory */
496 while ((filename = g_dir_read_name(dir_handle)))
497 {
498 /* Construct absolute path from dir and filename */
499 path = g_build_filename(dir, filename, NULL);
500
501 /* If path is not directory then move on to next */
502 if (! g_file_test (path, G_FILE_TEST_IS_DIR))
503 continue;
504
505 /* recursively walk down into sub directory */
506 cache_directory (path, dirs_hash);
507 g_free (path);
508 }
509
510 g_dir_close(dir_handle);
511 }
512
513
514 /**
515 * sync_playlist:
516 *
517 * @playlist: playlist to sync with contents on hard disk
518 * @syncdir: directory to sync with. If @syncdir is NULL, a list of
519 * directories is created from all the filenames of the
520 * member tracks
521 * @key_sync_confirm_dirs: preference key to specify whether or not
522 * the list of directories should be confirmed. The
523 * confirmation dialog may change the value of this prefs
524 * entry. If NULL, @sync_confirm_dirs decides whether
525 * confirmation takes place or not.
526 * FIXME: not implemented at present.
527 * @sync_confirm_dirs: see under @key_sync_confirm_dirs.
528 * @key_sync_delete_tracks: preference key to specify whether or not
529 * tracks no longer present in the directory list should be
530 * removed from the iPod/database or not. Normally tracks
531 * are only removed from the current playlist. If this key's
532 * value is set to TRUE (1), they will be removed from the
533 * iPod /database completely, if they are not a member of
534 * other playlists. Note: to remove tracks from the MPL,
535 * this has to be TRUE.
536 * If NULL, @sync_delete_tracks will determine whether
537 * tracks are removed or not. Also, if @playlist is the
538 * MPL, tracks will be removed irrespective of this key's
539 * value.
540 * @sync_delete_tracks: see under @key_sync_delete_tracks.
541 * @key_sync_confirm_delete: preference key to specify whether or not
542 * the removal of tracks should be confirmed. The
543 * confirmation dialog may change the value of this prefs
544 * entry. If NULL, @sync_confirm_delete will determine
545 * whether or not confirmation takes place.
546 * @sync_confirm_delete: see under @key_sync_confirm_delete
547 * @key_sync_show_summary: preference key to specify whether or not a
548 * summary of removed and newly added or updated tracks
549 * should be displayed. If NULL, @sync_show_shummary will
550 * determine whether or not a summary is displayed.
551 * @sync_show_shummary: see under @key_sync_show_shummary
552 *
553 * Return value: none, but will give status information via the
554 * statusbar and information windows.
555 **/
sync_playlist(Playlist * playlist,const gchar * syncdir,const gchar * key_sync_confirm_dirs,gboolean sync_confirm_dirs,const gchar * key_sync_delete_tracks,gboolean sync_delete_tracks,const gchar * key_sync_confirm_delete,gboolean sync_confirm_delete,const gchar * key_sync_show_summary,gboolean sync_show_summary)556 void sync_playlist (Playlist *playlist,
557 const gchar *syncdir,
558 const gchar *key_sync_confirm_dirs,
559 gboolean sync_confirm_dirs,
560 const gchar *key_sync_delete_tracks,
561 gboolean sync_delete_tracks,
562 const gchar *key_sync_confirm_delete,
563 gboolean sync_confirm_delete,
564 const gchar *key_sync_show_summary,
565 gboolean sync_show_summary)
566 {
567 GHashTable *dirs_hash, *filepath_hash;
568 gboolean delete_tracks, is_mpl;
569 time_t current_time;
570 GList *tracks_to_delete_from_ipod = NULL;
571 GList *tracks_to_delete_from_playlist = NULL;
572 GList *tracks_updated = NULL;
573 struct add_files_data afd;
574 GList *gl;
575
576 g_return_if_fail (playlist);
577
578 /* Create a hash to keep the directory names ("key", and "value"
579 to be freed with g_free). key is dirname in local encoding,
580 value is dirname in utf8, if available */
581 dirs_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
582
583 /* If @syncdir is not NULL, put @syndir into the hash
584 table. Otherwise put the dirs of all tracks in @playlist into
585 the table. */
586
587 if (syncdir)
588 {
589 /* make sure the directory name does not end in '/' -- the
590 code below does not seem to like this */
591 gint len = strlen (syncdir);
592 gchar *dir = g_strdup (syncdir);
593 if (len > 1)
594 {
595 if (G_IS_DIR_SEPARATOR (dir[len-1]))
596 {
597 dir[len-1] = 0;
598 }
599 }
600 cache_directory (dir, dirs_hash);
601 }
602 else
603 {
604 for (gl = playlist->members; gl; gl=gl->next)
605 {
606 ExtraTrackData *etr;
607 Track *track = gl->data;
608 g_return_if_fail (track);
609 etr = track->userdata;
610 g_return_if_fail (etr);
611
612 if (etr->pc_path_locale && *etr->pc_path_locale)
613 {
614 gchar *dirname_local;
615
616 dirname_local = g_path_get_dirname (etr->pc_path_locale);
617 if (etr->pc_path_utf8 && *etr->pc_path_utf8)
618 {
619 g_hash_table_insert (
620 dirs_hash,
621 dirname_local,
622 g_path_get_dirname (etr->pc_path_utf8));
623 }
624 else
625 { /* no utf8 -- make sure we don't replace a dir
626 * entry that had the utf8 data set */
627 if (!g_hash_table_lookup (dirs_hash, dirname_local))
628 {
629 g_hash_table_insert (dirs_hash,
630 dirname_local, NULL);
631 }
632 else
633 {
634 g_free (dirname_local);
635 }
636 }
637 }
638 }
639 }
640
641 /* Confirm directories */
642 if (key_sync_confirm_dirs || sync_confirm_dirs)
643 {
644 if (!confirm_sync_dirs (dirs_hash, key_sync_confirm_dirs))
645 { /* aborted */
646 g_hash_table_destroy (dirs_hash);
647 return;
648 }
649 }
650
651 /* current_time can be used to recognize newly added/updated
652 tracks */
653 current_time = time (NULL);
654
655 /* craete a hash with all files in the current playlist for faster
656 * comparison with files in the directory */
657 filepath_hash = get_itdb_filepath_hash (playlist);
658
659 afd.playlist = playlist;
660 afd.tracks_updated = &tracks_updated;
661 afd.filepath_hash = filepath_hash;
662 /* Add all files in all directories present in dirs_hash */
663 g_hash_table_foreach (dirs_hash, add_files, &afd);
664
665 /* we won't need this hash any more */
666 g_hash_table_destroy (filepath_hash);
667 filepath_hash = NULL;
668
669 /* Remove updated and duplicate list so it won't pop up at a later
670 time */
671 display_updated ((void *)-1, NULL);
672 display_non_updated ((void *)-1, NULL);
673 gp_duplicate_remove (NULL, (void *)-1);
674
675 /* Should tracks be deleted that were not present in the
676 * directories? */
677 if (key_sync_delete_tracks == NULL)
678 {
679 delete_tracks = TRUE;
680 }
681 else
682 {
683 delete_tracks = prefs_get_int (key_sync_delete_tracks);
684 }
685 /* Is playlist the MPL? */
686 is_mpl = itdb_playlist_is_mpl (playlist);
687
688 /* Identify all tracks in playlist not being located in one of the
689 specified dirs, or no longer existing. */
690 for (gl=playlist->members; gl; gl=gl->next)
691 {
692 ExtraTrackData *etr;
693 gboolean remove;
694
695 Track *tr = gl->data;
696 g_return_if_fail (tr);
697 etr = tr->userdata;
698 g_return_if_fail (etr);
699
700 remove = FALSE;
701 if (etr->pc_path_locale && *etr->pc_path_locale)
702 {
703 gchar *dirname_local;
704
705 dirname_local = g_path_get_dirname (etr->pc_path_locale);
706 if (!g_hash_table_lookup_extended (dirs_hash, dirname_local,
707 NULL, NULL))
708 { /* file is not in one of the specified directories */
709 remove = TRUE;
710 }
711 else
712 { /* check if file exists */
713 if (g_file_test (etr->pc_path_locale,
714 G_FILE_TEST_EXISTS) == FALSE)
715 { /* no -- remove */
716 remove = TRUE;
717 }
718 }
719 g_free (dirname_local);
720 }
721
722 if (remove)
723 { /* decide whether track needs to be removed from the iPod
724 * (only member of this playlist) or only from this
725 * playlist (if delete_tracks is not set, no tracks are
726 * removed from the MPL) */
727 if (delete_tracks &&
728 (is_mpl || (itdb_playlist_contain_track_number (tr)==1)))
729 {
730 tracks_to_delete_from_ipod =
731 g_list_append (tracks_to_delete_from_ipod, tr);
732 }
733 else
734 {
735 if (!is_mpl)
736 {
737 tracks_to_delete_from_playlist =
738 g_list_append (tracks_to_delete_from_playlist, tr);
739 }
740 }
741 }
742 }
743
744
745 if (tracks_to_delete_from_ipod &&
746 (key_sync_confirm_delete || sync_confirm_delete) &&
747 (confirm_delete_tracks (tracks_to_delete_from_ipod,
748 key_sync_confirm_delete) == FALSE))
749 { /* User doesn't want us to remove those tracks from the
750 * iPod. We'll therefore just remove them from the playlist
751 * (if playlist is the MPL, don't remove at all) */
752 if (!is_mpl)
753 {
754 tracks_to_delete_from_playlist = g_list_concat (
755 tracks_to_delete_from_playlist,
756 tracks_to_delete_from_ipod);
757 }
758 else
759 {
760 g_list_free (tracks_to_delete_from_ipod);
761 }
762 tracks_to_delete_from_ipod = NULL;
763 }
764
765
766 if (key_sync_show_summary || sync_show_summary)
767 {
768 show_sync_summary (key_sync_show_summary,
769 playlist,
770 tracks_to_delete_from_ipod,
771 tracks_to_delete_from_playlist,
772 tracks_updated);
773 }
774
775 /* Remove completely */
776 for (gl=tracks_to_delete_from_ipod; gl; gl=gl->next)
777 {
778 Track *tr = gl->data;
779 g_return_if_fail (tr);
780
781 if (tr->itdb->usertype & GP_ITDB_TYPE_IPOD)
782 gp_playlist_remove_track (NULL, tr, DELETE_ACTION_IPOD);
783 else if (tr->itdb->usertype & GP_ITDB_TYPE_LOCAL)
784 gp_playlist_remove_track (NULL, tr, DELETE_ACTION_DATABASE);
785 }
786
787 /* Remove from playlist */
788 for (gl=tracks_to_delete_from_playlist; gl; gl=gl->next)
789 {
790 Track *tr = gl->data;
791 g_return_if_fail (tr);
792
793 gp_playlist_remove_track (playlist, tr, DELETE_ACTION_PLAYLIST);
794 }
795
796 /* Was any data changed? */
797 if (tracks_to_delete_from_ipod ||
798 tracks_to_delete_from_playlist ||
799 tracks_updated)
800 {
801 data_changed (playlist->itdb);
802 gtkpod_tracks_statusbar_update ();
803 }
804
805 g_list_free (tracks_to_delete_from_ipod);
806 g_list_free (tracks_to_delete_from_playlist);
807 g_list_free (tracks_updated);
808 }
809
810
811 /**
812 * sync_all_playlists:
813 *
814 * @itdb: repository whose playlists are to be updated
815 *
816 * Will update all playlists in @itdb according to options set. The
817 * following pref subkeys are relevant:
818 *
819 * sync_confirm_dirs
820 * sync_delete_tracks
821 * sync_confirm_delete
822 * sync_show_summary
823 */
824
sync_all_playlists(iTunesDB * itdb)825 void sync_all_playlists (iTunesDB *itdb)
826 {
827 gint index;
828 GList *gl;
829
830 g_return_if_fail (itdb);
831
832 index = get_itdb_index (itdb);
833
834 for (gl=itdb->playlists; gl; gl=gl->next)
835 {
836 gint syncmode;
837 Playlist *pl = gl->data;
838 g_return_if_fail (pl);
839
840 syncmode = get_playlist_prefs_int (pl, KEY_SYNCMODE);
841 if (syncmode != SYNC_PLAYLIST_MODE_NONE)
842 {
843 gchar *key_sync_confirm_dirs =
844 get_playlist_prefs_key (index, pl, KEY_SYNC_CONFIRM_DIRS);
845 gchar *key_sync_delete_tracks =
846 get_playlist_prefs_key (index, pl, KEY_SYNC_DELETE_TRACKS);
847 gchar *key_sync_confirm_delete =
848 get_playlist_prefs_key (index, pl, KEY_SYNC_CONFIRM_DELETE);
849 gchar *key_sync_show_summary =
850 get_playlist_prefs_key (index, pl, KEY_SYNC_SHOW_SUMMARY);
851 gchar *syncdir = NULL;
852
853 if (syncmode == SYNC_PLAYLIST_MODE_MANUAL)
854 {
855 syncdir = get_playlist_prefs_string (pl,
856 KEY_MANUAL_SYNCDIR);
857 }
858
859 sync_playlist (pl, syncdir,
860 key_sync_confirm_dirs, 0,
861 key_sync_delete_tracks, 0,
862 key_sync_confirm_delete, 0,
863 key_sync_show_summary, 0);
864
865 g_free (key_sync_confirm_dirs);
866 g_free (key_sync_delete_tracks);
867 g_free (key_sync_confirm_delete);
868 g_free (key_sync_show_summary);
869 g_free (syncdir);
870 }
871 }
872 }
873