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