1 parent
186c37639e
commit
68e35e4f97
1 file changed
+263
-63
+263
-63
@@ -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
|
||||
|
||||
Reference in new issue
Block a user