1 /*
2 | Copyright (C) 2002-2005 Jorg Schuler <jcsjcs at users sourceforge net>
3 | Part of the gtkpod project.
4 |
5 | URL: http://www.gtkpod.org/
6 | URL: http://gtkpod.sourceforge.net/
7 |
8 | 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 <ctype.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <sys/file.h>
37 #include <sys/stat.h>
38 #include <sys/types.h>
39 #include <unistd.h>
40 #include <glib.h>
41 #include <glib/gstdio.h>
42
43
44 #include "charset.h"
45 #include "clientserver.h"
46 #include "confirmation.h"
47 #include "info.h"
48 #include "file.h"
49 #include "file_convert.h"
50 #include "itdb.h"
51 #include "sha1.h"
52 #include "misc.h"
53 #include "misc_track.h"
54 #include "mp3file.h"
55 #include "mp4file.h"
56 #include "prefs.h"
57 #include "wavfile.h"
58 #include "oggfile.h"
59 #include "flacfile.h"
60
61 /* The uppercase version of these extensions is tried as well. */
62 static const gchar *imageext[] =
63 {".jpg", ".jpeg", ".png", ".pbm", ".pgm", ".ppm", ".tif", ".tiff",
64 ".gif", NULL};
65
66
67 /*------------------------------------------------------------------*\
68 * *
69 * Temporary Video stuff -- move to appropriate files when *
70 * properly supported. *
71 * *
72 \*------------------------------------------------------------------*/
73
video_get_file_info(gchar * filename)74 Track *video_get_file_info (gchar *filename)
75 {
76 Track *track;
77 track = gp_track_new ();
78 switch (determine_file_type (filename))
79 {
80 case FILE_TYPE_M4V:
81 track->filetype = g_strdup ("M4V video file");
82 break;
83 case FILE_TYPE_MP4:
84 track->filetype = g_strdup ("MP4 video file");
85 break;
86 case FILE_TYPE_MOV:
87 track->filetype = g_strdup ("MOV video file");
88 break;
89 case FILE_TYPE_MPG:
90 track->filetype = g_strdup ("MPG video file");
91 break;
92 case FILE_TYPE_UNKNOWN:
93 case FILE_TYPE_MP3:
94 case FILE_TYPE_M4A:
95 case FILE_TYPE_M4P:
96 case FILE_TYPE_M4B:
97 case FILE_TYPE_WAV:
98 case FILE_TYPE_M3U:
99 case FILE_TYPE_PLS:
100 case FILE_TYPE_IMAGE:
101 case FILE_TYPE_DIRECTORY:
102 case FILE_TYPE_OGG:
103 case FILE_TYPE_FLAC:
104 g_free (track);
105 g_return_val_if_reached (NULL);
106 }
107 return track;
108 }
109
110
111 /* Determine the type of a file.
112 *
113 * Currently this is done by checking the suffix of the filename. An improved
114 * version should probably only fall back to that method if everything else
115 * fails.
116 * -jlt
117 */
118
determine_file_type(const gchar * path)119 FileType determine_file_type (const gchar *path)
120 {
121 gchar *path_utf8, *suf;
122 FileType type = FILE_TYPE_UNKNOWN;
123
124 g_return_val_if_fail (path, type);
125
126 if (g_file_test (path, G_FILE_TEST_IS_DIR))
127 return FILE_TYPE_DIRECTORY;
128
129 path_utf8 = charset_to_utf8 (path);
130 suf = strrchr (path_utf8, '.');
131 if (suf)
132 {
133 if (g_strcasecmp (suf, ".mp3") == 0) type = FILE_TYPE_MP3;
134 else if (g_strcasecmp (suf, ".m4a") == 0) type = FILE_TYPE_M4A;
135 else if (g_strcasecmp (suf, ".m4p") == 0) type = FILE_TYPE_M4P;
136 else if (g_strcasecmp (suf, ".m4b") == 0) type = FILE_TYPE_M4B;
137 else if (g_strcasecmp (suf, ".wav") == 0) type = FILE_TYPE_WAV;
138
139 else if (g_strcasecmp (suf, ".m4v") == 0) type = FILE_TYPE_M4V;
140 else if (g_strcasecmp (suf, ".mp4") == 0) type = FILE_TYPE_MP4;
141 else if (g_strcasecmp (suf, ".mov") == 0) type = FILE_TYPE_MOV;
142 else if (g_strcasecmp (suf, ".mpg") == 0) type = FILE_TYPE_MPG;
143 else if (g_strcasecmp (suf, ".mpeg") == 0) type = FILE_TYPE_MPG;
144
145 else if (g_strcasecmp (suf, ".m3u") == 0) type = FILE_TYPE_M3U;
146 else if (g_strcasecmp (suf, ".pls") == 0) type = FILE_TYPE_PLS;
147 else if (g_strcasecmp (suf, ".oga") == 0) type = FILE_TYPE_OGG;
148 else if (g_strcasecmp (suf, ".ogg") == 0) type = FILE_TYPE_OGG;
149 else if (g_strcasecmp (suf, ".ogv") == 0) type = FILE_TYPE_OGG;
150 else if (g_strcasecmp (suf, ".ogx") == 0) type = FILE_TYPE_OGG;
151 else if (g_strcasecmp (suf, ".flac") == 0) type = FILE_TYPE_FLAC;
152
153 else
154 {
155 const gchar **extp=imageext;
156 while (*extp)
157 {
158 if (g_strcasecmp (suf, *extp) == 0)
159 {
160 type = FILE_TYPE_IMAGE;
161 break;
162 }
163 ++extp;
164 }
165 }
166 }
167
168 g_free(path_utf8);
169 return type;
170 }
171
172
173 /** check a filename against the "excludes file mask" from the preferences
174 * and return TRUE if it should be excluded based on the mask
175 */
excludefile(gchar * filename)176 static gboolean excludefile (gchar *filename)
177 {
178 gboolean matched = FALSE;
179 gchar **masks, *prefs_masks;
180
181 prefs_masks = prefs_get_string ("exclude_file_mask");
182
183 if (prefs_masks == NULL)
184 return FALSE;
185
186 masks = g_strsplit (prefs_masks, ";", -1);
187
188 if (masks != NULL)
189 {
190 gint i;
191 for(i=0; !matched && masks[i]; i++)
192 {
193 matched = g_pattern_match_simple (g_strstrip(masks[i]), filename);
194 }
195 g_strfreev (masks);
196 }
197
198 g_free(prefs_masks);
199
200 return matched;
201 }
202
203
204 /*------------------------------------------------------------------*\
205 * *
206 * Add Playlists *
207 * *
208 \*------------------------------------------------------------------*/
209
210
211 /* Add all files specified in playlist @plfile. Will create a new
212 * playlist with the name "basename (plfile)", even if one of the same
213 * name already exists (if @plitem is != NULL, all tracks will be added
214 * to @plitem and no new playlist will be created).
215 * It will then add all tracks listed in @plfile. If set in the prefs,
216 * duplicates will be detected (and the track already present in the
217 * database will be added to the playlist instead). */
218 /* @addtrackfunc: if != NULL this will be called instead of
219 "add_track_to_playlist () -- used for dropping tracks at a specific
220 position in the track view */
221 /* Return value: playlist to which the files were added to or NULL on
222 * error */
223 Playlist *
add_playlist_by_filename(iTunesDB * itdb,gchar * plfile,Playlist * plitem,gint plitem_pos,AddTrackFunc addtrackfunc,gpointer data)224 add_playlist_by_filename (iTunesDB *itdb, gchar *plfile,
225 Playlist *plitem, gint plitem_pos,
226 AddTrackFunc addtrackfunc, gpointer data)
227 {
228 gchar *bufp, *plfile_utf8;
229 gchar *dirname = NULL, *plname = NULL;
230 gchar buf[PATH_MAX];
231 FileType type = FILE_TYPE_UNKNOWN; /* type of playlist file */
232 gint line, tracks;
233 FILE *fp;
234 gboolean error;
235
236 g_return_val_if_fail (plfile, FALSE);
237 g_return_val_if_fail (itdb, FALSE);
238
239 if (g_file_test (plfile, G_FILE_TEST_IS_DIR))
240 {
241 gtkpod_warning (_("'%s' is a directory, not a playlist file.\n\n"),
242 plfile);
243 return FALSE; /* definitely not! */
244 }
245
246 plfile_utf8 = charset_to_utf8 (plfile);
247
248 plname = g_path_get_basename (plfile_utf8);
249 g_free (plfile_utf8);
250
251 bufp = g_strrstr (plname, "."); /* find last occurence of '.' */
252 if (bufp)
253 {
254 *bufp = 0; /* truncate playlist name */
255 type = determine_file_type(plfile);
256 switch (type)
257 {
258 case FILE_TYPE_MP3:
259 case FILE_TYPE_M4A:
260 case FILE_TYPE_M4P:
261 case FILE_TYPE_M4B:
262 case FILE_TYPE_WAV:
263 case FILE_TYPE_M4V:
264 case FILE_TYPE_MP4:
265 case FILE_TYPE_MOV:
266 case FILE_TYPE_MPG:
267 case FILE_TYPE_OGG:
268 case FILE_TYPE_FLAC:
269 case FILE_TYPE_IMAGE:
270 case FILE_TYPE_DIRECTORY:
271 gtkpod_warning (_("'%s' is a not a known playlist file.\n\n"),
272 plfile);
273 g_free(plname);
274 return FALSE;
275 case FILE_TYPE_M3U:
276 case FILE_TYPE_PLS:
277 break;
278 case FILE_TYPE_UNKNOWN:
279 /* assume MISC (M3U like) style */
280 type = -2;
281 break;
282 }
283 }
284
285 /* attempt to open playlist file */
286 if (!(fp = fopen (plfile, "r")))
287 {
288 gtkpod_warning (_("Could not open '%s' for reading.\n"), plfile);
289 g_free (plname);
290 return FALSE; /* definitely not! */
291 }
292 /* create playlist (if none is specified) */
293 if (!plitem) plitem = gp_playlist_add_new (itdb, plname,
294 FALSE, plitem_pos);
295 C_FREE (plname);
296 g_return_val_if_fail (plitem, NULL);
297
298 /* need dirname if playlist file contains relative paths */
299 dirname = g_path_get_dirname (plfile);
300
301 /* for now assume that all playlist files will be line-based
302 all of these are line based -- add different code for different
303 playlist files */
304 line = -1; /* nr of line being read */
305 tracks = 0; /* nr of tracks added */
306 error = FALSE;
307 while (!error && fgets (buf, PATH_MAX, fp))
308 {
309 gchar *bufp = buf;
310 gchar *filename = NULL;
311 gint len = strlen (bufp); /* remove newline */
312
313 ++line;
314 if (len == 0) continue; /* skip empty lines */
315 if (bufp[len-1] == 0x0a) bufp[len-1] = 0;
316 if (type == -2)
317 {
318 /* skip whitespace */
319 while (isspace (*bufp)) ++bufp;
320 /* assume comments start with ';' or '#' */
321 if ((*bufp == ';') || (*bufp == '#')) break;
322 /* assume the rest of the line is a filename */
323 filename = concat_dir_if_relative (dirname, bufp);
324 break;
325 }
326 else switch (type)
327 {
328 case FILE_TYPE_M3U:
329 /* comments start with '#' */
330 if (*bufp == '#') break;
331 /* assume the rest of the line is a filename */
332 filename = concat_dir_if_relative (dirname, bufp);
333 break;
334 case FILE_TYPE_PLS:
335 /* I don't know anything about pls playlist files and just
336 looked at one example produced by xmms -- please
337 correct the code if you know more */
338 if (line == 0)
339 { /* check for "[playlist]" */
340 if (strncasecmp (bufp, "[playlist]", 10) != 0)
341 error = TRUE;
342 }
343 else if (strncasecmp (bufp, "File", 4) == 0)
344 { /* looks like a file entry */
345 bufp = strchr (bufp, '=');
346 if (bufp)
347 {
348 ++bufp;
349 filename = concat_dir_if_relative (dirname, bufp);
350 }
351 }
352 break;
353 case FILE_TYPE_UNKNOWN:
354 case FILE_TYPE_DIRECTORY:
355 case FILE_TYPE_MP3:
356 case FILE_TYPE_M4A:
357 case FILE_TYPE_M4P:
358 case FILE_TYPE_M4B:
359 case FILE_TYPE_WAV:
360 case FILE_TYPE_M4V:
361 case FILE_TYPE_MP4:
362 case FILE_TYPE_MOV:
363 case FILE_TYPE_MPG:
364 case FILE_TYPE_OGG:
365 case FILE_TYPE_FLAC:
366 case FILE_TYPE_IMAGE:
367 break;
368 }
369 if (filename)
370 {
371 /* do not allow to add directories! */
372 if (g_file_test (filename, G_FILE_TEST_IS_DIR))
373 {
374 gtkpod_warning (_("Skipping '%s' because it is a directory.\n"), filename);
375 }
376 /* do not allow to add playlist file recursively */
377 else if (strcmp (plfile, filename) == 0)
378 {
379 gtkpod_warning (_("Skipping '%s' to avoid adding playlist file recursively\n"), filename);
380 }
381 else if (add_track_by_filename (itdb, filename, plitem,
382 prefs_get_int("add_recursively"),
383 addtrackfunc, data))
384 {
385 ++tracks;
386 }
387 g_free (filename);
388 }
389 }
390 fclose (fp);
391 C_FREE (dirname);
392
393 /* I don't think it's too interesting to pop up the list of
394 duplicates -- but we should reset the list. */
395 gp_duplicate_remove (NULL, (void *)-1);
396
397 if (!error) return plitem;
398 return NULL;
399 }
400
401
402
403 /*------------------------------------------------------------------*\
404 * *
405 * Add Dir *
406 * *
407 \*------------------------------------------------------------------*/
408
409 /* Add all files in directory and subdirectories.
410 If @name is a regular file, just add that.
411 If @plitem != NULL, add tracks also to Playlist @plitem
412 @descend: TRUE: add recursively
413 FALSE: don't enter subdirectories */
414 /* Not nice: the return value has not much meaning. TRUE: all files
415 * were added successfully. FALSE: some files could not be
416 added (e.g: duplicates) */
417 /* @addtrackfunc: if != NULL this will be called instead of
418 "add_track_to_playlist () -- used for dropping tracks at a specific
419 position in the track view */
add_directory_by_name(iTunesDB * itdb,gchar * name,Playlist * plitem,gboolean descend,AddTrackFunc addtrackfunc,gpointer data)420 gboolean add_directory_by_name (iTunesDB *itdb, gchar *name,
421 Playlist *plitem, gboolean descend,
422 AddTrackFunc addtrackfunc, gpointer data)
423 {
424 gboolean result = TRUE;
425
426 g_return_val_if_fail (itdb, FALSE);
427 g_return_val_if_fail (name, FALSE);
428
429 if (g_file_test (name, G_FILE_TEST_IS_DIR))
430 {
431 GDir *dir = g_dir_open (name, 0, NULL);
432 block_widgets ();
433 if (dir != NULL) {
434 G_CONST_RETURN gchar *next;
435 do {
436 next = g_dir_read_name (dir);
437 if (next != NULL)
438 {
439 gchar *nextfull = g_build_filename (name, next, NULL);
440 if (descend ||
441 !g_file_test (nextfull, G_FILE_TEST_IS_DIR))
442 result &= add_directory_by_name (
443 itdb, nextfull, plitem,
444 descend, addtrackfunc, data);
445 g_free (nextfull);
446 }
447 } while (next != NULL);
448 g_dir_close (dir);
449 }
450 release_widgets ();
451 }
452 else
453 {
454 result = add_track_by_filename (itdb, name, plitem,
455 descend, addtrackfunc, data);
456 }
457 return result;
458 }
459
460
461
462 /*------------------------------------------------------------------*\
463 * *
464 * Fill in track struct with data from file *
465 * *
466 \*------------------------------------------------------------------*/
467
468 /* parse the file with name @filename (UTF8) and fill extract the tags
469 * to @trackas specified in @template. @track can be NULL if you just
470 * want to verify the template */
parse_filename_with_template(Track * track,gchar * filename,const gchar * template)471 static gboolean parse_filename_with_template (Track *track,
472 gchar *filename,
473 const gchar *template)
474 {
475 GList *tokens=NULL, *gl;
476 gchar *tpl, *fn;
477 gchar *sps, *sp, *str;
478 gboolean parse_error = FALSE;
479
480 if (!template || !filename) return FALSE;
481
482 #ifdef DEBUG
483 printf ("fn: '%s', tpl: '%s'\n", filename, template);
484 #endif
485
486 /* If the template starts with a '%.' (except for '%%') we add a
487 '/' in front so that the field has a boundary */
488 if ((template[0] == '%') && (template[1] != '%'))
489 tpl = g_strdup_printf ("%c%s", G_DIR_SEPARATOR, template);
490 else tpl = g_strdup (template);
491
492 fn = g_strdup (filename);
493
494 /* We split the template into tokens. Each token starting with a
495 '%' and two chars long specifies a field ('%.'), otherwise it's
496 normal text (used to separate two fields).
497 "/%a - %t.mp3" would become ".mp3" "%t" " - " "%a" "/" */
498 sps = tpl;
499 while ((sp = strchr (sps, '%')))
500 {
501 if (sps != sp)
502 tokens = g_list_prepend (tokens, g_strndup (sps, sp-sps));
503 if (sp[1] != '%')
504 {
505 tokens = g_list_prepend (tokens, g_strndup (sp, 2));
506 }
507 else
508 {
509 tokens = g_list_prepend (tokens, g_strdup ("%"));
510 }
511 if (!sp[1]) break;
512 sps = sp+2;
513 }
514 /* add what's left */
515 if (sps[0] != 0)
516 tokens = g_list_prepend (tokens, g_strdup (sps));
517
518 /* If the "last" token does not contain a '.' (like in ".mp3"),
519 remove the filename extension ("somefile.mp3" -> "somefile")
520 because no extension was given in the template */
521 str = g_list_nth_data (tokens, 0);
522 if (str && (strchr (str, '.') == NULL))
523 {
524 gchar *str = strrchr (fn, '.');
525 if (str) str[0] = 0;
526 }
527
528 #ifdef DEBUG
529 puts (tpl);
530 for (gl=tokens; gl; gl=gl->next)
531 puts (gl->data);
532 puts (fn);
533 #endif
534
535 /* use the tokens to parse the filename */
536 gl = tokens;
537 while (gl)
538 {
539 gchar *token = gl->data;
540 /* remember: all tokens starting with '%' and two chars long
541 specify a field */
542 if ((token[0] == '%') && (strlen (token) == 2))
543 { /* handle tag item */
544 GList *gln = gl->next;
545 if (gln)
546 {
547 gchar *itm;
548 gchar *next_token = gln->data;
549 /* find next token so we can determine where the
550 current field ends */
551 gchar *fnp = g_strrstr (fn, next_token);
552
553 if (!fnp)
554 {
555 parse_error = TRUE;
556 break;
557 }
558
559 /* truncate the filename (for the next token) */
560 fnp[0] = 0;
561 /* adjust fnp to point to the start of the field */
562 fnp = fnp + strlen (next_token);
563 #ifdef DEBUG
564 printf ("%s: '%s'\n", token, fnp);
565 #endif
566 itm = g_strstrip (g_strdup (fnp));
567 switch (token[1])
568 {
569 case 'a': /* artist */
570 if (track &&
571 (!track->artist || prefs_get_int("parsetags_overwrite")))
572 {
573 g_free (track->artist);
574 track->artist = itm;
575 }
576 break;
577 case 'A': /* album */
578 if (track &&
579 (!track->album || prefs_get_int("parsetags_overwrite")))
580 {
581 g_free (track->album);
582 track->album = itm;
583 }
584 break;
585 case 'c': /* composer */
586 if (track &&
587 (!track->composer || prefs_get_int("parsetags_overwrite")))
588 {
589 g_free (track->composer);
590 track->composer = itm;
591 }
592 break;
593 case 't': /* title */
594 if (track &&
595 (!track->title || prefs_get_int("parsetags_overwrite")))
596 {
597 g_free (track->title);
598 track->title = itm;
599 }
600 break;
601 case 'g': /* genre */
602 case 'G': /* genre */
603 if (track &&
604 (!track->genre || prefs_get_int("parsetags_overwrite")))
605 {
606 g_free (track->genre);
607 track->genre = itm;
608 }
609 break;
610 case 'T': /* track */
611 if (track &&
612 ((track->track_nr == 0) ||
613 prefs_get_int("parsetags_overwrite")))
614 track->track_nr = atoi (itm);
615 g_free (itm);
616 break;
617 case 'C': /* CD */
618 if (track &&
619 (track->cd_nr == 0 || prefs_get_int("parsetags_overwrite")))
620 track->cd_nr = atoi (itm);
621 g_free (itm);
622 break;
623 case 'Y': /* year */
624 if (track &&
625 (track->year == 0 || prefs_get_int("parsetags_overwrite")))
626 track->year = atoi (itm);
627 g_free (itm);
628 break;
629 case '*': /* placeholder to skip a field */
630 g_free (itm);
631 break;
632 default:
633 g_free (itm);
634 gtkpod_warning (_("Unknown token '%s' in template '%s'\n"),
635 token, template);
636 parse_error = TRUE;
637 break;
638 }
639 if (parse_error) break;
640 gl = gln->next;
641 }
642 else
643 { /* if (gln)...else... */
644 break;
645 }
646 }
647 else
648 { /* skip text */
649 gchar *fnp = g_strrstr (fn, token);
650 if (!fnp)
651 {
652 parse_error = TRUE;
653 break; /* could not match */
654 }
655 if (fnp - fn + strlen (fnp) != strlen (fn))
656 {
657 parse_error = TRUE;
658 break; /* not at the last position */
659 }
660 fnp[0] = 0;
661 gl = gl->next;
662 }
663 }
664
665 g_free (fn);
666 g_free (tpl);
667 g_list_foreach (tokens, (GFunc)g_free, NULL);
668 g_list_free (tokens);
669 return (!parse_error);
670 }
671
672 /* parse the filename of @track and extract the tags as specified in
673 prefs_get_string("parsetags_template"). Several templates can be separated
674 with the "," character. */
parse_filename(Track * track)675 static void parse_filename (Track *track)
676 {
677 ExtraTrackData *etr;
678 gchar *template;
679 gchar **templates, **tplp;
680
681 g_return_if_fail (track);
682 etr = track->userdata;
683 g_return_if_fail (etr);
684
685 template = prefs_get_string("parsetags_template");
686 if (!template) return;
687 templates = g_strsplit (template, ";", 0);
688 tplp = templates;
689 while (*tplp)
690 {
691 if (parse_filename_with_template (NULL, etr->pc_path_utf8, *tplp))
692 break;
693 ++tplp;
694 }
695 if (*tplp) parse_filename_with_template (track, etr->pc_path_utf8, *tplp);
696 g_strfreev (templates);
697 g_free(template);
698 }
699
700 /* Set entry "column" (TM_COLUMN_TITLE etc) according to filename */
701 /* TODO: make the TAG extraction more intelligent -- if possible, this
702 should be user configurable. */
set_entry_from_filename(Track * track,gint column)703 static void set_entry_from_filename (Track *track, gint column)
704 {
705 ExtraTrackData *etr;
706
707 g_return_if_fail (track);
708 etr = track->userdata;
709 g_return_if_fail (etr);
710
711 if (prefs_get_int_index("tag_autoset", column) &&
712 etr->pc_path_utf8 && strlen (etr->pc_path_utf8))
713 {
714 switch (column)
715 {
716 case TM_COLUMN_TITLE:
717 g_free (track->title);
718 track->title = g_path_get_basename (etr->pc_path_utf8);
719 break;
720 case TM_COLUMN_ALBUM:
721 g_free (track->album);
722 track->album = g_path_get_basename (etr->pc_path_utf8);
723 break;
724 case TM_COLUMN_ARTIST:
725 g_free (track->artist);
726 track->artist = g_path_get_basename (etr->pc_path_utf8);
727 break;
728 case TM_COLUMN_GENRE:
729 g_free (track->genre);
730 track->genre = g_path_get_basename (etr->pc_path_utf8);
731 break;
732 case TM_COLUMN_COMPOSER:
733 g_free (track->composer);
734 track->composer = g_path_get_basename (etr->pc_path_utf8);
735 break;
736 }
737 }
738 }
739
740
set_unset_entries_from_filename(Track * track)741 static void set_unset_entries_from_filename (Track *track)
742 {
743 /* try to fill tags from filename */
744 if (prefs_get_int("parsetags"))
745 parse_filename (track);
746 /* fill up what is left unset */
747 if (!track->album && prefs_get_int_index("tag_autoset", TM_COLUMN_ALBUM))
748 set_entry_from_filename (track, TM_COLUMN_ALBUM);
749 if (!track->artist && prefs_get_int_index("tag_autoset", TM_COLUMN_ARTIST))
750 set_entry_from_filename (track, TM_COLUMN_ARTIST);
751 if (!track->title && prefs_get_int_index("tag_autoset", TM_COLUMN_TITLE))
752 set_entry_from_filename (track, TM_COLUMN_TITLE);
753 if (!track->genre && prefs_get_int_index("tag_autoset", TM_COLUMN_GENRE))
754 set_entry_from_filename (track, TM_COLUMN_GENRE);
755 if (!track->composer && prefs_get_int_index("tag_autoset",
756 TM_COLUMN_COMPOSER))
757 {
758 set_entry_from_filename (track, TM_COLUMN_COMPOSER);
759 }
760 }
761
762
763 /* update the track->charset info with the currently used charset */
update_charset_info(Track * track)764 void update_charset_info (Track *track)
765 {
766 const gchar *charset = prefs_get_string("charset");
767 ExtraTrackData *etr;
768
769 g_return_if_fail (track);
770 etr = track->userdata;
771 g_return_if_fail (etr);
772
773 C_FREE (etr->charset);
774 if (!charset || !strlen (charset))
775 { /* use standard locale charset */
776 g_get_charset (&charset);
777 }
778 /* only set charset if it's not GTKPOD_JAPAN_AUTOMATIC */
779 if (charset && (strcmp (charset, GTKPOD_JAPAN_AUTOMATIC) != 0))
780 {
781 etr->charset = g_strdup (charset);
782 }
783 }
784
785
786 /* Copy "new" info read from file to an old Track structure. */
787 /* Return value: TRUE: at least one item was changed. FALSE: track was
788 unchanged */
copy_new_info(Track * from,Track * to)789 static gboolean copy_new_info (Track *from, Track *to)
790 {
791 ExtraTrackData *efrom, *eto;
792 T_item item;
793 gboolean changed = FALSE;
794
795 g_return_val_if_fail (from, FALSE);
796 g_return_val_if_fail (to, FALSE);
797 efrom = from->userdata;
798 eto = to->userdata;
799 g_return_val_if_fail (efrom, FALSE);
800 g_return_val_if_fail (eto, FALSE);
801
802 for (item=0; item<T_ITEM_NUM; ++item)
803 {
804 switch (item)
805 {
806 case T_ALBUM:
807 case T_ARTIST:
808 case T_TITLE:
809 case T_GENRE:
810 case T_COMMENT:
811 case T_COMPOSER:
812 case T_FILETYPE:
813 case T_DESCRIPTION:
814 case T_PODCASTURL:
815 case T_PODCASTRSS:
816 case T_SUBTITLE:
817 case T_TV_SHOW:
818 case T_TV_EPISODE:
819 case T_TV_NETWORK:
820 case T_THUMB_PATH:
821 case T_PC_PATH:
822 case T_ALBUMARTIST:
823 case T_SORT_ARTIST:
824 case T_SORT_TITLE:
825 case T_SORT_ALBUM:
826 case T_SORT_ALBUMARTIST:
827 case T_SORT_COMPOSER:
828 case T_SORT_TVSHOW:
829 case T_YEAR:
830 case T_TRACK_NR:
831 case T_TRACKLEN:
832 case T_STARTTIME:
833 case T_STOPTIME:
834 case T_SIZE:
835 case T_BITRATE:
836 case T_SAMPLERATE:
837 case T_BPM:
838 case T_TIME_ADDED:
839 case T_TIME_MODIFIED:
840 case T_TIME_RELEASED:
841 case T_SOUNDCHECK:
842 case T_CD_NR:
843 case T_COMPILATION:
844 case T_MEDIA_TYPE:
845 case T_SEASON_NR:
846 case T_EPISODE_NR:
847 case T_GROUPING:
848 case T_LYRICS:
849 changed |= track_copy_item (from, to, item);
850 break;
851 case T_CATEGORY:
852 /* not implemented from tags */
853 break;
854 case T_RATING:
855 case T_REMEMBER_PLAYBACK_POSITION:
856 case T_SKIP_WHEN_SHUFFLING:
857 case T_CHECKED:
858 case T_TIME_PLAYED:
859 case T_IPOD_PATH:
860 case T_ALL:
861 case T_IPOD_ID:
862 case T_TRANSFERRED:
863 case T_PLAYCOUNT:
864 case T_VOLUME:
865 case T_GAPLESS_TRACK_FLAG:
866 /* not applicable */
867 break;
868 case T_ITEM_NUM:
869 g_return_val_if_reached (FALSE);
870 }
871 }
872
873 if ((eto->charset == NULL) || (strcmp (efrom->charset, eto->charset) != 0))
874 {
875 g_free (eto->charset);
876 eto->charset = g_strdup (efrom->charset);
877 changed = TRUE;
878 }
879
880 if (from->chapterdata)
881 {
882 itdb_chapterdata_free (to->chapterdata);
883 to->chapterdata = itdb_chapterdata_duplicate (from->chapterdata);
884 changed = TRUE; /* FIXME: probably should actually compare chapters for changes */
885 }
886
887 itdb_artwork_free (to->artwork);
888 to->artwork = itdb_artwork_duplicate (from->artwork);
889 if ((to->artwork_size != from->artwork_size) ||
890 (to->artwork_count != from->artwork_count) ||
891 (to->has_artwork != from->has_artwork))
892 { /* FIXME -- artwork might have changed nevertheless */
893 changed = TRUE;
894 to->artwork_size = from->artwork_size;
895 to->artwork_count = from->artwork_count;
896 to->has_artwork = from->has_artwork;
897 }
898
899 if ((to->lyrics_flag != from->lyrics_flag) ||
900 (to->movie_flag != from->movie_flag))
901 {
902 changed = TRUE;
903 to->lyrics_flag = from->lyrics_flag;
904 to->movie_flag = from->movie_flag;
905 }
906
907 if ((to->pregap != from->pregap) ||
908 (to->postgap != from->postgap) ||
909 (to->samplecount != from->samplecount) ||
910 (to->gapless_data != from->gapless_data) ||
911 (to->gapless_track_flag != from->gapless_track_flag))
912 {
913 changed = TRUE;
914 to->pregap = from->pregap;
915 to->postgap = from->postgap;
916 to->samplecount = from->samplecount;
917 to->gapless_data = from->gapless_data;
918 to->gapless_track_flag = from->gapless_track_flag;
919 }
920
921 if (eto->mtime != efrom->mtime)
922 {
923 changed = TRUE;
924 eto->mtime = efrom->mtime;
925 }
926
927 return changed;
928 }
929
930 /* Updates mserv data (rating only) of @track using filename @name to
931 look up mserv information.
932 Return TRUE if successfully updated (or disabled), FALSE if not */
update_mserv_data_from_file(gchar * name,Track * track)933 gboolean update_mserv_data_from_file (gchar *name, Track *track)
934 {
935 gboolean success=TRUE;
936
937 if (!name || !track) return FALSE;
938
939 if (g_file_test (name, G_FILE_TEST_IS_DIR)) return FALSE;
940 if (!g_file_test (name, G_FILE_TEST_EXISTS))
941 {
942 gchar *buf = g_strdup_printf (_("Local filename not valid (%s)"), name);
943 display_mserv_problems (track, buf);
944 g_free (buf);
945 return FALSE;
946 }
947
948 if (prefs_get_int("mserv_use"))
949 {
950 /* try getting the user's rating from the mserv db */
951 gchar *music_root = prefs_get_string ("path_mserv_music_root");
952 gchar *trackinfo_root = prefs_get_string ("path_mserv_trackinfo_root");
953
954 /* we expect music_root and trackinfo_root to be initialized */
955 if (!music_root)
956 music_root = g_strdup ("");
957 if (!trackinfo_root)
958 trackinfo_root = g_strdup ("");
959
960 success = FALSE;
961 /* printf("mroot %s troot %s fname %s\n", music_root, trackinfo_root, name); */
962
963 /* first, check if the file is in the mserv music directory */
964 if (*music_root == 0 || strstr(name, music_root))
965 {
966 gchar *infoname = g_strdup_printf ("%s%c%s.trk",
967 trackinfo_root,
968 G_DIR_SEPARATOR,
969 &(name[strlen(music_root)]));
970 /* printf("trying %s\n", infoname); */
971 FILE *fp = fopen (infoname, "r");
972 if (fp)
973 {
974 /* printf("opened\n");*/
975 gchar buff[PATH_MAX];
976 gchar *username = prefs_get_string("mserv_username");
977 guint usernamelen;
978 g_return_val_if_fail (username, (fclose (fp), FALSE));
979 usernamelen = strlen (username);
980 while (fgets(buff, PATH_MAX, fp))
981 {
982 /* printf("username %s (%d) read %s\n",
983 * prefs_get_string("mserv_username"), usernamelen,
984 * buff);*/
985 if (strncmp(buff, username, usernamelen) == 0
986 && buff[usernamelen] == (gchar)'=')
987 {
988 /* found it */
989 track->rating = atoi(&buff[usernamelen+1]) * ITDB_RATING_STEP;
990 /* printf("found it, = %d\n",
991 orig_track->rating/ITDB_RATING_STEP); */
992 success = TRUE;
993 break; /* while(fgets(... */
994 }
995 }
996 fclose(fp);
997 g_free(username);
998 if (!success)
999 {
1000 gchar *username = prefs_get_string("mserv_username");
1001 gchar *buf = g_strdup_printf (_("No information found for user '%s' in '%s'"),
1002 username, infoname);
1003 display_mserv_problems (track, buf);
1004 g_free (buf);
1005 g_free(username);
1006 }
1007 }
1008 else
1009 {
1010 gchar *buf = g_strdup_printf (_("mserv data file (%s) not available for track (%s)"), infoname, name);
1011 display_mserv_problems (track, buf);
1012 g_free (buf);
1013 }
1014 g_free (infoname);
1015 }
1016 else
1017 {
1018 gchar *buf = g_strdup_printf (_("Track (%s) not in mserv music root directory (%s)"), name, music_root);
1019 display_mserv_problems (track, buf);
1020 g_free (buf);
1021 }
1022 g_free (music_root);
1023 g_free (trackinfo_root);
1024 }
1025
1026 while (widgets_blocked && gtk_events_pending ())
1027 gtk_main_iteration ();
1028
1029 return success;
1030 }
1031
1032 /* ensure that a cache directory is available for video thumbnail generation.
1033 * largely copied from conversion_setup_cachedir, only slightly simplified
1034 * and modified to return the path to the caller for convenience.
1035 */
video_thumbnail_setup_cache()1036 static gchar* video_thumbnail_setup_cache()
1037 {
1038 gchar *cachedir = NULL;
1039 gchar *cfgdir = prefs_get_cfgdir();
1040
1041 if (!cfgdir)
1042 return NULL;
1043
1044 cachedir = g_build_filename (cfgdir, "video_thumbnail_cache", NULL);
1045 g_free (cfgdir);
1046
1047 if (!g_file_test (cachedir, G_FILE_TEST_IS_DIR) && (g_mkdir (cachedir, 0777) == -1))
1048 {
1049 gtkpod_warning (_("Could not create '%s'"), cachedir);
1050 g_free (cachedir);
1051 cachedir = NULL;
1052 }
1053
1054 return cachedir;
1055 }
1056
1057 /*
1058 * automatically generate a thumbnail for video input files using
1059 * an external program (the totem thumbnailer by default).
1060 * @input is the path of the video file to be thumbnailed.
1061 * Returns a path to a temporary file containing the thumbnail. The
1062 * temporary file is stored in a dedicated cache directory.
1063 */
create_video_thumbnail(gchar * input)1064 static gchar* create_video_thumbnail (gchar* input)
1065 {
1066 GString *cmd = NULL;
1067 GError *err = NULL;
1068 int fd, retval, forkstatus;
1069 gchar *thumbnailer = NULL, *tmp_fn = NULL, *tmp = NULL, *p = NULL;
1070 /* find (and set up if necessary) the thumbnail cache dir */
1071 gchar *tdir = video_thumbnail_setup_cache();
1072
1073 if (!tdir)
1074 {
1075 return NULL;
1076 }
1077
1078 /* safely generate a temporary file. we don't actually need the fd
1079 * so we close it on succesful generation */
1080 tmp_fn = g_build_filename (tdir, "thumb.XXXXXX", NULL);
1081 g_free(tdir);
1082 tdir = NULL;
1083
1084 if ((fd = g_mkstemp(tmp_fn)) == -1 || close(fd))
1085 {
1086 gtkpod_warning (_("Error creating thumbnail file"));
1087 g_free (tmp_fn);
1088 return NULL;
1089 }
1090
1091 /* get the string containing the (template) command for thumbnailing */
1092 thumbnailer = prefs_get_string("video_thumbnailer_prog");
1093 /* copy it into cmd, expanding any format characters */
1094 cmd = g_string_new("");
1095 p = thumbnailer;
1096
1097 while (*p)
1098 {
1099 if (*p == '%')
1100 {
1101 ++p;
1102
1103 switch (*p)
1104 {
1105 /* %f: input file */
1106 case 'f':
1107 tmp = g_shell_quote(input);
1108 break;
1109 /* %o: temporary output file */
1110 case 'o':
1111 tmp = g_shell_quote(tmp_fn);
1112 break;
1113 case '%':
1114 cmd = g_string_append_c (cmd, '%');
1115 break;
1116 default:
1117 gtkpod_warning (_("Unknown token '%%%c' in template '%s'"),
1118 *p, thumbnailer);
1119 break;
1120 }
1121
1122 if (tmp)
1123 {
1124 cmd = g_string_append (cmd, tmp);
1125 g_free (tmp);
1126 tmp = NULL;
1127 }
1128 }
1129 else
1130 {
1131 cmd = g_string_append_c (cmd, *p);
1132 }
1133
1134 ++p;
1135 }
1136 /* run the thumbnailing program */
1137 forkstatus = g_spawn_command_line_sync(cmd->str, NULL, NULL, &retval, &err);
1138
1139 if(!forkstatus)
1140 {
1141 gtkpod_warning (_("Unable to start video thumbnail generator\n(command line was: '%s')"), cmd->str);
1142 }
1143 else if(retval)
1144 {
1145 gtkpod_warning (_("Thumbnail generator returned status %d"), retval);
1146 }
1147
1148 g_string_free(cmd, TRUE);
1149 /* thumbnail is in tmp_fn */
1150 return tmp_fn;
1151 }
1152
1153 /* look for a picture specified by coverart_template */
add_coverart(Track * tr)1154 static void add_coverart (Track *tr)
1155 {
1156 ExtraTrackData *etr;
1157 gchar *full_template;
1158 gchar **templates, **tplp;
1159 gchar *dirname;
1160 gchar *filename_local = NULL;
1161 FileType type;
1162 gint vid_thumbnailer;
1163
1164 g_return_if_fail (tr);
1165 etr = tr->userdata;
1166 g_return_if_fail (etr);
1167
1168 dirname = g_path_get_dirname (etr->pc_path_utf8);
1169 type = determine_file_type(etr->pc_path_utf8);
1170
1171 full_template = prefs_get_string("coverart_template");
1172 vid_thumbnailer = prefs_get_int("video_thumbnailer");
1173
1174 templates = g_strsplit (full_template, ";", 0);
1175 tplp = templates;
1176 while (*tplp && !filename_local)
1177 {
1178 gchar *filename_utf8;
1179 gchar *fname = get_string_from_template (tr, *tplp, FALSE, FALSE);
1180 if (fname)
1181 {
1182 if (strchr (*tplp, '.') != NULL)
1183 { /* if template has an extension, try if it is valid */
1184 if (fname[0] == '/') /* allow absolute paths in template */
1185 filename_utf8 = g_build_filename ("", fname, NULL);
1186 else
1187 filename_utf8 = g_build_filename (dirname, fname, NULL);
1188 filename_local = charset_from_utf8 (filename_utf8);
1189 g_free (filename_utf8);
1190 if (!g_file_test (filename_local, G_FILE_TEST_EXISTS))
1191 {
1192 g_free (filename_local);
1193 filename_local = NULL;
1194 }
1195 }
1196 if (!filename_local)
1197 { /* if no filename is found try out different extensions */
1198 const gchar **extp = imageext;
1199 while (*extp && !filename_local)
1200 {
1201 gchar *ffname = g_strconcat (fname, *extp, NULL);
1202 if (ffname[0] == '/') /* allow absolute paths in template */
1203 filename_utf8 = g_build_filename ("", ffname, NULL);
1204 else
1205 filename_utf8 = g_build_filename (dirname, ffname,
1206 NULL);
1207 g_free (ffname);
1208 filename_local = charset_from_utf8 (filename_utf8);
1209 g_free (filename_utf8);
1210 if (!g_file_test (filename_local, G_FILE_TEST_EXISTS))
1211 {
1212 g_free (filename_local);
1213 filename_local = NULL;
1214 }
1215 ++extp;
1216 }
1217 extp = imageext;
1218 while (*extp && !filename_local)
1219 { /* try uppercase version of extension */
1220 gchar *upper_ext = g_ascii_strup (*extp, -1);
1221 gchar *ffname = g_strconcat (fname, upper_ext, NULL);
1222 g_free (upper_ext);
1223 if (ffname[0] == '/') /* allow absolute paths in template */
1224 filename_utf8 = g_build_filename ("", ffname, NULL);
1225 else
1226 filename_utf8 = g_build_filename (dirname, ffname,
1227 NULL);
1228 g_free (ffname);
1229 filename_local = charset_from_utf8 (filename_utf8);
1230 g_free (filename_utf8);
1231 if (!g_file_test (filename_local, G_FILE_TEST_EXISTS))
1232 {
1233 g_free (filename_local);
1234 filename_local = NULL;
1235 }
1236 ++extp;
1237 }
1238 }
1239 }
1240 g_free (fname);
1241 ++tplp;
1242 }
1243
1244 if (filename_local)
1245 {
1246 gp_track_set_thumbnails (tr, filename_local);
1247 }
1248 else if(vid_thumbnailer)
1249 {
1250 /* if a template match was not made, and we're dealing with a video
1251 * file, generate an arbitrary thumbnail if appropriate */
1252 switch(type)
1253 {
1254 case FILE_TYPE_M4V:
1255 case FILE_TYPE_MP4:
1256 case FILE_TYPE_MOV:
1257 case FILE_TYPE_MPG:
1258 filename_local = create_video_thumbnail (etr->pc_path_utf8);
1259 gp_track_set_thumbnails (tr, filename_local);
1260 break;
1261 default:
1262 break;
1263 }
1264 }
1265
1266 g_strfreev (templates);
1267 g_free (full_template);
1268 g_free (dirname);
1269 }
1270
1271
1272
1273 /* Fills the supplied @orig_track with data from the file @name. If
1274 * @orig_track is NULL, a new track struct is created. The entries
1275 * pc_path_utf8 and pc_path_locale are not changed if an entry already
1276 * exists. time_added is not modified if already set. */
1277 /* Returns NULL on error, a pointer to the Track otherwise */
get_track_info_from_file(gchar * name,Track * orig_track)1278 static Track *get_track_info_from_file (gchar *name, Track *orig_track)
1279 {
1280 Track *track = NULL;
1281 Track *nti = NULL;
1282 FileType filetype;
1283 gint len;
1284 gchar *name_utf8 = NULL;
1285
1286 g_return_val_if_fail (name, NULL);
1287
1288 if (g_file_test (name, G_FILE_TEST_IS_DIR)) return NULL;
1289
1290 name_utf8 = charset_to_utf8 (name);
1291
1292 if (!g_file_test (name, G_FILE_TEST_EXISTS))
1293 {
1294 gtkpod_warning (_("The following track could not be processed (file does not exist): '%s'\n"), name_utf8);
1295 g_free (name_utf8);
1296 return NULL;
1297 }
1298
1299 /* reset the auto detection charset (see explanation in charset.c) */
1300 charset_reset_auto ();
1301
1302 /* check for filetype */
1303 len = strlen (name);
1304 if (len < 4) return NULL;
1305
1306 filetype = determine_file_type(name);
1307 switch (filetype)
1308 {
1309 case FILE_TYPE_MP3:
1310 nti = mp3_get_file_info (name);
1311 /* Set mediatype to audio */
1312 if (nti)
1313 {
1314 nti->mediatype = ITDB_MEDIATYPE_AUDIO;
1315 if (nti->genre)
1316 {
1317 if (g_strcasecmp (nti->genre, "audiobook") == 0) nti->mediatype = ITDB_MEDIATYPE_AUDIOBOOK;
1318 else if (g_strcasecmp (nti->genre, "podcast") == 0) nti->mediatype = ITDB_MEDIATYPE_PODCAST;
1319 }
1320 }
1321 break;
1322 case FILE_TYPE_M4A:
1323 case FILE_TYPE_M4P:
1324 nti = mp4_get_file_info (name);
1325 /* Set mediatype to audio */
1326 if (nti && !nti->mediatype)
1327 {
1328 nti->mediatype = ITDB_MEDIATYPE_AUDIO;
1329 }
1330 break;
1331 case FILE_TYPE_M4B:
1332 nti = mp4_get_file_info (name);
1333 /* Set mediatype to audiobook */
1334 if (nti)
1335 {
1336 nti->mediatype = ITDB_MEDIATYPE_AUDIOBOOK;
1337 }
1338 break;
1339 case FILE_TYPE_WAV:
1340 nti = wav_get_file_info (name);
1341 /* Set mediatype to audio */
1342 if (nti)
1343 {
1344 nti->mediatype = ITDB_MEDIATYPE_AUDIO;
1345 }
1346 break;
1347 case FILE_TYPE_OGG:
1348 nti = ogg_get_file_info (name);
1349 /* Set mediatype to audio */
1350 if (nti)
1351 {
1352 nti->mediatype = ITDB_MEDIATYPE_AUDIO;
1353 }
1354 break;
1355 case FILE_TYPE_FLAC:
1356 nti = flac_get_file_info (name);
1357 /* Set mediatype to audio */
1358 if (nti)
1359 {
1360 nti->mediatype = ITDB_MEDIATYPE_AUDIO;
1361 }
1362 break;
1363 case FILE_TYPE_M4V:
1364 case FILE_TYPE_MP4:
1365 /* I don't know if .m4v and .mp4 can simply be handled like
1366 this. Let's see if someone complains. */
1367 nti = mp4_get_file_info (name);
1368 if (!nti) video_get_file_info (name);
1369 /* Set mediatype to video */
1370 if (nti)
1371 {
1372 if (!nti->mediatype)
1373 nti->mediatype = ITDB_MEDIATYPE_MOVIE;
1374 nti->movie_flag = 0x01;
1375 }
1376 break;
1377 case FILE_TYPE_MOV:
1378 case FILE_TYPE_MPG:
1379 /* for now treat all the same */
1380 nti = video_get_file_info (name);
1381 /* Set mediatype to video */
1382 if (nti)
1383 {
1384 nti->mediatype = ITDB_MEDIATYPE_MOVIE;
1385 nti->movie_flag = 0x01;
1386 }
1387 break;
1388 case FILE_TYPE_UNKNOWN:
1389 gtkpod_warning (_("The following track could not be processed (filetype unknown): '%s'\n"), name_utf8);
1390 g_free (name_utf8);
1391 return NULL;
1392 case FILE_TYPE_IMAGE:
1393 case FILE_TYPE_DIRECTORY:
1394 case FILE_TYPE_M3U:
1395 case FILE_TYPE_PLS:
1396 break;
1397 }
1398
1399 if (nti)
1400 {
1401 switch (nti->mediatype)
1402 {
1403 case ITDB_MEDIATYPE_AUDIOBOOK:
1404 case ITDB_MEDIATYPE_PODCAST:
1405 case ITDB_MEDIATYPE_PODCAST|ITDB_MEDIATYPE_MOVIE: /* Video podcast */
1406 /* For audiobooks and podcasts, default to remember playback position
1407 * and skip when shuffling. */
1408 nti->skip_when_shuffling = 1;
1409 nti->remember_playback_position = 1;
1410 break;
1411 }
1412 }
1413
1414 if (nti)
1415 {
1416 ExtraTrackData *enti=nti->userdata;
1417 struct stat filestat;
1418
1419 g_return_val_if_fail (enti, NULL);
1420
1421 if (enti->charset == NULL)
1422 { /* Fill in currently used charset. Try if auto_charset is
1423 * set first. If not, use the currently set charset. */
1424 enti->charset = charset_get_auto ();
1425 if (enti->charset == NULL)
1426 update_charset_info (nti);
1427 }
1428 /* set path file information */
1429 enti->pc_path_utf8 = charset_to_utf8 (name);
1430 enti->pc_path_locale = g_strdup (name);
1431 enti->lyrics=NULL;
1432 /* set length of file */
1433 stat (name, &filestat);
1434 nti->size = filestat.st_size; /* get the filesize in bytes */
1435 enti->mtime = filestat.st_mtime; /* get the modification date */
1436 if (nti->bitrate == 0)
1437 { /* estimate bitrate */
1438 if (nti->tracklen)
1439 nti->bitrate = nti->size * 8 / nti->tracklen;
1440 }
1441 /* Set unset strings (album...) from filename */
1442 set_unset_entries_from_filename (nti);
1443
1444 /* Set coverart */
1445 if (prefs_get_int("coverart_file"))
1446 {
1447 /* APIC data takes precedence */
1448 if (! itdb_track_has_thumbnails (nti))
1449 add_coverart (nti);
1450 }
1451
1452 /* Set modification date to the files modified date */
1453 nti->time_modified = enti->mtime;
1454 /* Set added date to *now* (unless orig_track is present) */
1455 if (orig_track)
1456 {
1457 nti->time_added = orig_track->time_added;
1458 }
1459 else
1460 {
1461 nti->time_added = time (NULL);
1462 }
1463
1464 /* Make sure all strings are initialized -- that way we don't
1465 have to worry about it when we are handling the
1466 strings. Also, validate_entries() will fill in the utf16
1467 strings if that hasn't already been done. */
1468 /* exception: sha1_hash, charset and hostname: these may be
1469 * NULL. */
1470
1471 gp_track_validate_entries (nti);
1472
1473 if (orig_track)
1474 { /* we need to copy all information over to the original
1475 * track */
1476 ExtraTrackData *eorigtr=orig_track->userdata;
1477
1478 g_return_val_if_fail (eorigtr, NULL);
1479
1480 eorigtr->tchanged = copy_new_info (nti, orig_track);
1481
1482 track = orig_track;
1483 itdb_track_free (nti);
1484 nti = NULL;
1485 }
1486 else
1487 { /* just use nti */
1488 track = nti;
1489 nti = NULL;
1490 }
1491
1492 update_mserv_data_from_file (name, track);
1493 }
1494 else
1495 {
1496 switch (filetype)
1497 {
1498 case FILE_TYPE_IMAGE:
1499 case FILE_TYPE_M3U:
1500 case FILE_TYPE_PLS:
1501 break;
1502 default:
1503 gtkpod_warning (_("The following track could not be processed (filetype is known but analysis failed): '%s'\n"), name_utf8);
1504 break;
1505 }
1506 }
1507
1508 while (widgets_blocked && gtk_events_pending ())
1509 gtk_main_iteration ();
1510
1511 g_free (name_utf8);
1512
1513 return track;
1514 }
1515
1516
1517 /*------------------------------------------------------------------*\
1518 * *
1519 * Update Track Data from File *
1520 * *
1521 \*------------------------------------------------------------------*/
1522
1523
1524 /* reads info from file and updates the ID3 tags of
1525 @selected_tracks. */
update_tracks(GList * selected_tracks)1526 void update_tracks (GList *selected_tracks)
1527 {
1528 GList *gl;
1529 iTunesDB *itdb = NULL;
1530
1531 if (selected_tracks == NULL)
1532 {
1533 gtkpod_statusbar_message(_("Nothing to update"));
1534 return;
1535 }
1536
1537 block_widgets ();
1538 for (gl=selected_tracks; gl; gl=gl->next)
1539 {
1540 Track *track = gl->data;
1541 g_return_if_fail (track);
1542
1543 /* update_track_from_file() may possibly remove tracks from
1544 the database, so we need to check if the track we are
1545 referencing to is still valid. To do so we first have to
1546 secure a valid pointer to an itdb. Since the first track in
1547 selected_tracks is always valid, we take that one. */
1548 if (!itdb)
1549 itdb = track->itdb;
1550 g_return_if_fail (itdb);
1551 if (g_list_find (itdb->tracks, track))
1552 {
1553 gchar *buf = get_track_info (track, TRUE);
1554 gtkpod_statusbar_message (_("Updating %s"), buf);
1555 g_free (buf);
1556 update_track_from_file (track->itdb, track);
1557 }
1558 }
1559 release_widgets ();
1560 /* display log of non-updated tracks */
1561 display_non_updated (NULL, NULL);
1562 /* display log of updated tracks */
1563 display_updated (NULL, NULL);
1564 /* display log of problems with mserv data */
1565 display_mserv_problems (NULL, NULL);
1566 /* display log of detected duplicates */
1567 gp_duplicate_remove (NULL, NULL);
1568 gtkpod_statusbar_message(_("Updated selected tracks with info from file."));
1569 }
1570
1571
1572 /*------------------------------------------------------------------*\
1573 * *
1574 * Update mserv Data from File *
1575 * *
1576 \*------------------------------------------------------------------*/
1577
1578
1579 /* reads info from file and updates the ID3 tags of
1580 @selected_tracks. */
mserv_from_file_tracks(GList * selected_tracks)1581 void mserv_from_file_tracks (GList *selected_tracks)
1582 {
1583 GList *gl;
1584
1585 if (selected_tracks == NULL)
1586 {
1587 gtkpod_statusbar_message(_("Nothing to update"));
1588 return;
1589 }
1590
1591 block_widgets ();
1592 for (gl = selected_tracks; gl; gl=gl->next)
1593 {
1594 ExtraTrackData *etr;
1595 Track *track = gl->data;
1596 gchar *buf;
1597 g_return_if_fail (track);
1598 etr = track->userdata;
1599 g_return_if_fail (etr);
1600
1601 buf = get_track_info (track, TRUE);
1602 gtkpod_statusbar_message (_("Retrieving mserv data %s"), buf);
1603 g_free (buf);
1604 if (etr->pc_path_locale && *etr->pc_path_locale)
1605 update_mserv_data_from_file (etr->pc_path_locale, track);
1606 else
1607 display_mserv_problems (track, _("no filename available"));
1608 }
1609 release_widgets ();
1610 /* display log of problems with mserv data */
1611 display_mserv_problems (NULL, NULL);
1612 gtkpod_statusbar_message(_("Updated selected tracks with data from mserv."));
1613 }
1614
1615
1616
1617 /* Logs tracks (@track) that could not be updated from file for some
1618 reason. Pop up a window with the log by calling with @track = NULL,
1619 or remove the log by calling with @track = -1.
1620 @txt (if available)w is added as an explanation as to why it was
1621 impossible to update the track */
display_non_updated(Track * track,gchar * txt)1622 void display_non_updated (Track *track, gchar *txt)
1623 {
1624 gchar *buf;
1625 static gint track_nr = 0;
1626 static GString *str = NULL;
1627
1628 if ((track == NULL) && str)
1629 {
1630 if (prefs_get_int("show_non_updated") && str->len)
1631 { /* Some tracks have not been updated. Print a notice */
1632 buf = g_strdup_printf (
1633 ngettext ("The following track could not be updated",
1634 "The following %d tracks could not be updated",
1635 track_nr), track_nr);
1636 gtkpod_confirmation
1637 (-1, /* gint id, */
1638 FALSE, /* gboolean modal, */
1639 _("Failed Track Update"), /* title */
1640 buf, /* label */
1641 str->str, /* scrolled text */
1642 NULL, 0, NULL, /* option 1 */
1643 NULL, 0, NULL, /* option 2 */
1644 TRUE, /* gboolean confirm_again, */
1645 "show_non_updated",/* confirm_again_key,*/
1646 CONF_NULL_HANDLER, /* ConfHandler ok_handler,*/
1647 NULL, /* don't show "Apply" button */
1648 NULL, /* cancel_handler,*/
1649 NULL, /* gpointer user_data1,*/
1650 NULL); /* gpointer user_data2,*/
1651 g_free (buf);
1652 }
1653 display_non_updated ((void *)-1, NULL);
1654 }
1655
1656 if (track == (void *)-1)
1657 { /* clean up */
1658 if (str) g_string_free (str, TRUE);
1659 str = NULL;
1660 track_nr = 0;
1661 gtkpod_tracks_statusbar_update();
1662 }
1663 else if (prefs_get_int("show_non_updated") && track)
1664 {
1665 /* add info about it to str */
1666 buf = get_track_info (track, TRUE);
1667 if (!str)
1668 {
1669 track_nr = 0;
1670 str = g_string_sized_new (2000); /* used to keep record of
1671 * non-updated tracks */
1672 }
1673 if (txt) g_string_append_printf (str, "%s (%s)\n", buf, txt);
1674 else g_string_append_printf (str, "%s\n", buf);
1675 g_free (buf);
1676 ++track_nr; /* count tracks */
1677 }
1678 }
1679
1680
1681 /* Logs tracks (@track) that could be updated from file. Pop up a window
1682 with the log by calling with @track = NULL, or remove the log by
1683 calling with @track = -1.
1684 @txt (if available)w is added as an explanation as to why it was
1685 impossible to update the track */
display_updated(Track * track,gchar * txt)1686 void display_updated (Track *track, gchar *txt)
1687 {
1688 gchar *buf;
1689 static gint track_nr = 0;
1690 static GString *str = NULL;
1691
1692 if (prefs_get_int("show_updated") && (track == NULL) && str)
1693 {
1694 if (str->len)
1695 { /* Some tracks have been updated. Print a notice */
1696 buf = g_strdup_printf (
1697 ngettext ("The following track has been updated",
1698 "The following %d tracks have been updated",
1699 track_nr), track_nr);
1700 gtkpod_confirmation
1701 (-1, /* gint id, */
1702 FALSE, /* gboolean modal, */
1703 _("Successful Track Update"), /* title */
1704 buf, /* label */
1705 str->str, /* scrolled text */
1706 NULL, 0, NULL, /* option 1 */
1707 NULL, 0, NULL, /* option 2 */
1708 TRUE, /* gboolean confirm_again, */
1709 "show_updated",/* confirm_again_key,*/
1710 CONF_NULL_HANDLER, /* ConfHandler ok_handler,*/
1711 NULL, /* don't show "Apply" button */
1712 NULL, /* cancel_handler,*/
1713 NULL, /* gpointer user_data1,*/
1714 NULL); /* gpointer user_data2,*/
1715 g_free (buf);
1716 }
1717 display_updated ((void *)-1, NULL);
1718 }
1719
1720 if (track == (void *)-1)
1721 { /* clean up */
1722 if (str) g_string_free (str, TRUE);
1723 str = NULL;
1724 track_nr = 0;
1725 gtkpod_tracks_statusbar_update();
1726 }
1727 else if (prefs_get_int("show_updated") && track)
1728 {
1729 /* add info about it to str */
1730 buf = get_track_info (track, TRUE);
1731 if (!str)
1732 {
1733 track_nr = 0;
1734 str = g_string_sized_new (2000); /* used to keep record of
1735 * non-updated tracks */
1736 }
1737 if (txt) g_string_append_printf (str, "%s (%s)\n", buf, txt);
1738 else g_string_append_printf (str, "%s\n", buf);
1739 g_free (buf);
1740 ++track_nr; /* count tracks */
1741 }
1742 }
1743
1744
1745 /* Logs tracks (@track) that could not be updated with info from mserv
1746 database for some reason. Pop up a window with the log by calling
1747 with @track = NULL, or remove the log by calling with @track = -1.
1748 @txt (if available) is added as an explanation as to why it was
1749 impossible to retrieve mserv information */
display_mserv_problems(Track * track,gchar * txt)1750 void display_mserv_problems (Track *track, gchar *txt)
1751 {
1752 gchar *buf;
1753 static gint track_nr = 0;
1754 static GString *str = NULL;
1755
1756 if ((track == NULL) && str)
1757 {
1758 if (prefs_get_int("mserv_use") &&
1759 prefs_get_int("mserv_report_probs") && str->len)
1760 { /* Some tracks have had problems. Print a notice */
1761 buf = g_strdup_printf (
1762 ngettext ("No mserv information could be retrieved for the following track",
1763 "No mserv information could be retrieved for the following %d tracks",
1764 track_nr), track_nr);
1765 gtkpod_confirmation
1766 (-1, /* gint id, */
1767 FALSE, /* gboolean modal, */
1768 _("mserv data retrieval problem"), /* title */
1769 buf, /* label */
1770 str->str, /* scrolled text */
1771 NULL, 0, NULL, /* option 1 */
1772 NULL, 0, NULL, /* option 2 */
1773 TRUE, /* gboolean confirm_again, */
1774 "mserv_report_probs",/* confirm_again_key,*/
1775 CONF_NULL_HANDLER, /* ConfHandler ok_handler,*/
1776 NULL, /* don't show "Apply" button */
1777 NULL, /* cancel_handler,*/
1778 NULL, /* gpointer user_data1,*/
1779 NULL); /* gpointer user_data2,*/
1780 g_free (buf);
1781 }
1782 display_mserv_problems ((void *)-1, NULL);
1783 }
1784
1785 if (track == (void *)-1)
1786 { /* clean up */
1787 if (str) g_string_free (str, TRUE);
1788 str = NULL;
1789 track_nr = 0;
1790 gtkpod_tracks_statusbar_update();
1791 }
1792 else if (prefs_get_int("mserv_use") &&
1793 prefs_get_int("mserv_report_probs") && track)
1794 {
1795 /* add info about it to str */
1796 buf = get_track_info (track, TRUE);
1797 if (!str)
1798 {
1799 track_nr = 0;
1800 str = g_string_sized_new (2000); /* used to keep record */
1801 }
1802 if (txt) g_string_append_printf (str, "%s (%s)\n", buf, txt);
1803 else g_string_append_printf (str, "%s\n", buf);
1804 g_free (buf);
1805 ++track_nr; /* count tracks */
1806 }
1807 }
1808
1809
1810 /* Update information of @track from data in original file. If the
1811 * original filename is not available, information will be updated
1812 * from the copy on the iPod and a warning is printed.
1813
1814 A list of non-updated tracks can be displayed by calling
1815 display_non_updated (NULL, NULL). This list can be deleted by
1816 calling display_non_updated ((void *)-1, NULL);
1817
1818 It is also possible that duplicates get detected in the process --
1819 a list of those can be displayed by calling "gp_duplicate_remove
1820 (NULL, NULL)", that list can be deleted by calling
1821 "gp_duplicate_remove (NULL, (void *)-1)"*/
update_track_from_file(iTunesDB * itdb,Track * track)1822 void update_track_from_file (iTunesDB *itdb, Track *track)
1823 {
1824 ExtraTrackData *oetr;
1825 Track *oldtrack;
1826 gchar *prefs_charset = NULL;
1827 gchar *trackpath = NULL;
1828 gint32 oldsize = 0;
1829 gboolean charset_set;
1830
1831 g_return_if_fail (itdb);
1832 g_return_if_fail (track);
1833 oetr = track->userdata;
1834 g_return_if_fail (oetr);
1835 /* remember size of track on iPod */
1836 if (track->transferred) oldsize = track->size;
1837 else oldsize = 0;
1838
1839 /* remember if charset was set */
1840 if (oetr->charset) charset_set = TRUE;
1841 else charset_set = FALSE;
1842
1843 if (!prefs_get_int("update_charset") && charset_set)
1844 { /* we should use the initial charset for the update */
1845 prefs_charset = prefs_get_string("charset");
1846 /* use the charset used when first importing the track */
1847 prefs_set_string("charset", oetr->charset);
1848 }
1849
1850 trackpath = get_file_name_from_source (track, SOURCE_PREFER_LOCAL);
1851
1852 if (!(oetr->pc_path_locale && *oetr->pc_path_locale))
1853 { /* no path available */
1854 if (trackpath)
1855 {
1856 display_non_updated (track, _("no local filename available, file on the iPod will be used instead"));
1857 }
1858 else
1859 {
1860 if (itdb->usertype & GP_ITDB_TYPE_IPOD)
1861 {
1862 display_non_updated (track, _("no local filename available and copy on iPod cannot be found"));
1863 }
1864 else
1865 {
1866 display_non_updated (track, _("no local filename available"));
1867 }
1868 }
1869 }
1870 else if (!g_file_test (oetr->pc_path_locale, G_FILE_TEST_EXISTS))
1871 {
1872 if (trackpath)
1873 {
1874 display_non_updated (track, _("local file could not be found, file on the iPod will be used instead"));
1875 }
1876 else
1877 {
1878 if (itdb->usertype & GP_ITDB_TYPE_IPOD)
1879 {
1880 display_non_updated (track, _("local file as well as copy on the iPod cannot be found"));
1881 }
1882 else
1883 {
1884 display_non_updated (track, _("no local filename available"));
1885 }
1886 }
1887 }
1888
1889 if (trackpath && get_track_info_from_file (trackpath, track))
1890 { /* update successful */
1891 ExtraTrackData *netr = track->userdata;
1892
1893 /* remove track from sha1 hash and reinsert it
1894 (hash value may have changed!) */
1895 gchar *oldhash = oetr->sha1_hash;
1896
1897 sha1_track_remove (track);
1898 /* need to remove the old value manually! */
1899 oetr->sha1_hash = NULL;
1900 oldtrack = sha1_track_exists_insert (itdb, track);
1901 if (oldtrack) { /* track exists, remove old track
1902 and register the new version */
1903 sha1_track_remove (oldtrack);
1904 gp_duplicate_remove (track, oldtrack);
1905 sha1_track_exists_insert (itdb, track);
1906 }
1907
1908 if (itdb->usertype & GP_ITDB_TYPE_IPOD)
1909 { /* track may have to be copied to iPod on next export */
1910 gchar *name_on_ipod;
1911 gboolean transfer_again = FALSE;
1912
1913 name_on_ipod = get_file_name_from_source (track, SOURCE_IPOD);
1914 if (name_on_ipod && (strcmp (name_on_ipod, trackpath) != 0))
1915 { /* trackpath is not on the iPod */
1916 if (oldhash && oetr->sha1_hash)
1917 { /* do we really have to copy the track again? */
1918 if (strcmp (oldhash, oetr->sha1_hash) != 0)
1919 {
1920 transfer_again = TRUE;
1921 }
1922 }
1923 else
1924 { /* no hash available -- copy! */
1925 transfer_again = TRUE;
1926 }
1927 }
1928 else
1929 {
1930 data_changed (itdb);
1931 }
1932
1933 if (transfer_again)
1934 { /* We need to copy the track back to the iPod. That's done
1935 marking a copy of the original track for deletion and
1936 then adding the original track to the
1937 conversion/transfer list */
1938 Track *new_track = gp_track_new ();
1939 ExtraTrackData *new_etr = new_track->userdata;
1940 g_return_if_fail (new_etr);
1941
1942 new_track->size = oldsize;
1943 new_track->ipod_path = track->ipod_path;
1944 track->ipod_path = g_strdup ("");
1945 track->transferred = FALSE;
1946
1947 /* cancel conversion/transfer of track */
1948 file_convert_cancel_track (track);
1949 /* mark the track for deletion on the ipod */
1950 mark_track_for_deletion (itdb, new_track);
1951 /* reschedule conversion/transfer of track */
1952 file_convert_add_track (track);
1953
1954 netr->tchanged = TRUE;
1955 }
1956
1957 g_free (name_on_ipod);
1958 }
1959
1960 /* Set this flag to true to ensure artwork is reread from file */
1961 netr->tartwork_changed = TRUE;
1962
1963 /* notify display model */
1964 if (netr->tchanged)
1965 {
1966 pm_track_changed (track);
1967 data_changed (itdb);
1968 netr->tchanged = FALSE;
1969 }
1970 else
1971 {
1972 /* Rather than depend on the track data being changed, only the artwork may have changed
1973 * hence the reason for the user updating from file
1974 */
1975 coverart_track_changed (track, COVERART_CHANGE_SIGNAL);
1976 }
1977
1978 display_updated (track, NULL);
1979 g_free (oldhash);
1980 }
1981 else if (trackpath)
1982 { /* update not successful -- log this track for later display */
1983 display_non_updated (track, _("update failed (format not supported?)"));
1984 }
1985
1986 if (!prefs_get_int("update_charset") && charset_set)
1987 { /* reset charset */
1988 prefs_set_string("charset", prefs_charset);
1989 }
1990
1991 g_free (trackpath);
1992 g_free(prefs_charset);
1993
1994 while (widgets_blocked && gtk_events_pending ()) gtk_main_iteration ();
1995 }
1996
1997
1998 /*------------------------------------------------------------------*\
1999 * *
2000 * Add File *
2001 * *
2002 \*------------------------------------------------------------------*/
2003
2004 /* Append file @fname to the list of tracks.
2005 @fname is in the current locale
2006 @plitem: if != NULL, add track to plitem as well (unless it's the MPL)
2007 descend: TRUE: add directories recursively
2008 FALSE: add contents of directories passed but don't descend
2009 into its subdirectories */
2010 /* @addtrackfunc: if != NULL this will be called instead of
2011 "add_track_to_playlist () -- used for dropping tracks at a specific
2012 position in the track view */
add_track_by_filename(iTunesDB * itdb,gchar * fname,Playlist * plitem,gboolean descend,AddTrackFunc addtrackfunc,gpointer data)2013 gboolean add_track_by_filename (iTunesDB *itdb, gchar *fname,
2014 Playlist *plitem, gboolean descend,
2015 AddTrackFunc addtrackfunc, gpointer data)
2016 {
2017 static gint count = 0; /* do a gtkpod_tracks_statusbar_update() every
2018 10 tracks */
2019 Track *oldtrack;
2020 gchar str[PATH_MAX];
2021 gchar *basename;
2022 Playlist *mpl;
2023 gboolean result = TRUE;
2024
2025 g_return_val_if_fail (fname, FALSE);
2026 g_return_val_if_fail (itdb, FALSE);
2027 mpl = itdb_playlist_mpl (itdb);
2028 g_return_val_if_fail (mpl, FALSE);
2029
2030 if (!plitem) plitem = mpl;
2031
2032 if (g_file_test (fname, G_FILE_TEST_IS_DIR))
2033 {
2034 return add_directory_by_name (itdb, fname, plitem, descend, addtrackfunc, data);
2035 }
2036
2037 /* check if file is a playlist */
2038 switch (determine_file_type(fname))
2039 {
2040 case FILE_TYPE_M3U:
2041 case FILE_TYPE_PLS:
2042 if (add_playlist_by_filename (itdb, fname, plitem, -1,
2043 addtrackfunc, data))
2044 return TRUE;
2045 return FALSE;
2046 case FILE_TYPE_MP3:
2047 case FILE_TYPE_M4A:
2048 case FILE_TYPE_M4P:
2049 case FILE_TYPE_M4B:
2050 case FILE_TYPE_WAV:
2051 case FILE_TYPE_M4V:
2052 case FILE_TYPE_MP4:
2053 case FILE_TYPE_MOV:
2054 case FILE_TYPE_MPG:
2055 case FILE_TYPE_OGG:
2056 case FILE_TYPE_FLAC:
2057 case FILE_TYPE_IMAGE:
2058 case FILE_TYPE_UNKNOWN:
2059 case FILE_TYPE_DIRECTORY:
2060 break;
2061 }
2062
2063 /* print a message about which file is being processed */
2064 basename = g_path_get_basename (fname);
2065 if (basename)
2066 {
2067 gchar *bn_utf8 = charset_to_utf8 (basename);
2068 gtkpod_statusbar_message (_("Processing '%s'..."), bn_utf8);
2069 while (widgets_blocked && gtk_events_pending ()) gtk_main_iteration ();
2070 g_free (bn_utf8);
2071
2072 if (excludefile(basename))
2073 {
2074 gtkpod_warning (_("Skipping '%s' because it matches exclude masks.\n"), basename);
2075 while (widgets_blocked && gtk_events_pending ())
2076 gtk_main_iteration ();
2077 g_free (basename);
2078 return FALSE;
2079 }
2080 }
2081 C_FREE (basename);
2082
2083 /* Check if there exists already a track with the same filename */
2084 oldtrack = gp_track_by_filename (itdb, fname);
2085 /* If a track already exists in the database, either update it or
2086 just add it to the current playlist (if it's not already there) */
2087 if (oldtrack)
2088 {
2089 if (prefs_get_int("update_existing"))
2090 { /* update the information */
2091 update_track_from_file (itdb, oldtrack);
2092 }
2093 /* add to current playlist if it's not already in there */
2094 if (!itdb_playlist_is_mpl (plitem))
2095 {
2096 if (!itdb_playlist_contains_track (plitem, oldtrack))
2097 {
2098 if (addtrackfunc)
2099 addtrackfunc (plitem, oldtrack, data);
2100 else
2101 gp_playlist_add_track (plitem, oldtrack, TRUE);
2102 }
2103 }
2104 }
2105 else /* oldtrack == NULL */
2106 { /* OK, the same filename does not already exist */
2107 Track *track = get_track_info_from_file (fname, NULL);
2108 if (track)
2109 {
2110 Track *added_track = NULL;
2111 ExtraTrackData *etr = track->userdata;
2112 g_return_val_if_fail (etr, FALSE);
2113
2114 track->id = 0;
2115 track->transferred = FALSE;
2116
2117 /* is 'fname' on the iPod? -- if yes mark as transfered, if
2118 * it's in the music directory */
2119 if (itdb->usertype & GP_ITDB_TYPE_IPOD)
2120 {
2121 const gchar *mountpoint = itdb_get_mountpoint (itdb);
2122 g_return_val_if_fail (mountpoint, FALSE);
2123 if (strstr (fname, mountpoint) == fname)
2124 { /* Yes */
2125 /* is 'fname' in the iPod's Music directory? */
2126 gchar *music_dir = itdb_get_music_dir (mountpoint);
2127 if (music_dir)
2128 {
2129 gchar *cdir = g_strdup_printf ("%s%c", music_dir,
2130 G_DIR_SEPARATOR);
2131 if (g_strncasecmp (fname, cdir, strlen (cdir)) == 0)
2132 { /* Yes */
2133 gchar *fname_i = fname + strlen (mountpoint);
2134 if (*fname_i == G_DIR_SEPARATOR) ++fname_i;
2135 track->transferred = TRUE;
2136 track->ipod_path = g_strdup_printf (
2137 "%c%s", G_DIR_SEPARATOR, fname_i);
2138 itdb_filename_fs2ipod (track->ipod_path);
2139 }
2140 g_free (music_dir);
2141 g_free (cdir);
2142 }
2143 }
2144 }
2145
2146 if (gethostname (str, PATH_MAX-2) == 0)
2147 {
2148 str[PATH_MAX-1] = 0;
2149 etr->hostname = g_strdup (str);
2150 }
2151 /* add_track may return pointer to a different track if an
2152 identical one (SHA1 checksum) was found */
2153 added_track = gp_track_add (itdb, track);
2154 g_return_val_if_fail (added_track, FALSE);
2155
2156 /* set flags to 'podcast' if adding to podcast list */
2157 if (itdb_playlist_is_podcasts (plitem))
2158 gp_track_set_flags_podcast (added_track);
2159
2160 if (itdb_playlist_is_mpl (plitem))
2161 { /* add track to master playlist if it wasn't a
2162 duplicate */
2163 if (added_track == track)
2164 {
2165 if (addtrackfunc)
2166 addtrackfunc (plitem, added_track, data);
2167 else
2168 gp_playlist_add_track (plitem, added_track, TRUE);
2169 }
2170 }
2171 else
2172 {
2173 #if 0 /* initially iTunes didn't add podcasts to the MPL */
2174 /* add track to master playlist if it wasn't a
2175 * duplicate and plitem is not the podcasts playlist
2176 */
2177 if (added_track == track)
2178 {
2179 if (!itdb_playlist_is_podcasts (plitem))
2180 gp_playlist_add_track (mpl, added_track, TRUE);
2181 }
2182 #else
2183 if (added_track == track)
2184 {
2185 gp_playlist_add_track (mpl, added_track, TRUE);
2186 }
2187 #endif
2188 /* add track to specified playlist -- unless adding
2189 * to podcasts list and track already exists there */
2190 if (itdb_playlist_is_podcasts (plitem) &&
2191 g_list_find (plitem->members, added_track))
2192 {
2193 gchar *buf = get_track_info (added_track, FALSE);
2194 gtkpod_warning (_("Podcast already present: '%s'\n\n"), buf);
2195 g_free (buf);
2196 }
2197 else
2198 {
2199 if (addtrackfunc)
2200 addtrackfunc (plitem, added_track, data);
2201 else
2202 gp_playlist_add_track (plitem, added_track, TRUE);
2203 }
2204 }
2205
2206 /* indicate that non-transferred files exist */
2207 data_changed (itdb);
2208 ++count;
2209 if (count >= 10) /* update every ten tracks added */
2210 {
2211 gtkpod_tracks_statusbar_update();
2212 count = 0;
2213 }
2214 }
2215 else
2216 { /* !track */
2217 result = FALSE;
2218 }
2219 }
2220 while (widgets_blocked && gtk_events_pending ()) gtk_main_iteration ();
2221 return result;
2222 }
2223
2224
2225 /*------------------------------------------------------------------*\
2226 * *
2227 * Write Tags *
2228 * *
2229 \*------------------------------------------------------------------*/
2230
2231
2232 /* Call the correct tag writing function for the filename @name */
file_write_info(gchar * name,Track * track)2233 static gboolean file_write_info (gchar *name, Track *track)
2234 {
2235 gchar *buf;
2236
2237 g_return_val_if_fail (name, FALSE);
2238 g_return_val_if_fail (track, FALSE);
2239
2240 switch (determine_file_type(name))
2241 {
2242 case FILE_TYPE_MP3:
2243 return mp3_write_file_info (name, track);
2244 case FILE_TYPE_M4A:
2245 case FILE_TYPE_M4P:
2246 case FILE_TYPE_M4B:
2247 return mp4_write_file_info (name, track);
2248 case FILE_TYPE_WAV:
2249 return wav_write_file_info (name, track);
2250 case FILE_TYPE_OGG:
2251 return ogg_write_file_info (name, track);
2252 case FILE_TYPE_FLAC:
2253 return flac_write_file_info (name, track);
2254 case FILE_TYPE_M4V:
2255 case FILE_TYPE_MP4:
2256 case FILE_TYPE_MOV:
2257 case FILE_TYPE_MPG:
2258 buf = get_track_info (track, FALSE);
2259 gtkpod_warning (_("Writing to video files not yet supported (%s).\n\n"),
2260 buf);
2261 g_free (buf);
2262 break;
2263 case FILE_TYPE_M3U:
2264 case FILE_TYPE_PLS:
2265 case FILE_TYPE_IMAGE:
2266 case FILE_TYPE_UNKNOWN:
2267 case FILE_TYPE_DIRECTORY:
2268 break;
2269 }
2270
2271 return FALSE;
2272 }
2273
2274
2275 /* Write tags to file */
write_tags_to_file(Track * track)2276 gboolean write_tags_to_file (Track *track)
2277 {
2278 ExtraTrackData *etr;
2279 iTunesDB *itdb;
2280 gchar *ipod_fullpath;
2281 gchar *prefs_charset = NULL;
2282 Track *oldtrack;
2283 gboolean track_charset_set;
2284
2285 g_return_val_if_fail (track, FALSE);
2286 etr = track->userdata;
2287 g_return_val_if_fail (etr, FALSE);
2288 itdb = track->itdb;
2289 g_return_val_if_fail (itdb, FALSE);
2290
2291 /* if we are to use the charset used when first importing
2292 the track, change the prefs settings temporarily */
2293 if (etr->charset) track_charset_set = TRUE;
2294 else track_charset_set = FALSE;
2295 if (!prefs_get_int("write_charset") && track_charset_set)
2296 { /* we should use the initial charset for the update */
2297 prefs_charset = prefs_get_string("charset");
2298 /* use the charset used when first importing the track */
2299 prefs_set_string("charset", etr->charset);
2300 }
2301 else
2302 { /* we should update the track->charset information */
2303 update_charset_info (track);
2304 }
2305
2306 if (etr->pc_path_locale && (strlen (etr->pc_path_locale) > 0))
2307 {
2308 if (file_write_info (
2309 etr->pc_path_locale, track) == FALSE)
2310 {
2311 gtkpod_warning (_("Couldn't change tags of file: %s\n"),
2312 etr->pc_path_locale);
2313 }
2314 }
2315 if (!get_offline (itdb) &&
2316 track->transferred &&
2317 track->ipod_path &&
2318 (g_utf8_strlen (track->ipod_path, -1) > 0))
2319 {
2320 /* need to get ipod filename */
2321 ipod_fullpath = get_file_name_from_source (track, SOURCE_IPOD);
2322 if (file_write_info (
2323 ipod_fullpath, track) == FALSE)
2324 {
2325 gtkpod_warning (_("Couldn't change tags of file: %s\n"),
2326 ipod_fullpath);
2327 }
2328 g_free (ipod_fullpath);
2329 }
2330 /* remove track from sha1 hash and reinsert it (hash value has changed!) */
2331 sha1_track_remove (track);
2332 C_FREE (etr->sha1_hash); /* need to remove the old value manually! */
2333 oldtrack = sha1_track_exists_insert (itdb, track);
2334 if (oldtrack) { /* track exists, remove and register the new version */
2335 sha1_track_remove (oldtrack);
2336 gp_duplicate_remove (track, oldtrack);
2337 sha1_track_exists_insert (itdb, track);
2338 }
2339
2340 if (!prefs_get_int("write_charset") && track_charset_set)
2341 { /* reset charset */
2342 prefs_set_string("charset", prefs_charset);
2343 }
2344 g_free (prefs_charset);
2345 return TRUE;
2346 }
2347
2348
2349 /* Get file name from source @source */
2350 /* File is guaranteed to exist, otherwise NULL is returned. */
get_file_name_from_source(Track * track,FileSource source)2351 gchar *get_file_name_from_source (Track *track, FileSource source)
2352 {
2353 gchar *result = NULL;
2354 ExtraTrackData *etr;
2355
2356 g_return_val_if_fail (track, NULL);
2357 etr = track->userdata;
2358 g_return_val_if_fail (etr, NULL);
2359
2360 switch (source)
2361 {
2362 case SOURCE_PREFER_LOCAL:
2363 result = get_file_name_from_source (track, SOURCE_LOCAL);
2364 if (!result)
2365 {
2366 if (track->itdb && (track->itdb->usertype & GP_ITDB_TYPE_IPOD))
2367 {
2368 result = get_file_name_from_source (track, SOURCE_IPOD);
2369 }
2370 }
2371 break;
2372 case SOURCE_PREFER_IPOD:
2373 result = get_file_name_from_source (track, SOURCE_IPOD);
2374 if (!result)
2375 result = get_file_name_from_source (track, SOURCE_LOCAL);
2376 break;
2377 case SOURCE_LOCAL:
2378 if (etr->pc_path_locale && (*etr->pc_path_locale))
2379 {
2380 if (g_file_test (etr->pc_path_locale, G_FILE_TEST_EXISTS))
2381 {
2382 result = g_strdup (etr->pc_path_locale);
2383 }
2384 }
2385 break;
2386 case SOURCE_IPOD:
2387 if(track && !get_offline (track->itdb))
2388 {
2389 result = itdb_filename_on_ipod (track);
2390 }
2391 break;
2392 }
2393 return result;
2394 }
2395
2396
2397
2398
2399
2400
2401 /* ------------------------------------------------------------
2402
2403 Reading of offline playcount file
2404
2405 ------------------------------------------------------------ */
2406
2407 /* Read the ~/.gtkpod/offline_playcount file and adjust the
2408 playcounts. The tracks will first be matched by their sha1 sum, if
2409 that fails, by their filename.
2410 If tracks could not be matched, the user will be queried whether to
2411 forget about them or write them back into the offline_playcount
2412 file. */
parse_offline_playcount(void)2413 void parse_offline_playcount (void)
2414 {
2415 gchar *cfgdir = prefs_get_cfgdir ();
2416 gchar *offlplyc = g_strdup_printf (
2417 "%s%c%s", cfgdir, G_DIR_SEPARATOR, "offline_playcount");
2418
2419 if (g_file_test (offlplyc, G_FILE_TEST_EXISTS))
2420 {
2421 FILE *file = fopen (offlplyc, "r+");
2422 size_t len = 0; /* how many bytes are written */
2423 gchar *buf;
2424 GString *gstr, *gstr_filenames;
2425 if (!file)
2426 {
2427 gtkpod_warning (_("Could not open '%s' for reading and writing.\n"),
2428 offlplyc);
2429 g_free (offlplyc);
2430 return;
2431 }
2432 if (flock (fileno (file), LOCK_EX) != 0)
2433 {
2434 gtkpod_warning (_("Could not obtain lock on '%s'.\n"), offlplyc);
2435 fclose (file);
2436 g_free (offlplyc);
2437 return;
2438 }
2439 buf = g_malloc (2*PATH_MAX);
2440 gstr = g_string_sized_new (PATH_MAX);
2441 gstr_filenames = g_string_sized_new (PATH_MAX);
2442 while (fgets (buf, 2*PATH_MAX, file))
2443 {
2444 gchar *buf_utf8 = charset_to_utf8 (buf);
2445 gchar *sha1=NULL;
2446 gchar *filename=NULL;
2447 gchar *ptr1, *ptr2;
2448 /* skip strings that do not start with "PLCT:" */
2449 if (strncmp (buf, SOCKET_PLYC, strlen (SOCKET_PLYC)) != 0)
2450 {
2451 gtkpod_warning (_("Malformed line in '%s': %s\n"), offlplyc, buf);
2452 goto cont;
2453 }
2454 /* start of SHA1 string */
2455 ptr1 = buf + strlen (SOCKET_PLYC);
2456 /* end of SHA1 string */
2457 ptr2 = strchr (ptr1, ' ');
2458 if (ptr2 == NULL)
2459 { /* error! */
2460 gtkpod_warning (_("Malformed line in '%s': %s\n"),
2461 offlplyc, buf_utf8);
2462 goto cont;
2463 }
2464 if (ptr1 != ptr2) sha1 = g_strndup (ptr1, ptr2-ptr1);
2465 /* start of filename */
2466 ptr1 = ptr2 + 1;
2467 /* end of filename string */
2468 ptr2 = strchr (ptr1, '\n');
2469 if (ptr2 == NULL)
2470 { /* error! */
2471 gtkpod_warning (_("Malformed line in '%s': %s\n"),
2472 offlplyc, buf_utf8);
2473 goto cont;
2474 }
2475 if (ptr1 != ptr2)
2476 {
2477 filename = g_strndup (ptr1, ptr2-ptr1);
2478 }
2479 else
2480 { /* error! */
2481 gtkpod_warning (_("Malformed line in '%s': %s\n"),
2482 offlplyc, buf_utf8);
2483 goto cont;
2484 }
2485 if (gp_increase_playcount (sha1, filename, 1) == FALSE)
2486 { /* didn't find the track -> store */
2487 gchar *filename_utf8 = charset_to_utf8 (filename);
2488 /* if (gstr->len == 0) */
2489 /* { */
2490 /* gtkpod_warning (_("Couldn't find track for playcount adjustment:\n")); */
2491 /* } */
2492 g_string_append (gstr_filenames, filename_utf8);
2493 g_string_append (gstr_filenames, "\n");
2494 g_free (filename_utf8);
2495 g_string_append (gstr, buf);
2496 }
2497 cont:
2498 g_free (buf_utf8);
2499 g_free (sha1);
2500 g_free (filename);
2501 }
2502
2503 /* rewind file pointer to beginning */
2504 rewind (file);
2505 if (gstr->len != 0)
2506 {
2507 gint result = gtkpod_confirmation
2508 (-1, /* gint id, */
2509 TRUE, /* gboolean modal, */
2510 _("Remove offline playcounts?"), /* title */
2511 _("Some tracks played offline could not be found in the iTunesDB. Press 'OK' to remove them from the offline playcount file, 'Cancel' to keep them."), /* label */
2512 gstr_filenames->str, /* scrolled text */
2513 NULL, 0, NULL, /* option 1 */
2514 NULL, 0, NULL, /* option 2 */
2515 TRUE, /* confirm_again, */
2516 NULL, /* confirm_again_key,*/
2517 CONF_NULL_HANDLER, /* ConfHandler ok_handler,*/
2518 NULL, /* don't show "Apply" button */
2519 CONF_NULL_HANDLER, /* cancel_handler,*/
2520 NULL, /* gpointer user_data1,*/
2521 NULL); /* gpointer user_data2,*/
2522
2523 if (result != GTK_RESPONSE_OK)
2524 {
2525 len = fwrite (gstr->str, sizeof (gchar), gstr->len, file);
2526 if (len != gstr->len)
2527 {
2528 gtkpod_warning (_("Error writing to '%s'.\n"), offlplyc);
2529 }
2530 }
2531 }
2532 ftruncate (fileno (file), len);
2533 fclose (file);
2534 g_string_free (gstr, TRUE);
2535 g_string_free (gstr_filenames, TRUE);
2536 g_free (buf);
2537 }
2538 g_free (cfgdir);
2539 g_free (offlplyc);
2540 }
2541
2542
2543 /* ------------------------------------------------------------
2544
2545 Reading of gain tags
2546
2547 ------------------------------------------------------------ */
2548 /**
2549 * Read the soundcheck value for @track.
2550 *
2551 * Return value: TRUE, if gain could be read
2552 */
read_soundcheck(Track * track)2553 gboolean read_soundcheck (Track *track)
2554 {
2555 gchar *path;
2556 gchar *buf;
2557 gboolean result = FALSE;
2558
2559 g_return_val_if_fail (track, FALSE);
2560
2561 path = get_file_name_from_source (track, SOURCE_PREFER_LOCAL);
2562
2563 if (path)
2564 {
2565 switch (determine_file_type (path))
2566 {
2567 case FILE_TYPE_MP3:
2568 result = mp3_read_soundcheck (path, track);
2569 break;
2570 case FILE_TYPE_M4A:
2571 case FILE_TYPE_M4P:
2572 case FILE_TYPE_M4B:
2573 result = mp4_read_soundcheck (path, track);
2574 break;
2575 case FILE_TYPE_OGG: /* FIXME */
2576 case FILE_TYPE_FLAC: /* FIXME */
2577 case FILE_TYPE_WAV: /* FIXME */
2578 case FILE_TYPE_M4V:
2579 case FILE_TYPE_MP4:
2580 case FILE_TYPE_MOV:
2581 case FILE_TYPE_MPG:
2582 case FILE_TYPE_UNKNOWN:
2583 buf = get_track_info (track, FALSE);
2584 gtkpod_warning (
2585 _("Normalization failed: file type not supported (%s).\n\n"),
2586 buf);
2587 g_free (buf);
2588 break;
2589 case FILE_TYPE_M3U:
2590 case FILE_TYPE_PLS:
2591 case FILE_TYPE_IMAGE:
2592 case FILE_TYPE_DIRECTORY:
2593 break;
2594 }
2595 g_free (path);
2596 }
2597 else
2598 {
2599 buf = get_track_info (track, FALSE);
2600 gtkpod_warning (
2601 _("Normalization failed: file not available (%s).\n\n"),
2602 buf);
2603 g_free (buf);
2604 }
2605 return result;
2606 }
2607
2608 /* Get lyrics from file */
read_lyrics_from_file(Track * track,gchar ** lyrics)2609 gboolean read_lyrics_from_file (Track *track, gchar **lyrics)
2610 {
2611 gchar *path;
2612 gchar *buf;
2613 gboolean result = FALSE;
2614 ExtraTrackData *etr;
2615
2616 g_return_val_if_fail (track, FALSE);
2617 etr = track->userdata;
2618 g_return_val_if_fail (etr,FALSE);
2619 path = get_file_name_from_source (track, SOURCE_PREFER_IPOD);
2620 if (path)
2621 {
2622 switch (determine_file_type (path))
2623 {
2624 case FILE_TYPE_MP3:
2625 result = id3_lyrics_read (path, lyrics);
2626 break;
2627 case FILE_TYPE_M4A:
2628 case FILE_TYPE_M4P:
2629 case FILE_TYPE_M4B:
2630 case FILE_TYPE_M4V:
2631 case FILE_TYPE_MP4:
2632 result = TRUE;
2633 *lyrics=g_strdup(
2634 _("Error: File format unsupported now."));
2635 break;
2636 case FILE_TYPE_MOV:
2637 case FILE_TYPE_MPG:
2638 case FILE_TYPE_WAV: /* FIXME */
2639 case FILE_TYPE_OGG: /* FIXME */
2640 case FILE_TYPE_FLAC: /* FIXME */
2641 case FILE_TYPE_UNKNOWN:
2642 result = TRUE;
2643 *lyrics=g_strdup(
2644 _("Error: Lyrics not supported for this file format."));
2645 break;
2646 case FILE_TYPE_M3U:
2647 case FILE_TYPE_PLS:
2648 case FILE_TYPE_IMAGE:
2649 case FILE_TYPE_DIRECTORY:
2650 break;
2651 }
2652 g_free (path);
2653 }
2654 else
2655 {
2656 buf = get_track_info (track, FALSE);
2657 *lyrics=g_strdup_printf(
2658 _("Error: Lyrics not found, file not available (%s).\n\n"),
2659 buf);
2660 g_free (buf);
2661 }
2662 if (result)
2663 {
2664 if (!*lyrics) *lyrics=g_strdup("");
2665 if (etr->lyrics) g_free(etr->lyrics);
2666 etr->lyrics=g_strdup(*lyrics);
2667 }
2668 return result;
2669 }
2670
2671 /* Write lyrics to file */
write_lyrics_to_file(Track * track)2672 gboolean write_lyrics_to_file (Track *track)
2673 {
2674 gchar *path=NULL;
2675 gchar *buf;
2676 Track *oldtrack;
2677 gboolean result = FALSE;
2678 gboolean warned = FALSE;
2679 ExtraTrackData *etr;
2680 iTunesDB *itdb;
2681
2682 g_return_val_if_fail (track, FALSE);
2683 etr = track->userdata;
2684 g_return_val_if_fail (etr,FALSE);
2685
2686 if (g_str_has_prefix(etr->lyrics, _("Error:"))) {
2687 /* Not writing lyrics as there are only errors */
2688 return FALSE;
2689 }
2690
2691 itdb = track->itdb;
2692 g_return_val_if_fail (itdb, FALSE);
2693 path = get_file_name_from_source (track, SOURCE_IPOD);
2694 if (!path)
2695 {
2696 if (prefs_get_int("id3_write"))
2697 {
2698 path = get_file_name_from_source (track, SOURCE_LOCAL);
2699 }
2700 else
2701 {
2702 buf = get_track_info (track, FALSE);
2703 gtkpod_warning (
2704 _("iPod File not available and ID3 saving disabled in options, cannot save lyrics to: %s.\n\n"),
2705 buf);
2706 g_free (buf);
2707 warned=TRUE;
2708 }
2709 }
2710 if (path!=NULL)
2711 {
2712 switch (determine_file_type (path))
2713 {
2714 case FILE_TYPE_MP3:
2715 result = id3_lyrics_save (path, etr->lyrics);
2716 break;
2717 case FILE_TYPE_M4A:
2718 case FILE_TYPE_M4P:
2719 case FILE_TYPE_M4B:
2720 case FILE_TYPE_M4V:
2721 case FILE_TYPE_MP4:
2722 result = TRUE;
2723 break;
2724 case FILE_TYPE_MOV:
2725 case FILE_TYPE_MPG:
2726 case FILE_TYPE_WAV: /* FIXME */
2727 case FILE_TYPE_OGG: /* FIXME */
2728 case FILE_TYPE_FLAC: /* FIXME */
2729 case FILE_TYPE_UNKNOWN:
2730 result = TRUE;
2731 break;
2732 case FILE_TYPE_M3U:
2733 case FILE_TYPE_PLS:
2734 case FILE_TYPE_IMAGE:
2735 case FILE_TYPE_DIRECTORY:
2736 break;
2737 }
2738 g_free (path);
2739 }
2740 else
2741 {
2742 if (!warned) {
2743 buf = get_track_info (track, FALSE);
2744 gtkpod_warning (
2745 _("Lyrics not written, file name not available (%s).\n\n"),
2746 buf);
2747 g_free (buf);
2748 }
2749 }
2750
2751 if (!result || !etr->lyrics || (strlen(etr->lyrics)==0))
2752 {
2753 track->lyrics_flag=0x00;
2754 }
2755 else
2756 {
2757 track->lyrics_flag=0x01;
2758 }
2759 if (!etr->lyrics)
2760 {
2761 etr->lyrics = g_strdup ("");
2762 }
2763
2764 if (result)
2765 {
2766 /* remove track from sha1 hash and reinsert it (hash value has changed!) */
2767 sha1_track_remove (track);
2768 C_FREE (etr->sha1_hash); /* need to remove the old value manually! */
2769 oldtrack = sha1_track_exists_insert (itdb, track);
2770 if (oldtrack)
2771 { /* track exists, remove the old track and register the new version */
2772 sha1_track_remove (oldtrack);
2773 gp_duplicate_remove (track, oldtrack);
2774 sha1_track_exists_insert (itdb, track);
2775 }
2776 }
2777 return result;
2778 }
2779