Using gnomekeyring for yt-dlp brave cookie parsing.

This commit is contained in:
Roland 2026-02-19 17:29:29 -07:00
parent a49f67e117
commit 929468821b
No known key found for this signature in database
GPG key ID: 1BA620CE6C9A4680
4 changed files with 42 additions and 22 deletions

View file

@ -124,6 +124,7 @@ def get_youtube_duration(video_url: str) -> Optional[int]:
"""Get video duration using YouTube's internal API. Returns None if URL is not YouTube or API fails."""
video_id = get_youtube_video_id(video_url)
if not video_id:
logger.debug(f'Could not extract YouTube video ID from {video_url}')
return None
payload = {
@ -145,10 +146,15 @@ def get_youtube_duration(video_url: str) -> Optional[int]:
response.raise_for_status()
data = response.json()
length_seconds = data.get("videoDetails", {}).get("lengthSeconds")
if length_seconds:
if length_seconds is not None:
return int(length_seconds)
else:
# Log why we couldn't get duration
status = data.get("playabilityStatus", {}).get("status", "UNKNOWN")
reason = data.get("playabilityStatus", {}).get("reason", "")
logger.warning(f'YouTube API returned no duration for {video_url}: status={status} reason={reason}')
except Exception as e:
logger.debug(f'YouTube API failed for {video_url}: {e}')
logger.debug(f'YouTube API request failed for {video_url}: {e}')
return None
@ -282,7 +288,8 @@ async def fetch_video_duration(url: str) -> int:
return duration
# Try YouTube API first (faster than yt-dlp)
if duration := get_youtube_duration(url):
duration = get_youtube_duration(url)
if duration is not None:
logger.info(f'Fetched video duration of {duration} from {url} using YouTube API')
return duration

View file

@ -1098,32 +1098,41 @@ def get_browser_profiles(home: pathlib.Path = WROLPI_HOME) -> dict:
def browser_profile_to_yt_dlp_arg(profile: pathlib.Path) -> str:
"""Takes a Path and returns a string that can be used as a yt-dlp `--cookies-from-browser` argument."""
"""Takes a Path and returns a string that can be used as a yt-dlp `--cookies-from-browser` argument.
Format: BROWSER+KEYRING:PROFILE
We explicitly specify GNOMEKEYRING because yt-dlp's auto-detection relies on desktop environment
variables which are not available when running as a systemd service.
"""
profile_str = str(profile)
for browser_key, config in BROWSER_CONFIGS.items():
if config['config_path'] in profile_str:
return f"{config['yt_dlp_name']}:{profile.name}"
return f"{config['yt_dlp_name']}+GNOMEKEYRING:{profile.name}"
# Fallback to original behavior for unknown browsers
*_, browser, profile_name = profile_str.split('/')
return f'{browser}:{profile_name}'
return f'{browser}+GNOMEKEYRING:{profile_name}'
def browser_profile_to_yt_dlp_tuple(profile: pathlib.Path) -> tuple:
"""Takes a Path and returns a tuple that can be used as yt-dlp `cookiesfrombrowser` option in Python API.
The yt-dlp Python API expects cookiesfrombrowser as a tuple: (browser, profile, keyring, container)
We explicitly specify GNOMEKEYRING because yt-dlp's auto-detection relies on desktop environment
variables which are not available when running as a systemd service.
"""
profile_str = str(profile)
for browser_key, config in BROWSER_CONFIGS.items():
if config['config_path'] in profile_str:
return (config['yt_dlp_name'], profile.name, None, None)
return (config['yt_dlp_name'], profile.name, 'GNOMEKEYRING', None)
# Fallback for unknown browsers
*_, browser, profile_name = profile_str.split('/')
return (browser, profile_name, None, None)
return (browser, profile_name, 'GNOMEKEYRING', None)
def format_video_filename(

View file

@ -370,34 +370,37 @@ async def test_videos_downloader_config_api(async_client, test_directory, test_v
@pytest.mark.parametrize('file_name,expected', [
('/home/wrolpi/.config/chromium/Default', 'chromium:Default'),
('/home/wrolpi/.config/chromium/Profile 1', 'chromium:Profile 1'),
('/home/wrolpi/.mozilla/firefox/29el0wk0.default-release', 'firefox:29el0wk0.default-release'),
('/home/wrolpi/.config/BraveSoftware/Brave-Browser/Default', 'brave:Default'),
('/home/wrolpi/.config/BraveSoftware/Brave-Browser/Profile 1', 'brave:Profile 1'),
('/home/wrolpi/.config/google-chrome/Default', 'chrome:Default'),
('/home/wrolpi/.config/google-chrome/Profile 1', 'chrome:Profile 1'),
('/home/wrolpi/.config/chromium/Default', 'chromium+GNOMEKEYRING:Default'),
('/home/wrolpi/.config/chromium/Profile 1', 'chromium+GNOMEKEYRING:Profile 1'),
('/home/wrolpi/.mozilla/firefox/29el0wk0.default-release', 'firefox+GNOMEKEYRING:29el0wk0.default-release'),
('/home/wrolpi/.config/BraveSoftware/Brave-Browser/Default', 'brave+GNOMEKEYRING:Default'),
('/home/wrolpi/.config/BraveSoftware/Brave-Browser/Profile 1', 'brave+GNOMEKEYRING:Profile 1'),
('/home/wrolpi/.config/google-chrome/Default', 'chrome+GNOMEKEYRING:Default'),
('/home/wrolpi/.config/google-chrome/Profile 1', 'chrome+GNOMEKEYRING:Profile 1'),
])
def test_browser_profile_to_yt_dlp_arg(file_name, expected):
"""
Test the conversion of a browser profile to yt-dlp arguments.
Format: BROWSER+KEYRING:PROFILE
Explicitly uses GNOMEKEYRING because yt-dlp auto-detection doesn't work in systemd services.
"""
file_name = pathlib.Path(file_name)
assert lib.browser_profile_to_yt_dlp_arg(file_name) == expected
@pytest.mark.parametrize('file_name,expected', [
('/home/wrolpi/.config/chromium/Default', ('chromium', 'Default', None, None)),
('/home/wrolpi/.config/chromium/Profile 1', ('chromium', 'Profile 1', None, None)),
('/home/wrolpi/.mozilla/firefox/29el0wk0.default-release', ('firefox', '29el0wk0.default-release', None, None)),
('/home/wrolpi/.config/BraveSoftware/Brave-Browser/Default', ('brave', 'Default', None, None)),
('/home/wrolpi/.config/BraveSoftware/Brave-Browser/Profile 1', ('brave', 'Profile 1', None, None)),
('/home/wrolpi/.config/google-chrome/Default', ('chrome', 'Default', None, None)),
('/home/wrolpi/.config/google-chrome/Profile 1', ('chrome', 'Profile 1', None, None)),
('/home/wrolpi/.config/chromium/Default', ('chromium', 'Default', 'GNOMEKEYRING', None)),
('/home/wrolpi/.config/chromium/Profile 1', ('chromium', 'Profile 1', 'GNOMEKEYRING', None)),
('/home/wrolpi/.mozilla/firefox/29el0wk0.default-release', ('firefox', '29el0wk0.default-release', 'GNOMEKEYRING', None)),
('/home/wrolpi/.config/BraveSoftware/Brave-Browser/Default', ('brave', 'Default', 'GNOMEKEYRING', None)),
('/home/wrolpi/.config/BraveSoftware/Brave-Browser/Profile 1', ('brave', 'Profile 1', 'GNOMEKEYRING', None)),
('/home/wrolpi/.config/google-chrome/Default', ('chrome', 'Default', 'GNOMEKEYRING', None)),
('/home/wrolpi/.config/google-chrome/Profile 1', ('chrome', 'Profile 1', 'GNOMEKEYRING', None)),
])
def test_browser_profile_to_yt_dlp_tuple(file_name, expected):
"""
Test the conversion of a browser profile to yt-dlp cookiesfrombrowser tuple.
Explicitly uses GNOMEKEYRING because yt-dlp auto-detection doesn't work in systemd services.
"""
file_name = pathlib.Path(file_name)
assert lib.browser_profile_to_yt_dlp_tuple(file_name) == expected