Clean up preparser/fetcher code.
authorLaurent Aimar <fenrir@videolan.org>
Mon, 22 Dec 2008 11:51:13 +0000 (12:51 +0100)
committerLaurent Aimar <fenrir@videolan.org>
Mon, 5 Jan 2009 19:49:05 +0000 (20:49 +0100)
No functionnality changes.

13 files changed:
src/Makefile.am
src/input/input_interface.h
src/input/meta.c
src/playlist/art.c [new file with mode: 0644]
src/playlist/art.h [new file with mode: 0644]
src/playlist/control.c
src/playlist/engine.c
src/playlist/fetcher.c [new file with mode: 0644]
src/playlist/fetcher.h [new file with mode: 0644]
src/playlist/playlist_internal.h
src/playlist/preparser.c [new file with mode: 0644]
src/playlist/preparser.h [new file with mode: 0644]
src/playlist/thread.c

index 0fe5b54..57a4ede 100644 (file)
@@ -295,11 +295,14 @@ SOURCES_libvlc_common = \
        interface/intf_eject.c \
        interface/interaction.c \
        playlist/playlist_internal.h \
+       playlist/art.c \
        playlist/thread.c \
        playlist/control.c \
        playlist/engine.c \
+       playlist/fetcher.c \
        playlist/sort.c \
        playlist/loadsave.c \
+       playlist/preparser.c \
        playlist/tree.c \
        playlist/item.c \
        playlist/search.c \
index 050ddc5..a585666 100644 (file)
 /**********************************************************************
  * Item metadata
  **********************************************************************/
-int  input_ArtFind( playlist_t *, input_item_t * );
-int  input_DownloadAndCacheArt( playlist_t *, input_item_t * );
-
 void input_item_SetPreparsed( input_item_t *p_i, bool b_preparsed );
-
-typedef struct
-{
-    char *psz_artist;
-    char *psz_album;
-    char *psz_arturl;
-    bool b_found;
-} playlist_album_t;
-
-
 void input_item_SetArtNotFound( input_item_t *p_i, bool b_not_found );
 void input_item_SetArtFetched( input_item_t *p_i, bool b_art_fetched );
 
index a086399..7df5ce3 100644 (file)
 #endif
 
 #include <vlc_common.h>
-#include <vlc_input.h>
-#include <vlc_stream.h>
-#include <vlc_meta.h>
 #include <vlc_playlist.h>
-#include <vlc_charset.h>
-#include <vlc_strings.h>
 #include "input_internal.h"
-#include "../playlist/playlist_internal.h"
-#include <errno.h>
-#include <limits.h>                                             /* PATH_MAX */
-#include <assert.h>
+#include "../playlist/art.h"
 
-#ifdef HAVE_SYS_STAT_H
-#   include <sys/stat.h>
-#endif
-
-#include "../libvlc.h"
-
-const char *
-input_MetaTypeToLocalizedString( vlc_meta_type_t meta_type )
+/* FIXME bad name convention */
+const char * input_MetaTypeToLocalizedString( vlc_meta_type_t meta_type )
 {
     switch( meta_type )
     {
@@ -72,415 +58,36 @@ input_MetaTypeToLocalizedString( vlc_meta_type_t meta_type )
     }
 };
 
