]> git.sesse.net Git - vlc/blob - modules/meta_engine/taglib.cpp
Remove stdlib.h
[vlc] / modules / meta_engine / taglib.cpp
1 /*****************************************************************************
2  * taglib.cpp: Taglib tag parser/writer
3  *****************************************************************************
4  * Copyright (C) 2003-2006 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Clément Stenac <zorglub@videolan.org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 #include <vlc/vlc.h>
25 #include <vlc_playlist.h>
26 #include <vlc_meta.h>
27 #include <vlc_demux.h>
28
29 #include <fileref.h>
30 #include <tag.h>
31 #include <tstring.h>
32 #include <id3v2tag.h>
33 #include <mpegfile.h>
34 #include <flacfile.h>
35 #if 0
36 #include <oggflacfile.h>
37 #endif
38 #include <flacfile.h>
39 #include <flacproperties.h>
40 #include <vorbisfile.h>
41 #include <vorbisproperties.h>
42 #include <uniquefileidentifierframe.h>
43 #include <textidentificationframe.h>
44 //#include <relativevolumeframe.h> /* parse the tags without taglib helpers? */
45
46 static int  ReadMeta    ( vlc_object_t * );
47 static int  DownloadArt ( vlc_object_t * );
48 static int  WriteMeta   ( vlc_object_t * );
49
50 vlc_module_begin();
51     set_capability( "meta reader", 1000 );
52     set_callbacks( ReadMeta, NULL );
53     add_submodule();
54         set_capability( "art downloader", 50 );
55         set_callbacks( DownloadArt, NULL );
56     add_submodule();
57         set_capability( "meta writer", 50 );
58         set_callbacks( WriteMeta, NULL );
59 vlc_module_end();
60
61 static bool checkID3Image( const TagLib::ID3v2::Tag *tag )
62 {
63     TagLib::ID3v2::FrameList l = tag->frameListMap()[ "APIC" ];
64     return !l.isEmpty();
65 }
66
67 /* Try detecting embedded art */
68 static void DetectImage( TagLib::FileRef f, vlc_meta_t *p_meta )
69 {
70     if( TagLib::MPEG::File *mpeg =
71                dynamic_cast<TagLib::MPEG::File *>(f.file() ) )
72     {
73         if( mpeg->ID3v2Tag() && checkID3Image( mpeg->ID3v2Tag() ) )
74             vlc_meta_SetArtURL( p_meta, "APIC" );
75     }
76     else if( TagLib::FLAC::File *flac =
77              dynamic_cast<TagLib::FLAC::File *>(f.file() ) )
78     {
79         if( flac->ID3v2Tag() && checkID3Image( flac->ID3v2Tag() ) )
80             vlc_meta_SetArtURL( p_meta, "APIC" );
81     }
82 #if 0
83 /* This needs special additions to taglib */
84  * else if( TagLib::MP4::File *mp4 =
85                dynamic_cast<TagLib::MP4::File *>( f.file() ) )
86     {
87         TagLib::MP4::Tag *mp4tag =
88                 dynamic_cast<TagLib::MP4::Tag *>( mp4->tag() );
89         if( mp4tag && mp4tag->cover().size() )
90             vlc_meta_SetArtURL( p_meta, "MP4C" );
91     }
92 #endif
93 }
94
95 static int ReadMeta( vlc_object_t *p_this )
96 {
97     demux_t *p_demux = (demux_t *)p_this;
98
99     if( !strncmp( p_demux->psz_access, "file", 4 ) )
100     {
101         if( !p_demux->p_private )
102             p_demux->p_private = (void*)vlc_meta_New();
103         TagLib::FileRef f( p_demux->psz_path );
104
105         if( !f.isNull() )
106         {
107             if( TagLib::Ogg::Vorbis::File *p_ogg_v =
108                 dynamic_cast<TagLib::Ogg::Vorbis::File *>(f.file() ) )
109             {
110                 int i_ogg_v_length = p_ogg_v->audioProperties()->length();
111
112                 input_thread_t *p_input = (input_thread_t *)vlc_object_find( p_demux, VLC_OBJECT_INPUT, FIND_PARENT );
113                 if( p_input )
114                 {
115                     input_item_t *p_item = input_GetItem( p_input );
116                     if( p_item )
117                     {
118                         vlc_mutex_lock( &p_item->lock );
119                         p_item->i_duration = i_ogg_v_length * 1000000;
120                         vlc_mutex_unlock( &p_item->lock );
121                     }
122                     vlc_object_release( p_input );
123                 }
124             }
125 #if 0 /* at this moment, taglib is unable to detect ogg/flac files
126        * becauses type detection is based on file extension:
127        * ogg = ogg/vorbis
128        * flac = flac
129        * ø = ogg/flac
130        */
131             else if( TagLib::Ogg::FLAC::File *p_ogg_f =
132                 dynamic_cast<TagLib::Ogg::FLAC::File *>(f.file() ) )
133             {
134                 long i_ogg_f_length = p_ogg_f->streamLength();
135                 input_thread_t *p_input = (input_thread_t *)vlc_object_find( p_demux, VLC_OBJECT_INPUT, FIND_PARENT );
136                 if( p_input )
137                 {
138                     input_item_t *p_item = input_GetItem( p_input );
139                     if( p_item )
140                     {
141                         vlc_mutex_lock( &p_item->lock );
142                         p_item->i_duration = i_ogg_f_length * 1000000;
143                         vlc_mutex_unlock( &p_item->lock );
144                     }
145                     vlc_object_release( p_input );
146                 }
147             }
148 #endif
149             else if( TagLib::FLAC::File *p_flac =
150                 dynamic_cast<TagLib::FLAC::File *>(f.file() ) )
151             {
152                 long i_flac_length = p_flac->audioProperties()->length();
153                 input_thread_t *p_input = (input_thread_t *)vlc_object_find( p_demux, VLC_OBJECT_INPUT, FIND_PARENT );
154                 if( p_input )
155                 {
156                     input_item_t *p_item = input_GetItem( p_input );
157                     if( p_item )
158                     {
159                         vlc_mutex_lock( &p_item->lock );
160                         p_item->i_duration = i_flac_length * 1000000;
161                         vlc_mutex_unlock( &p_item->lock );
162                     }
163                     vlc_object_release( p_input );
164                 }
165             }
166         }
167
168         if( !f.isNull() && f.tag() && !f.tag()->isEmpty() )
169         {
170             TagLib::Tag *tag = f.tag();
171             vlc_meta_t *p_meta = (vlc_meta_t *)(p_demux->p_private );
172
173 #define SET( foo, bar ) vlc_meta_Set##foo( p_meta, tag->bar ().toCString(true))
174 #define SETINT( foo, bar ) { \
175             char psz_tmp[10]; \
176             snprintf( (char*)psz_tmp, 10, "%d", tag->bar() ); \
177             vlc_meta_Set##foo( p_meta, (char*)psz_tmp ); \
178         }
179
180             SET( Title, title );
181             SET( Artist, artist );
182             SET( Album, album );
183             SET( Description, comment );
184             SET( Genre, genre );
185             SETINT( Date, year );
186             SETINT( Tracknum , track );
187 #undef SET
188 #undef SETINT
189
190             if( TagLib::MPEG::File *p_mpeg =
191                 dynamic_cast<TagLib::MPEG::File *>(f.file() ) )
192             {
193                 if( p_mpeg->ID3v2Tag() )
194                 {
195                     TagLib::ID3v2::Tag *tag = p_mpeg->ID3v2Tag();
196                     TagLib::ID3v2::FrameList list = tag->frameListMap()["UFID"];
197                     TagLib::ID3v2::UniqueFileIdentifierFrame* p_ufid;
198                     for( TagLib::ID3v2::FrameList::Iterator iter = list.begin();
199                             iter != list.end(); iter++ )
200                     {
201                         p_ufid = dynamic_cast<TagLib::ID3v2::UniqueFileIdentifierFrame*>(*iter);
202                         const char *owner = p_ufid->owner().toCString();
203                         if (!strcmp( owner, "http://musicbrainz.org" ))
204                         {
205                             /* ID3v2 UFID contains up to 64 bytes binary data
206                              * but in our case it will be a '\0' 
207                              * terminated string */
208                             char *psz_ufid = (char*) malloc( 64 );
209                             int j = 0;
210                             while( ( j < 63 ) &&
211                                     ( j < p_ufid->identifier().size() ) )
212                                 psz_ufid[j] = p_ufid->identifier()[j++];
213                             psz_ufid[j] = '\0';
214                             vlc_meta_SetTrackID( p_meta, psz_ufid );
215                             free( psz_ufid );
216                         }
217                     }
218
219                     list = tag->frameListMap()["TXXX"];
220                     TagLib::ID3v2::UserTextIdentificationFrame* p_txxx;
221                     for( TagLib::ID3v2::FrameList::Iterator iter = list.begin();
222                             iter != list.end(); iter++ )
223                     {
224                         p_txxx = dynamic_cast<TagLib::ID3v2::UserTextIdentificationFrame*>(*iter);
225                         const char *psz_desc= p_txxx->description().toCString();
226 #if 0 /* musicbrainz artist and album id: not useful (yet?) */
227                         if( !strncmp( psz_desc, "MusicBrainz Artist Id", 21 ) )
228                             vlc_meta_SetArtistID( p_meta,
229                                     p_txxx->fieldList().toString().toCString());
230                         if( !strncmp( psz_desc, "MusicBrainz Album Id", 20 ) )
231                             vlc_meta_SetAlbumID( p_meta,
232                                     p_txxx->fieldList().toString().toCString());
233 #endif
234                         vlc_meta_AddExtra( p_meta, psz_desc, 
235                                     p_txxx->fieldList().toString().toCString());
236                     }
237 #if 0
238                     list = tag->frameListMap()["RVA2"];
239                     TagLib::ID3v2::RelativeVolumeFrame* p_rva2;
240                     for( TagLib::ID3v2::FrameList::Iterator iter = list.begin();
241                             iter != list.end(); iter++ )
242                     {
243                         p_rva2 = dynamic_cast<TagLib::ID3v2::RelativeVolumeFrame*>(*iter);
244                         /* TODO: process rva2 frames */
245                     }
246 #endif
247                     list = tag->frameList();
248                     TagLib::ID3v2::Frame* p_t;
249                     char psz_tag[4];
250                     for( TagLib::ID3v2::FrameList::Iterator iter = list.begin();
251                             iter != list.end(); iter++ )
252                     {
253                         p_t = dynamic_cast<TagLib::ID3v2::Frame*> (*iter);
254                         memcpy( psz_tag, p_t->frameID().data(), 4);
255
256 #define SET( foo, bar ) if( !strncmp( psz_tag, foo, 4 ) ) \
257     vlc_meta_Set##bar( p_meta, p_t->toString().toCString(true))
258                         SET( "TPUB", Publisher );
259                         SET( "TCOP", Copyright );
260                         SET( "TENC", EncodedBy );
261                         SET( "TLAN", Language );
262                         //SET( "POPM", Rating );
263                         //if( !strncmp( psz_tag, "RVA2", 4 ) )
264                             /* TODO */
265 #undef SET
266                     }
267                 }
268             }
269
270             DetectImage( f, p_meta );
271
272             return VLC_SUCCESS;
273         }
274     }
275     return VLC_EGENERIC;
276 }
277
278 #define SET(a,b) if(b) { \
279         TagLib::String *psz_##a = new TagLib::String( b, \
280             TagLib::String::UTF8 ); \
281         tag->set##a( *psz_##a ); \
282         delete psz_##a; \
283     }
284
285 static int WriteMeta( vlc_object_t *p_this )
286 {
287     playlist_t *p_playlist = (playlist_t *)p_this;
288     meta_export_t *p_export = (meta_export_t *)p_playlist->p_private;
289     input_item_t *p_item = p_export->p_item;
290     
291     if( p_item == NULL )
292     {
293         msg_Err( p_this, "Can't save meta data of an empty input" );
294         return VLC_EGENERIC;
295     }
296
297     TagLib::FileRef f( p_export->psz_file );
298     if( !f.isNull() && f.tag() )
299     {
300         msg_Dbg( p_this, "Updating metadata for %s", p_export->psz_file );
301
302         TagLib::Tag *tag = f.tag();
303
304         char *psz_meta;
305
306         psz_meta = input_item_GetArtist( p_item );
307         SET( Artist, psz_meta );
308         free( psz_meta );
309
310         psz_meta = input_item_GetTitle( p_item );
311         if( !psz_meta ) psz_meta = input_item_GetName( p_item );
312         TagLib::String *psz_title = new TagLib::String( psz_meta,
313             TagLib::String::UTF8 );
314         tag->setTitle( *psz_title );
315         delete psz_title;
316         free( psz_meta );
317
318         psz_meta = input_item_GetAlbum( p_item );
319         SET( Album, psz_meta );
320         free( psz_meta );
321
322         psz_meta = input_item_GetGenre( p_item );
323         SET( Genre, psz_meta );
324         free( psz_meta );
325
326         psz_meta = input_item_GetDate( p_item );
327         if( psz_meta ) tag->setYear( atoi( psz_meta ) );
328         free( psz_meta );
329
330         psz_meta = input_item_GetTrackNum( p_item );
331         if( psz_meta ) tag->setTrack( atoi( psz_meta ) );
332         free( psz_meta );
333
334         f.save();
335         return VLC_SUCCESS;
336     }
337     msg_Err( p_this, "File %s can't be opened for tag writing\n",
338         p_export->psz_file );
339     return VLC_EGENERIC;
340 }
341
342 static int DownloadArt( vlc_object_t *p_this )
343 {
344     /* We need to be passed the file name
345      * Fetch the thing from the file, save it to the cache folder
346      */
347     return VLC_EGENERIC;
348 }