feat: make sure max_video_quality setting is working

This commit is contained in:
2026-05-16 16:07:52 +03:00
parent 4cd6255b0f
commit 1928f70928
9 changed files with 118 additions and 18 deletions
+32 -10
View File
@@ -8,30 +8,52 @@ from typing import Any, Dict, List, Optional
DEFAULT_CONFIG: Dict[str, Any] = {
"playlists": [],
"download_mode": "audio",
"max_video_quality": "1080p",
"max_download_quality": "1080p",
"save_path": "./downloads",
"ffmpeg_path": "ffmpeg",
}
class Settings:
def __init__(self, config_path: Optional[Path] = None) -> None:
def __init__(self) -> None:
base_dir = Path("config")
base_dir.mkdir(parents=True, exist_ok=True)
self.path = (config_path or (base_dir / "yt-playlist-config.json")).resolve()
self.path = (base_dir / "yt-playlist-config.json").resolve()
self.data: Dict[str, Any] = dict(DEFAULT_CONFIG)
if self.path.exists():
try:
self.data.update(json.loads(self.path.read_text(encoding="utf-8")))
except Exception:
# Leave defaults if invalid JSON; validation can be added later.
pass
# Ensure there is always a config file at the default path.
if not self.path.exists():
self._write_default_config(self.path)
self._load_from_path(self.path)
def _load_from_path(self, path: Path) -> None:
try:
self.data.update(json.loads(path.read_text(encoding="utf-8")))
except Exception:
# Leave defaults if invalid JSON; validation can be added later.
pass
def _write_default_config(self, path: Path) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
default_payload: Dict[str, Any] = {
"playlists": [
{
"url": "https://www.youtube.com/playlist?list=YOUR_PLAYLIST_ID",
"download_mode": "audio",
"max_download_quality": "1080p",
"save_path": "./downloads",
}
],
"ffmpeg_path": "ffmpeg",
}
path.write_text(json.dumps(default_payload, indent=2) + "\n", encoding="utf-8")
@property
def playlists(self) -> List[Dict[str, Any]]:
global_defaults = {
"download_mode": self.data.get("download_mode", DEFAULT_CONFIG["download_mode"]),
"max_video_quality": self.data.get("max_video_quality", DEFAULT_CONFIG["max_video_quality"]),
"max_download_quality": self.data.get("max_download_quality", DEFAULT_CONFIG["max_download_quality"]),
"save_path": self.data.get("save_path", DEFAULT_CONFIG["save_path"]),
"ffmpeg_path": self.data.get("ffmpeg_path", DEFAULT_CONFIG["ffmpeg_path"]),
}
+27 -1
View File
@@ -15,6 +15,30 @@ class Downloader:
self.yt_dlp_path = yt_dlp_path
self.ffmpeg_path = ffmpeg_path
@staticmethod
def build_format(max_download_quality) -> str:
def parse_height_cap(value) -> int | None:
if value is None:
return None
if isinstance(value, int):
return value if value > 0 else None
s = str(value).strip().lower()
if not s or s in {"best", "max", "auto", "none", "null"}:
return None
digits = "".join(ch for ch in s if ch.isdigit())
if not digits:
return None
try:
cap = int(digits)
except Exception:
return None
return cap if cap > 0 else None
cap = parse_height_cap(max_download_quality)
if cap is not None:
return f"best[ext=mp4][acodec!=none][vcodec!=none][height<={cap}]/best[ext=mp4][height<={cap}]/best[ext=mp4]"
return "best[ext=mp4][acodec!=none][vcodec!=none]/best[ext=mp4]"
async def handle_job(self, job: DownloadJob):
try:
job.state = JobState.DOWNLOADING
@@ -53,12 +77,14 @@ class Downloader:
outtmpl = str(job.output_path)
fmt = self.build_format(getattr(job, "max_download_quality", None))
# All modes download a single muxed mp4 when possible.
# This avoids any ffmpeg-driven merging during the download step, satisfying:
# - video: "original file, no processing"
# - audio/both: extraction is done separately after download
ydl_opts = {
"format": "best[ext=mp4][acodec!=none][vcodec!=none]/best[ext=mp4]",
"format": fmt,
"outtmpl": outtmpl,
"noplaylist": True,
"quiet": True,
+1
View File
@@ -28,6 +28,7 @@ class DownloadJob:
state: JobState = JobState.QUEUED
error: Optional[str] = None
ffmpeg_path: Optional[str] = None
max_download_quality: Optional[str] = None
audio_output_path: Optional[Path] = None # when mode=video and we also want mp3
keep_video: bool = True
+4
View File
@@ -152,6 +152,7 @@ class ActionExecutor:
d["video"] = a.to_name
ffmpeg_cfg = str(playlist_cfg.get("ffmpeg_path", "ffmpeg")) if playlist_cfg.get("ffmpeg_path") is not None else None
max_quality_cfg = playlist_cfg.get("max_download_quality")
temp_video_root = video_root / ".tmp"
temp_video_root.mkdir(parents=True, exist_ok=True)
@@ -176,6 +177,7 @@ class ActionExecutor:
url=url,
mode="video",
ffmpeg_path=ffmpeg_cfg,
max_download_quality=max_quality_cfg,
audio_output_path=audio_path,
)
jobs.append(job)
@@ -200,6 +202,7 @@ class ActionExecutor:
url=url,
mode="video",
ffmpeg_path=ffmpeg_cfg,
max_download_quality=max_quality_cfg,
audio_output_path=audio_path,
keep_video=False,
)
@@ -215,6 +218,7 @@ class ActionExecutor:
url=url,
mode="video",
ffmpeg_path=ffmpeg_cfg,
max_download_quality=max_quality_cfg,
)
jobs.append(job)
await queue.enqueue(job)