变更私人影库的key值方式

This commit is contained in:
mtvpls
2025-12-26 21:14:31 +08:00
parent df78abaf11
commit 3050cd48a9
15 changed files with 120 additions and 84 deletions
+11 -9
View File
@@ -307,12 +307,12 @@ async function handleOpenListProxy(request: NextRequest) {
if (wd) {
const results = Object.entries(metaInfo.folders)
.filter(
([folderName, info]) =>
folderName.toLowerCase().includes(wd.toLowerCase()) ||
([key, info]) =>
info.folderName.toLowerCase().includes(wd.toLowerCase()) ||
info.title.toLowerCase().includes(wd.toLowerCase())
)
.map(([folderName, info]) => ({
vod_id: folderName,
.map(([key, info]) => ({
vod_id: key,
vod_name: info.title,
vod_pic: getTMDBImageUrl(info.poster_path),
vod_remarks: info.media_type === 'movie' ? '电影' : '剧集',
@@ -333,8 +333,8 @@ async function handleOpenListProxy(request: NextRequest) {
// 详情模式
if (ids) {
const folderName = ids;
const info = metaInfo.folders[folderName];
const key = ids;
const info = metaInfo.folders[key];
if (!info) {
return NextResponse.json(
@@ -343,6 +343,8 @@ async function handleOpenListProxy(request: NextRequest) {
);
}
const folderName = info.folderName;
// 获取视频详情
try {
const detailResponse = await fetch(
@@ -376,7 +378,7 @@ async function handleOpenListProxy(request: NextRequest) {
total: 1,
list: [
{
vod_id: folderName,
vod_id: key,
vod_name: info.title,
vod_pic: getTMDBImageUrl(info.poster_path),
vod_remarks: info.media_type === 'movie' ? '电影' : '剧集',
@@ -399,8 +401,8 @@ async function handleOpenListProxy(request: NextRequest) {
// 默认返回所有视频
const results = Object.entries(metaInfo.folders).map(
([folderName, info]) => ({
vod_id: folderName,
([key, info]) => ({
vod_id: key,
vod_name: info.title,
vod_pic: getTMDBImageUrl(info.poster_path),
vod_remarks: info.media_type === 'movie' ? '电影' : '剧集',
+14 -23
View File
@@ -37,10 +37,10 @@ export async function GET(request: NextRequest) {
}
const rootPath = openListConfig.RootPath || '/';
const folderPath = `${rootPath}${rootPath.endsWith('/') ? '' : '/'}${id}`;
// 1. 读取 metainfo 获取元数据
let metaInfo: any = null;
let folderMeta: any = null;
try {
const { getCachedMetaInfo, setCachedMetaInfo } = await import('@/lib/openlist-cache');
const { db } = await import('@/lib/db');
@@ -54,10 +54,20 @@ export async function GET(request: NextRequest) {
setCachedMetaInfo(rootPath, metaInfo);
}
}
// 使用 key 查找文件夹信息
folderMeta = metaInfo?.folders?.[id];
if (!folderMeta) {
throw new Error('未找到该视频信息');
}
} catch (error) {
// 忽略错误
throw new Error('读取视频信息失败: ' + (error as Error).message);
}
// 使用 folderName 构建实际路径
const folderName = folderMeta.folderName;
const folderPath = `${rootPath}${rootPath.endsWith('/') ? '' : '/'}${folderName}`;
// 2. 直接调用 OpenList 客户端获取视频列表
const { OpenListClient } = await import('@/lib/openlist.client');
const { getCachedVideoInfo, setCachedVideoInfo } = await import('@/lib/openlist-cache');
@@ -71,24 +81,6 @@ export async function GET(request: NextRequest) {
let videoInfo = getCachedVideoInfo(folderPath);
if (!videoInfo) {
try {
const videoinfoPath = `${folderPath}/videoinfo.json`;
const fileResponse = await client.getFile(videoinfoPath);
if (fileResponse.code === 200 && fileResponse.data.raw_url) {
const contentResponse = await fetch(fileResponse.data.raw_url);
const content = await contentResponse.text();
videoInfo = JSON.parse(content);
if (videoInfo) {
setCachedVideoInfo(folderPath, videoInfo);
}
}
} catch (error) {
// 忽略错误
}
}
const listResponse = await client.listDirectory(folderPath);
if (listResponse.code !== 200) {
@@ -138,19 +130,18 @@ export async function GET(request: NextRequest) {
.sort((a, b) => a.episode !== b.episode ? a.episode - b.episode : a.fileName.localeCompare(b.fileName));
// 3. 从 metainfo 中获取元数据
const folderMeta = metaInfo?.folders?.[id];
const { getTMDBImageUrl } = await import('@/lib/tmdb.search');
const result = {
source: 'openlist',
source_name: '私人影库',
id: id,
title: folderMeta?.title || id,
title: folderMeta?.title || folderName,
poster: folderMeta?.poster_path ? getTMDBImageUrl(folderMeta.poster_path) : '',
year: folderMeta?.release_date ? folderMeta.release_date.split('-')[0] : '',
douban_id: 0,
desc: folderMeta?.overview || '',
episodes: episodes.map((ep) => `/api/openlist/play?folder=${encodeURIComponent(id)}&fileName=${encodeURIComponent(ep.fileName)}`),
episodes: episodes.map((ep) => `/api/openlist/play?folder=${encodeURIComponent(folderName)}&fileName=${encodeURIComponent(ep.fileName)}`),
episodes_titles: episodes.map((ep) => ep.title),
};
+15 -3
View File
@@ -28,7 +28,7 @@ export async function POST(request: NextRequest) {
const body = await request.json();
const {
folder,
key,
tmdbId,
title,
posterPath,
@@ -40,7 +40,7 @@ export async function POST(request: NextRequest) {
seasonName,
} = body;
if (!folder || !tmdbId) {
if (!key || !tmdbId) {
return NextResponse.json(
{ error: '缺少必要参数' },
{ status: 400 }
@@ -97,8 +97,20 @@ export async function POST(request: NextRequest) {
);
}
// 检查 key 是否存在
if (!metaInfo.folders[key]) {
return NextResponse.json(
{ error: '视频不存在' },
{ status: 404 }
);
}
// 保留原始文件夹名称
const folderName = metaInfo.folders[key].folderName;
// 更新视频信息
metaInfo.folders[folder] = {
metaInfo.folders[key] = {
folderName: folderName,
tmdb_id: tmdbId,
title: title,
poster_path: posterPath,
+7 -7
View File
@@ -28,10 +28,10 @@ export async function POST(request: NextRequest) {
// 获取请求参数
const body = await request.json();
const { folder } = body;
const { key } = body;
if (!folder) {
return NextResponse.json({ error: '缺少 folder 参数' }, { status: 400 });
if (!key) {
return NextResponse.json({ error: '缺少 key 参数' }, { status: 400 });
}
// 获取配置
@@ -62,16 +62,16 @@ export async function POST(request: NextRequest) {
const metaInfo: MetaInfo = JSON.parse(metainfoContent);
// 检查文件夹是否存在
if (!metaInfo.folders[folder]) {
// 检查 key 是否存在
if (!metaInfo.folders[key]) {
return NextResponse.json(
{ error: '未找到该视频记录' },
{ status: 404 }
);
}
// 删除文件夹记录
delete metaInfo.folders[folder];
// 删除记录
delete metaInfo.folders[key];
// 保存到数据库
const updatedMetainfoContent = JSON.stringify(metaInfo);
+3 -9
View File
@@ -125,16 +125,10 @@ export async function GET(request: NextRequest) {
const allVideos = Object.entries(metaInfo.folders)
.filter(([, info]) => includeFailed || !info.failed) // 根据参数过滤失败的视频
.map(
([folderName, info]) => {
// 构建 id:如果是第二季及以后,id 也要包含季度信息
let videoId = folderName;
if (info.season_number && info.season_number > 1 && info.season_name) {
videoId = `${folderName} ${info.season_name}`;
}
([key, info]) => {
return {
id: videoId,
folder: folderName,
id: key,
folder: info.folderName,
tmdbId: info.tmdb_id,
title: info.title,
poster: getTMDBImageUrl(info.poster_path),
@@ -48,15 +48,6 @@ export async function POST(request: NextRequest) {
openListConfig.Password
);
// 删除 videoinfo.json
const videoinfoPath = `${folderPath}/videoinfo.json`;
try {
await client.deleteFile(videoinfoPath);
} catch (error) {
console.log('videoinfo.json 不存在或删除失败');
}
// 清除缓存
invalidateVideoInfoCache(folderPath);
+21 -4
View File
@@ -4,6 +4,7 @@ import { NextRequest, NextResponse } from 'next/server';
import { getAuthInfoFromCookie } from '@/lib/auth';
import { getConfig } from '@/lib/config';
import { generateFolderKey } from '@/lib/crypto';
import { db } from '@/lib/db';
import { OpenListClient } from '@/lib/openlist.client';
import {
@@ -188,6 +189,15 @@ async function performScan(
let existingCount = 0;
let errorCount = 0;
// 收集已存在的 key,用于冲突检测
const existingKeys = new Set<string>(Object.keys(metaInfo.folders));
// 创建文件夹名到 key 的映射(用于查找已存在的文件夹)
const folderNameToKey = new Map<string, string>();
for (const [key, info] of Object.entries(metaInfo.folders)) {
folderNameToKey.set(info.folderName, key);
}
for (let i = 0; i < folders.length; i++) {
const folder = folders[i];
@@ -195,11 +205,15 @@ async function performScan(
updateScanTaskProgress(taskId, i + 1, folders.length, folder.name);
// 如果是立即扫描(不清空 metainfo),且文件夹已存在,跳过
if (!clearMetaInfo && metaInfo.folders[folder.name]) {
if (!clearMetaInfo && folderNameToKey.has(folder.name)) {
existingCount++;
continue;
}
// 生成文件夹的 key
const folderKey = generateFolderKey(folder.name, existingKeys);
existingKeys.add(folderKey);
try {
// 解析文件夹名称,提取季度信息和年份
const seasonInfo = parseSeasonFromTitle(folder.name);
@@ -221,6 +235,7 @@ async function performScan(
// 基础信息
const folderInfo: any = {
folderName: folder.name, // 保存原始文件夹名称
tmdb_id: result.id,
title: result.title || result.name || folder.name,
poster_path: result.poster_path,
@@ -275,11 +290,12 @@ async function performScan(
}
}
metaInfo.folders[folder.name] = folderInfo;
metaInfo.folders[folderKey] = folderInfo;
newCount++;
} else {
// 记录失败的文件夹
metaInfo.folders[folder.name] = {
metaInfo.folders[folderKey] = {
folderName: folder.name, // 保存原始文件夹名称
tmdb_id: 0,
title: folder.name,
poster_path: null,
@@ -298,7 +314,8 @@ async function performScan(
} catch (error) {
console.error(`[OpenList Refresh] 处理文件夹失败: ${folder.name}`, error);
// 记录失败的文件夹
metaInfo.folders[folder.name] = {
metaInfo.folders[folderKey] = {
folderName: folder.name, // 保存原始文件夹名称
tmdb_id: 0,
title: folder.name,
poster_path: null,
+4 -4
View File
@@ -72,13 +72,13 @@ export async function GET(request: NextRequest) {
if (metaInfo && metaInfo.folders) {
openlistResults = Object.entries(metaInfo.folders)
.filter(([folderName, info]: [string, any]) => {
const matchFolder = folderName.toLowerCase().includes(query.toLowerCase());
.filter(([key, info]: [string, any]) => {
const matchFolder = info.folderName.toLowerCase().includes(query.toLowerCase());
const matchTitle = info.title.toLowerCase().includes(query.toLowerCase());
return matchFolder || matchTitle;
})
.map(([folderName, info]: [string, any]) => ({
id: folderName,
.map(([key, info]: [string, any]) => ({
id: key,
source: 'openlist',
source_name: '私人影库',
title: info.title,
+4 -4
View File
@@ -109,13 +109,13 @@ export async function GET(request: NextRequest) {
if (metaInfo && metaInfo.folders) {
const openlistResults = Object.entries(metaInfo.folders)
.filter(([folderName, info]: [string, any]) => {
const matchFolder = folderName.toLowerCase().includes(query.toLowerCase());
.filter(([key, info]: [string, any]) => {
const matchFolder = info.folderName.toLowerCase().includes(query.toLowerCase());
const matchTitle = info.title.toLowerCase().includes(query.toLowerCase());
return matchFolder || matchTitle;
})
.map(([folderName, info]: [string, any]) => ({
id: folderName,
.map(([key, info]: [string, any]) => ({
id: key,
source: 'openlist',
source_name: '私人影库',
title: info.title,