async def get_60s_bullets(self, vid: str, tokens: dict, min_at: int): """获取一分钟的弹幕""" app_key = "24679788" api = "https://acs.youku.com/h5/mopen.youku.danmu.list/1.0/" timestamp = str(int(time.time() * 1000)) msg = { "ctime": timestamp, "ctype": 10004, "cver": "v1.0", "guid": tokens["cna"], "mat": min_at, "mcount": 1, "pid": 0, "sver": "3.1.0", "type": 1, "vid": vid } # 将 msg 转换为 base64 后作为 msg 的一个键值对 msg_b64 = b64encode(json.dumps(msg, separators=(',', ':'))) sign = md5(msg_b64 + "MkmC9SoIw6xCkSKHhJ7b5D2r51kBiREr") # 计算签名 msg.update({"msg": msg_b64, "sign": sign}) timestamp = str(int(time.time() * 1000)) data = json.dumps(msg, separators=(',', ':')) sign = md5(f"{tokens['_m_h5_tk'][:32]}&{timestamp}&{app_key}&{data}") params = { "jsv": "2.5.6", "appKey": app_key, "t": timestamp, "sign": sign, "api": "mopen.youku.danmu.list", "v": "1.0", "type": "originaljson", "dataType": "jsonp", "timeout": "20000", "jsonpIncPrefix": "utility" } self.session.cookie_jar.update_cookies(tokens) headers = {"Referer": "https://v.youku.com"} resp = await self.post(api, params=params, data={"data": data}, headers=headers) data = await resp.json(content_type=None) comments = data["data"]["result"] # 带转义的 json 串 comments = json.loads(comments)["data"]["result"] # 返回的数据层层套娃 :( result = DanmakuData() for comment in comments: properties = json.loads(comment["propertis"]) # 弹幕的属性(官方拼写错误) color = properties["color"] position = properties["pos"] # size = properties["size"] result.append_bullet(time=comment["playat"] // 1000, pos=position, color=color, message=comment["content"]) return result
async def parse(self, raw_url: str): # 加密算法 Smali 位置 # .class final Lorg/daimhim/zzzfun/data/remote/HttpRequestManager$getVideoPlayInfo$2; # .source "HttpRequestManager.kt" # .method public final invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object; # .line 460 ~ .line 463 secret_key = "zan109drdddzz" now = int(time.time() * 1000) # 13 位时间戳 sing = md5(secret_key + str(now)) # 接口随 App 更新变化 play_api = "http://service-agbhuggw-1259251677.gz.apigw.tencentcs.com/android/video/112play" payload = { "playid": raw_url, "userid": "", "apptoken": "", "sing": sing, "map": now } resp = await self.post(play_api, data=payload, headers={"User-Agent": "okhttp/3.12.0"}) if not resp or resp.status != 200: return "" data = await resp.json(content_type=None) if not data["data"]: return "" real_url = data["data"]["videoplayurl"] if "alicdn" in real_url or "zzzhls" in real_url: # m3u8 格式, 该资源解析后访问一次立刻失效, 内部视频片段不会立刻失效 return AnimeInfo(real_url, volatile=True) return AnimeInfo(real_url)
def get_signed_url(url: str) -> str: """计算 URL 的签名""" # 加密算法见: https://www.libvio.com/static/img/load.html # 代码使用 sojson v6 进行混淆 path = urlparse(url).path t = format(int(time.time()) + 300, 'x') # 时间戳的十六进制表示 key = "y4nZpZYXK7SOr3wWlvyD0RTl8ti61IbeVFTjpLQv21hPKKTy" sign = md5(key + path + t).lower() return f"{url}?sign={sign}&t={t}"
async def parse(self, raw_url: str): play_id, play_vid = raw_url.split("|") if play_vid.startswith("http"): return play_vid # 不用处理了 headers = { "Origin": "https://web.age-spa.com:8443", "Referer": "https://web.age-spa.com:8443/" } api = "https://api.agefans.app/v2/_getplay" resp = await self.get(api, headers=headers) if not resp or resp.status != 200: return "" data = await resp.json(content_type=None) # 参数 kp 算法见 https://vip.cqkeb.com/agefans/js/chunk-3a9344fa.3f1985c3.js play_key = "agefans3382-getplay-1719" timestamp = data["ServerTime"] kp = md5( str(timestamp) + "{|}" + play_id + "{|}" + play_vid + "{|}" + play_key) next_api = data["Location"] params = { "playid": play_id, "vid": play_vid, "kt": timestamp, "kp": kp } resp = await self.get(next_api, params=params, headers=headers) if not resp or resp.status != 200: return "" data = await resp.json(content_type=None) v_url, p_url = data["vurl"], data["purlf"] if v_url or p_url: url = p_url + v_url url = url.split("?url=")[-1] return url return ""
async def parse(self, raw_url: str): play_id, play_vid = raw_url.split("|") if play_vid.startswith("http"): return play_vid # 不用处理了 api = "https://api.agefans.app/v2/_getplay" resp = await self.get(api) if not resp or resp.status != 200: return "" data = await resp.json(content_type=None) next_api = data.get("Location") if not next_api: return "" # 参数 kp 算法见 https://vip.cqkeb.com/agefans/js/chunk-3a9344fa.3f1985c3.js play_key = "agefans3382-getplay-1719" timestamp = data["ServerTime"] kp = md5( str(timestamp) + "{|}" + play_id + "{|}" + play_vid + "{|}" + play_key) params = { "playid": play_id, "vid": play_vid, "kt": timestamp, "kp": kp } resp = await self.get(next_api, params=params) if not resp or resp.status != 200: return "" data = await resp.json(content_type=None) p_url = data.get("purlf", "") v_url = data.get("vurl", "") url = p_url + v_url if not url: return "" return url.split("?url=")[-1]