/** * Recursively follow the playlist * @param p_export: the export structure * @param p_root: the current node */ static void DoChildren( playlist_export_t *p_export, playlist_item_t *p_root ) { /* Go through the playlist and add items */ for( int i = 0; i < p_root->i_children ; i++) { playlist_item_t *p_current = p_root->pp_children[i]; assert( p_current ); if( p_current->i_flags & PLAYLIST_SAVE_FLAG ) continue; if( p_current->i_children >= 0 ) { DoChildren( p_export, p_current ); continue; } char* psz_name = NULL; char *psz_tmp = input_item_GetName( p_current->p_input ); if( psz_tmp ) psz_name = vlc_xml_encode( psz_tmp ); free( psz_tmp ); if( psz_name ) { char* psz_artist = NULL; psz_tmp = input_item_GetArtist( p_current->p_input ); if( psz_tmp ) psz_artist = vlc_xml_encode( psz_tmp ); free( psz_tmp ); mtime_t i_duration = input_item_GetDuration( p_current->p_input ); int min = ( i_duration / 1000000 ) / 60; int sec = ( i_duration / 1000000 ) - min * 60; // Print the artist if we have one if( psz_artist && *psz_artist ) fprintf( p_export->p_file, " <li>%s - %s (%02d:%02d)</li>\n", psz_artist, psz_name, min, sec ); else fprintf( p_export->p_file, " <li>%s (%2d:%2d)</li>\n", psz_name, min, sec ); free( psz_artist ); } free( psz_name ); } }
static char *input_xml( input_item_t *p_item, char *(*func)(input_item_t *) ) { char *tmp = func( p_item ); if( tmp == NULL ) return NULL; char *ret = vlc_xml_encode( tmp ); free( tmp ); return ret; }
/** * \brief Prints the XSPF header to file, writes each item by xspf_export_item() * and closes the open xml elements * \param p_this the VLC playlist object * \return VLC_SUCCESS if some memory is available, otherwise VLC_ENONMEM */ int xspf_export_playlist( vlc_object_t *p_this ) { const playlist_export_t *p_export = (playlist_export_t *)p_this; int i, i_count; char *psz_temp; playlist_item_t *p_node = p_export->p_root; /* write XSPF XML header */ fprintf( p_export->p_file, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" ); fprintf( p_export->p_file, "<playlist xmlns=\"http://xspf.org/ns/0/\" " \ "xmlns:vlc=\"http://www.videolan.org/vlc/playlist/ns/0/\" " \ "version=\"1\">\n" ); if( !p_node ) return VLC_SUCCESS; /* save name of the playlist node */ psz_temp = vlc_xml_encode( p_node->p_input->psz_name ); if( *psz_temp ) { fprintf( p_export->p_file, "\t<title>%s</title>\n", psz_temp ); } free( psz_temp ); /* export all items in a flat format */ fprintf( p_export->p_file, "\t<trackList>\n" ); i_count = 0; for( i = 0; i < p_node->i_children; i++ ) { xspf_export_item( p_node->pp_children[i], p_export->p_file, &i_count ); } fprintf( p_export->p_file, "\t</trackList>\n" ); /* export the tree structure in <extension> */ fprintf( p_export->p_file, "\t<extension application=\"" \ "http://www.videolan.org/vlc/playlist/0\">\n" ); i_count = 0; for( i = 0; i < p_node->i_children; i++ ) { xspf_extension_item( p_node->pp_children[i], p_export->p_file, &i_count, 2 ); } fprintf( p_export->p_file, "\t</extension>\n" ); /* close the header elements */ fprintf( p_export->p_file, "</playlist>\n" ); return VLC_SUCCESS; }
static void encode (const char *in, const char *out) { char *buf; printf ("\"%s\" -> \"%s\" ?\n", in, out); buf = vlc_xml_encode (in); if (strcmp (buf, out)) { printf (" ERROR: got \"%s\"\n", buf); exit (2); } free (buf); }
/** * \brief exports one item in extension to file and traverse if item is a node * \param p_item playlist item to export * \param p_file file to write xml-converted item to * \param p_i_count counter for track identifiers * \param i_depth identation depth */ static void xspf_extension_item( playlist_item_t *p_item, FILE *p_file, int *p_i_count, int i_depth ) { if( !p_item ) return; /* if we get a node here, we must traverse it */ if( p_item->i_children >= 0 ) { int i; char *psz_temp = NULL; if( p_item->p_input->psz_name ) psz_temp = vlc_xml_encode( p_item->p_input->psz_name ); for(int j=0;j<i_depth;j++) fprintf( p_file, "\t" ); fprintf( p_file, "<vlc:node title=\"%s\">\n", psz_temp ? psz_temp : "" ); free( psz_temp ); for( i = 0; i < p_item->i_children; i++ ) { xspf_extension_item( p_item->pp_children[i], p_file, p_i_count, i_depth + 1 ); } for(int j=0;j<i_depth;j++) fprintf( p_file, "\t" ); fprintf( p_file, "</vlc:node>\n" ); return; } /* print leaf and increase the counter */ for(int j=0;j<i_depth;j++) fprintf( p_file, "\t" ); fprintf( p_file, "<vlc:item tid=\"%i\"/>\n", *p_i_count ); ( *p_i_count )++; return; }
/** ************************************************************************** * \brief Write the XSPF playlist given the list of files *****************************************************************************/ static int WriteXSPF( char **pp_buffer, vlc_array_t *p_filenames, const char *psz_zippath ) { char *psz_zip = strrchr( psz_zippath, DIR_SEP_CHAR ); psz_zip = vlc_xml_encode( psz_zip ? (psz_zip+1) : psz_zippath ); if( asprintf( pp_buffer, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" "<playlist version=\"1\" xmlns=\"http://xspf.org/ns/0/\" " "xmlns:vlc=\"http://www.videolan.org/vlc/playlist/ns/0/\">\n" " <title>%s</title>\n" " <trackList>\n", psz_zip ) == -1) return -1; /* Root node */ node *playlist = new_node( psz_zip ); free( psz_zip ); /* Encode the URI and append ZIP_SEP */ char *psz_pathtozip; escapeToXml( &psz_pathtozip, psz_zippath ); if( astrcatf( &psz_pathtozip, "%s", ZIP_SEP ) < 0 ) { free_all_node( playlist ); return -1; } int i_track = 0; for( int i = 0; i < vlc_array_count( p_filenames ); ++i ) { char *psz_name = (char*) vlc_array_item_at_index( p_filenames, i ); int i_len = strlen( psz_name ); if( !i_len ) continue; /* Is it a folder ? */ if( psz_name[i_len-1] == '/' ) { /* Do nothing */ } else /* File */ { /* Extract file name */ char *psz_file = strrchr( psz_name, '/' ); psz_file = vlc_xml_encode( psz_file ? (psz_file+1) : psz_name ); /* Build full MRL */ char *psz_path = strdup( psz_pathtozip ); char *psz_escapedName; escapeToXml( &psz_escapedName, psz_name ); if( astrcatf( &psz_path, "%s", psz_escapedName ) < 0 ) { free( psz_escapedName ); return -1; } free( psz_escapedName ); /* Track information */ if( astrcatf( pp_buffer, " <track>\n" " <location>zip://%s</location>\n" " <title>%s</title>\n" " <extension application=\"http://www.videolan.org/vlc/playlist/0\">\n" " <vlc:id>%d</vlc:id>\n" " </extension>\n" " </track>\n", psz_path, psz_file, i_track ) < 0 ) return -1; free( psz_file ); free( psz_path ); /* Find the parent node */ node *parent = findOrCreateParentNode( playlist, psz_name ); assert( parent ); /* Add the item to this node */ item *tmp = parent->media; if( !tmp ) { parent->media = new_item( i_track ); } else { while( tmp->next ) { tmp = tmp->next; } tmp->next = new_item( i_track ); } ++i_track; } } free( psz_pathtozip ); /* Close tracklist, open the extension */ if( astrcatf( pp_buffer, " </trackList>\n" " <extension application=\"http://www.videolan.org/vlc/playlist/0\">\n" ) < 0 ) return -1; /* Write the tree */ if( nodeToXSPF( pp_buffer, playlist, true ) < 0 ) return -1; /* Close extension and playlist */ if( astrcatf( pp_buffer, " </extension>\n</playlist>\n" ) < 0 ) return -1; /* printf( "%s", *pp_buffer ); */ free_all_node( playlist ); return VLC_SUCCESS; }
inline static node* new_node( char *name ) { node *n = (node*) calloc( 1, sizeof(node) ); n->name = vlc_xml_encode( name ); return n; }
static int WriteCatalog( addons_storage_t *p_storage, addon_entry_t **pp_entries, int i_entries ) { addon_entry_t *p_entry; char *psz_file; char *psz_file_tmp; char *psz_tempstring; char *psz_userdir = config_GetUserDir( VLC_DATA_DIR ); if ( !psz_userdir ) return VLC_ENOMEM; if ( asprintf( &psz_file, "%s%s", psz_userdir, ADDONS_CATALOG ) < 1 ) { free( psz_userdir ); return VLC_ENOMEM; } free( psz_userdir ); if ( asprintf( &psz_file_tmp, "%s.tmp%"PRIu32, psz_file, (uint32_t)getpid() ) < 1 ) { free( psz_file ); return VLC_ENOMEM; } char *psz_path = strdup( psz_file ); if ( !psz_path ) { free( psz_file ); free( psz_file_tmp ); return VLC_ENOMEM; } char *psz_buf = strrchr( psz_path, DIR_SEP_CHAR ); if( psz_buf ) { *++psz_buf = '\0'; /* ensure directory exists */ if( !EMPTY_STR( psz_path ) ) recursive_mkdir( VLC_OBJECT(p_storage), psz_path ); } free( psz_path ); FILE *p_catalog = vlc_fopen( psz_file_tmp, "wt" ); if ( !p_catalog ) { free( psz_file ); free( psz_file_tmp ); return VLC_EGENERIC; } /* write XML header */ fprintf( p_catalog, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" ); fprintf( p_catalog, "<videolan xmlns=\"http://videolan.org/ns/vlc/addons/1.0\">\n" ); fprintf( p_catalog, "\t<addons>\n" ); for ( int i=0; i<i_entries; i++ ) { p_entry = pp_entries[i]; vlc_mutex_lock( &p_entry->lock ); psz_tempstring = NULL; if ( ( p_entry->e_state != ADDON_INSTALLED ) || !( p_entry->e_flags & ADDON_MANAGEABLE ) ) { vlc_mutex_unlock( &p_entry->lock ); continue; } if ( p_entry->psz_source_module ) psz_tempstring = vlc_xml_encode( p_entry->psz_source_module ); char *psz_uuid = addons_uuid_to_psz( ( const addon_uuid_t * ) & p_entry->uuid ); fprintf( p_catalog, "\t\t<addon source=\"%s\" type=\"%s\" id=\"%s\" " "downloads=\"%ld\" score=\"%d\"", ( psz_tempstring ) ? psz_tempstring : "", getTypePsz( p_entry->e_type ), psz_uuid, p_entry->i_downloads, p_entry->i_score ); free( psz_uuid ); free( psz_tempstring ); WRITE_WITH_ENTITIES( " version=\"%s\"", p_entry->psz_version ) fprintf( p_catalog, ">\n" ); WRITE_WITH_ENTITIES( "\t\t\t<name>%s</name>\n", p_entry->psz_name ) WRITE_WITH_ENTITIES( "\t\t\t<summary>%s</summary>\n", p_entry->psz_summary ) if ( p_entry->psz_description ) { psz_tempstring = p_entry->psz_description; /* FIXME: do real escaping */ while( ( psz_tempstring = strstr( psz_tempstring, "]]>" ) ) ) *psz_tempstring = ' '; fprintf( p_catalog, "\t\t\t<description><![CDATA[%s]]></description>\n", p_entry->psz_description ); } WRITE_WITH_ENTITIES( "\t\t\t<image>%s</image>\n", p_entry->psz_image_data ) WRITE_WITH_ENTITIES( "\t\t\t<archive>%s</archive>\n", p_entry->psz_archive_uri ) fprintf( p_catalog, "\t\t\t<authorship>\n" ); WRITE_WITH_ENTITIES( "\t\t\t\t<creator>%s</creator>\n", p_entry->psz_author ) WRITE_WITH_ENTITIES( "\t\t\t\t<sourceurl>%s</sourceurl>\n", p_entry->psz_source_uri ) fprintf( p_catalog, "\t\t\t</authorship>\n" ); FOREACH_ARRAY( addon_file_t *p_file, p_entry->files ) psz_tempstring = vlc_xml_encode( p_file->psz_filename ); fprintf( p_catalog, "\t\t\t<resource type=\"%s\">%s</resource>\n", getTypePsz( p_file->e_filetype ), psz_tempstring ); free( psz_tempstring ); FOREACH_END(); fprintf( p_catalog, "\t\t</addon>\n" ); vlc_mutex_unlock( &p_entry->lock ); } fprintf( p_catalog, "\t</addons>\n" ); fprintf( p_catalog, "</videolan>\n" ); fclose( p_catalog ); int i_ret = vlc_rename( psz_file_tmp, psz_file ); free( psz_file ); free( psz_file_tmp ); if( i_ret == -1 ) { msg_Err( p_storage, "could not rename temp catalog: %s", vlc_strerror_c(errno) ); return VLC_EGENERIC; } return VLC_SUCCESS; }
/** * \brief exports one item to file or traverse if item is a node * \param p_item playlist item to export * \param p_file file to write xml-converted item to * \param p_i_count counter for track identifiers */ static void xspf_export_item( playlist_item_t *p_item, FILE *p_file, int *p_i_count ) { if( !p_item ) return; /* if we get a node here, we must traverse it */ if( p_item->i_children > 0 ) { for( int i = 0; i < p_item->i_children; i++ ) xspf_export_item( p_item->pp_children[i], p_file, p_i_count ); return; } /* don't write empty nodes */ if( p_item->i_children == 0 ) return; input_item_t *p_input = p_item->p_input; char *psz; mtime_t i_duration; /* leaves can be written directly */ fputs( "\t\t<track>\n", p_file ); /* -> the location */ char *psz_uri = input_xml( p_input, input_item_GetURI ); if( psz_uri && *psz_uri ) fprintf( p_file, "\t\t\t<location>%s</location>\n", psz_uri ); /* -> the name/title (only if different from uri)*/ psz = input_xml( p_input, input_item_GetTitle ); if( psz && strcmp( psz_uri, psz ) ) fprintf( p_file, "\t\t\t<title>%s</title>\n", psz ); free( psz ); free( psz_uri ); if( p_item->p_input->p_meta == NULL ) { goto xspfexportitem_end; } /* -> the artist/creator */ psz = input_xml( p_input, input_item_GetArtist ); if( psz && *psz ) fprintf( p_file, "\t\t\t<creator>%s</creator>\n", psz ); free( psz ); /* -> the album */ psz = input_xml( p_input, input_item_GetAlbum ); if( psz && *psz ) fprintf( p_file, "\t\t\t<album>%s</album>\n", psz ); free( psz ); /* -> the track number */ psz = input_xml( p_input, input_item_GetTrackNum ); if( psz ) { int i_tracknum = atoi( psz ); free( psz ); if( i_tracknum > 0 ) fprintf( p_file, "\t\t\t<trackNum>%i</trackNum>\n", i_tracknum ); } /* -> the description */ psz = input_xml( p_input, input_item_GetDescription ); if( psz && *psz ) fprintf( p_file, "\t\t\t<annotation>%s</annotation>\n", psz ); free( psz ); psz = input_xml( p_input, input_item_GetURL ); if( psz && *psz ) fprintf( p_file, "\t\t\t<info>%s</info>\n", psz ); free( psz ); psz = input_xml( p_input, input_item_GetArtURL ); if( psz && *psz ) fprintf( p_file, "\t\t\t<image>%s</image>\n", psz ); free( psz ); xspfexportitem_end: /* -> the duration */ i_duration = input_item_GetDuration( p_item->p_input ); if( i_duration > 0 ) fprintf( p_file, "\t\t\t<duration>%"PRIu64"</duration>\n", i_duration / 1000 ); /* export the intenal id and the input's options (bookmarks, ...) * in <extension> */ fputs( "\t\t\t<extension application=\"" "http://www.videolan.org/vlc/playlist/0\">\n", p_file ); /* print the id and increase the counter */ fprintf( p_file, "\t\t\t\t<vlc:id>%i</vlc:id>\n", *p_i_count ); ( *p_i_count )++; for( int i = 0; i < p_item->p_input->i_options; i++ ) { char* psz_src = p_item->p_input->ppsz_options[i]; char* psz_ret = NULL; if ( psz_src[0] == ':' ) psz_src++; psz_ret = vlc_xml_encode( psz_src ); if ( psz_ret == NULL ) continue; fprintf( p_file, "\t\t\t\t<vlc:option>%s</vlc:option>\n", psz_ret ); free( psz_ret ); } fputs( "\t\t\t</extension>\n", p_file ); fputs( "\t\t</track>\n", p_file ); }