mirror of
https://github.com/yt-dlp/yt-dlp.git
synced 2024-11-16 16:13:35 +01:00
Compare commits
No commits in common. "ff028f2ac40aa65184646d93a0017f25304c0318" and "ed82d270d7a730943de5302b89de289180f14062" have entirely different histories.
ff028f2ac4
...
ed82d270d7
@ -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}')
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user