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

midi

开发平台:

Unix_Linux

  1. /*******************************************************************************
  2.  * itml.c : iTunes Music Library import functions
  3.  *******************************************************************************
  4.  * Copyright (C) 2007 the VideoLAN team
  5.  * $Id: 9022c568a02cf1de2120ef4c0a92bd21c86dedb5 $
  6.  *
  7.  * Authors: Yoann Peronneau <yoann@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.  * file modules/demux/playlist/itml.c
  25.  * brief iTunes Music Library import functions
  26.  */
  27. #ifdef HAVE_CONFIG_H
  28. # include "config.h"
  29. #endif
  30. #include <vlc_common.h>
  31. #include <vlc_demux.h>
  32. #include "playlist.h"
  33. #include "vlc_xml.h"
  34. #include "vlc_strings.h"
  35. #include "vlc_url.h"
  36. #include "itml.h"
  37. struct demux_sys_t
  38. {
  39.     int i_ntracks;
  40. };
  41. static int Control( demux_t *, int, va_list );
  42. static int Demux( demux_t * );
  43. /**
  44.  * brief iTML submodule initialization function
  45.  */
  46. int Import_iTML( vlc_object_t *p_this )
  47. {
  48.     DEMUX_BY_EXTENSION_OR_FORCED_MSG( ".xml", "itml",
  49.                                       "using iTunes Media Library reader" );
  50.     return VLC_SUCCESS;
  51. }
  52. void Close_iTML( vlc_object_t *p_this )
  53. {
  54.     demux_t *p_demux = (demux_t *)p_this;
  55.     free( p_demux->p_sys );
  56. }
  57. /**
  58.  * brief demuxer function for iTML parsing
  59.  */
  60. int Demux( demux_t *p_demux )
  61. {
  62.     int i_ret = VLC_SUCCESS;
  63.     xml_t *p_xml = NULL;
  64.     xml_reader_t *p_xml_reader = NULL;
  65.     char *psz_name = NULL;
  66.     INIT_PLAYLIST_STUFF;
  67.     p_demux->p_sys->i_ntracks = 0;
  68.     /* create new xml parser from stream */
  69.     p_xml = xml_Create( p_demux );
  70.     if( !p_xml )
  71.         i_ret = VLC_ENOMOD;
  72.     else
  73.     {
  74.         p_xml_reader = xml_ReaderCreate( p_xml, p_demux->s );
  75.         if( !p_xml_reader )
  76.             i_ret = VLC_EGENERIC;
  77.     }
  78.     /* locating the root node */
  79.     if( i_ret == VLC_SUCCESS )
  80.     {
  81.         do
  82.         {
  83.             if( xml_ReaderRead( p_xml_reader ) != 1 )
  84.             {
  85.                 msg_Err( p_demux, "can't read xml stream" );
  86.                 i_ret = VLC_EGENERIC;
  87.             }
  88.         } while( i_ret == VLC_SUCCESS &&
  89.                  xml_ReaderNodeType( p_xml_reader ) != XML_READER_STARTELEM );
  90.     }
  91.     /* checking root node name */
  92.     if( i_ret == VLC_SUCCESS )
  93.     {
  94.         psz_name = xml_ReaderName( p_xml_reader );
  95.         if( !psz_name || strcmp( psz_name, "plist" ) )
  96.         {
  97.             msg_Err( p_demux, "invalid root node name: %s", psz_name );
  98.             i_ret = VLC_EGENERIC;
  99.         }
  100.         FREE_NAME();
  101.     }
  102.     if( i_ret == VLC_SUCCESS )
  103.     {
  104.         xml_elem_hnd_t pl_elements[] =
  105.             { {"dict",    COMPLEX_CONTENT, {.cmplx = parse_plist_dict} } };
  106.         i_ret = parse_plist_node( p_demux, p_current_input,
  107.                                      NULL, p_xml_reader, "plist",
  108.                                      pl_elements );
  109.         HANDLE_PLAY_AND_RELEASE;
  110.     }
  111.     if( p_xml_reader )
  112.         xml_ReaderDelete( p_xml, p_xml_reader );
  113.     if( p_xml )
  114.         xml_Delete( p_xml );
  115.     return 0; /* Needed for correct operation of go back */
  116. }
  117. /** brief dummy function for demux callback interface */
  118. static int Control( demux_t *p_demux, int i_query, va_list args )
  119. {
  120.     VLC_UNUSED(p_demux); VLC_UNUSED(i_query); VLC_UNUSED(args);
  121.     return VLC_EGENERIC;
  122. }
  123. /**
  124.  * brief parse the root node of the playlist
  125.  */
  126. static bool parse_plist_node COMPLEX_INTERFACE
  127. {
  128.     VLC_UNUSED(p_track); VLC_UNUSED(psz_element);
  129.     char *psz_name = NULL;
  130.     char *psz_value = NULL;
  131.     bool b_version_found = false;
  132.     /* read all playlist attributes */
  133.     while( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
  134.     {
  135.         psz_name = xml_ReaderName( p_xml_reader );
  136.         psz_value = xml_ReaderValue( p_xml_reader );
  137.         if( !psz_name || !psz_value )
  138.         {
  139.             msg_Err( p_demux, "invalid xml stream @ <plist>" );
  140.             FREE_ATT();
  141.             return false;
  142.         }
  143.         /* attribute: version */
  144.         if( !strcmp( psz_name, "version" ) )
  145.         {
  146.             b_version_found = true;
  147.             if( strcmp( psz_value, "1.0" ) )
  148.                 msg_Warn( p_demux, "unsupported iTunes Media Library version" );
  149.         }
  150.         /* unknown attribute */
  151.         else
  152.             msg_Warn( p_demux, "invalid <plist> attribute:"%s"", psz_name);
  153.         FREE_ATT();
  154.     }
  155.     /* attribute version is mandatory !!! */
  156.     if( !b_version_found )
  157.         msg_Warn( p_demux, "<plist> requires "version" attribute" );
  158.     return parse_dict( p_demux, p_input_item, NULL, p_xml_reader,
  159.                        "plist", p_handlers );
  160. }
  161. /**
  162.  * brief parse a <dict>
  163.  * param COMPLEX_INTERFACE
  164.  */
  165. static bool parse_dict COMPLEX_INTERFACE
  166. {
  167.     int i_node;
  168.     char *psz_name = NULL;
  169.     char *psz_value = NULL;
  170.     char *psz_key = NULL;
  171.     xml_elem_hnd_t *p_handler = NULL;
  172.     while( xml_ReaderRead( p_xml_reader ) == 1 )
  173.     {
  174.         i_node = xml_ReaderNodeType( p_xml_reader );
  175.         switch( i_node )
  176.         {
  177.             case XML_READER_NONE:
  178.                 break;
  179.             case XML_READER_STARTELEM:
  180.                 /*  element start tag  */
  181.                 psz_name = xml_ReaderName( p_xml_reader );
  182.                 if( !psz_name || !*psz_name )
  183.                 {
  184.                     msg_Err( p_demux, "invalid xml stream" );
  185.                     FREE_ATT_KEY();
  186.                     return false;
  187.                 }
  188.                 /* choose handler */
  189.                 for( p_handler = p_handlers;
  190.                      p_handler->name && strcmp( psz_name, p_handler->name );
  191.                      p_handler++ );
  192.                 if( !p_handler->name )
  193.                 {
  194.                     msg_Err( p_demux, "unexpected element <%s>", psz_name );
  195.                     FREE_ATT_KEY();
  196.                     return false;
  197.                 }
  198.                 FREE_NAME();
  199.                 /* complex content is parsed in a separate function */
  200.                 if( p_handler->type == COMPLEX_CONTENT )
  201.                 {
  202.                     if( p_handler->pf_handler.cmplx( p_demux,
  203.                                                      p_input_item,
  204.                                                      NULL,
  205.                                                      p_xml_reader,
  206.                                                      p_handler->name,
  207.                                                      NULL ) )
  208.                     {
  209.                         p_handler = NULL;
  210.                         FREE_ATT_KEY();
  211.                     }
  212.                     else
  213.                     {
  214.                         FREE_ATT_KEY();
  215.                         return false;
  216.                     }
  217.                 }
  218.                 break;
  219.             case XML_READER_TEXT:
  220.                 /* simple element content */
  221.                 FREE_ATT();
  222.                 psz_value = xml_ReaderValue( p_xml_reader );
  223.                 if( !psz_value )
  224.                 {
  225.                     msg_Err( p_demux, "invalid xml stream" );
  226.                     FREE_ATT_KEY();
  227.                     return false;
  228.                 }
  229.                 break;
  230.             case XML_READER_ENDELEM:
  231.                 /* element end tag */
  232.                 psz_name = xml_ReaderName( p_xml_reader );
  233.                 if( !psz_name )
  234.                 {
  235.                     msg_Err( p_demux, "invalid xml stream" );
  236.                     FREE_ATT_KEY();
  237.                     return false;
  238.                 }
  239.                 /* leave if the current parent node <track> is terminated */
  240.                 if( !strcmp( psz_name, psz_element ) )
  241.                 {
  242.                     FREE_ATT_KEY();
  243.                     return true;
  244.                 }
  245.                 /* there MUST have been a start tag for that element name */
  246.                 if( !p_handler || !p_handler->name
  247.                     || strcmp( p_handler->name, psz_name ))
  248.                 {
  249.                     msg_Err( p_demux, "there's no open element left for <%s>",
  250.                              psz_name );
  251.                     FREE_ATT_KEY();
  252.                     return false;
  253.                 }
  254.                 /* special case: key */
  255.                 if( !strcmp( p_handler->name, "key" ) )
  256.                 {
  257.                     free( psz_key );
  258.                     psz_key = strdup( psz_value );
  259.                 }
  260.                 /* call the simple handler */
  261.                 else if( p_handler->pf_handler.smpl )
  262.                 {
  263.                     p_handler->pf_handler.smpl( p_track, psz_key, psz_value );
  264.                 }
  265.                 FREE_ATT();
  266.                 p_handler = NULL;
  267.                 break;
  268.             default:
  269.                 /* unknown/unexpected xml node */
  270.                 msg_Err( p_demux, "unexpected xml node %i", i_node );
  271.                 FREE_ATT_KEY();
  272.                 return false;
  273.         }
  274.         FREE_NAME();
  275.     }
  276.     msg_Err( p_demux, "unexpected end of xml data" );
  277.     FREE_ATT_KEY();
  278.     return false;
  279. }
  280. static bool parse_plist_dict COMPLEX_INTERFACE
  281. {
  282.     VLC_UNUSED(p_track); VLC_UNUSED(psz_element); VLC_UNUSED(p_handlers);
  283.     xml_elem_hnd_t pl_elements[] =
  284.         { {"dict",    COMPLEX_CONTENT, {.cmplx = parse_tracks_dict} },
  285.           {"array",   SIMPLE_CONTENT,  {NULL} },
  286.           {"key",     SIMPLE_CONTENT,  {NULL} },
  287.           {"integer", SIMPLE_CONTENT,  {NULL} },
  288.           {"string",  SIMPLE_CONTENT,  {NULL} },
  289.           {"date",    SIMPLE_CONTENT,  {NULL} },
  290.           {"true",    SIMPLE_CONTENT,  {NULL} },
  291.           {"false",   SIMPLE_CONTENT,  {NULL} },
  292.           {NULL,      UNKNOWN_CONTENT, {NULL} }
  293.         };
  294.     return parse_dict( p_demux, p_input_item, NULL, p_xml_reader,
  295.                        "dict", pl_elements );
  296. }
  297. static bool parse_tracks_dict COMPLEX_INTERFACE
  298. {
  299.     VLC_UNUSED(p_track); VLC_UNUSED(psz_element); VLC_UNUSED(p_handlers);
  300.     xml_elem_hnd_t tracks_elements[] =
  301.         { {"dict",    COMPLEX_CONTENT, {.cmplx = parse_track_dict} },
  302.           {"key",     SIMPLE_CONTENT,  {NULL} },
  303.           {NULL,      UNKNOWN_CONTENT, {NULL} }
  304.         };
  305.     parse_dict( p_demux, p_input_item, NULL, p_xml_reader,
  306.                 "dict", tracks_elements );
  307.     msg_Info( p_demux, "added %i tracks successfully",
  308.               p_demux->p_sys->i_ntracks );
  309.     return true;
  310. }
  311. static bool parse_track_dict COMPLEX_INTERFACE
  312. {
  313.     VLC_UNUSED(psz_element); VLC_UNUSED(p_handlers);
  314.     input_item_t *p_new_input = NULL;
  315.     int i_ret = -1;
  316.     char *psz_uri = NULL;
  317.     p_track = new_track();
  318.     xml_elem_hnd_t track_elements[] =
  319.         { {"array",   COMPLEX_CONTENT, {.cmplx = skip_element} },
  320.           {"key",     SIMPLE_CONTENT,  {.smpl = save_data} },
  321.           {"integer", SIMPLE_CONTENT,  {.smpl = save_data} },
  322.           {"string",  SIMPLE_CONTENT,  {.smpl = save_data} },
  323.           {"date",    SIMPLE_CONTENT,  {.smpl = save_data} },
  324.           {"true",    SIMPLE_CONTENT,  {NULL} },
  325.           {"false",   SIMPLE_CONTENT,  {NULL} },
  326.           {NULL,      UNKNOWN_CONTENT, {NULL} }
  327.         };
  328.     i_ret = parse_dict( p_demux, p_input_item, p_track,
  329.                         p_xml_reader, "dict", track_elements );
  330.     msg_Dbg( p_demux, "name: %s, artist: %s, album: %s, genre: %s, trackNum: %s, location: %s",
  331.              p_track->name, p_track->artist, p_track->album, p_track->genre, p_track->trackNum, p_track->location );
  332.     if( !p_track->location )
  333.     {
  334.         msg_Err( p_demux, "Track needs Location" );
  335.         free_track( p_track );
  336.         return false;
  337.     }
  338.     psz_uri = decode_URI_duplicate( p_track->location );
  339.     if( psz_uri )
  340.     {
  341.         if( strlen( psz_uri ) > 17 &&
  342.             !strncmp( psz_uri, "file://localhost/", 17 ) )
  343.         {
  344.             /* remove 'localhost/' */
  345.             memmove( psz_uri + 7, psz_uri + 17, strlen( psz_uri ) - 9 );
  346.             msg_Info( p_demux, "Adding '%s'", psz_uri );
  347.             p_new_input = input_item_New( p_demux, psz_uri, NULL );
  348.             input_item_AddSubItem( p_input_item, p_new_input );
  349.             /* add meta info */
  350.             add_meta( p_new_input, p_track );
  351.             vlc_gc_decref( p_new_input );
  352.     
  353.             p_demux->p_sys->i_ntracks++;
  354.         }
  355.         else
  356.         {
  357.             msg_Err( p_demux, "Don't know how to handle %s", psz_uri );
  358.         }
  359.         free( psz_uri );
  360.     }
  361.     free_track( p_track );
  362.     return i_ret;
  363. }
  364. static track_elem_t *new_track()
  365. {
  366.     track_elem_t *p_track = NULL;
  367.     p_track = (track_elem_t *)malloc( sizeof( track_elem_t ) );
  368.     if( p_track )
  369.     {
  370.         p_track->name = NULL;
  371.         p_track->artist = NULL;
  372.         p_track->album = NULL;
  373.         p_track->genre = NULL;
  374.         p_track->trackNum = NULL;
  375.         p_track->location = NULL;
  376.         p_track->duration = 0;
  377.     }
  378.     return p_track;
  379. }
  380. static void free_track( track_elem_t *p_track )
  381. {
  382.     fprintf( stderr, "free trackn" );
  383.     if ( !p_track )
  384.         return;
  385.     FREE( p_track->name )
  386.     FREE( p_track->artist )
  387.     FREE( p_track->album )
  388.     FREE( p_track->genre )
  389.     FREE( p_track->trackNum )
  390.     FREE( p_track->location )
  391.     p_track->duration = 0;
  392.     free( p_track );
  393. }
  394. static bool save_data SIMPLE_INTERFACE
  395. {
  396.     /* exit if setting is impossible */
  397.     if( !psz_name || !psz_value || !p_track )
  398.         return false;
  399.     /* re-convert xml special characters inside psz_value */
  400.     resolve_xml_special_chars( psz_value );
  401. #define SAVE_INFO( name, value ) 
  402.     if( !strcmp( psz_name, name ) ) { p_track->value = strdup( psz_value ); }
  403.     SAVE_INFO( "Name", name )
  404.     else SAVE_INFO( "Artist", artist )
  405.     else SAVE_INFO( "Album", album )
  406.     else SAVE_INFO( "Genre", genre )
  407.     else SAVE_INFO( "Track Number", trackNum )
  408.     else SAVE_INFO( "Location", location )
  409.     else if( !strcmp( psz_name, "Total Time" ) )
  410.     {
  411.         long i_num = atol( psz_value );
  412.         p_track->duration = (mtime_t) i_num*1000;
  413.     }
  414.     return true;
  415. }
  416. /**
  417.  * brief handles the supported <track> sub-elements
  418.  */
  419. static bool add_meta( input_item_t *p_input_item,
  420.                             track_elem_t *p_track )
  421. {
  422.     /* exit if setting is impossible */
  423.     if( !p_input_item || !p_track )
  424.         return false;
  425. #define SET_INFO( func, prop ) 
  426.     if( p_track->prop ) { func( p_input_item, p_track->prop ); }
  427.     SET_INFO( input_item_SetTitle, name )
  428.     SET_INFO( input_item_SetArtist, artist )
  429.     SET_INFO( input_item_SetAlbum, album )
  430.     SET_INFO( input_item_SetGenre, genre )
  431.     SET_INFO( input_item_SetTrackNum, trackNum )
  432.     SET_INFO( input_item_SetDuration, duration )
  433.     return true;
  434. }
  435. /**
  436.  * brief skips complex element content that we can't manage
  437.  */
  438. static bool skip_element COMPLEX_INTERFACE
  439. {
  440.     VLC_UNUSED(p_demux); VLC_UNUSED(p_input_item);
  441.     VLC_UNUSED(p_track); VLC_UNUSED(p_handlers);
  442.     char *psz_endname;
  443.     while( xml_ReaderRead( p_xml_reader ) == 1 )
  444.     {
  445.         if( xml_ReaderNodeType( p_xml_reader ) == XML_READER_ENDELEM )
  446.         {
  447.             psz_endname = xml_ReaderName( p_xml_reader );
  448.             if( !psz_endname )
  449.                 return false;
  450.             if( !strcmp( psz_element, psz_endname ) )
  451.             {
  452.                 free( psz_endname );
  453.                 return true;
  454.             }
  455.             else
  456.                 free( psz_endname );
  457.         }
  458.     }
  459.     return false;
  460. }