私人影库增加扫描模式

This commit is contained in:
mtvpls
2026-01-01 20:57:44 +08:00
parent 42b11c3679
commit a698c6b493
4 changed files with 62 additions and 18 deletions
+22
View File
@@ -2766,6 +2766,7 @@ const OpenListConfigComponent = ({
const [rootPath, setRootPath] = useState('/'); const [rootPath, setRootPath] = useState('/');
const [offlineDownloadPath, setOfflineDownloadPath] = useState('/'); const [offlineDownloadPath, setOfflineDownloadPath] = useState('/');
const [scanInterval, setScanInterval] = useState(0); const [scanInterval, setScanInterval] = useState(0);
const [scanMode, setScanMode] = useState<'torrent' | 'name' | 'hybrid'>('hybrid');
const [videos, setVideos] = useState<any[]>([]); const [videos, setVideos] = useState<any[]>([]);
const [refreshing, setRefreshing] = useState(false); const [refreshing, setRefreshing] = useState(false);
const [scanProgress, setScanProgress] = useState<{ const [scanProgress, setScanProgress] = useState<{
@@ -2785,6 +2786,7 @@ const OpenListConfigComponent = ({
setRootPath(config.OpenListConfig.RootPath || '/'); setRootPath(config.OpenListConfig.RootPath || '/');
setOfflineDownloadPath(config.OpenListConfig.OfflineDownloadPath || '/'); setOfflineDownloadPath(config.OpenListConfig.OfflineDownloadPath || '/');
setScanInterval(config.OpenListConfig.ScanInterval || 0); setScanInterval(config.OpenListConfig.ScanInterval || 0);
setScanMode(config.OpenListConfig.ScanMode || 'hybrid');
} }
}, [config]); }, [config]);
@@ -2825,6 +2827,7 @@ const OpenListConfigComponent = ({
RootPath: rootPath, RootPath: rootPath,
OfflineDownloadPath: offlineDownloadPath, OfflineDownloadPath: offlineDownloadPath,
ScanInterval: scanInterval, ScanInterval: scanInterval,
ScanMode: scanMode,
}), }),
}); });
@@ -3151,6 +3154,25 @@ const OpenListConfigComponent = ({
</p> </p>
</div> </div>
<div>
<label className='block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2'>
</label>
<select
value={scanMode}
onChange={(e) => setScanMode(e.target.value as 'torrent' | 'name' | 'hybrid')}
disabled={!enabled}
className='w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 focus:ring-2 focus:ring-blue-500 focus:border-transparent disabled:opacity-50 disabled:cursor-not-allowed'
>
<option value='hybrid'></option>
<option value='torrent'></option>
<option value='name'></option>
</select>
<p className='mt-1 text-xs text-gray-500 dark:text-gray-400'>
</p>
</div>
<div className='flex gap-3'> <div className='flex gap-3'>
<button <button
onClick={handleCheckConnectivity} onClick={handleCheckConnectivity}
+3 -1
View File
@@ -26,7 +26,7 @@ export async function POST(request: NextRequest) {
try { try {
const body = await request.json(); const body = await request.json();
const { action, Enabled, URL, Username, Password, RootPath, OfflineDownloadPath, ScanInterval } = body; const { action, Enabled, URL, Username, Password, RootPath, OfflineDownloadPath, ScanInterval, ScanMode } = body;
const authInfo = getAuthInfoFromCookie(request); const authInfo = getAuthInfoFromCookie(request);
if (!authInfo || !authInfo.username) { if (!authInfo || !authInfo.username) {
@@ -58,6 +58,7 @@ export async function POST(request: NextRequest) {
LastRefreshTime: adminConfig.OpenListConfig?.LastRefreshTime, LastRefreshTime: adminConfig.OpenListConfig?.LastRefreshTime,
ResourceCount: adminConfig.OpenListConfig?.ResourceCount, ResourceCount: adminConfig.OpenListConfig?.ResourceCount,
ScanInterval: 0, ScanInterval: 0,
ScanMode: ScanMode || 'hybrid',
}; };
await db.saveAdminConfig(adminConfig); await db.saveAdminConfig(adminConfig);
@@ -108,6 +109,7 @@ export async function POST(request: NextRequest) {
LastRefreshTime: adminConfig.OpenListConfig?.LastRefreshTime, LastRefreshTime: adminConfig.OpenListConfig?.LastRefreshTime,
ResourceCount: adminConfig.OpenListConfig?.ResourceCount, ResourceCount: adminConfig.OpenListConfig?.ResourceCount,
ScanInterval: scanInterval, ScanInterval: scanInterval,
ScanMode: ScanMode || 'hybrid',
}; };
await db.saveAdminConfig(adminConfig); await db.saveAdminConfig(adminConfig);
+1
View File
@@ -111,6 +111,7 @@ export interface AdminConfig {
LastRefreshTime?: number; // 上次刷新时间戳 LastRefreshTime?: number; // 上次刷新时间戳
ResourceCount?: number; // 资源数量 ResourceCount?: number; // 资源数量
ScanInterval?: number; // 定时扫描间隔(分钟),0表示关闭,最低60分钟 ScanInterval?: number; // 定时扫描间隔(分钟),0表示关闭,最低60分钟
ScanMode?: 'torrent' | 'name' | 'hybrid'; // 扫描模式:torrent=种子库匹配,name=名字匹配,hybrid=混合模式(默认)
}; };
AIConfig?: { AIConfig?: {
Enabled: boolean; // 是否启用AI问片功能 Enabled: boolean; // 是否启用AI问片功能
+36 -17
View File
@@ -19,6 +19,7 @@ import {
} from '@/lib/scan-task'; } from '@/lib/scan-task';
import { parseSeasonFromTitle } from '@/lib/season-parser'; import { parseSeasonFromTitle } from '@/lib/season-parser';
import { searchTMDB, getTVSeasonDetails } from '@/lib/tmdb.search'; import { searchTMDB, getTVSeasonDetails } from '@/lib/tmdb.search';
import parseTorrentName from 'parse-torrent-name';
/** /**
* 启动 OpenList 刷新任务 * 启动 OpenList 刷新任务
@@ -55,7 +56,8 @@ export async function startOpenListRefresh(clearMetaInfo: boolean = false): Prom
tmdbProxy, tmdbProxy,
openListConfig.Username, openListConfig.Username,
openListConfig.Password, openListConfig.Password,
clearMetaInfo clearMetaInfo,
openListConfig.ScanMode || 'hybrid'
).catch((error) => { ).catch((error) => {
console.error('[OpenList Refresh] 后台扫描失败:', error); console.error('[OpenList Refresh] 后台扫描失败:', error);
failScanTask(taskId, (error as Error).message); failScanTask(taskId, (error as Error).message);
@@ -75,7 +77,8 @@ async function performScan(
tmdbProxy?: string, tmdbProxy?: string,
username?: string, username?: string,
password?: string, password?: string,
clearMetaInfo?: boolean clearMetaInfo?: boolean,
scanMode: 'torrent' | 'name' | 'hybrid' = 'hybrid'
): Promise<void> { ): Promise<void> {
const client = new OpenListClient(url, username!, password!); const client = new OpenListClient(url, username!, password!);
@@ -161,18 +164,34 @@ async function performScan(
existingKeys.add(folderKey); existingKeys.add(folderKey);
try { try {
const seasonInfo = parseSeasonFromTitle(folder.name); let searchQuery: string;
const searchQuery = seasonInfo.cleanTitle || folder.name; let seasonNumber: number | null = null;
let year: number | null = null;
let searchResult: any;
console.log(`[OpenList Refresh] 处理文件夹: ${folder.name}`); if (scanMode === 'torrent' || scanMode === 'hybrid') {
console.log(`[OpenList Refresh] 清理后标题: ${searchQuery}, 季度: ${seasonInfo.seasonNumber}, 年份: ${seasonInfo.year}`); const torrentInfo = parseTorrentName(folder.name);
searchQuery = torrentInfo.title || folder.name;
seasonNumber = torrentInfo.season || null;
year = torrentInfo.year || null;
const searchResult = await searchTMDB( console.log(`[OpenList Refresh] 种子库模式 - 文件夹: ${folder.name}`);
tmdbApiKey, console.log(`[OpenList Refresh] 解析结果 - 标题: ${searchQuery}, 季度: ${seasonNumber}, 年份: ${year}`);
searchQuery,
tmdbProxy, searchResult = await searchTMDB(tmdbApiKey, searchQuery, tmdbProxy, year || undefined);
seasonInfo.year || undefined }
);
if (scanMode === 'name' || (scanMode === 'hybrid' && (!searchResult || searchResult.code !== 200 || !searchResult.result))) {
const seasonInfo = parseSeasonFromTitle(folder.name);
searchQuery = seasonInfo.cleanTitle || folder.name;
seasonNumber = seasonInfo.seasonNumber;
year = seasonInfo.year;
console.log(`[OpenList Refresh] 名字匹配模式 - 文件夹: ${folder.name}`);
console.log(`[OpenList Refresh] 清理后标题: ${searchQuery}, 季度: ${seasonNumber}, 年份: ${year}`);
searchResult = await searchTMDB(tmdbApiKey, searchQuery, tmdbProxy, year || undefined);
}
if (searchResult.code === 200 && searchResult.result) { if (searchResult.code === 200 && searchResult.result) {
const result = searchResult.result; const result = searchResult.result;
@@ -190,12 +209,12 @@ async function performScan(
failed: false, failed: false,
}; };
if (result.media_type === 'tv' && seasonInfo.seasonNumber) { if (result.media_type === 'tv' && seasonNumber) {
try { try {
const seasonDetails = await getTVSeasonDetails( const seasonDetails = await getTVSeasonDetails(
tmdbApiKey, tmdbApiKey,
result.id, result.id,
seasonInfo.seasonNumber, seasonNumber,
tmdbProxy tmdbProxy
); );
@@ -217,12 +236,12 @@ async function performScan(
folderInfo.release_date = seasonDetails.season.air_date; folderInfo.release_date = seasonDetails.season.air_date;
} }
} else { } else {
console.warn(`[OpenList Refresh] 获取季度 ${seasonInfo.seasonNumber} 详情失败`); console.warn(`[OpenList Refresh] 获取季度 ${seasonNumber} 详情失败`);
folderInfo.season_number = seasonInfo.seasonNumber; folderInfo.season_number = seasonNumber;
} }
} catch (error) { } catch (error) {
console.error(`[OpenList Refresh] 获取季度详情异常:`, error); console.error(`[OpenList Refresh] 获取季度详情异常:`, error);
folderInfo.season_number = seasonInfo.seasonNumber; folderInfo.season_number = seasonNumber;
} }
} }