-#define input_FindArtInCache(a,b) __input_FindArtInCache(VLC_OBJECT(a),b)
-static int __input_FindArtInCache( vlc_object_t *, input_item_t *p_item );
-
-/* Return codes:
- *   0 : Art is in cache or is a local file
- *   1 : Art found, need to download
- *  -X : Error/not found
- */
-int input_ArtFind( playlist_t *p_playlist, input_item_t *p_item )
-{
-    int i_ret = VLC_EGENERIC;
-    module_t *p_module;
-    char *psz_title, *psz_artist, *psz_album;
-
-    psz_artist = input_item_GetArtist( p_item );
-    psz_album = input_item_GetAlbum( p_item );
-    psz_title = input_item_GetTitle( p_item );
-    if(!psz_title)
-        psz_title = input_item_GetName( p_item );
-
-    if( !psz_title && !psz_artist && !psz_album )
-        return VLC_EGENERIC;
-
-    free( psz_title );
-
-    /* If we already checked this album in this session, skip */
-    if( psz_artist && psz_album )
-    {
-        FOREACH_ARRAY( playlist_album_t album, pl_priv(p_playlist)->fetcher.albums )
-            if( !strcmp( album.psz_artist, psz_artist ) &&
-                !strcmp( album.psz_album, psz_album ) )
-            {
-                msg_Dbg( p_playlist, " %s - %s has already been searched",
-                         psz_artist, psz_album );
-        /* TODO-fenrir if we cache art filename too, we can go faster */
-                free( psz_artist );
-                free( psz_album );
-                if( album.b_found )
-                {
-                    if( !strncmp( album.psz_arturl, "file://", 7 ) )
-                        input_item_SetArtURL( p_item, album.psz_arturl );
-                    else /* Actually get URL from cache */
-                        input_FindArtInCache( p_playlist, p_item );
-                    return 0;
-                }
-                else
-                {
-                    return VLC_EGENERIC;
-                }
-            }
-        FOREACH_END();
-    }
-    free( psz_artist );
-    free( psz_album );
-
-    input_FindArtInCache( p_playlist, p_item );
-
-    char *psz_arturl = input_item_GetArtURL( p_item );
-    if( psz_arturl )
-    {
-        /* We already have an URL */
-        if( !strncmp( psz_arturl, "file://", strlen( "file://" ) ) )
-        {
-            free( psz_arturl );
-            return 0; /* Art is in cache, no need to go further */
-        }
-
-        free( psz_arturl );
-        
-        /* Art need to be put in cache */
-        return 1;
-    }
-
-    PL_LOCK;
-    p_playlist->p_private = p_item;
-    psz_album = input_item_GetAlbum( p_item );
-    psz_artist = input_item_GetArtist( p_item );
-    psz_title = input_item_GetTitle( p_item );
-    if( !psz_title )
-        psz_title = input_item_GetName( p_item );
-
-    if( psz_album && psz_artist )
-    {
-        msg_Dbg( p_playlist, "searching art for %s - %s",
-             psz_artist, psz_album );
-    }
-    else
-    {
-        msg_Dbg( p_playlist, "searching art for %s",
-             psz_title );
-    }
-    free( psz_title );
-
-    p_module = module_need( p_playlist, "art finder", NULL, false );
-
-    if( p_module )
-        i_ret = 1;
-    else
-        msg_Dbg( p_playlist, "unable to find art" );
-
-    /* Record this album */
-    if( psz_artist && psz_album )
-    {
-        playlist_album_t a;
-        a.psz_artist = psz_artist;
-        a.psz_album = psz_album;
-        a.psz_arturl = input_item_GetArtURL( p_item );
-        a.b_found = (i_ret == VLC_EGENERIC ? false : true );
-        ARRAY_APPEND( pl_priv(p_playlist)->fetcher.albums, a );
-    }
-    else
-    {
-        free( psz_artist );
-        free( psz_album );
-    }
-
-    if( p_module )
-        module_unneed( p_playlist, p_module );
-    p_playlist->p_private = NULL;
-    PL_UNLOCK;
-
-    return i_ret;
-}
-
-static void ArtCacheCreateDir( const char *psz_dir )
-{
-    char newdir[strlen( psz_dir ) + 1];
-    strcpy( newdir, psz_dir );
-    char * psz_newdir = newdir;
-    char * psz = psz_newdir;
-
-    while( *psz )
-    {
-        while( *psz && *psz != DIR_SEP_CHAR) psz++;
-        if( !*psz ) break;
-        *psz = 0;
-        if( !EMPTY_STR( psz_newdir ) )
-            utf8_mkdir( psz_newdir, 0700 );
-        *psz = DIR_SEP_CHAR;
-        psz++;
-    }
-    utf8_mkdir( psz_dir, 0700 );
-}
-
-static char * ArtCacheGetSanitizedFileName( const char *psz )
-{
-    char *dup = strdup(psz);
-    int i;
-
-    filename_sanitize( dup );
-
-    /* Doesn't create a filename with invalid characters
-     * TODO: several filesystems forbid several characters: list them all
-     */
-    for( i = 0; dup[i] != '\0'; i++ )
-    {
-        if( dup[i] == DIR_SEP_CHAR )
-            dup[i] = ' ';
-    }
-    return dup;
-}
-
-#define ArtCacheGetDirPath(a,b,c,d,e) __ArtCacheGetDirPath(VLC_OBJECT(a),b,c,d,e)
-static void __ArtCacheGetDirPath( vlc_object_t *p_obj,
-                                  char *psz_dir,
-                                  const char *psz_title,
-                                  const char *psz_artist, const char *psz_album )
-{
-    (void)p_obj;
-    char *psz_cachedir = config_GetCacheDir();
-
-    if( !EMPTY_STR(psz_artist) && !EMPTY_STR(psz_album) )
-    {
-        char * psz_album_sanitized = ArtCacheGetSanitizedFileName( psz_album );
-        char * psz_artist_sanitized = ArtCacheGetSanitizedFileName( psz_artist );
-        snprintf( psz_dir, PATH_MAX, "%s" DIR_SEP
-                  "art" DIR_SEP "artistalbum" DIR_SEP "%s" DIR_SEP "%s",
-                  psz_cachedir, psz_artist_sanitized, psz_album_sanitized );
-        free( psz_album_sanitized );
-        free( psz_artist_sanitized );
-    }
-    else
-    {
-        char * psz_title_sanitized = ArtCacheGetSanitizedFileName( psz_title );
-        snprintf( psz_dir, PATH_MAX, "%s" DIR_SEP
-                  "art" DIR_SEP "title" DIR_SEP "%s",
-                  psz_cachedir, psz_title_sanitized );
-        free( psz_title_sanitized );
-    }
-    free( psz_cachedir );
-}
-
-
-
-#define ArtCacheGetFilePath(a,b,c,d,e,f) __ArtCacheGetFilePath(VLC_OBJECT(a),b,c,d,e,f)
-static void __ArtCacheGetFilePath( vlc_object_t *p_obj,
-                                   char * psz_filename,
-                                   const char *psz_title,
-                                   const char *psz_artist, const char *psz_album,
-                                   const char *psz_extension )
-{
-    char psz_dir[PATH_MAX+1];
-    char * psz_ext;
-    ArtCacheGetDirPath( p_obj, psz_dir, psz_title, psz_artist, psz_album );
-
-    if( psz_extension )
-    {
-        psz_ext = strndup( psz_extension, 6 );
-        filename_sanitize( psz_ext );
-    }
-    else psz_ext = strdup( "" );
-
-    snprintf( psz_filename, PATH_MAX, "file://%s" DIR_SEP "art%s",
-              psz_dir, psz_ext );
-
-    free( psz_ext );
-}
-
-static int __input_FindArtInCache( vlc_object_t *p_obj, input_item_t *p_item )
-{
-    char *psz_artist;
-    char *psz_album;
-    char *psz_title;
-    char psz_dirpath[PATH_MAX+1];
-    char psz_filepath[PATH_MAX+1];
-    char * psz_filename;
-    DIR * p_dir;
-
-    psz_artist = input_item_GetArtist( p_item );
-    psz_album = input_item_GetAlbum( p_item );
-    psz_title = input_item_GetTitle( p_item );
-    if( !psz_title ) psz_title = input_item_GetName( p_item );
-
-    if( !psz_title && ( !psz_album || !psz_artist ) )
-    {
-        free( psz_artist );
-        free( psz_album );
-        free( psz_title );
-        return VLC_EGENERIC;
-    }
-
-    ArtCacheGetDirPath( p_obj, psz_dirpath, psz_title,
-                           psz_artist, psz_album );
-
-    free( psz_artist );
-    free( psz_album );
-    free( psz_title );
-
-    /* Check if file exists */
-    p_dir = utf8_opendir( psz_dirpath );
-    if( !p_dir )
-        return VLC_EGENERIC;
-
-    while( (psz_filename = utf8_readdir( p_dir )) )
-    {
-        if( !strncmp( psz_filename, "art", 3 ) )
-        {
-            snprintf( psz_filepath, PATH_MAX, "file://%s" DIR_SEP "%s",
-                      psz_dirpath, psz_filename );
-            input_item_SetArtURL( p_item, psz_filepath );
-            free( psz_filename );
-            closedir( p_dir );
-            return VLC_SUCCESS;
-        }
-        free( psz_filename );
-    }
-
-    /* Not found */
-    closedir( p_dir );
-    return VLC_EGENERIC;
-}
-
-/**
- * Download the art using the URL or an art downloaded
- * This function should be called only if data is not already in cache
- */
-int input_DownloadAndCacheArt( playlist_t *p_playlist, input_item_t *p_item )
-{
-    int i_status = VLC_EGENERIC;
-    stream_t *p_stream;
-    char psz_filename[PATH_MAX+1];
-    char *psz_artist = NULL;
-    char *psz_album = NULL;
-    char *psz_title = NULL;
-    char *psz_arturl;
-    char *psz_type;
-
-    psz_artist = input_item_GetArtist( p_item );
-    psz_album = input_item_GetAlbum( p_item );
-    psz_title = input_item_GetTitle( p_item );
-    if( !psz_title )
-        psz_title = input_item_GetName( p_item );
-
-    if( !psz_title && (!psz_artist || !psz_album) )
-    {
-        free( psz_title );
-        free( psz_album );
-        free( psz_artist );
-        return VLC_EGENERIC;
-    }
-
-    psz_arturl = input_item_GetArtURL( p_item );
-    assert( !EMPTY_STR( psz_arturl ) );
-
-    if( !strncmp( psz_arturl , "file://", 7 ) )
-    {
-        msg_Dbg( p_playlist, "Album art is local file, no need to cache" );
-        free( psz_arturl );
-        return VLC_SUCCESS;
-    }
-    else if( !strncmp( psz_arturl , "APIC", 4 ) )
-    {
-        msg_Warn( p_playlist, "APIC fetch not supported yet" );
-        free( psz_arturl );
-        return VLC_EGENERIC;
-    }
-
-    psz_type = strrchr( psz_arturl, '.' );
-    if( psz_type && strlen( psz_type ) > 5 )
-        psz_type = NULL; /* remove extension if it's > to 4 characters */
-
-    /* Warning: psz_title, psz_artist, psz_album may change in ArtCache*() */
-
-    ArtCacheGetDirPath( p_playlist, psz_filename, psz_title, psz_artist,
-                        psz_album );
-    ArtCacheCreateDir( psz_filename );
-    ArtCacheGetFilePath( p_playlist, psz_filename, psz_title, psz_artist,
-                         psz_album, psz_type );
-
-    free( psz_artist );
-    free( psz_album );
-    free( psz_title );
-
-    p_stream = stream_UrlNew( p_playlist, psz_arturl );
-    if( p_stream )
-    {
-        uint8_t p_buffer[65536];
-        long int l_read;
-        FILE *p_file = utf8_fopen( psz_filename+7, "w" );
-        if( p_file == NULL ) {
-            msg_Err( p_playlist, "Unable write album art in %s",
-                     psz_filename + 7 );
-            free( psz_arturl );
-            return VLC_EGENERIC;
-        }
-        int err = 0;
-        while( ( l_read = stream_Read( p_stream, p_buffer, sizeof (p_buffer) ) ) )
-        {
-            if( fwrite( p_buffer, l_read, 1, p_file ) != 1 )
-            {
-                err = errno;
-                break;
-            }
-        }
-        if( fclose( p_file ) && !err )
-            err = errno;
-        stream_Delete( p_stream );
-
-        if( err )
-        {
-            errno = err;
-            msg_Err( p_playlist, "%s: %m", psz_filename );
-        }
-        else
-            msg_Dbg( p_playlist, "album art saved to %s\n", psz_filename );
-
-        input_item_SetArtURL( p_item, psz_filename );
-        i_status = VLC_SUCCESS;
-    }
-    free( psz_arturl );
-    return i_status;
-}
-
 void input_ExtractAttachmentAndCacheArt( input_thread_t *p_input )
 {
     input_item_t *p_item = p_input->p->p_item;
-    const char *psz_arturl;
-    const char *psz_artist = NULL;
-    const char *psz_album = NULL;
-    const char *psz_title = NULL;
-    char *psz_type = NULL;
-    char psz_filename[PATH_MAX+1];
-    FILE *f;
-    input_attachment_t *p_attachment;
-    struct stat s;
-    int i_idx;
-
-    /* TODO-fenrir merge input_ArtFind with download and make it set the flags FETCH
-     * and then set it here to to be faster */
-
-    psz_arturl = vlc_meta_Get( p_item->p_meta, vlc_meta_ArtworkURL );
 
+    /* */
+    const char *psz_arturl = vlc_meta_Get( p_item->p_meta, vlc_meta_ArtworkURL );
     if( !psz_arturl || strncmp( psz_arturl, "attachment://", strlen("attachment://") ) )
     {
         msg_Err( p_input, "internal input error with input_ExtractAttachmentAndCacheArt" );
         return;
     }
 
+    playlist_t *p_playlist = pl_Hold( p_input );
+    if( !p_playlist )
+        return;
+
+
     if( input_item_IsArtFetched( p_item ) )
     {
         /* XXX Weird, we should not have end up with attachment:// art url unless there is a race
          * condition */
         msg_Warn( p_input, "internal input error with input_ExtractAttachmentAndCacheArt" );
-        input_FindArtInCache( p_input, p_item );
+        playlist_FindArtInCache( p_item );
+        pl_Release( p_playlist );
         return;
     }
 
     /* */
-    for( i_idx = 0, p_attachment = NULL; i_idx < p_input->p->i_attachment; i_idx++ )
+    input_attachment_t *p_attachment = NULL;
+    for( int i_idx = 0; i_idx < p_input->p->i_attachment; i_idx++ )
     {
         if( !strcmp( p_input->p->attachment[i_idx]->psz_name,
                      &psz_arturl[strlen("attachment://")] ) )
@@ -492,48 +99,21 @@ void input_ExtractAttachmentAndCacheArt( input_thread_t *p_input )
     if( !p_attachment || p_attachment->i_data <= 0 )
     {
         msg_Warn( p_input, "internal input error with input_ExtractAttachmentAndCacheArt" );
+        pl_Release( p_playlist );
         return;
     }
 
-    psz_artist = vlc_meta_Get( p_item->p_meta, vlc_meta_Artist );
-    psz_album = vlc_meta_Get( p_item->p_meta, vlc_meta_Album );
-    psz_title = vlc_meta_Get( p_item->p_meta, vlc_meta_Title );
+    /* */
+    const char *psz_type = NULL;
     if( !strcmp( p_attachment->psz_mime, "image/jpeg" ) )
-        psz_type = strdup( ".jpg" );
+        psz_type = ".jpg";
     else if( !strcmp( p_attachment->psz_mime, "image/png" ) )
-        psz_type = strdup( ".png" );
-
-    if( !psz_title )
-        psz_title = p_item->psz_name;
-
-    if( (!psz_artist || !psz_album ) && !psz_title )
-    {
-        free( psz_type );
-        return;
-    }
+        psz_type = ".png";
 
-    ArtCacheGetDirPath( p_input, psz_filename, psz_title, psz_artist, psz_album );
-    ArtCacheCreateDir( psz_filename );
-    ArtCacheGetFilePath( p_input, psz_filename, psz_title, psz_artist, psz_album, psz_type );
-    free( psz_type );
-
-    /* Check if we already dumped it */
-    if( !utf8_stat( psz_filename+7, &s ) )
-    {
-        vlc_meta_Set( p_item->p_meta, vlc_meta_ArtworkURL, psz_filename );
-        return;
-    }
+    /* */
+    playlist_SaveArt( p_playlist, p_item,
+                      p_attachment->p_data, p_attachment->i_data, psz_type );
 
-    f = utf8_fopen( psz_filename+7, "w" );
-    if( f )
-    {
-        if( fwrite( p_attachment->p_data, p_attachment->i_data, 1, f ) != 1 )
-            msg_Err( p_input, "%s: %m", psz_filename );
-        else
-        {
-            msg_Dbg( p_input, "album art saved to %s\n", psz_filename );
-            vlc_meta_Set( p_item->p_meta, vlc_meta_ArtworkURL, psz_filename );
-        }
-        fclose( f );
-    }
+    pl_Release( p_playlist );
 }
+
diff --git a/src/playlist/art.c b/src/playlist/art.c
new file mode 100644 (file)
index 0000000..8d6b0d8
--- /dev/null
@@ -0,0 +1,303 @@
+/*****************************************************************************
+ * art.c : Art metadata handling
+ *****************************************************************************
+ * Copyright (C) 1998-2008 the VideoLAN team
+ * $Id$
+ *
+ * Authors: Antoine Cellerier <dionoea@videolan.org>
+ *          Clément Stenac <zorglub@videolan.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <assert.h>
+#include <vlc_common.h>
+#include <vlc_playlist.h>
+#include <vlc_charset.h>
+#include <vlc_strings.h>
+#include <vlc_stream.h>
+
+#ifdef HAVE_SYS_STAT_H
+#   include <sys/stat.h>
+#endif
+
+#include "../libvlc.h"
+#include "playlist_internal.h"
+
+static void ArtCacheCreateDir( const char *psz_dir )
+{
+    char newdir[strlen( psz_dir ) + 1];
+    strcpy( newdir, psz_dir );
+    char * psz_newdir = newdir;
+    char * psz = psz_newdir;
+
+    while( *psz )
+    {
+        while( *psz && *psz != DIR_SEP_CHAR) psz++;
+        if( !*psz ) break;
+        *psz = 0;
+        if( !EMPTY_STR( psz_newdir ) )
+            utf8_mkdir( psz_newdir, 0700 );
+        *psz = DIR_SEP_CHAR;
+        psz++;
+    }
+    utf8_mkdir( psz_dir, 0700 );
+}
+
+static char *ArtCacheGetSanitizedFileName( const char *psz )
+{
+    char *dup = strdup(psz);
+    int i;
+
+    filename_sanitize( dup );
+
+    /* Doesn't create a filename with invalid characters
+     * TODO: several filesystems forbid several characters: list them all
+     */
+    for( i = 0; dup[i] != '\0'; i++ )
+    {
+        if( dup[i] == DIR_SEP_CHAR )
+            dup[i] = ' ';
+    }
+    return dup;
+}
+
+static void ArtCacheGetDirPath( char *psz_dir,
+                                const char *psz_title,
+                                const char *psz_artist, const char *psz_album )
+{
+    char *psz_cachedir = config_GetCacheDir();
+
+    if( !EMPTY_STR(psz_artist) && !EMPTY_STR(psz_album) )
+    {
+        char * psz_album_sanitized = ArtCacheGetSanitizedFileName( psz_album );
+        char * psz_artist_sanitized = ArtCacheGetSanitizedFileName( psz_artist );
+        snprintf( psz_dir, PATH_MAX, "%s" DIR_SEP
+                  "art" DIR_SEP "artistalbum" DIR_SEP "%s" DIR_SEP "%s",
+                  psz_cachedir, psz_artist_sanitized, psz_album_sanitized );
+        free( psz_album_sanitized );
+        free( psz_artist_sanitized );
+    }
+    else
+    {
+        char * psz_title_sanitized = ArtCacheGetSanitizedFileName( psz_title );
+        snprintf( psz_dir, PATH_MAX, "%s" DIR_SEP
+                  "art" DIR_SEP "title" DIR_SEP "%s",
+                  psz_cachedir, psz_title_sanitized );
+        free( psz_title_sanitized );
+    }
+    free( psz_cachedir );
+}
+
+static char *ArtCachePath( input_item_t *p_item )
+{
+    char psz_path[PATH_MAX+1]; /* FIXME */
+
+    vlc_mutex_lock( &p_item->lock );
+
+    if( !p_item->p_meta )
+        p_item->p_meta = vlc_meta_New();
+    if( !p_item->p_meta )
+    {
+        vlc_mutex_unlock( &p_item->lock );
+        return NULL;
+    }
+
+    const char *psz_artist = vlc_meta_Get( p_item->p_meta, vlc_meta_Artist );
+    const char *psz_album = vlc_meta_Get( p_item->p_meta, vlc_meta_Album );
+    const char *psz_title = vlc_meta_Get( p_item->p_meta, vlc_meta_Title );
+
+    if( !psz_title )
+        psz_title = p_item->psz_name;
+
+    if( (!psz_artist || !psz_album ) && !psz_title )
+    {
+        vlc_mutex_unlock( &p_item->lock );
+        return NULL;
+    }
+
+    ArtCacheGetDirPath( psz_path, psz_title, psz_artist, psz_album );
+
+    vlc_mutex_unlock( &p_item->lock );
+
+    return strdup( psz_path );
+}
+
+static char *ArtCacheName( input_item_t *p_item, const char *psz_type )
+{
+    char *psz_path = ArtCachePath( p_item );
+    if( !psz_path )
+        return NULL;
+
+    ArtCacheCreateDir( psz_path );
+
+    char *psz_ext = strdup( psz_type ? psz_type : "" );
+    filename_sanitize( psz_ext );
+
+    char *psz_filename;
+    if( asprintf( &psz_filename, "file://%s" DIR_SEP "art%s", psz_path, psz_ext ) < 0 )
+        psz_filename = NULL;
+
+    free( psz_ext );
+    free( psz_path );
+
+    return psz_filename;
+}
+
+int playlist_FindArtInCache( input_item_t *p_item )
+{
+    char *psz_path = ArtCachePath( p_item );
+
+    if( !psz_path )
+        return VLC_EGENERIC;
+
+    /* Check if file exists */
+    DIR *p_dir = utf8_opendir( psz_path );
+    if( !p_dir )
+    {
+        free( psz_path );
+        return VLC_EGENERIC;
+    }
+
+    bool b_found = false;
+    char *psz_filename;
+    while( !b_found && (psz_filename = utf8_readdir( p_dir )) )
+    {
+        if( !strncmp( psz_filename, "art", 3 ) )
+        {
+            char *psz_file;
+            if( asprintf( &psz_file, "file://%s" DIR_SEP "%s",
+                          psz_path, psz_filename ) < 0 )
+                psz_file = NULL;
+            if( psz_file )
+                input_item_SetArtURL( p_item, psz_file );
+            free( psz_file );
+
+            b_found = true;
+        }
+        free( psz_filename );
+    }
+
+    /* */
+    closedir( p_dir );
+    free( psz_path );
+    return b_found ? VLC_SUCCESS : VLC_EGENERIC;
+}
+
+/**
+ * Download the art using the URL or an art downloaded
+ * This function should be called only if data is not already in cache
+ */
+int playlist_DownloadArt( playlist_t *p_playlist, input_item_t *p_item )
+{
+    char *psz_arturl = input_item_GetArtURL( p_item );
+    assert( *psz_arturl );
+
+    if( !strncmp( psz_arturl , "file://", 7 ) )
+    {
+        msg_Dbg( p_playlist, "Album art is local file, no need to cache" );
+        free( psz_arturl );
+        return VLC_SUCCESS;
+    }
+
+    if( !strncmp( psz_arturl , "APIC", 4 ) )
+    {
+        msg_Warn( p_playlist, "APIC fetch not supported yet" );
+        goto error;
+    }
+
+    stream_t *p_stream = stream_UrlNew( p_playlist, psz_arturl );
+    if( !p_stream )
+        goto error;
+
+    uint8_t *p_data = NULL;
+    int i_data = 0;
+    for( ;; )
+    {
+        int i_read = 65536;
+
+        if( i_data + i_read <= i_data ) /* Protect gainst overflow */
+            break;
+
+        p_data = realloc( p_data, i_data + i_read );
+        if( !p_data )
+            break;
+
+        i_read = stream_Read( p_stream, &p_data[i_data], i_read );
+        if( i_read <= 0 )
+            break;
+
+        i_data += i_read;
+    }
+    stream_Delete( p_stream );
+
+    if( p_data && i_data > 0 )
+    {
+        char *psz_type = strrchr( psz_arturl, '.' );
+        if( psz_type && strlen( psz_type ) > 5 )
+            psz_type = NULL; /* remove extension if it's > to 4 characters */
+
+        playlist_SaveArt( p_playlist, p_item, p_data, i_data, psz_type );
+    }
+
+    free( p_data );
+
+    free( psz_arturl );
+    return VLC_SUCCESS;
+
+error:
+    free( psz_arturl );
+    return VLC_EGENERIC;
+}
+
+/* */
+int playlist_SaveArt( playlist_t *p_playlist, input_item_t *p_item,
+                      const uint8_t *p_buffer, int i_buffer, const char *psz_type )
+{
+    char *psz_filename = ArtCacheName( p_item, psz_type );
+
+    if( !psz_filename )
+        return VLC_EGENERIC;
+
+    /* Check if we already dumped it */
+    struct stat s;
+    if( !utf8_stat( psz_filename+7, &s ) )
+    {
+        input_item_SetArtURL( p_item, psz_filename );
+        return VLC_SUCCESS;
+    }
+
+    /* Dump it otherwise */
+    FILE *f = utf8_fopen( psz_filename+7, "w" );
+    if( f )
+    {
+        if( fwrite( p_buffer, i_buffer, 1, f ) != 1 )
+        {
+            msg_Err( p_playlist, "%s: %m", psz_filename );
+        }
+        else
+        {
+            msg_Dbg( p_playlist, "album art saved to %s\n", psz_filename );
+            input_item_SetArtURL( p_item, psz_filename );
+        }
+        fclose( f );
+    }
+    return VLC_SUCCESS;
+}
+
diff --git a/src/playlist/art.h b/src/playlist/art.h
new file mode 100644 (file)
index 0000000..26162a1
--- /dev/null
@@ -0,0 +1,43 @@
+/*****************************************************************************
+ * art.h:
+ *****************************************************************************
+ * Copyright (C) 1999-2008 the VideoLAN team
+ * $Id$
+ *
+ * Authors: Samuel Hocevar <sam@zoy.org>
+ *          Clément Stenac <zorglub@videolan.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifndef _PLAYLIST_ART_H
+#define _PLAYLIST_ART_H 1
+
+typedef struct
+{
+    char *psz_artist;
+    char *psz_album;
+    char *psz_arturl;
+    bool b_found;
+
+} playlist_album_t;
+
+int playlist_FindArtInCache( input_item_t * );
+
+int playlist_DownloadArt( playlist_t *, input_item_t * );
+int playlist_SaveArt( playlist_t *, input_item_t *, const uint8_t *p_buffer, int i_buffer, const char *psz_type );
+
+#endif
+
index 04ca864..958af7a 100644 (file)
@@ -188,30 +188,20 @@ static int PlaylistVAControl( playlist_t * p_playlist, int i_query, va_list args
 int playlist_PreparseEnqueue( playlist_t *p_playlist,
                               input_item_t *p_item )
 {
-    playlist_preparse_t *p_preparse = &pl_priv(p_playlist)->preparse;
+    playlist_private_t *p_sys = pl_priv(p_playlist);
 
-    vlc_gc_incref( p_item );
+    playlist_preparser_Push( p_sys->p_preparser, p_item );
 
-    vlc_mutex_lock( &p_preparse->lock );
-    INSERT_ELEM( p_preparse->pp_waiting, p_preparse->i_waiting,
-                 p_preparse->i_waiting, p_item );
-    vlc_cond_signal( &p_preparse->wait );
-    vlc_mutex_unlock( &p_preparse->lock );
     return VLC_SUCCESS;
 }
 
 int playlist_AskForArtEnqueue( playlist_t *p_playlist,
                                input_item_t *p_item )
 {
-    playlist_fetcher_t *p_fetcher = &pl_priv(p_playlist)->fetcher;
+    playlist_private_t *p_sys = pl_priv(p_playlist);
 
-    vlc_gc_incref( p_item );
+    playlist_fetcher_Push( p_sys->p_fetcher, p_item );
 
-    vlc_mutex_lock( &p_fetcher->lock );
-    INSERT_ELEM( p_fetcher->pp_waiting, p_fetcher->i_waiting,
-                 p_fetcher->i_waiting, p_item );
-    vlc_cond_signal( &p_fetcher->wait );
-    vlc_mutex_unlock( &p_fetcher->lock );
     return VLC_SUCCESS;
 }
 
@@ -490,7 +480,8 @@ int playlist_PlayItem( playlist_t *p_playlist, playlist_item_t *p_item )
     }
     free( psz_uri );
 
-    if( pl_priv(p_playlist)->fetcher.i_art_policy == ALBUM_ART_WHEN_PLAYED )
+    /* FIXME remove access to fetcher private data */
+    if( pl_priv(p_playlist)->p_fetcher->i_art_policy == ALBUM_ART_WHEN_PLAYED )
     {
         bool b_has_art;
 
index 14a6243..5a8e377 100644 (file)
@@ -174,37 +174,14 @@ playlist_t * playlist_Create( vlc_object_t *p_parent )
 
 static void playlist_Destructor( vlc_object_t * p_this )
 {
-    playlist_t * p_playlist = (playlist_t *)p_this;
+    playlist_t *p_playlist = (playlist_t *)p_this;
+    playlist_private_t *p_sys = pl_priv(p_playlist);
 
-    /* Destroy the item preparser */
-    playlist_preparse_t *p_preparse = &pl_priv(p_playlist)->preparse;
-    if (p_preparse->up)
-    {
-        vlc_cancel (p_preparse->thread);
-        vlc_join (p_preparse->thread, NULL);
-    }
-    while (p_preparse->i_waiting > 0)
-    {   /* Any left-over unparsed item? */
-        vlc_gc_decref (p_preparse->pp_waiting[0]);
-        REMOVE_ELEM (p_preparse->pp_waiting, p_preparse->i_waiting, 0);
-    }
-    vlc_cond_destroy (&p_preparse->wait);
-    vlc_mutex_destroy (&p_preparse->lock);
+    if( p_sys->p_preparser )
+        playlist_preparser_Delete( p_sys->p_preparser );
 
-    /* Destroy the item meta-infos fetcher */
-    playlist_fetcher_t *p_fetcher = &pl_priv(p_playlist)->fetcher;
-    if (p_fetcher->up)
-    {
-        vlc_cancel (p_fetcher->thread);
-        vlc_join (p_fetcher->thread, NULL);
-    }
-    while (p_fetcher->i_waiting > 0)
-    {   /* Any left-over unparsed item? */
-        vlc_gc_decref (p_fetcher->pp_waiting[0]);
-        REMOVE_ELEM (p_fetcher->pp_waiting, p_fetcher->i_waiting, 0);
-    }
-    vlc_cond_destroy (&p_fetcher->wait);
-    vlc_mutex_destroy (&p_fetcher->lock);
+    if( p_sys->p_fetcher )
+        playlist_fetcher_Delete( p_sys->p_fetcher );
 
     msg_Dbg( p_this, "Destroyed" );
 }
@@ -562,177 +539,6 @@ void playlist_LastLoop( playlist_t *p_playlist )
     PL_UNLOCK;
 }
 
-/**
- * Preparse queue loop
- *
- * @param p_obj preparse structure
- * @return never
- */
-void *playlist_PreparseLoop( void *data )
-{
-    playlist_preparse_t *p_preparse = data;
-    playlist_t *p_playlist = &((playlist_private_t *)(((char *)p_preparse)
-             - offsetof(playlist_private_t, preparse)))->public_data;
-
-    for( ;; )
-    {
-        input_item_t *p_current;
-
-        vlc_mutex_lock( &p_preparse->lock );
-        mutex_cleanup_push( &p_preparse->lock );
-
-        while( p_preparse->i_waiting == 0 )
-            vlc_cond_wait( &p_preparse->wait, &p_preparse->lock );
-
-        p_current = p_preparse->pp_waiting[0];
-        REMOVE_ELEM( p_preparse->pp_waiting, p_preparse->i_waiting, 0 );
-        vlc_cleanup_run( );
-
-        if( p_current )
-        {
-            int canc = vlc_savecancel ();
-            PL_LOCK;
-            if( p_current->i_type == ITEM_TYPE_FILE )
-            {
-                stats_TimerStart( p_playlist, "Preparse run",
-                                  STATS_TIMER_PREPARSE );
-                /* Do not preparse if it is already done (like by playing it) */
-                if( !input_item_IsPreparsed( p_current ) )
-                {
-                    PL_UNLOCK;
-                    input_Preparse( p_playlist, p_current );
-                    PL_LOCK;
-                }
-                stats_TimerStop( p_playlist, STATS_TIMER_PREPARSE );
-                PL_UNLOCK;
-                input_item_SetPreparsed( p_current, true );
-                var_SetInteger( p_playlist, "item-change", p_current->i_id );
-                PL_LOCK;
-            }
-            /* If we haven't retrieved enough meta, add to secondary queue
-             * which will run the "meta fetchers".
-             * This only checks for meta, not for art
-             * \todo don't do this for things we won't get meta for, like vids
-             */
-            char *psz_arturl = input_item_GetArtURL( p_current );
-            char *psz_name = input_item_GetName( p_current );
-            playlist_fetcher_t *p_fetcher = &pl_priv(p_playlist)->fetcher;
-            if( p_fetcher->i_art_policy == ALBUM_ART_ALL &&
-                ( !psz_arturl || strncmp( psz_arturl, "file://", 7 ) ) )
-            {
-                PL_DEBUG("meta ok for %s, need to fetch art", psz_name );
-                vlc_mutex_lock( &p_fetcher->lock );
-                INSERT_ELEM( p_fetcher->pp_waiting, p_fetcher->i_waiting,
-                             p_fetcher->i_waiting, p_current);
-                vlc_cond_signal( &p_fetcher->wait );
-                vlc_mutex_unlock( &p_fetcher->lock );
-            }
-            else
-            {
-                PL_DEBUG( "no fetch required for %s (art currently %s)",
-                          psz_name, psz_arturl );
-                vlc_gc_decref( p_current );
-            }
-            free( psz_name );
-            free( psz_arturl );
-           PL_UNLOCK;
-            vlc_restorecancel( canc );
-        }
-
-        int i_activity = var_GetInteger( p_playlist, "activity" );
-        if( i_activity < 0 ) i_activity = 0;
-        /* Sleep at least 1ms */
-        msleep( (i_activity+1) * 1000 );
-    }
-
-    assert( 0 );
-    return NULL;
-}
-
-/**
- * Fetcher loop
- *
- * \return never
- */
-void *playlist_FetcherLoop( void *data )
-{
-    playlist_fetcher_t *p_fetcher = data;
-    playlist_t *p_playlist = &((playlist_private_t *)(((char *)p_fetcher)
-             - offsetof(playlist_private_t, fetcher)))->public_data;
-
-    for( ;; )
-    {
-        input_item_t *p_item;
-
-        vlc_mutex_lock( &p_fetcher->lock );
-        mutex_cleanup_push( &p_fetcher->lock );
-
-        while( p_fetcher->i_waiting == 0 )
-            vlc_cond_wait( &p_fetcher->wait, &p_fetcher->lock );
-
-        p_item = p_fetcher->pp_waiting[0];
-        REMOVE_ELEM( p_fetcher->pp_waiting, p_fetcher->i_waiting, 0 );
-        vlc_cleanup_run( );
-
-        int canc = vlc_savecancel();
-        if( p_item )
-        {
-            int i_ret;
-
-            /* Check if it is not yet preparsed and if so wait for it
-             * (at most 0.5s)
-             * (This can happen if we fetch art on play)
-             * FIXME this doesn't work if we need to fetch meta before art...
-             */
-            for( i_ret = 0; i_ret < 10 && !input_item_IsPreparsed( p_item ); i_ret++ )
-            {
-                bool b_break;
-                PL_LOCK;
-                b_break = ( !pl_priv(p_playlist)->p_input || input_GetItem(pl_priv(p_playlist)->p_input) != p_item  ||
-                            pl_priv(p_playlist)->p_input->b_die || pl_priv(p_playlist)->p_input->b_eof || pl_priv(p_playlist)->p_input->b_error );
-                PL_UNLOCK;
-                if( b_break )
-                    break;
-                msleep( 50000 );
-            }
-
-            i_ret = input_ArtFind( p_playlist, p_item );
-            if( i_ret == 1 )
-            {
-                PL_DEBUG( "downloading art for %s", p_item->psz_name );
-                if( input_DownloadAndCacheArt( p_playlist, p_item ) )
-                    input_item_SetArtNotFound( p_item, true );
-                else {
-                    input_item_SetArtFetched( p_item, true );
-                    var_SetInteger( p_playlist, "item-change",
-                                    p_item->i_id );
-                }
-            }
-            else if( i_ret == 0 ) /* Was in cache */
-            {
-                PL_DEBUG( "found art for %s in cache", p_item->psz_name );
-                input_item_SetArtFetched( p_item, true );
-                var_SetInteger( p_playlist, "item-change", p_item->i_id );
-            }
-            else
-            {
-                PL_DEBUG( "art not found for %s", p_item->psz_name );
-                input_item_SetArtNotFound( p_item, true );
-            }
-            vlc_gc_decref( p_item );
-        }
-        vlc_restorecancel( canc );
-
-        int i_activity = var_GetInteger( p_playlist, "activity" );
-        if( i_activity < 0 ) i_activity = 0;
-        /* Sleep at least 1ms */
-        msleep( (i_activity+1) * 1000 );
-    }
-
-    assert( 0 );
-    return NULL;
-}
-
 static void VariablesInit( playlist_t *p_playlist )
 {
     vlc_value_t val;
diff --git a/src/playlist/fetcher.c b/src/playlist/fetcher.c
new file mode 100644 (file)
index 0000000..fc29fc2
--- /dev/null
@@ -0,0 +1,296 @@
+/*****************************************************************************
+ * preparse.c: Preparser thread.
+ *****************************************************************************
+ * Copyright © 1999-2009 the VideoLAN team
+ * $Id$
+ *
+ * Authors: Samuel Hocevar <sam@zoy.org>
+ *          Clément Stenac <zorglub@videolan.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc_common.h>
+#include <vlc_playlist.h>
+
+#include "art.h"
+#include "fetcher.h"
+#include "playlist_internal.h"
+
+static void *Thread( void * );
+
+playlist_fetcher_t *playlist_fetcher_New( playlist_t *p_playlist )
+{
+    // Secondary Preparse
+    playlist_fetcher_t *p_fetcher = malloc( sizeof(*p_fetcher) );
+    if( !p_fetcher )
+        return NULL;
+
+    p_fetcher->p_playlist = p_playlist;
+    vlc_mutex_init( &p_fetcher->lock );
+    vlc_cond_init( &p_fetcher->wait );
+    p_fetcher->i_waiting = 0;
+    p_fetcher->pp_waiting = NULL;
+    p_fetcher->i_art_policy = var_CreateGetInteger( p_playlist, "album-art" );
+    ARRAY_INIT( p_fetcher->albums );
+
+    if( vlc_clone( &p_fetcher->thread, Thread, p_fetcher,
+                   VLC_THREAD_PRIORITY_LOW ) )
+    {
+        msg_Err( p_playlist, "cannot spawn secondary preparse thread" );
+        free( p_fetcher );
+        return NULL;
+    }
+
+    return p_fetcher;
+}
+
+void playlist_fetcher_Push( playlist_fetcher_t *p_fetcher, input_item_t *p_item )
+{
+    vlc_gc_incref( p_item );
+
+    vlc_mutex_lock( &p_fetcher->lock );
+    INSERT_ELEM( p_fetcher->pp_waiting, p_fetcher->i_waiting,
+                 p_fetcher->i_waiting, p_item );
+    vlc_cond_signal( &p_fetcher->wait );
+    vlc_mutex_unlock( &p_fetcher->lock );
+}
+
+void playlist_fetcher_Delete( playlist_fetcher_t *p_fetcher )
+{
+    /* Destroy the item meta-infos fetcher */
+    vlc_cancel( p_fetcher->thread );
+    vlc_join( p_fetcher->thread, NULL );
+
+    while( p_fetcher->i_waiting > 0 )
+    {   /* Any left-over unparsed item? */
+        vlc_gc_decref( p_fetcher->pp_waiting[0] );
+        REMOVE_ELEM( p_fetcher->pp_waiting, p_fetcher->i_waiting, 0 );
+    }
+    vlc_cond_destroy( &p_fetcher->wait );
+    vlc_mutex_destroy( &p_fetcher->lock );
+    free( p_fetcher );
+}
+
+/**
+ * This function locates the art associated to an input item.
+ * Return codes:
+ *   0 : Art is in cache or is a local file
+ *   1 : Art found, need to download
+ *  -X : Error/not found
+ */
+static int FindArt( playlist_fetcher_t *p_fetcher, input_item_t *p_item )
+{
+    playlist_t *p_playlist = p_fetcher->p_playlist;
+    int i_ret = VLC_EGENERIC;
+    module_t *p_module;
+    char *psz_title, *psz_artist, *psz_album;
+
+    psz_artist = input_item_GetArtist( p_item );
+    psz_album = input_item_GetAlbum( p_item );
+    psz_title = input_item_GetTitle( p_item );
+    if( !psz_title )
+        psz_title = input_item_GetName( p_item );
+
+    if( !psz_title && !psz_artist && !psz_album )
+        return VLC_EGENERIC;
+
+    free( psz_title );
+
+    /* If we already checked this album in this session, skip */
+    if( psz_artist && psz_album )
+    {
+        FOREACH_ARRAY( playlist_album_t album, p_fetcher->albums )
+            if( !strcmp( album.psz_artist, psz_artist ) &&
+                !strcmp( album.psz_album, psz_album ) )
+            {
+                msg_Dbg( p_playlist, " %s - %s has already been searched",
+                         psz_artist, psz_album );
+                /* TODO-fenrir if we cache art filename too, we can go faster */
+                free( psz_artist );
+                free( psz_album );
+                if( album.b_found )
+                {
+                    if( !strncmp( album.psz_arturl, "file://", 7 ) )
+                        input_item_SetArtURL( p_item, album.psz_arturl );
+                    else /* Actually get URL from cache */
+                        playlist_FindArtInCache( p_item );
+                    return 0;
+                }
+                else
+                {
+                    return VLC_EGENERIC;
+                }
+            }
+        FOREACH_END();
+    }
+    free( psz_artist );
+    free( psz_album );
+
+    playlist_FindArtInCache( p_item );
+
+    char *psz_arturl = input_item_GetArtURL( p_item );
+    if( psz_arturl )
+    {
+        /* We already have an URL */
+        if( !strncmp( psz_arturl, "file://", strlen( "file://" ) ) )
+        {
+            free( psz_arturl );
+            return 0; /* Art is in cache, no need to go further */
+        }
+
+        free( psz_arturl );
+
+        /* Art need to be put in cache */
+        return 1;
+    }
+
+    PL_LOCK;
+    p_playlist->p_private = p_item;
+    psz_album = input_item_GetAlbum( p_item );
+    psz_artist = input_item_GetArtist( p_item );
+    psz_title = input_item_GetTitle( p_item );
+    if( !psz_title )
+        psz_title = input_item_GetName( p_item );
+
+    if( psz_album && psz_artist )
+    {
+        msg_Dbg( p_playlist, "searching art for %s - %s",
+             psz_artist, psz_album );
+    }
+    else
+    {
+        msg_Dbg( p_playlist, "searching art for %s",
+             psz_title );
+    }
+    free( psz_title );
+
+    p_module = module_need( p_playlist, "art finder", NULL, false );
+
+    if( p_module )
+        i_ret = 1;
+    else
+        msg_Dbg( p_playlist, "unable to find art" );
+
+    /* Record this album */
+    if( psz_artist && psz_album )
+    {
+        playlist_album_t a;
+        a.psz_artist = psz_artist;
+        a.psz_album = psz_album;
+        a.psz_arturl = input_item_GetArtURL( p_item );
+        a.b_found = (i_ret == VLC_EGENERIC ? false : true );
+        ARRAY_APPEND( p_fetcher->albums, a );
+    }
+    else
+    {
+        free( psz_artist );
+        free( psz_album );
+    }
+
+    if( p_module )
+        module_unneed( p_playlist, p_module );
+    p_playlist->p_private = NULL;
+    PL_UNLOCK;
+
+    return i_ret;
+}
+
+
+static void *Thread( void *p_data )
+{
+    playlist_fetcher_t *p_fetcher = p_data;
+    playlist_t *p_playlist = p_fetcher->p_playlist;
+    playlist_private_t *p_sys = pl_priv( p_playlist );
+
+    for( ;; )
+    {
+        input_item_t *p_item;
+
+        /* */
+        vlc_mutex_lock( &p_fetcher->lock );
+        mutex_cleanup_push( &p_fetcher->lock );
+
+        while( p_fetcher->i_waiting == 0 )
+            vlc_cond_wait( &p_fetcher->wait, &p_fetcher->lock );
+
+        p_item = p_fetcher->pp_waiting[0];
+        REMOVE_ELEM( p_fetcher->pp_waiting, p_fetcher->i_waiting, 0 );
+        vlc_cleanup_run( );
+
+        if( !p_item )
+            continue;
+
+        /* */
+        int canc = vlc_savecancel();
+        {
+            int i_ret;
+
+            /* Check if it is not yet preparsed and if so wait for it
+             * (at most 0.5s)
+             * (This can happen if we fetch art on play)
+             * FIXME this doesn't work if we need to fetch meta before art...
+             */
+            for( i_ret = 0; i_ret < 10 && !input_item_IsPreparsed( p_item ); i_ret++ )
+            {
+                bool b_break;
+                PL_LOCK;
+                b_break = ( !p_sys->p_input || input_GetItem(p_sys->p_input) != p_item  ||
+                            p_sys->p_input->b_die || p_sys->p_input->b_eof || p_sys->p_input->b_error );
+                PL_UNLOCK;
+                if( b_break )
+                    break;
+                msleep( 50000 );
+            }
+
+            i_ret = FindArt( p_fetcher, p_item );
+            if( i_ret == 1 )
+            {
+                PL_DEBUG( "downloading art for %s", p_item->psz_name );
+                if( playlist_DownloadArt( p_playlist, p_item ) )
+                    input_item_SetArtNotFound( p_item, true );
+                else {
+                    input_item_SetArtFetched( p_item, true );
+                    var_SetInteger( p_playlist, "item-change",
+                                    p_item->i_id );
+                }
+            }
+            else if( i_ret == 0 ) /* Was in cache */
+            {
+                PL_DEBUG( "found art for %s in cache", p_item->psz_name );
+                input_item_SetArtFetched( p_item, true );
+                var_SetInteger( p_playlist, "item-change", p_item->i_id );
+            }
+            else
+            {
+                PL_DEBUG( "art not found for %s", p_item->psz_name );
+                input_item_SetArtNotFound( p_item, true );
+            }
+            vlc_gc_decref( p_item );
+        }
+        vlc_restorecancel( canc );
+
+        int i_activity = var_GetInteger( p_playlist, "activity" );
+        if( i_activity < 0 ) i_activity = 0;
+        /* Sleep at least 1ms */
+        msleep( (i_activity+1) * 1000 );
+    }
+    return NULL;
+}
+
+
diff --git a/src/playlist/fetcher.h b/src/playlist/fetcher.h
new file mode 100644 (file)
index 0000000..1c422cc
--- /dev/null
@@ -0,0 +1,47 @@
+/*****************************************************************************
+ * playlist_fetcher.h:
+ *****************************************************************************
+ * Copyright (C) 1999-2008 the VideoLAN team
+ * $Id$
+ *
+ * Authors: Samuel Hocevar <sam@zoy.org>
+ *          Clément Stenac <zorglub@videolan.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifndef _PLAYLIST_FETCHER_H
+#define _PLAYLIST_FETCHER_H 1
+
+typedef struct
+{
+    playlist_t      *p_playlist;
+
+    vlc_thread_t    thread;
+    vlc_mutex_t     lock;
+    vlc_cond_t      wait;
+    int             i_art_policy;
+    int             i_waiting;
+    input_item_t    **pp_waiting;
+
+    DECL_ARRAY(playlist_album_t) albums;
+} playlist_fetcher_t;
+
+playlist_fetcher_t *playlist_fetcher_New( playlist_t * );
+void playlist_fetcher_Push( playlist_fetcher_t *, input_item_t * );
+void playlist_fetcher_Delete( playlist_fetcher_t * );
+
+#endif
+
index f3d05b1..224b1bd 100644 (file)
 #include "input/input_interface.h"
 #include <assert.h>
 
-typedef struct playlist_preparse_t
-{
-    vlc_thread_t    thread;
-    vlc_mutex_t     lock;
-    vlc_cond_t      wait;
-    input_item_t  **pp_waiting;
-    int             i_waiting;
-    bool            up;
-} playlist_preparse_t;
-
-typedef struct playlist_fetcher_t
-{
-    vlc_thread_t    thread;
-    vlc_mutex_t     lock;
-    vlc_cond_t      wait;
-    int             i_art_policy;
-    int             i_waiting;
-    input_item_t    **pp_waiting;
-    bool            up;
-
-    DECL_ARRAY(playlist_album_t) albums;
-} playlist_fetcher_t;
+#include "art.h"
+#include "fetcher.h"
+#include "preparser.h"
 
 typedef struct playlist_private_t
 {
     playlist_t           public_data;
-    playlist_preparse_t  preparse; /**< Preparser data */
-    playlist_fetcher_t   fetcher; /**< Meta and art fetcher data */
-    sout_instance_t      *p_sout; /**< Kept sout instance */
+    playlist_preparser_t *p_preparser;  /**< Preparser data */
+    playlist_fetcher_t   *p_fetcher;    /**< Meta and art fetcher data */
+    sout_instance_t      *p_sout;       /**< Kept sout instance */
 
     playlist_item_array_t items_to_delete; /**< Array of items and nodes to
             delete... At the very end. This sucks. */
