Skip to content

feat: Support cookies via yt-dlp & integrate logging#17

Open
giantcow wants to merge 4 commits into
ryze312:mainfrom
giantcow:cookies
Open

feat: Support cookies via yt-dlp & integrate logging#17
giantcow wants to merge 4 commits into
ryze312:mainfrom
giantcow:cookies

Conversation

@giantcow
Copy link
Copy Markdown

@giantcow giantcow commented Apr 11, 2026

The original goal of this commit was to add support for exporting
cookies via yt-dlp, but I also added tracing-logging & integrated it
with journald (at least for linux, the windows logging integration is
under a feature-flag and not tested as of this commit).

To support cookies, we look to see if the user has configured a profile
(in yt-dlp syntax) and then execute yt-dlp to export those cookies into
a temporary file (via the tempfile crate). Then, we read from that file
and create an mpv argument including all the headers, filtering it to
only the cookies for the host the user asked for.

Tested with local-network file, youtube, and twitch:

Apr 11 00:27:48 ClockTower ff2mpv-rust[356641]: Loaded config Config { log_level: "trace", player_command: "/usr/local/bin/mpv", player_args: ["--no-terminal", "--script-opts=ytdl_hook-ytdl_path=/home/kayo/.local/bin/yt-dlp", "--ytdl-raw-options-append=cookies-from-browser=firefox::none", "--"], ytdl_path: "/home/kayo/.local/bin/yt-dlp", cookies_from_browser: Some("firefox::none") }
Apr 11 00:27:48 ClockTower ff2mpv-rust[356641]: Parsed message: FF2MpvMessage { url: "http://10.0.0.138:8082/<snip>.mp4#t=15.964853", options: [] }
Apr 11 00:27:48 ClockTower ff2mpv-rust[356641]: Configured 12 cookies for 10.0.0.138
Apr 11 00:27:48 ClockTower ff2mpv-rust[356641]: Launching mpv
Apr 11 00:27:48 ClockTower ff2mpv-rust[356641]: Reply sent to browser

Apr 11 00:27:53 ClockTower ff2mpv-rust[356721]: Loaded config Config { log_level: "trace", player_command: "/usr/local/bin/mpv", player_args: ["--no-terminal", "--script-opts=ytdl_hook-ytdl_path=/home/kayo/.local/bin/yt-dlp", "--ytdl-raw-options-append=cookies-from-browser=firefox::none", "--"], ytdl_path: "/home/kayo/.local/bin/yt-dlp", cookies_from_browser: Some("firefox::none") }
Apr 11 00:27:53 ClockTower ff2mpv-rust[356721]: Parsed message: FF2MpvMessage { url: "https://www.youtube.com/watch?v=kt1HSEcVNp0", options: [] }
Apr 11 00:27:55 ClockTower ff2mpv-rust[356721]: Configured 2 cookies for www.youtube.com
Apr 11 00:27:55 ClockTower ff2mpv-rust[356721]: Launching mpv
Apr 11 00:27:55 ClockTower ff2mpv-rust[356721]: Reply sent to browser

Apr 11 00:28:22 ClockTower ff2mpv-rust[357046]: Loaded config Config { log_level: "trace", player_command: "/usr/local/bin/mpv", player_args: ["--no-terminal", "--script-opts=ytdl_hook-ytdl_path=/home/kayo/.local/bin/yt-dlp", "--ytdl-raw-options-append=cookies-from-browser=firefox::none", "--"], ytdl_path: "/home/kayo/.local/bin/yt-dlp", cookies_from_browser: Some("firefox::none") }
Apr 11 00:28:22 ClockTower ff2mpv-rust[357046]: Parsed message: FF2MpvMessage { url: "https://www.twitch.tv/alveussanctuary", options: [] }
Apr 11 00:28:23 ClockTower ff2mpv-rust[357046]: Configured 1 cookies for www.twitch.tv
Apr 11 00:28:23 ClockTower ff2mpv-rust[357046]: Launching mpv
Apr 11 00:28:23 ClockTower ff2mpv-rust[357046]: Reply sent to browser

My ff2mpv-rust config:

# ~/.config/ff2mpv-rust.json
{
  "log_level": "trace",
  "player_command": "/usr/local/bin/mpv",
  "cookies_from_browser": "firefox::none",
  "ytdl_path": "/home/kayo/.local/bin/yt-dlp",
  "player_args": ["--no-terminal", "--"]
}

Firefox native hosts config:

# ~/.mozilla/native-messaging-hosts/ff2mpv.json
{
  "name": "ff2mpv",
  "description": "ff2mpv's external manifest",
  "path": "/home/kayo/workspace/ff2mpv-rust/target/release/ff2mpv-rust",
  "type": "stdio",
  "allowed_extensions": [
    "ff2mpv@yossarian.net"
  ]
}

The added dependencies to increase the binary size, but it seems appropriate to me

