feat(API): 添加视频选集信息获取接口
- 新增 /api/video/episodes 端点 - 支持解析B站视频链接(BV号和AV号格式) - 调用B站 API 获取视频分P信息 - 返回选集列表包含: 分P编号、标题、时长、cid等 - 对非B站视频返回空选集列表
This commit is contained in:
parent
1e81e9151b
commit
cab19672e0
149
src/app/api/video/episodes/route.ts
Normal file
149
src/app/api/video/episodes/route.ts
Normal file
@ -0,0 +1,149 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
|
||||
/**
|
||||
* 视频选集信息
|
||||
*/
|
||||
interface VideoEpisode {
|
||||
page: number; // 分P编号,从1开始
|
||||
part: string; // 分P标题
|
||||
duration: number; // 时长(秒)
|
||||
cid?: number; // B站视频分片ID
|
||||
}
|
||||
|
||||
/**
|
||||
* B站 API 返回的视频详情结构
|
||||
*/
|
||||
interface BilibiliVideoResponse {
|
||||
code: number;
|
||||
message: string;
|
||||
data?: {
|
||||
bvid: string;
|
||||
title: string;
|
||||
pages: {
|
||||
cid: number;
|
||||
page: number;
|
||||
part: string;
|
||||
duration: number;
|
||||
}[];
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 URL 中解析 B站视频 ID
|
||||
*/
|
||||
function parseBilibiliUrl(url: string): { bvid?: string; aid?: string } | null {
|
||||
try {
|
||||
// BV号格式: bilibili.com/video/BVxxxxx
|
||||
const bvMatch = url.match(/bilibili\.com\/video\/(BV[\w]+)/i);
|
||||
if (bvMatch) {
|
||||
return { bvid: bvMatch[1] };
|
||||
}
|
||||
|
||||
// AV号格式: bilibili.com/video/avxxxxx
|
||||
const avMatch = url.match(/bilibili\.com\/video\/av(\d+)/i);
|
||||
if (avMatch) {
|
||||
return { aid: avMatch[1] };
|
||||
}
|
||||
|
||||
// 短链接格式: b23.tv/xxxxx (需要跟随重定向)
|
||||
// 暂不支持,返回 null
|
||||
|
||||
return null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /api/video/episodes
|
||||
* 获取视频选集列表
|
||||
*
|
||||
* Query params:
|
||||
* - url: 视频链接
|
||||
*/
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
const { searchParams } = new URL(request.url);
|
||||
const videoUrl = searchParams.get('url');
|
||||
|
||||
if (!videoUrl) {
|
||||
return NextResponse.json(
|
||||
{ error: '缺少 url 参数' },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
// 解析视频 URL
|
||||
const videoId = parseBilibiliUrl(videoUrl);
|
||||
|
||||
if (!videoId) {
|
||||
// 不是 B站视频或无法解析,返回空选集
|
||||
return NextResponse.json({
|
||||
platform: 'unknown',
|
||||
episodes: [],
|
||||
totalEpisodes: 0,
|
||||
});
|
||||
}
|
||||
|
||||
// 构建 B站 API 请求 URL
|
||||
let apiUrl = 'https://api.bilibili.com/x/web-interface/view?';
|
||||
if (videoId.bvid) {
|
||||
apiUrl += `bvid=${videoId.bvid}`;
|
||||
} else if (videoId.aid) {
|
||||
apiUrl += `aid=${videoId.aid}`;
|
||||
}
|
||||
|
||||
// 调用 B站 API
|
||||
const response = await fetch(apiUrl, {
|
||||
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',
|
||||
'Referer': 'https://www.bilibili.com/',
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
console.error('[API/video/episodes] B站 API 请求失败:', response.status);
|
||||
return NextResponse.json({
|
||||
platform: 'bilibili',
|
||||
episodes: [],
|
||||
totalEpisodes: 0,
|
||||
error: 'B站 API 请求失败',
|
||||
});
|
||||
}
|
||||
|
||||
const data: BilibiliVideoResponse = await response.json();
|
||||
|
||||
if (data.code !== 0 || !data.data) {
|
||||
console.error('[API/video/episodes] B站 API 返回错误:', data.message);
|
||||
return NextResponse.json({
|
||||
platform: 'bilibili',
|
||||
episodes: [],
|
||||
totalEpisodes: 0,
|
||||
error: data.message || '获取视频信息失败',
|
||||
});
|
||||
}
|
||||
|
||||
// 提取选集信息
|
||||
const episodes: VideoEpisode[] = data.data.pages.map((page) => ({
|
||||
page: page.page,
|
||||
part: page.part,
|
||||
duration: page.duration,
|
||||
cid: page.cid,
|
||||
}));
|
||||
|
||||
return NextResponse.json({
|
||||
platform: 'bilibili',
|
||||
bvid: data.data.bvid,
|
||||
title: data.data.title,
|
||||
episodes,
|
||||
totalEpisodes: episodes.length,
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('[API/video/episodes] 错误:', error);
|
||||
return NextResponse.json(
|
||||
{ error: '获取选集信息失败' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user