Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  ytdl.c   Sprache: C

 

/*****************************************************************************
 * ytdl.c:
 *****************************************************************************
 * Copyright (C) 2019-2020 Rémi Denis-Courmont
 *
 * 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 <assert.h>
#include <errno.h>
#include <math.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>         /* STDERR_FILENO */
#ifdef _WIN32
# include <processthreadsapi.h>
#endif

#include "json/json.h"
#include <vlc_common.h>
#include <vlc_configuration.h>
#include <vlc_demux.h>
#include <vlc_stream.h>
#include <vlc_fs.h>
#include <vlc_input_item.h>
#include <vlc_plugin.h>
#include <vlc_spawn.h>
#include <vlc_strings.h>
#include <vlc_interrupt.h>

struct ytdl_json {
    struct vlc_logger *logger;
    int fd;
};

void json_parse_error(void *data, const char *msg)
{
    struct ytdl_json *sys = data;

    vlc_error(sys->logger, "%s", msg);
}

size_t json_read(void *data, void *buf, size_t size)
{
    struct ytdl_json *sys = data;

    while (!vlc_killed()) {
        ssize_t val = vlc_read_i11e(sys->fd, buf, size);

        if (val >= 0)
            return val;
    }

    return 0;
}

static int ytdl_popen(pid_t *restrict pid, const char *argv[])
{
    int fds[2];

    if (vlc_pipe(fds))
        return -1;

    int fdv[] = { -1, fds[1], STDERR_FILENO, -1 };
    int val = vlc_spawnp(pid, argv[0], fdv, argv);

    vlc_close(fds[1]);

    if (val) {
        vlc_close(fds[0]);
        errno = val;
        return -1;
    }

    return fds[0];
}

#ifdef _WIN32
static int vlc_kill(pid_t pid, int signum)
{
    HANDLE h = OpenProcess(PROCESS_TERMINATE, FALSE, pid);
    BOOL res;

    if (h == NULL) {
        errno = ESRCH;
        return -1;
    }

    if (signum > 0)
        res = TerminateProcess(h, signum);
    else
        res = TRUE;

    CloseHandle(h);

    if (!res) {
        errno = EINVAL;
        return -1;
    }

    return 0;
}
#define kill(p, s) vlc_kill(p, s)
#endif

struct ytdl_playlist {
    struct json_object json;
    stream_t *source;
};

static int CompareFormats(const struct json_object *f_a,
                          const struct json_object *f_b, double pref_height)
{
    double h_a = json_get_num(f_a, "height");
    double abr_a = json_get_num(f_a, "abr");
    double h_b = json_get_num(f_b, "height");
    double abr_b = json_get_num(f_b, "abr");

    /* Prefer non-mute formats */
    if (!isnan(abr_a) != !isnan(abr_b))
        return isnan(abr_a) ? -1 : +1;

    /* Prefer non-blind formats */
    if (!isnan(h_a) != !isnan(h_b))
        return isnan(h_a) ? -1 : +1;

    if (islessequal(h_a, pref_height)) {
        if (!islessequal(h_b, pref_height))
            return +1;
        if (h_a > h_b)
            return -1;
        if (h_a < h_b)
            return +1;
    } else {
        if (islessequal(h_b, pref_height))
            return -1;
        if (isless(h_a, h_b))
            return -1;
        if (isgreater(h_a, h_b))
            return +1;
    }

    if (isgreater(abr_a, abr_b))
        return +1;
    if (isgreater(abr_b, abr_a))
        return -1;

    return 0;
}

static const struct json_object *PickFormat(stream_t *s,
                                            const struct json_object *entry)
{
     const struct json_value *fmts = json_get(entry, "formats");

     if (fmts == NULL)
         return entry; /* only one format */
     if (fmts->type != JSON_ARRAY)
         return NULL;

     const struct json_object *best_fmt = NULL;
     double pref_height = var_InheritInteger(s, "preferred-resolution");

     if (isless(pref_height, 0.))
         pref_height = NAN;

     for (size_t i = 0; i < fmts->array.size; i++) {
         const struct json_value *v = &fmts->array.entries[i];

         if (v->type != JSON_OBJECT)
             continue;

         const struct json_object *fmt = &v->object;

         if (best_fmt == NULL) {
             best_fmt = fmt;
             continue;
         }

         if (CompareFormats(fmt, best_fmt, pref_height) > 0)
              best_fmt = fmt;
     }

     return best_fmt;
}

