diff --git a/internal/handlers/entries.go b/internal/handlers/entries.go index 291ec04d7..6659a79a4 100644 --- a/internal/handlers/entries.go +++ b/internal/handlers/entries.go @@ -283,7 +283,7 @@ func HandleMediaEntryManualMatch(c *RouteCtx) error { return filepath.Dir(item.GetNormalizedPath()) }) - selectedLfs, found := groupedLfs[b.Dir] + selectedLfs, found := groupedLfs[strings.ToLower(b.Dir)] if !found { return c.RespondWithError(errors.New("no local files found for selected directory")) } diff --git a/seanime-parser/parser.go b/seanime-parser/parser.go index c39160b82..ddc8d36ce 100644 --- a/seanime-parser/parser.go +++ b/seanime-parser/parser.go @@ -313,13 +313,26 @@ parenLoop: // Get other episode numbers _, otherEpisodeNumberTkns := p.tokenManager.tokens.findWithMetadataCategory(metadataOtherEpisodeNumber) + includesMovieToken := false + for idx, tkn := range animeTypeTkns { title += " " + tkn.getValue() if otherEpisodeNumberTkns != nil && idx < len(otherEpisodeNumberTkns) { title += " " + otherEpisodeNumberTkns[idx].getValue() } + // check movie token + if strings.Contains(tkn.getNormalizedValue(), "MOVIE") { + includesMovieToken = true + } } + /* Include episode title if movie */ + // Get title + if found, epTitleTkns := p.tokenManager.tokens.findWithMetadataCategory(metadataEpisodeTitle); found && includesMovieToken { + title += " " + epTitleTkns[0].getValue() + } + /* end */ + p.metadata.FormattedTitle = title } diff --git a/seanime-parser/parser_episode.go b/seanime-parser/parser_episode.go index de97a1f00..079994f36 100644 --- a/seanime-parser/parser_episode.go +++ b/seanime-parser/parser_episode.go @@ -49,9 +49,10 @@ func (p *parser) parseEpisode() { } -// --------------------------------------------------------------------------------------------------------------------- -// Searching for alt episode number -// --------------------------------------------------------------------------------------------------------------------- +// +---------------------+ +// | Alt number | +// +---------------------+ +// e.g. 01 (12) func (p *parser) parseEpisodeBySearchingForAltNumber() bool { for _, numTkn := range *p.tokenManager.tokens { @@ -84,6 +85,11 @@ func (p *parser) parseEpisodeBySearchingForAltNumber() bool { return false } +// +---------------------+ +// | Alt number | +// +---------------------+ +// e.g. {epTkn} (12) + // parseKnownEpisodeAltNumber parses the alt episode number if an episode number already exists. func (p *parser) parseKnownEpisodeAltNumber() (foundEpisode bool) { found, tkns := p.tokenManager.tokens.findWithMetadataCategory(metadataEpisodeNumber) @@ -137,6 +143,9 @@ func (p *parser) parseKnownEpisodeAltNumber() (foundEpisode bool) { // parseEpisodeBySearching parses the episode number by searching for different patterns. func (p *parser) parseEpisodeBySearching(aggressive bool) bool { + // +---------------------+ + // | Case 1 | + // +---------------------+ // Check "- 01 [...]" or "- 01 480p" for { var openingBracketTkn *token @@ -168,7 +177,7 @@ func (p *parser) parseEpisodeBySearching(aggressive bool) bool { // Check we find a range before // e.g. "01-{numTkn} [...]" // making sure that the range has no delimiters - if rangeTkns, found := p.tokenManager.tokens.checkEpisodeRangeBefore(numTkn); found { + if rangeTkns, found := p.tokenManager.tokens.checkEpisodeRangeBefore(numTkn); found && isReasonableEpisodeNumber(rangeTkns[1].getValue()) { // Make sure that there is no number range before the range // e.g. Avoid this "009-1-02 [...]", where "1-02" is considered as a range if _, found := p.tokenManager.tokens.checkNumberRangeBefore(rangeTkns[1], false); !found { @@ -184,6 +193,9 @@ func (p *parser) parseEpisodeBySearching(aggressive bool) bool { break } + // +---------------------+ + // | Aggressive | + // +---------------------+ // When searching aggressively // Check that the number might really be an episode number // e.g. if {lastNumTkn} < 10, lastNumTkn should be zero padded to avoid false positives like "Title 2" @@ -209,6 +221,9 @@ func (p *parser) parseEpisodeBySearching(aggressive bool) bool { return true // Found episode number, end } + // +---------------------+ + // | Case 2 | + // +---------------------+ // Check for first occurrence of unknown number preceded and followed by a dash separator // e.g. "- 01 -" for _, numTkn := range *p.tokenManager.tokens { @@ -224,10 +239,12 @@ func (p *parser) parseEpisodeBySearching(aggressive bool) bool { continue // Check next token } // Check that it is not a range - // e.g. "01-03" + // e.g. "03-{numTkn} -" if _, found := p.tokenManager.tokens.checkNumberRangeBefore(numTkn, false); found { continue // Check next token } + // Check that it is not a range + // e.g. "- {numTkn}-03" if _, found := p.tokenManager.tokens.checkNumberRangeAfter(numTkn, false); found { continue // Check next token } @@ -236,6 +253,9 @@ func (p *parser) parseEpisodeBySearching(aggressive bool) bool { return true // Found episode number, end } + // +---------------------+ + // | Case 3 | + // +---------------------+ // Check for last unknown number for { var lastNumTkn *token @@ -258,9 +278,9 @@ func (p *parser) parseEpisodeBySearching(aggressive bool) bool { // Check we find a range before // e.g. "1 - {lastNumTkn} [...]" - if rangeTkns, found := p.tokenManager.tokens.checkNumberRangeBefore(lastNumTkn, true); found { + if rangeTkns, found := p.tokenManager.tokens.checkNumberRangeBefore(lastNumTkn, true); found && isReasonableEpisodeNumber(rangeTkns[1].getValue()) { if isNumberZeroPadded(lastNumTkn.getValue()) && !isNumberZeroPadded(rangeTkns[1].getValue()) { - + // Do not consider "01-1" as a range } else { rangeTkns[1].setMetadataCategory(metadataEpisodeNumber) } diff --git a/seanime-parser/parser_title.go b/seanime-parser/parser_title.go index 76ebc036d..002a4781b 100644 --- a/seanime-parser/parser_title.go +++ b/seanime-parser/parser_title.go @@ -12,7 +12,10 @@ func (p *parser) parseEpisodeTitle() { // Get all tokens after the last episode number token and before an opening bracket/file info metadata/EOF found, epTkns := p.tokenManager.tokens.findWithMetadataCategory(metadataEpisodeNumber) if !found { - return // Next try + found, epTkns = p.tokenManager.tokens.findWithMetadataCategory(metadataOtherEpisodeNumber) + if !found { + return // Next try + } } // Get the last episode number token diff --git a/seanime-parser/test/data.json b/seanime-parser/test/data.json index 6bf7a36fd..52edb6166 100644 --- a/seanime-parser/test/data.json +++ b/seanime-parser/test/data.json @@ -1,4 +1,37 @@ [ + { + "file_name": "[Anime Time] Evangelion 3.0+1.11 Thrice Upon A Time", + "title": "Evangelion 3.0+1.11 Thrice Upon A Time", + "formatted_title": "Evangelion 3.0+1.11 Thrice Upon A Time", + "release_group": "Anime Time" + }, + { + "file_name": "One Piece Movie 11 - Film Z [BD][1080p][x264][JPN][SUB]-df68.mkv", + "title": "One Piece", + "formatted_title": "One Piece Movie 11 Film Z", + "anime_type": [ + "Movie" + ], + "episode_number": [ + "11" + ], + "episode_title": "Film Z", + "language": [ + "JPN" + ], + "release_group": "df68", + "source": [ + "BD" + ], + "video_resolution": "1080p", + "video_term": [ + "x264" + ], + "subtitles": [ + "SUB" + ], + "file_extension": "mkv" + }, { "file_name": "[Thomku] Kill la Kill 01 - 07 Batch [720p][AAC][MP4]", "title": "Kill la Kill", @@ -2291,10 +2324,11 @@ { "file_name": "[FB] Crayon Shin-Chan Movie 2 The Secret of Buri Buri Kingdom [DivX5 AC3] 1994 [852X480] V2.avi", "title": "Crayon Shin-Chan", - "formatted_title": "Crayon Shin-Chan Movie 2", + "formatted_title": "Crayon Shin-Chan Movie 2 The Secret of Buri Buri Kingdom", "episode_number": [ "2" ], + "episode_title": "The Secret of Buri Buri Kingdom", "anime_type": [ "Movie" ], diff --git a/seanime-parser/test/seanime_parser_test.go b/seanime-parser/test/seanime_parser_test.go index 21dc0c99c..40edcd122 100644 --- a/seanime-parser/test/seanime_parser_test.go +++ b/seanime-parser/test/seanime_parser_test.go @@ -66,7 +66,7 @@ func TestSeanimeParserIsolated(t *testing.T) { data := getData() assert.NotNil(t, data) - filename := "[모에-Raws] Abarenbou Rikishi!! Matsutarou #04 (ABC 1280x720 x264 AAC).mp4" + filename := "One Piece Movie 11 - Film Z [BD][1080p][x264][JPN][SUB]-df68.mkv" for _, tt := range data { diff --git a/seanime-parser/token_manager.go b/seanime-parser/token_manager.go index 0a15dafb9..b4f3af6d0 100644 --- a/seanime-parser/token_manager.go +++ b/seanime-parser/token_manager.go @@ -332,11 +332,6 @@ func (t *tokens) foundDashSeparatorAfter(tkn *token) bool { } // e.g. "01-{tkn}" or "1 ~ {tkn}" -// When rangeWithDelimiters is true, the function will ignore delimiters when checking for a number range -// So, "01 - {tkn}" and "01-{tkn} will return true, -// When it's false, the function will return false for "01 - {tkn}" and true for "01-{tkn} -// -// Returns [0] separator, [1] number or false func (t *tokens) checkEpisodeRangeBefore(tkn *token) ([]*token, bool) { tkns, found, nSkipped := t.getCategorySequenceBefore(t.getIndexOf(tkn), []tokenCategory{ tokenCatSeparator,