## 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 не пустой; старые записи останутся без ссылки, новые получат при следующем распознавании.