Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/VLC/src/preparser/   (VLC Media Plyer ©)  Datei vom 23.9.2026 mit Größe 14 kB image not shown  

Quelle  fetcher.c   Sprache: C

 

/*****************************************************************************
 * fetcher.c
 *****************************************************************************
 * Copyright © 2017-2017 VLC authors and VideoLAN
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser 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_atomic.h>
#include <vlc_stream.h>
#include <vlc_modules.h>
#include <vlc_interrupt.h>
#include <vlc_arrays.h>
#include <vlc_threads.h>
#include <vlc_memstream.h>
#include <vlc_meta_fetcher.h>
#include <vlc_executor.h>

#include "art.h"
#include "../libvlc.h"
#include "fetcher.h"
#include "input/input_interface.h"
#include "misc/interrupt.h"

struct input_fetcher_t {
    vlc_executor_t *executor_local;
    vlc_executor_t *executor_network;
    vlc_executor_t *executor_downloader;

    vlc_dictionary_t album_cache;
    vlc_object_t* owner;

    vlc_mutex_t lock;
    struct vlc_list submitted_tasks; /**< list of struct task */
};

struct task {
    input_fetcher_t *fetcher;
    vlc_executor_t *executor;
    input_item_t* item;
    int options;
    const input_fetcher_callbacks_t *cbs;
    void *userdata;

    vlc_interrupt_t interrupt;

    struct vlc_runnable runnable; /**< to be passed to the executor */
    struct vlc_list node; /**< node of input_fetcher_t.submitted_tasks */
};

static void RunDownloader(void *);
static void RunSearchLocal(void *);
static void RunSearchNetwork(void *);

static struct task *
TaskNew(input_fetcher_t *fetcher, vlc_executor_t *executor, input_item_t *item,
        int options,
        const input_fetcher_callbacks_t *cbs, void *userdata)
{
    struct task *task = malloc(sizeof(*task));
    if (!task)
        return NULL;

    task->fetcher = fetcher;
    task->executor = executor;
    task->item = item;
    task->options = options;
    task->cbs = cbs;
    task->userdata = userdata;

    vlc_interrupt_init(&task->interrupt);

    input_item_Hold(item);

    if (executor == fetcher->executor_local)
        task->runnable.run = RunSearchLocal;
    else if (executor == fetcher->executor_network)
        task->runnable.run = RunSearchNetwork;
    else
        task->runnable.run = RunDownloader;

    task->runnable.userdata = task;

    return task;
}

static void
TaskDelete(struct task *task)
{
    input_item_Release(task->item);
    vlc_interrupt_deinit(&task->interrupt);
    free(task);
}

static void
FetcherAddTask(input_fetcher_t *fetcher, struct task *task)
{
    vlc_mutex_lock(&fetcher->lock);
    vlc_list_append(&task->node, &fetcher->submitted_tasks);
    vlc_mutex_unlock(&fetcher->lock);
}

static void
FetcherRemoveTask(input_fetcher_t *fetcher, struct task *task)
{
    vlc_mutex_lock(&fetcher->lock);
    vlc_list_remove(&task->node);
    vlc_mutex_unlock(&fetcher->lock);
}

static int
Submit(input_fetcher_t *fetcher, vlc_executor_t *executor, input_item_t *item,
       int options,
       const input_fetcher_callbacks_t *cbs, void *userdata)
{
    struct task *task =
        TaskNew(fetcher, executor, item, options, cbs, userdata);
    if (!task)
        return VLC_ENOMEM;

    FetcherAddTask(fetcher, task);
    vlc_executor_Submit(task->executor, &task->runnable);

    return VLC_SUCCESS;
}

static char* CreateCacheKey( input_item_t* item )
{
    vlc_mutex_lock( &item->lock );

    if( !item->p_meta )
    {
        vlc_mutex_unlock( &item->lock );
        return NULL;
    }

    char const* artist = vlc_meta_Get( item->p_meta, vlc_meta_Artist );
    char const* album = vlc_meta_Get( item->p_meta, vlc_meta_Album );
    char const *date = vlc_meta_Get( item->p_meta, vlc_meta_Date );
    char* key;

    /**
     * Simple concatenation of artist and album can lead to the same key
     * for entities that should not have such. Imagine { dogs, tick } and
     * { dog, stick } */

    if( !artist || !album || asprintf( &key, "%s:%zu:%s:%zu:%s",
          artist, strlen( artist ), album, strlen( album ),
          date ? date : "0000" ) < 0 )
    {
        key = NULL;
    }
    vlc_mutex_unlock( &item->lock );

    return key;
}

static void FreeCacheEntry( void* data, void* obj )
{
    free( data );
    VLC_UNUSED( obj );
}

static int ReadAlbumCache( input_fetcher_t* fetcher, input_item_t* item )
{
    char* key = CreateCacheKey( item );

    if( key == NULL )
        return VLC_EGENERIC;

    vlc_mutex_lock( &fetcher->lock );
    char const* art = vlc_dictionary_value_for_key( &fetcher->album_cache,
                                                    key );
    if( art )
        input_item_SetArtURL( item, art );
    vlc_mutex_unlock( &fetcher->lock );

    free( key );
    return art ? VLC_SUCCESS : VLC_EGENERIC;
}

static void AddAlbumCache( input_fetcher_t* fetcher, input_item_t* item,
                           bool overwrite )
{
    char* art = input_item_GetArtURL( item );
    char* key = CreateCacheKey( item );

    if( key && art && strncasecmp( art, "attachment://", 13 ) )
    {
        vlc_mutex_lock( &fetcher->lock );
        if( overwrite || !vlc_dictionary_has_key( &fetcher->album_cache, key ) )
        {
            vlc_dictionary_insert( &fetcher->album_cache, key, art );
            art = NULL;
        }
        vlc_mutex_unlock( &fetcher->lock );
    }

    free( art );
    free( key );
}

static int InvokeModule( input_fetcher_t* fetcher, input_item_t* item,
                         int scope, char const* type )
{
    meta_fetcher_t* mf = vlc_custom_create( fetcher->owner,
                                            sizeof( *mf ), type );
    if( unlikely( !mf ) )
        return VLC_ENOMEM;

    mf->e_scope = scope;
    mf->p_item = item;

    module_t* mf_module = module_need( mf, type, NULL, false );

    if( mf_module )
        module_unneed( mf, mf_module );

    vlc_object_delete(mf);

    return VLC_SUCCESS;
}

static int CheckMeta( input_item_t* item )
{
    vlc_mutex_lock( &item->lock );
    bool error = !item->p_meta ||
                 !vlc_meta_Get( item->p_meta, vlc_meta_Title ) ||
                 !vlc_meta_Get( item->p_meta, vlc_meta_Artist ) ||
                 !vlc_meta_Get( item->p_meta, vlc_meta_Album );
    vlc_mutex_unlock( &item->lock );
    return error;
}

static int CheckArt( input_item_t* item )
{
    vlc_mutex_lock( &item->lock );
    bool error = !item->p_meta ||
                 !vlc_meta_Get( item->p_meta, vlc_meta_ArtworkURL );
    vlc_mutex_unlock( &item->lock );
    return error;
}

static int SearchArt( input_fetcher_t* fetcher, input_item_t* item, int scope)
{
    InvokeModule( fetcher, item, scope, "art finder" );
    return CheckArt( item );
}

static int SearchByScope(struct task *task, int scope)
{
    input_fetcher_t *fetcher = task->fetcher;
    input_item_t* item = task->item;

    if( CheckMeta( item ) &&
        InvokeModule( fetcher, item, scope, "meta fetcher" ) )
    {
        return VLC_EGENERIC;
    }

    if( ! CheckArt( item )                         ||
        ! ReadAlbumCache( fetcher, item )          ||
        ! input_FindArtInCacheUsingItemUID( item ) ||
        ! input_FindArtInCache( item )             ||
        ! SearchArt( fetcher, item, scope ) )
    {
        AddAlbumCache( fetcher, task->item, false );
        int ret = Submit(fetcher, fetcher->executor_downloader, item,
                         task->options, task->cbs, task->userdata);
        if (ret == VLC_SUCCESS)
            return VLC_SUCCESS;
    }

    return VLC_EGENERIC;
}