diff --git a/src/playlist/preparser.c b/src/playlist/preparser.c
new file mode 100644 (file)
index 0000000..55e20e0
--- /dev/null
@@ -0,0 +1,163 @@
+/*****************************************************************************
+ * preparse.c: Preparser thread.
+ *****************************************************************************
+ * Copyright © 1999-2009 the VideoLAN team
+ * $Id$
+ *
+ * Authors: Samuel Hocevar <sam@zoy.org>
+ *          Clément Stenac <zorglub@videolan.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc_common.h>
+#include <vlc_playlist.h>
+
+#include "art.h"
+#include "fetcher.h"
+#include "preparser.h"
+#include "../input/input_interface.h"
+
+static void *Thread( void * );
+
+playlist_preparser_t *playlist_preparser_New( playlist_t *p_playlist, playlist_fetcher_t *p_fetcher )
+{
+    playlist_preparser_t *p_preparser = malloc( sizeof(*p_preparser) );
+    if( !p_preparser )
+        return NULL;
+
+    p_preparser->p_playlist = p_playlist;
+    p_preparser->p_fetcher = p_fetcher;
+    vlc_mutex_init( &p_preparser->lock );
+    vlc_cond_init( &p_preparser->wait );
+    p_preparser->i_waiting = 0;
+    p_preparser->pp_waiting = NULL;
+
+    if( vlc_clone( &p_preparser->thread, Thread, p_preparser,
+                   VLC_THREAD_PRIORITY_LOW ) )
+    {
+        msg_Err( p_playlist, "cannot spawn preparse thread" );
+        free( p_preparser );
+        return NULL;
+    }
+    return p_preparser;
+}
+
+void playlist_preparser_Push( playlist_preparser_t *p_preparser, input_item_t *p_item )
+{
+    vlc_gc_incref( p_item );
+
+    vlc_mutex_lock( &p_preparser->lock );
+    INSERT_ELEM( p_preparser->pp_waiting, p_preparser->i_waiting,
+                 p_preparser->i_waiting, p_item );
+    vlc_cond_signal( &p_preparser->wait );
+    vlc_mutex_unlock( &p_preparser->lock );
+}
+
+void playlist_preparser_Delete( playlist_preparser_t *p_preparser )
+{
+    /* Destroy the item preparser */
+    vlc_cancel( p_preparser->thread );
+    vlc_join( p_preparser->thread, NULL );
+
+    while( p_preparser->i_waiting > 0 )
+    {   /* Any left-over unparsed item? */
+        vlc_gc_decref( p_preparser->pp_waiting[0] );
+        REMOVE_ELEM( p_preparser->pp_waiting, p_preparser->i_waiting, 0 );
+    }
+    vlc_cond_destroy( &p_preparser->wait );
+    vlc_mutex_destroy( &p_preparser->lock );
+}
+
+static void *Thread( void *data )
+{
+    playlist_preparser_t *p_preparser = data;
+    playlist_t *p_playlist = p_preparser->p_playlist;
+    playlist_fetcher_t *p_fetcher = p_preparser->p_fetcher;
+
+    for( ;; )
+    {
+        input_item_t *p_current;
+
+        /* */
+        vlc_mutex_lock( &p_preparser->lock );
+        mutex_cleanup_push( &p_preparser->lock );
+
+        while( p_preparser->i_waiting == 0 )
+            vlc_cond_wait( &p_preparser->wait, &p_preparser->lock );
+
+        p_current = p_preparser->pp_waiting[0];
+        REMOVE_ELEM( p_preparser->pp_waiting, p_preparser->i_waiting, 0 );
+        vlc_cleanup_run( );
+
+        if( !p_current )
+            continue;
+
+        int canc = vlc_savecancel ();
+        {
+            PL_LOCK;
+            if( p_current->i_type == ITEM_TYPE_FILE )
+            {
+                stats_TimerStart( p_playlist, "Preparse run",
+                                  STATS_TIMER_PREPARSE );
+                /* Do not preparse if it is already done (like by playing it) */
+                if( !input_item_IsPreparsed( p_current ) )
+                {
+                    PL_UNLOCK;
+                    input_Preparse( p_playlist, p_current );
+                    PL_LOCK;
+                }
+                stats_TimerStop( p_playlist, STATS_TIMER_PREPARSE );
+                PL_UNLOCK;
+                input_item_SetPreparsed( p_current, true );
+                var_SetInteger( p_playlist, "item-change", p_current->i_id );
+                PL_LOCK;
+            }
+            /* If we haven't retrieved enough meta, add to secondary queue
+             * which will run the "meta fetchers".
+             * This only checks for meta, not for art
+             * \todo don't do this for things we won't get meta for, like vids
+             */
+            char *psz_arturl = input_item_GetArtURL( p_current );
+            char *psz_name = input_item_GetName( p_current );
+            if( p_fetcher && p_fetcher->i_art_policy == ALBUM_ART_ALL &&
+                ( !psz_arturl || strncmp( psz_arturl, "file://", 7 ) ) )
+            {
+                msg_Dbg( p_playlist, "meta ok for %s, need to fetch art", psz_name );
+                playlist_fetcher_Push( p_fetcher, p_current );
+            }
+            else
+            {
+                msg_Dbg( p_playlist, "no fetch required for %s (art currently %s)",
+                         psz_name, psz_arturl );
+            }
+            vlc_gc_decref( p_current );
+            free( psz_name );
+            free( psz_arturl );
+           PL_UNLOCK;
+        }
+        vlc_restorecancel( canc );
+
+        int i_activity = var_GetInteger( p_playlist, "activity" );
+        if( i_activity < 0 ) i_activity = 0;
+        /* Sleep at least 1ms */
+        msleep( (i_activity+1) * 1000 );
+    }
+    return NULL;
+}
+
diff --git a/src/playlist/preparser.h b/src/playlist/preparser.h
new file mode 100644 (file)
index 0000000..f4ebc75
--- /dev/null
@@ -0,0 +1,45 @@
+/*****************************************************************************
+ * playlist_preparser.h:
+ *****************************************************************************
+ * Copyright (C) 1999-2008 the VideoLAN team
+ * $Id$
+ *
+ * Authors: Samuel Hocevar <sam@zoy.org>
+ *          Clément Stenac <zorglub@videolan.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifndef _PLAYLIST_PREPARSER_H
+#define _PLAYLIST_PREPARSER_H 1
+
+typedef struct
+{
+    playlist_t          *p_playlist;
+    playlist_fetcher_t  *p_fetcher;
+
+    vlc_thread_t    thread;
+    vlc_mutex_t     lock;
+    vlc_cond_t      wait;
+    input_item_t  **pp_waiting;
+    int             i_waiting;
+} playlist_preparser_t;
+
+playlist_preparser_t *playlist_preparser_New( playlist_t *, playlist_fetcher_t * );
+void playlist_preparser_Push( playlist_preparser_t *, input_item_t * );
+void playlist_preparser_Delete( playlist_preparser_t * );
+
+#endif
+
index 5ac497f..4c7e9a1 100644 (file)
@@ -52,42 +52,23 @@ static void* RunControlThread   ( vlc_object_t * );
 void __playlist_ThreadCreate( vlc_object_t *p_parent )
 {
     playlist_t *p_playlist = playlist_Create( p_parent );
-    if( !p_playlist ) return;
+    if( !p_playlist )
+        return;
 
-    // Preparse
-    playlist_preparse_t *p_preparse = &pl_priv(p_playlist)->preparse;
-    vlc_mutex_init (&p_preparse->lock);
-    vlc_cond_init (&p_preparse->wait);
-    p_preparse->i_waiting = 0;
-    p_preparse->pp_waiting = NULL;
+    /* */
+    playlist_private_t *p_sys = pl_priv(p_playlist);
 
