feat(domain): add SourceRateLimited transcript source
429 from the caption endpoint means the IP is rate-limited (retry later), not that the video has no captions. Distinguishing it from SourceNone is the prerequisite for the runner's backoff/retry logic. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 116 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 73 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 58 KiB |
@@ -18,6 +18,12 @@ type TranscriptSource string
|
||||
const (
|
||||
SourceCaptions TranscriptSource = "captions"
|
||||
SourceNone TranscriptSource = "none"
|
||||
// SourceRateLimited records that the caption endpoint returned HTTP 429.
|
||||
// Unlike SourceNone (a permanent absence), this is a transient "retry later":
|
||||
// the IP is rate-limited, not the video caption-less. It carries no text
|
||||
// (HasText is false), so the engine degrades the same as SourceNone, but the
|
||||
// runner persists it distinctly to retry after a backoff window.
|
||||
SourceRateLimited TranscriptSource = "rate_limited"
|
||||
)
|
||||
|
||||
// User is the Tapir-side profile. At Stage 0 there is exactly one.
|
||||
|
||||
@@ -535,6 +535,11 @@ a.btn, a.btn:visited { color: var(--accent-fg); }
|
||||
.empty { text-align: center; color: var(--muted); padding: var(--s5) var(--s4); border: 1px dashed var(--line); border-radius: var(--radius); background: var(--card); }
|
||||
.empty strong { display: block; color: var(--fg); font-size: 1.05rem; margin-bottom: var(--s2); }
|
||||
.empty code { background: var(--accent-weak); color: var(--accent); padding: .1rem .35rem; border-radius: .3rem; }
|
||||
.empty p { margin: var(--s3) 0 0; }
|
||||
/* connected-but-empty: a distinct accent callout, not a muted blank state, so a
|
||||
fresh account knows the next step is to run tapir, not "something is broken". */
|
||||
.empty-connected { border-style: solid; border-color: var(--accent); background: var(--accent-weak); color: var(--fg); }
|
||||
.empty-connected strong { color: var(--accent); }
|
||||
|
||||
/* flash / notification banner */
|
||||
.flash { padding: var(--s2) var(--s3); border-radius: var(--radius); margin-bottom: var(--s4); font-size: .92rem; border: 1px solid var(--line); }
|
||||
|
||||
@@ -79,12 +79,12 @@ templ flashBanner(code string) {
|
||||
// #summary-list region; a non-HTMX request renders the whole page. flash carries
|
||||
// a one-shot notification (e.g. "connected", "registered") surfaced on arrival
|
||||
// after a POST→redirect.
|
||||
templ ListPage(rows []store.SummaryRow, f Filter, flash string) {
|
||||
templ ListPage(rows []store.SummaryRow, f Filter, flash string, hasConnected bool) {
|
||||
@Layout("Tapir — Summaries") {
|
||||
@flashBanner(flash)
|
||||
@filterForm(f)
|
||||
<div id="summary-list">
|
||||
@summaryList(rows)
|
||||
@summaryList(rows, hasConnected)
|
||||
</div>
|
||||
}
|
||||
}
|
||||
@@ -110,12 +110,20 @@ templ filterForm(f Filter) {
|
||||
// summaryList is the swappable list fragment: one card per video (summarized or
|
||||
// not). Cards reflow to a single column on mobile; an empty list shows a friendly
|
||||
// first-run state instead of a blank table.
|
||||
templ summaryList(rows []store.SummaryRow) {
|
||||
templ summaryList(rows []store.SummaryRow, hasConnected bool) {
|
||||
if len(rows) == 0 {
|
||||
if hasConnected {
|
||||
<div class="empty empty-connected">
|
||||
<strong>Your YouTube account is connected!</strong>
|
||||
<span>Run <code>tapir run</code> to discover your subscriptions. Videos will appear here once discovered. In manual mode, each new video gets a Summarize button.</span>
|
||||
</div>
|
||||
} else {
|
||||
<div class="empty">
|
||||
<strong>No videos yet</strong>
|
||||
<span>Videos appear here as your subscriptions are processed — run <code>tapir run</code> to fetch them. In manual mode, use the Summarize button to queue one.</span>
|
||||
<span>Connect your YouTube account to get started.</span>
|
||||
<p><a class="btn" href="/oauth/youtube/connect">Connect YouTube</a></p>
|
||||
</div>
|
||||
}
|
||||
} else {
|
||||
<ul class="cards">
|
||||
for _, r := range rows {
|
||||
|
||||
Reference in New Issue
Block a user