完善小雅
This commit is contained in:
@@ -28,11 +28,11 @@ export function parseVideoFileName(fileName: string): ParsedVideoInfo {
|
||||
|
||||
// 降级方案:使用多种正则模式提取集数
|
||||
// 按优先级排序:更具体的模式优先
|
||||
const patterns: Array<{ pattern: RegExp; isOVA?: boolean }> = [
|
||||
const patterns: Array<{ pattern: RegExp; isOVA?: boolean; extractSeason?: boolean }> = [
|
||||
// OVA01, OVA 01, ova01, ova 01 (OVA特殊处理) - 最优先
|
||||
{ pattern: /OVA\s*(\d+(?:\.\d+)?)/i, isOVA: true },
|
||||
// S01E01, s01e01, S01E01.5 (支持小数) - 最具体
|
||||
{ pattern: /[Ss]\d+[Ee](\d+(?:\.\d+)?)/ },
|
||||
// S01E01, s01e01, S01E1234, S01E01.5 (支持1-4位数字和小数) - 最具体
|
||||
{ pattern: /[Ss](\d+)[Ee](\d{1,4}(?:\.\d+)?)/, extractSeason: true },
|
||||
// [01], (01), [01.5], (01.5) (支持小数,但要排除中文括号内容) - 很具体
|
||||
{ pattern: /[\[\(](\d+(?:\.\d+)?)[\]\)]/ },
|
||||
// E01, E1, e01, e1, E01.5 (支持小数)
|
||||
@@ -45,12 +45,22 @@ export function parseVideoFileName(fileName: string): ParsedVideoInfo {
|
||||
{ pattern: /^(\d+(?:\.\d+)?)[^\d.]/ },
|
||||
];
|
||||
|
||||
for (const { pattern, isOVA } of patterns) {
|
||||
for (const { pattern, isOVA, extractSeason } of patterns) {
|
||||
const match = fileName.match(pattern);
|
||||
if (match && match[1]) {
|
||||
const episode = parseFloat(match[1]);
|
||||
if (episode > 0 && episode < 10000) { // 合理的集数范围
|
||||
return { episode, isOVA };
|
||||
if (extractSeason && match[2]) {
|
||||
// 同时提取 season 和 episode
|
||||
const season = parseInt(match[1]);
|
||||
const episode = parseFloat(match[2]);
|
||||
if (season > 0 && season < 100 && episode > 0 && episode < 10000) {
|
||||
return { season, episode };
|
||||
}
|
||||
} else {
|
||||
// 只提取 episode
|
||||
const episode = parseFloat(match[1]);
|
||||
if (episode > 0 && episode < 10000) {
|
||||
return { episode, isOVA };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,7 +139,7 @@ export async function getXiaoyaMetadata(
|
||||
};
|
||||
}
|
||||
|
||||
// 优先级 3: 实时搜索 TMDb
|
||||
// 优先级 3: 实时搜索 TMDb(使用文件名)
|
||||
if (tmdbApiKey) {
|
||||
const fileName = pathParts[pathParts.length - 1];
|
||||
const searchQuery = fileName
|
||||
@@ -147,6 +147,39 @@ export async function getXiaoyaMetadata(
|
||||
.replace(/[\[\]()]/g, ' ')
|
||||
.trim();
|
||||
|
||||
// 如果文件名是纯数字(可能带小数点)或者是 SxxExx 格式,跳过文件名搜索,直接使用文件夹名
|
||||
const isPureNumber = /^[\d.]+$/.test(searchQuery);
|
||||
const isSeasonEpisode = /^S\d+E\d+/i.test(searchQuery);
|
||||
|
||||
if (!isPureNumber && !isSeasonEpisode) {
|
||||
const { searchTMDB, getTMDBImageUrl } = await import('./tmdb.search');
|
||||
const tmdbResult = await searchTMDB(tmdbApiKey, searchQuery, tmdbProxy);
|
||||
|
||||
if (tmdbResult.code === 200 && tmdbResult.result) {
|
||||
return {
|
||||
tmdbId: tmdbResult.result.id,
|
||||
title: tmdbResult.result.title || tmdbResult.result.name || folderName,
|
||||
year: tmdbResult.result.release_date?.substring(0, 4) ||
|
||||
tmdbResult.result.first_air_date?.substring(0, 4),
|
||||
rating: tmdbResult.result.vote_average,
|
||||
plot: tmdbResult.result.overview,
|
||||
poster: tmdbResult.result.poster_path
|
||||
? getTMDBImageUrl(tmdbResult.result.poster_path)
|
||||
: undefined,
|
||||
mediaType: tmdbResult.result.media_type,
|
||||
source: 'tmdb',
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 优先级 4: 实时搜索 TMDb(使用文件夹名)
|
||||
if (tmdbApiKey) {
|
||||
const searchQuery = folderName
|
||||
.replace(/[\[\](){}]/g, ' ')
|
||||
.replace(/\d{4}/g, '')
|
||||
.trim();
|
||||
|
||||
const { searchTMDB, getTMDBImageUrl } = await import('./tmdb.search');
|
||||
const tmdbResult = await searchTMDB(tmdbApiKey, searchQuery, tmdbProxy);
|
||||
|
||||
@@ -200,6 +233,7 @@ export async function getXiaoyaEpisodes(
|
||||
|
||||
return videoFiles.map(file => {
|
||||
const parsed = parseVideoFileName(file.name);
|
||||
console.log('[xiaoya-metadata] 解析文件名:', file.name, '结果:', parsed);
|
||||
let title = file.name;
|
||||
|
||||
if (parsed.season && parsed.episode) {
|
||||
@@ -214,11 +248,21 @@ export async function getXiaoyaEpisodes(
|
||||
};
|
||||
});
|
||||
} else {
|
||||
// 电影:只有一个文件
|
||||
const fileName = pathParts[pathParts.length - 1];
|
||||
return [{
|
||||
path: videoPath,
|
||||
title: fileName,
|
||||
}];
|
||||
// 电影:列出同一文件夹下的所有视频
|
||||
const parentDir = pathParts.slice(0, -1).join('/');
|
||||
const listResponse = await xiaoyaClient.listDirectory(`/${parentDir}`);
|
||||
|
||||
const videoExtensions = ['.mp4', '.mkv', '.avi', '.m3u8', '.flv', '.ts', '.mov', '.wmv', '.webm'];
|
||||
const videoFiles = listResponse.content
|
||||
.filter(item =>
|
||||
!item.is_dir &&
|
||||
videoExtensions.some(ext => item.name.toLowerCase().endsWith(ext))
|
||||
)
|
||||
.sort((a, b) => a.name.localeCompare(b.name));
|
||||
|
||||
return videoFiles.map(file => ({
|
||||
path: `/${parentDir}/${file.name}`,
|
||||
title: file.name,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ export class XiaoyaClient {
|
||||
/**
|
||||
* 获取缓存的 Token 或重新登录
|
||||
*/
|
||||
private async getToken(): Promise<string> {
|
||||
async getToken(): Promise<string> {
|
||||
// 如果配置了 Token,直接使用
|
||||
if (this.configToken) {
|
||||
return this.configToken;
|
||||
|
||||
Reference in New Issue
Block a user