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