From 534f7bb9014d11613dd9ec36d266d8dfd2467a26 Mon Sep 17 00:00:00 2001 From: Silo Server Developer Date: Wed, 27 May 2026 18:43:18 +0200 Subject: [PATCH] fix(naming): numeric-only titles are not bare provider IDs '86' / '22 7' were parsed as trailing tvdb ids, tripping the trusted-ID gate so the correct title match was rejected. Require a letter in the name before treating a trailing number as a bare id. Co-Authored-By: Claude Opus 4.7 (1M context) --- internal/naming/folderid.go | 16 +++++++++++++++ internal/naming/folderid_numeric_test.go | 25 ++++++++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 internal/naming/folderid_numeric_test.go diff --git a/internal/naming/folderid.go b/internal/naming/folderid.go index af5f7a3f..ade90a1c 100644 --- a/internal/naming/folderid.go +++ b/internal/naming/folderid.go @@ -3,6 +3,7 @@ package naming import ( "regexp" "strings" + "unicode" ) // folderIDPattern matches patterns like [tmdbid-27205], {tmdb-27205}, @@ -66,12 +67,27 @@ func ParseFolderIDs(folderName string, folderType string) *FolderIDHints { return nil } + // A bare trailing number is only an ID when appended to a real title. If the + // name has no letters (e.g. "86", "22 7"), it's a numeric title, not an ID. + if !containsLetter(trimmed) { + return nil + } + if strings.EqualFold(strings.TrimSpace(folderType), "series") { return &FolderIDHints{TvdbID: id} } return &FolderIDHints{TmdbID: id} } +func containsLetter(s string) bool { + for _, r := range s { + if unicode.IsLetter(r) { + return true + } + } + return false +} + func looksLikeYear(value string) bool { return len(value) == 4 && value >= "1800" && value <= "2100" } diff --git a/internal/naming/folderid_numeric_test.go b/internal/naming/folderid_numeric_test.go new file mode 100644 index 00000000..a50a71a9 --- /dev/null +++ b/internal/naming/folderid_numeric_test.go @@ -0,0 +1,25 @@ +package naming + +import "testing" + +func TestParseFolderIDs_NumericTitleIsNotAnID(t *testing.T) { + // Numeric-only anime titles must NOT be parsed as a bare trailing tvdb/tmdb id. + if got := ParseFolderIDs("86", "series"); got != nil { + t.Errorf(`ParseFolderIDs("86","series") = %+v, want nil`, got) + } + if got := ParseFolderIDs("22 7", "series"); got != nil { + t.Errorf(`ParseFolderIDs("22 7","series") = %+v, want nil`, got) + } + + // A real bare trailing id with title text must still be parsed. + got := ParseFolderIDs("Some Show 81189", "series") + if got == nil || got.TvdbID != "81189" { + t.Errorf(`ParseFolderIDs("Some Show 81189","series") = %+v, want TvdbID="81189"`, got) + } + + // Structured tags must still win regardless of letters. + got = ParseFolderIDs("{tmdb-27205}", "movies") + if got == nil || got.TmdbID != "27205" { + t.Errorf(`ParseFolderIDs("{tmdb-27205}","movies") = %+v, want TmdbID="27205"`, got) + } +}