Ссылки на внешние базы в ревью (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
@@ -0,0 +1,56 @@
## Context
Сейчас `metadata.Candidate` и `store.MetadataCandidate` хранят провайдера и
id, но не URL. Человек на ревью видит таблицу кандидатов с названием и id,
но чтобы проверить матч — должен вручную открыть сайт базы и вставить id в
поиск. Изменение добавляет поле `URL` на всех слоях: от клиентов метабаз до
шаблона ревью.
## Goals / Non-Goals
**Goals:**
- Каждый кандидат внешней базы несёт URL, по которому человек может
перейти и проверить матч
- URL генерируется клиентом провайдера при поиске (source of truth)
- Сохраняется в БД и отображается в веб-интерфейсе ревью
**Non-Goals:**
- Не меняем Telegram-бот (в чате кандидатов нет, выбор — через веб)
- Не добавляем отдельную колонку для нативного провайдера (всегда можно
вывести из URL или добавить позже)
- Не кешируем и не валидируем URL (не наша ответственность)
## Decisions
### 1. URL генерируется клиентом провайдера при Search, а не на лету при отображении
**Почему:** клиент знает и нативный провайдер, и нативный id. При
отображении в ревью мы имеем только теговые provider/provider_id (TVMaze →
tvdb/269613), и нативный tvMaze-id уже потерян. Генерация при поиске
сохраняет точную ссылку.
**Альтернатива (отклонена):** генерировать URL на лету в HTTP-обработчике
из provider/provider_id. Не работает для TVMaze (provider в БД уже
подменён на теговый). Хранить же оба id ради одной ссылки — дороже, чем
одно поле URL.
### 2. URL — простая строка, без структуры (provider + path + id)
**Почему:** URL у каждого провайдера формируется по-разному (у TMDB зависит
от mediaType, у TVDB — dereferrer, у IMDb — `/title/`). Хранить готовую
строку проще, чем набор параметров + функцию сборки.
### 3. БД: новая миграция `0004_candidate_url.sql`
**Почему:** изменение схемы — стандартно через goose-миграцию. Миграция
только добавляет nullable колонку (без DEFAULT, обратная совместимость).
## Risks / Trade-offs
- **URL может измениться** (провайдер меняет структуру сайта) → ссылка
сломается. Вероятность низкая (TMDB/TVDB/IMDb не меняли схемы URL
годами). Если сломается — фикс в одном месте (клиент провайдера),
миграция не нужна.
- **Для старых кандидатов url будет пустым** → в шаблоне показываем ссылку
только если url не пустой; старые записи останутся без ссылки, новые
получат при следующем распознавании.