From 64f885c98dd9f328ea6185e998cf47372e895fb0 Mon Sep 17 00:00:00 2001 From: lilixu3 Date: Fri, 17 Jul 2026 07:27:47 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E8=8A=92=E6=9E=9CTV=E6=90=9C=E7=B4=A2AP?= =?UTF-8?q?I=E6=94=B9=E7=94=A8=E6=96=B0=E7=89=88v2=E6=8E=A5=E5=8F=A3+seqId?= =?UTF-8?q?=E7=AD=BE=E5=90=8D=20(#374)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: 芒果TV搜索API改用新版v2接口+seqId签名 旧版 /msite/search/v2 已返回403,改用 /aphone/search/rebirth/v2 新增 seqId=md5(deviceId + "." + timestamp) 签名 适配新版响应格式 mediaRebirthV2 * 使用纯JS md5替代Node crypto模块以兼容fw播放器 --- danmu_api/sources/mango.js | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/danmu_api/sources/mango.js b/danmu_api/sources/mango.js index 5b04e96..86eba6a 100644 --- a/danmu_api/sources/mango.js +++ b/danmu_api/sources/mango.js @@ -5,7 +5,7 @@ import { httpGet} from "../utils/http-util.js"; import { printFirst200Chars, titleMatches, getExplicitSeasonNumber, extractSeasonNumberFromAnimeTitle } from "../utils/common-util.js"; import { time_to_second, generateValidStartDate } from "../utils/time-util.js"; import { rgbToInt } from "../utils/danmu-util.js"; -import { convertToAsciiSum } from "../utils/codec-util.js"; +import { md5, convertToAsciiSum } from "../utils/codec-util.js"; import { addAnime, removeEarliestAnime } from "../utils/cache-util.js"; import { SegmentListResponse } from '../models/dandan-model.js'; @@ -82,14 +82,22 @@ export default class MangoSource extends BaseSource { try { log("info", `[Mango] 开始搜索: ${keyword}`); + // 生成设备ID(session内持久化) + if (!this._deviceId) { + this._deviceId = md5(`danmu-api-mango-${Date.now()}`); + } + + // 生成签名 seqId = md5(deviceId + "." + timestamp) + const timestamp = Date.now(); + const seqId = md5(this._deviceId + "." + timestamp); + const encodedKeyword = encodeURIComponent(keyword); - const searchUrl = `https://mobileso.bz.mgtv.com/msite/search/v2?q=${encodedKeyword}&pc=30&pn=1&sort=-99&ty=0&du=0&pt=0&corr=1&abroad=0&_support=10000000000000000`; + const searchUrl = `https://mobileso.bz.mgtv.com/aphone/search/rebirth/v2?q=${encodedKeyword}&_support=10100001&device=23127PN0CC&osVersion=16&appVersion=9.3.3&did=${this._deviceId}&mac=${this._deviceId}&seqId=${seqId}&ticket=&userId=0&osType=android&type=10&abroad=0`; const response = await httpGet(searchUrl, { headers: { - 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', + 'User-Agent': 'Dalvik/2.1.0 (Linux; U; Android 16; 23127PN0CC Build/AP1A.240505.003)', 'Accept': 'application/json', - 'Referer': 'https://www.mgtv.com/' } }); @@ -108,33 +116,33 @@ export default class MangoSource extends BaseSource { // 处理搜索结果 const results = []; for (const content of data.data.contents) { - if (content.type !== "media") { + if (content.type !== "mediaRebirthV2") { continue; } for (const item of content.data) { - // 只处理芒果TV自有内容 (source为'imgo') - if (item.source !== "imgo") { + // 只处理芒果TV自有内容 (新版API中source为空或'imgo') + if (item.source && item.source !== "imgo") { continue; } - // 从URL中提取 collection_id - const urlMatch = item.url ? item.url.match(/\/b\/(\d+)/) : null; - if (!urlMatch) { + // 新版API使用clipId作为媒体ID + if (!item.clipId) { continue; } - const mediaId = urlMatch[1]; + const mediaId = String(item.clipId); // 清理标题 (移除HTML标签) const cleanedTitle = item.title ? item.title.replace(/<[^>]+>/g, '').replace(/:/g, ':') : ''; - // 提取年份 - const yearMatch = item.desc && item.desc[0] ? item.desc[0].match(/[12][890][0-9][0-9]/) : null; + // 提取年份 (desc始终为数组) + const descText = Array.isArray(item.desc) ? (item.desc[0] || '') : (item.desc || ''); + const yearMatch = descText.match(/[12][890][0-9][0-9]/); const year = yearMatch ? parseInt(yearMatch[0]) : null; - // 提取媒体类型(参考优化后的 youku.js 和 bilibili.js) - const typeMatch = item.desc && item.desc[0] ? item.desc[0].split('/')[0].replace("类型:", "").trim() : ''; + // 提取媒体类型 + const typeMatch = descText.split('/')[0].replace("类型:", "").trim(); const mediaType = this._extractMediaType(typeMatch); results.push({