mirror of
				https://github.com/yt-dlp/yt-dlp.git
				synced 2025-10-29 13:41:05 +00:00 
			
		
		
		
	 86e5f3ed2e
			
		
	
	
		86e5f3ed2e
		
			
		
	
	
	
	
		
			
			Using https://github.com/asottile/pyupgrade 1. `__future__` imports and `coding: utf-8` were removed 2. Files were rewritten with `pyupgrade --py36-plus --keep-percent-format` 3. f-strings were cherry-picked from `pyupgrade --py36-plus` Extractors are left untouched (except removing header) to avoid unnecessary merge conflicts
		
			
				
	
	
		
			296 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			296 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| import functools
 | ||
| import json
 | ||
| import uuid
 | ||
| 
 | ||
| from .common import InfoExtractor
 | ||
| from ..utils import (
 | ||
|     determine_ext,
 | ||
|     dict_get,
 | ||
|     ExtractorError,
 | ||
|     float_or_none,
 | ||
|     OnDemandPagedList,
 | ||
|     traverse_obj,
 | ||
| )
 | ||
| 
 | ||
| 
 | ||
| class MildomBaseIE(InfoExtractor):
 | ||
|     _GUEST_ID = None
 | ||
| 
 | ||
|     def _call_api(self, url, video_id, query=None, note='Downloading JSON metadata', body=None):
 | ||
|         if not self._GUEST_ID:
 | ||
|             self._GUEST_ID = f'pc-gp-{str(uuid.uuid4())}'
 | ||
| 
 | ||
|         content = self._download_json(
 | ||
|             url, video_id, note=note, data=json.dumps(body).encode() if body else None,
 | ||
|             headers={'Content-Type': 'application/json'} if body else {},
 | ||
|             query={
 | ||
|                 '__guest_id': self._GUEST_ID,
 | ||
|                 '__platform': 'web',
 | ||
|                 **(query or {}),
 | ||
|             })
 | ||
| 
 | ||
|         if content['code'] != 0:
 | ||
|             raise ExtractorError(
 | ||
|                 f'Mildom says: {content["message"]} (code {content["code"]})',
 | ||
|                 expected=True)
 | ||
|         return content['body']
 | ||
| 
 | ||
| 
 | ||
| class MildomIE(MildomBaseIE):
 | ||
|     IE_NAME = 'mildom'
 | ||
|     IE_DESC = 'Record ongoing live by specific user in Mildom'
 | ||
|     _VALID_URL = r'https?://(?:(?:www|m)\.)mildom\.com/(?P<id>\d+)'
 | ||
| 
 | ||
|     def _real_extract(self, url):
 | ||
|         video_id = self._match_id(url)
 | ||
|         webpage = self._download_webpage(f'https://www.mildom.com/{video_id}', video_id)
 | ||
| 
 | ||
|         enterstudio = self._call_api(
 | ||
|             'https://cloudac.mildom.com/nonolive/gappserv/live/enterstudio', video_id,
 | ||
|             note='Downloading live metadata', query={'user_id': video_id})
 | ||
|         result_video_id = enterstudio.get('log_id', video_id)
 | ||
| 
 | ||
|         servers = self._call_api(
 | ||
|             'https://cloudac.mildom.com/nonolive/gappserv/live/liveserver', result_video_id,
 | ||
|             note='Downloading live server list', query={
 | ||
|                 'user_id': video_id,
 | ||
|                 'live_server_type': 'hls',
 | ||
|             })
 | ||
| 
 | ||
|         playback_token = self._call_api(
 | ||
|             'https://cloudac.mildom.com/nonolive/gappserv/live/token', result_video_id,
 | ||
|             note='Obtaining live playback token', body={'host_id': video_id, 'type': 'hls'})
 | ||
|         playback_token = traverse_obj(playback_token, ('data', ..., 'token'), get_all=False)
 | ||
|         if not playback_token:
 | ||
|             raise ExtractorError('Failed to obtain live playback token')
 | ||
| 
 | ||
|         formats = self._extract_m3u8_formats(
 | ||
|             f'{servers["stream_server"]}/{video_id}_master.m3u8?{playback_token}',
 | ||
|             result_video_id, 'mp4', headers={
 | ||
|                 'Referer': 'https://www.mildom.com/',
 | ||
|                 'Origin': 'https://www.mildom.com',
 | ||
|             })
 | ||
