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 <math.h>
34 #include <string.h>
35 
36 #include "charset.h"
37 #include "details.h"
38 #include "display_itdb.h"
39 #include "display.h"
40 #include "sha1.h"
41 #include "file.h"
42 #include "file_convert.h"
43 #include "misc.h"
44 #include "misc_track.h"
45 #include "info.h"
46 #include "prefs.h"
47 #include "syncdir.h"
48 
49 
50 /* A struct containing a list with available iTunesDBs. A pointer to
51    this struct is stored in gtkpod_window as itdbs_head */
52 static struct itdbs_head *itdbs_head = NULL;
53 
54 /* for convenience */
gp_get_itdbs_head(GtkWidget * gtkpod_win)55 struct itdbs_head *gp_get_itdbs_head (GtkWidget *gtkpod_win)
56 {
57     g_return_val_if_fail (gtkpod_win, NULL);
58     return g_object_get_data (G_OBJECT (gtkpod_win),
59 			      "itdbs_head");
60 }
61 
gp_itdb_extra_destroy(ExtraiTunesDBData * eitdb)62 void gp_itdb_extra_destroy (ExtraiTunesDBData *eitdb)
63 {
64     if (eitdb)
65     {
66 	sha1_free_eitdb (eitdb);
67 	gp_itdb_pc_path_hash_destroy (eitdb);
68 	g_free (eitdb->offline_filename);
69 	itdb_photodb_free (eitdb->photodb);
70 	g_free (eitdb);
71     }
72 }
73 
gp_itdb_extra_duplicate(ExtraiTunesDBData * eitdb)74 ExtraiTunesDBData *gp_itdb_extra_duplicate (ExtraiTunesDBData *eitdb)
75 {
76     ExtraiTunesDBData *eitdb_dup = NULL;
77     if (eitdb)
78     {
79 	/* FIXME: not yet implemented */
80 	g_return_val_if_reached (NULL);
81     }
82     return eitdb_dup;
83 }
84 
gp_playlist_extra_destroy(ExtraPlaylistData * epl)85 void gp_playlist_extra_destroy (ExtraPlaylistData *epl)
86 {
87     if (epl)
88     {
89 	g_free (epl);
90     }
91 }
92 
gp_playlist_extra_duplicate(ExtraPlaylistData * epl)93 ExtraPlaylistData *gp_playlist_extra_duplicate (ExtraPlaylistData *epl)
94 {
95     ExtraPlaylistData *epl_dup = NULL;
96 
97     if (epl)
98     {
99 	epl_dup = g_new (ExtraPlaylistData, 1);
100 	memcpy (epl_dup, epl, sizeof (ExtraPlaylistData));
101     }
102     return epl_dup;
103 }
104 
gp_track_extra_destroy(ExtraTrackData * etrack)105 void gp_track_extra_destroy (ExtraTrackData *etrack)
106 {
107     if (etrack)
108     {
109 	g_free (etrack->year_str);
110 	g_free (etrack->pc_path_locale);
111 	g_free (etrack->pc_path_utf8);
112 	g_free (etrack->converted_file);
113 	g_free (etrack->thumb_path_locale);
114 	g_free (etrack->thumb_path_utf8);
115 	g_free (etrack->hostname);
116 	g_free (etrack->sha1_hash);
117 	g_free (etrack->charset);
118 	g_free (etrack->lyrics);
119 	g_free (etrack);
120     }
121 }
122 
gp_track_extra_duplicate(ExtraTrackData * etr)123 ExtraTrackData *gp_track_extra_duplicate (ExtraTrackData *etr)
124 {
125     ExtraTrackData *etr_dup = NULL;
126 
127     if (etr)
128     {
129 	etr_dup = g_new (ExtraTrackData, 1);
130 	memcpy (etr_dup, etr, sizeof (ExtraTrackData));
131 	/* copy strings */
132 	etr_dup->year_str = g_strdup (etr->year_str);
133 	etr_dup->pc_path_locale = g_strdup (etr->pc_path_locale);
134 	etr_dup->pc_path_utf8 = g_strdup (etr->pc_path_utf8);
135 	etr_dup->converted_file = g_strdup (etr->converted_file);
136 	etr_dup->thumb_path_locale = g_strdup (etr->thumb_path_locale);
137 	etr_dup->thumb_path_utf8 = g_strdup (etr->thumb_path_utf8);
138 	etr_dup->hostname = g_strdup (etr->hostname);
139 	etr_dup->sha1_hash = g_strdup (etr->sha1_hash);
140 	etr_dup->charset = g_strdup (etr->charset);
141 	etr_dup->lyrics = g_strdup (etr->lyrics);
142 	/* clear the pc_path_hashed flag */
143 	etr_dup->pc_path_hashed = FALSE;
144     }
145     return etr_dup;
146 }
147 
148 
149 /* Create a new itdb struct including ExtraiTunesDBData */
gp_itdb_new(void)150 iTunesDB *gp_itdb_new (void)
151 {
152     iTunesDB *itdb = itdb_new ();
153     gp_itdb_add_extra (itdb);
154     return itdb;
155 }
156 
157 
158 /* Free itdb and take care of dependencies */
gp_itdb_free(iTunesDB * itdb)159 void gp_itdb_free (iTunesDB *itdb)
160 {
161     if (space_get_ipod_itdb () == itdb)
162     {
163 	space_set_ipod_itdb (NULL);
164     }
165 
166     /* cancel all pending conversions */
167     file_convert_cancel_itdb (itdb);
168 
169     itdb_free (itdb);
170 }
171 
172 
173     /* Add and initialize ExtraiTunesDBData if missing */
gp_itdb_add_extra(iTunesDB * itdb)174 void gp_itdb_add_extra (iTunesDB *itdb)
175 {
176     g_return_if_fail (itdb);
177 
178     if (!itdb->userdata)
179     {
180 	ExtraiTunesDBData *eitdb = g_new0 (ExtraiTunesDBData, 1);
181 	itdb->userdata = eitdb;
182 	itdb->userdata_destroy =
183 	    (ItdbUserDataDestroyFunc)gp_itdb_extra_destroy;
184 	itdb->userdata_duplicate =
185 	    (ItdbUserDataDuplicateFunc)gp_itdb_extra_duplicate;
186 	eitdb->data_changed = FALSE;
187 	eitdb->itdb_imported = FALSE;
188 	gp_itdb_pc_path_hash_init (eitdb);
189     }
190 }
191 
192 
193 /* Validate a complete @itdb (including tracks and playlists),
194  * i.e. add the Extra*Data */
gp_itdb_add_extra_full(iTunesDB * itdb)195 void gp_itdb_add_extra_full (iTunesDB *itdb)
196 {
197     GList *gl;
198 
199     g_return_if_fail (itdb);
200 
201     /* Add and initialize ExtraiTunesDBData if missing */
202     gp_itdb_add_extra (itdb);
203 
204     /* validate tracks */
205     for (gl=itdb->tracks; gl; gl=gl->next)
206     {
207 	Track *track = gl->data;
208 	g_return_if_fail (track);
209 	gp_track_add_extra (track);
210     }
211 
212     /* validate playlists */
213     for (gl=itdb->playlists; gl; gl=gl->next)
214     {
215 	Playlist *pl = gl->data;
216 	g_return_if_fail (pl);
217 	gp_playlist_add_extra (pl);
218     }
219 }
220 
gp_playlist_new(const gchar * title,gboolean spl)221 Playlist *gp_playlist_new (const gchar *title, gboolean spl)
222 {
223     Playlist *pl = itdb_playlist_new (title, spl);
224     pl->userdata = g_new0 (ExtraPlaylistData, 1);
225     pl->userdata_destroy =
226 	(ItdbUserDataDestroyFunc)gp_playlist_extra_destroy;
227     pl->userdata_duplicate =
228 	(ItdbUserDataDuplicateFunc)gp_playlist_extra_duplicate;
229     return pl;
230 }
231 
232 /* Add and initialize the ExtraPlaylistData if missing */
gp_playlist_add_extra(Playlist * pl)233 void gp_playlist_add_extra (Playlist *pl)
234 {
235     g_return_if_fail (pl);
236 
237     if (!pl->userdata)
238     {
239 	ExtraPlaylistData *epl = g_new0 (ExtraPlaylistData, 1);
240 	pl->userdata = epl;
241 	pl->userdata_destroy =
242 	    (ItdbUserDataDestroyFunc)gp_playlist_extra_destroy;
243 	pl->userdata_duplicate =
244 	    (ItdbUserDataDuplicateFunc)gp_playlist_extra_duplicate;
245     }
246 }
247 
gp_track_new(void)248 Track *gp_track_new (void)
249 {
250     Track *track = itdb_track_new ();
251     /* Add ExtraTrackData */
252     gp_track_add_extra (track);
253     gp_track_set_flags_default (track);
254     return track;
255 }
256 
257 /* Add and initialize the ExtraTrackData if missing */
gp_track_add_extra(Track * track)258 void gp_track_add_extra (Track *track)
259 {
260     g_return_if_fail (track);
261 
262     if (!track->userdata)
263     {
264 	ExtraTrackData *etr = g_new0 (ExtraTrackData, 1);
265 	track->userdata = etr;
266 	etr->lyrics=NULL;
267 	track->userdata_destroy =
268 	    (ItdbUserDataDestroyFunc)gp_track_extra_destroy;
269 	track->userdata_duplicate =
270 	    (ItdbUserDataDuplicateFunc)gp_track_extra_duplicate;
271     }
272 }
273 
274 
275 /* Append track to the track list of @itdb */
276 /* Note: the track will also have to be added to the playlists */
277 /* Returns: pointer to the added track -- may be different in the case
278    of duplicates. In that case a pointer to the already existing track
279    is returned. */
gp_track_add(iTunesDB * itdb,Track * track)280 Track *gp_track_add (iTunesDB *itdb, Track *track)
281 {
282     Track *result=NULL;
283     Track *oldtrack = sha1_track_exists_insert (itdb, track);
284 
285     if(oldtrack)
286     {
287 	gp_duplicate_remove (oldtrack, track);
288 	itdb_track_free (track);
289 	result = oldtrack;
290     }
291     else
292     {
293 	/* Make sure all strings are initialised -- that way we don't
294 	   have to worry about it when we are handling the strings */
295 	/* exception: sha1_hash, hostname, charset: these may be NULL. */
296 	gp_track_validate_entries (track);
297 	itdb_track_add (itdb, track, -1);
298 	/* add to filename hash */
299 	gp_itdb_pc_path_hash_add_track (track);
300 	/* add to background conversion if necessary */
301 	file_convert_add_track (track);
302 	result = track;
303 	data_changed (itdb);
304     }
305     return result;
306 }
307 
308 
309 /* Remove track and notify all windows of the change
310    NOTE: you need to notify the main display via pm_remove_track()
311    before when you make sure @track is no longer referenced in any
312    playlist -- see gp_playlist_remove_track for details */
gp_track_remove(Track * track)313 void gp_track_remove (Track *track)
314 {
315     /* call gp_track_unlink() and itdb_track_free() instead of
316        itdb_track_remove() so we don't have to maintain two remove
317        functions separately. If something needs to be done before
318        removing the track do it in gp_track_unlink */
319     gp_track_unlink (track);
320     itdb_track_free (track);
321 }
322 
323 
324 /* Unlink track and notify all windows of the change
325    NOTE: you need to notify the main display via pm_remove_track()
326    before when you make sure @track is no longer referenced in any
327    playlist -- see gp_playlist_remove_track for details */
gp_track_unlink(Track * track)328 void gp_track_unlink (Track *track)
329 {
330     /* the details window may be accessing the tracks */
331     details_remove_track (track);
332     /* cancel pending conversions */
333     file_convert_cancel_track (track);
334     /* remove from SHA1 hash */
335     sha1_track_remove (track);
336     /* remove from pc_path_hash */
337     gp_itdb_pc_path_hash_remove_track (track);
338     /* remove from database */
339     itdb_track_unlink (track);
340 }
341 
342 
343 /* Set a thumbnail and update ExtraTrackData (e.g. filename) */
gp_track_set_thumbnails_internal(Track * track,const gchar * filename,const guchar * image_data,gsize image_data_len)344 static gboolean gp_track_set_thumbnails_internal (Track *track,
345 						  const gchar *filename,
346 						  const guchar *image_data,
347 						  gsize image_data_len)
348 {
349     gboolean result = FALSE;
350     ExtraTrackData *etr;
351 
352     g_return_val_if_fail (track, FALSE);
353 
354     etr = track->userdata;
355     g_return_val_if_fail (etr, FALSE);
356 
357     if (filename)
358     {
359 	result = itdb_track_set_thumbnails (track, filename);
360     }
361     else if (image_data)
362     {
363 	result = itdb_track_set_thumbnails_from_data (track,
364 						      image_data,
365 						      image_data_len);
366     }
367 
368     g_free (etr->thumb_path_locale);
369     g_free (etr->thumb_path_utf8);
370 
371     if (filename && (result == TRUE))
372     {
373 	etr->thumb_path_locale = g_strdup (filename);
374 	etr->thumb_path_utf8 = charset_to_utf8 (filename);
375     }
376     else
377     {
378 	etr->thumb_path_locale = g_strdup ("");
379 	etr->thumb_path_utf8 = g_strdup ("");
380     }
381 
382     if (result == FALSE)
383     {
384 	if (filename)
385 	    gtkpod_warning (_("Failed to set cover art: '%s'\n"), filename);
386     }
387 
388     return result;
389 }
390 
391 /* Set a thumbnail and update data in ExtraTrackData */
gp_track_set_thumbnails_from_data(Track * track,const guchar * image_data,gsize image_data_len)392 gboolean gp_track_set_thumbnails_from_data (Track *track,
393 					    const guchar *image_data,
394 					    gsize image_data_len)
395 {
396     g_return_val_if_fail (track, FALSE);
397     g_return_val_if_fail (image_data, FALSE);
398 
399     return gp_track_set_thumbnails_internal (track, NULL,
400 					     image_data, image_data_len);
401 }
402 
403 
404 /* Set a thumbnail and store the filename in ExtraTrackData */
gp_track_set_thumbnails(Track * track,const gchar * filename)405 gboolean gp_track_set_thumbnails (Track *track, const gchar *filename)
406 {
407     g_return_val_if_fail (track, FALSE);
408     g_return_val_if_fail (filename, FALSE);
409 
410     return gp_track_set_thumbnails_internal (track, filename,
411 					     NULL, 0);
412 }
413 
414 
415 /* Remove a thumbnail and remove the filename in ExtraTrackData */
416 /* Return value:
417    FALSE: track did not have any thumbnails, so no change was done
418    TRUE: track did have thumbnails which were removed */
gp_track_remove_thumbnails(Track * track)419 gboolean gp_track_remove_thumbnails (Track *track)
420 {
421     gboolean changed=FALSE;
422 
423     ExtraTrackData *etr;
424     g_return_val_if_fail (track, FALSE);
425     etr = track->userdata;
426     g_return_val_if_fail (etr, FALSE);
427 
428     if (itdb_track_has_thumbnails (track))
429 	changed = TRUE;
430 
431     itdb_track_remove_thumbnails (track);
432     g_free (etr->thumb_path_locale);
433     g_free (etr->thumb_path_utf8);
434     etr->thumb_path_locale = g_strdup ("");
435     etr->thumb_path_utf8 = g_strdup ("");
436 
437     return changed;
438 }
439 
440 /* add itdb to itdbs (and add to display) */
gp_itdb_add(iTunesDB * itdb,gint pos)441 void gp_itdb_add (iTunesDB *itdb, gint pos)
442 {
443     ExtraiTunesDBData *eitdb;
444 
445     g_return_if_fail (itdbs_head);
446     g_return_if_fail (itdb);
447     eitdb = itdb->userdata;
448     g_return_if_fail (eitdb);
449 
450     eitdb->itdbs_head = itdbs_head;
451     itdbs_head->itdbs = g_list_insert (itdbs_head->itdbs, itdb, pos);
452     pm_add_itdb (itdb, pos);
453 }
454 
455 /* Remove itdb to itdbs (and remove from display). Call
456  * itdb_free() to free the memory of the itdb. */
gp_itdb_remove(iTunesDB * itdb)457 void gp_itdb_remove (iTunesDB *itdb)
458 {
459     g_return_if_fail (itdbs_head);
460     g_return_if_fail (itdb);
461 
462     pm_remove_playlist (itdb_playlist_mpl (itdb), FALSE);
463     itdbs_head->itdbs = g_list_remove (itdbs_head->itdbs, itdb);
464 }
465 
466 /* Also replaces @old_itdb in the itdbs GList and take care that the
467  * displayed itdb gets replaced as well */
gp_replace_itdb(iTunesDB * old_itdb,iTunesDB * new_itdb)468 void gp_replace_itdb (iTunesDB *old_itdb, iTunesDB *new_itdb)
469 {
470     ExtraiTunesDBData *new_eitdb;
471     Playlist *old_pl, *mpl;
472     GList *old_link;
473     gchar *old_pl_name = NULL;
474     gint pos = -1; /* default: add to the end */
475 
476     g_return_if_fail (old_itdb);
477     g_return_if_fail (new_itdb);
478     g_return_if_fail (itdbs_head);
479 
480     new_eitdb = new_itdb->userdata;
481     g_return_if_fail (new_eitdb);
482 
483     old_link = g_list_find (itdbs_head->itdbs, old_itdb);
484     g_return_if_fail (old_link);
485 
486     /* remember old selection */
487     old_pl = pm_get_selected_playlist ();
488     if (old_pl)
489     {   /* remember name of formerly selected playlist if it's in the
490 	   same itdb */
491 	if (old_pl->itdb == old_itdb)
492 	    old_pl_name = g_strdup (old_pl->name);
493     }
494 
495     /* get position of @old_itdb */
496     pos = pm_get_position_for_itdb (old_itdb);
497 
498     /* remove @old_itdb (all playlists are removed if the MPL is
499        removed and add @new_itdb at its place */
500     pm_remove_playlist (itdb_playlist_mpl (old_itdb), FALSE);
501 
502     /* replace old_itdb with new_itdb */
503     new_eitdb->itdbs_head = itdbs_head;
504     old_link->data = new_itdb;
505     /* free old_itdb */
506     gp_itdb_free (old_itdb);
507 
508     /* display replacement */
509     pm_add_itdb (new_itdb, pos);
510 
511     /* reselect old playlist if still available */
512     if (old_pl_name)
513     {
514 	Playlist *pl = itdb_playlist_by_name (new_itdb, old_pl_name);
515 	if (pl) pm_select_playlist (pl);
516     }
517 
518     /* Set prefs system with name of MPL */
519     mpl = itdb_playlist_mpl (new_itdb);
520     set_itdb_prefs_string (new_itdb, "name", mpl->name);
521 
522     /* Clean up */
523     g_free (old_pl_name);
524 }
525 
526 
527 /* add playlist to itdb and to display */
gp_playlist_add(iTunesDB * itdb,Playlist * pl,gint32 pos)528 void gp_playlist_add (iTunesDB *itdb, Playlist *pl, gint32 pos)
529 {
530     g_return_if_fail (itdb);
531     g_return_if_fail (pl);
532 
533     itdb_playlist_add (itdb, pl, pos);
534     pm_add_child (itdb, PM_COLUMN_PLAYLIST, pl, pos);
535     data_changed (itdb);
536 }
537 
538 /* create new playlist with title @name and add to @itdb and to
539  * display at position @pos */
gp_playlist_add_new(iTunesDB * itdb,gchar * name,gboolean spl,gint32 pos)540 Playlist *gp_playlist_add_new (iTunesDB *itdb, gchar *name,
541 			       gboolean spl, gint32 pos)
542 {
543     Playlist *pl;
544 
545     g_return_val_if_fail (itdb, NULL);
546     g_return_val_if_fail (name, NULL);
547 
548     pl = gp_playlist_new (name, spl);
549     itdb_playlist_add (itdb, pl, pos);
550     pm_add_child (itdb, PM_COLUMN_PLAYLIST, pl, pos);
551     data_changed (itdb);
552     return pl;
553 }
554 
555 /** If playlist @pl_name doesn't exist, then it will be created
556  * and added to the tail of playlists, otherwise pointer to an existing
557  * playlist will be returned
558  */
gp_playlist_by_name_or_add(iTunesDB * itdb,gchar * pl_name,gboolean spl)559 Playlist *gp_playlist_by_name_or_add (iTunesDB *itdb, gchar *pl_name,
560 				      gboolean spl)
561 {
562     Playlist *pl = NULL;
563 
564     g_return_val_if_fail (itdb, pl);
565     g_return_val_if_fail (pl_name, pl);
566     pl = itdb_playlist_by_name (itdb, pl_name);
567     if (pl)
568     {   /* check if it's the same type (spl or normal) */
569 	if (pl->is_spl == spl) return pl;
570     }
571     /* Create a new playlist */
572     pl = gp_playlist_add_new (itdb, pl_name, spl, -1);
573     return pl;
574 }
575 
576 
577 /* Remove a playlist from the itdb and from the display */
gp_playlist_remove(Playlist * pl)578 void gp_playlist_remove (Playlist *pl)
579 {
580     g_return_if_fail (pl);
581     g_return_if_fail (pl->itdb);
582     pm_remove_playlist (pl, TRUE);
583     data_changed (pl->itdb);
584     itdb_playlist_remove (pl);
585 }
586 
587 
588 
589 /* FIXME: this is a bit dangerous. . . we delete all
590  * playlists with titles @pl_name and return how many
591  * pl have been removed.
592  ***/
gp_playlist_remove_by_name(iTunesDB * itdb,gchar * pl_name)593 guint gp_playlist_remove_by_name (iTunesDB *itdb, gchar *pl_name)
594 {
595     guint i;
596     guint pl_removed=0;
597 
598     g_return_val_if_fail (itdb, pl_removed);
599 
600     for(i=1; i < itdb_playlists_number(itdb); i++)
601     {
602 	Playlist *pl = itdb_playlist_by_nr (itdb, i);
603 	g_return_val_if_fail (pl, pl_removed);
604 	g_return_val_if_fail (pl->name, pl_removed);
605         if(strcmp (pl->name, pl_name) == 0)
606         {
607             gp_playlist_remove (pl);
608 	    /* we just deleted the ith element of playlists, so
609 	     * we must examine the new ith element. */
610             pl_removed++;
611             i--;
612         }
613     }
614     return pl_removed;
615 }
616 
617 
618 /* This function removes the track "track" from the
619    playlist "plitem" and also adjusts the display.
620    No action is taken if "track" is not in the playlist.
621    If "plitem" == NULL, remove from master playlist.
622    If the track is removed from the MPL, it's also removed
623    from memory completely (i.e. from the tracklist and sha1 hash).
624    Depending on @deleteaction, the track is either marked for deletion
625    on the ipod/hard disk or just removed from the database
626  */
gp_playlist_remove_track(Playlist * plitem,Track * track,DeleteAction deleteaction)627 void gp_playlist_remove_track (Playlist *plitem, Track *track,
628 			       DeleteAction deleteaction)
629 {
630     iTunesDB *itdb;
631     Playlist *mpl;
632     gboolean remove_track = FALSE;
633 
634     g_return_if_fail (track);
635     itdb = track->itdb;
636     g_return_if_fail (itdb);
637 
638     switch (deleteaction)
639     {
640     case DELETE_ACTION_IPOD:
641     case DELETE_ACTION_LOCAL:
642     case DELETE_ACTION_DATABASE:
643 	/* remove from MPL in these cases */
644 	plitem = NULL;
645 	break;
646     case DELETE_ACTION_PLAYLIST:
647 	/* cannot remove from MPL */
648 	g_return_if_fail (plitem);
649 	break;
650     }
651 
652     mpl = itdb_playlist_mpl (track->itdb);
653 
654     if (plitem == NULL)
655 	plitem = mpl;
656 
657     g_return_if_fail (plitem);
658 
659     /* remove track from display */
660     pm_remove_track (plitem, track);
661 
662     /* remove track from playlist */
663     itdb_playlist_remove_track (plitem, track);
664 
665 #if 0
666     /* podcasts are no longer treated differently from other playlists */
667     /* if we removed a podcasts, remove it from memory as well, unless
668        it's present in the MPL (this happens if this podcast was on
669        the iPod as podcast as well as standard track) */
670     if (itdb_playlist_is_podcasts (plitem))
671     {
672 	/* just for safety: remove possible duplicates of @track in
673 	   the podcast playlist before removing it from memory */
674 	while (g_list_find (plitem->members, track))
675 	{
676 	    pm_remove_track (plitem, track);
677 	    itdb_playlist_remove_track (plitem, track);
678 	}
679 	if (!itdb_playlist_contains_track (mpl, track))
680 	{
681 	    remove_track = TRUE;
682 	}
683 	else
684 	{   /* strip the podcast flags */
685 	    gp_track_set_flags_default (track);
686 	}
687     }
688 #endif
689 
690     if (itdb_playlist_is_mpl (plitem))
691     { /* if it's the MPL, we remove the track permanently */
692 	GList *gl = g_list_nth (itdb->playlists, 1);
693 	ExtraiTunesDBData *eitdb = itdb->userdata;
694 	g_return_if_fail (eitdb);
695 
696 	while (gl)
697 	{  /* first we remove the track from all other playlists (i=1) */
698 	    Playlist *pl = gl->data;
699 	    g_return_if_fail (pl);
700 	    while (g_list_find (pl->members, track))
701 	    {
702 		pm_remove_track (pl, track);
703 		itdb_playlist_remove_track (pl, track);
704 	    }
705 	    gl=gl->next;
706 	}
707 	remove_track = TRUE;
708     }
709 
710     if (remove_track)
711     {
712 	if (itdb->usertype & GP_ITDB_TYPE_IPOD)
713 	{
714 	    switch (deleteaction)
715 	    {
716 	    case DELETE_ACTION_DATABASE:
717 		/* ATTENTION: this might create a dangling file on the
718 		   iPod! */
719 		gp_track_remove (track);
720 		break;
721 	    case DELETE_ACTION_IPOD:
722 		if (track->transferred)
723 		{
724 		    gp_track_unlink (track);
725 		    mark_track_for_deletion (itdb, track);
726 		}
727 		else
728 		{
729 		    gp_track_remove (track);
730 		}
731 		break;
732 	    case DELETE_ACTION_PLAYLIST:
733 	    case DELETE_ACTION_LOCAL:
734 		break;
735 		/* not allowed -- programming error */
736 		g_return_if_reached ();
737 		break;
738 	    }
739 	}
740 	if (itdb->usertype & GP_ITDB_TYPE_LOCAL)
741 	{
742 	    switch (deleteaction)
743 	    {
744 	    case DELETE_ACTION_LOCAL:
745 		gp_track_unlink (track);
746 		mark_track_for_deletion (itdb, track);
747 		break;
748 	    case DELETE_ACTION_DATABASE:
749 		gp_track_remove (track);
750 		break;
751 	    case DELETE_ACTION_PLAYLIST:
752 	    case DELETE_ACTION_IPOD:
753 		/* not allowed -- programming error */
754 		g_return_if_reached ();
755 		break;
756 	    }
757 	}
758     }
759 
760     data_changed (itdb);
761 }
762 
763 /* This function appends the track "track" to the
764    playlist @pl. It then lets the display model know.
765    @display: if TRUE, track is added the display.  Otherwise it's only
766    added to memory */
767 /* All tracks added to the podcast playlist will get the mark_unplayed
768    flag get set */
gp_playlist_add_track(Playlist * pl,Track * track,gboolean display)769 void gp_playlist_add_track (Playlist *pl, Track *track, gboolean display)
770 {
771     iTunesDB *itdb;
772 
773     g_return_if_fail (track);
774     g_return_if_fail (pl);
775     itdb = pl->itdb;
776     g_return_if_fail (itdb);
777 
778     itdb_playlist_add_track (pl, track, -1);
779     if (itdb_playlist_is_podcasts (pl))
780     {   /* have the iPod display a bullet in front of the track as it
781 	   has been newly added */
782 	track->mark_unplayed = 0x02;
783     }
784     if (display)  pm_add_track (pl, track, TRUE);
785 
786     data_changed (itdb);
787 }
788 
789 
790 /* Make sure all strings are initialised -- that way we don't
791    have to worry about it when we are handling the strings.
792    exception: sha1_hash, hostname and charset: these may be NULL. */
gp_track_validate_entries(Track * track)793 void gp_track_validate_entries (Track *track)
794 {
795     ExtraTrackData *etr;
796 
797     g_return_if_fail (track);
798     etr = track->userdata;
799     g_return_if_fail (etr);
800 
801     if (!track->title)           track->title = g_strdup ("");
802     if (!track->artist)          track->artist = g_strdup ("");
803     if (!track->album)           track->album = g_strdup ("");
804     if (!track->genre)           track->genre = g_strdup ("");
805     if (!track->composer)        track->composer = g_strdup ("");
806     if (!track->comment)         track->comment = g_strdup ("");
807     if (!track->filetype)        track->filetype = g_strdup ("");
808     if (!track->grouping)        track->grouping = g_strdup ("");
809     if (!track->category)        track->category = g_strdup ("");
810     if (!track->description)     track->description = g_strdup ("");
811     if (!track->podcasturl)      track->podcasturl = g_strdup ("");
812     if (!track->podcastrss)      track->podcastrss = g_strdup ("");
813     if (!track->subtitle)        track->subtitle = g_strdup ("");
814     if (!track->ipod_path)       track->ipod_path = g_strdup ("");
815     if (!track->tvshow)          track->tvshow = g_strdup("");
816     if (!track->tvepisode)       track->tvepisode = g_strdup("");
817     if (!track->tvnetwork)       track->tvnetwork = g_strdup("");
818     if (!track->albumartist)     track->albumartist = g_strdup ("");
819     if (!track->sort_artist)     track->sort_artist = g_strdup ("");
820     if (!track->sort_title)      track->sort_title = g_strdup ("");
821     if (!track->sort_album)      track->sort_album = g_strdup ("");
822     if (!track->sort_albumartist)track->sort_albumartist = g_strdup ("");
823     if (!track->sort_composer)   track->sort_composer = g_strdup ("");
824     if (!track->sort_tvshow)     track->sort_tvshow = g_strdup ("");
825     if (!etr->pc_path_utf8)      etr->pc_path_utf8 = g_strdup ("");
826     if (!etr->pc_path_locale)    etr->pc_path_locale = g_strdup ("");
827     if (!etr->thumb_path_utf8)   etr->thumb_path_utf8 = g_strdup ("");
828     if (!etr->thumb_path_locale) etr->thumb_path_locale = g_strdup ("");
829     if (!etr->lyrics)            etr->lyrics = g_strdup ("");
830     /* Make sure year_str is identical to year */
831     g_free (etr->year_str);
832     etr->year_str = g_strdup_printf ("%d", track->year);
833 }
834 
835 
836 
837 /* Initialize the itdb data
838  *
839  * If itdb_n_type/filename/mountpoint... exist in the prefs, that data
840  * is being used and local databases are read in directly if they
841  * exist. Otherwise a simple two-itdb list is constructed consisting
842  * of one local database and one ipod database.
843  *
844  */
init_data(GtkWidget * window)845 void init_data (GtkWidget *window)
846 {
847     gchar *cfgdir;
848     gint i;
849 
850     g_return_if_fail (window);
851     g_return_if_fail (itdbs_head == NULL);
852 
853     cfgdir = prefs_get_cfgdir ();
854 
855     itdbs_head = g_new0 (struct itdbs_head, 1);
856 
857     g_object_set_data (G_OBJECT (window), "itdbs_head", itdbs_head);
858 
859     if (!prefs_get_int_value ("itdb_0_type", NULL))
860     {
861 	/* databases have not been set up previously -- take care of
862 	   this */
863 #ifndef HAVE_GIO
864 	gchar *mountpoint;
865 #endif
866 	gchar *filename;
867 
868 	/* Local database */
869 	filename = g_build_filename (cfgdir, "local_0.itdb", NULL);
870 	prefs_set_int ("itdb_0_type", GP_ITDB_TYPE_LOCAL);
871 	prefs_set_string ("itdb_0_name", _("Music Library"));
872 	prefs_set_string ("itdb_0_filename", filename);
873 	g_free (filename);
874 
875 	/* Podcasts database */
876 	filename = g_build_filename (cfgdir, "podcasts.itdb", NULL);
877 	prefs_set_int ("itdb_1_type",
878 		       GP_ITDB_TYPE_PODCASTS|GP_ITDB_TYPE_LOCAL);
879 	prefs_set_string ("itdb_1_name", _("Podcasts"));
880 	prefs_set_string ("itdb_1_filename", filename);
881 	g_free (filename);
882 
883 #ifndef HAVE_GIO
884 	/* iPod database -- only set up if autodetection is not active */
885 	mountpoint = prefs_get_string ("initial_mountpoint");
886 	filename = g_build_filename (cfgdir, "iTunesDB", NULL);
887 	prefs_set_int ("itdb_2_type", GP_ITDB_TYPE_IPOD);
888 	prefs_set_string ("itdb_2_name", _("iPod"));
889 	prefs_set_string ("itdb_2_filename", filename);
890 	prefs_set_string ("itdb_2_mountpoint", mountpoint);
891 	g_free (mountpoint);
892 	g_free (filename);
893 #endif
894     }
895 
896 
897     for (i=0;;++i)
898     {
899 	ExtraiTunesDBData *eitdb;
900 	iTunesDB *itdb = setup_itdb_n (i);
901 
902 	if (itdb == NULL)
903 	    break;
904 
905 	/* add to the display */
906 	gp_itdb_add (itdb, -1);
907 
908 	/* update/sync playlists according to options set */
909 	eitdb = itdb->userdata;
910 	g_return_if_fail (eitdb);
911 	if (eitdb->itdb_imported)
912 	{
913 	    /* take care of autosync... */
914 	    sync_all_playlists (itdb);
915 
916 	    /* update all live SPLs */
917 	    itdb_spl_update_live (itdb);
918 	}
919     }
920 
921     g_free (cfgdir);
922 	pm_show_all_playlists ();
923 }
924 
925 
926 
927 /* Create an repository according to the settings in the preferences
928    system. */
setup_itdb_n(gint i)929 iTunesDB *setup_itdb_n (gint i)
930 {
931     iTunesDB *itdb = NULL;
932     gchar *property = get_itdb_prefs_key (i, "type");
933     gint type;
934     gboolean valid = prefs_get_int_value (property, &type);
935     g_free (property);
936     if (valid)
937     {
938 	gchar *cfgdir = prefs_get_cfgdir ();
939 	Playlist *pl = NULL;
940 	ExtraiTunesDBData *eitdb;
941 	gchar *filename = NULL;
942 	gchar *mountpoint = NULL;
943 	gchar *offline_filename = NULL;
944 
945 	if (type & GP_ITDB_TYPE_LOCAL)
946 	{
947 	    gchar *fn = get_itdb_prefs_key (i, "filename");
948 
949 	    filename = prefs_get_string (fn);
950 
951 	    if (!filename)
952 	    {
953 		gchar *local = g_strdup_printf ("local%d.itdb",i);
954 		filename = g_build_filename (cfgdir, local, NULL);
955 		g_free (local);
956 	    }
957 	    g_free (fn);
958 	    if (g_file_test (filename, G_FILE_TEST_EXISTS))
959 		itdb = gp_import_itdb (NULL, type,
960 				       NULL, NULL, filename);
961 	}
962 	else if (type & GP_ITDB_TYPE_IPOD)
963 	{
964 	    gchar *key;
965 
966 	    key = get_itdb_prefs_key (i, KEY_MOUNTPOINT);
967 	    mountpoint = prefs_get_string (key);
968 	    g_free (key);
969 
970 	    key = get_itdb_prefs_key (i, "filename");
971 	    offline_filename = prefs_get_string (key);
972 	    g_free (key);
973 
974 	    if (!offline_filename)
975 	    {
976 		gchar *local = g_strdup_printf ("gtkpod_%d.itdb",i);
977 		offline_filename = g_build_filename (
978 		    cfgdir, local, NULL);
979 		g_free (local);
980 	    }
981 	}
982 	else
983 	{
984 	    g_return_val_if_reached (NULL);
985 	}
986 
987 	if (!itdb)
988 	{
989 	    gchar *nm, *name;
990 
991 	    itdb = gp_itdb_new ();
992 	    eitdb = itdb->userdata;
993 	    g_return_val_if_fail (eitdb, NULL);
994 	    itdb->usertype = type;
995 	    itdb->filename = filename;
996 	    itdb_set_mountpoint (itdb, mountpoint);
997 	    eitdb->offline_filename = offline_filename;
998 
999 	    nm = g_strdup_printf ("itdb_%d_name", i);
1000 	    if (!prefs_get_string_value (nm, &name))
1001 	    {
1002 		if (type & GP_ITDB_TYPE_PODCASTS)
1003 		    name = g_strdup (_("Podcasts"));
1004 		else if (type & GP_ITDB_TYPE_LOCAL)
1005 		    name = g_strdup (_("Local"));
1006 		else
1007 		    name = g_strdup (_("iPod"));
1008 	    }
1009 	    pl = gp_playlist_new (name, FALSE);
1010 	    g_free (name);
1011 	    g_free (nm);
1012 	    itdb_playlist_set_mpl (pl);
1013 	    itdb_playlist_add (itdb, pl, -1);
1014 
1015            if ((type & GP_ITDB_TYPE_PODCASTS) || (type & GP_ITDB_TYPE_LOCAL))
1016              {
1017                eitdb->data_changed = TRUE;
1018                eitdb->itdb_imported = TRUE;
1019              }
1020            else
1021              {
1022                eitdb->data_changed = FALSE;
1023                eitdb->itdb_imported = FALSE;
1024              }
1025 	}
1026 	else
1027 	{
1028 	    g_free (filename);
1029 	    g_free (offline_filename);
1030 	}
1031 	g_free (mountpoint);
1032 
1033 	/* Check if Podcast Playlist is present on IPOD itdb
1034 	 * and add if not. If Podcast Playlist is present on
1035 	 * local itdb remove it. */
1036 	pl = itdb_playlist_podcasts (itdb);
1037 	if ((type & GP_ITDB_TYPE_IPOD) && !pl)
1038 	{   /* add podcast playlist */
1039 	    pl = gp_playlist_new (_("Podcasts"), FALSE);
1040 	    itdb_playlist_set_podcasts (pl);
1041 	    itdb_playlist_add (itdb, pl, -1);
1042 	    eitdb = itdb->userdata;
1043 	    g_return_val_if_fail (eitdb, NULL);
1044 	    eitdb->data_changed = FALSE;
1045 	}
1046 	if ((type & GP_ITDB_TYPE_LOCAL) && pl)
1047 	{   /* Remove podcast playlist. Normally no playlist
1048 	     * should be present, except for a few people who
1049 	     * used the CVS version between September and
1050 	     * October 2005. */
1051 	    if (itdb_playlist_tracks_number (pl) == 0)
1052 	    {
1053 		gp_playlist_remove (pl);
1054 	    }
1055 	    else
1056 	    {   /* OK, let's be nice and just drop the
1057 		   'podcast' flag instead of removing */
1058 		pl->podcastflag = 0;
1059 	    }
1060 	}
1061 	g_free (cfgdir);
1062     }
1063     return itdb;
1064 }
1065 
1066 
1067 /* Increase playcount of filename <file> by <num>. If sha1 is activated,
1068    use sha1 to find the track. Otherwise use the filename. If @sha1 is
1069    set, this value is used directly to look up the track in the
1070    database (instead of calculating it from the file).
1071 
1072    Return value:
1073    TRUE: OK (playcount increased in GP_ITDB_TYPE_IPOD)
1074    FALSE: file could not be found in the GP_ITDB_TYPE_IPOD */
gp_increase_playcount(gchar * sha1,gchar * file,gint num)1075 gboolean gp_increase_playcount (gchar *sha1, gchar *file, gint num)
1076 {
1077     gboolean result = FALSE;
1078     Track *track = NULL;
1079     GList *gl;
1080 
1081     g_return_val_if_fail (itdbs_head, FALSE);
1082 
1083     for (gl=itdbs_head->itdbs; gl; gl=gl->next)
1084     {
1085 	iTunesDB *itdb = gl->data;
1086 	g_return_val_if_fail (itdb, FALSE);
1087 
1088 	if (sha1) track = sha1_sha1_exists (itdb, sha1);
1089 	else     track = sha1_file_exists (itdb, file, TRUE);
1090 	if (!track)	  track = gp_track_by_filename (itdb, file);
1091 	if (track)
1092 	{
1093 	    gchar *buf1;
1094 	    track->playcount += num;
1095 	    data_changed (itdb);
1096 	    pm_track_changed (track);
1097 	    buf1 = get_track_info (track, TRUE);
1098 	    gtkpod_statusbar_message (_("Increased playcount for '%s'"),
1099 				      buf1);
1100 	    g_free (buf1);
1101 	    if (itdb->usertype & GP_ITDB_TYPE_IPOD)    result = TRUE;
1102 	}
1103     }
1104     return result;
1105 }
1106 
1107 
1108 /* get the currently selected itdb. NULL is
1109  * returned if no itdb is active. */
gp_get_selected_itdb(void)1110 iTunesDB *gp_get_selected_itdb (void)
1111 {
1112     return pm_get_selected_itdb ();
1113 }
1114 
1115 
1116 /* Get the "ipod" itdb. If only one iPod itdb exists, this itdb is
1117  * returned. If more than one iPod itdb exists, the currently selected
1118  * itdb is returned if it's an iPod itdb, otherwise NULL is returned.
1119  */
gp_get_ipod_itdb(void)1120 iTunesDB *gp_get_ipod_itdb (void)
1121 {
1122     struct itdbs_head *itdbs_head;
1123     iTunesDB *itdb;
1124     GList *gl;
1125     gint i;
1126 
1127     /* if an iPod itdb is selected, return this */
1128     itdb = gp_get_selected_itdb ();
1129     if (itdb && (itdb->usertype & GP_ITDB_TYPE_IPOD))
1130 	return itdb;
1131 
1132     itdb = NULL;
1133 
1134     g_return_val_if_fail (gtkpod_window, NULL);
1135     itdbs_head = g_object_get_data (G_OBJECT (gtkpod_window),
1136 				    "itdbs_head");
1137 
1138     if (itdbs_head == NULL) return NULL;
1139 
1140     i=0;
1141     for (gl=itdbs_head->itdbs; gl; gl=gl->next)
1142     {
1143 	iTunesDB *itdbgl = gl->data;
1144 	g_return_val_if_fail (itdbgl, NULL);
1145 	if (itdbgl->usertype & GP_ITDB_TYPE_IPOD)
1146 	{
1147 	    itdb = itdbgl;
1148 	    ++i;
1149 	}
1150     }
1151     /* return iPod itdb if only one was found */
1152     if (i == 1)
1153 	return itdb;
1154 
1155     return NULL;
1156 }
1157 
1158 
1159 /* return the podcast itdb */
gp_get_podcast_itdb()1160 iTunesDB *gp_get_podcast_itdb ()
1161 {
1162     GList *gl;
1163 
1164     g_return_val_if_fail (itdbs_head, NULL);
1165 
1166     for (gl=itdbs_head->itdbs; gl; gl=gl->next)
1167     {
1168 	iTunesDB *itdb = gl->data;
1169 	g_return_val_if_fail (itdb, NULL);
1170 
1171 	if (itdb->usertype & GP_ITDB_TYPE_PODCASTS)
1172 	    return itdb;
1173     }
1174     return NULL;
1175 }
1176 
1177 
1178