diff --git a/modules/videos/lib.py b/modules/videos/lib.py index 0ec3fa54..d6ad3f65 100644 --- a/modules/videos/lib.py +++ b/modules/videos/lib.py @@ -1092,6 +1092,22 @@ def browser_profile_to_yt_dlp_arg(profile: pathlib.Path) -> str: return f'{browser}:{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) + """ + 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) + + # Fallback for unknown browsers + *_, browser, profile_name = profile_str.split('/') + return (browser, profile_name, None, None) + + def format_video_filename( video: Video, file_name_format: str = None, diff --git a/modules/videos/test/test_lib.py b/modules/videos/test/test_lib.py index 27447fc1..67c082ad 100644 --- a/modules/videos/test/test_lib.py +++ b/modules/videos/test/test_lib.py @@ -386,6 +386,23 @@ def test_browser_profile_to_yt_dlp_arg(file_name, expected): 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)), +]) +def test_browser_profile_to_yt_dlp_tuple(file_name, expected): + """ + Test the conversion of a browser profile to yt-dlp cookiesfrombrowser tuple. + """ + file_name = pathlib.Path(file_name) + assert lib.browser_profile_to_yt_dlp_tuple(file_name) == expected + + def test_get_browser_profiles(test_directory): """Test discovery of browser profiles for all supported browsers.""" # Create a mock home directory with browser profiles diff --git a/modules/videos/video/lib.py b/modules/videos/video/lib.py index aee3872f..bcc1a35a 100644 --- a/modules/videos/video/lib.py +++ b/modules/videos/video/lib.py @@ -1,4 +1,5 @@ import asyncio +import pathlib import random from datetime import timedelta from typing import Tuple, Optional, List @@ -173,7 +174,13 @@ def delete_videos(session: Session, *video_ids: int): session.commit() -def download_video_info_json(url: str) -> dict: +def download_video_info_json(url: str, browser_profile: 'pathlib.Path' = None) -> dict: + """Download video info JSON, optionally using browser cookies for authentication. + + Args: + url: The video URL to fetch info for. + browser_profile: Optional path to browser profile for cookie-based authentication. + """ ydl_opts = dict( getcomments=True, skip_download=True, @@ -181,6 +188,10 @@ def download_video_info_json(url: str) -> dict: cachedir=YTDLP_CACHE_DIR, ) + if browser_profile and browser_profile.exists(): + from modules.videos.lib import browser_profile_to_yt_dlp_tuple + ydl_opts['cookiesfrombrowser'] = browser_profile_to_yt_dlp_tuple(browser_profile) + ydl_logger = logger.getChild('youtube-dl') with yt_dlp.YoutubeDL(ydl_opts) as ydl: @@ -195,7 +206,15 @@ def download_video_info_json(url: str) -> dict: async def get_missing_videos_comments(limit: int = VIDEO_COMMENTS_FETCH_COUNT): """ Fetches Video info json for videos without comments, if comments are found then the info json file is replaced. + Uses browser profile for authentication if configured. """ + from modules.videos.lib import get_videos_downloader_config + + config = get_videos_downloader_config() + browser_profile = None + if config.browser_profile and config.always_use_browser_profile: + browser_profile = pathlib.Path(config.browser_profile) + one_month_ago = now() - timedelta(days=30) # Get any videos over a month old that do not have comments. @@ -262,7 +281,7 @@ async def get_missing_videos_comments(limit: int = VIDEO_COMMENTS_FETCH_COUNT): try: # Get info json about the video. - info = download_video_info_json(url) + info = download_video_info_json(url, browser_profile=browser_profile) if not info or not isinstance(info.get('comments'), list): logger.error(f'Unable to get comments for video: {url=}')