This commit is contained in:
Pierre
2026-01-10 13:41:14 +01:00
parent 4bd3264743
commit e1fc075b7b
2 changed files with 60 additions and 10 deletions
+49 -7
View File
@@ -126,21 +126,63 @@ export class FileNameVideoMetaExtractor {
return this.cleanTitle(this.getParentFolder(filePath, 2));
}
/**
* Check if a filename contains a season/episode pattern (indicates TV show)
*/
private filenameHasSeasonEpisodePattern(filename: string): boolean {
const cleanedFilename = this.serieFilesNameAnalysisTool.removeKeywordsFromFilename(filename);
const cleanedFilename2 = this.serieFilesNameAnalysisTool.removeSFV(cleanedFilename);
const cleanedFilename3 = this.fileNameCleaner.cleanSpecialChars(cleanedFilename2);
const cleanedFilename4 = this.fileNameCleaner.cleanSpace(cleanedFilename3);
const [season, episode] = this.serieFilesNameAnalysisTool.findSeasonAndEpisode(cleanedFilename4);
return season >= 0 || episode >= 0;
}
computeTitleFromFilePath(filePath: string): string | undefined {
let filename = this.fileNameCleaner.getFilenameFromPath(filePath);
let titleFromFileName = this.computeTitleFromFileName(filename);
let titleFromParentFolder = this.computeTitleFromParentFolder(filePath);
let titleFromGrandParentFolder = this.computeTitleFromGrandParentFolder(filePath);
let simpleTitle = this.stringArrayOperation.longestString([titleFromFileName, titleFromParentFolder, titleFromGrandParentFolder]);
// Check if filename contains season/episode pattern (indicates TV show)
const isTvShow = this.filenameHasSeasonEpisodePattern(filename);
let selectedTitle;
if (titleFromFileName && titleFromFileName.length > MIN_TITLE_LENGTH) {
selectedTitle = titleFromFileName;
} else if (titleFromParentFolder && titleFromParentFolder.length > MIN_TITLE_LENGTH) {
selectedTitle = titleFromParentFolder;
} else if (titleFromGrandParentFolder && titleFromGrandParentFolder.length > MIN_TITLE_LENGTH) {
selectedTitle = titleFromGrandParentFolder;
if (isTvShow) {
// For TV shows, prefer the longer title between filename and folder
// This handles cases like "S01E01-The Fool.mkv" where "The Fool" is the episode name
// but "Lord of Mysteries" in the parent folder is the actual series title
const filenameTitleValid = titleFromFileName && titleFromFileName.length > MIN_TITLE_LENGTH;
const folderTitleValid = titleFromParentFolder && titleFromParentFolder.length > MIN_TITLE_LENGTH;
if (filenameTitleValid && folderTitleValid) {
// Both valid - prefer the longer one (series names are typically longer than episode names)
// Use strict > to avoid preferring folder when lengths are equal (e.g., "Dexter" vs "Series")
selectedTitle = titleFromParentFolder.length > titleFromFileName.length
? titleFromParentFolder
: titleFromFileName;
} else if (folderTitleValid) {
selectedTitle = titleFromParentFolder;
} else if (filenameTitleValid) {
selectedTitle = titleFromFileName;
} else if (titleFromGrandParentFolder && titleFromGrandParentFolder.length > MIN_TITLE_LENGTH) {
selectedTitle = titleFromGrandParentFolder;
} else {
selectedTitle = simpleTitle;
}
} else {
selectedTitle = simpleTitle;
// For non-TV shows (movies, etc.), use original logic: prefer filename
if (titleFromFileName && titleFromFileName.length > MIN_TITLE_LENGTH) {
selectedTitle = titleFromFileName;
} else if (titleFromParentFolder && titleFromParentFolder.length > MIN_TITLE_LENGTH) {
selectedTitle = titleFromParentFolder;
} else if (titleFromGrandParentFolder && titleFromGrandParentFolder.length > MIN_TITLE_LENGTH) {
selectedTitle = titleFromGrandParentFolder;
} else {
selectedTitle = simpleTitle;
}
}
if (selectedTitle && selectedTitle.length > MIN_TITLE_LENGTH) {
+11 -3
View File
@@ -164,6 +164,14 @@ describe('FileNameMetaExtractor', () => {
describe('Edge cases - title from folder (skipped)', () => {
// These tests are skipped because the parser doesn't extract title from folder name
// when the filename doesn't contain the show title
// Pattern: FolderWithTitle/S01E01-EpisodeName.mkv should extract title from folder
it('Lord of Mysteries - title from folder, filename is S01E01-EpisodeName pattern', async () => {
// Real-world case: folder contains title + quality info, filename is S01E01-EpisodeName format
// Expected: extract "Lord of Mysteries" from folder, not "The Fool" from filename
await titleSeasonEpTester('Lord of Mysteries', '1', '1',
'/data/watch/test-files/Anime/Lord of Mysteries S01 1080p Dual Audio WEBRip DD+ x265-EMBER/S01E01-The Fool [8A4E8B1F].mkv');
});
it.skip('handles files named with season in folder', async () => {
await titleSeasonEpTester('MythBusters', '2', '1',
@@ -180,7 +188,7 @@ describe('FileNameMetaExtractor', () => {
'/data/watch/test-files/Anime/Le Depart/13 it a new start.mkv');
});
it.skip('Akudama Drive - title from folder', async () => {
it('Akudama Drive - title from folder', async () => {
await titleSeasonEpTester('Akudama Drive', '1', '2',
'/data/watch/test-files/Anime/Akudama Drive/S01E02-RESERVOIR DOGS [F1D64557].mkv');
});
@@ -210,8 +218,8 @@ describe('FileNameMetaExtractor', () => {
'/data/watch/test-files/Anime/K-Project/[CBM]_K_-_02_-_Kitten_[1080p-FLAC]_[A83DF131].mkv');
});
it.skip('Made in Abyss - title from folder', async () => {
await titleSeasonEpTester('Made In Abyss', '1', '2',
it('Made in Abyss - title from folder', async () => {
await titleSeasonEpTester('Made in Abyss', '1', '2',
'/data/watch/test-files/Anime/Made in Abyss/S01E02-Resurrection Festival [AE07AD68].mkv');
});