Overhaul For You feed ranking and freshness

Ranking improvements:
- Wider candidate pool (4x limit) with ±12pt score perturbation so
  same-score videos shuffle differently each load
- Recent channel engagement signal: channels watched in past 30 days
  get a +4pts/watch boost
- Bail penalty: -25pts for videos started but abandoned before 20%
- Impression penalty: -3pts per prior feed appearance (capped at 10),
  so repeatedly-skipped videos sink naturally
- rn cap raised to 5 for more candidates; Python-side sampling picks top limit

Feed UX:
- Reshuffle button now available on For You (ranked) mode, not just Explore
- shuffleKey now always included in query key (not just random mode)
- Ranked mode staleTime reduced from 10min to 90s

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-27 01:14:10 +02:00
parent c11e1fdaf7
commit bbf7cc939b
3 changed files with 48 additions and 13 deletions

View File

@@ -56,9 +56,9 @@ export default function Home() {
};
const { data: feedData = [], isLoading: loadingFeed } = useQuery({
queryKey: ["home-feed", mode, page, hideWatched, duration, mode === "random" ? shuffleKey : 0],
queryKey: ["home-feed", mode, page, hideWatched, duration, shuffleKey],
queryFn: () => homeFeed(page, PAGE_SIZE, mode, duration).then((r) => r.data),
staleTime: 10 * 60_000,
staleTime: mode === "ranked" ? 90_000 : 10 * 60_000,
placeholderData: (prev) => prev,
});
@@ -195,9 +195,13 @@ export default function Home() {
{mode === "chronological" && (
<p className="text-xs text-zinc-600">All videos from channels you follow, newest first.</p>
)}
{mode === "random" && (
{(mode === "ranked" || mode === "random") && (
<div className="flex items-center justify-between">
<p className="text-xs text-zinc-600">Random from your discovery pool no weighting, no ranking.</p>
<p className="text-xs text-zinc-600">
{mode === "ranked"
? "Ranked by your taste — reshuffles show a fresh mix."
: "Random from your discovery pool — no weighting, no ranking."}
</p>
<button
onClick={handleReshuffle}
className="flex items-center gap-1.5 text-xs text-zinc-500 hover:text-zinc-300 transition-colors shrink-0 ml-4"