aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--yt_dlp/extractor/crunchyroll.py30
1 files changed, 25 insertions, 5 deletions
diff --git a/yt_dlp/extractor/crunchyroll.py b/yt_dlp/extractor/crunchyroll.py
index 90967c160..ea54f0195 100644
--- a/yt_dlp/extractor/crunchyroll.py
+++ b/yt_dlp/extractor/crunchyroll.py
@@ -2,6 +2,7 @@ import base64
import uuid
from .common import InfoExtractor
+from ..networking import Request
from ..networking.exceptions import HTTPError
from ..utils import (
ExtractorError,
@@ -24,6 +25,7 @@ class CrunchyrollBaseIE(InfoExtractor):
_BASE_URL = 'https://www.crunchyroll.com'
_API_BASE = 'https://api.crunchyroll.com'
_NETRC_MACHINE = 'crunchyroll'
+ _SWITCH_USER_AGENT = 'Crunchyroll/1.8.0 Nintendo Switch/12.3.12.0 UE4/4.27'
_REFRESH_TOKEN = None
_AUTH_HEADERS = None
_AUTH_EXPIRY = None
@@ -179,10 +181,19 @@ class CrunchyrollBaseIE(InfoExtractor):
display_id = identifier
self._update_auth()
- stream_response = self._download_json(
- f'https://cr-play-service.prd.crunchyrollsvc.com/v1/{identifier}/console/switch/play',
- display_id, note='Downloading stream info', errnote='Failed to download stream info',
- headers=CrunchyrollBaseIE._AUTH_HEADERS)
+ headers = {**CrunchyrollBaseIE._AUTH_HEADERS, 'User-Agent': self._SWITCH_USER_AGENT}
+ try:
+ stream_response = self._download_json(
+ f'https://cr-play-service.prd.crunchyrollsvc.com/v1/{identifier}/console/switch/play',
+ display_id, note='Downloading stream info', errnote='Failed to download stream info', headers=headers)
+ except ExtractorError as error:
+ if self.get_param('ignore_no_formats_error'):
+ self.report_warning(error.orig_msg)
+ return [], {}
+ elif isinstance(error.cause, HTTPError) and error.cause.status == 420:
+ raise ExtractorError(
+ 'You have reached the rate-limit for active streams; try again later', expected=True)
+ raise
available_formats = {'': ('', '', stream_response['url'])}
for hardsub_lang, stream in traverse_obj(stream_response, ('hardSubs', {dict.items}, lambda _, v: v[1]['url'])):
@@ -211,7 +222,7 @@ class CrunchyrollBaseIE(InfoExtractor):
fatal=False, note=f'Downloading {f"{format_id} " if hardsub_lang else ""}MPD manifest')
self._merge_subtitles(dash_subs, target=subtitles)
else:
- continue # XXX: Update this if/when meta mpd formats are working
+ continue # XXX: Update this if meta mpd formats work; will be tricky with token invalidation
for f in adaptive_formats:
if f.get('acodec') != 'none':
f['language'] = audio_locale
@@ -221,6 +232,15 @@ class CrunchyrollBaseIE(InfoExtractor):
for locale, subtitle in traverse_obj(stream_response, (('subtitles', 'captions'), {dict.items}, ...)):
subtitles.setdefault(locale, []).append(traverse_obj(subtitle, {'url': 'url', 'ext': 'format'}))
+ # Invalidate stream token to avoid rate-limit
+ error_msg = 'Unable to invalidate stream token; you may experience rate-limiting'
+ if stream_token := stream_response.get('token'):
+ self._request_webpage(Request(
+ f'https://cr-play-service.prd.crunchyrollsvc.com/v1/token/{identifier}/{stream_token}/inactive',
+ headers=headers, method='PATCH'), display_id, 'Invalidating stream token', error_msg, fatal=False)
+ else:
+ self.report_warning(error_msg)
+
return formats, subtitles