diff --git a/tubesync/sync/youtube.py b/tubesync/sync/youtube.py index 33cdb795..6911eabd 100644 --- a/tubesync/sync/youtube.py +++ b/tubesync/sync/youtube.py @@ -5,6 +5,7 @@ import os +import json from pathlib import Path from django.conf import settings from copy import copy @@ -74,6 +75,28 @@ def get_channel_image_info(url): raise YouTubeError(f'Failed to extract channel info for "{url}": {e}') from e +def _subscriber_only(msg='', response=None): + if response is None: + # process msg only + msg = str(msg) + if 'access to members-only content' in msg: + return True + if ': Join this channel' in msg: + return True + else: + # ignore msg entirely + try: + data = json.loads(response) + except (TypeError, ValueError, AttributeError): + return False + + if 'availability' not in data.keys(): + return False + + # check for the specific expected value + return 'subscriber_only' == data.get('availability') + return False + def get_media_info(url): ''' @@ -83,7 +106,8 @@ def get_media_info(url): ''' opts = get_yt_opts() opts.update({ - 'ignore_no_formats_error': False, + 'ignoreerrors': False, # explicitly set this to catch exceptions + 'ignore_no_formats_error': False, # we must fail first to try again with this enabled 'skip_download': True, 'forcejson': True, 'simulate': True, @@ -94,13 +118,20 @@ def get_media_info(url): with yt_dlp.YoutubeDL(opts) as y: try: response = y.extract_info(url, download=False) - except yt_dlp.utils.ExtractorError as e: - if not e.expected: - raise e - log.warn(e.msg) - pass except yt_dlp.utils.DownloadError as e: - raise YouTubeError(f'Failed to extract_info for "{url}": {e}') from e + if not _subscriber_only(msg=e.msg): + raise YouTubeError(f'Failed to extract_info for "{url}": {e}') from e + # adjust options and try again + opts.update({'ignore_no_formats_error': True,}) + with yt_dlp.YoutubeDL(opts) as yy: + try: + response = yy.extract_info(url, download=False) + except yt_dlp.utils.DownloadError as ee: + raise YouTubeError(f'Failed (again) to extract_info for "{url}": {ee}') from ee + # validate the response is what we expected + if not _subscriber_only(response=response): + response = {} + if not response: raise YouTubeError(f'Failed to extract_info for "{url}": No metadata was ' f'returned by youtube-dl, check for error messages in the '