static void NotifyArtFetchEnded(struct task *task, bool fetched)
{
    if (task->cbs && task->cbs->on_art_fetch_ended)
        task->cbs->on_art_fetch_ended(task->item, fetched, task->userdata);
}

static void RunDownloader(void *userdata)
{
    vlc_thread_set_name("vlc-run-fetcher");

    bool fetched = false;
    struct task *task = userdata;
    input_fetcher_t *fetcher = task->fetcher;

    vlc_interrupt_set(&task->interrupt);

    ReadAlbumCache( fetcher, task->item );

    char *psz_arturl = input_item_GetArtURL( task->item );
    if( !psz_arturl )
        goto out;

    if( !strncasecmp( psz_arturl, "file://", 7 ) ||
        !strncasecmp( psz_arturl, "attachment://", 13 ) )
    {
        fetched = true;
        goto out; /* no fetch required */
    }

    stream_t* source = vlc_stream_NewURL( fetcher->owner, psz_arturl );

    if( !source )
        goto out;

    struct vlc_memstream output_stream;
    vlc_memstream_open( &output_stream );

    for( ;; )
    {
        char buffer[2048];

        int read = vlc_stream_Read( source, buffer, sizeof( buffer ) );
        if( read <= 0 )
            break;

        if( (int)vlc_memstream_write( &output_stream, buffer, read ) < read )
            break;
    }

    vlc_stream_Delete( source );

    if( vlc_memstream_close( &output_stream ) )
        goto out;

    if( vlc_killed() )
    {
        free( output_stream.ptr );
        goto out;
    }

    input_SaveArt( fetcher->owner, task->item, output_stream.ptr,
                   output_stream.length, NULL );

    free( output_stream.ptr );
    AddAlbumCache( fetcher, task->item, true );
    fetched = true;

out:
    vlc_interrupt_set(NULL);

    if( fetched )
        input_item_SetArtFetched( task->item, true );
    free( psz_arturl );
    NotifyArtFetchEnded(task, fetched);
    FetcherRemoveTask(fetcher, task);
    TaskDelete(task);
}

static void RunSearchLocal(void *userdata)
{
    vlc_thread_set_name("vlc-run-searchl");

    struct task *task = userdata;
    input_fetcher_t *fetcher = task->fetcher;

    vlc_interrupt_set(&task->interrupt);

    if( SearchByScope( task, FETCHER_SCOPE_LOCAL ) == VLC_SUCCESS )
        goto end; /* done */

    if( task->options & VLC_PREPARSER_TYPE_FETCHMETA_NET )
    {
        int ret = Submit(fetcher, fetcher->executor_network, task->item,
                         task->options, task->cbs, task->userdata);
        if (ret != VLC_SUCCESS)
            NotifyArtFetchEnded(task, false);
    }
    else
    {
        input_item_SetArtNotFound( task->item, true );
        NotifyArtFetchEnded(task, false);
    }

end:
    vlc_interrupt_set(NULL);

    FetcherRemoveTask(fetcher, task);
    TaskDelete(task);
}

static void RunSearchNetwork(void *userdata)
{
    vlc_thread_set_name("vlc-run-searchn");

    struct task *task = userdata;

    vlc_interrupt_set(&task->interrupt);

    if( SearchByScope( task, FETCHER_SCOPE_NETWORK ) != VLC_SUCCESS )
    {
        input_item_SetArtNotFound( task->item, true );
        NotifyArtFetchEnded(task, false);
    }

    vlc_interrupt_set(NULL);

    input_fetcher_t *fetcher = task->fetcher;
    FetcherRemoveTask(fetcher, task);
    TaskDelete(task);
}

input_fetcher_t* input_fetcher_New( vlc_object_t* owner,
                                    int request_type )
{
    assert(request_type & VLC_PREPARSER_TYPE_FETCHMETA_ALL);
    input_fetcher_t* fetcher = malloc( sizeof( *fetcher ) );

    if( unlikely( !fetcher ) )
        return NULL;

    int max_threads = var_InheritInteger(owner, "fetch-art-threads");
    if (max_threads < 1)
        max_threads = 1;

    if (request_type & VLC_PREPARSER_TYPE_FETCHMETA_LOCAL)
    {
        fetcher->executor_local = vlc_executor_New(max_threads);
        if (!fetcher->executor_local)
        {
            free(fetcher);
            return NULL;
        }
    }
    else
        fetcher->executor_local = NULL;

    if (request_type & VLC_PREPARSER_TYPE_FETCHMETA_NET)
    {
        fetcher->executor_network = vlc_executor_New(max_threads);
        if (!fetcher->executor_network)
        {
            if (fetcher->executor_local)
                vlc_executor_Delete(fetcher->executor_local);
            free(fetcher);
            return NULL;
        }

    }
    else
        fetcher->executor_network = NULL;

    fetcher->executor_downloader = vlc_executor_New(max_threads);
    if (!fetcher->executor_downloader)
    {
        if (fetcher->executor_network)
            vlc_executor_Delete(fetcher->executor_network);
        if (fetcher->executor_local)
            vlc_executor_Delete(fetcher->executor_local);
        free(fetcher);
        return NULL;
    }

    fetcher->owner = owner;

    vlc_mutex_init(&fetcher->lock);
    vlc_list_init(&fetcher->submitted_tasks);

    vlc_dictionary_init( &fetcher->album_cache, 0 );

    return fetcher;
}

int input_fetcher_Push(input_fetcher_t* fetcher, input_item_t* item,
                       int options,
                       const input_fetcher_callbacks_t *cbs, void *cbs_userdata)
{
    assert(options & VLC_PREPARSER_TYPE_FETCHMETA_ALL);
    if (options & VLC_PREPARSER_TYPE_FETCHMETA_LOCAL)
        assert(fetcher->executor_local != NULL);
    if (options & VLC_PREPARSER_TYPE_FETCHMETA_NET)
        assert(fetcher->executor_network != NULL);

    vlc_executor_t *executor = options & VLC_PREPARSER_TYPE_FETCHMETA_LOCAL
                             ? fetcher->executor_local
                             : fetcher->executor_network;

    return Submit(fetcher, executor, item, options, cbs, cbs_userdata);
}

static void
CancelAllTasks(input_fetcher_t *fetcher)
{
    vlc_mutex_lock(&fetcher->lock);

    struct task *task;
    vlc_list_foreach(task, &fetcher->submitted_tasks, node)
    {
        bool canceled = vlc_executor_Cancel(task->executor, &task->runnable);
        if (canceled)
        {
            NotifyArtFetchEnded(task, false);
            vlc_list_remove(&task->node);
            TaskDelete(task);
        }
        /* Otherwise, the task will be finished and destroyed after run() */
    }

    vlc_mutex_unlock(&fetcher->lock);
}

void input_fetcher_Delete( input_fetcher_t* fetcher )
{
    CancelAllTasks(fetcher);

    if (fetcher->executor_local)
        vlc_executor_Delete(fetcher->executor_local);
    if (fetcher->executor_network)
        vlc_executor_Delete(fetcher->executor_network);
    assert(fetcher->executor_downloader);
    vlc_executor_Delete(fetcher->executor_downloader);

    vlc_dictionary_clear( &fetcher->album_cache, FreeCacheEntry, NULL );
    free( fetcher );
}

Messung
CHZG
849410092

¤ Dauer der Verarbeitung: 0.2 Sekunden  (vorverarbeitet am  2026-11-04) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover


Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.