mirror of https://github.com/yt-dlp/yt-dlp.git
[ximalaya.com] Add VIP support
This commit is contained in:
parent
6f9e653743
commit
ed0433a69f
|
@ -1,7 +1,8 @@
|
|||
import math
|
||||
import time
|
||||
|
||||
from .common import InfoExtractor
|
||||
from ..utils import InAdvancePagedList, str_or_none, traverse_obj, try_call
|
||||
from ..utils import InAdvancePagedList, float_or_none, int_or_none, str_or_none, traverse_obj, try_call
|
||||
|
||||
|
||||
class XimalayaBaseIE(InfoExtractor):
|
||||
|
@ -71,8 +72,157 @@ class XimalayaIE(XimalayaBaseIE):
|
|||
'like_count': int,
|
||||
},
|
||||
},
|
||||
{
|
||||
# VIP-restricted audio
|
||||
'url': 'https://www.ximalaya.com/album/70349771',
|
||||
'only_matching': True,
|
||||
},
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def _decrypt_filename(audio_info):
|
||||
seed = float_or_none(audio_info['seed'])
|
||||
file_id = audio_info['fileId']
|
||||
cgstr = ''
|
||||
key = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/\\:._-1234567890'
|
||||
for _ in range(len(key)):
|
||||
j = 211 * seed + 30031
|
||||
seed = float_or_none(int_or_none(j) % 65536)
|
||||
ran = seed / float_or_none(65536)
|
||||
r = int_or_none(ran * float_or_none(len(key)))
|
||||
cgstr += key[r]
|
||||
key = key.replace(key[r], '')
|
||||
strs = file_id.split('*')
|
||||
filename = ''
|
||||
for n in range(len(strs) - 1):
|
||||
if strs[n] != '':
|
||||
index = int_or_none(strs[n])
|
||||
filename += cgstr[index]
|
||||
if filename[0] != '/':
|
||||
filename = '/' + filename
|
||||
return filename
|
||||
|
||||
@staticmethod
|
||||
def _decrypt_url_params(audio_info):
|
||||
def char_code_at(s: str, n: int) -> int:
|
||||
return ord(s[n]) if n < len(s) else 0
|
||||
|
||||
def decrypt(e: str, t: list) -> str:
|
||||
r = list(range(256))
|
||||
a = 0
|
||||
s = ''
|
||||
|
||||
for o in range(256):
|
||||
a = (a + r[o] + int_or_none(char_code_at(e, o % len(e)))) % 256
|
||||
r[o], r[a] = r[a], r[o]
|
||||
|
||||
a, o = 0, 0
|
||||
for u in range(len(t)):
|
||||
o = (o + 1) % 256
|
||||
a = (a + r[o]) % 256
|
||||
r[o], r[a] = r[a], r[o]
|
||||
s += chr(t[u] ^ r[(r[o] + r[a]) % 256])
|
||||
|
||||
return s
|
||||
|
||||
def decrypt2(key: str, key2: list) -> str:
|
||||
n = []
|
||||
for r in range(len(key)):
|
||||
a = ord('a')
|
||||
if ord('a') <= ord(key[r]) <= ord('z'):
|
||||
a = ord(key[r]) - 97
|
||||
else:
|
||||
a = ord(key[r]) - 48 + 26
|
||||
|
||||
for i in range(36):
|
||||
if key2[i] == a:
|
||||
a = i
|
||||
break
|
||||
|
||||
if a > 25:
|
||||
n.append(chr(a - 26 + 48))
|
||||
else:
|
||||
n.append(chr(a + 97))
|
||||
|
||||
return ''.join(n)
|
||||
|
||||
def decrypt3(s: str) -> list:
|
||||
t = 0
|
||||
n = 0
|
||||
r = 0
|
||||
s_len = len(s)
|
||||
i = []
|
||||
o = [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1,
|
||||
-1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
|
||||
17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30,
|
||||
31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
|
||||
51, -1, -1, -1, -1, -1]
|
||||
|
||||
while r < s_len:
|
||||
t = o[char_code_at(s, r) & 255]
|
||||
r += 1
|
||||
while r < s_len and t == -1:
|
||||
t = o[char_code_at(s, r) & 255]
|
||||
r += 1
|
||||
if t == -1:
|
||||
break
|
||||
|
||||
n = o[char_code_at(s, r) & 255]
|
||||
r += 1
|
||||
while r < s_len and n == -1:
|
||||
n = o[char_code_at(s, r) & 255]
|
||||
r += 1
|
||||
if t == -1:
|
||||
break
|
||||
|
||||
i.append((t << 2) | ((n & 48) >> 4))
|
||||
|
||||
t = int_or_none(char_code_at(s, r)) & 255
|
||||
r += 1
|
||||
if t == 61:
|
||||
return i
|
||||
t = o[t]
|
||||
while r < s_len and t == -1:
|
||||
t = int_or_none(char_code_at(s, r)) & 255
|
||||
r += 1
|
||||
if t == 61:
|
||||
return i
|
||||
t = o[t]
|
||||
if t == -1:
|
||||
break
|
||||
|
||||
i.append(((n & 15) << 4) | ((t & 60) >> 2))
|
||||
|
||||
n = int_or_none(char_code_at(s, r)) & 255
|
||||
r += 1
|
||||
if n == 61:
|
||||
return i
|
||||
n = o[n]
|
||||
while r < s_len and n == -1:
|
||||
n = int_or_none(char_code_at(s, r)) & 255
|
||||
r += 1
|
||||
if n == 61:
|
||||
return i
|
||||
n = o[n]
|
||||
if n == -1:
|
||||
break
|
||||
i.append(((t & 3) << 6) | n)
|
||||
|
||||
return i
|
||||
|
||||
o = 'g3utf1k6yxdwi0'
|
||||
u = [19, 1, 4, 7, 30, 14, 28, 8, 24, 17, 6, 35, 34, 16, 9, 10, 13, 22,
|
||||
32, 29, 31, 21, 18, 3, 2, 23, 25, 27, 11, 20, 5, 15, 12, 0, 33, 26]
|
||||
s1 = decrypt3(audio_info['ep'])
|
||||
s2 = decrypt(decrypt2('d' + o + '9', u), s1)
|
||||
ss = s2.split('-')
|
||||
sign = ss[1]
|
||||
token = ss[2]
|
||||
timestamp = ss[3]
|
||||
return sign, token, timestamp
|
||||
|
||||
def _real_extract(self, url):
|
||||
scheme = 'https' if url.startswith('https') else 'http'
|
||||
|
||||
|
@ -82,6 +232,24 @@ class XimalayaIE(XimalayaBaseIE):
|
|||
audio_info_file, audio_id,
|
||||
f'Downloading info json {audio_info_file}', 'Unable to download info file')
|
||||
|
||||
# NOTE(xcsong): VIP-restricted audio
|
||||
if audio_info.get('is_paid', False):
|
||||
ts = int_or_none(time.time())
|
||||
audio_info_file = f'{scheme}://mpay.ximalaya.com/mobile/track/pay/{audio_id}/{ts}?device=pc&isBackend=true&_={ts}'
|
||||
audio_info = self._download_json(
|
||||
audio_info_file, audio_id,
|
||||
f'Downloading VIP info json {audio_info_file}', 'Unable to download VIP info file')
|
||||
filename = self._decrypt_filename(audio_info)
|
||||
sign, token, timestamp = self._decrypt_url_params(audio_info)
|
||||
buy_key = audio_info.get('buyKey')
|
||||
duration = audio_info.get('duration')
|
||||
domain = audio_info.get('domain')
|
||||
api_version = audio_info.get('apiVersion')
|
||||
args = f'?sign={sign}&buy_key={buy_key}&token={token}×tamp={timestamp}&duration={duration}'
|
||||
audio_info['play_path_64'] = f'{domain}/download/{api_version}{filename}{args}'
|
||||
if '_preview_' in audio_info['play_path_64']:
|
||||
self.report_warning('Please use correct cookies to download VIP audios!')
|
||||
|
||||
formats = [{
|
||||
'format_id': f'{bps}k',
|
||||
'url': audio_info[k],
|
||||
|
|
Loading…
Reference in New Issue