Files
MoonTVPlus/src/app/api/openlist/list/route.ts
T
2025-12-29 10:19:38 +08:00

172 lines
5.1 KiB
TypeScript

/* eslint-disable no-console */
import { NextRequest, NextResponse } from 'next/server';
import { getAuthInfoFromCookie } from '@/lib/auth';
import { getConfig } from '@/lib/config';
import { db } from '@/lib/db';
import { OpenListClient } from '@/lib/openlist.client';
import {
getCachedMetaInfo,
MetaInfo,
setCachedMetaInfo,
} from '@/lib/openlist-cache';
import { getTMDBImageUrl } from '@/lib/tmdb.search';
export const runtime = 'nodejs';
/**
* GET /api/openlist/list?page=1&pageSize=20&includeFailed=false&noCache=false
* 获取私人影库视频列表
*/
export async function GET(request: NextRequest) {
try {
const authInfo = getAuthInfoFromCookie(request);
if (!authInfo || !authInfo.username) {
return NextResponse.json({ error: '未授权' }, { status: 401 });
}
const { searchParams } = new URL(request.url);
const page = parseInt(searchParams.get('page') || '1');
const pageSize = parseInt(searchParams.get('pageSize') || '20');
const includeFailed = searchParams.get('includeFailed') === 'true';
const noCache = searchParams.get('noCache') === 'true';
const config = await getConfig();
const openListConfig = config.OpenListConfig;
if (
!openListConfig ||
!openListConfig.Enabled ||
!openListConfig.URL ||
!openListConfig.Username ||
!openListConfig.Password
) {
return NextResponse.json(
{ error: 'OpenList 未配置或未启用', list: [], total: 0 },
{ status: 200 }
);
}
const rootPath = openListConfig.RootPath || '/';
const client = new OpenListClient(
openListConfig.URL,
openListConfig.Username,
openListConfig.Password
);
// 读取 metainfo (从数据库或缓存)
let metaInfo: MetaInfo | null = null;
// 如果不使用缓存,直接从数据库读取
if (noCache) {
// noCache 模式:跳过缓存
} else {
metaInfo = getCachedMetaInfo(rootPath);
}
if (!metaInfo) {
try {
const metainfoJson = await db.getGlobalValue('video.metainfo');
if (metainfoJson) {
try {
metaInfo = JSON.parse(metainfoJson);
// 验证数据结构
if (!metaInfo || typeof metaInfo !== 'object') {
throw new Error('metaInfo 不是有效对象');
}
if (!metaInfo.folders || typeof metaInfo.folders !== 'object') {
throw new Error('metaInfo.folders 不存在或不是对象');
}
// 只有在不是 noCache 模式时才更新缓存
if (!noCache) {
setCachedMetaInfo(rootPath, metaInfo);
}
} catch (parseError) {
console.error('[OpenList List] JSON 解析或验证失败:', parseError);
throw new Error(`JSON 解析失败: ${(parseError as Error).message}`);
}
} else {
throw new Error('数据库中没有 metainfo 数据');
}
} catch (error) {
console.error('[OpenList List] 从数据库读取 metainfo 失败:', error);
return NextResponse.json(
{
error: 'metainfo 读取失败',
details: (error as Error).message,
list: [],
total: 0,
},
{ status: 200 }
);
}
}
if (!metaInfo) {
return NextResponse.json(
{ error: '无数据', list: [], total: 0 },
{ status: 200 }
);
}
// 验证 metaInfo 结构
if (!metaInfo.folders || typeof metaInfo.folders !== 'object') {
return NextResponse.json(
{ error: 'metainfo.json 结构无效', list: [], total: 0 },
{ status: 200 }
);
}
// 转换为数组并分页
const allVideos = Object.entries(metaInfo.folders)
.filter(([, info]) => includeFailed || !info.failed) // 根据参数过滤失败的视频
.map(
([key, info]) => {
return {
id: key,
folder: info.folderName,
tmdbId: info.tmdb_id,
doubanId: info.douban_id,
title: info.title,
poster: getTMDBImageUrl(info.poster_path),
releaseDate: info.release_date,
overview: info.overview,
voteAverage: info.vote_average,
mediaType: info.media_type,
lastUpdated: info.last_updated,
failed: info.failed || false,
seasonNumber: info.season_number,
seasonName: info.season_name,
};
}
);
// 按更新时间倒序排序
allVideos.sort((a, b) => b.lastUpdated - a.lastUpdated);
const total = allVideos.length;
const start = (page - 1) * pageSize;
const end = start + pageSize;
const list = allVideos.slice(start, end);
return NextResponse.json({
success: true,
list,
total,
page,
pageSize,
totalPages: Math.ceil(total / pageSize),
});
} catch (error) {
console.error('获取视频列表失败:', error);
return NextResponse.json(
{ error: '获取失败', details: (error as Error).message, list: [], total: 0 },
{ status: 500 }
);
}
}