| 
 | ||
|         for fmt in formats:
 | ||
|             fmt.setdefault('http_headers', {})['Referer'] = 'https://www.mildom.com/'
 | ||
| 
 | ||
|         self._sort_formats(formats)
 | ||
| 
 | ||
|         return {
 | ||
|             'id': result_video_id,
 | ||
|             'title': self._html_search_meta('twitter:description', webpage, default=None) or traverse_obj(enterstudio, 'anchor_intro'),
 | ||
|             'description': traverse_obj(enterstudio, 'intro', 'live_intro', expected_type=str),
 | ||
|             'timestamp': float_or_none(enterstudio.get('live_start_ms'), scale=1000),
 | ||
|             'uploader': self._html_search_meta('twitter:title', webpage, default=None) or traverse_obj(enterstudio, 'loginname'),
 | ||
|             'uploader_id': video_id,
 | ||
|             'formats': formats,
 | ||
|             'is_live': True,
 | ||
|         }
 | ||
| 
 | ||
| 
 | ||
| class MildomVodIE(MildomBaseIE):
 | ||
|     IE_NAME = 'mildom:vod'
 | ||
|     IE_DESC = 'VOD in Mildom'
 | ||
|     _VALID_URL = r'https?://(?:(?:www|m)\.)mildom\.com/playback/(?P<user_id>\d+)/(?P<id>(?P=user_id)-[a-zA-Z0-9]+-?[0-9]*)'
 | ||
|     _TESTS = [{
 | ||
|         'url': 'https://www.mildom.com/playback/10882672/10882672-1597662269',
 | ||
|         'info_dict': {
 | ||
|             'id': '10882672-1597662269',
 | ||
|             'ext': 'mp4',
 | ||
|             'title': '始めてのミルダム配信じゃぃ!',
 | ||
|             'thumbnail': r're:^https?://.*\.(png|jpg)$',
 | ||
|             'upload_date': '20200817',
 | ||
|             'duration': 4138.37,
 | ||
|             'description': 'ゲームをしたくて!',
 | ||
|             'timestamp': 1597662269.0,
 | ||
|             'uploader_id': '10882672',
 | ||
|             'uploader': 'kson組長(けいそん)',
 | ||
|         },
 | ||
|     }, {
 | ||
|         'url': 'https://www.mildom.com/playback/10882672/10882672-1597758589870-477',
 | ||
|         'info_dict': {
 | ||
|             'id': '10882672-1597758589870-477',
 | ||
|             'ext': 'mp4',
 | ||
|             'title': '【kson】感染メイズ!麻酔銃で無双する',
 | ||
|             'thumbnail': r're:^https?://.*\.(png|jpg)$',
 | ||
|             'timestamp': 1597759093.0,
 | ||
|             'uploader': 'kson組長(けいそん)',
 | ||
|             'duration': 4302.58,
 | ||
|             'uploader_id': '10882672',
 | ||
|             'description': 'このステージ絶対乗り越えたい',
 | ||
|             'upload_date': '20200818',
 | ||
|         },
 | ||
|     }, {
 | ||
|         'url': 'https://www.mildom.com/playback/10882672/10882672-buha9td2lrn97fk2jme0',
 | ||
|         'info_dict': {
 | ||
|             'id': '10882672-buha9td2lrn97fk2jme0',
 | ||
|             'ext': 'mp4',
 | ||
|             'title': '【kson組長】CART RACER!!!',
 | ||
|             'thumbnail': r're:^https?://.*\.(png|jpg)$',
 | ||
|             'uploader_id': '10882672',
 | ||
|             'uploader': 'kson組長(けいそん)',
 | ||
|             'upload_date': '20201104',
 | ||
|             'timestamp': 1604494797.0,
 | ||
|             'duration': 4657.25,
 | ||
|             'description': 'WTF',
 | ||
|         },
 | ||
|     }]
 | ||
| 
 | ||
|     def _real_extract(self, url):
 | ||
|         user_id, video_id = self._match_valid_url(url).group('user_id', 'id')
 | ||
|         webpage = self._download_webpage(f'https://www.mildom.com/playback/{user_id}/{video_id}', video_id)
 | ||
