[vlc-commits] [Git][videolan/vlc][master] 9 commits: ytdl: sanitise before JSON serialisation

Rémi Denis-Courmont (@Courmisch) gitlab at videolan.org
Tue Nov 11 18:53:03 UTC 2025



Rémi Denis-Courmont pushed to branch master at VideoLAN / VLC


Commits:
9caa432e by Rémi Denis-Courmont at 2025-11-11T20:38:52+02:00
ytdl: sanitise before JSON serialisation

- - - - -
27684ada by Rémi Denis-Courmont at 2025-11-11T20:38:52+02:00
ytdl: don't disable DASH

- - - - -
ffe76312 by Rémi Denis-Courmont at 2025-11-11T20:38:52+02:00
ytdl: add explicit entry point for access submodule

- - - - -
28350729 by Rémi Denis-Courmont at 2025-11-11T20:38:52+02:00
ytdl: wrap playlist item URLs

We want to process playlist item with the access submodule, not with
the HTTP or whatever other access matches the URL of the playlist item.

So far, this was handled by the Python script prepending "ytdl://" but
we can do it in the C side as well.

- - - - -
b818b8c2 by Rémi Denis-Courmont at 2025-11-11T20:38:52+02:00
ytdl: remove the Python JSON encoding/decoding

- - - - -
14cc1bcf by Rémi Denis-Courmont at 2025-11-11T20:38:52+02:00
Revert "demux: add YT-DLP module path argument"

This reverts commit 81cda618161bdb7dab6e66d9d7f751b3777e2efc.

- - - - -
f3625b76 by Rémi Denis-Courmont at 2025-11-11T20:38:52+02:00
ytdl: invoke the yt-dlp script

This runs the upstream yt-dlp script directly instead of our own.

- - - - -
3960585e by Rémi Denis-Courmont at 2025-11-11T20:38:52+02:00
ytdl: remove custom Python

- - - - -
bbcf06da by Rémi Denis-Courmont at 2025-11-11T20:38:52+02:00
ytdl: allow selecting a custom path to yt-dlp

- - - - -


4 changed files:

- modules/demux/Makefile.am
- modules/demux/ytdl.c
- share/Makefile.am
- − share/ytdl-extract.py


Changes:

=====================================
modules/demux/Makefile.am
=====================================
@@ -537,6 +537,7 @@ check_PROGRAMS += adaptive_test
 TESTS += adaptive_test
 
 libytdl_plugin_la_SOURCES = demux/ytdl.c
+libytdl_plugin_la_CPPFLAGS = $(AM_CPPFLAGS) -DEXEEXT=\"$(EXEEXT)\"
 libytdl_plugin_la_LIBADD = libvlc_json.la
 if !HAVE_WIN32
 if !HAVE_ANDROID


=====================================
modules/demux/ytdl.c
=====================================
@@ -22,6 +22,7 @@
 # include "config.h"
 #endif
 
+#include <assert.h>
 #include <errno.h>
 #include <math.h>
 #include <signal.h>
@@ -38,6 +39,7 @@
 #include <vlc_input_item.h>
 #include <vlc_plugin.h>
 #include <vlc_spawn.h>
