新增源站寻片
This commit is contained in:
77
src/app/api/source-search/categories/route.ts
Normal file
77
src/app/api/source-search/categories/route.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
|
||||
import { getAuthInfoFromCookie } from '@/lib/auth';
|
||||
import { API_CONFIG, getAvailableApiSites } from '@/lib/config';
|
||||
|
||||
export const runtime = 'nodejs';
|
||||
|
||||
interface CmsClassResponse {
|
||||
class?: Array<{
|
||||
type_id: string | number;
|
||||
type_name: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定视频源的分类列表
|
||||
*/
|
||||
export async function GET(request: NextRequest) {
|
||||
const authInfo = getAuthInfoFromCookie(request);
|
||||
if (!authInfo || !authInfo.username) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
}
|
||||
|
||||
const { searchParams } = new URL(request.url);
|
||||
const sourceKey = searchParams.get('source');
|
||||
|
||||
if (!sourceKey) {
|
||||
return NextResponse.json(
|
||||
{ error: '缺少参数: source' },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
const apiSites = await getAvailableApiSites(authInfo.username);
|
||||
const targetSite = apiSites.find((site) => site.key === sourceKey);
|
||||
|
||||
if (!targetSite) {
|
||||
return NextResponse.json(
|
||||
{ error: `未找到指定的视频源: ${sourceKey}` },
|
||||
{ status: 404 }
|
||||
);
|
||||
}
|
||||
|
||||
// 请求分类列表
|
||||
const classUrl = `${targetSite.api}?ac=list`;
|
||||
const classResponse = await fetch(classUrl, {
|
||||
headers: API_CONFIG.search.headers,
|
||||
signal: AbortSignal.timeout(10000),
|
||||
});
|
||||
|
||||
if (!classResponse.ok) {
|
||||
throw new Error('获取分类列表失败');
|
||||
}
|
||||
|
||||
const classData: CmsClassResponse = await classResponse.json();
|
||||
|
||||
if (!classData.class || !Array.isArray(classData.class)) {
|
||||
return NextResponse.json({
|
||||
categories: [],
|
||||
});
|
||||
}
|
||||
|
||||
return NextResponse.json({
|
||||
categories: classData.class.map((item) => ({
|
||||
id: item.type_id.toString(),
|
||||
name: item.type_name,
|
||||
})),
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Failed to get categories:', error);
|
||||
return NextResponse.json(
|
||||
{ error: '获取分类列表失败' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
128
src/app/api/source-search/search/route.ts
Normal file
128
src/app/api/source-search/search/route.ts
Normal file
@@ -0,0 +1,128 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
|
||||
import { getAuthInfoFromCookie } from '@/lib/auth';
|
||||
import { API_CONFIG, getAvailableApiSites } from '@/lib/config';
|
||||
import { SearchResult } from '@/lib/types';
|
||||
|
||||
export const runtime = 'nodejs';
|
||||
|
||||
interface CmsVideoItem {
|
||||
vod_id: string | number;
|
||||
vod_name: string;
|
||||
vod_pic: string;
|
||||
vod_remarks?: string;
|
||||
vod_year?: string;
|
||||
vod_play_from?: string;
|
||||
vod_play_url?: string;
|
||||
}
|
||||
|
||||
interface CmsVideoResponse {
|
||||
list?: CmsVideoItem[];
|
||||
total?: number;
|
||||
page?: number;
|
||||
pagecount?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 在指定视频源中搜索视频
|
||||
*/
|
||||
export async function GET(request: NextRequest) {
|
||||
const authInfo = getAuthInfoFromCookie(request);
|
||||
if (!authInfo || !authInfo.username) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
}
|
||||
|
||||
const { searchParams } = new URL(request.url);
|
||||
const sourceKey = searchParams.get('source');
|
||||
const keyword = searchParams.get('keyword');
|
||||
const page = searchParams.get('page') || '1';
|
||||
|
||||
if (!sourceKey) {
|
||||
return NextResponse.json(
|
||||
{ error: '缺少参数: source' },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
if (!keyword || keyword.trim() === '') {
|
||||
return NextResponse.json(
|
||||
{ error: '缺少参数: keyword' },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
const apiSites = await getAvailableApiSites(authInfo.username);
|
||||
const targetSite = apiSites.find((site) => site.key === sourceKey);
|
||||
|
||||
if (!targetSite) {
|
||||
return NextResponse.json(
|
||||
{ error: `未找到指定的视频源: ${sourceKey}` },
|
||||
{ status: 404 }
|
||||
);
|
||||
}
|
||||
|
||||
// 请求搜索结果
|
||||
const searchUrl = `${targetSite.api}?ac=videolist&wd=${encodeURIComponent(keyword)}&pg=${page}`;
|
||||
const searchResponse = await fetch(searchUrl, {
|
||||
headers: API_CONFIG.search.headers,
|
||||
signal: AbortSignal.timeout(10000),
|
||||
});
|
||||
|
||||
if (!searchResponse.ok) {
|
||||
throw new Error('搜索失败');
|
||||
}
|
||||
|
||||
const searchData: CmsVideoResponse = await searchResponse.json();
|
||||
|
||||
if (!searchData.list || !Array.isArray(searchData.list)) {
|
||||
return NextResponse.json({
|
||||
results: [],
|
||||
total: 0,
|
||||
page: parseInt(page),
|
||||
pageCount: 0,
|
||||
});
|
||||
}
|
||||
|
||||
// 转换为 SearchResult 格式
|
||||
const results: SearchResult[] = searchData.list.map((item) => {
|
||||
const episodes: string[] = [];
|
||||
const episodes_titles: string[] = [];
|
||||
|
||||
// 解析播放信息
|
||||
if (item.vod_play_url && item.vod_play_from) {
|
||||
const playUrls = item.vod_play_url.split('#');
|
||||
playUrls.forEach((episodeStr) => {
|
||||
if (episodeStr.trim()) {
|
||||
const [name, url] = episodeStr.split('$');
|
||||
if (name && url) {
|
||||
episodes.push(url.trim());
|
||||
episodes_titles.push(name.trim());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
id: item.vod_id.toString(),
|
||||
title: item.vod_name,
|
||||
poster: item.vod_pic || '',
|
||||
year: item.vod_year || 'unknown',
|
||||
episodes,
|
||||
episodes_titles,
|
||||
source: targetSite.key,
|
||||
source_name: targetSite.name,
|
||||
};
|
||||
});
|
||||
|
||||
return NextResponse.json({
|
||||
results,
|
||||
total: searchData.total || 0,
|
||||
page: parseInt(page),
|
||||
pageCount: searchData.pagecount || 0,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Failed to search videos:', error);
|
||||
return NextResponse.json({ error: '搜索失败' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
31
src/app/api/source-search/sources/route.ts
Normal file
31
src/app/api/source-search/sources/route.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
|
||||
import { getAuthInfoFromCookie } from '@/lib/auth';
|
||||
import { getAvailableApiSites } from '@/lib/config';
|
||||
|
||||
export const runtime = 'nodejs';
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
const authInfo = getAuthInfoFromCookie(request);
|
||||
if (!authInfo || !authInfo.username) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
}
|
||||
|
||||
try {
|
||||
const apiSites = await getAvailableApiSites(authInfo.username);
|
||||
|
||||
return NextResponse.json({
|
||||
sources: apiSites.map((site) => ({
|
||||
key: site.key,
|
||||
name: site.name,
|
||||
api: site.api,
|
||||
})),
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Failed to get available API sites:', error);
|
||||
return NextResponse.json(
|
||||
{ error: 'Failed to load sources' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
131
src/app/api/source-search/videos/route.ts
Normal file
131
src/app/api/source-search/videos/route.ts
Normal file
@@ -0,0 +1,131 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
|
||||
import { getAuthInfoFromCookie } from '@/lib/auth';
|
||||
import { API_CONFIG, getAvailableApiSites } from '@/lib/config';
|
||||
import { SearchResult } from '@/lib/types';
|
||||
|
||||
export const runtime = 'nodejs';
|
||||
|
||||
interface CmsVideoItem {
|
||||
vod_id: string | number;
|
||||
vod_name: string;
|
||||
vod_pic: string;
|
||||
vod_remarks?: string;
|
||||
vod_year?: string;
|
||||
vod_play_from?: string;
|
||||
vod_play_url?: string;
|
||||
}
|
||||
|
||||
interface CmsVideoResponse {
|
||||
list?: CmsVideoItem[];
|
||||
total?: number;
|
||||
page?: number;
|
||||
pagecount?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定视频源的分类视频列表
|
||||
*/
|
||||
export async function GET(request: NextRequest) {
|
||||
const authInfo = getAuthInfoFromCookie(request);
|
||||
if (!authInfo || !authInfo.username) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
}
|
||||
|
||||
const { searchParams } = new URL(request.url);
|
||||
const sourceKey = searchParams.get('source');
|
||||
const categoryId = searchParams.get('categoryId');
|
||||
const page = searchParams.get('page') || '1';
|
||||
|
||||
if (!sourceKey) {
|
||||
return NextResponse.json(
|
||||
{ error: '缺少参数: source' },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
if (!categoryId) {
|
||||
return NextResponse.json(
|
||||
{ error: '缺少参数: categoryId' },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
const apiSites = await getAvailableApiSites(authInfo.username);
|
||||
const targetSite = apiSites.find((site) => site.key === sourceKey);
|
||||
|
||||
if (!targetSite) {
|
||||
return NextResponse.json(
|
||||
{ error: `未找到指定的视频源: ${sourceKey}` },
|
||||
{ status: 404 }
|
||||
);
|
||||
}
|
||||
|
||||
// 请求分类视频列表
|
||||
const videoUrl = `${targetSite.api}?ac=videolist&t=${categoryId}&pg=${page}`;
|
||||
const videoResponse = await fetch(videoUrl, {
|
||||
headers: API_CONFIG.search.headers,
|
||||
signal: AbortSignal.timeout(10000),
|
||||
});
|
||||
|
||||
if (!videoResponse.ok) {
|
||||
throw new Error('获取视频列表失败');
|
||||
}
|
||||
|
||||
const videoData: CmsVideoResponse = await videoResponse.json();
|
||||
|
||||
if (!videoData.list || !Array.isArray(videoData.list)) {
|
||||
return NextResponse.json({
|
||||
results: [],
|
||||
total: 0,
|
||||
page: parseInt(page),
|
||||
pageCount: 0,
|
||||
});
|
||||
}
|
||||
|
||||
// 转换为 SearchResult 格式
|
||||
const results: SearchResult[] = videoData.list.map((item) => {
|
||||
const episodes: string[] = [];
|
||||
const episodes_titles: string[] = [];
|
||||
|
||||
// 解析播放信息
|
||||
if (item.vod_play_url && item.vod_play_from) {
|
||||
const playUrls = item.vod_play_url.split('#');
|
||||
playUrls.forEach((episodeStr) => {
|
||||
if (episodeStr.trim()) {
|
||||
const [name, url] = episodeStr.split('$');
|
||||
if (name && url) {
|
||||
episodes.push(url.trim());
|
||||
episodes_titles.push(name.trim());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
id: item.vod_id.toString(),
|
||||
title: item.vod_name,
|
||||
poster: item.vod_pic || '',
|
||||
year: item.vod_year || 'unknown',
|
||||
episodes,
|
||||
episodes_titles,
|
||||
source: targetSite.key,
|
||||
source_name: targetSite.name,
|
||||
};
|
||||
});
|
||||
|
||||
return NextResponse.json({
|
||||
results,
|
||||
total: videoData.total || 0,
|
||||
page: parseInt(page),
|
||||
pageCount: videoData.pagecount || 0,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Failed to get videos:', error);
|
||||
return NextResponse.json(
|
||||
{ error: '获取视频列表失败' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user