]> git.sesse.net Git - vlc/blob - modules/meta_engine/taglib.cpp
meta_engine/taglib: Write MusicBrainz Track IDs
[vlc] / modules / meta_engine / taglib.cpp
1 /*****************************************************************************
2  * taglib.cpp: Taglib tag parser/writer
3  *****************************************************************************
4  * Copyright (C) 2003-2011 VLC authors and VideoLAN
5  * $Id$
6  *
7  * Authors: Clément Stenac <zorglub@videolan.org>
8  *          Rafaël Carré <funman@videolanorg>
9  *          Rémi Duraffort <ivoire@videolan.org>
10  *
11  * This program is free software; you can redistribute it and/or modify it
12  * under the terms of the GNU Lesser General Public License as published by
13  * the Free Software Foundation; either version 2.1 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * along with this program; if not, write to the Free Software Foundation,
23  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24  *****************************************************************************/
25
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29
30 #include <vlc_common.h>
31 #include <vlc_plugin.h>
32 #include <vlc_demux.h>              /* demux_meta_t */
33 #include <vlc_strings.h>            /* vlc_b64_decode_binary */
34 #include <vlc_input.h>              /* for attachment_new */
35 #include <vlc_url.h>                /* make_path */
36 #include <vlc_mime.h>               /* mime type */
37 #include <vlc_fs.h>
38
39 #include <sys/stat.h>
40
41 #ifdef _WIN32
42 # include <vlc_charset.h>
43 # include <io.h>
44 #else
45 # include <unistd.h>
46 #endif
47
48
49 // Taglib headers
50 #ifdef _WIN32
51 # define TAGLIB_STATIC
52 #endif
53 #include <taglib.h>
54 #define VERSION_INT(a, b, c) ((a)<<16 | (b)<<8 | (c))
55 #define TAGLIB_VERSION VERSION_INT(TAGLIB_MAJOR_VERSION, \
56                                    TAGLIB_MINOR_VERSION, \
57                                    TAGLIB_PATCH_VERSION)
58
59 #include <fileref.h>
60 #include <tag.h>
61 #include <tbytevector.h>
62
63 #if TAGLIB_VERSION >= VERSION_INT(1,7,0)
64 # define TAGLIB_HAVE_APEFILE_H
65 # include <apefile.h>
66 # ifdef TAGLIB_WITH_ASF                     // ASF pictures comes with v1.7.0
67 #  define TAGLIB_HAVE_ASFPICTURE_H
68 #  include <asffile.h>
69 # endif
70 #endif
71
72 #if TAGLIB_VERSION >= VERSION_INT(1,9,0)
73 # include <opusfile.h>
74 #endif
75
76 #include <apetag.h>
77 #include <flacfile.h>
78 #include <mpcfile.h>
79 #include <mpegfile.h>
80 #include <oggfile.h>
81 #include <oggflacfile.h>
82 #include "../demux/xiph_metadata.h"
83
84 #include <aifffile.h>
85 #include <wavfile.h>
86
87 #if defined(TAGLIB_WITH_MP4)
88 # include <mp4file.h>
89 #endif
90
91 #include <speexfile.h>
92 #include <trueaudiofile.h>
93 #include <vorbisfile.h>
94 #include <wavpackfile.h>
95
96 #include <attachedpictureframe.h>
97 #include <textidentificationframe.h>
98 #include <uniquefileidentifierframe.h>
99
100 // taglib is not thread safe
101 static vlc_mutex_t taglib_lock = VLC_STATIC_MUTEX;
102
103 // Local functions
104 static int ReadMeta    ( vlc_object_t * );
105 static int WriteMeta   ( vlc_object_t * );
106
107 vlc_module_begin ()
108     set_capability( "meta reader", 1000 )
109     set_callbacks( ReadMeta, NULL )
110     add_submodule ()
111         set_capability( "meta writer", 50 )
112         set_callbacks( WriteMeta, NULL )
113 vlc_module_end ()
114
115 using namespace TagLib;
116
117 static void ExtractTrackNumberValues( vlc_meta_t* p_meta, const char *psz_value )
118 {
119     unsigned int i_trknum, i_trktot;
120     if( sscanf( psz_value, "%u/%u", &i_trknum, &i_trktot ) == 2 )
121     {
122         char psz_trck[11];
123         snprintf( psz_trck, sizeof( psz_trck ), "%u", i_trknum );
124         vlc_meta_SetTrackNum( p_meta, psz_trck );
125         snprintf( psz_trck, sizeof( psz_trck ), "%u", i_trktot );
126         vlc_meta_Set( p_meta, vlc_meta_TrackTotal, psz_trck );
127     }
128 }
129
130 /**
131  * Read meta information from APE tags
132  * @param tag: the APE tag
133  * @param p_demux_meta: the demuxer meta
134  * @param p_meta: the meta
135  */
136 static void ReadMetaFromAPE( APE::Tag* tag, demux_meta_t* p_demux_meta, vlc_meta_t* p_meta )
137 {
138     APE::Item item;
139
140     item = tag->itemListMap()["COVER ART (FRONT)"];
141     if( !item.isEmpty() )
142     {
143         input_attachment_t *p_attachment;
144
145         const ByteVector picture = item.value();
146         const char *p_data = picture.data();
147         unsigned i_data = picture.size();
148
149         size_t desc_len = strnlen(p_data, i_data);
150         if (desc_len < i_data) {
151             const char *psz_name = p_data;
152             p_data += desc_len + 1; /* '\0' */
153             i_data -= desc_len + 1;
154             msg_Dbg( p_demux_meta, "Found embedded art: %s (%s) is %u bytes",
155                      psz_name, "image/jpeg", i_data );
156
157             p_attachment = vlc_input_attachment_New( "cover", "image/jpeg",
158                                     psz_name, p_data, i_data );
159             if( p_attachment )
160                 TAB_APPEND_CAST( (input_attachment_t**),
161                                  p_demux_meta->i_attachments, p_demux_meta->attachments,
162                                  p_attachment );
163
164             vlc_meta_SetArtURL( p_meta, "attachment://cover" );
165         }
166     }
167
168 #define SET( keyName, metaName ) \
169     item = tag->itemListMap()[keyName]; \
170     if( !item.isEmpty() ) vlc_meta_Set##metaName( p_meta, item.toString().toCString( true ) );
171
172 #define SET_EXTRA( keyName, metaName ) \
173     item = tag->itemListMap()[keyName]; \
174     if( !item.isEmpty() ) vlc_meta_AddExtra( p_meta, metaName, item.toString().toCString( true ) );
175
176     SET( "ALBUM", Album );
177     SET( "ARTIST", Artist );
178     SET( "COMMENT", Description );
179     SET( "GENRE", Genre );
180     SET( "TITLE", Title );
181     SET( "COPYRIGHT", Copyright );
182     SET( "LANGUAGE", Language );
183     SET( "PUBLISHER", Publisher );
184     SET( "MUSICBRAINZ_TRACKID", TrackID );
185
186     SET_EXTRA( "MUSICBRAINZ_ALBUMID", VLC_META_EXTRA_MB_ALBUMID );
187
188 #undef SET
189 #undef SET_EXTRA
190
191     /* */
192     item = tag->itemListMap()["TRACK"];
193     if( !item.isEmpty() )
194     {
195         ExtractTrackNumberValues( p_meta, item.toString().toCString( true ) );
196     }
197 }
198
199
200 /**
201  * Read meta information from APE tags
202  * @param tag: the APE tag
203  * @param p_demux_meta: the demuxer meta
204  * @param p_meta: the meta
205  */
206 static void ReadMetaFromASF( ASF::Tag* tag, demux_meta_t* p_demux_meta, vlc_meta_t* p_meta )
207 {
208
209     ASF::AttributeList list;
210 #define SET( keyName, metaName )                                                     \
211     if( tag->attributeListMap().contains(keyName) )                                  \
212     {                                                                                \
213         list = tag->attributeListMap()[keyName];                                     \
214         vlc_meta_Set##metaName( p_meta, list.front().toString().toCString( true ) ); \
215     }
216
217 #define SET_EXTRA( keyName, metaName )                                                     \
218     if( tag->attributeListMap().contains(keyName) )                                  \
219     {                                                                                \
220         list = tag->attributeListMap()[keyName];                                     \
221         vlc_meta_AddExtra( p_meta, metaName, list.front().toString().toCString( true ) ); \
222     }
223
224     SET("MusicBrainz/Track Id", TrackID );
225     SET_EXTRA("MusicBrainz/Album Id", VLC_META_EXTRA_MB_ALBUMID );
226
227 #undef SET
228 #undef SET_EXTRA
229
230 #ifdef TAGLIB_HAVE_ASFPICTURE_H
231     // List the pictures
232     list = tag->attributeListMap()["WM/Picture"];
233     ASF::AttributeList::Iterator iter;
234     for( iter = list.begin(); iter != list.end(); iter++ )
235     {
236         const ASF::Picture asfPicture = (*iter).toPicture();
237         const ByteVector picture = asfPicture.picture();
238         const char *psz_mime = asfPicture.mimeType().toCString();
239         const char *p_data = picture.data();
240         const unsigned i_data = picture.size();
241         char *psz_name;
242         input_attachment_t *p_attachment;
243
244         if( asfPicture.description().size() > 0 )
245             psz_name = strdup( asfPicture.description().toCString( true ) );
246         else
247         {
248             if( asprintf( &psz_name, "%i", asfPicture.type() ) == -1 )
249                 continue;
250         }
251
252         msg_Dbg( p_demux_meta, "Found embedded art: %s (%s) is %u bytes",
253                  psz_name, psz_mime, i_data );
254
255         p_attachment = vlc_input_attachment_New( psz_name, psz_mime,
256                                 psz_name, p_data, i_data );
257         if( p_attachment )
258             TAB_APPEND_CAST( (input_attachment_t**),
259                              p_demux_meta->i_attachments, p_demux_meta->attachments,
260                              p_attachment );
261         free( psz_name );
262
263         char *psz_url;
264         if( asprintf( &psz_url, "attachment://%s",
265                       p_attachment->psz_name ) == -1 )
266             continue;
267         vlc_meta_SetArtURL( p_meta, psz_url );
268         free( psz_url );
269     }
270 #endif
271 }
272
273
274 /**
275  * Read meta information from id3v2 tags
276  * @param tag: the id3v2 tag
277  * @param p_demux_meta: the demuxer meta
278  * @param p_meta: the meta
279  */
280 static void ReadMetaFromId3v2( ID3v2::Tag* tag, demux_meta_t* p_demux_meta, vlc_meta_t* p_meta )
281 {
282     // Get the unique file identifier
283     ID3v2::FrameList list = tag->frameListMap()["UFID"];
284     ID3v2::FrameList::Iterator iter;
285     for( iter = list.begin(); iter != list.end(); iter++ )
286     {
287         ID3v2::UniqueFileIdentifierFrame* p_ufid =
288                 dynamic_cast<ID3v2::UniqueFileIdentifierFrame*>(*iter);
289         if( !p_ufid )
290             continue;
291         const char *owner = p_ufid->owner().toCString();
292         if (!strcmp( owner, "http://musicbrainz.org" ))
293         {
294             /* ID3v2 UFID contains up to 64 bytes binary data
295              * but in our case it will be a '\0'
296              * terminated string */
297             char psz_ufid[64];
298             int max_size = __MIN( p_ufid->identifier().size(), 63);
299             strncpy( psz_ufid, p_ufid->identifier().data(), max_size );
300             psz_ufid[max_size] = '\0';
301             vlc_meta_SetTrackID( p_meta, psz_ufid );
302         }
303     }
304
305     // Get the use text
306     list = tag->frameListMap()["TXXX"];
307     for( iter = list.begin(); iter != list.end(); iter++ )
308     {
309         ID3v2::UserTextIdentificationFrame* p_txxx =
310                 dynamic_cast<ID3v2::UserTextIdentificationFrame*>(*iter);
311         if( !p_txxx )
312             continue;
313         if( !strcmp( p_txxx->description().toCString( true ), "TRACKTOTAL" ) )
314         {
315             vlc_meta_Set( p_meta, vlc_meta_TrackTotal, p_txxx->fieldList().back().toCString( true ) );
316             continue;
317         }
318         if( !strcmp( p_txxx->description().toCString( true ), "MusicBrainz Album Id" ) )
319         {
320             vlc_meta_AddExtra( p_meta, VLC_META_EXTRA_MB_ALBUMID, p_txxx->fieldList().back().toCString( true ) );
321             continue;
322         }
323         vlc_meta_AddExtra( p_meta, p_txxx->description().toCString( true ),
324                            p_txxx->fieldList().back().toCString( true ) );
325     }
326
327     // Get some more information
328 #define SET( tagName, metaName )                                               \
329     list = tag->frameListMap()[tagName];                                       \
330     if( !list.isEmpty() )                                                      \
331         vlc_meta_Set##metaName( p_meta,                                        \
332                                 (*list.begin())->toString().toCString( true ) );
333
334     SET( "TCOP", Copyright );
335     SET( "TENC", EncodedBy );
336     SET( "TLAN", Language );
337     SET( "TPUB", Publisher );
338
339 #undef SET
340
341     /* */
342     list = tag->frameListMap()["TRCK"];
343     if( !list.isEmpty() )
344     {
345         ExtractTrackNumberValues( p_meta, (*list.begin())->toString().toCString( true ) );
346     }
347
348     /* Preferred type of image
349      * The 21 types are defined in id3v2 standard:
350      * http://www.id3.org/id3v2.4.0-frames */
351     static const int pi_cover_score[] = {
352         0,  /* Other */
353         5,  /* 32x32 PNG image that should be used as the file icon */
354         4,  /* File icon of a different size or format. */
355         20, /* Front cover image of the album. */
356         19, /* Back cover image of the album. */
357         13, /* Inside leaflet page of the album. */
358         18, /* Image from the album itself. */
359         17, /* Picture of the lead artist or soloist. */
360         16, /* Picture of the artist or performer. */
361         14, /* Picture of the conductor. */
362         15, /* Picture of the band or orchestra. */
363         9,  /* Picture of the composer. */
364         8,  /* Picture of the lyricist or text writer. */
365         7,  /* Picture of the recording location or studio. */
366         10, /* Picture of the artists during recording. */
367         11, /* Picture of the artists during performance. */
368         6,  /* Picture from a movie or video related to the track. */
369         1,  /* Picture of a large, coloured fish. */
370         12, /* Illustration related to the track. */
371         3,  /* Logo of the band or performer. */
372         2   /* Logo of the publisher (record company). */
373     };
374     #define PI_COVER_SCORE_SIZE (sizeof (pi_cover_score) / sizeof (pi_cover_score[0]))
375     int i_score = -1;
376
377     // Try now to get embedded art
378     list = tag->frameListMap()[ "APIC" ];
379     if( list.isEmpty() )
380         return;
381
382     TAB_INIT( p_demux_meta->i_attachments, p_demux_meta->attachments );
383     for( iter = list.begin(); iter != list.end(); iter++ )
384     {
385         ID3v2::AttachedPictureFrame* p_apic =
386             dynamic_cast<ID3v2::AttachedPictureFrame*>(*iter);
387         if( !p_apic )
388             continue;
389         input_attachment_t *p_attachment;
390
391         const char *psz_mime;
392         char *psz_name, *psz_description;
393
394         // Get the mime and description of the image.
395         // If the description is empty, take the type as a description
396         psz_mime = p_apic->mimeType().toCString( true );
397         if( p_apic->description().size() > 0 )
398             psz_description = strdup( p_apic->description().toCString( true ) );
399         else
400         {
401             if( asprintf( &psz_description, "%i", p_apic->type() ) == -1 )
402                 psz_description = NULL;
403         }
404
405         if( !psz_description )
406             continue;
407         psz_name = psz_description;
408
409         /* some old iTunes version not only sets incorrectly the mime type
410          * or the description of the image,
411          * but also embeds incorrectly the image.
412          * Recent versions seem to behave correctly */
413         if( !strncmp( psz_mime, "PNG", 3 ) ||
414             !strncmp( psz_name, "\xC2\x89PNG", 5 ) )
415         {
416             msg_Warn( p_demux_meta, "Invalid picture embedded by broken iTunes version" );
417             free( psz_description );
418             continue;
419         }
420
421         const ByteVector picture = p_apic->picture();
422         const char *p_data = picture.data();
423         const unsigned i_data = picture.size();
424
425         msg_Dbg( p_demux_meta, "Found embedded art: %s (%s) is %u bytes",
426                  psz_name, psz_mime, i_data );
427
428         p_attachment = vlc_input_attachment_New( psz_name, psz_mime,
429                                 psz_description, p_data, i_data );
430         if( !p_attachment )
431         {
432             free( psz_description );
433             continue;
434         }
435         TAB_APPEND_CAST( (input_attachment_t**),
436                          p_demux_meta->i_attachments, p_demux_meta->attachments,
437                          p_attachment );
438         free( psz_description );
439
440         unsigned i_pic_type = p_apic->type();
441         if( i_pic_type >= PI_COVER_SCORE_SIZE )
442             i_pic_type = 0; // Defaults to "Other"
443
444         if( pi_cover_score[i_pic_type] > i_score )
445         {
446             i_score = pi_cover_score[i_pic_type];
447             char *psz_url;
448             if( asprintf( &psz_url, "attachment://%s",
449                           p_attachment->psz_name ) == -1 )
450                 continue;
451             vlc_meta_SetArtURL( p_meta, psz_url );
452             free( psz_url );
453         }
454     }
455 }
456
457
458 /**
459  * Read the meta information from XiphComments
460  * @param tag: the Xiph Comment
461  * @param p_demux_meta: the demuxer meta
462  * @param p_meta: the meta
463  */
464 static void ReadMetaFromXiph( Ogg::XiphComment* tag, demux_meta_t* p_demux_meta, vlc_meta_t* p_meta )
465 {
466     StringList list;
467     bool hasTrackTotal = false;
468 #define SET( keyName, metaName )                                               \
469     list = tag->fieldListMap()[keyName];                                       \
470     if( !list.isEmpty() )                                                      \
471         vlc_meta_Set##metaName( p_meta, (*list.begin()).toCString( true ) );
472
473 #define SET_EXTRA( keyName, metaName ) \
474     list = tag->fieldListMap()[keyName]; \
475     if( !list.isEmpty() ) \
476         vlc_meta_AddExtra( p_meta, keyName, (*list.begin()).toCString( true ) );
477
478     SET( "COPYRIGHT", Copyright );
479     SET( "ORGANIZATION", Publisher );
480     SET( "DATE", Date );
481     SET( "ENCODER", EncodedBy );
482     SET( "RATING", Rating );
483     SET( "LANGUAGE", Language );
484     SET( "MUSICBRAINZ_TRACKID", TrackID );
485
486     SET_EXTRA( "MUSICBRAINZ_ALBUMID", VLC_META_EXTRA_MB_ALBUMID );
487 #undef SET
488 #undef SET_EXTRA
489
490     list = tag->fieldListMap()["TRACKNUMBER"];
491     if( !list.isEmpty() )
492     {
493         const char *psz_value;
494         unsigned short u_track;
495         unsigned short u_total;
496         psz_value = (*list.begin()).toCString( true );
497         if( sscanf( psz_value, "%hu/%hu", &u_track, &u_total ) == 2)
498         {
499             char str[6];
500             snprintf(str, 6, "%u", u_track);
501             vlc_meta_SetTrackNum( p_meta, str);
502             snprintf(str, 6, "%u", u_total);
503             vlc_meta_SetTrackTotal( p_meta, str);
504             hasTrackTotal = true;
505         }
506         else
507             vlc_meta_SetTrackNum( p_meta, psz_value);
508     }
509     if( !hasTrackTotal )
510     {
511         list = tag->fieldListMap()["TRACKTOTAL"];
512         if( list.isEmpty() )
513             list = tag->fieldListMap()["TOTALTRACKS"];
514         if( !list.isEmpty() )
515             vlc_meta_SetTrackTotal( p_meta, (*list.begin()).toCString( true ) );
516     }
517
518     // Try now to get embedded art
519     StringList mime_list = tag->fieldListMap()[ "COVERARTMIME" ];
520     StringList art_list = tag->fieldListMap()[ "COVERART" ];
521
522     input_attachment_t *p_attachment;
523
524     if( mime_list.size() != 0 && art_list.size() != 0 )
525     {
526         // We get only the first covert art
527         if( mime_list.size() > 1 || art_list.size() > 1 )
528             msg_Warn( p_demux_meta, "Found %i embedded arts, so using only the first one",
529                     art_list.size() );
530
531         const char* psz_name = "cover";
532         const char* psz_mime = mime_list[0].toCString(true);
533         const char* psz_description = "cover";
534
535         uint8_t *p_data;
536         int i_data = vlc_b64_decode_binary( &p_data, art_list[0].toCString(true) );
537
538         msg_Dbg( p_demux_meta, "Found embedded art: %s (%s) is %i bytes",
539                 psz_name, psz_mime, i_data );
540
541         p_attachment = vlc_input_attachment_New( psz_name, psz_mime,
542                 psz_description, p_data, i_data );
543         free( p_data );
544     }
545     else
546     {
547         art_list = tag->fieldListMap()[ "METADATA_BLOCK_PICTURE" ];
548         if( art_list.size() == 0 )
549             return;
550
551         uint8_t *p_data;
552         int i_cover_score;
553         int i_cover_idx;
554         int i_data = vlc_b64_decode_binary( &p_data, art_list[0].toCString(true) );
555         i_cover_score = i_cover_idx = 0;
556         /* TODO: Use i_cover_score / i_cover_idx to select the picture. */
557         p_attachment = ParseFlacPicture( p_data, i_data, 0,
558             &i_cover_score, &i_cover_idx );
559     }
560
561     TAB_INIT( p_demux_meta->i_attachments, p_demux_meta->attachments );
562     if (p_attachment) {
563         TAB_APPEND_CAST( (input_attachment_t**),
564                 p_demux_meta->i_attachments, p_demux_meta->attachments,
565                 p_attachment );
566
567         char *psz_url;
568         if( asprintf( &psz_url, "attachment://%s", p_attachment->psz_name ) != -1 ) {
569             vlc_meta_SetArtURL( p_meta, psz_url );
570             free( psz_url );
571         }
572     }
573 }
574
575
576 #if defined(TAGLIB_WITH_MP4)
577 /**
578  * Read the meta information from mp4 specific tags
579  * @param tag: the mp4 tag
580  * @param p_demux_meta: the demuxer meta
581  * @param p_meta: the meta
582  */
583 static void ReadMetaFromMP4( MP4::Tag* tag, demux_meta_t *p_demux_meta, vlc_meta_t* p_meta )
584 {
585     MP4::Item list;
586 #define SET( keyName, metaName )                                                             \
587     if( tag->itemListMap().contains(keyName) )                                               \
588     {                                                                                        \
589         list = tag->itemListMap()[keyName];                                                  \
590         vlc_meta_Set##metaName( p_meta, list.toStringList().front().toCString( true ) );     \
591     }
592 #define SET_EXTRA( keyName, metaName )                                                   \
593     if( tag->itemListMap().contains(keyName) )                                  \
594     {                                                                                \
595         list = tag->itemListMap()[keyName];                                     \
596         vlc_meta_AddExtra( p_meta, metaName, list.toStringList().front().toCString( true ) ); \
597     }
598
599     SET("----:com.apple.iTunes:MusicBrainz Track Id", TrackID );
600     SET_EXTRA("----:com.apple.iTunes:MusicBrainz Album Id", VLC_META_EXTRA_MB_ALBUMID );
601
602 #undef SET
603 #undef SET_EXTRA
604
605     if( tag->itemListMap().contains("covr") )
606     {
607         MP4::CoverArtList list = tag->itemListMap()["covr"].toCoverArtList();
608         const char *psz_format = list[0].format() == MP4::CoverArt::PNG ? "image/png" : "image/jpeg";
609
610         msg_Dbg( p_demux_meta, "Found embedded art (%s) is %i bytes",
611                  psz_format, list[0].data().size() );
612
613         TAB_INIT( p_demux_meta->i_attachments, p_demux_meta->attachments );
614         input_attachment_t *p_attachment =
615                 vlc_input_attachment_New( "cover", psz_format, "cover",
616                                           list[0].data().data(), list[0].data().size() );
617         TAB_APPEND_CAST( (input_attachment_t**),
618                          p_demux_meta->i_attachments, p_demux_meta->attachments,
619                          p_attachment );
620         vlc_meta_SetArtURL( p_meta, "attachment://cover" );
621     }
622 }
623 #endif
624
625
626 /**
627  * Get the tags from the file using TagLib
628  * @param p_this: the demux object
629  * @return VLC_SUCCESS if the operation success
630  */
631 static int ReadMeta( vlc_object_t* p_this)
632 {
633     vlc_mutex_locker locker (&taglib_lock);
634     demux_meta_t*   p_demux_meta = (demux_meta_t *)p_this;
635     demux_t*        p_demux = p_demux_meta->p_demux;
636     vlc_meta_t*     p_meta;
637     FileRef f;
638
639     p_demux_meta->p_meta = NULL;
640     if( strcmp( p_demux->psz_access, "file" ) )
641         return VLC_EGENERIC;
642
643     char *psz_path = strdup( p_demux->psz_file );
644     if( !psz_path )
645         return VLC_ENOMEM;
646
647 #if defined(_WIN32)
648     wchar_t *wpath = ToWide( psz_path );
649     if( wpath == NULL )
650     {
651         free( psz_path );
652         return VLC_EGENERIC;
653     }
654     f = FileRef( wpath );
655     free( wpath );
656 #else
657     f = FileRef( psz_path );
658 #endif
659     free( psz_path );
660
661     if( f.isNull() )
662         return VLC_EGENERIC;
663     if( !f.tag() || f.tag()->isEmpty() )
664         return VLC_EGENERIC;
665
666     p_demux_meta->p_meta = p_meta = vlc_meta_New();
667     if( !p_meta )
668         return VLC_ENOMEM;
669
670
671     // Read the tags from the file
672     Tag* p_tag = f.tag();
673
674 #define SET( tag, meta )                                                       \
675     if( !p_tag->tag().isNull() && !p_tag->tag().isEmpty() )                    \
676         vlc_meta_Set##meta( p_meta, p_tag->tag().toCString(true) )
677 #define SETINT( tag, meta )                                                    \
678     if( p_tag->tag() )                                                         \
679     {                                                                          \
680         char psz_tmp[10];                                                      \
681         snprintf( psz_tmp, 10, "%d", p_tag->tag() );                           \
682         vlc_meta_Set##meta( p_meta, psz_tmp );                                 \
683     }
684
685     SET( title, Title );
686     SET( artist, Artist );
687     SET( album, Album );
688     SET( comment, Description );
689     SET( genre, Genre );
690     SETINT( year, Date );
691     SETINT( track, TrackNum );
692
693 #undef SETINT
694 #undef SET
695
696
697     // Try now to read special tags
698 #ifdef TAGLIB_HAVE_APEFILE_H
699     if( APE::File* ape = dynamic_cast<APE::File*>(f.file()) )
700     {
701         if( ape->APETag() )
702             ReadMetaFromAPE( ape->APETag(), p_demux_meta, p_meta );
703     }
704     else
705 #endif
706 #ifdef TAGLIB_WITH_ASF
707     if( ASF::File* asf = dynamic_cast<ASF::File*>(f.file()) )
708     {
709         if( asf->tag() )
710             ReadMetaFromASF( asf->tag(), p_demux_meta, p_meta );
711     }
712     else
713 #endif
714     if( FLAC::File* flac = dynamic_cast<FLAC::File*>(f.file()) )
715     {
716         if( flac->ID3v2Tag() )
717             ReadMetaFromId3v2( flac->ID3v2Tag(), p_demux_meta, p_meta );
718         else if( flac->xiphComment() )
719             ReadMetaFromXiph( flac->xiphComment(), p_demux_meta, p_meta );
720     }
721 #if defined(TAGLIB_WITH_MP4)
722     else if( MP4::File *mp4 = dynamic_cast<MP4::File*>(f.file()) )
723     {
724         if( mp4->tag() )
725             ReadMetaFromMP4( mp4->tag(), p_demux_meta, p_meta );
726     }
727 #endif
728     else if( MPC::File* mpc = dynamic_cast<MPC::File*>(f.file()) )
729     {
730         if( mpc->APETag() )
731             ReadMetaFromAPE( mpc->APETag(), p_demux_meta, p_meta );
732     }
733     else if( MPEG::File* mpeg = dynamic_cast<MPEG::File*>(f.file()) )
734     {
735         if( mpeg->ID3v2Tag() )
736             ReadMetaFromId3v2( mpeg->ID3v2Tag(), p_demux_meta, p_meta );
737         else if( mpeg->APETag() )
738             ReadMetaFromAPE( mpeg->APETag(), p_demux_meta, p_meta );
739     }
740     else if( dynamic_cast<Ogg::File*>(f.file()) )
741     {
742         if( Ogg::FLAC::File* ogg_flac = dynamic_cast<Ogg::FLAC::File*>(f.file()))
743             ReadMetaFromXiph( ogg_flac->tag(), p_demux_meta, p_meta );
744         else if( Ogg::Speex::File* ogg_speex = dynamic_cast<Ogg::Speex::File*>(f.file()) )
745             ReadMetaFromXiph( ogg_speex->tag(), p_demux_meta, p_meta );
746         else if( Ogg::Vorbis::File* ogg_vorbis = dynamic_cast<Ogg::Vorbis::File*>(f.file()) )
747             ReadMetaFromXiph( ogg_vorbis->tag(), p_demux_meta, p_meta );
748 #if defined(TAGLIB_OPUSFILE_H)
749         else if( Ogg::Opus::File* ogg_opus = dynamic_cast<Ogg::Opus::File*>(f.file()) )
750             ReadMetaFromXiph( ogg_opus->tag(), p_demux_meta, p_meta );
751 #endif
752     }
753     else if( dynamic_cast<RIFF::File*>(f.file()) )
754     {
755         if( RIFF::AIFF::File* riff_aiff = dynamic_cast<RIFF::AIFF::File*>(f.file()) )
756             ReadMetaFromId3v2( riff_aiff->tag(), p_demux_meta, p_meta );
757         else if( RIFF::WAV::File* riff_wav = dynamic_cast<RIFF::WAV::File*>(f.file()) )
758             ReadMetaFromId3v2( riff_wav->tag(), p_demux_meta, p_meta );
759     }
760     else if( TrueAudio::File* trueaudio = dynamic_cast<TrueAudio::File*>(f.file()) )
761     {
762         if( trueaudio->ID3v2Tag() )
763             ReadMetaFromId3v2( trueaudio->ID3v2Tag(), p_demux_meta, p_meta );
764     }
765     else if( WavPack::File* wavpack = dynamic_cast<WavPack::File*>(f.file()) )
766     {
767         if( wavpack->APETag() )
768             ReadMetaFromAPE( wavpack->APETag(), p_demux_meta, p_meta );
769     }
770
771     return VLC_SUCCESS;
772 }
773
774
775 /**
776  * Write meta information to APE tags
777  * @param tag: the APE tag
778  * @param p_item: the input item
779  */
780 static void WriteMetaToAPE( APE::Tag* tag, input_item_t* p_item )
781 {
782     char* psz_meta;
783 #define WRITE( metaName, keyName )                      \
784     psz_meta = input_item_Get##metaName( p_item );      \
785     if( psz_meta )                                      \
786     {                                                   \
787         String key( keyName, String::UTF8 );            \
788         String value( psz_meta, String::UTF8 );         \
789         tag->addValue( key, value, true );              \
790     }                                                   \
791     free( psz_meta );
792
793     WRITE( Copyright, "COPYRIGHT" );
794     WRITE( Language, "LANGUAGE" );
795     WRITE( Publisher, "PUBLISHER" );
796     WRITE( TrackID, "MUSICBRAINZ_TRACKID" );
797 #undef WRITE
798 }
799
800
801 /**
802  * Write meta information to id3v2 tags
803  * @param tag: the id3v2 tag
804  * @param p_input: the input item
805  */
806 static void WriteMetaToId3v2( ID3v2::Tag* tag, input_item_t* p_item )
807 {
808     char* psz_meta;
809 #define WRITE( metaName, tagName )                                            \
810     psz_meta = input_item_Get##metaName( p_item );                            \
811     if( psz_meta )                                                            \
812     {                                                                         \
813         ByteVector p_byte( tagName, 4 );                                      \
814         tag->removeFrames( p_byte );                                         \
815         ID3v2::TextIdentificationFrame* p_frame =                             \
816             new ID3v2::TextIdentificationFrame( p_byte, String::UTF8 );       \
817         p_frame->setText( psz_meta );                                         \
818         tag->addFrame( p_frame );                                             \
819     }                                                                         \
820     free( psz_meta );
821
822     WRITE( Copyright, "TCOP" );
823     WRITE( EncodedBy, "TENC" );
824     WRITE( Language,  "TLAN" );
825     WRITE( Publisher, "TPUB" );
826
827 #undef WRITE
828     /* Known TXXX frames */
829     ID3v2::FrameList list = tag->frameListMap()["TXXX"];
830
831 #define WRITETXXX( metaName, txxName )\
832     psz_meta = input_item_Get##metaName( p_item );                                       \
833     if ( psz_meta )                                                                      \
834     {                                                                                    \
835         ID3v2::UserTextIdentificationFrame *p_txxx;                                      \
836         for( ID3v2::FrameList::Iterator iter = list.begin(); iter != list.end(); iter++ )\
837         {                                                                                \
838             p_txxx = dynamic_cast<ID3v2::UserTextIdentificationFrame*>(*iter);           \
839             if( !p_txxx )                                                                \
840                 continue;                                                                \
841             if( !strcmp( p_txxx->description().toCString( true ), txxName ) )            \
842             {                                                                            \
843                 p_txxx->setText( psz_meta );                                             \
844                 FREENULL( psz_meta );                                                    \
845                 break;                                                                   \
846             }                                                                            \
847         }                                                                                \
848         if( psz_meta ) /* not found in existing custom fields */                         \
849         {                                                                                \
850             ByteVector p_byte( "TXXX", 4 );                                              \
851             p_txxx = new ID3v2::UserTextIdentificationFrame( p_byte );                   \
852             p_txxx->setDescription( txxName );                                           \
853             p_txxx->setText( psz_meta );                                                 \
854             free( psz_meta );                                                            \
855             tag->addFrame( p_txxx );                                                     \
856         }                                                                                \
857     }
858
859     WRITETXXX( TrackTotal, "TRACKTOTAL" );
860
861 #undef WRITETXXX
862
863     /* Write album art */
864     char *psz_url = input_item_GetArtworkURL( p_item );
865     if( psz_url == NULL )
866         return;
867
868     char *psz_path = make_path( psz_url );
869     free( psz_url );
870     if( psz_path == NULL )
871         return;
872
873     const char *psz_mime = vlc_mime_Ext2Mime( psz_path );
874
875     FILE *p_file = vlc_fopen( psz_path, "rb" );
876     if( p_file == NULL )
877     {
878         free( psz_path );
879         return;
880     }
881
882     struct stat st;
883     if( vlc_stat( psz_path, &st ) == -1 )
884     {
885         free( psz_path );
886         fclose( p_file );
887         return;
888     }
889     off_t file_size = st.st_size;
890
891     free( psz_path );
892
893     /* Limit picture size to 10MiB */
894     if( file_size > 10485760 )
895     {
896       fclose( p_file );
897       return;
898     }
899
900     char *p_buffer = new (std::nothrow) char[file_size];
901     if( p_buffer == NULL )
902     {
903         fclose( p_file );
904         return;
905     }
906
907     if( fread( p_buffer, 1, file_size, p_file ) != (unsigned)file_size )
908     {
909         fclose( p_file );
910         delete[] p_buffer;
911         return;
912     }
913     fclose( p_file );
914
915     ByteVector data( p_buffer, file_size );
916     delete[] p_buffer;
917
918     ID3v2::FrameList frames = tag->frameList( "APIC" );
919     ID3v2::AttachedPictureFrame *frame = NULL;
920     if( frames.isEmpty() )
921     {
922         frame = new TagLib::ID3v2::AttachedPictureFrame;
923         tag->addFrame( frame );
924     }
925     else
926     {
927         frame = static_cast<ID3v2::AttachedPictureFrame *>( frames.back() );
928     }
929
930     frame->setPicture( data );
931     frame->setMimeType( psz_mime );
932 }
933
934
935 /**
936  * Write the meta information to XiphComments
937  * @param tag: the Xiph Comment
938  * @param p_input: the input item
939  */
940 static void WriteMetaToXiph( Ogg::XiphComment* tag, input_item_t* p_item )
941 {
942     char* psz_meta;
943 #define WRITE( metaName, keyName )                      \
944     psz_meta = input_item_Get##metaName( p_item );      \
945     if( psz_meta )                                      \
946     {                                                   \
947         String key( keyName, String::UTF8 );            \
948         String value( psz_meta, String::UTF8 );         \
949         tag->addField( key, value, true );              \
950     }                                                   \
951     free( psz_meta );
952
953     WRITE( TrackNum, "TRACKNUMBER" );
954     WRITE( TrackTotal, "TRACKTOTAL" );
955     WRITE( Copyright, "COPYRIGHT" );
956     WRITE( Publisher, "ORGANIZATION" );
957     WRITE( Date, "DATE" );
958     WRITE( EncodedBy, "ENCODER" );
959     WRITE( Rating, "RATING" );
960     WRITE( Language, "LANGUAGE" );
961     WRITE( TrackID, "MUSICBRAINZ_TRACKID" );
962 #undef WRITE
963 }
964
965
966 /**
967  * Set the tags to the file using TagLib
968  * @param p_this: the demux object
969  * @return VLC_SUCCESS if the operation success
970  */
971
972 static int WriteMeta( vlc_object_t *p_this )
973 {
974     vlc_mutex_locker locker (&taglib_lock);
975     meta_export_t *p_export = (meta_export_t *)p_this;
976     input_item_t *p_item = p_export->p_item;
977     FileRef f;
978
979     if( !p_item )
980     {
981         msg_Err( p_this, "Can't save meta data of an empty input" );
982         return VLC_EGENERIC;
983     }
984
985 #if defined(_WIN32)
986     wchar_t *wpath = ToWide( p_export->psz_file );
987     if( wpath == NULL )
988         return VLC_EGENERIC;
989     f = FileRef( wpath );
990     free( wpath );
991 #else
992     f = FileRef( p_export->psz_file );
993 #endif
994
995     if( f.isNull() || !f.tag() || f.file()->readOnly() )
996     {
997         msg_Err( p_this, "File %s can't be opened for tag writing",
998                  p_export->psz_file );
999         return VLC_EGENERIC;
1000     }
1001
1002     msg_Dbg( p_this, "Writing metadata for %s", p_export->psz_file );
1003
1004     Tag *p_tag = f.tag();
1005
1006     char *psz_meta;
1007
1008 #define SET( a, b )                                             \
1009     psz_meta = input_item_Get ## a( p_item );                   \
1010     if( psz_meta )                                              \
1011     {                                                           \
1012         String tmp( psz_meta, String::UTF8 );                   \
1013         p_tag->set##b( tmp );                                   \
1014     }                                                           \
1015     free( psz_meta );
1016
1017     // Saving all common fields
1018     // If the title is empty, use the name
1019     SET( TitleFbName, Title );
1020     SET( Artist, Artist );
1021     SET( Album, Album );
1022     SET( Description, Comment );
1023     SET( Genre, Genre );
1024
1025 #undef SET
1026
1027     psz_meta = input_item_GetDate( p_item );
1028     if( !EMPTY_STR(psz_meta) ) p_tag->setYear( atoi( psz_meta ) );
1029     else p_tag->setYear( 0 );
1030     free( psz_meta );
1031
1032     psz_meta = input_item_GetTrackNum( p_item );
1033     if( !EMPTY_STR(psz_meta) ) p_tag->setTrack( atoi( psz_meta ) );
1034     else p_tag->setTrack( 0 );
1035     free( psz_meta );
1036
1037
1038     // Try now to write special tags
1039 #ifdef TAGLIB_HAVE_APEFILE_H
1040     if( APE::File* ape = dynamic_cast<APE::File*>(f.file()) )
1041     {
1042         if( ape->APETag() )
1043             WriteMetaToAPE( ape->APETag(), p_item );
1044     }
1045     else
1046 #endif
1047     if( FLAC::File* flac = dynamic_cast<FLAC::File*>(f.file()) )
1048     {
1049         if( flac->ID3v2Tag() )
1050             WriteMetaToId3v2( flac->ID3v2Tag(), p_item );
1051         else if( flac->xiphComment() )
1052             WriteMetaToXiph( flac->xiphComment(), p_item );
1053     }
1054     else if( MPC::File* mpc = dynamic_cast<MPC::File*>(f.file()) )
1055     {
1056         if( mpc->APETag() )
1057             WriteMetaToAPE( mpc->APETag(), p_item );
1058     }
1059     else if( MPEG::File* mpeg = dynamic_cast<MPEG::File*>(f.file()) )
1060     {
1061         if( mpeg->ID3v2Tag() )
1062             WriteMetaToId3v2( mpeg->ID3v2Tag(), p_item );
1063         else if( mpeg->APETag() )
1064             WriteMetaToAPE( mpeg->APETag(), p_item );
1065     }
1066     else if( dynamic_cast<Ogg::File*>(f.file()) )
1067     {
1068         if( Ogg::FLAC::File* ogg_flac = dynamic_cast<Ogg::FLAC::File*>(f.file()))
1069             WriteMetaToXiph( ogg_flac->tag(), p_item );
1070         else if( Ogg::Speex::File* ogg_speex = dynamic_cast<Ogg::Speex::File*>(f.file()) )
1071             WriteMetaToXiph( ogg_speex->tag(), p_item );
1072         else if( Ogg::Vorbis::File* ogg_vorbis = dynamic_cast<Ogg::Vorbis::File*>(f.file()) )
1073             WriteMetaToXiph( ogg_vorbis->tag(), p_item );
1074 #if defined(TAGLIB_OPUSFILE_H)
1075         else if( Ogg::Opus::File* ogg_opus = dynamic_cast<Ogg::Opus::File*>(f.file()) )
1076             WriteMetaToXiph( ogg_opus->tag(), p_item );
1077 #endif
1078     }
1079     else if( dynamic_cast<RIFF::File*>(f.file()) )
1080     {
1081         if( RIFF::AIFF::File* riff_aiff = dynamic_cast<RIFF::AIFF::File*>(f.file()) )
1082             WriteMetaToId3v2( riff_aiff->tag(), p_item );
1083         else if( RIFF::WAV::File* riff_wav = dynamic_cast<RIFF::WAV::File*>(f.file()) )
1084             WriteMetaToId3v2( riff_wav->tag(), p_item );
1085     }
1086     else if( TrueAudio::File* trueaudio = dynamic_cast<TrueAudio::File*>(f.file()) )
1087     {
1088         if( trueaudio->ID3v2Tag() )
1089             WriteMetaToId3v2( trueaudio->ID3v2Tag(), p_item );
1090     }
1091     else if( WavPack::File* wavpack = dynamic_cast<WavPack::File*>(f.file()) )
1092     {
1093         if( wavpack->APETag() )
1094             WriteMetaToAPE( wavpack->APETag(), p_item );
1095     }
1096
1097     // Save the meta data
1098     f.save();
1099
1100     return VLC_SUCCESS;
1101 }
1102