From 740f6ea2e52906a5cc9af497376c90109ce5cc8a Mon Sep 17 00:00:00 2001 From: doe1080 <98906116+doe1080@users.noreply.github.com> Date: Thu, 12 Jun 2025 01:44:31 +0900 Subject: [PATCH] cleanup --- yt_dlp/extractor/onsen.py | 87 ++++++++++++++++++++++----------------- 1 file changed, 50 insertions(+), 37 deletions(-) diff --git a/yt_dlp/extractor/onsen.py b/yt_dlp/extractor/onsen.py index 809d4ca282..b5ca66aa2e 100644 --- a/yt_dlp/extractor/onsen.py +++ b/yt_dlp/extractor/onsen.py @@ -4,11 +4,14 @@ import json from .common import InfoExtractor +from ..networking.exceptions import HTTPError from ..utils import ( ExtractorError, + clean_html, + int_or_none, parse_qs, str_or_none, - strip_or_none, + update_url, url_or_none, ) from ..utils.traversal import traverse_obj @@ -18,8 +21,8 @@ class OnsenIE(InfoExtractor): IE_NAME = 'onsen' IE_DESC = 'インターネットラジオステーション<音泉>' - _BASE_URL = 'https://www.onsen.ag/' - _HEADERS = {'Referer': _BASE_URL} + _BASE_URL = 'https://www.onsen.ag' + _HEADERS = {'Referer': f'{_BASE_URL}/'} _NETRC_MACHINE = 'onsen' _VALID_URL = r'https?://(?:(?:share|www)\.)onsen\.ag/program/(?P[^/?#]+)' _TESTS = [{ @@ -28,16 +31,15 @@ class OnsenIE(InfoExtractor): 'id': '10462', 'ext': 'm4a', 'title': '第SP回', - 'cast': ['下野紘', '佐藤元', '守屋亨香'], - 'description': 'md5:083c1eddf198694cd3cc83f4d5c03863', + 'cast': 'count:3', + 'description': 'md5:de62c80a41c4c8d84da53a1ee681ad18', 'display_id': 'MTA0NjI=', - 'http_headers': {'Referer': 'https://www.onsen.ag/'}, 'media_type': 'sound', 'section_start': 0, 'series': '音泉キング「下野紘」のラジオ きみはもちろん、<音泉>ファミリーだよね?', 'series_id': 'onsenking', - 'tags': ['音泉キング', '音泉ジュニア'], - 'thumbnail': r're:https?://d3bzklg4lms4gh\.cloudfront\.net/program_info/image/default/production/.+$', + 'tags': 'count:2', + 'thumbnail': r're:https?://d3bzklg4lms4gh\.cloudfront\.net/program_info/image/default/production/.+', 'upload_date': '20220627', 'webpage_url': 'https://www.onsen.ag/program/onsenking?c=MTA0NjI=', }, @@ -47,32 +49,31 @@ class OnsenIE(InfoExtractor): 'id': '18001', 'ext': 'mp4', 'title': '第4回', - 'cast': ['夕莉', '理名', '朱李', '凪都', '美怜'], + 'cast': 'count:5', 'description': 'md5:1d7f6a2f1f5a3e2a8ada4e9f652262dd', 'display_id': 'MTgwMDE=', - 'http_headers': {'Referer': 'https://www.onsen.ag/'}, 'media_type': 'movie', 'section_start': 0, 'series': 'TVアニメ『ガールズバンドクライ』WEBラジオ「ガールズバンドクライ~ラジオにも全部ぶち込め。~」', 'series_id': 'girls-band-cry-radio', - 'tags': ['ガールズバンドクライ', 'ガルクラ', 'ガルクラジオ'], - 'thumbnail': r're:https?://d3bzklg4lms4gh\.cloudfront\.net/program_info/image/default/production/.+$', + 'tags': 'count:3', + 'thumbnail': r're:https?://d3bzklg4lms4gh\.cloudfront\.net/program_info/image/default/production/.+', 'upload_date': '20240425', 'webpage_url': 'https://www.onsen.ag/program/girls-band-cry-radio?c=MTgwMDE=', }, 'skip': 'Only available for premium supporters', }, { - 'url': 'https://www.onsen.ag/program/g-witch', + 'url': 'https://www.onsen.ag/program/uma', 'info_dict': { - 'id': 'g-witch', - 'title': '機動戦士ガンダム 水星の魔女~アスティカシア高等専門学園 ラジオ委員会~', + 'id': 'uma', + 'title': 'UMA YELL RADIO', }, - 'playlist_mincount': 7, + 'playlist_mincount': 35, }] def _perform_login(self, username, password): sign_in = self._download_json( - f'{self._BASE_URL}web_api/signin', None, 'Logging in', headers={ + f'{self._BASE_URL}/web_api/signin', None, 'Logging in', headers={ 'Accept': 'application/json, text/plain, */*', 'Content-Type': 'application/json; charset=UTF-8', }, data=json.dumps({ @@ -87,12 +88,17 @@ def _perform_login(self, username, password): def _real_extract(self, url): program_id = self._match_id(url) - qs = {k: v[0] for k, v in parse_qs(url).items() if v} - programs = self._download_json( - f'{self._BASE_URL}web_api/programs/{program_id}', program_id) - enc_id = lambda p: base64.b64encode(str(p['id']).encode()).decode() + try: + programs = self._download_json( + f'{self._BASE_URL}/web_api/programs/{program_id}', program_id) + except ExtractorError as e: + if isinstance(e.cause, HTTPError) and e.cause.status == 404: + raise ExtractorError('Invalid URL', expected=True) + raise - if 'c' not in qs: + enc_id = lambda p: base64.b64encode(str(p['id']).encode()).decode() + query = {k: v[0] for k, v in parse_qs(url).items() if v} + if 'c' not in query: entries = [self.url_result( f'{url}?c={enc_id(program)}', OnsenIE, ) for program in traverse_obj(programs, ('contents', lambda _, v: v['id']))] @@ -100,16 +106,23 @@ def _real_extract(self, url): return self.playlist_result( entries, program_id, programs['program_info']['title']) - raw_id = base64.b64decode(qs['c'] + '=' * (-len(qs['c']) % 4)).decode() + raw_id = base64.b64decode(query['c'] + '=' * (-len(query['c']) & 3)).decode() p_keys = ('contents', lambda _, v: v['id'] == int(raw_id)) - if not (program := traverse_obj(programs, (*p_keys, any))): - raise ExtractorError('This program is no longer available', expected=True) - if not (m3u8_url := traverse_obj(program, ('streaming_url', {url_or_none}))): - self.raise_login_required('This program is only available for premium supporters') + + program = traverse_obj(programs, (*p_keys, any)) + if not program: + raise ExtractorError( + 'This program is no longer available', expected=True) + m3u8_url = traverse_obj(program, ('streaming_url', {url_or_none})) + if not m3u8_url: + self.raise_login_required( + 'This program is only available for premium supporters') display_id = enc_id(program) upload_date = None - if date_str := self._search_regex(rf'{program_id}0?(\d{{6}})', m3u8_url, 'date string', default=None): + if date_str := self._search_regex( + rf'{program_id}0?(\d{{6}})', m3u8_url, 'date string', default=None, + ): with contextlib.suppress(ValueError): upload_date = dt.datetime.strptime(f'20{date_str}', '%Y%m%d').strftime('%Y%m%d') @@ -117,22 +130,22 @@ def _real_extract(self, url): 'display_id': display_id, 'formats': self._extract_m3u8_formats(m3u8_url, display_id, headers=self._HEADERS), 'http_headers': self._HEADERS, - 'section_start': int(qs.get('t', 0)), + 'section_start': int_or_none(query.get('t', 0)), 'upload_date': upload_date, - 'webpage_url': f'{self._BASE_URL}program/{program_id}?c={display_id}', + 'webpage_url': f'{self._BASE_URL}/program/{program_id}?c={display_id}', **traverse_obj(program, { 'id': ('id', {str_or_none}), - 'title': ('title', {strip_or_none}), + 'title': ('title', {clean_html}), 'media_type': ('media_type', {str}), - 'thumbnail': ('poster_image_url', {url_or_none}, {lambda x: x.partition('?')[0]}), + 'thumbnail': ('poster_image_url', {url_or_none}, {update_url(query=None)}), }), **traverse_obj(programs, { - 'cast': (('performers', (*p_keys, 'guests')), ..., 'name', {str}), - 'series_id': ('directory_name', {str_or_none}), + 'cast': (('performers', (*p_keys, 'guests')), ..., 'name', {str}, filter, all, filter), + 'series_id': ('directory_name', {str}), }), **traverse_obj(programs, ('program_info', { - 'description': ('description', {str}), - 'series': ('title', {str}), - 'tags': ('hashtag_list', ..., {str}), + 'description': ('description', {clean_html}), + 'series': ('title', {clean_html}), + 'tags': ('hashtag_list', ..., {str}, filter, all, filter), })), }