Anti-bot: configurable YouTube player clients (global + per-channel)
YouTube's bot-detection cracks down on different player clients over time — the `web` client gets captcha-walled, then later `tv`/`mweb` are the safe ones, etc. Let the user route around whichever client is currently being targeted instead of being stuck with yt-dlp's pick. - New backup.youtube_player_clients config (comma-separated, blank = yt-dlp default). Maps to --extractor-args youtube:player_client=… - Per-channel DownloadOptions.youtube_player_clients override (None = defer to global). Resolved in the downloader's apply_player_client, which omits the flag entirely when empty so yt-dlp keeps its default client set (the recommended baseline). - Threaded into the live downloader at construction + settings save (app + web). UI: a "YouTube player clients" field in both global Settings (desktop + web) and the per-channel options dialog, with hints pointing at "tv,mweb" as the current least-bot-checked combo for captcha-prone channels. Verified the settings round-trip (global persists to config + echoes; per-channel override saves + reads back). 87 tests pass. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
fe229bc98d
commit
2f711604f0
6 changed files with 95 additions and 1 deletions
32
src/app.rs
32
src/app.rs
|
|
@ -232,6 +232,7 @@ struct ChannelOptionsForm {
|
|||
subs_auto_idx: usize,
|
||||
subs_embed_idx: usize,
|
||||
subtitle_format: String, // "" = global default
|
||||
youtube_player_clients: String, // "" = global default
|
||||
extra_args: String, // one per line
|
||||
}
|
||||
|
||||
|
|
@ -314,6 +315,7 @@ impl App {
|
|||
use_bundled_ytdlp, use_pot_provider,
|
||||
);
|
||||
downloader.subtitle_defaults = config.subtitles.clone();
|
||||
downloader.youtube_player_clients = config.backup.youtube_player_clients.clone();
|
||||
let config_bind = config.web.bind.clone();
|
||||
let password_set = db.get_setting("password_hash").ok().flatten().is_some();
|
||||
let plex_path_str = config.plex.library_path
|
||||
|
|
@ -474,6 +476,7 @@ impl App {
|
|||
subs_auto_idx: tri_to_idx(opts.subtitles_auto),
|
||||
subs_embed_idx: tri_to_idx(opts.subtitles_embed),
|
||||
subtitle_format: opts.subtitle_format.clone().unwrap_or_default(),
|
||||
youtube_player_clients: opts.youtube_player_clients.clone().unwrap_or_default(),
|
||||
extra_args: opts.extra_args.join("\n"),
|
||||
}
|
||||
}
|
||||
|
|
@ -510,6 +513,7 @@ impl App {
|
|||
subtitles_auto: idx_to_tri(f.subs_auto_idx),
|
||||
subtitles_embed: idx_to_tri(f.subs_embed_idx),
|
||||
subtitle_format: trim_opt(&f.subtitle_format),
|
||||
youtube_player_clients: trim_opt(&f.youtube_player_clients),
|
||||
extra_args: f.extra_args.lines()
|
||||
.map(|s| s.trim().to_string()).filter(|s| !s.is_empty()).collect(),
|
||||
}
|
||||
|
|
@ -2006,6 +2010,20 @@ impl App {
|
|||
).on_hover_text("Blank = use the global setting. e.g. srt for Plex.");
|
||||
});
|
||||
|
||||
ui.add_space(6.0);
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("YouTube player clients");
|
||||
ui.add(
|
||||
egui::TextEdit::singleline(&mut form.youtube_player_clients)
|
||||
.desired_width(160.0)
|
||||
.hint_text("global (e.g. tv,mweb)"),
|
||||
).on_hover_text(
|
||||
"Per-channel --extractor-args youtube:player_client override. \
|
||||
Blank = use the global setting. If this channel keeps hitting \
|
||||
captchas, try 'tv,mweb' — those clients are currently the least \
|
||||
bot-checked.");
|
||||
});
|
||||
|
||||
ui.add_space(6.0);
|
||||
ui.label("Extra yt-dlp args (one per line):");
|
||||
ui.add(
|
||||
|
|
@ -2457,6 +2475,19 @@ impl App {
|
|||
});
|
||||
ui.end_row();
|
||||
|
||||
ui.label("YouTube player clients:");
|
||||
ui.add(
|
||||
egui::TextEdit::singleline(&mut self.config.backup.youtube_player_clients)
|
||||
.desired_width(180.0)
|
||||
.hint_text("default (e.g. tv,mweb)"),
|
||||
).on_hover_text(
|
||||
"Comma-separated --extractor-args youtube:player_client list. \
|
||||
Blank = let yt-dlp pick (recommended). YouTube's bot-detection \
|
||||
targets different clients over time; if you keep hitting \
|
||||
captchas, 'tv,mweb' are currently the least-checked. \
|
||||
Per-channel overrides live in each channel's options.");
|
||||
ui.end_row();
|
||||
|
||||
ui.label("Web UI port:");
|
||||
ui.add(
|
||||
egui::DragValue::new(&mut self.config.web.port)
|
||||
|
|
@ -2801,6 +2832,7 @@ impl App {
|
|||
self.downloader.use_bundled_ytdlp = self.config.backup.use_bundled_ytdlp;
|
||||
self.downloader.use_pot_provider = self.config.backup.use_pot_provider;
|
||||
self.downloader.subtitle_defaults = self.config.subtitles.clone();
|
||||
self.downloader.youtube_player_clients = self.config.backup.youtube_player_clients.clone();
|
||||
if dir_changed {
|
||||
self.channels_root = new_dir.clone();
|
||||
self.library_root = new_dir
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue