YouTube enricher: observable + fast-fail proxy + live channel re-poll
Addresses 'says running but I see nothing': - Real logs: 'enriching <ch>…', 'proxy returned N', 'done — N proxy + N per-video (X/Y dated)' so you can see exactly what it's doing. - Proxy timeout 8s→4s so dead instances (most public ones) fail fast instead of hanging ~40s before the yt-dlp fallback. - Channel page now re-fetches a few times (25/60/110s) after load while videos are still undated, re-rendering only when NEW year-seasons appear — so dates the background enricher fills in pop in without a manual reload.
This commit is contained in:
parent
3a24fc24b1
commit
3235b196b7
3 changed files with 37 additions and 5 deletions
|
|
@ -309,7 +309,9 @@ def _proxy_get(url, fetch):
|
|||
if fetch is not None:
|
||||
return fetch(url)
|
||||
import requests
|
||||
r = requests.get(url, timeout=8, headers={"User-Agent": _UA, "Accept": "application/json"})
|
||||
# Short timeout so a dead instance (most public ones are flaky) fails fast and
|
||||
# we fall through to the next / to the yt-dlp fallback instead of hanging.
|
||||
r = requests.get(url, timeout=4, headers={"User-Agent": _UA, "Accept": "application/json"})
|
||||
return r.json() if r.status_code == 200 else None
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -116,15 +116,16 @@ class YoutubeDateEnricher:
|
|||
if not cid or db.channel_dates_enriched_recently(cid):
|
||||
return
|
||||
self._current = self._titles.get(cid) or cid
|
||||
logger.info("YouTube dates: enriching %s (%s)…", self._current, cid)
|
||||
|
||||
dates = {}
|
||||
try:
|
||||
dates = yt.proxy_channel_dates(cid) or {}
|
||||
except Exception:
|
||||
logger.info("proxy date fetch failed for %s", cid, exc_info=True)
|
||||
logger.info("YouTube dates: proxy returned %d for %s", len(dates), cid)
|
||||
if dates:
|
||||
db.cache_video_dates([{"youtube_id": k, "published_at": v} for k, v in dates.items()])
|
||||
logger.info("YouTube dates: %d cached for %s (proxy)", len(dates), cid)
|
||||
|
||||
# Fallback (proxy down): date the channel's RECENT UPLOADS via yt-dlp, not
|
||||
# just wished videos — so years populate fully even with no working proxy.
|
||||
|
|
@ -150,12 +151,11 @@ class YoutubeDateEnricher:
|
|||
db.cache_video_dates([{"youtube_id": vid, "published_at": v["published_at"]}])
|
||||
filled += 1
|
||||
time.sleep(_FALLBACK_DELAY)
|
||||
if filled:
|
||||
logger.info("YouTube dates: %d cached for %s (per-video fallback)", filled, cid)
|
||||
|
||||
self._channels_done += 1
|
||||
self._dates_total += len(dates) + filled
|
||||
db.mark_channel_dates_enriched(cid, len(dates) + filled)
|
||||
logger.info("YouTube dates: %s done — %d proxy + %d per-video (%d/%d dated)",
|
||||
cid, len(dates), filled, len(dates) + filled, len(ids))
|
||||
|
||||
|
||||
_enricher = None
|
||||
|
|
|
|||
|
|
@ -1341,11 +1341,38 @@
|
|||
episode_total: (ch.videos || []).length, episode_owned: 0 };
|
||||
}
|
||||
|
||||
// Freshly-followed channels get their upload dates filled in over the next
|
||||
// minute+ by the background enricher. Re-fetch a few times so new year-seasons
|
||||
// pop in WITHOUT a manual reload (only re-renders if more years appeared).
|
||||
var ytRepollTimers = [];
|
||||
function ytClearRepoll() { ytRepollTimers.forEach(function (t) { clearTimeout(t); }); ytRepollTimers = []; }
|
||||
function ytRefetch(id) {
|
||||
var before = ((data && data.seasons) || []).length;
|
||||
fetch('/api/video/youtube/channel/' + encodeURIComponent(id) + '?limit=90', { headers: { 'Accept': 'application/json' } })
|
||||
.then(function (r) { return r.ok ? r.json() : null; })
|
||||
.then(function (resp) {
|
||||
if (!resp || !resp.success || currentId !== id || currentSource !== 'youtube') return;
|
||||
var nd = ytToShow(resp);
|
||||
if (nd.seasons.length <= before) return; // no new years yet → don't disrupt the view
|
||||
data = nd;
|
||||
if (!seasonByNum(selectedSeason)) selectedSeason = nd.seasons.length ? nd.seasons[0].season_number : null;
|
||||
renderBillboard(nd); renderSeasonNav(); renderEpisodes();
|
||||
})
|
||||
.catch(function () { /* ignore */ });
|
||||
}
|
||||
function ytScheduleRepoll(id) {
|
||||
ytClearRepoll();
|
||||
[25000, 60000, 110000].forEach(function (ms) {
|
||||
ytRepollTimers.push(setTimeout(function () { ytRefetch(id); }, ms));
|
||||
});
|
||||
}
|
||||
|
||||
function loadChannel(id) {
|
||||
currentKind = 'show'; currentSource = 'youtube'; // render into the show container
|
||||
if (currentId !== id) artAttemptedFor = null;
|
||||
currentId = id;
|
||||
if (!root()) return;
|
||||
ytClearRepoll();
|
||||
showLoading(true); resetExtras(); showEpSyncing(false);
|
||||
['[data-vd-episodes]', '[data-vd-season-nav]'].forEach(function (s) { var n = q(s); if (n) n.innerHTML = ''; });
|
||||
var r0 = root(); if (r0) r0.style.removeProperty('--vd-accent-rgb');
|
||||
|
|
@ -1364,6 +1391,9 @@
|
|||
var sub = document.querySelector('.video-subpage[data-video-subpage="video-show-detail"]');
|
||||
if (sub) sub.scrollTop = 0;
|
||||
ytLoadPlaylists(id);
|
||||
// If videos are still undated, the enricher is (or will be) filling
|
||||
// them in — poll a few times so the years appear live.
|
||||
if (data.seasons.some(function (s) { return s.season_number === 0; })) ytScheduleRepoll(id);
|
||||
})
|
||||
.catch(function () { showLoading(false); setText('[data-vd-title]', 'Could not load channel'); });
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue