修改metainfo为数据库读取,修复tmdb代理连接错误

This commit is contained in:
mtvpls
2025-12-22 20:58:14 +08:00
parent 7ca6379d93
commit 0450edf9bc
13 changed files with 154 additions and 271 deletions

View File

@@ -3,6 +3,7 @@
import { NextRequest, NextResponse } from 'next/server';
import { getConfig } from '@/lib/config';
import { db } from '@/lib/db';
import { OpenListClient } from '@/lib/openlist.client';
import {
getCachedMetaInfo,
@@ -273,30 +274,19 @@ async function handleOpenListProxy(request: NextRequest) {
const rootPath = openListConfig.RootPath || '/';
const client = new OpenListClient(openListConfig.URL, openListConfig.Token);
// 读取 metainfo.json
// 读取 metainfo (从数据库或缓存)
let metaInfo: MetaInfo | null = getCachedMetaInfo(rootPath);
if (!metaInfo) {
try {
const metainfoPath = `${rootPath}${rootPath.endsWith('/') ? '' : '/'}metainfo.json`;
const fileResponse = await client.getFile(metainfoPath);
if (fileResponse.code === 200 && fileResponse.data.raw_url) {
const downloadUrl = fileResponse.data.raw_url;
const contentResponse = await fetch(downloadUrl, {
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',
'Accept': '*/*',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
},
});
const content = await contentResponse.text();
metaInfo = JSON.parse(content) as MetaInfo;
const metainfoJson = await db.getGlobalValue('video.metainfo');
if (metainfoJson) {
metaInfo = JSON.parse(metainfoJson) as MetaInfo;
setCachedMetaInfo(rootPath, metaInfo);
}
} catch (error) {
return NextResponse.json(
{ code: 0, msg: 'metainfo.json 不存在', list: [] },
{ code: 0, msg: 'metainfo 不存在', list: [] },
{ status: 200 }
);
}

View File

@@ -32,35 +32,24 @@ export async function GET(request: NextRequest) {
const rootPath = openListConfig.RootPath || '/';
// 1. 读取 metainfo.json 获取元数据
// 1. 读取 metainfo 获取元数据
let metaInfo: any = null;
try {
const { OpenListClient } = await import('@/lib/openlist.client');
const { getCachedMetaInfo } = await import('@/lib/openlist-cache');
const { getCachedMetaInfo, setCachedMetaInfo } = await import('@/lib/openlist-cache');
const { getTMDBImageUrl } = await import('@/lib/tmdb.search');
const { db } = await import('@/lib/db');
const client = new OpenListClient(openListConfig.URL, openListConfig.Token);
metaInfo = getCachedMetaInfo(rootPath);
if (!metaInfo) {
const metainfoPath = `${rootPath}${rootPath.endsWith('/') ? '' : '/'}metainfo.json`;
const fileResponse = await client.getFile(metainfoPath);
if (fileResponse.code === 200 && fileResponse.data.raw_url) {
const downloadUrl = fileResponse.data.raw_url;
const contentResponse = await fetch(downloadUrl, {
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',
'Accept': '*/*',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
},
});
const content = await contentResponse.text();
metaInfo = JSON.parse(content);
const metainfoJson = await db.getGlobalValue('video.metainfo');
if (metainfoJson) {
metaInfo = JSON.parse(metainfoJson);
setCachedMetaInfo(rootPath, metaInfo);
}
}
} catch (error) {
console.error('[Detail] 读取 metainfo.json 失败:', error);
console.error('[Detail] 从数据库读取 metainfo 失败:', error);
}
// 2. 调用 openlist detail API

View File

@@ -4,6 +4,7 @@ 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,
@@ -53,35 +54,21 @@ export async function POST(request: NextRequest) {
openListConfig.Password
);
// 读取现有 metainfo.json
// 读取现有 metainfo (从数据库或缓存)
let metaInfo: MetaInfo | null = getCachedMetaInfo(rootPath);
if (!metaInfo) {
try {
const metainfoPath = `${rootPath}${rootPath.endsWith('/') ? '' : '/'}metainfo.json`;
const fileResponse = await client.getFile(metainfoPath);
console.log('[OpenList Correct] 尝试从数据库读取 metainfo');
const metainfoJson = await db.getGlobalValue('video.metainfo');
if (fileResponse.code === 200 && fileResponse.data.raw_url) {
const downloadUrl = fileResponse.data.raw_url;
const contentResponse = await fetch(downloadUrl, {
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',
'Accept': '*/*',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
},
});
if (!contentResponse.ok) {
throw new Error(`下载失败: ${contentResponse.status}`);
}
const content = await contentResponse.text();
metaInfo = JSON.parse(content);
if (metainfoJson) {
metaInfo = JSON.parse(metainfoJson);
}
} catch (error) {
console.error('[OpenList Correct] 读取 metainfo.json 失败:', error);
console.error('[OpenList Correct] 从数据库读取 metainfo 失败:', error);
return NextResponse.json(
{ error: 'metainfo.json 读取失败' },
{ error: 'metainfo 读取失败' },
{ status: 500 }
);
}
@@ -107,11 +94,10 @@ export async function POST(request: NextRequest) {
failed: false, // 纠错后标记为成功
};
// 保存 metainfo.json
const metainfoPath = `${rootPath}${rootPath.endsWith('/') ? '' : '/'}metainfo.json`;
const metainfoContent = JSON.stringify(metaInfo, null, 2);
// 保存 metainfo 到数据库
const metainfoContent = JSON.stringify(metaInfo);
await client.uploadFile(metainfoPath, metainfoContent);
await db.setGlobalValue('video.metainfo', metainfoContent);
// 更新缓存
invalidateMetaInfoCache(rootPath);

View File

@@ -125,14 +125,7 @@ export async function GET(request: NextRequest) {
};
}
// 保存 videoinfo.json
const videoinfoPath = `${folderPath}/videoinfo.json`;
await client.uploadFile(
videoinfoPath,
JSON.stringify(videoInfo, null, 2)
);
// 缓存
// 仅缓存到内存,不再持久化到 OpenList
setCachedVideoInfo(folderPath, videoInfo);
}

View File

@@ -4,6 +4,7 @@ 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,
@@ -48,7 +49,7 @@ export async function GET(request: NextRequest) {
openListConfig.Password
);
// 读取 metainfo.json
// 读取 metainfo (从数据库或缓存)
let metaInfo: MetaInfo | null = getCachedMetaInfo(rootPath);
console.log('[OpenList List] 缓存检查:', {
@@ -58,40 +59,15 @@ export async function GET(request: NextRequest) {
if (!metaInfo) {
try {
const metainfoPath = `${rootPath}${rootPath.endsWith('/') ? '' : '/'}metainfo.json`;
console.log('[OpenList List] 尝试读取 metainfo.json:', metainfoPath);
console.log('[OpenList List] 尝试从数据库读取 metainfo');
const fileResponse = await client.getFile(metainfoPath);
console.log('[OpenList List] getFile 完整响应:', JSON.stringify(fileResponse, null, 2));
const metainfoJson = await db.getGlobalValue('video.metainfo');
if (fileResponse.code === 200 && fileResponse.data.raw_url) {
console.log('[OpenList List] 使用 raw_url 获取文件内容');
const downloadUrl = fileResponse.data.raw_url;
console.log('[OpenList List] 下载 URL:', downloadUrl);
const contentResponse = await fetch(downloadUrl, {
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',
'Accept': '*/*',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
},
});
console.log('[OpenList List] fetch 响应:', {
status: contentResponse.status,
ok: contentResponse.ok,
});
if (!contentResponse.ok) {
throw new Error(`获取文件内容失败: ${contentResponse.status}`);
}
const content = await contentResponse.text();
console.log('[OpenList List] 文件内容长度:', content.length);
console.log('[OpenList List] 文件内容预览:', content.substring(0, 200));
if (metainfoJson) {
console.log('[OpenList List] 从数据库获取到数据,长度:', metainfoJson.length);
try {
metaInfo = JSON.parse(content);
metaInfo = JSON.parse(metainfoJson);
console.log('[OpenList List] JSON 解析成功');
console.log('[OpenList List] metaInfo 结构:', {
hasfolders: !!metaInfo?.folders,
@@ -114,18 +90,13 @@ export async function GET(request: NextRequest) {
throw new Error(`JSON 解析失败: ${(parseError as Error).message}`);
}
} else {
console.error('[OpenList List] getFile 失败或无 sign:', {
code: fileResponse.code,
message: fileResponse.message,
data: fileResponse.data,
});
throw new Error(`getFile 返回错误: code=${fileResponse.code}, message=${fileResponse.message}`);
throw new Error('数据库中没有 metainfo 数据');
}
} catch (error) {
console.error('[OpenList List] 读取 metainfo.json 失败:', error);
console.error('[OpenList List] 从数据库读取 metainfo 失败:', error);
return NextResponse.json(
{
error: 'metainfo.json 读取失败',
error: 'metainfo 读取失败',
details: (error as Error).message,
list: [],
total: 0,

View File

@@ -117,54 +117,25 @@ async function performScan(
updateScanTaskProgress(taskId, 0, 0);
try {
// 1. 读取现有 metainfo.json (如果存在)
// 1. 读取现有 metainfo (从数据库或缓存)
let existingMetaInfo: MetaInfo | null = getCachedMetaInfo(rootPath);
if (!existingMetaInfo) {
try {
const metainfoPath = `${rootPath}${rootPath.endsWith('/') ? '' : '/'}metainfo.json`;
console.log('[OpenList Refresh] 尝试读取现有 metainfo.json:', metainfoPath);
console.log('[OpenList Refresh] 尝试从数据库读取 metainfo');
const metainfoJson = await db.getGlobalValue('video.metainfo');
const fileResponse = await client.getFile(metainfoPath);
console.log('[OpenList Refresh] getFile 完整响应:', JSON.stringify(fileResponse, null, 2));
if (fileResponse.code === 200 && fileResponse.data.raw_url) {
const downloadUrl = fileResponse.data.raw_url;
console.log('[OpenList Refresh] 下载 URL:', downloadUrl);
const contentResponse = await fetch(downloadUrl, {
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',
'Accept': '*/*',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
},
});
console.log('[OpenList Refresh] fetch 响应:', {
status: contentResponse.status,
ok: contentResponse.ok,
});
if (!contentResponse.ok) {
throw new Error(`下载失败: ${contentResponse.status}`);
}
const content = await contentResponse.text();
console.log('[OpenList Refresh] 文件内容:', {
length: content.length,
preview: content.substring(0, 300),
});
existingMetaInfo = JSON.parse(content);
console.log('[OpenList Refresh] 读取到现有数据:', {
if (metainfoJson) {
existingMetaInfo = JSON.parse(metainfoJson);
console.log('[OpenList Refresh] 从数据库读取到现有数据:', {
hasfolders: !!existingMetaInfo?.folders,
foldersType: typeof existingMetaInfo?.folders,
videoCount: Object.keys(existingMetaInfo?.folders || {}).length,
});
}
} catch (error) {
console.error('[OpenList Refresh] 读取 metainfo.json 失败:', error);
console.log('[OpenList Refresh] 将创建新文件');
console.error('[OpenList Refresh] 从数据库读取 metainfo 失败:', error);
console.log('[OpenList Refresh] 将创建新数据');
}
} else {
console.log('[OpenList Refresh] 使用缓存的 metainfo视频数:', Object.keys(existingMetaInfo.folders).length);
@@ -294,41 +265,23 @@ async function performScan(
}
}
// 4. 更新 metainfo.json
// 4. 保存 metainfo 到数据库
metaInfo.last_refresh = Date.now();
const metainfoPath = `${rootPath}${rootPath.endsWith('/') ? '' : '/'}metainfo.json`;
const metainfoContent = JSON.stringify(metaInfo, null, 2);
console.log('[OpenList Refresh] 上传 metainfo.json:', {
path: metainfoPath,
const metainfoContent = JSON.stringify(metaInfo);
console.log('[OpenList Refresh] 保存 metainfo 到数据库:', {
videoCount: Object.keys(metaInfo.folders).length,
contentLength: metainfoContent.length,
contentPreview: metainfoContent.substring(0, 300),
});
await client.uploadFile(metainfoPath, metainfoContent);
console.log('[OpenList Refresh] 上传成功');
await db.setGlobalValue('video.metainfo', metainfoContent);
console.log('[OpenList Refresh] 保存成功');
// 验证上传:立即读取文件
// 验证保存:立即读取数据库
try {
console.log('[OpenList Refresh] 验证上传:读取文件');
const verifyResponse = await client.getFile(metainfoPath);
if (verifyResponse.code === 200 && verifyResponse.data.raw_url) {
const downloadUrl = verifyResponse.data.raw_url;
const verifyContentResponse = await fetch(downloadUrl, {
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',
'Accept': '*/*',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
},
});
const verifyContent = await verifyContentResponse.text();
console.log('[OpenList Refresh] 验证读取成功:', {
contentLength: verifyContent.length,
contentPreview: verifyContent.substring(0, 300),
});
// 尝试解析
console.log('[OpenList Refresh] 验证保存:读取数据库');
const verifyContent = await db.getGlobalValue('video.metainfo');
if (verifyContent) {
const verifyParsed = JSON.parse(verifyContent);
console.log('[OpenList Refresh] 验证解析成功:', {
hasfolders: !!verifyParsed.folders,

View File

@@ -80,37 +80,25 @@ export async function GET(request: NextRequest) {
// 搜索 OpenList如果配置了
if (hasOpenList) {
try {
const { getCachedMetaInfo } = await import('@/lib/openlist-cache');
const { getCachedMetaInfo, setCachedMetaInfo } = await import('@/lib/openlist-cache');
const { getTMDBImageUrl } = await import('@/lib/tmdb.search');
const { OpenListClient } = await import('@/lib/openlist.client');
const { db } = await import('@/lib/db');
const rootPath = config.OpenListConfig!.RootPath || '/';
let metaInfo = getCachedMetaInfo(rootPath);
// 如果没有缓存,尝试从 OpenList 读取
// 如果没有缓存,尝试从数据库读取
if (!metaInfo) {
try {
const client = new OpenListClient(
config.OpenListConfig!.URL,
config.OpenListConfig!.Token
);
const metainfoPath = `${rootPath}${rootPath.endsWith('/') ? '' : '/'}metainfo.json`;
const fileResponse = await client.getFile(metainfoPath);
if (fileResponse.code === 200 && fileResponse.data.raw_url) {
const downloadUrl = fileResponse.data.raw_url;
const contentResponse = await fetch(downloadUrl, {
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',
'Accept': '*/*',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
},
});
const content = await contentResponse.text();
metaInfo = JSON.parse(content);
const metainfoJson = await db.getGlobalValue('video.metainfo');
if (metainfoJson) {
metaInfo = JSON.parse(metainfoJson);
if (metaInfo) {
setCachedMetaInfo(rootPath, metaInfo);
}
}
} catch (error) {
console.error('[Search WS] 读取 metainfo.json 失败:', error);
console.error('[Search WS] 从数据库读取 metainfo 失败:', error);
}
}

View File

@@ -5,26 +5,10 @@ import { NextRequest, NextResponse } from 'next/server';
import { getAuthInfoFromCookie } from '@/lib/auth';
import { getConfig } from '@/lib/config';
import { HttpsProxyAgent } from 'https-proxy-agent';
import nodeFetch from 'node-fetch';
export const runtime = 'nodejs';
// 代理 agent 缓存
const proxyAgentCache = new Map<string, HttpsProxyAgent<string>>();
function getProxyAgent(proxy: string): HttpsProxyAgent<string> {
if (!proxyAgentCache.has(proxy)) {
const agent = new HttpsProxyAgent(proxy, {
timeout: 30000,
keepAlive: true,
keepAliveMsecs: 60000,
maxSockets: 10,
maxFreeSockets: 5,
});
proxyAgentCache.set(proxy, agent);
}
return proxyAgentCache.get(proxy)!;
}
/**
* GET /api/tmdb/search?query=xxx
* 搜索TMDB返回多个结果供用户选择
@@ -59,14 +43,18 @@ export async function GET(request: NextRequest) {
const fetchOptions: any = tmdbProxy
? {
agent: getProxyAgent(tmdbProxy),
agent: new HttpsProxyAgent(tmdbProxy, {
timeout: 30000,
keepAlive: false,
}),
signal: AbortSignal.timeout(30000),
}
: {
signal: AbortSignal.timeout(15000),
};
const response = await fetch(url, fetchOptions);
// 使用 node-fetch 而不是原生 fetch
const response = await nodeFetch(url, fetchOptions);
if (!response.ok) {
console.error('TMDB 搜索失败:', response.status, response.statusText);