+#include <vlc_strings.h>
 #include <vlc_interrupt.h>
 
 struct ytdl_json {
@@ -74,7 +76,7 @@ static int ytdl_popen(pid_t *restrict pid, const char *argv[])
         return -1;
 
     int fdv[] = { -1, fds[1], STDERR_FILENO, -1 };
-    int val = vlc_spawn(pid, argv[0], fdv, argv);
+    int val = vlc_spawnp(pid, argv[0], fdv, argv);
 
     vlc_close(fds[1]);
 
@@ -219,18 +221,22 @@ static int ReadItem(stream_t *s, input_item_node_t *node,
     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);
 
-    input_item_t *item = input_item_NewStream(url, 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_AddOption(item, "no-ytdl", 0);
     input_item_node_AppendItem(node, item);
     input_item_Release(item);
 
@@ -339,7 +345,7 @@ static void Close(vlc_object_t *obj)
     json_free(&sys->json);
 }
 
-static int OpenCommon(vlc_object_t *obj)
+static int OpenCommon(vlc_object_t *obj, const char *src_url)
 {
     stream_t *s = (stream_t *)obj;
 
@@ -347,22 +353,15 @@ static int OpenCommon(vlc_object_t *obj)
     if (unlikely(sys == NULL))
         return VLC_EGENERIC;
 
-    char *path = config_GetSysPath(VLC_PKG_DATA_DIR, "ytdl-extract.py");
+    char *path = var_InheritString(obj, "ytdl-path");
     if (unlikely(path == NULL))
-        return VLC_EGENERIC;
+        return VLC_EINVAL;
 
     struct ytdl_json jsdata;
     pid_t pid;
-    char *py_path = var_InheritString(obj, "ytdl-path");
-    const char *argv[5];
-    size_t i = 0;
-    argv[i++] = path;
-    if (py_path != NULL && py_path[0] != '\0') {
-        argv[i++] = "--py-path"; // add additional path
-        argv[i++] = py_path;
-    }
-    argv[i++] = s->psz_url;
-    argv[i] = NULL;
+    const char *argv[] = {
+        path, "--flat-playlist", "--dump-single-json", "--", src_url, NULL
+    };
 
     jsdata.logger = s->obj.logger;
     jsdata.fd = ytdl_popen(&pid, argv);
@@ -370,11 +369,10 @@ static int OpenCommon(vlc_object_t *obj)
     if (jsdata.fd == -1) {
         msg_Dbg(obj, "cannot start %s: %s", path, vlc_strerror_c(errno));
         free(path);
-        free(py_path);
         return VLC_EGENERIC;
     }
+
     free(path);
-    free(py_path);
 
     int val = json_parse(&jsdata, &sys->json);
 
@@ -441,7 +439,20 @@ static int OpenFilter(vlc_object_t *obj)
     if (!var_InheritBool(obj, "ytdl"))
         return VLC_EGENERIC;
 
-    return OpenCommon(obj);
+    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()
@@ -452,9 +463,11 @@ vlc_module_begin()
     set_callbacks(OpenFilter, Close)
     add_bool("ytdl", true, N_("Enable YT-DL"), NULL)
         change_safe()
-    add_directory("ytdl-path", NULL, N_("YT-DL Module Path"), NULL)
+    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(OpenCommon, Close)
+    set_callbacks(OpenAccess, Close)
 vlc_module_end()


=====================================
share/Makefile.am
=====================================
@@ -104,8 +104,6 @@ nobase_dist_pkgdata_SCRIPTS = \
 	utils/audio-vlc-default.sh \
 	utils/video-vlc-default.sh \
 	$(NULL)
-
-dist_pkgdata_SCRIPTS = ytdl-extract.py
 endif
 
 EXTRA_DIST += \


=====================================
share/ytdl-extract.py deleted
=====================================
@@ -1,89 +0,0 @@
-#! /usr/bin/python3
-#
-# Copyright (C) 2020 Rémi Denis-Courmont, Brandon Li
-#
-# 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.
-
-import sys
-import os
-import json
-import urllib.parse
-import argparse
-
-# Parse first so we can set sys.path
-parser = argparse.ArgumentParser(add_help=False)
-parser.add_argument('--py-path', dest='py_path')
-parser.add_argument('url')
-args, _ = parser.parse_known_args()
-
-if args.py_path:
-    sys.path.insert(0, args.py_path)
-
-import yt_dlp
-
-class logger(object):
-    def debug(self, msg):
-        pass
-
-    def warning(self, msg):
-        pass
-
-    def error(self, msg):
-        sys.stderr.write(msg + '\n')
-
-def url_extract(url):
-    opts = {
-        'extract_flat': 'in_playlist',
-        'logger': logger(),
-        'youtube_include_dash_manifest': False,
-    }
-
-    dl = yt_dlp.YoutubeDL(opts)
-
-    # Process a given URL
-    infos = dl.extract_info(url, download=False)
-
-    if 'entries' in infos:
-        for entry in infos['entries']:
-             if 'ie_key' in entry and entry['ie_key']:
-                 # Flat-extracted playlist entry
-                 url = 'ytdl:///?' + urllib.parse.urlencode(entry)
-                 entry['url'] = url;
-
-    print(json.dumps(infos))
-
-def url_process(ie_url):
-    opts = {
-        'logger': logger(),
-        'youtube_include_dash_manifest': False,
-    }
-
-    dl = yt_dlp.YoutubeDL(opts)
-
-    # Rebuild the original IE entry
-    entry = { }
-
-    for p in urllib.parse.parse_qsl(ie_url[9:]):  # <-- use parameter
-        entry[p[0]] = p[1]
-
-    infos = dl.process_ie_result(entry, download=False)
-    print(json.dumps(infos))
-
-url = args.url
-
-if url.startswith('ytdl:///?'):
-    url_process(url)
-else:
-    url_extract(url)



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/27de59e1f8f0ce91632db844a07f63b4b91221ec...bbcf06da37f0c2cfd082b63e5cff44b5d91cc69b

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/27de59e1f8f0ce91632db844a07f63b4b91221ec...bbcf06da37f0c2cfd082b63e5cff44b5d91cc69b
You're receiving this email because of your account on code.videolan.org.


VideoLAN code repository instance


More information about the vlc-commits mailing list