-    if( vlc_clone( &p_preparse->thread, playlist_PreparseLoop, p_preparse,
-                   VLC_THREAD_PRIORITY_LOW ) )
-    {
-        msg_Err( p_playlist, "cannot spawn preparse thread" );
-        p_preparse->up = false;
-        return;
-    }
-    p_preparse->up = true;
-
-    // Secondary Preparse
-    playlist_fetcher_t *p_fetcher = &pl_priv(p_playlist)->fetcher;
-    vlc_mutex_init (&p_fetcher->lock);
-    vlc_cond_init (&p_fetcher->wait);
-    p_fetcher->i_waiting = 0;
-    p_fetcher->pp_waiting = NULL;
-    p_fetcher->i_art_policy = var_CreateGetInteger( p_playlist, "album-art" );
-
-    if( vlc_clone( &p_fetcher->thread, playlist_FetcherLoop, p_fetcher,
-                   VLC_THREAD_PRIORITY_LOW ) )
-    {
-        msg_Err( p_playlist, "cannot spawn secondary preparse thread" );
-        p_fetcher->up = false;
-        return;
-    }
-    p_fetcher->up = true;
+    /* Fetcher */
+    p_sys->p_fetcher = playlist_fetcher_New( p_playlist );
+    if( !p_sys->p_fetcher )
+        msg_Err( p_playlist, "cannot create playlist fetcher" );
 
-    // Start the thread
+    /* Preparse */
+    p_sys->p_preparser = playlist_preparser_New( p_playlist, p_sys->p_fetcher );
+    if( !p_sys->p_preparser )
+        msg_Err( p_playlist, "cannot create playlist preparser" );
+
+    /* Start the playlist thread */
     if( vlc_thread_create( p_playlist, "playlist", RunControlThread,
                            VLC_THREAD_PRIORITY_LOW, false ) )
     {
@@ -98,8 +79,6 @@ void __playlist_ThreadCreate( vlc_object_t *p_parent )
 
     /* The object has been initialized, now attach it */
     vlc_object_attach( p_playlist, p_parent );
-
-    return;
 }
 
 /**