# kayo @ ClockTower in ~/workspace/ff2mpv-rust on git:cookies x [11:28:55] 
$ cargo bloated
   Compiling ff2mpv-rust v1.1.7 (/home/kayo/workspace/ff2mpv-rust)
    Finished `release` profile [optimized] target(s) in 1.22s
   Analyzing binary "ff2mpv-rust": /home/kayo/workspace/ff2mpv-rust/target/release/ff2mpv-rust
   File   Size Section
100.0%  287KiB (file)
142.2%  408KiB (unstripped)
 58.4%  168KiB .text
 10.0% 28.6KiB .rodata
  8.5% 24.3KiB .eh_frame
  6.6% 18.9KiB .rela.dyn

  File  .text    Size  Crate
 65.9% 112.8%  189KiB  *
 13.7%  23.4% 39.2KiB  tracing_subscriber
 12.2%  20.9% 35.1KiB  ff2mpv_rust
 10.4%  17.8% 29.9KiB  serde_json
  4.8%   8.1% 13.6KiB  tracing_journald
  4.5%   7.8% 13.1KiB  ryu
  4.5%   7.7% 12.9KiB  tracing_core
  3.5%   6.0% 10.1KiB  std
  2.0%   3.4% 5.66KiB  memchr

  File    Size  .text  .rodata   (SHR) .data.rel.ro   (SHR) Crates                           Name
  4.4% 12.7KiB 2.26KiB 10.4KiB  200  B         0  B    0  B ryu                              ryu::pretty::format64
  4.1% 11.6KiB 9.68KiB  823  B 4.84KiB      1184  B    0  B ff2mpv_rust                      <ff2mpv_rust::command::Command>::execute
  3.4% 9.87KiB 9.70KiB  133  B 5.43KiB        32  B    0  B ff2mpv_rust                      <ff2mpv_rust::config::Config>::parse_config_file
  2.1% 5.89KiB 5.78KiB   50  B 4.85KiB        56  B    0  B ff2mpv_rust                      ff2mpv_rust::browser::get_mpv_message
  1.8% 5.04KiB 4.89KiB   29  B 5.39KiB       120  B  128  B tracing_subscriber,alloc,core,std,tracing_core <std::thread::local::LocalKey<core::cell::RefCell<alloc::string::String>>>::with::<<tracing_subscriber::fmt::fmt_layer::Layer<tracing_subscriber::layer::layered::Layered<tracing_core::metadata::LevelFilter, tracing_subscriber::registry::sharded::Registry>, tracing_subscriber::fmt::format::DefaultFields, tracing_subscriber::fmt::format::Format, std::fs::File> as tracing_subscriber::layer::Layer<tracing_subscriber::layer::layered::Layered<tracing_core::metadata::LevelFilter, tracing_subscriber::registry::sharded::Registry>>>::on_event::{closure#0}, ()>
  1.5% 4.25KiB 3.21KiB  185  B 5.44KiB       880  B   48  B ff2mpv_rust                      ff2mpv_rust::main
  1.2% 3.48KiB 3.25KiB   60  B 5.40KiB       176  B   56  B tracing_journald,tracing_core,tracing_subscriber <tracing_subscriber::layer::layered::Layered<tracing_journald::Layer, tracing_subscriber::layer::layered::Layered<tracing_core::metadata::LevelFilter, tracing_subscriber::registry::sharded::Registry>> as tracing_core::subscriber::Subscriber>::event
  1.0% 2.98KiB 2.72KiB  176  B 5.32KiB        88  B    0  B ff2mpv_rust                      <ff2mpv_rust::command::Command>::show_manifest

Note that I had to apply the following diff to use bloated

# kayo @ ClockTower in ~/workspace/ff2mpv-rust on git:cookies x [1:07:11] 
$ gd --patch -U1
diff --git a/Cargo.toml b/Cargo.toml
index df093c0..2a55a37 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -24,3 +24,3 @@ strip = "symbols"
 lto = "fat"
-panic = "abort"
+# panic = "abort"
 codegen-units = 1

The original goal of this commit was to add support for exporting
    cookies via yt-dlp, but I also added tracing-logging & integrated it
    with journald (at least for linux, the windows logging integration is
    under a feature-flag and not tested as of this commit).

    To support cookies, we look to see if the user has configured a profile
    (in yt-dlp syntax) and then execute yt-dlp to export those cookies into
    a temporary file (via the tempfile crate). Then, we read from that file
    and create an mpv argument including all the headers, filtering it to
    only the cookies for the host the user asked for.
@ryze312
Copy link
Copy Markdown
Owner

ryze312 commented Apr 11, 2026

Hey, thanks for contributing, this is great!

Not entirely sure what's the purpose of setting cookies for mpv itself (instead of yt-dlp using ytdl-raw-options). iirc googlevideo.com doesn't have the checks for bot detection and age restriction as frontend player does, so it shouldn't be neccessary to provide cookies for mpv.
It's still great to keep it consistent, of course, just asking for motivation behind this.

Also perhaps having ytdl-raw-options and ytdl_hook-ytdl_path for mpv filled in automatically from cookies_from_browser and ytdl_path config options respectively would be nice.

allows the windows compilation to build
@giantcow
Copy link
Copy Markdown
Author

giantcow commented Apr 11, 2026

You betchya! 🫡

This helps support streaming content that has access control, ie. Youtube members only content or when a login is required. The built in lua-hook for yt-dlp is hard-coded to -J so MPV only ever gets metadata about the content.

https://github.com/mpv-player/mpv/blob/062f4bf04798e5cf697348e3a95e1960bc9e7e9f/player/lua/ytdl_hook.lua#L894-L897

The addition of the player_args like below only works for that yt-dlp command itself, once the URL is passed back to mpv (thus FFmpeg), it's a "clean" URL that has non of the required auth cookies

...
  "player_args": [
    "--no-terminal",
    "--script-opts=ytdl_hook-ytdl_path=/home/kayo/.local/bin/yt-dlp",
    "--ytdl-raw-options-append=cookies-from-browser=firefox::none",
    "--"
  ]
...

Also perhaps having ytdl-raw-options and ytdl_hook-ytdl_path for mpv filled in automatically from cookies_from_browser and ytdl_path config options respectively would be nice.

For sure!

@giantcow
Copy link
Copy Markdown
Author

I had a shower thought and I think your question is valid- we should just be able to pass the ytdlp options in and it "just works" without having to inject the headers to the mpv command. I'll do more testing

@ryze312
Copy link
Copy Markdown
Owner

ryze312 commented May 4, 2026

@giantcow I took a look at the source code for yt-dlp mpv hook and it does set HTTP headers according to http_headers field from yt-dlp. It doesn't set anything special for YouTube links, even for private and age-restricted videos.
https://github.com/mpv-player/mpv/blob/333857403f01d760d6cf6b80654cf7a734f0ece5/player/lua/ytdl_hook.lua#L139

So I don't think we should be doing anything on our side, in fact, as you can see in the source code, it can prevent yt-dlp hook from adding headers by itself. If YouTube at some point starts requiring additional headers (please don't), it should be easy for yt-dlp to add these.

