Ссылки на внешние базы в ревью (recognition)

Каждый кандидат внешней базы метаданных (TMDB/TVDB/TVMaze) теперь несёт
URL на страницу элемента — при ревью можно кликнуть и проверить матч.
URL формируется клиентом провайдера при поиске, сохраняется в БД
(metadata_candidate.url) и отображается ссылкой в веб-интерфейсе.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
av
2026-06-29 20:09:20 +03:00
co-authored by Claude
parent 6b7c090ce4
commit dfa182a5a9
20 changed files with 278 additions and 5 deletions
+27 -1
View File
@@ -1026,7 +1026,7 @@ func TestRecognizeOne_PersistsCandidates(t *testing.T) {
}
res := seriesResult()
res.Candidates = []metadata.Candidate{
{Provider: "tvmaze", ID: "1", Title: "Show A", Year: 2006, TagProvider: "tvdb", TagID: "269613"},
{Provider: "tvmaze", ID: "1", Title: "Show A", Year: 2006, TagProvider: "tvdb", TagID: "269613", URL: "https://www.tvmaze.com/shows/1"},
{Provider: "tvmaze", ID: "2", Title: "Show B", Year: 2007},
}
w := testWorkerWith(st, qb, &fakeRecognizer{result: res}, nil)
@@ -1040,6 +1040,14 @@ func TestRecognizeOne_PersistsCandidates(t *testing.T) {
if st.candidates[0].Provider != "tvdb" || st.candidates[0].ProviderID != "269613" {
t.Errorf("candidate[0] = %+v", st.candidates[0])
}
// URL первого кандидата сохранён.
if st.candidates[0].URL.String != "https://www.tvmaze.com/shows/1" || !st.candidates[0].URL.Valid {
t.Errorf("candidate[0].URL = (%q, valid=%v), want url", st.candidates[0].URL.String, st.candidates[0].URL.Valid)
}
// У второго кандидата URL не задан — в БД должен быть NULL (Valid=false).
if st.candidates[1].URL.Valid {
t.Error("candidate[1].URL must be NULL (Valid=false) for candidate without URL")
}
}
func TestChooseCandidate_PinsOverrides(t *testing.T) {
@@ -1135,6 +1143,24 @@ func TestReviewData_IncludesCandidates(t *testing.T) {
}
}
func TestToStoreCandidates_URL(t *testing.T) {
// Кандидат с URL: URL должен быть проброшен как непустой NullString.
// Кандидат без URL: URL должен быть пустым NullString (Valid=false → NULL).
candURL := toStoreCandidates(1, []metadata.Candidate{
{Provider: "tmdb", ID: "603", Title: "With URL", URL: "https://www.themoviedb.org/movie/603"},
{Provider: "tvdb", ID: "1", Title: "Without URL", URL: ""},
})
if len(candURL) != 2 {
t.Fatalf("len = %d, want 2", len(candURL))
}
if c := candURL[0]; c.URL.String != "https://www.themoviedb.org/movie/603" || !c.URL.Valid {
t.Errorf("URL[0] = (%q, valid=%v), want (url, true)", c.URL.String, c.URL.Valid)
}
if c := candURL[1]; c.URL.String != "" || c.URL.Valid {
t.Errorf("URL[1] = (%q, valid=%v), want (\"\", false)", c.URL.String, c.URL.Valid)
}
}
func TestToLayoutPlan(t *testing.T) {
s, e := 1, 3
plan := recognize.Plan{