static const char *PickArt(const struct json_object *entry)
{
    const struct json_value *v = json_get(entry, "thumbnails");

    if (v == NULL || v->type != JSON_ARRAY || v->array.size == 0)
        return NULL;

    v = &v->array.entries[0];

    if (v->type != JSON_OBJECT)
        return NULL;

    return json_get_str(&v->object, "url");
}

static void GetMeta(vlc_meta_t *meta, const struct json_object *json)
{
    const char *title = json_get_str(json, "title");
    if (title != NULL)
        vlc_meta_Set(meta, vlc_meta_Title, title);

    const char *desc = json_get_str(json, "description");
    if (desc != NULL)
        vlc_meta_Set(meta, vlc_meta_Description, desc);

    const char *author = json_get_str(json, "uploader");
    if (author != NULL)
        vlc_meta_Set(meta, vlc_meta_Artist, author);

    const char *arturl = PickArt(json);
    if (arturl != NULL)
        vlc_meta_Set(meta, vlc_meta_ArtworkURL, arturl);
}

static int ReadItem(stream_t *s, input_item_node_t *node,
                    const struct json_object *json)
{
    const struct json_object *fmt = PickFormat(s, json);

    if (fmt == NULL)
        return VLC_EGENERIC;

    const char *url = json_get_str(fmt, "url");

    if (url == NULL)
        return VLC_EGENERIC;

    const char *title = json_get_str(json, "title");
    double duration = json_get_num(json, "duration");
    vlc_tick_t ticks = isnan(duration) ? INPUT_DURATION_UNSET
                                       : lround(duration * CLOCK_FREQ);
    char *wrapurl;

    if (title == NULL)
        title = url;
    if (unlikely(asprintf(&wrapurl, "ytdl://%s", url) == -1))
        return VLC_ENOMEM;

    input_item_t *item = input_item_NewStream(wrapurl, title, ticks);

    free(wrapurl);

    if (unlikely(item == NULL))
        return VLC_ENOMEM;

    /* Don't care to lock, the item is still private. */
    GetMeta(item->p_meta, json);
    input_item_node_AppendItem(node, item);
    input_item_Release(item);

    return VLC_SUCCESS;
}

static int ReadDir(stream_t *s, input_item_node_t *node)
{
    struct ytdl_playlist *sys = s->p_sys;
    const struct json_value *v = json_get(&sys->json, "entries");

    if (v == NULL) /* Single item */
        return ReadItem(s, node, &sys->json);

    /* Playlist: parse each entry */
    if (v->type != JSON_ARRAY)
        return VLC_EGENERIC;

    for (size_t i = 0; i < v->array.size; i++) {
         const struct json_value *e = &v->array.entries[i];

         if (e->type == JSON_OBJECT)
             ReadItem(s, node, &e->object);
    }

    return VLC_SUCCESS;
}

static int Control(stream_t *s, int query, va_list args)
{
    switch (query)
    {
        case STREAM_CAN_SEEK:
        case STREAM_CAN_FASTSEEK:
        case STREAM_CAN_PAUSE:
        case STREAM_CAN_CONTROL_PACE:
            *va_arg(args, bool *) = false;
            break;

        case STREAM_GET_TYPE:
            *va_arg(args, int *) = ITEM_TYPE_PLAYLIST;
            break;

        case STREAM_GET_PTS_DELAY:
            *va_arg(args, vlc_tick_t *) =
                 VLC_TICK_FROM_MS(var_InheritInteger(s, "network-caching"));
            break;

        default:
            return VLC_EGENERIC;

    }

    return VLC_SUCCESS;
}

static int DemuxNested(stream_t *s)
{
    struct ytdl_playlist *sys = s->p_sys;

    return demux_Demux(sys->source);
}

static int ControlNested(stream_t *s, int query, va_list args)
{
    struct ytdl_playlist *sys = s->p_sys;

    switch (query) {
        case DEMUX_GET_META: {
            vlc_meta_t *meta = va_arg(args, vlc_meta_t *);

            GetMeta(meta, &sys->json);
            return demux_Control(sys->source, query, meta);
        }

        default:
            return demux_vaControl(sys->source, query, args);
    }
}

