From 4b5e48a26d647d4a5d8db34ece8001928a7c6e83 Mon Sep 17 00:00:00 2001 From: Greg Pomerantz Date: Wed, 21 Oct 2026 08:17:21 -0400 Subject: [PATCH] Walk all YouTube sources fully; channel order is not newest-first The Iron Culture channel lists recent episodes mid-list (Ep 392/393 at ~position 360 of 396), so the 'channels enumerate newest-first' boundary assumption is wrong: with it, rssd would break at the first pre-start-date video and miss recent episodes, while re-resolving a boundary video every poll. Walk channels exactly like playlists: always record out-of-range videos as skipped so discovery stays incremental. Also ignore media files (manual yt-dlp downloads have landed in the working tree before). --- .gitignore | 6 ++++++ main.go | 19 +++++-------------- 2 files changed, 11 insertions(+), 14 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e7b96f6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ + +# Podcast download byproducts +*.webm +*.mp4 +*.opus +*.mp3 diff --git a/main.go b/main.go index 83f1631..165670e 100644 --- a/main.go +++ b/main.go @@ -175,11 +175,6 @@ func pollYouTube(feed config.Feed, sm *state.Manager, jobs chan<- downloader.Dow }) start := feed.PublishedAt() - // Channels enumerate newest-first, so the first video older than the start - // date is a stable boundary: everything after it is older too. Playlists - // have no guaranteed order, so they must be walked fully and their - // out-of-range videos recorded as skipped to avoid re-resolving them. - newestFirst := !strings.Contains(strings.ToLower(feed.URL), "list=") newCount, enqueued, skipped := 0, 0, 0 // Per-video resolve timeout. A single pathological video (JS-challenge @@ -200,16 +195,12 @@ func pollYouTube(feed config.Feed, sm *state.Manager, jobs chan<- downloader.Dow continue } - // Out of the start_date window. + // Out of the start_date window. Record as skipped so it isn't + // re-resolved next poll. (Channel video order is not reliably + // newest-first — e.g. the Iron Culture channel lists recent episodes + // mid-list — so we can't stop at the first out-of-range video and + // must walk the whole source.) if !start.IsZero() && !info.UploadDate.IsZero() && info.UploadDate.Before(start) { - if newestFirst { - // Stop at the first out-of-range video without recording it. It - // stays "new" each poll, so it remains a stable boundary and we - // never walk backward through older videos. - fmt.Fprintf(os.Stderr, "rssd: %s reached pre-start-date boundary, stopping\n", feed.URL) - break - } - // Playlist: record as skipped so it isn't re-resolved next poll. dp := computeYTDestPath(feed.OutputDir, info.UploadDate, info.Title, v.ID) if sm.AddJobWithStatus(feed.URL, watchURL(v.ID), dp, state.StatusSkipped, info.Duration) { skipped++