feat: cookies-from-browser per bypass HTTP 403 YouTube (v1.10.1)

Alcuni video YouTube restituiscono 403 anche con yt-dlp aggiornato.
Fix: nuova opzione Impostazioni "Cookies da browser" con dropdown
(chrome/safari/firefox/edge/brave). Priorita': file cookies.txt
esistente -> --cookies-from-browser <name> -> nessuno.

Bonus:
- core/paths.py::_bundle_dirs() ora include <project>/bundle_bin/
  anche in dev, cosi' python main.py usa lo stesso yt-dlp del build
  invece di Homebrew (spesso obsoleto)
- Helper _cookie_args() in downloader.py per non ripetere la logica
  su tutti i punti dove serve --cookies
This commit is contained in:
luciano committed 2026-08-19 15:30:46 +02:00
1 parent e7d11d0235
commit 39a117f08b
6 files changed
+60 -15

No files matched your search

+1
View File
@@ -170,6 +170,7 @@ class Api:
"bitrate": payload.get("bitrate", "320K"), "bitrate": payload.get("bitrate", "320K"),
"hq_threshold": threshold, "hq_threshold": threshold,
"cookies_path": (payload.get("cookies_path") or "").strip(), "cookies_path": (payload.get("cookies_path") or "").strip(),
"cookies_browser": (payload.get("cookies_browser") or "").strip().lower(),
"output_dir": (payload.get("output_dir") or "").strip(), "output_dir": (payload.get("output_dir") or "").strip(),
"theme": payload.get("theme", "dark"), "theme": payload.get("theme", "dark"),
}) })
+2 -1
View File
@@ -5,7 +5,7 @@ import os
import sys import sys
from pathlib import Path from pathlib import Path
VERSION = "v1.10.0" VERSION = "v1.10.1"
APP_NAME = "MusicTools" APP_NAME = "MusicTools"
@@ -68,6 +68,7 @@ DEFAULTS = {
"bitrate": "320K", "bitrate": "320K",
"hq_threshold": 310, "hq_threshold": 310,
"cookies_path": str(_project_dir / "cookies.txt"), "cookies_path": str(_project_dir / "cookies.txt"),
"cookies_browser": "", # "" | chrome | safari | firefox | edge | brave
"output_dir": str(_project_dir / "MUSICA"), "output_dir": str(_project_dir / "MUSICA"),
"theme": "dark", "theme": "dark",
# ---- Beatport ---- # ---- Beatport ----
+30 -14
View File
@@ -12,6 +12,29 @@ from typing import Callable, Optional
from core.paths import find_ytdlp, find_ffmpeg_dir, subprocess_flags from core.paths import find_ytdlp, find_ffmpeg_dir, subprocess_flags
_ALLOWED_BROWSERS = {"chrome", "safari", "firefox", "edge", "brave", "chromium", "opera", "vivaldi"}
def _cookie_args(cookies_path: Optional[str]) -> list:
"""Ritorna gli argomenti yt-dlp per i cookies.
Priorità: file cookies_path se esiste → altrimenti --cookies-from-browser
<name> se `cookies_browser` è settato in config → altrimenti niente.
"""
if cookies_path and Path(cookies_path).exists():
return ["--cookies", cookies_path]
# Fallback: legge il browser dalla config al volo (evita di cambiare
# firma di tutte le funzioni download_*)
try:
from core.config import load_config
browser = (load_config().get("cookies_browser") or "").strip().lower()
except Exception:
browser = ""
if browser in _ALLOWED_BROWSERS:
return ["--cookies-from-browser", browser]
return []
# Flag globale per interruzione # Flag globale per interruzione
_stop_event = threading.Event() _stop_event = threading.Event()
_current_process: Optional[subprocess.Popen] = None _current_process: Optional[subprocess.Popen] = None
@@ -99,8 +122,7 @@ def _search_youtube(query: str, cookies_path: Optional[str] = None) -> tuple[str
"--no-warnings", "--no-warnings",
"--flat-playlist", "--flat-playlist",
] ]
if cookies_path and Path(cookies_path).exists(): cmd.extend(_cookie_args(cookies_path))
cmd.extend(["--cookies", cookies_path])
result = subprocess.run(cmd, capture_output=True, text=True, timeout=30, **subprocess_flags()) result = subprocess.run(cmd, capture_output=True, text=True, timeout=30, **subprocess_flags())
if result.returncode != 0: if result.returncode != 0:
@@ -216,8 +238,7 @@ def download_playlist(
ffmpeg_dir = find_ffmpeg_dir() ffmpeg_dir = find_ffmpeg_dir()
if ffmpeg_dir: if ffmpeg_dir:
cmd.extend(["--ffmpeg-location", ffmpeg_dir]) cmd.extend(["--ffmpeg-location", ffmpeg_dir])
if cookies_path and Path(cookies_path).exists(): cmd.extend(_cookie_args(cookies_path))
cmd.extend(["--cookies", cookies_path])
try: try:
with _process_lock: with _process_lock:
@@ -300,8 +321,7 @@ def download_direct_url(
"--no-warnings", "--no-warnings",
url, url,
] ]
if cookies_path and Path(cookies_path).exists(): probe_cmd.extend(_cookie_args(cookies_path))
probe_cmd.extend(["--cookies", cookies_path])
try: try:
result = subprocess.run(probe_cmd, capture_output=True, text=True, timeout=60, **subprocess_flags()) result = subprocess.run(probe_cmd, capture_output=True, text=True, timeout=60, **subprocess_flags())
@@ -375,8 +395,7 @@ def download_direct_url(
ffmpeg_dir = find_ffmpeg_dir() ffmpeg_dir = find_ffmpeg_dir()
if ffmpeg_dir: if ffmpeg_dir:
cmd.extend(["--ffmpeg-location", ffmpeg_dir]) cmd.extend(["--ffmpeg-location", ffmpeg_dir])
if cookies_path and Path(cookies_path).exists(): cmd.extend(_cookie_args(cookies_path))
cmd.extend(["--cookies", cookies_path])
try: try:
with _process_lock: with _process_lock:
@@ -511,8 +530,7 @@ def download_urls(
ffmpeg_dir = find_ffmpeg_dir() ffmpeg_dir = find_ffmpeg_dir()
if ffmpeg_dir: if ffmpeg_dir:
cmd.extend(["--ffmpeg-location", ffmpeg_dir]) cmd.extend(["--ffmpeg-location", ffmpeg_dir])
if cookies_path and Path(cookies_path).exists(): cmd.extend(_cookie_args(cookies_path))
cmd.extend(["--cookies", cookies_path])
try: try:
with _process_lock: with _process_lock:
@@ -608,8 +626,7 @@ def download_video(
probe_cmd = [ probe_cmd = [
ytdlp, "--dump-json", "--flat-playlist", "--no-download", "--no-warnings", url, ytdlp, "--dump-json", "--flat-playlist", "--no-download", "--no-warnings", url,
] ]
if cookies_path and Path(cookies_path).exists(): probe_cmd.extend(_cookie_args(cookies_path))
probe_cmd.extend(["--cookies", cookies_path])
try: try:
result = subprocess.run(probe_cmd, capture_output=True, text=True, timeout=60, **subprocess_flags()) result = subprocess.run(probe_cmd, capture_output=True, text=True, timeout=60, **subprocess_flags())
@@ -684,8 +701,7 @@ def download_video(
ffmpeg_dir = find_ffmpeg_dir() ffmpeg_dir = find_ffmpeg_dir()
if ffmpeg_dir: if ffmpeg_dir:
cmd.extend(["--ffmpeg-location", ffmpeg_dir]) cmd.extend(["--ffmpeg-location", ffmpeg_dir])
if cookies_path and Path(cookies_path).exists(): cmd.extend(_cookie_args(cookies_path))
cmd.extend(["--cookies", cookies_path])
try: try:
with _process_lock: with _process_lock:
+7
View File
@@ -45,6 +45,13 @@ def _bundle_dirs() -> list[Path]:
sub_macos = frameworks / sub / "Contents" / "MacOS" sub_macos = frameworks / sub / "Contents" / "MacOS"
if sub_macos.exists(): if sub_macos.exists():
dirs.append(sub_macos) dirs.append(sub_macos)
else:
# Dev: cerca in <project_root>/bundle_bin/ così `python main.py`
# usa gli stessi binari del bundle (aggiornati via build_*.py)
# invece di Homebrew/PATH che possono essere obsoleti.
project_bundle = Path(__file__).resolve().parent.parent / "bundle_bin"
if project_bundle.exists():
dirs.append(project_bundle)
return dirs return dirs
+18
View File
@@ -563,6 +563,24 @@
</div> </div>
</div> </div>
<div class="field">
<label class="field-label">Cookies da browser (bypass 403 YouTube)</label>
<div class="row">
<select id="cookiesBrowserSelect" class="input pill" style="max-width:220px;">
<option value="">Nessuno</option>
<option value="chrome">Chrome</option>
<option value="safari">Safari</option>
<option value="firefox">Firefox</option>
<option value="edge">Edge</option>
<option value="brave">Brave</option>
</select>
</div>
<div class="hint">
<span class="hint-ico">ⓘ</span>
<div>Alcuni video YouTube (musica protetta, region-lock, età) danno HTTP 403 senza cookies autenticati. Selezionando un browser, l'app legge i cookies dal tuo profilo — richiede che tu sia loggato su YouTube in quel browser. Se hai anche cookies.txt sopra, ha priorità il file.</div>
</div>
</div>
<div class="field"> <div class="field">
<label class="field-label">Cartella output predefinita</label> <label class="field-label">Cartella output predefinita</label>
<div class="row"> <div class="row">
+2
View File
@@ -485,6 +485,7 @@ async function init() {
$("#bitrateSelect").value = state.config.bitrate || "320K"; $("#bitrateSelect").value = state.config.bitrate || "320K";
$("#hqThresholdInput").value = state.config.hq_threshold || 310; $("#hqThresholdInput").value = state.config.hq_threshold || 310;
$("#cookiesInput").value = state.config.cookies_path || ""; $("#cookiesInput").value = state.config.cookies_path || "";
if ($("#cookiesBrowserSelect")) $("#cookiesBrowserSelect").value = state.config.cookies_browser || "";
$("#outputInput").value = state.config.output_dir || ""; $("#outputInput").value = state.config.output_dir || "";
$("#themeSelect").value = state.config.theme || "dark"; $("#themeSelect").value = state.config.theme || "dark";
applyTheme(state.config.theme); applyTheme(state.config.theme);
@@ -1293,6 +1294,7 @@ $("#saveBtn").addEventListener("click", async () => {
bitrate: $("#bitrateSelect").value, bitrate: $("#bitrateSelect").value,
hq_threshold: parseInt($("#hqThresholdInput").value, 10) || 310, hq_threshold: parseInt($("#hqThresholdInput").value, 10) || 310,
cookies_path: $("#cookiesInput").value, cookies_path: $("#cookiesInput").value,
cookies_browser: $("#cookiesBrowserSelect") ? $("#cookiesBrowserSelect").value : "",
output_dir: $("#outputInput").value, output_dir: $("#outputInput").value,
theme: $("#themeSelect").value, theme: $("#themeSelect").value,
}; };