static stream_t *vlc_demux_NewURL(vlc_object_t *obj, const char *url,
                                  es_out_t *out)
{
    stream_t *stream = vlc_stream_NewURL(obj, url);

    if (stream != NULL) {
        demux_t *demux = demux_New(obj, "any", url, stream, out);

        if (demux != NULL)
            return demux;

        vlc_stream_Delete(stream);
    }

    return NULL;
}

static void Close(vlc_object_t *obj)
{
    stream_t *s = (stream_t *)obj;
    struct ytdl_playlist *sys = s->p_sys;

    if (sys->source != NULL)
        vlc_stream_Delete(sys->source);

    json_free(&sys->json);
}

static int OpenCommon(vlc_object_t *obj, const char *src_url)
{
    stream_t *s = (stream_t *)obj;

    struct ytdl_playlist *sys = vlc_obj_malloc(obj, sizeof (*sys));
    if (unlikely(sys == NULL))
        return VLC_EGENERIC;

    char *path = var_InheritString(obj, "ytdl-path");
    if (unlikely(path == NULL))
        return VLC_EINVAL;

    struct ytdl_json jsdata;
    pid_t pid;
    const char *argv[] = {
        path, "--flat-playlist", "--dump-single-json", "--", src_url, NULL
    };

    jsdata.logger = s->obj.logger;
    jsdata.fd = ytdl_popen(&pid, argv);

    if (jsdata.fd == -1) {
        msg_Dbg(obj, "cannot start %s: %s", path, vlc_strerror_c(errno));
        free(path);
        return VLC_EGENERIC;
    }

    free(path);

    int val = json_parse(&jsdata, &sys->json);

    kill(pid, SIGTERM);
    vlc_close(jsdata.fd);
    vlc_waitpid(pid);

    if (val) {
        /* Location not handled */
        msg_Dbg(s, "cannot extract infos");
        return VLC_EGENERIC;
    }

    s->p_sys = sys;
    sys->source = NULL;

    if (json_get(&sys->json, "entries") != NULL) {
        /* Playlist */
        s->pf_readdir = ReadDir;
        s->pf_control = Control;
        return VLC_SUCCESS;
    }

    /* Redirect if there is a single URL, so that we can refresh it every
     * time it is opened.
     */

    const struct json_object *fmt = PickFormat(s, &sys->json);
    stream_t *demux = NULL;

    if (fmt != NULL) {
        const char *url = json_get_str(fmt, "url");

        if (url != NULL) {
            var_Create(obj, "ytdl", VLC_VAR_BOOL);
            demux = vlc_demux_NewURL(obj, url, s->out);

            if (demux == NULL)
                msg_Err(s, "cannot open URL: %s", url);
            else
                msg_Dbg(s, "redirecting to: %s", url);
         }
    }

    if (demux == NULL) {
        json_free(&sys->json);
        return VLC_EGENERIC;
    }

    s->pf_demux = DemuxNested;
    s->pf_control = ControlNested;
    sys->source = demux;
    return VLC_SUCCESS;
}

static int OpenFilter(vlc_object_t *obj)
{
    stream_t *s = (stream_t *)obj;

    if (s->psz_url == NULL)
        return VLC_EGENERIC;
    if (strncasecmp(s->psz_url, "http:", 5)
     && strncasecmp(s->psz_url, "https:", 6))
        return VLC_EGENERIC;
    if (!var_InheritBool(obj, "ytdl"))
        return VLC_EGENERIC;

    return OpenCommon(obj, s->psz_url);
}

static int OpenAccess(vlc_object_t *obj)
{
    stream_t *s = (stream_t *)obj;
    const char *url = s->psz_url;

    assert(url != NULL);

    if (vlc_ascii_strncasecmp(url, "ytdl://", 7) == 0)
        url += 7;

    return OpenCommon(obj, url);
}

vlc_module_begin()
    set_shortname("YT-DL")
    set_description("YT-DL extractor")
    set_subcategory(SUBCAT_INPUT_STREAM_FILTER)
    set_capability("demux", 7)
    set_callbacks(OpenFilter, Close)
    add_bool("ytdl", true, N_("Enable YT-DL"), NULL)
        change_safe()
    add_loadfile("ytdl-path", "yt-dlp" EXEEXT,
                 N_("Path to YT-DLP"), N_("Path to YT-DLP"))

    add_submodule()
    set_capability("access", 0)
    add_shortcut("ytdl")
    set_callbacks(OpenAccess, Close)
vlc_module_end()

Messung
CHZG
82999190

¤ 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.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=1104856
#Domains=417336