| 
 | ||
|         autoplay = self._call_api(
 | ||
|             'https://cloudac.mildom.com/nonolive/videocontent/playback/getPlaybackDetail', video_id,
 | ||
|             note='Downloading playback metadata', query={
 | ||
|                 'v_id': video_id,
 | ||
|             })['playback']
 | ||
| 
 | ||
|         formats = [{
 | ||
|             'url': autoplay['audio_url'],
 | ||
|             'format_id': 'audio',
 | ||
|             'protocol': 'm3u8_native',
 | ||
|             'vcodec': 'none',
 | ||
|             'acodec': 'aac',
 | ||
|             'ext': 'm4a'
 | ||
|         }]
 | ||
|         for fmt in autoplay['video_link']:
 | ||
|             formats.append({
 | ||
|                 'format_id': 'video-%s' % fmt['name'],
 | ||
|                 'url': fmt['url'],
 | ||
|                 'protocol': 'm3u8_native',
 | ||
|                 'width': fmt['level'] * autoplay['video_width'] // autoplay['video_height'],
 | ||
|                 'height': fmt['level'],
 | ||
|                 'vcodec': 'h264',
 | ||
|                 'acodec': 'aac',
 | ||
|                 'ext': 'mp4'
 | ||
|             })
 | ||
| 
 | ||
|         self._sort_formats(formats)
 | ||
| 
 | ||
|         return {
 | ||
|             'id': video_id,
 | ||
|             'title': self._html_search_meta(('og:description', 'description'), webpage, default=None) or autoplay.get('title'),
 | ||
|             'description': traverse_obj(autoplay, 'video_intro'),
 | ||
|             'timestamp': float_or_none(autoplay.get('publish_time'), scale=1000),
 | ||
|             'duration': float_or_none(autoplay.get('video_length'), scale=1000),
 | ||
|             'thumbnail': dict_get(autoplay, ('upload_pic', 'video_pic')),
 | ||
|             'uploader': traverse_obj(autoplay, ('author_info', 'login_name')),
 | ||
|             'uploader_id': user_id,
 | ||
|             'formats': formats,
 | ||
|         }
 | ||
| 
 | ||
| 
 | ||
| class MildomClipIE(MildomBaseIE):
 | ||
|     IE_NAME = 'mildom:clip'
 | ||
|     IE_DESC = 'Clip in Mildom'
 | ||
|     _VALID_URL = r'https?://(?:(?:www|m)\.)mildom\.com/clip/(?P<id>(?P<user_id>\d+)-[a-zA-Z0-9]+)'
 | ||
|     _TESTS = [{
 | ||
|         'url': 'https://www.mildom.com/clip/10042245-63921673e7b147ebb0806d42b5ba5ce9',
 | ||
|         'info_dict': {
 | ||
|             'id': '10042245-63921673e7b147ebb0806d42b5ba5ce9',
 | ||
|             'title': '全然違ったよ',
 | ||
|             'timestamp': 1619181890,
 | ||
|             'duration': 59,
 | ||
|             'thumbnail': r're:https?://.+',
 | ||
|             'uploader': 'ざきんぽ',
 | ||
|             'uploader_id': '10042245',
 | ||
|         },
 | ||
|     }, {
 | ||
|         'url': 'https://www.mildom.com/clip/10111524-ebf4036e5aa8411c99fb3a1ae0902864',
 | ||
|         'info_dict': {
 | ||
|             'id': '10111524-ebf4036e5aa8411c99fb3a1ae0902864',
 | ||
|             'title': 'かっこいい',
 | ||
|             'timestamp': 1621094003,
 | ||
|             'duration': 59,
 | ||
|             'thumbnail': r're:https?://.+',
 | ||
|             'uploader': '(ルーキー',
 | ||
|             'uploader_id': '10111524',
 | ||
|         },
 | ||
|     }, {
 | ||
|         'url': 'https://www.mildom.com/clip/10660174-2c539e6e277c4aaeb4b1fbe8d22cb902',
 | ||
|         'info_dict': {
 | ||
|             'id': '10660174-2c539e6e277c4aaeb4b1fbe8d22cb902',
 | ||
|             'title': 'あ',
 | ||
|             'timestamp': 1614769431,
 | ||
|             'duration': 31,
 | ||
|             'thumbnail': r're:https?://.+',
 | ||
|             'uploader': 'ドルゴルスレンギーン=ダグワドルジ',
 | ||
|             'uploader_id': '10660174',
 | ||
|         },
 | ||
|     }]
 | ||
