Compare commits

..

1 Commits

Author SHA1 Message Date
matt e0d3af2a4f feat(yt): pull cookies from firefox dev edition, firefox, then chrome
age/login-gated videos need browser cookies to download.
`ydl_opts` becomes a computed_field that picks the first available source.
2026-08-07 12:38:13 -07:00
+32 -11
View File
@@ -1,12 +1,13 @@
import asyncio
import glob
import os
import re
from pathlib import Path
from typing import ClassVar
from pydantic_settings import CliImplicitFlag, CliPositionalArg
import structlog
import yt_dlp
import yt_dlp.cookies
from mutagen.easyid3 import EasyID3
from pydantic import Field, computed_field
from shazamio import Shazam
@@ -73,22 +74,24 @@ def current_quarter() -> Path:
return Path().home() / "Music" / f"{date.quarter}_{date.year}"
def _cookiesfrombrowser() -> tuple | None:
"""First available cookies source: firefox dev edition, firefox, then chrome."""
for root in yt_dlp.cookies._firefox_browser_dirs():
# dev edition lives in a randomly-prefixed dir, so pass an absolute path
matches = glob.glob(os.path.join(root, "*.dev-edition-default"))
if matches:
return ("firefox", matches[0], None, None)
if any(yt_dlp.cookies._firefox_cookie_dbs(yt_dlp.cookies._firefox_browser_dirs())):
return ("firefox", None, None, None)
return ("chrome", None, None, None)
class YtDownload(Command):
"""
download a youtube song from {URL} to current quarter dir.
"""
url: CliPositionalArg[str] = Field(description="youtube url to download")
ydl_opts: ClassVar = {
"format": "mp3/bestaudio/best",
"postprocessors": [
{
"key": "FFmpegExtractAudio",
"preferredcodec": "mp3",
}
],
"remote_components": ["ejs:github"],
}
parents: CliImplicitFlag[bool] = Field(
default=True, description="create the parent dirs if not exists"
)
@@ -99,6 +102,24 @@ class YtDownload(Command):
date = DateParse()
return Path().home() / "Music" / f"{date.quarter}_{date.year}"
@computed_field()
@property
def ydl_opts(self) -> dict:
opts = {
"format": "mp3/bestaudio/best",
"postprocessors": [
{
"key": "FFmpegExtractAudio",
"preferredcodec": "mp3",
}
],
"remote_components": ["ejs:github"],
}
cookies = _cookiesfrombrowser()
if cookies is not None:
opts["cookiesfrombrowser"] = cookies
return opts
def run(self) -> None:
os.chdir(Path.home() / "Downloads")
with yt_dlp.YoutubeDL(self.ydl_opts) as ydl: