首页增加短剧推荐

This commit is contained in:
mtvpls
2025-12-18 21:36:31 +08:00
parent 6f780d66fe
commit 0ae5923b4b
12 changed files with 497 additions and 1 deletions

View File

@@ -79,6 +79,16 @@ export async function POST(request: NextRequest) {
adminConfig = refineConfig(adminConfig);
// 更新配置文件
await db.saveAdminConfig(adminConfig);
// 清除短剧视频源缓存(因为配置文件可能包含新的视频源)
try {
await db.deleteGlobalValue('duanju');
console.log('已清除短剧视频源缓存');
} catch (error) {
console.error('清除短剧视频源缓存失败:', error);
// 不影响主流程,继续执行
}
return NextResponse.json({
success: true,
message: '配置文件更新成功',

View File

@@ -85,6 +85,15 @@ export async function POST(req: NextRequest) {
await db.saveAdminConfig(importData.data.adminConfig);
await setCachedConfig(importData.data.adminConfig);
// 清除短剧视频源缓存(因为导入的配置可能包含不同的视频源)
try {
await db.deleteGlobalValue('duanju');
console.log('已清除短剧视频源缓存');
} catch (error) {
console.error('清除短剧视频源缓存失败:', error);
// 不影响主流程,继续执行
}
// 导入用户数据
const userData = importData.data.userData;
for (const username in userData) {

View File

@@ -226,6 +226,15 @@ export async function POST(request: NextRequest) {
// 持久化到存储
await db.saveAdminConfig(adminConfig);
// 清除短剧视频源缓存(因为视频源发生了变动)
try {
await db.deleteGlobalValue('duanju');
console.log('已清除短剧视频源缓存');
} catch (error) {
console.error('清除短剧视频源缓存失败:', error);
// 不影响主流程,继续执行
}
return NextResponse.json(
{ ok: true },
{

View File

@@ -99,6 +99,15 @@ async function refreshConfig() {
config.ConfigSubscribtion.LastCheck = new Date().toISOString();
config = refineConfig(config);
await db.saveAdminConfig(config);
// 清除短剧视频源缓存(因为配置文件可能包含新的视频源)
try {
await db.deleteGlobalValue('duanju');
console.log('已清除短剧视频源缓存');
} catch (error) {
console.error('清除短剧视频源缓存失败:', error);
// 不影响主流程,继续执行
}
} catch (e) {
console.error('刷新配置失败:', e);
}

View File

@@ -0,0 +1,194 @@
/* eslint-disable @typescript-eslint/no-explicit-any,no-console */
import { NextResponse } from 'next/server';
import { API_CONFIG, getCacheTime } from '@/lib/config';
import { getDuanjuSources } from '@/lib/duanju';
import { SearchResult } from '@/lib/types';
import { cleanHtmlTags } from '@/lib/utils';
export const runtime = 'nodejs';
interface ApiSearchItem {
vod_id: string;
vod_name: string;
vod_pic: string;
vod_remarks?: string;
vod_play_url?: string;
vod_class?: string;
vod_year?: string;
vod_content?: string;
vod_douban_id?: number;
type_name?: string;
}
interface CmsClassResponse {
class?: Array<{
type_id: string | number;
type_name: string;
}>;
}
/**
* 获取热播短剧推荐视频
*/
export async function GET() {
try {
// 获取短剧视频源列表
const sources = await getDuanjuSources();
if (!sources || sources.length === 0) {
return NextResponse.json({
code: 200,
message: '暂无短剧视频源',
data: [],
});
}
// 取第一个视频源
const firstSource = sources[0];
console.log(`使用视频源: ${firstSource.name}`);
// 获取该视频源的分类列表找到短剧分类的ID
const classUrl = `${firstSource.api}?ac=list`;
const classResponse = await fetch(classUrl, {
headers: API_CONFIG.search.headers,
});
if (!classResponse.ok) {
throw new Error('获取分类列表失败');
}
const classData: CmsClassResponse = await classResponse.json();
// 找到短剧分类的ID
let duanjuTypeId: string | number | null = null;
if (classData.class && Array.isArray(classData.class)) {
const duanjuClass = classData.class.find((item) => {
const typeName = item.type_name?.toLowerCase() || '';
return (
typeName.includes('短剧') ||
typeName.includes('短视频') ||
typeName.includes('微短剧')
);
});
if (duanjuClass) {
duanjuTypeId = duanjuClass.type_id;
}
}
if (!duanjuTypeId) {
return NextResponse.json({
code: 200,
message: '未找到短剧分类',
data: [],
});
}
console.log(`短剧分类ID: ${duanjuTypeId}`);
// 请求该分类下的视频列表
const videoListUrl = `${firstSource.api}?ac=videolist&t=${duanjuTypeId}&pg=1`;
const videoListResponse = await fetch(videoListUrl, {
headers: API_CONFIG.search.headers,
});
if (!videoListResponse.ok) {
throw new Error('获取视频列表失败');
}
const videoListData = await videoListResponse.json();
if (
!videoListData ||
!videoListData.list ||
!Array.isArray(videoListData.list) ||
videoListData.list.length === 0
) {
return NextResponse.json({
code: 200,
message: '暂无短剧视频',
data: [],
});
}
// 处理视频数据
const videos: SearchResult[] = videoListData.list.map((item: ApiSearchItem) => {
let episodes: string[] = [];
let titles: string[] = [];
// 使用正则表达式从 vod_play_url 提取 m3u8 链接
if (item.vod_play_url) {
// 先用 $$$ 分割
const vod_play_url_array = item.vod_play_url.split('$$$');
// 分集之间#分割,标题和播放链接 $ 分割
vod_play_url_array.forEach((url: string) => {
const matchEpisodes: string[] = [];
const matchTitles: string[] = [];
const title_url_array = url.split('#');
title_url_array.forEach((title_url: string) => {
const episode_title_url = title_url.split('$');
if (
episode_title_url.length === 2 &&
episode_title_url[1].endsWith('.m3u8')
) {
matchTitles.push(episode_title_url[0]);
matchEpisodes.push(episode_title_url[1]);
}
});
if (matchEpisodes.length > episodes.length) {
episodes = matchEpisodes;
titles = matchTitles;
}
});
}
return {
id: item.vod_id.toString(),
title: item.vod_name.trim().replace(/\s+/g, ' '),
poster: item.vod_pic,
episodes,
episodes_titles: titles,
source: firstSource.key,
source_name: firstSource.name,
class: item.vod_class,
year: item.vod_year ? item.vod_year.match(/\d{4}/)?.[0] || '' : 'unknown',
desc: cleanHtmlTags(item.vod_content || ''),
type_name: item.type_name,
douban_id: item.vod_douban_id,
};
});
// 过滤掉集数为 0 的结果,并限制返回数量
const filteredVideos = videos
.filter((video) => video.episodes.length > 0)
.slice(0, 20);
console.log(`返回 ${filteredVideos.length} 个短剧视频`);
const cacheTime = await getCacheTime();
return NextResponse.json(
{
code: 200,
message: '获取成功',
data: filteredVideos,
},
{
headers: {
'Cache-Control': `public, max-age=${cacheTime}, s-maxage=${cacheTime}`,
},
}
);
} catch (error) {
console.error('获取热播短剧推荐失败:', error);
return NextResponse.json(
{
code: 500,
message: '获取热播短剧推荐失败',
error: (error as Error).message,
},
{ status: 500 }
);
}
}

View File

@@ -0,0 +1,41 @@
/* eslint-disable @typescript-eslint/no-explicit-any,no-console */
import { NextResponse } from 'next/server';
import { getCacheTime } from '@/lib/config';
import { getDuanjuSources } from '@/lib/duanju';
export const runtime = 'nodejs';
/**
* 获取包含短剧分类的视频源列表
*/
export async function GET() {
try {
const sources = await getDuanjuSources();
const cacheTime = await getCacheTime();
return NextResponse.json(
{
code: 200,
message: '获取成功',
data: sources,
},
{
headers: {
'Cache-Control': `public, max-age=${cacheTime}, s-maxage=${cacheTime}`,
},
}
);
} catch (error) {
console.error('获取短剧视频源失败:', error);
return NextResponse.json(
{
code: 500,
message: '获取短剧视频源失败',
error: (error as Error).message,
},
{ status: 500 }
);
}
}