@ryze312
Copy link
Copy Markdown
Owner

ryze312 commented May 4, 2026

I'll still be happy to merge the logging and yt-dlp options parts of this PR, as these are definitely nice :3

@giantcow
Copy link
Copy Markdown
Author

giantcow commented May 5, 2026

I was able to verify that yt-dlp-supported websites worked without the extra cookie injections, however it doesn't work with unsupported websites (like my media server at home). I've separated the extra cookie injections into its own flag to differentiate that

@ryze312
Copy link
Copy Markdown
Owner

ryze312 commented May 17, 2026

What's your media server running? Perhaps the extractor for it in yt-dlp could be modified to support authentication, unless you have a plain HTTP file server?

@giantcow
Copy link
Copy Markdown
Author

@ryze312
Copy link
Copy Markdown
Owner

ryze312 commented May 18, 2026

Hmm, I don't see an extractor for this in yt-dlp codebase, do you just right click the video and open it in ff2mpv with direct link?

@giantcow
Copy link
Copy Markdown
Author

I have to right click on the video element and open in ff2mpv, so the input into this native host is the link to an mp4

@ryze312
Copy link
Copy Markdown
Owner

ryze312 commented May 18, 2026

I did some testing with mpv and found out a few things.

yt-dlp is only used on Twitch and YouTube URLs by default, you can change that using the script option with regex pattern. Also, Lua hook returns early if it detects a direct link, which prevents cookies from being set.

I think a better solution would be to make a PR to mpv to expose an option for processing direct links with yt-dlp, this will make mpv work with cookies both from ff2mpv and directly from command line.
Patch may look something like this:

diff --git a/player/lua/ytdl_hook.lua b/player/lua/ytdl_hook.lua
index c8d3d71..ddaf9f9 100644
--- a/player/lua/ytdl_hook.lua
+++ b/player/lua/ytdl_hook.lua
@@ -9,6 +9,7 @@ local o = {
     use_manifests = false,
     all_formats = true,
     force_all_formats = true,
+    force_direct_url = false,
     thumbnails = "none",
     ytdl_path = "",
 }
@@ -1008,7 +1009,7 @@ local function run_ytdl_hook(url)
     json["proxy"] = json["proxy"] or proxy
 
     -- what did we get?
-    if json["direct"] then
+    if json["direct"] and not o.force_direct_url then
         -- direct URL, nothing to do
         msg.verbose("Got direct URL")
         return

Then in your mpv config you could simply do

# .config/mpv/script-opts/ytdl_hook.conf
# Omitted the defaults for clarity
include=^10.0.0.138:8082/
force_direct_url=yes

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants