From 24ac38d382cec1b6fb80db002ec87785a58b5d29 Mon Sep 17 00:00:00 2001 From: Pitchfork-and-Torch <297513015+Pitchfork-and-Torch@users.noreply.github.com> Date: Thu, 3 Sep 2026 22:04:43 -0400 Subject: [PATCH] Honor exclude_videos on quotes of video posts VideoFilter only checked min_video_duration_ms on the primary card. QuoteHydrator already hydrates quoted_video_duration_ms, but only when the VQV ranking param is on. Fetch that duration when exclude_videos is set and drop quotes of videos the same as native video posts. --- .../candidate_hydrators/quote_hydrator.rs | 3 +- home-mixer/filters/video_filter.rs | 33 +++++++++++++++++-- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/home-mixer/candidate_hydrators/quote_hydrator.rs b/home-mixer/candidate_hydrators/quote_hydrator.rs index 47ff7842..ff899e66 100644 --- a/home-mixer/candidate_hydrators/quote_hydrator.rs +++ b/home-mixer/candidate_hydrators/quote_hydrator.rs @@ -130,7 +130,8 @@ impl Hydrator for QuoteHydrator { .into_iter() .collect(); - let fetch_quoted_duration = query.params.get(EnableQuotedVqvDurationCheck); + let fetch_quoted_duration = + query.params.get(EnableQuotedVqvDurationCheck) || query.exclude_videos; let quoted_tweet_ids: Vec = if fetch_quoted_duration { resolved .iter() diff --git a/home-mixer/filters/video_filter.rs b/home-mixer/filters/video_filter.rs index f9b1c841..9dedc6bd 100644 --- a/home-mixer/filters/video_filter.rs +++ b/home-mixer/filters/video_filter.rs @@ -14,9 +14,9 @@ impl Filter for VideoFilter { _query: &ScoredPostsQuery, candidates: Vec, ) -> FilterResult { - let (kept, removed): (Vec<_>, Vec<_>) = candidates - .into_iter() - .partition(|c| c.min_video_duration_ms.is_none()); + let (kept, removed): (Vec<_>, Vec<_>) = candidates.into_iter().partition(|c| { + c.min_video_duration_ms.is_none() && c.quoted_video_duration_ms.is_none() + }); FilterResult { kept, removed } } @@ -96,4 +96,31 @@ mod tests { assert_eq!(result.kept.len(), 2); assert!(result.removed.is_empty()); } + + #[test] + fn test_removes_quotes_of_videos() { + let query = ScoredPostsQuery { + exclude_videos: true, + ..Default::default() + }; + + let candidates = vec![ + PostCandidate { + tweet_id: 1, + quoted_video_duration_ms: Some(5000), + ..Default::default() + }, + PostCandidate { + tweet_id: 2, + quoted_video_duration_ms: None, + ..Default::default() + }, + ]; + + let result = VideoFilter.filter(&query, candidates); + assert_eq!(result.kept.len(), 1); + assert_eq!(result.kept[0].tweet_id, 2); + assert_eq!(result.removed.len(), 1); + assert_eq!(result.removed[0].tweet_id, 1); + } }