feat(store,runner,web): channel unavailability notice (migration 013)
CI / Lint / Test / Vet (push) Successful in 26s
CI / Build & Import (push) Successful in 11s

YouTube channels that 404 on playlist discovery (deleted/private) are now:
1. Wrapped in domain.ErrChannelUnavailable by the YouTube adapter (instead of
   a generic error), so the runner can identify them without string-matching.
2. Stored per-user in channel_errors (migration 013, RLS-guarded) via runner's
   new UpsertChannelError path — removed from the generic Errors counter,
   counted separately as ChannelUnavailable.
3. Shown on the account page under "Unavailable channels" with name, chip-warn
   badge, and first-seen date, so users know why some subscribed channels
   produce no videos.
This commit is contained in:
2026-06-06 10:09:52 +02:00
parent 940f80899a
commit f1e9739900
13 changed files with 380 additions and 161 deletions
+20 -1
View File
@@ -1,6 +1,7 @@
package web
import (
"fmt"
"strings"
"gitea.d-ma.be/mathias/tapir/internal/adapters/store"
@@ -397,7 +398,7 @@ templ InviteNoticePage(message string, showLogin bool) {
// signed-in email, the user's connected video accounts (each with a Disconnect
// control), a Connect-YouTube link when none is connected, and the delete-account
// danger zone. flash surfaces a one-shot notification (disconnect/connect).
templ AccountPage(displayName, email string, conns []store.Connection, autoSummarize bool, flash string) {
templ AccountPage(displayName, email string, conns []store.Connection, autoSummarize bool, channelErrors []store.ChannelError, flash string) {
@Layout("Tapir — Account") {
@flashBanner(flash)
<article class="account">
@@ -419,6 +420,24 @@ templ AccountPage(displayName, email string, conns []store.Connection, autoSumma
</p>
@summarizeModeControl(autoSummarize)
</section>
if len(channelErrors) > 0 {
<section class="channel-errors">
<h2>Unavailable channels</h2>
<p class="muted">
{ fmt.Sprintf("%d channel(s) returned errors on the last discovery pass.", len(channelErrors)) }
These may have been deleted or made private on YouTube.
</p>
<ul class="channel-error-list">
for _, ce := range channelErrors {
<li>
<span class="channel-error-name">{ ce.ChannelName }</span>
<span class="chip chip-warn">unavailable</span>
<span class="muted channel-error-since">since { ce.FirstSeen.Format("2006-01-02") }</span>
</li>
}
</ul>
</section>
}
<section>
<h2>Connected accounts</h2>
if len(conns) == 0 {