Files
TVBoxSE/TVBox_PY/spider_aliyundrive.py
T
2022-12-02 13:36:22 +08:00

427 lines
15 KiB
Python

from spider import Spider, SpiderEpisode
from cache import get_cache, set_cache
from proxy import get_proxy_func_url, ProxyFuncResult
from downloader import get_download_url
import requests
import re
import json
import time
import xbmcaddon
_ADDON = xbmcaddon.Addon()
base_headers = {
'User-Agent':
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.54 Safari/537.36',
'Referer': 'https://www.aliyundrive.com/',
}
class SpiderAliyunDrive(Spider):
setting_key_refresh_token = 'aliyundrive_refresh_token'
setting_key_display_file_size_switch = 'aliyundrive_display_file_size_switch'
setting_key_downloder_switch = 'downloader_switch'
regex_share_id = re.compile(
r'www.aliyundrive.com\/s\/([^\/]+)(\/folder\/([^\/]+))?')
cache = {}
def name(self):
return '阿里云盘'
def is_searchable(self):
return False
def hide(self):
return True
def list_categories(self):
return []
def list_videos(self, category_id, page):
return [], False
def list_episodes(self, video_id):
m = self.regex_share_id.search(video_id)
share_id = m.group(1)
file_id = m.group(3)
r = requests.post(
'https://api.aliyundrive.com/adrive/v3/share_link/get_share_by_anonymous',
json={'share_id': share_id})
share_info = r.json()
if len(share_info['file_infos']) == 0:
return []
file_info = None
if file_id:
for fi in share_info['file_infos']:
if fi['file_id'] == file_id:
file_info = fi
break
if file_info is None:
return []
else:
file_info = share_info['file_infos'][0]
file_id = file_info['file_id']
parent_file_id = None
if file_info['type'] == 'folder':
parent_file_id = file_id
elif file_info['type'] == 'file' and file_info['category'] == 'video':
parent_file_id = 'root'
else:
return []
share_token = self._get_share_token(share_id)
video_file_infos = []
subtitle_file_infos = []
self._list_files(video_file_infos, subtitle_file_infos, share_id,
share_token, parent_file_id)
video_file_infos.sort(key=lambda x: x['name'])
subtitles = []
for file_info in subtitle_file_infos:
subtitles.append({
'name':
file_info['name'],
'url':
get_proxy_func_url(
SpiderAliyunDrive.__name__,
self.proxy_download_url.__name__,
{
'share_id': file_info['share_id'],
'file_id': file_info['file_id'],
'drive_id': file_info['drive_id'],
},
)
})
episodes = []
display_file_size = _ADDON.getSettingBool(
self.setting_key_display_file_size_switch)
for file_info in video_file_infos:
if display_file_size:
name = '[{}] {}'.format(
self._sizeof_fmt(file_info['size']),
file_info['name'],
)
else:
name = file_info['name']
sources = [{
'name': '原画',
'params': {
'template_id': '',
'share_id': file_info['share_id'],
'file_id': file_info['file_id'],
'drive_id': file_info['drive_id'],
},
}, {
'name': '超高清',
'params': {
'template_id': 'FHD',
'share_id': file_info['share_id'],
'file_id': file_info['file_id'],
'drive_id': file_info['drive_id'],
},
}, {
'name': '高清',
'params': {
'template_id': 'HD',
'share_id': file_info['share_id'],
'file_id': file_info['file_id'],
'drive_id': file_info['drive_id'],
},
}, {
'name': '标清',
'params': {
'template_id': 'SD',
'share_id': file_info['share_id'],
'file_id': file_info['file_id'],
'drive_id': file_info['drive_id'],
},
}]
episodes.append(
SpiderEpisode(
name=name,
sources=sources,
cover=share_info['avatar'],
description=video_id,
subtitles=subtitles,
))
return episodes
def resolve_play_url(self, episode_params):
if len(episode_params['template_id']) == 0:
downloader_switch = _ADDON.getSettingBool(
self.setting_key_downloder_switch)
if downloader_switch:
return get_download_url(
spider_class=SpiderAliyunDrive.__name__,
func_name=self.proxy_download_url.__name__,
params=episode_params)
else:
return get_proxy_func_url(SpiderAliyunDrive.__name__,
self.proxy_download_url.__name__,
episode_params)
else:
return get_proxy_func_url(SpiderAliyunDrive.__name__,
self.proxy_preview_m3u8.__name__,
episode_params)
def search_videos(self, keyword):
return []
def _get_refresh_token(self):
token_or_url = _ADDON.getSettingString(self.setting_key_refresh_token)
if token_or_url.startswith('http://') or token_or_url.startswith(
'https://'):
return requests.get(token_or_url).text.strip()
else:
return token_or_url
def _get_access_token(self):
key = 'aliyundrive:access_token'
data = self._get_cache(key)
if data:
return data['access_token']
r = requests.post('https://api.aliyundrive.com/token/refresh',
json={
'refresh_token': self._get_refresh_token(),
})
data = r.json()
access_token = '{} {}'.format(data['token_type'], data['access_token'])
expires_at = int(time.time()) + int(data['expires_in'] / 2)
self._set_cache(key, {
'access_token': access_token,
'expires_at': expires_at
})
return access_token
def _get_share_token(self, share_id, share_pwd=''):
key = 'aliyundrive:share_token'
data = self._get_cache(key)
if data:
if data['share_id'] == share_id and data['share_pwd'] == share_pwd:
return data['share_token']
r = requests.post(
'https://api.aliyundrive.com/v2/share_link/get_share_token',
json={
'share_id': share_id,
'share_pwd': share_pwd
})
data = r.json()
share_token = data['share_token']
expires_at = int(time.time()) + int(data['expires_in'] / 2)
self._set_cache(
key, {
'share_token': share_token,
'expires_at': expires_at,
'share_id': share_id,
'share_pwd': share_pwd
})
return share_token
def _list_files(self, video_file_infos, subtitle_file_infos, share_id,
share_token, parent_file_id):
marker = ''
headers = base_headers.copy()
headers['x-share-token'] = share_token
for page in range(1, 51):
if page >= 2 and len(marker) == 0:
break
r = requests.post(
'https://api.aliyundrive.com/adrive/v3/file/list',
json={
"image_thumbnail_process":
"image/resize,w_160/format,jpeg",
"image_url_process": "image/resize,w_1920/format,jpeg",
"limit": 200,
"order_by": "updated_at",
"order_direction": "DESC",
"parent_file_id": parent_file_id,
"share_id": share_id,
"video_thumbnail_process":
"video/snapshot,t_1000,f_jpg,ar_auto,w_300",
'marker': marker,
},
headers=headers)
data = r.json()
for item in data['items']:
if item['type'] == 'folder':
self._list_files(video_file_infos, subtitle_file_infos,
share_id, share_token, item['file_id'])
elif item['type'] == 'file' and item['category'] == 'video':
video_file_infos.append(item)
elif item['type'] == 'file' and item['file_extension'] in [
'srt', 'ass', 'nfo', 'vtt'
]:
subtitle_file_infos.append(item)
marker = data['next_marker']
def _get_m3u8_cache(self, share_id, file_id, template_id):
key = 'aliyundrive:m3u8'
data = self._get_cache(key)
if data:
if data['share_id'] == share_id and data[
'file_id'] == file_id and data[
'template_id'] == template_id:
return data['m3u8'], data['media_urls']
access_token = self._get_access_token()
share_token = self._get_share_token(share_id)
headers = base_headers.copy()
headers['x-share-token'] = share_token
headers['Authorization'] = access_token
r = requests.post(
'https://api.aliyundrive.com/v2/file/get_share_link_video_preview_play_info',
json={
'share_id': share_id,
'category': 'live_transcoding',
'file_id': file_id,
'template_id': '',
},
headers=headers,
)
preview_url = ''
for t in r.json(
)['video_preview_play_info']['live_transcoding_task_list']:
if t['template_id'] == template_id:
preview_url = t['url']
break
r = requests.get(preview_url,
headers=base_headers.copy(),
allow_redirects=False)
preview_url = r.headers['Location']
lines = []
media_urls = []
r = requests.get(preview_url, headers=base_headers.copy(), stream=True)
media_id = 0
for line in r.iter_lines():
line = line.decode()
if 'x-oss-expires' in line:
media_url = preview_url[:preview_url.rindex('/') + 1] + line
media_urls.append(media_url)
line = get_proxy_func_url(
SpiderAliyunDrive.__name__,
self.proxy_preview_media.__name__, {
'share_id': share_id,
'file_id': file_id,
'template_id': template_id,
'media_id': media_id
})
media_id += 1
lines.append(line)
m3u8 = '\n'.join(lines)
self._set_cache(
key, {
'share_id': share_id,
'file_id': file_id,
'template_id': template_id,
'm3u8': m3u8,
'media_urls': media_urls,
'expires_at': int(time.time()) + 300,
})
return m3u8, media_urls
def proxy_preview_m3u8(self, params):
share_id = params['share_id']
file_id = params['file_id']
template_id = params['template_id']
m3u8, _ = self._get_m3u8_cache(share_id, file_id, template_id)
return ProxyFuncResult(
body=m3u8,
headers={'Content-Type': 'application/vnd.apple.mpegurl'})
def proxy_preview_media(self, params):
share_id = params['share_id']
file_id = params['file_id']
template_id = params['template_id']
media_id = params['media_id']
_, media_urls = self._get_m3u8_cache(share_id, file_id, template_id)
media_url = media_urls[media_id]
return ProxyFuncResult(url=media_url, headers=base_headers.copy())
def proxy_download_url(self, params):
share_id = params['share_id']
file_id = params['file_id']
key = 'aliyundrive:download_url:{}:{}'.format(share_id, file_id)
data = self._get_cache(key)
if data:
return ProxyFuncResult(url=data['download_url'],
headers=base_headers.copy())
access_token = self._get_access_token()
share_token = self._get_share_token(share_id)
headers = base_headers.copy()
headers['x-share-token'] = share_token
headers['Authorization'] = access_token
r = requests.post(
'https://api.aliyundrive.com/v2/file/get_share_link_download_url',
json={
'share_id': share_id,
'file_id': file_id,
'expires_sec': 7200,
},
headers=headers)
data = r.json()
r = requests.get(data['download_url'],
headers=base_headers.copy(),
allow_redirects=False)
download_url = r.headers['Location']
self._set_cache(
key, {
'download_url': download_url,
'expires_at': int(time.time()) + 300,
'share_id': share_id,
'file_id': file_id,
})
return ProxyFuncResult(url=download_url, headers=base_headers.copy())
def _sizeof_fmt(self, num, suffix="B"):
for unit in ["", "K", "M", "G", "T", "P", "E", "Z"]:
if num < 1024.0:
return f"{num:3.1f} {unit}{suffix}"
num /= 1024.0
return f"{num:.1f}Yi{suffix}"
def _get_cache(self, key):
if key in self.cache:
data = self.cache[key]
if data['expires_at'] >= int(time.time()):
return data
data = get_cache(key)
if data:
data = json.loads(data)
if data['expires_at'] >= int(time.time()):
return data
return None
def _set_cache(self, key, value):
set_cache(key, json.dumps(value))
self.cache[key] = value