修复 iqiyi 电影 综艺信息获取 (#79)

* 修复iqiyi综艺视频信息获取

* 修复iqiyi电影类别信息获取
This commit is contained in:
woleigedouer authored and GitHub committed 2025-10-29 23:54:22 +08:00
1 parent 186c37639e
commit 68e35e4f97
1 file changed
+263 -63
+263 -63
View File
@@ -122,28 +122,16 @@ export default class IqiyiSource extends BaseSource {
return null;
}
// 提取 link_id (从 pageUrl 或 playUrl 中提取)
const url = album.pageUrl || album.playUrl;
if (!url) {
return null;
}
const linkIdMatch = url.match(/v_(\w+?)\.html/);
if (!linkIdMatch) {
return null;
}
const linkId = linkIdMatch[1];
// 过滤外站付费播放
if (album.btnText === '外站付费播放') {
log("debug", `[iQiyi] 过滤掉外站付费播放内容: ${album.title}`);
return null;
}
// 提取媒体类型(参考优化后的 youku.js 和 bilibili.js)
// 提取媒体类型
const channel = album.channel || "";
let mediaType = "电视剧"; // 默认类型
if (channel.includes("电影")) {
mediaType = "电影";
} else if (channel.includes("动漫")) {
@@ -159,6 +147,52 @@ export default class IqiyiSource extends BaseSource {
return null;
}
// 电影类型:使用 qipuId 作为 mediaId
if (mediaType === "电影") {
const qipuId = album.qipuId || album.playQipuId;
if (!qipuId) {
log("debug", `[iQiyi] 电影缺少 qipuId: ${album.title}`);
return null;
}
// 提取年份
let year = null;
if (album.year) {
const yearStr = album.year.value || album.year.name;
if (yearStr && typeof yearStr === 'string' && yearStr.length === 4 && /^\d{4}$/.test(yearStr)) {
year = parseInt(yearStr);
}
}
// 清理标题
const cleanedTitle = album.title.replace(/<[^>]+>/g, '').replace(/:/g, ':');
return {
provider: "iqiyi",
mediaId: `movie_${qipuId}`, // 使用特殊前缀标识电影
title: cleanedTitle,
type: mediaType,
year: year,
imageUrl: album.img || album.imgH,
episodeCount: 1, // 电影只有1集
_qipuId: qipuId // 保存原始 qipuId 供后续使用
};
}
// 非电影类型:从 pageUrl 提取 link_id
const url = album.pageUrl;
if (!url) {
log("debug", `[iQiyi] 非电影内容缺少 pageUrl: ${album.title}`);
return null;
}
const linkIdMatch = url.match(/v_(\w+?)\.html/);
if (!linkIdMatch) {
log("debug", `[iQiyi] 无法从 pageUrl 提取 link_id: ${url}`);
return null;
}
const linkId = linkIdMatch[1];
// 提取年份
let year = null;
if (album.year) {
@@ -201,13 +235,34 @@ export default class IqiyiSource extends BaseSource {
/**
* 获取分集列表
* @param {string} id - 视频 ID (link_id)
* @param {string} id - 视频 ID (link_id 或 movie_qipuId)
* @returns {Promise<Array>} 分集列表
*/
async getEpisodes(id) {
try {
log("info", `[iQiyi] 获取分集列表: media_id=${id}`);
// 检查是否是电影类型(以 movie_ 开头)
if (id.startsWith('movie_')) {
const qipuId = id.substring(6); // 移除 "movie_" 前缀
log("info", `[iQiyi] 电影类型,调用 base_info API 获取视频ID: qipuId=${qipuId}`);
// 调用 base_info API 获取电影详情
const videoId = await this._getMovieVideoId(qipuId);
if (!videoId) {
log("error", `[iQiyi] 无法获取电影的视频ID: qipuId=${qipuId}`);
return [];
}
log("info", `[iQiyi] 电影视频ID: ${videoId}`);
return [{
id: videoId,
title: "正片",
order: 1,
link: `https://www.iqiyi.com/v_${videoId}.html`
}];
}
// 第一步:将 video_id 转换为 entity_id
const entityId = this._videoIdToEntityId(id);
if (!entityId) {
@@ -271,68 +326,125 @@ export default class IqiyiSource extends BaseSource {
}
const blocks = tabs[0].blocks || [];
const episodeGroups = [];
let foundEpisodes = false;
for (const block of blocks) {
if (block.bk_type === "album_episodes") {
const blockData = block.data?.data || [];
episodeGroups.push(...blockData);
}
}
// 查找 video_list 类型的块(新版API)
if (block.bk_type === "video_list" && block.data?.data) {
log("debug", `[iQiyi] 找到 video_list 类型的分集数据块, bk_id: ${block.bk_id}`);
if (episodeGroups.length === 0) {
log("info", "[iQiyi] 未找到分集数据块");
return [];
}
// 第五步:处理分集数据
for (const group of episodeGroups) {
let videosData = group.videos;
// 如果 videos 是 URL,需要额外请求
if (typeof videosData === 'string') {
log("info", `[iQiyi] 发现分季URL,正在获取: ${videosData}`);
try {
const seasonResponse = await httpGet(videosData);
videosData = typeof seasonResponse.data === "string" ? JSON.parse(seasonResponse.data) : seasonResponse.data;
} catch (error) {
log("error", `[iQiyi] 获取分季数据失败: ${error.message}`);
// 检查是否是分集选择器块
if (!block.tag || !block.tag.includes("episodes")) {
log("debug", `[iQiyi] 跳过非分集块: ${block.bk_id}`);
continue;
}
}
// 处理分页数据
if (videosData && typeof videosData === 'object' && videosData.feature_paged) {
for (const pageKey in videosData.feature_paged) {
const pagedList = videosData.feature_paged[pageKey];
for (const epData of pagedList) {
if (epData.content_type !== 1) continue;
foundEpisodes = true;
const playUrl = epData.play_url || "";
const tvidMatch = playUrl.match(/tvid=(\d+)/);
if (!tvidMatch) continue;
const dataGroups = block.data.data;
if (!Array.isArray(dataGroups)) {
log("warn", "[iQiyi] data.data 不是数组,跳过此块");
continue;
}
const tvid = tvidMatch[1];
let title = epData.short_display_name || epData.title || "未知分集";
const subtitle = epData.subtitle;
if (subtitle && !title.includes(subtitle)) {
title = `${title} ${subtitle}`;
}
for (const group of dataGroups) {
if (!group.videos || !Array.isArray(group.videos)) continue;
const order = epData.album_order;
const pageUrl = epData.page_url;
// 遍历每个年份/季度分组
for (const videoGroup of group.videos) {
if (!videoGroup.data || !Array.isArray(videoGroup.data)) continue;
if (tvid && title && order && pageUrl) {
allEpisodes.push({
id: tvid,
title: title,
order: order,
link: pageUrl
});
// 处理每个分集
for (const epData of videoGroup.data) {
// 只处理正片内容 (content_type === 1)
if (epData.content_type !== 1) continue;
const playUrl = epData.play_url || "";
const tvidMatch = playUrl.match(/tvid=(\d+)/);
if (!tvidMatch) continue;
const tvid = tvidMatch[1];
let title = epData.short_display_name || epData.title || "未知分集";
const subtitle = epData.subtitle;
if (subtitle && !title.includes(subtitle)) {
title = `${title} ${subtitle}`;
}
const order = epData.album_order;
const pageUrl = epData.page_url;
if (tvid && title && pageUrl) {
allEpisodes.push({
id: tvid,
title: title,
order: order !== undefined ? order : allEpisodes.length,
link: pageUrl
});
}
}
}
}
}
// 兼容旧版 API 的 album_episodes 类型
else if (block.bk_type === "album_episodes" && block.data?.data) {
log("debug", "[iQiyi] 找到 album_episodes 类型的分集数据块");
foundEpisodes = true;
const episodeGroups = block.data.data;
for (const group of episodeGroups) {
let videosData = group.videos;
// 如果 videos 是 URL,需要额外请求
if (typeof videosData === 'string') {
log("info", `[iQiyi] 发现分季URL,正在获取: ${videosData}`);
try {
const seasonResponse = await httpGet(videosData);
videosData = typeof seasonResponse.data === "string" ? JSON.parse(seasonResponse.data) : seasonResponse.data;
} catch (error) {
log("error", `[iQiyi] 获取分季数据失败: ${error.message}`);
continue;
}
}
// 处理分页数据
if (videosData && typeof videosData === 'object' && videosData.feature_paged) {
for (const pageKey in videosData.feature_paged) {
const pagedList = videosData.feature_paged[pageKey];
for (const epData of pagedList) {
if (epData.content_type !== 1) continue;
const playUrl = epData.play_url || "";
const tvidMatch = playUrl.match(/tvid=(\d+)/);
if (!tvidMatch) continue;
const tvid = tvidMatch[1];
let title = epData.short_display_name || epData.title || "未知分集";
const subtitle = epData.subtitle;
if (subtitle && !title.includes(subtitle)) {
title = `${title} ${subtitle}`;
}
const order = epData.album_order;
const pageUrl = epData.page_url;
if (tvid && title && order && pageUrl) {
allEpisodes.push({
id: tvid,
title: title,
order: order,
link: pageUrl
});
}
}
}
}
}
}
}
if (!foundEpisodes) {
log("info", "[iQiyi] 未找到分集数据块");
return [];
}
// 去重并排序
@@ -350,6 +462,94 @@ export default class IqiyiSource extends BaseSource {
}
}
/**
* 获取电影的视频ID(从 qipuId 获取正确的 video_id)
* @param {string} qipuId - 电影的 qipuId
* @returns {Promise<string|null>} 视频ID
*/
async _getMovieVideoId(qipuId) {
try {
// 构建 base_info API 请求参数
const params = {
entity_id: qipuId,
device_id: 'qd5fwuaj4hunxxdgzwkcqmefeb3ww5hx',
auth_cookie: '',
user_id: '0',
vip_type: '-1',
vip_status: '0',
conduit_id: '',
pcv: '13.103.23529',
app_version: '13.103.23529',
ext: '',
app_mode: 'standard',
scale: '125',
timestamp: String(Date.now()),
src: 'pca_tvg',
os: '',
ad_ext: '{"r":"2.5.0-ares6-pure"}'
};
// 生成签名(使用与 getEpisodes 相同的方法)
params.sign = this._createSign(params);
// 构建 URL
const queryString = buildQueryString(params);
const url = `https://mesh.if.iqiyi.com/tvg/v2/lw/base_info?${queryString}`;
log("debug", `[iQiyi] 请求电影详情: ${url}`);
const response = await httpGet(url, {
headers: {
'accept': '*/*',
'origin': 'https://www.iqiyi.com',
'referer': 'https://www.iqiyi.com/',
'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'
}
});
if (!response || !response.data) {
log("error", "[iQiyi] base_info API 响应为空");
return null;
}
const data = typeof response.data === "string" ? JSON.parse(response.data) : response.data;
// 从响应中提取视频ID
// 尝试多个可能的路径
if (data.data && data.data.base_data) {
const baseData = data.data.base_data;
// 尝试 1: 从 share_url 中提取(最可靠)
if (baseData.share_url) {
const match = baseData.share_url.match(/v_(\w+)\.html/);
if (match) {
const videoId = match[1];
log("info", `[iQiyi] 从 share_url 提取视频ID: ${videoId}`);
return videoId;
}
}
// 尝试 2: 从 page_url 中提取
if (baseData.page_url) {
const match = baseData.page_url.match(/v_(\w+)\.html/);
if (match) {
const videoId = match[1];
log("info", `[iQiyi] 从 page_url 提取视频ID: ${videoId}`);
return videoId;
}
}
}
log("error", "[iQiyi] base_info API 响应中未找到视频ID");
log("debug", `[iQiyi] 响应数据结构: ${JSON.stringify(data).substring(0, 1000)}...`);
return null;
} catch (error) {
log("error", `[iQiyi] 获取电影视频ID时出错: ${error.message}`);
return null;
}
}
/**
* 将 video_id 转换为 entity_id
* @param {string} videoId - 视频 ID