taglib.cpp
上传用户:kjfoods
上传日期:2020-07-06
资源大小:29949k
文件大小:22k
源码类别:

midi

开发平台:

Unix_Linux

  1. /*****************************************************************************
  2.  * taglib.cpp: Taglib tag parser/writer
  3.  *****************************************************************************
  4.  * Copyright (C) 2003-2009 the VideoLAN team
  5.  * $Id: 43ac6974601bcc17d14532159eae7902e7411f9c $
  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
  12.  * it under the terms of the GNU General Public License as published by
  13.  * the Free Software Foundation; either version 2 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 General Public License for more details.
  20.  *
  21.  * You should have received a copy of the GNU General Public License
  22.  * along with this program; if not, write to the Free Software
  23.  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  24.  *****************************************************************************/
  25. #ifdef HAVE_CONFIG_H
  26. # include "config.h"
  27. #endif
  28. #include <vlc_common.h>
  29. #include <vlc_plugin.h>
  30. #include <vlc_playlist.h>
  31. #include <vlc_meta.h>
  32. #include <vlc_demux.h>
  33. #include <vlc_strings.h>
  34. #include <vlc_charset.h>
  35. #ifdef WIN32
  36. # include <io.h>
  37. #else
  38. # include <unistd.h>
  39. #endif
  40. // Taglib headers
  41. #include <fileref.h>
  42. #include <tag.h>
  43. #include <tbytevector.h>
  44. #include <apetag.h>
  45. #include <id3v2tag.h>
  46. #include <xiphcomment.h>
  47. #include <flacfile.h>
  48. #include <mpcfile.h>
  49. #include <mpegfile.h>
  50. #include <oggfile.h>
  51. #include <oggflacfile.h>
  52. #include <speexfile.h>
  53. #include <trueaudiofile.h>
  54. #include <vorbisfile.h>
  55. #include <wavpackfile.h>
  56. #include <attachedpictureframe.h>
  57. #include <textidentificationframe.h>
  58. #include <uniquefileidentifierframe.h>
  59. // Local functions
  60. static int ReadMeta    ( vlc_object_t * );
  61. static int WriteMeta   ( vlc_object_t * );
  62. vlc_module_begin ()
  63.     set_capability( "meta reader", 1000 )
  64.     set_callbacks( ReadMeta, NULL )
  65.     add_submodule ()
  66.         set_capability( "meta writer", 50 )
  67.         set_callbacks( WriteMeta, NULL )
  68. vlc_module_end ()
  69. using namespace TagLib;
  70. /**
  71.  * Read meta informations from APE tags
  72.  * @param tag: the APE tag
  73.  * @param p_demux; the demux object
  74.  * @param p_demux_meta: the demuxer meta
  75.  * @param p_meta: the meta
  76.  */
  77. static void ReadMetaFromAPE( APE::Tag* tag, demux_t* p_demux, demux_meta_t* p_demux_meta, vlc_meta_t* p_meta )
  78. {
  79.     APE::Item item;
  80. #define SET( keyName, metaName ) 
  81.     item = tag->itemListMap()[keyName]; 
  82.     vlc_meta_Set##metaName( p_meta, item.toString().toCString( true ) );
  83.     SET( "COPYRIGHT", Copyright );
  84.     SET( "LANGUAGE", Language );
  85.     SET( "PUBLISHER", Publisher );
  86. #undef SET
  87. }
  88. /**
  89.  * Read meta information from id3v2 tags
  90.  * @param tag: the id3v2 tag
  91.  * @param p_demux; the demux object
  92.  * @param p_demux_meta: the demuxer meta
  93.  * @param p_meta: the meta
  94.  */
  95. static void ReadMetaFromId3v2( ID3v2::Tag* tag, demux_t* p_demux, demux_meta_t* p_demux_meta, vlc_meta_t* p_meta )
  96. {
  97.     // Get the unique file identifier
  98.     ID3v2::FrameList list = tag->frameListMap()["UFID"];
  99.     ID3v2::FrameList::Iterator iter;
  100.     for( iter = list.begin(); iter != list.end(); iter++ )
  101.     {
  102.         ID3v2::UniqueFileIdentifierFrame* p_ufid =
  103.                 dynamic_cast<ID3v2::UniqueFileIdentifierFrame*>(*iter);
  104.         const char *owner = p_ufid->owner().toCString();
  105.         if (!strcmp( owner, "http://musicbrainz.org" ))
  106.         {
  107.             /* ID3v2 UFID contains up to 64 bytes binary data
  108.              * but in our case it will be a ''
  109.              * terminated string */
  110.             char psz_ufid[64];
  111.             int max_size = __MIN( p_ufid->identifier().size(), 63);
  112.             strncpy( psz_ufid, p_ufid->identifier().data(), max_size );
  113.             psz_ufid[max_size] = '';
  114.             vlc_meta_SetTrackID( p_meta, psz_ufid );
  115.         }
  116.     }
  117.     // Get the use text
  118.     list = tag->frameListMap()["TXXX"];
  119.     for( iter = list.begin(); iter != list.end(); iter++ )
  120.     {
  121.         ID3v2::UserTextIdentificationFrame* p_txxx =
  122.                 dynamic_cast<ID3v2::UserTextIdentificationFrame*>(*iter);
  123.         vlc_meta_AddExtra( p_meta, p_txxx->description().toCString( true ),
  124.                            p_txxx->fieldList().toString().toCString( true ) );
  125.     }
  126.     // Get some more informations
  127. #define SET( tagName, metaName )                                               
  128.     list = tag->frameListMap()[tagName];                                       
  129.     if( !list.isEmpty() )                                                      
  130.         vlc_meta_Set##metaName( p_meta,                                        
  131.                                 (*list.begin())->toString().toCString( true ) );
  132.     SET( "TCOP", Copyright );
  133.     SET( "TENC", EncodedBy );
  134.     SET( "TLAN", Language );
  135.     SET( "TPUB", Publisher );
  136. #undef SET
  137.     /* Preferred type of image
  138.      * The 21 types are defined in id3v2 standard:
  139.      * http://www.id3.org/id3v2.4.0-frames */
  140.     static const int pi_cover_score[] = {
  141.         0,  /* Other */
  142.         5,  /* 32x32 PNG image that should be used as the file icon */
  143.         4,  /* File icon of a different size or format. */
  144.         20, /* Front cover image of the album. */
  145.         19, /* Back cover image of the album. */
  146.         13, /* Inside leaflet page of the album. */
  147.         18, /* Image from the album itself. */
  148.         17, /* Picture of the lead artist or soloist. */
  149.         16, /* Picture of the artist or performer. */
  150.         14, /* Picture of the conductor. */
  151.         15, /* Picture of the band or orchestra. */
  152.         9,  /* Picture of the composer. */
  153.         8,  /* Picture of the lyricist or text writer. */
  154.         7,  /* Picture of the recording location or studio. */
  155.         10, /* Picture of the artists during recording. */
  156.         11, /* Picture of the artists during performance. */
  157.         6,  /* Picture from a movie or video related to the track. */
  158.         1,  /* Picture of a large, coloured fish. */
  159.         12, /* Illustration related to the track. */
  160.         3,  /* Logo of the band or performer. */
  161.         2   /* Logo of the publisher (record company). */
  162.     };
  163.     int i_score = -1;
  164.     // Try now to get embedded art
  165.     list = tag->frameListMap()[ "APIC" ];
  166.     if( list.isEmpty() )
  167.         return;
  168.     TAB_INIT( p_demux_meta->i_attachments, p_demux_meta->attachments );
  169.     for( iter = list.begin(); iter != list.end(); iter++ )
  170.     {
  171.         ID3v2::AttachedPictureFrame* p_apic =
  172.             dynamic_cast<ID3v2::AttachedPictureFrame*>(*iter);
  173.         input_attachment_t *p_attachment;
  174.         const char *psz_mime;
  175.         char *psz_name, *psz_description;
  176.         // Get the mime and description of the image.
  177.         // If the description is empty, take the type as a description
  178.         psz_mime = p_apic->mimeType().toCString( true );
  179.         if( p_apic->description().size() > 0 )
  180.             psz_description = strdup( p_apic->description().toCString( true ) );
  181.         else
  182.         {
  183.             if( asprintf( &psz_description, "%i", p_apic->type() ) == -1 )
  184.                 psz_description = NULL;
  185.         }
  186.         if( !psz_description )
  187.             continue;
  188.         psz_name = psz_description;
  189.         /* some old iTunes version not only sets incorrectly the mime type
  190.          * or the description of the image,
  191.          * but also embeds incorrectly the image.
  192.          * Recent versions seem to behave correctly */
  193.         if( !strncmp( psz_mime, "PNG", 3 ) ||
  194.             !strncmp( psz_name, "xC2x89PNG", 5 ) )
  195.         {
  196.             msg_Warn( p_demux, "Invalid picture embedded by broken iTunes version" );
  197.             free( psz_description );
  198.             continue;
  199.         }
  200.         const ByteVector picture = p_apic->picture();
  201.         const char *p_data = picture.data();
  202.         const unsigned i_data = picture.size();
  203.         msg_Dbg( p_demux, "Found embedded art: %s (%s) is %u bytes",
  204.                  psz_name, psz_mime, i_data );
  205.         p_attachment = vlc_input_attachment_New( psz_name, psz_mime,
  206.                                 psz_description, p_data, i_data );
  207.         if( p_attachment )
  208.             TAB_APPEND_CAST( (input_attachment_t**),
  209.                              p_demux_meta->i_attachments, p_demux_meta->attachments,
  210.                              p_attachment );
  211.         free( psz_description );
  212.         if( pi_cover_score[p_apic->type()] > i_score )
  213.         {
  214.             i_score = pi_cover_score[p_apic->type()];
  215.             char *psz_url;
  216.             if( asprintf( &psz_url, "attachment://%s",
  217.                           p_attachment->psz_name ) == -1 )
  218.                 continue;
  219.             vlc_meta_SetArtURL( p_meta, psz_url );
  220.             free( psz_url );
  221.         }
  222.     }
  223. }
  224. /**
  225.  * Read the meta informations from XiphComments
  226.  * @param tag: the Xiph Comment
  227.  * @param p_demux; the demux object
  228.  * @param p_demux_meta: the demuxer meta
  229.  * @param p_meta: the meta
  230.  */
  231. static void ReadMetaFromXiph( Ogg::XiphComment* tag, demux_t* p_demux, demux_meta_t* p_demux_meta, vlc_meta_t* p_meta )
  232. {
  233. #define SET( keyName, metaName )                                               
  234.     StringList list = tag->fieldListMap()[keyName];                            
  235.     if( !list.isEmpty() )                                                      
  236.         vlc_meta_Set##metaName( p_meta, (*list.begin()).toCString( true ) );
  237.     SET( "COPYRIGHT", Copyright );
  238. #undef SET
  239.     // Try now to get embedded art
  240.     StringList mime_list = tag->fieldListMap()[ "COVERARTMIME" ];
  241.     StringList art_list = tag->fieldListMap()[ "COVERART" ];
  242.     // We get only the first covert art
  243.     if( mime_list.size() > 1 || art_list.size() > 1 )
  244.         msg_Warn( p_demux, "Found %i embedded arts, so using only the first one",
  245.                   art_list.size() );
  246.     else if( mime_list.size() == 0 || art_list.size() == 0 )
  247.         return;
  248.     input_attachment_t *p_attachment;
  249.     const char* psz_name = "cover";
  250.     const char* psz_mime = mime_list[0].toCString(true);
  251.     const char* psz_description = "cover";
  252.     uint8_t *p_data;
  253.     int i_data = vlc_b64_decode_binary( &p_data, art_list[0].toCString(true) );
  254.     msg_Dbg( p_demux, "Found embedded art: %s (%s) is %i bytes",
  255.              psz_name, psz_mime, i_data );
  256.     TAB_INIT( p_demux_meta->i_attachments, p_demux_meta->attachments );
  257.               p_attachment = vlc_input_attachment_New( psz_name, psz_mime,
  258.               psz_description, p_data, i_data );
  259.     free( p_data );
  260.     TAB_APPEND_CAST( (input_attachment_t**),
  261.                      p_demux_meta->i_attachments, p_demux_meta->attachments,
  262.                      p_attachment );
  263.     vlc_meta_SetArtURL( p_meta, "attachment://cover" );
  264. }
  265. /**
  266.  * Get the tags from the file using TagLib
  267.  * @param p_this: the demux object
  268.  * @return VLC_SUCCESS if the operation success
  269.  */
  270. static int ReadMeta( vlc_object_t* p_this)
  271. {
  272.     demux_t*        p_demux = (demux_t*)p_this;
  273.     demux_meta_t*   p_demux_meta = (demux_meta_t*)p_demux->p_private;
  274.     vlc_meta_t*     p_meta;
  275.     FileRef f;
  276.     p_demux_meta->p_meta = NULL;
  277. #if defined(WIN32) || defined (UNDER_CE)
  278.     if(GetVersion() < 0x80000000)
  279.     {
  280.         wchar_t wpath[MAX_PATH + 1];
  281.         if( !MultiByteToWideChar( CP_UTF8, 0, p_demux->psz_path, -1, wpath, MAX_PATH) )
  282.             return VLC_EGENERIC;
  283.         wpath[MAX_PATH] = L'';
  284.         f = FileRef( wpath );
  285.     }
  286.     else
  287.         return VLC_EGENERIC;
  288. #else
  289.     const char* local_name = ToLocale( p_demux->psz_path );
  290.     if( !local_name )
  291.         return VLC_EGENERIC;
  292.     f = FileRef( local_name );
  293.     LocaleFree( local_name );
  294. #endif
  295.     if( f.isNull() )
  296.         return VLC_EGENERIC;
  297.     if( !f.tag() || f.tag()->isEmpty() )
  298.         return VLC_EGENERIC;
  299.     p_demux_meta->p_meta = p_meta = vlc_meta_New();
  300.     if( !p_meta )
  301.         return VLC_ENOMEM;
  302.     // Read the tags from the file
  303.     Tag* p_tag = f.tag();
  304. #define SET( tag, meta )                                                       
  305.     if( !p_tag->tag().isNull() && !p_tag->tag().isEmpty() )                    
  306.         vlc_meta_Set##meta( p_meta, p_tag->tag().toCString(true) )
  307. #define SETINT( tag, meta )                                                    
  308.     if( p_tag->tag() )                                                         
  309.     {                                                                          
  310.         char psz_tmp[10];                                                      
  311.         snprintf( psz_tmp, 10, "%d", p_tag->tag() );                           
  312.         vlc_meta_Set##meta( p_meta, psz_tmp );                                 
  313.     }
  314.     SET( title, Title );
  315.     SET( artist, Artist );
  316.     SET( album, Album );
  317.     SET( comment, Description );
  318.     SET( genre, Genre );
  319.     SETINT( year, Date );
  320.     SETINT( track, TrackNum );
  321. #undef SETINT
  322. #undef SET
  323.     // Try now to read special tags
  324.     if( FLAC::File* flac = dynamic_cast<FLAC::File*>(f.file()) )
  325.     {
  326.         if( flac->ID3v2Tag() )
  327.             ReadMetaFromId3v2( flac->ID3v2Tag(), p_demux, p_demux_meta, p_meta );
  328.         else if( flac->xiphComment() )
  329.             ReadMetaFromXiph( flac->xiphComment(), p_demux, p_demux_meta, p_meta );
  330.     }
  331.     else if( MPC::File* mpc = dynamic_cast<MPC::File*>(f.file()) )
  332.     {
  333.         if( mpc->APETag() )
  334.             ReadMetaFromAPE( mpc->APETag(), p_demux, p_demux_meta, p_meta );
  335.     }
  336.     else if( MPEG::File* mpeg = dynamic_cast<MPEG::File*>(f.file()) )
  337.     {
  338.         if( mpeg->ID3v2Tag() )
  339.             ReadMetaFromId3v2( mpeg->ID3v2Tag(), p_demux, p_demux_meta, p_meta );
  340.         else if( mpeg->APETag() )
  341.             ReadMetaFromAPE( mpeg->APETag(), p_demux, p_demux_meta, p_meta );
  342.     }
  343.     else if( Ogg::File* ogg = dynamic_cast<Ogg::File*>(f.file()) )
  344.     {
  345.         if( Ogg::FLAC::File* ogg_flac = dynamic_cast<Ogg::FLAC::File*>(f.file()))
  346.             ReadMetaFromXiph( ogg_flac->tag(), p_demux, p_demux_meta, p_meta );
  347.         else if( Ogg::Speex::File* ogg_speex = dynamic_cast<Ogg::Speex::File*>(f.file()) )
  348.             ReadMetaFromXiph( ogg_speex->tag(), p_demux, p_demux_meta, p_meta );
  349.         else if( Ogg::Vorbis::File* ogg_vorbis = dynamic_cast<Ogg::Vorbis::File*>(f.file()) )
  350.             ReadMetaFromXiph( ogg_vorbis->tag(), p_demux, p_demux_meta, p_meta );
  351.     }
  352.     else if( TrueAudio::File* trueaudio = dynamic_cast<TrueAudio::File*>(f.file()) )
  353.     {
  354.         if( trueaudio->ID3v2Tag() )
  355.             ReadMetaFromId3v2( trueaudio->ID3v2Tag(), p_demux, p_demux_meta, p_meta );
  356.     }
  357.     else if( WavPack::File* wavpack = dynamic_cast<WavPack::File*>(f.file()) )
  358.     {
  359.         if( wavpack->APETag() )
  360.             ReadMetaFromAPE( wavpack->APETag(), p_demux, p_demux_meta, p_meta );
  361.     }
  362.     return VLC_SUCCESS;
  363. }
  364. /**
  365.  * Write meta informations to APE tags
  366.  * @param tag: the APE tag
  367.  * @param p_item: the input item
  368.  */
  369. static void WriteMetaToAPE( APE::Tag* tag, input_item_t* p_item )
  370. {
  371.     char* psz_meta;
  372. #define WRITE( metaName, keyName )                      
  373.     psz_meta = input_item_Get##metaName( p_item );      
  374.     if( psz_meta )                                      
  375.     {                                                   
  376.         String key( keyName, String::UTF8 );            
  377.         String value( psz_meta, String::UTF8 );         
  378.         tag->addValue( key, value, true );              
  379.     }                                                   
  380.     free( psz_meta );
  381.     WRITE( Copyright, "COPYRIGHT" );
  382.     WRITE( Language, "LANGUAGE" );
  383.     WRITE( Publisher, "PUBLISHER" );
  384. #undef WRITE
  385. }
  386. /**
  387.  * Write meta information to id3v2 tags
  388.  * @param tag: the id3v2 tag
  389.  * @param p_input: the input item
  390.  */
  391. static void WriteMetaToId3v2( ID3v2::Tag* tag, input_item_t* p_item )
  392. {
  393.     char* psz_meta;
  394. #define WRITE( metaName, tagName )                                            
  395.     psz_meta = input_item_Get##metaName( p_item );                            
  396.     if( psz_meta )                                                            
  397.     {                                                                         
  398.         ByteVector p_byte( tagName, 4 );                                      
  399.         tag->removeFrames( p_byte );                                         
  400.         ID3v2::TextIdentificationFrame* p_frame =                             
  401.             new ID3v2::TextIdentificationFrame( p_byte, String::UTF8 );       
  402.         p_frame->setText( psz_meta );                                         
  403.         tag->addFrame( p_frame );                                             
  404.     }                                                                         
  405.     free( psz_meta );
  406.     WRITE( Copyright, "TCOP" );
  407.     WRITE( EncodedBy, "TENC" );
  408.     WRITE( Language,  "TLAN" );
  409.     WRITE( Publisher, "TPUB" );
  410. #undef WRITE
  411. }
  412. /**
  413.  * Write the meta informations to XiphComments
  414.  * @param tag: the Xiph Comment
  415.  * @param p_input: the input item
  416.  */
  417. static void WriteMetaToXiph( Ogg::XiphComment* tag, input_item_t* p_item )
  418. {
  419.     char* psz_meta;
  420. #define WRITE( metaName, keyName )                      
  421.     psz_meta = input_item_Get##metaName( p_item );      
  422.     if( psz_meta )                                      
  423.     {                                                   
  424.         String key( keyName, String::UTF8 );            
  425.         String value( psz_meta, String::UTF8 );         
  426.         tag->addField( key, value, true );              
  427.     }                                                   
  428.     free( psz_meta );
  429.     WRITE( Copyright, "COPYRIGHT" );
  430. #undef WRITE
  431. }
  432. /**
  433.  * Set the tags to the file using TagLib
  434.  * @param p_this: the demux object
  435.  * @return VLC_SUCCESS if the operation success
  436.  */
  437. static int WriteMeta( vlc_object_t *p_this )
  438. {
  439.     playlist_t *p_playlist = (playlist_t *)p_this;
  440.     meta_export_t *p_export = (meta_export_t *)p_playlist->p_private;
  441.     input_item_t *p_item = p_export->p_item;
  442.     FileRef f;
  443.     if( !p_item )
  444.     {
  445.         msg_Err( p_this, "Can't save meta data of an empty input" );
  446.         return VLC_EGENERIC;
  447.     }
  448. #if defined(WIN32) || defined (UNDER_CE)
  449.     if(GetVersion() < 0x80000000)
  450.     {
  451.         wchar_t wpath[MAX_PATH + 1];
  452.         if( !MultiByteToWideChar( CP_UTF8, 0, p_export->psz_file, -1, wpath, MAX_PATH) )
  453.             return VLC_EGENERIC;
  454.         wpath[MAX_PATH] = L'';
  455.         f = FileRef( wpath );
  456.     }
  457.     else
  458.         return VLC_EGENERIC;
  459. #else
  460.     const char* local_name = ToLocale( p_export->psz_file );
  461.     if( !local_name )
  462.         return VLC_EGENERIC;
  463.     f = FileRef( local_name );
  464.     LocaleFree( local_name );
  465. #endif
  466.     if( f.isNull() || !f.tag() || f.file()->readOnly() )
  467.     {
  468.         msg_Err( p_this, "File %s can't be opened for tag writing",
  469.             p_export->psz_file );
  470.         return VLC_EGENERIC;
  471.     }
  472.     msg_Dbg( p_this, "Writing metadata for %s", p_export->psz_file );
  473.     Tag *p_tag = f.tag();
  474.     char *psz_meta;
  475. #define SET( a, b )                                         
  476.     if( b )                                                 
  477.     {                                                       
  478.         String* psz_tmp = new String( b, String::UTF8 );    
  479.         p_tag->set##a( *psz_tmp );                          
  480.         delete psz_tmp;                                     
  481.     }
  482.     // Saving all common fields
  483.     // If the title is empty, use the name
  484.     psz_meta = input_item_GetTitleFbName( p_item );
  485.     SET( Title, psz_meta );
  486.     free( psz_meta );
  487.     psz_meta = input_item_GetArtist( p_item );
  488.     SET( Artist, psz_meta );
  489.     free( psz_meta );
  490.     psz_meta = input_item_GetAlbum( p_item );
  491.     SET( Album, psz_meta );
  492.     free( psz_meta );
  493.     psz_meta = input_item_GetDescription( p_item );
  494.     SET( Comment, psz_meta );
  495.     free( psz_meta );
  496.     psz_meta = input_item_GetGenre( p_item );
  497.     SET( Genre, psz_meta );
  498.     free( psz_meta );
  499. #undef SET
  500.     psz_meta = input_item_GetDate( p_item );
  501.     if( psz_meta ) p_tag->setYear( atoi( psz_meta ) );
  502.     free( psz_meta );
  503.     psz_meta = input_item_GetTrackNum( p_item );
  504.     if( psz_meta ) p_tag->setTrack( atoi( psz_meta ) );
  505.     free( psz_meta );
  506.     // Try now to write special tags
  507.     if( FLAC::File* flac = dynamic_cast<FLAC::File*>(f.file()) )
  508.     {
  509.         if( flac->ID3v2Tag() )
  510.             WriteMetaToId3v2( flac->ID3v2Tag(), p_item );
  511.         else if( flac->xiphComment() )
  512.             WriteMetaToXiph( flac->xiphComment(), p_item );
  513.     }
  514.     else if( MPC::File* mpc = dynamic_cast<MPC::File*>(f.file()) )
  515.     {
  516.         if( mpc->APETag() )
  517.             WriteMetaToAPE( mpc->APETag(), p_item );
  518.     }
  519.     else if( MPEG::File* mpeg = dynamic_cast<MPEG::File*>(f.file()) )
  520.     {
  521.         if( mpeg->ID3v2Tag() )
  522.             WriteMetaToId3v2( mpeg->ID3v2Tag(), p_item );
  523.         else if( mpeg->APETag() )
  524.             WriteMetaToAPE( mpeg->APETag(), p_item );
  525.     }
  526.     else if( Ogg::File* ogg = dynamic_cast<Ogg::File*>(f.file()) )
  527.     {
  528.         if( Ogg::FLAC::File* ogg_flac = dynamic_cast<Ogg::FLAC::File*>(f.file()))
  529.             WriteMetaToXiph( ogg_flac->tag(), p_item );
  530.         else if( Ogg::Speex::File* ogg_speex = dynamic_cast<Ogg::Speex::File*>(f.file()) )
  531.             WriteMetaToXiph( ogg_speex->tag(), p_item );
  532.         else if( Ogg::Vorbis::File* ogg_vorbis = dynamic_cast<Ogg::Vorbis::File*>(f.file()) )
  533.             WriteMetaToXiph( ogg_vorbis->tag(), p_item );
  534.     }
  535.     else if( TrueAudio::File* trueaudio = dynamic_cast<TrueAudio::File*>(f.file()) )
  536.     {
  537.         if( trueaudio->ID3v2Tag() )
  538.             WriteMetaToId3v2( trueaudio->ID3v2Tag(), p_item );
  539.     }
  540.     else if( WavPack::File* wavpack = dynamic_cast<WavPack::File*>(f.file()) )
  541.     {
  542.         if( wavpack->APETag() )
  543.             WriteMetaToAPE( wavpack->APETag(), p_item );
  544.     }
  545.     // Save the meta data
  546.     f.save();
  547.     return VLC_SUCCESS;
  548. }