| 
 | ||
|     def _real_extract(self, url):
 | ||
|         user_id, video_id = self._match_valid_url(url).group('user_id', 'id')
 | ||
|         webpage = self._download_webpage(f'https://www.mildom.com/clip/{video_id}', video_id)
 | ||
| 
 | ||
|         clip_detail = self._call_api(
 | ||
|             'https://cloudac-cf-jp.mildom.com/nonolive/videocontent/clip/detail', video_id,
 | ||
|             note='Downloading playback metadata', query={
 | ||
|                 'clip_id': video_id,
 | ||
|             })
 | ||
| 
 | ||
|         return {
 | ||
|             'id': video_id,
 | ||
|             'title': self._html_search_meta(
 | ||
|                 ('og:description', 'description'), webpage, default=None) or clip_detail.get('title'),
 | ||
|             'timestamp': float_or_none(clip_detail.get('create_time')),
 | ||
|             'duration': float_or_none(clip_detail.get('length')),
 | ||
|             'thumbnail': clip_detail.get('cover'),
 | ||
|             'uploader': traverse_obj(clip_detail, ('user_info', 'loginname')),
 | ||
|             'uploader_id': user_id,
 | ||
| 
 | ||
|             'url': clip_detail['url'],
 | ||
|             'ext': determine_ext(clip_detail.get('url'), 'mp4'),
 | ||
|         }
 | ||
| 
 | ||
| 
 | ||
| class MildomUserVodIE(MildomBaseIE):
 | ||
|     IE_NAME = 'mildom:user:vod'
 | ||
|     IE_DESC = 'Download all VODs from specific user in Mildom'
 | ||
|     _VALID_URL = r'https?://(?:(?:www|m)\.)mildom\.com/profile/(?P<id>\d+)'
 | ||
|     _TESTS = [{
 | ||
|         'url': 'https://www.mildom.com/profile/10093333',
 | ||
|         'info_dict': {
 | ||
|             'id': '10093333',
 | ||
|             'title': 'Uploads from ねこばたけ',
 | ||
|         },
 | ||
|         'playlist_mincount': 732,
 | ||
|     }, {
 | ||
|         'url': 'https://www.mildom.com/profile/10882672',
 | ||
|         'info_dict': {
 | ||
|             'id': '10882672',
 | ||
|             'title': 'Uploads from kson組長(けいそん)',
 | ||
|         },
 | ||
|         'playlist_mincount': 201,
 | ||
|     }]
 | ||
| 
 | ||
|     def _fetch_page(self, user_id, page):
 | ||
|         page += 1
 | ||
|         reply = self._call_api(
 | ||
|             'https://cloudac.mildom.com/nonolive/videocontent/profile/playbackList',
 | ||
|             user_id, note=f'Downloading page {page}', query={
 | ||
|                 'user_id': user_id,
 | ||
|                 'page': page,
 | ||
|                 'limit': '30',
 | ||
|             })
 | ||
|         if not reply:
 | ||
|             return
 | ||
|         for x in reply:
 | ||
|             v_id = x.get('v_id')
 | ||
|             if not v_id:
 | ||
|                 continue
 | ||
|             yield self.url_result(f'https://www.mildom.com/playback/{user_id}/{v_id}')
 | ||
| 
 | ||
|     def _real_extract(self, url):
 | ||
|         user_id = self._match_id(url)
 | ||
|         self.to_screen('This will download all VODs belonging to user. To download ongoing live video, use "https://www.mildom.com/%s" instead' % user_id)
 | ||
| 
 | ||
|         profile = self._call_api(
 | ||
|             'https://cloudac.mildom.com/nonolive/gappserv/user/profileV2', user_id,
 | ||
|             query={'user_id': user_id}, note='Downloading user profile')['user_info']
 | ||
| 
 | ||
|         return self.playlist_result(
 | ||
|             OnDemandPagedList(functools.partial(self._fetch_page, user_id), 30),
 | ||
|             user_id, f'Uploads from {profile["loginname"]}')
 |