py_ali.py 63 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228
  1. # coding=utf-8
  2. # !/usr/bin/python
  3. import sys
  4. import time
  5. import json
  6. import hashlib
  7. from base64 import b64decode
  8. from Crypto.Cipher import AES
  9. from difflib import SequenceMatcher
  10. from collections import OrderedDict
  11. from urllib.parse import quote, unquote
  12. from concurrent.futures import ThreadPoolExecutor, as_completed
  13. sys.path.append('..')
  14. from base.spider import Spider
  15. class Spider(Spider):
  16. fileidList = []
  17. shareidList = []
  18. header = {
  19. "Referer": "https://www.aliyundrive.com/",
  20. "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"
  21. }
  22. def getName(self):
  23. return "首页"
  24. def init(self, extend):
  25. try:
  26. self.extendDict = json.loads(extend)
  27. except:
  28. self.extendDict = {}
  29. def isVideoFormat(self, url):
  30. pass
  31. def manualVideoCheck(self):
  32. pass
  33. def homeVideoContent(self):
  34. from re import sub
  35. videos = []
  36. header = {
  37. 'Host': 'frodo.douban.com', 'Connection': 'Keep-Alive',
  38. 'Referer': 'https://servicewechat.com/wx2f9b06c1de1ccfca/84/page-frame.html',
  39. 'Content-Type': 'application/json',
  40. 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36 MicroMessenger/7.0.9.501 NetType/WIFI MiniProgramEnv/Windows WindowsWechat'}
  41. url = 'https://frodo.douban.com/api/v2/subject_collection/subject_real_time_hotest/items?start=0&count=30&apikey=0ac44ae016490db2204ce0a042db2916'
  42. try:
  43. vodList = self.fetch(url, headers=header, verify=False).json()['subject_collection_items']
  44. for vod in vodList:
  45. remark = vod['rating']['value']
  46. if remark != '':
  47. remark = '{}分'.format(remark)
  48. else:
  49. remark = '暂无评分'
  50. videos.append({
  51. "vod_db_id": vod['id'],
  52. "vod_name": vod['title'],
  53. "vod_pic": sub(r'photo/(.*?)/', 'photo/l/', vod['pic']['large']) + '@User-Agent=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36@Referer=https://www.douban.com/',
  54. "vod_remarks": remark
  55. })
  56. except:
  57. pass
  58. result = {'list': videos}
  59. return result
  60. def homeContent(self, filter):
  61. result = {}
  62. result['class'] = [{'type_id': 'hot_gaia', 'type_name': '热门电影'}, {'type_id': 'tv_hot', 'type_name': '热播剧集'}, {'type_id': 'show_hot', 'type_name': '热播综艺'}, {'type_id': 'movie', 'type_name': '电影筛选'}, {'type_id': 'tv', 'type_name': '电视筛选'}, {'type_id': 'rank_list_movie', 'type_name': '电影榜单'}, {'type_id': 'rank_list_tv', 'type_name': '电视榜单'}]
  63. if filter:
  64. from datetime import datetime
  65. currentYear = datetime.now().year
  66. result['filters'] = {'hot_gaia': [{'key': 'sort', 'name': '排序', 'value': [{'n': '热度', 'v': 'recommend'}, {'n': '最新', 'v': 'time'}, {'n': '评分', 'v': 'rank'}]}, {'key': 'area', 'name': '地区', 'value': [{'n': '全部', 'v': '全部'}, {'n': '华语', 'v': '华语'}, {'n': '欧美', 'v': '欧美'}, {'n': '韩国', 'v': '韩国'}, {'n': '日本', 'v': '日本'}]}], 'tv_hot': [{'key': 'type', 'name': '分类', 'value': [{'n': '综合', 'v': 'tv_hot'}, {'n': '国产剧', 'v': 'tv_domestic'}, {'n': '欧美剧', 'v': 'tv_american'}, {'n': '日剧', 'v': 'tv_japanese'}, {'n': '韩剧', 'v': 'tv_korean'}, {'n': '动画', 'v': 'tv_animation'}]}], 'show_hot': [{'key': 'type', 'name': '分类', 'value': [{'n': '综合', 'v': 'show_hot'}, {'n': '国内', 'v': 'show_domestic'}, {'n': '国外', 'v': 'show_foreign'}]}], 'movie': [{'key': '类型', 'name': '类型', 'value': [{'n': '全部类型', 'v': ''}, {'n': '喜剧', 'v': '喜剧'}, {'n': '爱情', 'v': '爱情'}, {'n': '动作', 'v': '动作'}, {'n': '科幻', 'v': '科幻'}, {'n': '动画', 'v': '动画'}, {'n': '悬疑', 'v': '悬疑'}, {'n': '犯罪', 'v': '犯罪'}, {'n': '惊悚', 'v': '惊悚'}, {'n': '冒险', 'v': '冒险'}, {'n': '音乐', 'v': '音乐'}, {'n': '历史', 'v': '历史'}, {'n': '奇幻', 'v': '奇幻'}, {'n': '恐怖', 'v': '恐怖'}, {'n': '战争', 'v': '战争'}, {'n': '传记', 'v': '传记'}, {'n': '歌舞', 'v': '歌舞'}, {'n': '武侠', 'v': '武侠'}, {'n': '情色', 'v': '情色'}, {'n': '灾难', 'v': '灾难'}, {'n': '西部', 'v': '西部'}, {'n': '纪录片', 'v': '纪录片'}, {'n': '短片', 'v': '短片'}]}, {'key': '地区', 'name': '地区', 'value': [{'n': '全部地区', 'v': ''}, {'n': '华语', 'v': '华语'}, {'n': '欧美', 'v': '欧美'}, {'n': '韩国', 'v': '韩国'}, {'n': '日本', 'v': '日本'}, {'n': '中国大陆', 'v': '中国大陆'}, {'n': '美国', 'v': '美国'}, {'n': '中国香港', 'v': '中国香港'}, {'n': '中国台湾', 'v': '中国台湾'}, {'n': '英国', 'v': '英国'}, {'n': '法国', 'v': '法国'}, {'n': '德国', 'v': '德国'}, {'n': '意大利', 'v': '意大利'}, {'n': '西班牙', 'v': '西班牙'}, {'n': '印度', 'v': '印度'}, {'n': '泰国', 'v': '泰国'}, {'n': '俄罗斯', 'v': '俄罗斯'}, {'n': '加拿大', 'v': '加拿大'}, {'n': '澳大利亚', 'v': '澳大利亚'}, {'n': '爱尔兰', 'v': '爱尔兰'}, {'n': '瑞典', 'v': '瑞典'}, {'n': '巴西', 'v': '巴西'}, {'n': '丹麦', 'v': '丹麦'}]}, {'key': 'sort', 'name': '排序', 'value': [{'n': '近期热度', 'v': 'T'}, {'n': '首映时间', 'v': 'R'}, {'n': '高分优先', 'v': 'S'}]}, {'key': '年代', 'name': '年代', 'value': [{'n': '全部年代', 'v': ''}, {'n': '2020年代', 'v': '2020年代'}, {'n': '2022', 'v': '2022'}, {'n': '2021', 'v': '2021'}, {'n': '2020', 'v': '2020'}, {'n': '2019', 'v': '2019'}, {'n': '2010年代', 'v': '2010年代'}, {'n': '2000年代', 'v': '2000年代'}, {'n': '90年代', 'v': '90年代'}, {'n': '80年代', 'v': '80年代'}, {'n': '70年代', 'v': '70年代'}, {'n': '60年代', 'v': '60年代'}, {'n': '更早', 'v': '更早'}]}], 'tv': [{'key': '类型', 'name': '类型', 'value': [{'n': '不限', 'v': ''}, {'n': '电视剧', 'v': '电视剧'}, {'n': '综艺', 'v': '综艺'}]}, {'key': '电视剧形式', 'name': '电视剧形式', 'value': [{'n': '不限', 'v': ''}, {'n': '喜剧', 'v': '喜剧'}, {'n': '爱情', 'v': '爱情'}, {'n': '悬疑', 'v': '悬疑'}, {'n': '动画', 'v': '动画'}, {'n': '武侠', 'v': '武侠'}, {'n': '古装', 'v': '古装'}, {'n': '家庭', 'v': '家庭'}, {'n': '犯罪', 'v': '犯罪'}, {'n': '科幻', 'v': '科幻'}, {'n': '恐怖', 'v': '恐怖'}, {'n': '历史', 'v': '历史'}, {'n': '战争', 'v': '战争'}, {'n': '动作', 'v': '动作'}, {'n': '冒险', 'v': '冒险'}, {'n': '传记', 'v': '传记'}, {'n': '剧情', 'v': '剧情'}, {'n': '奇幻', 'v': '奇幻'}, {'n': '惊悚', 'v': '惊悚'}, {'n': '灾难', 'v': '灾难'}, {'n': '歌舞', 'v': '歌舞'}, {'n': '音乐', 'v': '音乐'}]}, {'key': '综艺形式', 'name': '综艺形式', 'value': [{'n': '不限', 'v': ''}, {'n': '真人秀', 'v': '真人秀'}, {'n': '脱口秀', 'v': '脱口秀'}, {'n': '音乐', 'v': '音乐'}, {'n': '歌舞', 'v': '歌舞'}]}, {'key': '地区', 'name': '地区', 'value': [{'n': '全部地区', 'v': ''}, {'n': '华语', 'v': '华语'}, {'n': '欧美', 'v': '欧美'}, {'n': '国外', 'v': '国外'}, {'n': '韩国', 'v': '韩国'}, {'n': '日本', 'v': '日本'}, {'n': '中国大陆', 'v': '中国大陆'}, {'n': '中国香港', 'v': '中国香港'}, {'n': '美国', 'v': '美国'}, {'n': '英国', 'v': '英国'}, {'n': '泰国', 'v': '泰国'}, {'n': '中国台湾', 'v': '中国台湾'}, {'n': '意大利', 'v': '意大利'}, {'n': '法国', 'v': '法国'}, {'n': '德国', 'v': '德国'}, {'n': '西班牙', 'v': '西班牙'}, {'n': '俄罗斯', 'v': '俄罗斯'}, {'n': '瑞典', 'v': '瑞典'}, {'n': '巴西', 'v': '巴西'}, {'n': '丹麦', 'v': '丹麦'}, {'n': '印度', 'v': '印度'}, {'n': '加拿大', 'v': '加拿大'}, {'n': '爱尔兰', 'v': '爱尔兰'}, {'n': '澳大利亚', 'v': '澳大利亚'}]}, {'key': 'sort', 'name': '排序', 'value': [{'n': '近期热度', 'v': 'T'}, {'n': '首播时间', 'v': 'R'}, {'n': '高分优先', 'v': 'S'}]}, {'key': '年代', 'name': '年代', 'value': [{'n': '全部', 'v': ''}, {'n': '2020年代', 'v': '2020年代'}, {'n': '2022', 'v': '2022'}, {'n': '2021', 'v': '2021'}, {'n': '2020', 'v': '2020'}, {'n': '2019', 'v': '2019'}, {'n': '2010年代', 'v': '2010年代'}, {'n': '2000年代', 'v': '2000年代'}, {'n': '90年代', 'v': '90年代'}, {'n': '80年代', 'v': '80年代'}, {'n': '70年代', 'v': '70年代'}, {'n': '60年代', 'v': '60年代'}, {'n': '更早', 'v': '更早'}]}, {'key': '平台', 'name': '平台', 'value': [{'n': '全部', 'v': ''}, {'n': '腾讯视频', 'v': '腾讯视频'}, {'n': '爱奇艺', 'v': '爱奇艺'}, {'n': '优酷', 'v': '优酷'}, {'n': '湖南卫视', 'v': '湖南卫视'}, {'n': 'Netflix', 'v': 'Netflix'}, {'n': 'HBO', 'v': 'HBO'}, {'n': 'BBC', 'v': 'BBC'}, {'n': 'NHK', 'v': 'NHK'}, {'n': 'CBS', 'v': 'CBS'}, {'n': 'NBC', 'v': 'NBC'}, {'n': 'tvN', 'v': 'tvN'}]}], 'rank_list_movie': [{'key': '榜单', 'name': '榜单', 'value': [{'n': '实时热门电影', 'v': 'movie_real_time_hotest'}, {'n': '一周口碑电影榜', 'v': 'movie_weekly_best'}, {'n': '豆瓣电影Top250', 'v': 'movie_top250'}]}], 'rank_list_tv': [{'key': '榜单', 'name': '榜单', 'value': [{'n': '实时热门电视', 'v': 'tv_real_time_hotest'}, {'n': '华语口碑剧集榜', 'v': 'tv_chinese_best_weekly'}, {'n': '全球口碑剧集榜', 'v': 'tv_global_best_weekly'}, {'n': '国内口碑综艺榜', 'v': 'show_chinese_best_weekly'}, {'n': '国外口碑综艺榜', 'v': 'show_global_best_weekly'}]}]}
  67. maxYear = float('-inf')
  68. for tv in result['filters']['tv']:
  69. if tv['key'] == '年代':
  70. for item in tv['value']:
  71. v = item['v']
  72. if v.isnumeric():
  73. numericValue = int(v)
  74. maxYear = max(maxYear, numericValue)
  75. for year in range(currentYear, 0, -1):
  76. if year > maxYear:
  77. pos = tv['value'].index({'n': str(maxYear), 'v': str(maxYear)})
  78. tv['value'].insert(pos, {'n': str(year), 'v': str(year)})
  79. else:
  80. break
  81. break
  82. for movie in result['filters']['movie']:
  83. if movie['key'] == '年代':
  84. for item in movie['value']:
  85. v = item['v']
  86. if v.isnumeric():
  87. numericValue = int(v)
  88. maxYear = max(maxYear, numericValue)
  89. for year in range(currentYear, 0, -1):
  90. if year > maxYear:
  91. pos = movie['value'].index({'n': str(maxYear), 'v': str(maxYear)})
  92. movie['value'].insert(pos, {'n': str(year), 'v': str(year)})
  93. else:
  94. break
  95. break
  96. return result
  97. def categoryContent(self, cid, page, filter, ext):
  98. from re import sub
  99. page = int(page)
  100. result = {}
  101. videos = []
  102. header = {
  103. 'Content-Type': 'application/json',
  104. 'Host': 'frodo.douban.com', 'Connection': 'Keep-Alive',
  105. 'Referer': 'https://servicewechat.com/wx2f9b06c1de1ccfca/84/page-frame.html',
  106. 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36 MicroMessenger/7.0.9.501 NetType/WIFI MiniProgramEnv/Windows WindowsWechat'}
  107. if cid == 'hot_gaia':
  108. if 'area' in ext.keys():
  109. area = ext['area']
  110. else:
  111. area = '全部'
  112. if 'sort' in ext.keys():
  113. sort = ext['sort']
  114. else:
  115. sort = 'recommend'
  116. params = {'area': area, 'sort': sort, 'start': ((int(page) - 1) * 30), 'count': 30, 'apikey': '0ac44ae016490db2204ce0a042db2916'}
  117. url = f'https://frodo.douban.com/api/v2/movie/{cid}?'
  118. for key in params:
  119. url += f'&{key}={params[key]}'
  120. append = 'items'
  121. elif cid == 'tv_hot' or cid == 'show_hot':
  122. if 'type' in ext.keys():
  123. cid = ext['type']
  124. url = f'https://frodo.douban.com/api/v2/subject_collection/{cid}/items?'
  125. params = {'start': (int(page) - 1) * 30, 'count': 30, 'apikey': '0ac44ae016490db2204ce0a042db2916'}
  126. for key in params:
  127. url += f'&{key}={params[key]}'
  128. append = 'subject_collection_items'
  129. elif cid == 'tv' or cid == 'movie':
  130. tags = ''
  131. tagsList = []
  132. if '类型' in ext.keys():
  133. movieType = ext['类型']
  134. else:
  135. movieType = ''
  136. if '地区' in ext.keys():
  137. area = ext['地区']
  138. else:
  139. area = ''
  140. if 'sort' in ext.keys():
  141. sort = ext['sort']
  142. else:
  143. sort = 'T'
  144. selectedCategories = {"类型": movieType, "地区": area}
  145. for key in ext:
  146. if '形式' in key:
  147. selectedCategories.update({key: ext[key]})
  148. if key == 'sort':
  149. continue
  150. tagsList.append(ext[key])
  151. tagsList = [item for item in tagsList if item != '']
  152. if len(tagsList) == 1:
  153. tags = tagsList[0]
  154. elif len(tagsList) > 1:
  155. tags = json.dumps(tagsList, ensure_ascii=False)
  156. url = f'https://frodo.douban.com/api/v2/{cid}/recommend?'
  157. params = {'tags': tags, 'sort': sort, 'refresh': 0, 'selected_categories': json.dumps(selectedCategories, ensure_ascii=False), 'start': (int(page) - 1) * 30, 'count': 30, 'apikey': '0ac44ae016490db2204ce0a042db2916'}
  158. for key in params:
  159. url += f'&{key}={params[key]}'
  160. append = 'items'
  161. else:
  162. if '榜单' in ext.keys():
  163. cid = ext['榜单']
  164. else:
  165. cid = cid.split('_')[2] + '_real_time_hotest'
  166. url = f'https://frodo.douban.com/api/v2/subject_collection/{cid}/items?'
  167. params = {'start': ((int(page) - 1) * 30), 'count': 30, 'apikey': '0ac44ae016490db2204ce0a042db2916'}
  168. for key in params:
  169. url += f'&{key}={params[key]}'
  170. append = 'subject_collection_items'
  171. data = self.fetch(url, headers=header, verify=False, timeout=5).json()
  172. for video in data[append]:
  173. vid = video['id']
  174. if not vid.isnumeric():
  175. continue
  176. img = sub(r'photo/(.*?)/', 'photo/l/', video['pic']['large']) + '@User-Agent=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36@Referer=https://www.douban.com/'
  177. name = video['title'].strip()
  178. try:
  179. remark = video['rating']['value']
  180. except:
  181. remark = video['episodes_info']
  182. if remark != '':
  183. remark = '{}分'.format(remark)
  184. else:
  185. remark = '暂无评分'
  186. videos.append({
  187. "vod_db_id": vid,
  188. "vod_name": name,
  189. "vod_pic": img,
  190. "vod_remarks": remark
  191. })
  192. lenvodList = len(videos)
  193. if page * 30 < data['total']:
  194. pagecount = page + 1
  195. else:
  196. pagecount = page
  197. result['list'] = videos
  198. result['page'] = page
  199. result['pagecount'] = pagecount
  200. result['limit'] = lenvodList
  201. result['total'] = lenvodList
  202. return result
  203. def detailContent(self, did):
  204. name = ''
  205. did = did[0]
  206. if '###' in did:
  207. idsList = did.split('###')
  208. tag = idsList[0]
  209. tid = idsList[1]
  210. if tag == 'wogg':
  211. if '---' in tid:
  212. tids = tid.split('---')
  213. tid = tids[0]
  214. name = tids[1]
  215. if not 'www.aliyundrive.com' in tid:
  216. url = 'http://wogg.xyz/index.php/voddetail/{}.html'.format(tid)
  217. r = self.fetch(url, headers={"User-Agent": "okhttp/3.12.13"}, verify=False)
  218. m = self.regStr(reg='https://www.aliyundrive.com/s/[^"]+', src=r.text.replace('www.alipan.com', 'www.aliyundrive.com'), group=0)
  219. else:
  220. m = self.regStr(reg='https://www.aliyundrive.com/s/[^"]+', src=tid.replace('www.alipan.com', 'www.aliyundrive.com'), group=0)
  221. tid = m.replace('\\', '')
  222. elif tag == 'ps':
  223. if '---' in tid:
  224. tids = tid.split('---')
  225. tid = tids[0]
  226. name = tids[1]
  227. header = {
  228. 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36',
  229. 'Referer': 'https://www.alipansou.com' + '/s/' + tid
  230. }
  231. r = self.fetch('https://www.alipansou.com' + '/cv/' + tid, allow_redirects=False, headers=header, timeout=5)
  232. tid = self.regStr(r.text.replace('www.alipan.com', 'www.aliyundrive.com'), 'https://www.aliyundrive.com/s/[^"]+', 0).replace('\\', '')
  233. elif tag == 'cz':
  234. url = 'https://www.czzy88.com/' + tid + '.html'
  235. r = self.getContent({'pf': 'cz', 'url': url})
  236. html = self.html(r.content.decode())
  237. name = self.xpText(html, "//div[contains(@class,'moviedteail_tt')]/h1/text()")
  238. pic = self.xpText(html, "//div[contains(@class,'dyimg')]/img/@src")
  239. content = self.xpText(html, "//div[contains(@class,'yp_context')]/text()").strip().replace('\t\t', '')
  240. vodList = {
  241. 'vod_id': did,
  242. 'vod_name': name,
  243. 'vod_pic': pic,
  244. 'vod_content': content,
  245. 'vod_play_from': '厂长'
  246. }
  247. playUrl = ''
  248. playInfosList = html.xpath("//div[contains(@class,'paly_list_btn')]/a")
  249. i = 0
  250. for playInfos in playInfosList:
  251. i += 1
  252. playUrl += '#' + self.xpText(playInfos, "./text()").replace('\xa0', '') + '$' + self.xpText(playInfos, "./@href") + '---{}---{}'.format(name, i)
  253. vodList['vod_play_url'] = playUrl.strip('#')
  254. result = {'list': [vodList]}
  255. return result
  256. else:
  257. tid = did
  258. if 'www.aliyundrive.com' in tid or 'www.alipan.com' in tid:
  259. tid = tid.replace('www.alipan.com', 'www.aliyundrive.com')
  260. if '---' in tid:
  261. tids = tid.split('---')
  262. tid = tids[0]
  263. name = tids[1]
  264. shareId = self.regStr(reg='www.aliyundrive.com\\/s\\/([^\\/]+)(\\/folder\\/([^\\/]+))?', src=tid, group=1)
  265. fileId = self.regStr(reg='www.aliyundrive.com\\/s\\/([^\\/]+)(\\/folder\\/([^\\/]+))?', src=tid, group=3)
  266. url = 'https://api.aliyundrive.com/adrive/v3/share_link/get_share_by_anonymous'
  267. params = {'share_id': shareId}
  268. data = self.postJson(url, json=params, headers=self.header, verify=False, timeout=5).json()
  269. fileInfos = []
  270. if 'file_infos' in data:
  271. fileInfos = data['file_infos']
  272. if len(fileInfos) <= 0:
  273. return {'list': [], 'msg': '分享链接已失效'}
  274. fileInfo = fileInfos[0]
  275. if fileId == None or len(fileId) <= 0:
  276. fileId = fileInfo['file_id']
  277. if name == '':
  278. name = data['share_name']
  279. vodList = {
  280. 'vod_id': tid,
  281. 'vod_name': name,
  282. 'vod_pic': data['avatar'],
  283. 'vod_content': tid,
  284. 'vod_play_from': '原画$$$普画'
  285. }
  286. fileType = fileInfo['type']
  287. if fileType != 'folder':
  288. if fileType != 'file' or fileInfo['category'] != 'video':
  289. return {'list': [], 'msg': '分享链接已失效'}
  290. fileId = 'root'
  291. shareToken = self.getshareToken(shareId, '')
  292. itemsDict = self.listFiles({}, shareId, fileId, shareToken)
  293. if not itemsDict:
  294. return {'list': [], 'msg': '无可播放资源'}
  295. itemsDict = sorted(itemsDict.items(), key=lambda x: x[0])
  296. videoList = []
  297. playList = []
  298. for item in itemsDict:
  299. videoList.append(item[0] + '$' + '{}---'.format(name) + quote(item[1]))
  300. playList.append('#'.join(videoList))
  301. vodList['vod_play_url'] = '$$$'.join(playList + playList)
  302. result = {
  303. 'list': [vodList]
  304. }
  305. else:
  306. vodList = {
  307. 'vod_id': tid,
  308. 'vod_name': tid,
  309. 'vod_content': tid,
  310. 'vod_play_from': '直链$$$嗅探$$$解析',
  311. 'vod_play_url': '推送${}$$$推送${}$$$推送${}'.format(tid, tid, tid)
  312. }
  313. result = {'list': [vodList]}
  314. return result
  315. def playerContent(self, flag, pid, vipFlags):
  316. result = {}
  317. result["url"] = pid
  318. if flag == '原画':
  319. name = pid.split('---')[0]
  320. pos = pid.split('---')[1]
  321. pid = pid.split('---')[2]
  322. params = self.getDanmaku(name, pos)
  323. result = self.ognContent(flag, pid)
  324. if params:
  325. danmuUrl = f'https://api-lmteam.koyeb.app/danmu?params={quote(json.dumps(params))}'
  326. result['danmaku'] = danmuUrl
  327. return result
  328. elif flag == '普画':
  329. name = pid.split('---')[0]
  330. pos = pid.split('---')[1]
  331. pid = pid.split('---')[2]
  332. params = self.getDanmaku(name, pos)
  333. result = self.fhdContent(flag, pid)
  334. if params:
  335. danmuUrl = f'https://api-lmteam.koyeb.app/danmu?params={quote(json.dumps(params))}'
  336. result['danmaku'] = danmuUrl
  337. return result
  338. elif flag == '直链':
  339. result["parse"] = 0
  340. elif flag == '嗅探':
  341. result["parse"] = 1
  342. elif flag == '解析':
  343. result["jx"] = 1
  344. elif flag == '厂长':
  345. result["parse"] = 0
  346. url = pid.split('---')[0]
  347. name = pid.split('---')[1]
  348. pos = pid.split('---')[2]
  349. params = self.getDanmaku(name, pos)
  350. if params:
  351. danmuUrl = f'https://api-lmteam.koyeb.app/danmu?params={quote(json.dumps(params))}'
  352. result['danmaku'] = danmuUrl
  353. r = self.getContent({'pf': 'cz', 'url': url})
  354. b64 = self.regStr(reg='\"([^\"]+)\";var [\d\w]+=function dncry.*md5.enc.Utf8.parse\(\"([\d\w]+)\".*md5.enc.Utf8.parse\(([\d]+)\)', src=r.text, group=1)
  355. key = self.regStr(reg='\"([^\"]+)\";var [\d\w]+=function dncry.*md5.enc.Utf8.parse\(\"([\d\w]+)\".*md5.enc.Utf8.parse\(([\d]+)\)', src=r.text, group=2).encode()
  356. iv = self.regStr(reg='\"([^\"]+)\";var [\d\w]+=function dncry.*md5.enc.Utf8.parse\(\"([\d\w]+)\".*md5.enc.Utf8.parse\(([\d]+)\)', src=r.text, group=3).encode()
  357. enc = b64decode(b64)
  358. cipher = AES.new(key, AES.MODE_CBC, iv)
  359. data = cipher.decrypt(enc)
  360. content = data[:-data[-1]].decode()
  361. playUrl = self.regStr(reg='video: *\{url: *\"([^\"]+)\"', src=content)
  362. subUrl = self.regStr(reg='subtitle: *\{url: *\"([^\"]+)\"', src=content)
  363. if len(subUrl) > 0:
  364. result['subs'] = [{'url': subUrl, 'name': 'czspp'}]
  365. if len(playUrl) == 0:
  366. url = self.regStr(reg='<iframe.*?src=\"(.*?)\".*?</iframe>', src=r.text)
  367. r = self.getContent({'pf': 'cz', 'url': url})
  368. b64 = self.regStr(reg='var rand = \"(.*?)\".*var player = \"(.*?)\"', src=r.text.replace('\n', ''), group=2)
  369. iv = self.regStr(reg='var rand = \"(.*?)\".*var player = \"(.*?)\"', src=r.text.replace('\n', ''), group=1).encode()
  370. enc = b64decode(b64)
  371. cipher = AES.new('VFBTzdujpR9FWBhe'.encode(), AES.MODE_CBC, iv)
  372. data = cipher.decrypt(enc)
  373. content = data[:-data[-1]].decode()
  374. playUrl = json.loads(content)['url']
  375. result["url"] = playUrl
  376. else:
  377. result = {}
  378. return result
  379. def searchContent(self, key, quick):
  380. return self.searchContentPage(key, quick, '1')
  381. def searchContentPage(self, key, quick, page):
  382. self.fileidList = []
  383. self.shareidList = []
  384. page = int(page)
  385. items = []
  386. keyword = key
  387. if page == 1:
  388. siteList = ['cz', 'ps', 'zt', 'xy', 'wogg']
  389. else:
  390. siteList = self.getCache('alisiteList_{}_{}'.format(keyword, page))
  391. self.delCache('alisiteList_{}_{}'.format(keyword, page))
  392. if not siteList:
  393. return {'list': items}
  394. contents = []
  395. if quick:
  396. siteList = ['cz']
  397. with ThreadPoolExecutor(max_workers=5) as executor:
  398. searchList = []
  399. try:
  400. for site in siteList:
  401. tag = site
  402. future = executor.submit(self.runSearch, key, tag, page)
  403. searchList.append(future)
  404. for result in as_completed(searchList, timeout=10):
  405. contents.append(result.result())
  406. except:
  407. pass
  408. finally:
  409. executor.shutdown(wait=False)
  410. nextpageList = []
  411. for content in contents:
  412. if content is None:
  413. continue
  414. contkey = list(content.keys())[0]
  415. infos = content[contkey]
  416. items = items + content[contkey][0]
  417. nextpageList.append(infos[1])
  418. if not infos[1]:
  419. siteList.remove(contkey)
  420. self.setCache('alisiteList_{}_{}'.format(keyword, page+1), siteList)
  421. result = {
  422. 'list': items,
  423. 'hasNext': True in nextpageList
  424. }
  425. return result
  426. def runSearch(self, key, tag, pg):
  427. try:
  428. defname = 'self.search' + tag
  429. result = eval(defname)(key, tag, pg)
  430. return result
  431. except Exception as e:
  432. return {tag: [[], False]}
  433. def searchcz(self, key, tag, pg):
  434. items = []
  435. header = {
  436. "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36 Edg/114.0.1823.37"
  437. }
  438. r = self.post('https://ymck.pro/API/v2.php', headers=header, data={'q': key, 'size': 25}, timeout=5, verify=False)
  439. vList = json.loads(b64decode(self.cleanText(r.text)))[1:]
  440. vidList = []
  441. for video in vList[1:]:
  442. if 'website' not in video or video['website'] != '厂长资源':
  443. continue
  444. name = video['text']
  445. vid = self.regStr(reg='http.*?//.*?/(\S+/.*?).html', src=video['url'])
  446. if vid in vidList:
  447. continue
  448. else:
  449. vidList.append(vid)
  450. items.append({
  451. 'vod_id': 'cz###' + vid,
  452. 'vod_name': name,
  453. "vod_remarks": '厂长'
  454. })
  455. return {tag: [items, False]}
  456. def searchps(self, key, tag, pg):
  457. pg = int(pg)
  458. items = []
  459. header = {
  460. "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36 Edg/114.0.1823.37",
  461. # "Content-Type": "text/html; charset=UTF-8",
  462. "Referer": "https://www.alipansou.com/"
  463. }
  464. r = self.fetch(f'https://www.alipansou.com/search?page={pg}&k={key}&t=7', headers=header, verify=False, timeout=5)
  465. html = self.html(self.cleanText(r.content.decode('utf-8')))
  466. vList = html.xpath("//van-row/a")
  467. for video in vList:
  468. href = self.xpText(video, "./@href")
  469. if 'xunlei' in href:
  470. continue
  471. vid = self.regStr(reg=r'/s/(.*)', src=href)
  472. nameElement = self.xpText(video, ".//template/div")
  473. name = ''.join(nameElement.xpath('.//text()')).strip()
  474. if name.count(key) > 1 or len(name) - len(key) > 10:
  475. name = ''.join(OrderedDict.fromkeys(name))
  476. if SequenceMatcher(None, name, key).ratio() < 0.6 and not key in name:
  477. continue
  478. items.append({
  479. 'vod_id': 'ps###' + vid + "---{}".format(key),
  480. 'vod_name': name,
  481. 'vod_pic': 'https://api-lmteam.koyeb.app/files/aliyun.png',
  482. "vod_remarks": '阿里盘搜'
  483. })
  484. try:
  485. maxPage = int(self.xpText(html, ".//van-row/van-col/van-pagination/@page-count"))
  486. except:
  487. maxPage = pg
  488. if len(items) == 0:
  489. maxPage = pg
  490. return {tag: [items, pg < maxPage]}
  491. def searchzt(self, key, tag, pg):
  492. items = []
  493. header = {
  494. "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36 Edg/114.0.1823.37",
  495. "Referer": "http://a.gitcafe.net/"
  496. }
  497. params = {
  498. "action": "search",
  499. "from": "web",
  500. "token": "c128f28b5aca32c462c6bb0e032e77ebacca8c",
  501. "keyword": key
  502. }
  503. r = self.post('https://gitcafe.net/tool/alipaper/', data=params, headers=header, timeout=5)
  504. vList = json.loads(self.cleanText(r.text))['data']
  505. for video in vList:
  506. if video['alikey'] in self.shareidList:
  507. continue
  508. self.shareidList.append(video['alikey'])
  509. name = video['title']
  510. if len(name) > len(key) + 20:
  511. name = ''.join(OrderedDict.fromkeys(name))
  512. items.append({
  513. 'vod_id': 'https://www.aliyundrive.com/s/' + video['alikey'] + "---{}".format(key),
  514. 'vod_name': name,
  515. 'vod_pic': 'https://api-lmteam.koyeb.app/files/aliyun.png',
  516. "vod_remarks": '阿里纸条'
  517. })
  518. return {tag: [items, False]}
  519. def searchxy(self, key, tag, pg):
  520. items = []
  521. header = {
  522. "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36 Edg/114.0.1823.37",
  523. "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
  524. "Referer": f"http://www.yunso.net/index/user/s?wd={quote(key)}"
  525. }
  526. r = self.post(f'http://www.yunso.net/api/validate/search?wd={key}&page={pg}&uk=&mode=90001&stype=20100&scope_content=', data=f'/api/validate/search?wd={key}&page={pg}&uk=&mode=90001&stype=20100&scope_content=', verify=False, headers=header, timeout=5)
  527. data = json.loads(self.cleanText(r.text))
  528. html = self.html(data['data'].replace('</>', ''))
  529. vList = html.xpath("//div[contains(@class,'layui-card-header')]")
  530. count = 0
  531. for video in vList:
  532. name = self.xpText(video, './/u/a/text()').strip()
  533. if name.count(key) > 1 or len(name) > len(key) + 20:
  534. name = ''.join(OrderedDict.fromkeys(name))
  535. vid = b64decode(self.xpText(video, './/u/a/@url')).decode().replace('www.alipan.com', 'www.aliyundrive.com')
  536. if 'www.aliyundrive.com' not in vid:
  537. continue
  538. shareId = self.regStr(vid, 'www.aliyundrive.com\/s\/([^\/]+)(\/folder\/([^\/]+))?')
  539. if shareId not in self.shareidList:
  540. self.shareidList.append(shareId)
  541. else:
  542. count += 1
  543. continue
  544. items.append({
  545. 'vod_id': vid + "---{}".format(key),
  546. 'vod_name': name,
  547. 'vod_pic': 'https://api-lmteam.koyeb.app/files/aliyun.png',
  548. "vod_remarks": '阿里小云'
  549. })
  550. return {tag: [items, len(items) + count == 20]}
  551. def searchwogg(self, key, tag, pg):
  552. pg = int(pg)
  553. items = []
  554. header = {
  555. "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36"}
  556. url = 'http://wogg.xyz/index.php/vodsearch/{}----------{}---.html'.format(key, pg)
  557. r = self.fetch(url, headers=header, verify=False, timeout=5)
  558. html = self.html(self.cleanText(r.text))
  559. vList = html.xpath("//div[contains(@class,'module-items')]/div")
  560. nextpage = True
  561. for video in vList:
  562. img = self.xpText(video, './/img/@data-src')
  563. if not img.startswith('http'):
  564. img = self.regStr(img, '\((http.*?)\)')
  565. title = self.xpText(video, './/img/@alt').strip()
  566. vid = self.xpText(video, ".//div[contains(@class,'video-info-footer')]/a/@href")
  567. vid = self.regStr(vid, '/(\d+)\.html')
  568. items.append({
  569. 'vod_id': 'wogg###' + vid + "---{}".format(key),
  570. 'vod_name': title,
  571. 'vod_pic': img,
  572. "vod_remarks": '阿里玩偶哥哥'
  573. })
  574. maxPageLiset = html.xpath(".//div[@id='page']/a")
  575. if maxPageLiset != []:
  576. maxPage = self.xpText(maxPageLiset[-1], './@href')
  577. maxPage = self.regStr(maxPage, '-(\d+)-')
  578. if pg == int(maxPage):
  579. nextpage = False
  580. else:
  581. nextpage = False
  582. return {tag: [items, nextpage]}
  583. def localProxy(self, params):
  584. if params['type'] == "m3u8":
  585. return self.proxyM3u8(params)
  586. elif params['type'] == "media":
  587. return self.proxyMedia(params)
  588. elif params['type'] == "ts":
  589. return self.proxyTs(params)
  590. return None
  591. def ognContent(self, flag, oid):
  592. oid = unquote(oid)
  593. ids = oid.split('+')
  594. shareId = ids[0]
  595. fileId = ids[2]
  596. if 'thread' in self.extendDict:
  597. thread = self.extendDict['thread']
  598. else:
  599. thread = '0'
  600. token = self.extendDict['token']
  601. if token.startswith('http'):
  602. token = quote(token)
  603. subtitleList = self.subtitleContent(oid)
  604. result = {
  605. 'parse': '0',
  606. 'playUrl': '',
  607. 'url': f"http://127.0.0.1:UndCover/proxy?do=py&type=media&shareId={shareId}&fileId={fileId}&token={token}&thread={thread}",
  608. 'header': self.header,
  609. 'subs': subtitleList
  610. }
  611. return result
  612. def fhdContent(self, flag, fid):
  613. fid = unquote(fid)
  614. ids = fid.split('+')
  615. shareId = ids[0]
  616. fileId = ids[2]
  617. token = self.extendDict['token']
  618. if token.startswith('http'):
  619. token = quote(token)
  620. subtitleList = self.subtitleContent(fid)
  621. result = {
  622. 'parse': '0',
  623. 'playUrl': '',
  624. 'url': f"http://127.0.0.1:UndCover/proxy?do=py&type=m3u8&shareId={shareId}&fileId={fileId}&token={token}",
  625. 'header': self.header,
  626. 'subs': subtitleList
  627. }
  628. return result
  629. def subtitleContent(self, sid):
  630. ids = sid.split('+')
  631. shareId = ids[0]
  632. shareToken = ids[1]
  633. subtitle = ids[4]
  634. token = self.extendDict['token']
  635. if token.startswith('http'):
  636. token = quote(token)
  637. if len(subtitle) == 0:
  638. return []
  639. tokenDict = self.getToken(self.extendDict['token'])
  640. header = self.header.copy()
  641. header['Content-Type'] = 'application/json'
  642. header['x-share-token'] = shareToken
  643. header['authorization'] = tokenDict['authorization']
  644. subtitleList = subtitle.strip("&&&").split('&&&')
  645. subs = []
  646. for sub in subtitleList:
  647. subList = sub.split('###')
  648. subname = subList[0]
  649. if subname.split('.')[-1].lower() == 'ssa' or subname.split('.')[-1].lower() == 'ass':
  650. subformat = 'text/x-ssa'
  651. elif subname.split('.')[-1].lower() == 'srt':
  652. subformat = 'application/x-subrip'
  653. elif subname.split('.')[-1].lower() == 'vtt':
  654. subformat = 'text/vtt'
  655. else:
  656. subformat = 'text/plain'
  657. fileId = subList[1]
  658. subs.append({'url': f"http://127.0.0.1:UndCover/proxy?do=py&type=media&shareId={shareId}&fileId={fileId}&token={token}&subformat{subformat}", 'name': subname, 'format': subformat})
  659. return subs
  660. def delFiles(self, header, toDriveId, tempidsList):
  661. delidsList = []
  662. for fileId in tempidsList:
  663. jsonStr = '{\"requests\":[{\"body\":{\"drive_id\":\"%s\",\"file_id\":\"%s\"},\"headers\":{\"Content-Type\":\"application/json\"},\"id\":\"%s\",\"method\":\"POST\",\"url\":\"/file/delete\"}],\"resource\":\"file\"}' % (toDriveId, fileId, fileId)
  664. r = self.post('https://api.aliyundrive.com/v3/batch', data=jsonStr, headers=header, verify=False, timeout=5)
  665. if r.status_code == 200 and r.json()['responses'][0]['status'] == 404:
  666. delidsList.append(fileId)
  667. for fileId in delidsList:
  668. tempidsList.remove(fileId)
  669. if tempidsList != []:
  670. self.setCache('tempidsList', tempidsList)
  671. else:
  672. self.delCache('tempidsList')
  673. def proxyMedia(self, params):
  674. thread = 0
  675. downloadUrl = ''
  676. token = params['token']
  677. fileId = params['fileId']
  678. shareId = params['shareId']
  679. if 'thread' in params:
  680. thread = int(params['thread'])
  681. tokenDict = self.getToken(token, True)
  682. shareToken = self.getshareToken(shareId, '')
  683. header = self.header.copy()
  684. header['Content-Type'] = 'application/json'
  685. header['x-share-token'] = shareToken
  686. header['authorization'] = tokenDict['authorization']
  687. toDriveId = tokenDict['drive_id']
  688. tempidsList = self.getCache('tempidsList')
  689. if not tempidsList:
  690. tempidsList = []
  691. if tempidsList != []:
  692. self.delFiles(header, toDriveId, tempidsList)
  693. key = f'alidownloadUrl_{shareId}_{fileId}'
  694. data = self.getCache(key)
  695. if data and 'downloadUrl' in data:
  696. header = self.header.copy()
  697. if 'range' in params:
  698. header['Range'] = params['range']
  699. contentType = data['contentType']
  700. if contentType == "video/MP2T":
  701. action = {'url': data['downloadUrl'], 'header': header, 'param': '', 'type': 'redirect'}
  702. return [302, contentType, action, data['downloadUrl']]
  703. action = {'url': data['downloadUrl'], 'header': header, 'param': '', 'type': 'stream'}
  704. return [206, "application/octet-stream", action, '']
  705. code = 200
  706. contentType = "application/octet-stream"
  707. if tokenDict['open_token'] == '':
  708. thread = 10
  709. if 'thread' in params:
  710. thread = int(params['thread'])
  711. if thread == 0:
  712. code = 206
  713. contentType = "application/octet-stream"
  714. try:
  715. jsonStr = "{\"requests\":[{\"body\":{\"file_id\":\"%s\",\"share_id\":\"%s\",\"auto_rename\":true,\"to_parent_file_id\":\"root\",\"to_drive_id\":\"%s\"},\"headers\":{\"Content-Type\":\"application/json\"},\"id\":\"0\",\"method\":\"POST\",\"url\":\"/file/copy\"}],\"resource\":\"file\"}" % (fileId, shareId, toDriveId)
  716. r = self.post('https://api.aliyundrive.com/v3/batch', data=jsonStr, headers=header, verify=False, timeout=5)
  717. if r.status_code == 400:
  718. r = self.post('https://user.aliyundrive.com/v2/user/get', headers=header, verify=False)
  719. toDriveId = r.json()['resource_drive_id']
  720. jsonStr = "{\"requests\":[{\"body\":{\"file_id\":\"%s\",\"share_id\":\"%s\",\"auto_rename\":true,\"to_parent_file_id\":\"root\",\"to_drive_id\":\"%s\"},\"headers\":{\"Content-Type\":\"application/json\"},\"id\":\"0\",\"method\":\"POST\",\"url\":\"/file/copy\"}],\"resource\":\"file\"}" % (fileId, shareId, toDriveId)
  721. r = self.post('https://api.aliyundrive.com/v3/batch', data=jsonStr, headers=header, verify=False, timeout=5)
  722. myFileId = r.json()['responses'][0]['body']['file_id']
  723. tempidsList.append(myFileId)
  724. header['authorization'] = tokenDict['open_authorization']
  725. data = self.postJson('https://open.aliyundrive.com/adrive/v1.0/openFile/getDownloadUrl',
  726. json={
  727. "expire_sec": 115200,
  728. 'file_id': myFileId,
  729. 'drive_id': toDriveId
  730. },
  731. headers=header,
  732. verify=False,
  733. timeout=5).json()
  734. downloadUrl = data['url']
  735. try:
  736. if 'auth_key=' in downloadUrl:
  737. expiresAt = int(self.regStr(reg="auth_key=(\d+)-", src=downloadUrl)) - 60
  738. elif 'x-oss-expires=' in downloadUrl:
  739. expiresAt = int(self.regStr(reg="x-oss-expires=(\d+)", src=downloadUrl)) - 60
  740. else:
  741. expiresAt = int(time.time()) - 60
  742. except:
  743. expiresAt = int(time.time()) - 60
  744. self.setCache(key, {"thread": 0, 'downloadUrl': downloadUrl, 'expiresAt': expiresAt, 'shareId': shareId, 'fileId': fileId, "contentType": contentType})
  745. except:
  746. if 'thread' in params and int(params['thread']) != 0:
  747. thread = int(params['thread'])
  748. else:
  749. thread = 10
  750. finally:
  751. if tempidsList != []:
  752. header['authorization'] = tokenDict['authorization']
  753. self.delFiles(header, toDriveId, tempidsList)
  754. if thread > 0:
  755. code = 302
  756. contentType = "video/MP2T"
  757. header['authorization'] = tokenDict['authorization']
  758. r = self.postJson(
  759. 'https://api.aliyundrive.com/v2/file/get_share_link_download_url',
  760. json={
  761. 'share_id': shareId,
  762. 'file_id': fileId,
  763. "expire_sec": 600,
  764. },
  765. headers=header,
  766. verify=False,
  767. timeout=5)
  768. downloadUrl = r.json()['url']
  769. try:
  770. if 'auth_key=' in downloadUrl:
  771. expiresAt = int(self.regStr(reg="auth_key=(\d+)-", src=downloadUrl)) - 60
  772. elif 'x-oss-expires=' in downloadUrl:
  773. expiresAt = int(self.regStr(reg="x-oss-expires=(\d+)", src=downloadUrl)) - 60
  774. else:
  775. expiresAt = int(time.time()) - 60
  776. except:
  777. expiresAt = int(time.time()) - 60
  778. try:
  779. # self.fetch('http://192.168.1.254:7777')
  780. self.fetch('http://127.0.0.1:7777')
  781. except:
  782. # self.fetch('http://192.168.1.254:9978/go')
  783. self.fetch('http://127.0.0.1:9978/go')
  784. downloadUrl = f'http://127.0.0.1:7777?url={quote(downloadUrl)}&thread={thread}'
  785. self.setCache(key, {"thread": thread, 'downloadUrl': downloadUrl, 'expiresAt': expiresAt, 'shareId': shareId, 'fileId': fileId, "contentType": contentType})
  786. action = {'url': downloadUrl, 'header': self.header, 'param': '', 'type': 'redirect'}
  787. return [code, contentType, action, downloadUrl]
  788. header = self.header.copy()
  789. if 'range' in params:
  790. header['Range'] = params['range']
  791. action = {'url': downloadUrl, 'header': header, 'param': '', 'type': 'stream'}
  792. return [code, contentType, action, '']
  793. def proxyTs(self, params):
  794. mediaId = params['mediaId']
  795. _, m3u8Infos = self.getM3u8(params)
  796. url = m3u8Infos[str(mediaId)]
  797. if url.count('https') > 1:
  798. url = self.regStr(url, 'http.*?(http.*?://.*)')
  799. action = {'url': url, 'header': self.header, 'param': '', 'type': 'stream'}
  800. return [200, "video/MP2T", action, '']
  801. def proxyM3u8(self, params):
  802. content, _ = self.getM3u8(params)
  803. action = {'url': '', 'header': self.header, 'param': '', 'type': 'string'}
  804. return [200, "application/vnd.apple.mpegurl", action, content]
  805. def getM3u8(self, params):
  806. token = params['token']
  807. fileId = params['fileId']
  808. shareId = params['shareId']
  809. key = f'alim3u8Cache_{fileId}_{shareId}'
  810. data = self.getCache(key)
  811. if data:
  812. return data['content'], data['m3u8Infos']
  813. tokenDict = self.getToken(token)
  814. shareToken = self.getshareToken(shareId, '')
  815. params = {
  816. "share_id": shareId,
  817. "category": "live_transcoding",
  818. "file_id": fileId,
  819. "template_id": "",
  820. }
  821. header = self.header.copy()
  822. header['x-share-token'] = shareToken
  823. header['x-device-id'] = tokenDict['device_id']
  824. header['x-signature'] = tokenDict['signature']
  825. header['authorization'] = tokenDict['authorization']
  826. r = self.postJson(
  827. 'https://api.aliyundrive.com/users/v1/users/device/create_session',
  828. json={
  829. 'deviceName': 'Edge浏览器',
  830. 'modelName': 'Windows网页版',
  831. 'pubKey': tokenDict['public_key'],
  832. },
  833. headers=header,
  834. verify=False,
  835. timeout=5)
  836. result = r.json()
  837. if 'success' not in result or not result['success']:
  838. return '', {}
  839. header['authorization'] = tokenDict['authorization']
  840. url = 'https://api.aliyundrive.com/v2/file/get_share_link_video_preview_play_info'
  841. data = self.postJson(url, json=params, headers=header, verify=False, timeout=5).json()
  842. quality = ['UHD', 'QHD', 'FHD', 'HD', 'SD']
  843. videoList = data['video_preview_play_info']['live_transcoding_task_list']
  844. url = ''
  845. for q in quality:
  846. if len(url) > 0:
  847. break
  848. for video in videoList:
  849. if video['template_id'] == q:
  850. url = video['url']
  851. break
  852. r = self.fetch(url, headers=self.header, verify=False, timeout=5)
  853. host = '/'.join(url.split('/')[0:-1]) + '/'
  854. m3u8List = []
  855. m3u8Infos = {}
  856. slices = r.text.split("\n")
  857. count = 0
  858. deadlineList = []
  859. for mediaSlice in slices:
  860. if 'auth_key' in mediaSlice or 'x-oss-expires' in mediaSlice:
  861. try:
  862. if 'auth_key=' in mediaSlice:
  863. deadline = int(self.regStr(reg="auth_key=(\d+)-", src=mediaSlice))
  864. elif 'x-oss-expires=' in mediaSlice:
  865. deadline = int(self.regStr(reg="x-oss-expires=(\d+)", src=mediaSlice))
  866. else:
  867. deadline = int(time.time()) + 660
  868. except:
  869. deadline = int(time.time()) + 660
  870. deadlineList.append(deadline)
  871. count += 1
  872. m3u8Infos[str(count)] = host + mediaSlice
  873. mediaSlice = f"http://127.0.0.1:UndCover/proxy?do=py&type=ts&shareId={shareId}&fileId={fileId}&token={token}&mediaId={count}"
  874. m3u8List.append(mediaSlice)
  875. expiresAt = min(deadlineList) - 60
  876. content = '\n'.join(m3u8List).strip()
  877. self.setCache(key, {'content': content, 'm3u8Infos': m3u8Infos, 'expiresAt': expiresAt})
  878. return content, m3u8Infos
  879. def getshareToken(self, shareId, sharePwd):
  880. key = f'shareToken_{shareId}'
  881. data = self.getCache(key)
  882. if data:
  883. return data['share_token']
  884. params = {
  885. 'share_id': shareId,
  886. 'share_pwd': sharePwd
  887. }
  888. url = 'https://api.aliyundrive.com/v2/share_link/get_share_token'
  889. data = self.postJson(url, json=params, headers=self.header, verify=False, timeout=5).json()
  890. ShareToken = data['share_token']
  891. self.setCache(key, {"share_token": ShareToken, "expiresAt": int(time.time()) + data['expires_in'] - 60})
  892. return ShareToken
  893. def listFiles(self, resultDict, shareId, fileId, shareToken, dirName='', nextMaker='', subtDict={}, folderList=[]):
  894. url = 'https://api.aliyundrive.com/adrive/v3/file/list'
  895. header = self.header.copy()
  896. header['x-share-token'] = shareToken
  897. params = {
  898. 'limit': 200,
  899. 'marker': nextMaker,
  900. 'share_id': shareId,
  901. 'order_by': 'updated_at',
  902. 'parent_file_id': fileId,
  903. 'order_direction': 'DESC',
  904. 'image_url_process': 'image/resize,w_1920/format,jpeg',
  905. 'image_thumbnail_process': 'image/resize,w_160/format,jpeg',
  906. 'video_thumbnail_process': 'video/snapshot,t_1000,f_jpg,ar_auto,w_300'
  907. }
  908. retry = 0
  909. while retry <= 5:
  910. r = self.postJson(url, json=params, headers=header, verify=False, timeout=5)
  911. if r.status_code == 200:
  912. break
  913. retry += 1
  914. data = r.json()
  915. nextMaker = data['next_marker']
  916. if dirName != '':
  917. dirName = '[' + dirName + ']|'
  918. pos = 0
  919. itemsList = sorted(data['items'], key=lambda x: x['name'])
  920. for item in itemsList:
  921. if item['type'] == 'folder':
  922. folder = item['file_id'] + '&&&' + item['name']
  923. folderList.append(folder)
  924. else:
  925. if 'video' in item['mime_type'] or 'video' in item['category']:
  926. pos += 1
  927. remark = self.getSize(item['size'])
  928. resultDictKey = dirName + item['name'].replace("#", "_").replace("$", "_") + remark
  929. resultDict[resultDictKey] = str(pos) + '---' + shareId + "+" + shareToken + "+" + item['file_id'] + "+" + item['category'] + "+"
  930. elif 'others' == item['category'] and item['file_extension'] in ['srt', 'ass', 'ssa', 'vtt']:
  931. remark = self.getSize(item['size'])
  932. subtDictKey = dirName + item['name'].replace("#", "_").replace("$", "_") + remark
  933. subtDict[subtDictKey] = item['file_id']
  934. if len(nextMaker) > 0:
  935. self.listFiles(resultDict, shareId, fileId, shareToken, dirName, nextMaker, subtDict, folderList)
  936. for folder in folderList:
  937. folderList.remove(folder)
  938. if '&&&' in folder:
  939. folderInfos = folder.split('&&&')
  940. fileId = folderInfos[0]
  941. dirName = folderInfos[1]
  942. return self.listFiles(resultDict, shareId, fileId, shareToken, dirName, nextMaker, subtDict, folderList)
  943. for vkey in resultDict.keys():
  944. for sKey in subtDict.keys():
  945. if ']|' in sKey:
  946. subKey = sKey.split(']|')[1].split('/[')[0]
  947. else:
  948. subKey = sKey.split('/[')[0]
  949. if subKey + '###' + subtDict[sKey] + '&&&' not in resultDict[vkey]:
  950. resultDict[vkey] = resultDict[vkey] + subKey + '###' + subtDict[sKey] + '&&&'
  951. return resultDict
  952. def getSize(self, size):
  953. size = int(size)
  954. if size > 1024 * 1024 * 1024 * 1024.0:
  955. fs = "TB"
  956. sz = round(size / (1024 * 1024 * 1024 * 1024.0), 2)
  957. elif size > 1024 * 1024 * 1024.0:
  958. fs = "GB"
  959. sz = round(size / (1024 * 1024 * 1024.0), 2)
  960. elif size > 1024 * 1024.0:
  961. fs = "MB"
  962. sz = round(size / (1024 * 1024.0), 2)
  963. elif size > 1024.0:
  964. fs = "KB"
  965. sz = round(size / (1024.0), 2)
  966. else:
  967. fs = "KB"
  968. sz = round(size / (1024.0), 2)
  969. remark = '/[' + str(sz) + fs + ']'
  970. return remark
  971. def getToken(self, token, getOpen=True):
  972. if token.startswith('http'):
  973. token = unquote(token)
  974. header = {"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"}
  975. r = self.fetch(url=token, headers=header, verify=False, timeout=5)
  976. token = r.text.strip()
  977. tokenDict = self.getCache('aliToken')
  978. if tokenDict:
  979. return tokenDict['tokenDict']
  980. tokenDict = {}
  981. header = self.header.copy()
  982. data = self.postJson(url='https://auth.aliyundrive.com/v2/account/token',
  983. json={'grant_type': 'refresh_token', 'refresh_token': token},
  984. headers=header,
  985. verify=False,
  986. timeout=5).json()
  987. tokenDict['token'] = data['refresh_token']
  988. tokenDict['authorization'] = f"{data['token_type']} {data['access_token']}"
  989. tokenDict['user_id'] = data['user_id']
  990. tokenDict['drive_id'] = data['default_drive_id']
  991. tokenDict['device_id'] = data['device_id']
  992. tokenDict['export_in'] = data['expires_in']
  993. header['authorization'] = tokenDict['authorization']
  994. # 获取 opentoken
  995. if getOpen:
  996. try:
  997. header['User-Agent'] = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) aDrive/4.1.0 Chrome/108.0.5359.215 Electron/22.3.1 Safari/537.36'
  998. r = self.postJson(
  999. url='https://open.aliyundrive.com/oauth/users/authorize?client_id=76917ccccd4441c39457a04f6084fb2f&redirect_uri=https://alist.nn.ci/tool/aliyundrive/callback&scope=user:base,file:all:read,file:all:write&state=',
  1000. json={
  1001. 'authorize': 1,
  1002. 'scope': 'user:base,file:all:read,file:all:write'
  1003. },
  1004. headers=header,
  1005. verify=False,
  1006. timeout=5)
  1007. code = self.regStr(r.text, 'code=(.*?)\"')
  1008. data = self.postJson(url='https://api-cf.nn.ci/alist/ali_open/code',
  1009. json={
  1010. 'code': code,
  1011. 'grant_type': 'authorization_code'
  1012. },
  1013. headers=header,
  1014. verify=False,
  1015. timeout=5).json()
  1016. openExportIn = data['expires_in']
  1017. openToken = data['refresh_token']
  1018. opAuthorization = f"{data['token_type']} {data['access_token']}"
  1019. except:
  1020. openToken = ''
  1021. opAuthorization = ''
  1022. openExportIn = 7200
  1023. else:
  1024. openToken = ''
  1025. opAuthorization = ''
  1026. openExportIn = 7200
  1027. tokenDict['open_token'] = openToken
  1028. tokenDict['open_authorization'] = opAuthorization
  1029. tokenDict['expires_at'] = int(int(time.time()) + min(tokenDict['export_in'], openExportIn) / 2)
  1030. # 获取 signature 和 public_key
  1031. params = {"user_id": tokenDict['user_id'], "device_id": tokenDict['device_id']}
  1032. data = self.fetch(f"https://api-lmteam.koyeb.app/proxy?spider=apifan&function=aliSignature&params={quote(json.dumps(params))}").json()
  1033. tokenDict['public_key'] = data['public_key']
  1034. tokenDict['signature'] = data['signature']
  1035. self.setCache('aliToken', {"tokenDict": tokenDict, "expiresAt": tokenDict['expires_at']})
  1036. return tokenDict
  1037. def getDanmaku(self, name, pos):
  1038. danmuUrl = ''
  1039. pos = int(pos)
  1040. pos = pos - 1
  1041. if pos < 0:
  1042. pos = 0
  1043. try:
  1044. url = f'https://api.so.360kan.com/index?force_v=1&kw={name}&from=&pageno=1&v_ap=1&tab=all'
  1045. header = {
  1046. 'Referer': 'https://so.360kan.com/',
  1047. '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'}
  1048. r = self.fetch(url, headers=header, timeout=15)
  1049. diffList = []
  1050. vodList = r.json()['data']['longData']['rows']
  1051. for vod in vodList:
  1052. diffList.append(SequenceMatcher(None, vod['titleTxt'], name).ratio())
  1053. diffList.sort(reverse=True)
  1054. for diff in diffList:
  1055. infos = vodList[diffList.index(diff)]
  1056. if 'seriesPlaylinks' in infos:
  1057. if type(infos['seriesPlaylinks'][-1]) == str:
  1058. info = sorted(infos['seriesPlaylinks'][:-1], key=lambda x: x['c'])
  1059. info.append({'url': infos['seriesPlaylinks'][-1]})
  1060. else:
  1061. info = sorted(infos['seriesPlaylinks'], key=lambda x: x['c'])
  1062. else:
  1063. key = list(infos['playlinks'].keys())[0]
  1064. if type(infos['playlinks'][key]) == dict:
  1065. info = sorted(infos['playlinks'][key], key=lambda x: x['updateline'])
  1066. else:
  1067. pos = 0
  1068. info = [{'url': infos['playlinks'][key]}]
  1069. try:
  1070. danmuUrl = info[pos]['url']
  1071. break
  1072. except:
  1073. pass
  1074. if 'qq.com' in danmuUrl:
  1075. params = {'platform': 'qq', 'url': danmuUrl}
  1076. elif 'mgtv.com' in danmuUrl:
  1077. params = {'platform': 'mgtv', 'url': danmuUrl}
  1078. elif 'iqiyi.com' in danmuUrl:
  1079. params = {'platform': 'iqiyi', 'url': danmuUrl}
  1080. elif 'youku.com' in danmuUrl:
  1081. params = {'platform': 'youku', 'url': danmuUrl}
  1082. elif 'bilibili.com' in danmuUrl:
  1083. params = {'platform': 'bilibili', 'url': danmuUrl}
  1084. else:
  1085. params = {}
  1086. return params
  1087. except:
  1088. pass
  1089. def getContent(self, params):
  1090. pf = params['pf']
  1091. try:
  1092. if pf == 'cz':
  1093. header = {
  1094. 'Referer': 'https://www.czzy88.com/',
  1095. 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36',
  1096. }
  1097. url = params['url']
  1098. data = self.getCache('czCookie')
  1099. if data:
  1100. cookie = data['cookieDict'].copy()
  1101. r = self.fetch(params['url'], headers=header, verify=False, cookies=cookie, timeout=5)
  1102. if 'huadong' in r.text or 'renji' in r.text or 'btwaf' in r.text:
  1103. self.delCache('czCookie')
  1104. return self.getContent(params)
  1105. return r
  1106. from requests import session
  1107. session = session()
  1108. r = session.get(url, headers=header, verify=False, timeout=5)
  1109. content = r.content.decode()
  1110. cookie = session.cookies
  1111. if 'huadong' in content or 'renji' in content:
  1112. url = 'https://www.czzy88.com' + self.regStr(content, 'src=\"(.*?)\"')
  1113. r = session.get(url, headers=header, verify=False, timeout=5)
  1114. if 'huadong' in url:
  1115. key = self.regStr(r.text, 'key=\"(.*?)\"')
  1116. value = self.regStr(r.text, 'value=\"(.*?)\"')
  1117. val = ""
  1118. for i in range(len(value)):
  1119. code = ord(value[i])
  1120. val += str(code + 1)
  1121. value = hashlib.md5(val.encode()).hexdigest()
  1122. url = 'https://www.czzy88.com{}&key={}&value={}'.format(self.regStr(r.text, 'c.get\(\"(\S+\?type=\S+)&key='), key, value)
  1123. session.get(url, headers=header, verify=False, timeout=5)
  1124. cookie = session.cookies
  1125. elif 'renji' in url:
  1126. key = self.regStr(r.text, 'var key=\"(.*?)\"')
  1127. value = self.regStr(r.text, 'value=\"(.*?)\"')
  1128. val = ''
  1129. for i in range(0, len(value)):
  1130. code = ord(value[i])
  1131. val += str(code)
  1132. value = hashlib.md5(val.encode()).hexdigest()
  1133. url = 'https://www.czzy88.com{}&key={}&value={}'.format(self.regStr(r.text, 'c.get\(\"(\S+\?type=\S+)&key='), key, value)
  1134. session.get(url, headers=header, verify=False, timeout=5)
  1135. cookie = session.cookies
  1136. r = session.get(params['url'], headers=header, verify=False, cookies=cookie, timeout=5)
  1137. elif 'btwaf' in content:
  1138. imgData = session.get('https://www.czzy88.com/get_btwaf_captcha_base64?captcha={}'.format(int(time.time())), timeout=5).json()['msg']
  1139. code = self.postJson('https://api-lmteam.koyeb.app/ocr', json={'imgList': [imgData], 'lenth': 4}).json()['result']
  1140. session.get(f'https://www.czzy88.com/Verification_auth_btwaf?captcha={code}', headers=header, timeout=5)
  1141. cookie = session.cookies
  1142. r = session.get(params['url'], headers=header, verify=False, cookies=cookie, timeout=5)
  1143. else:
  1144. r._content = content.encode('utf-8')
  1145. r.cookies = session.cookies
  1146. r.url = url
  1147. try:
  1148. result = int(self.regStr(r.text, 'method=\"post\">(\d+) \+ (\d+) =', 1)) + int(self.regStr(r.text, 'method=\"post\">(\d+) \+ (\d+) =', 2))
  1149. cookie.set('result', str(result))
  1150. cookie.set('esc_search_captcha', '1')
  1151. r = session.get(params['url'], headers=header, verify=False, cookies=cookie, timeout=5)
  1152. session.close()
  1153. except:
  1154. session.close()
  1155. self.setCache('czCookie', {'cookieDict': cookie.get_dict(), 'expiresAt': int(time.time()) + 5400})
  1156. return r
  1157. else:
  1158. return None
  1159. except:
  1160. return None
  1161. def getCache(self, key):
  1162. # value = self.fetch(f'http://192.168.1.254:9978/cache?do=get&key={key}', timeout=5).text
  1163. value = self.fetch(f'http://127.0.0.1:9978/cache?do=get&key={key}', timeout=5).text
  1164. if len(value) > 0:
  1165. if value.startswith('{') and value.endswith('}') or value.startswith('[') and value.endswith(']'):
  1166. value = json.loads(value)
  1167. if type(value) == dict:
  1168. if not 'expiresAt' in value or value['expiresAt'] >= int(time.time()):
  1169. return value
  1170. else:
  1171. self.delCache(key)
  1172. return None
  1173. return value
  1174. else:
  1175. return None
  1176. def setCache(self, key, value):
  1177. if len(value) > 0:
  1178. if type(value) == dict or type(value) == list:
  1179. value = json.dumps(value, ensure_ascii=False)
  1180. # self.post(f'http://192.168.1.254:9978/cache?do=set&key={key}', data={"value": value}, timeout=5)
  1181. self.post(f'http://127.0.0.1:9978/cache?do=set&key={key}', data={"value": value}, timeout=5)
  1182. def delCache(self, key):
  1183. self.fetch(f'http://127.0.0.1:9978/cache?do=del&key={key}', timeout=5)