1
0
mirror of https://github.com/yt-dlp/yt-dlp.git synced 2024-07-05 11:36:12 +02:00

Compare commits

..

1 Commits

Author SHA1 Message Date
hafeoz
f82613b329
Merge ed82d270d7 into f3411af12e 2024-06-25 16:56:26 +00:00

View File

@ -22,12 +22,12 @@
class NetEaseMusicBaseIE(InfoExtractor): class NetEaseMusicBaseIE(InfoExtractor):
_LEVELS = ('standard', 'exhigh', 'lossless', 'hires', 'jyeffect', 'sky', 'jymaster') _LEVELS = ['standard', 'exhigh', 'lossless', 'hires', 'jyeffect', 'sky', 'jymaster']
_API_BASE = 'http://music.163.com/api/' _API_BASE = 'http://music.163.com/api/'
_GEO_BYPASS = False _GEO_BYPASS = False
@staticmethod @staticmethod
def _kilo_or_none(value): def kilo_or_none(value):
return int_or_none(value, scale=1000) return int_or_none(value, scale=1000)
def _create_eapi_cipher(self, api_path, query_body, cookies): def _create_eapi_cipher(self, api_path, query_body, cookies):
@ -71,31 +71,36 @@ def _call_player_api(self, song_id, level):
'/song/enhance/player/url/v1', song_id, {'ids': f'[{song_id}]', 'level': level, 'encodeType': 'flac'}, '/song/enhance/player/url/v1', song_id, {'ids': f'[{song_id}]', 'level': level, 'encodeType': 'flac'},
note=f'Downloading song URL info: level {level}') note=f'Downloading song URL info: level {level}')
def _extract_formats(self, info): def extract_formats(self, info):
err = 0
formats = [] formats = []
song_id = info['id'] song_id = info['id']
for level in self._LEVELS: for song_level in self._LEVELS:
song = traverse_obj(self._call_player_api(song_id, level), ('data', 0, {dict})) or {} for song in traverse_obj(self._call_player_api(song_id, song_level), ('data', lambda _, v: url_or_none(v['url']))):
if song.get('level') != level: song_url = song['url']
break # We have already extracted the highest level the user has access to if self._is_valid_url(song_url, info['id'], 'song'):
if not url_or_none(song.get('url')): formats.append({
continue 'url': song_url,
formats.append({ 'format_id': song_level,
'url': song['url'], 'vcodec': 'none',
'format_id': level, **traverse_obj(song, {
'vcodec': 'none', 'ext': ('type', {str}),
**traverse_obj(song, { 'abr': ('br', {self.kilo_or_none}),
'ext': ('type', {str}), 'filesize': ('size', {int_or_none}),
'abr': ('br', {self._kilo_or_none}), }),
'filesize': ('size', {int_or_none}), })
}), elif err == 0:
}) err = traverse_obj(song, ('code', {int})) or 0
if not formats: if not formats:
self.raise_geo_restricted( if err != 0 and (err < 200 or err >= 400):
'No media links found; possibly due to geo restriction', countries=['CN']) raise ExtractorError(f'No media links found (site code {err})', expected=True)
else:
self.raise_geo_restricted(
'No media links found: probably due to geo restriction.', countries=['CN'])
return formats return formats
def _query_api(self, endpoint, video_id, note): def query_api(self, endpoint, video_id, note):
result = self._download_json( result = self._download_json(
f'{self._API_BASE}{endpoint}', video_id, note, headers={'Referer': self._API_BASE}) f'{self._API_BASE}{endpoint}', video_id, note, headers={'Referer': self._API_BASE})
code = traverse_obj(result, ('code', {int})) code = traverse_obj(result, ('code', {int}))
@ -248,12 +253,12 @@ def _process_lyrics(self, lyrics_info):
def _real_extract(self, url): def _real_extract(self, url):
song_id = self._match_id(url) song_id = self._match_id(url)
info = self._query_api( info = self.query_api(
f'song/detail?id={song_id}&ids=%5B{song_id}%5D', song_id, 'Downloading song info')['songs'][0] f'song/detail?id={song_id}&ids=%5B{song_id}%5D', song_id, 'Downloading song info')['songs'][0]
formats = self._extract_formats(info) formats = self.extract_formats(info)
lyrics = self._process_lyrics(self._query_api( lyrics = self._process_lyrics(self.query_api(
f'song/lyric?id={song_id}&lv=-1&tv=-1', song_id, 'Downloading lyrics data')) f'song/lyric?id={song_id}&lv=-1&tv=-1', song_id, 'Downloading lyrics data'))
lyric_data = { lyric_data = {
'description': traverse_obj(lyrics, (('lyrics_merged', 'lyrics'), 0, 'data'), get_all=False), 'description': traverse_obj(lyrics, (('lyrics_merged', 'lyrics'), 0, 'data'), get_all=False),
@ -269,9 +274,9 @@ def _real_extract(self, url):
**lyric_data, **lyric_data,
**traverse_obj(info, { **traverse_obj(info, {
'title': ('name', {str}), 'title': ('name', {str}),
'timestamp': ('album', 'publishTime', {self._kilo_or_none}), 'timestamp': ('album', 'publishTime', {self.kilo_or_none}),
'thumbnail': ('album', 'picUrl', {url_or_none}), 'thumbnail': ('album', 'picUrl', {url_or_none}),
'duration': ('duration', {self._kilo_or_none}), 'duration': ('duration', {self.kilo_or_none}),
'album': ('album', 'name', {str}), 'album': ('album', 'name', {str}),
'average_rating': ('score', {int_or_none}), 'average_rating': ('score', {int_or_none}),
}), }),
@ -355,7 +360,7 @@ class NetEaseMusicSingerIE(NetEaseMusicBaseIE):
def _real_extract(self, url): def _real_extract(self, url):
singer_id = self._match_id(url) singer_id = self._match_id(url)
info = self._query_api( info = self.query_api(
f'artist/{singer_id}?id={singer_id}', singer_id, note='Downloading singer data') f'artist/{singer_id}?id={singer_id}', singer_id, note='Downloading singer data')
name = join_nonempty( name = join_nonempty(
@ -423,7 +428,7 @@ def _real_extract(self, url):
'tags': ('tags', ..., {str}), 'tags': ('tags', ..., {str}),
'uploader': ('creator', 'nickname', {str}), 'uploader': ('creator', 'nickname', {str}),
'uploader_id': ('creator', 'userId', {str_or_none}), 'uploader_id': ('creator', 'userId', {str_or_none}),
'timestamp': ('updateTime', {self._kilo_or_none}), 'timestamp': ('updateTime', {self.kilo_or_none}),
})) }))
if traverse_obj(info, ('playlist', 'specialType')) == 10: if traverse_obj(info, ('playlist', 'specialType')) == 10:
metainfo['title'] = f'{metainfo.get("title")} {strftime_or_none(metainfo.get("timestamp"), "%Y-%m-%d")}' metainfo['title'] = f'{metainfo.get("title")} {strftime_or_none(metainfo.get("timestamp"), "%Y-%m-%d")}'
@ -470,7 +475,7 @@ class NetEaseMusicMvIE(NetEaseMusicBaseIE):
def _real_extract(self, url): def _real_extract(self, url):
mv_id = self._match_id(url) mv_id = self._match_id(url)
info = self._query_api( info = self.query_api(
f'mv/detail?id={mv_id}&type=mp4', mv_id, 'Downloading mv info')['data'] f'mv/detail?id={mv_id}&type=mp4', mv_id, 'Downloading mv info')['data']
formats = [ formats = [
@ -487,7 +492,7 @@ def _real_extract(self, url):
'creator': ('artistName', {str}), 'creator': ('artistName', {str}),
'upload_date': ('publishTime', {unified_strdate}), 'upload_date': ('publishTime', {unified_strdate}),
'thumbnail': ('cover', {url_or_none}), 'thumbnail': ('cover', {url_or_none}),
'duration': ('duration', {self._kilo_or_none}), 'duration': ('duration', {self.kilo_or_none}),
'view_count': ('playCount', {int_or_none}), 'view_count': ('playCount', {int_or_none}),
'like_count': ('likeCount', {int_or_none}), 'like_count': ('likeCount', {int_or_none}),
'comment_count': ('commentCount', {int_or_none}), 'comment_count': ('commentCount', {int_or_none}),
@ -547,7 +552,7 @@ class NetEaseMusicProgramIE(NetEaseMusicBaseIE):
def _real_extract(self, url): def _real_extract(self, url):
program_id = self._match_id(url) program_id = self._match_id(url)
info = self._query_api( info = self.query_api(
f'dj/program/detail?id={program_id}', program_id, note='Downloading program info')['program'] f'dj/program/detail?id={program_id}', program_id, note='Downloading program info')['program']
metainfo = traverse_obj(info, { metainfo = traverse_obj(info, {
@ -555,17 +560,17 @@ def _real_extract(self, url):
'description': ('description', {str}), 'description': ('description', {str}),
'creator': ('dj', 'brand', {str}), 'creator': ('dj', 'brand', {str}),
'thumbnail': ('coverUrl', {url_or_none}), 'thumbnail': ('coverUrl', {url_or_none}),
'timestamp': ('createTime', {self._kilo_or_none}), 'timestamp': ('createTime', {self.kilo_or_none}),
}) })
if not self._yes_playlist( if not self._yes_playlist(
info['songs'] and program_id, info['mainSong']['id'], playlist_label='program', video_label='song'): info['songs'] and program_id, info['mainSong']['id'], playlist_label='program', video_label='song'):
formats = self._extract_formats(info['mainSong']) formats = self.extract_formats(info['mainSong'])
return { return {
'id': str(info['mainSong']['id']), 'id': str(info['mainSong']['id']),
'formats': formats, 'formats': formats,
'duration': traverse_obj(info, ('mainSong', 'duration', {self._kilo_or_none})), 'duration': traverse_obj(info, ('mainSong', 'duration', {self.kilo_or_none})),
**metainfo, **metainfo,
} }
@ -594,7 +599,7 @@ def _real_extract(self, url):
metainfo = {} metainfo = {}
entries = [] entries = []
for offset in itertools.count(start=0, step=self._PAGE_SIZE): for offset in itertools.count(start=0, step=self._PAGE_SIZE):
info = self._query_api( info = self.query_api(
f'dj/program/byradio?asc=false&limit={self._PAGE_SIZE}&radioId={dj_id}&offset={offset}', f'dj/program/byradio?asc=false&limit={self._PAGE_SIZE}&radioId={dj_id}&offset={offset}',
dj_id, note=f'Downloading dj programs - {offset}') dj_id, note=f'Downloading dj programs - {offset}')