Add trending videos page with YouTube-style algorithm

- Trending algorithm based on:
  - Recent views (last 48h): 70% weight
  - View velocity (views/hour): 15% weight
  - Recency bonus: 10% weight
  - Like count: 5% weight
- Excludes videos older than 10 days
- Filter options: Today/This Week/This Month
- Added route, controller method, and view
- Updated nav to link to trending page
This commit is contained in:
ghassan committed 2026-03-03 21:30:44 +03:00
1 parent 59870862db
commit 9ad842dcd5
5 files changed
+327 -2

No files matched your search

+65
View File
@@ -186,3 +186,68 @@ class Video extends Model
return $this->comments()->count();
}
}
// Get recent views count (within hours)
public function getRecentViews( = 48)
{
return \DB::table('video_views')
->where('video_id', ->id)
->where('watched_at', '>=', now()->subHours())
->count();
}
// Get views in last 24 hours (for velocity calculation)
public function getViewsLast24Hours()
{
return ->getRecentViews(24);
}
// Calculate trending score (YouTube-style algorithm)
public function getTrendingScore( = 48)
{
= ->getRecentViews();
// Don't include videos older than 10 days
if (->created_at->diffInDays(now()) > 10) {
return 0;
}
// Don't include videos with no recent views
if ( < 5) {
return 0;
}
// Calculate view velocity (views per hour in last 48 hours)
= / ;
// Recency bonus: newer videos get a boost
= ->created_at->diffInHours(now());
= max(0, 1 - ( / 240)); // Decreases over 10 days
// Like count bonus
= ->like_count * 0.1;
// Calculate final score
// Weight: 70% recent views, 15% velocity, 10% recency, 5% likes
= ( * 0.70) +
( * 100 * 0.15) +
( * 50 * 0.10) +
( * 0.05);
return round(, 2);
}
// Scope for trending videos
public function scopeTrending(, = 48, = 50)
{
return ->public()
->where('status', 'ready')
->where('created_at', '>=', now()->subDays(10))
->orderByDesc(\DB::raw('(
(SELECT COUNT(*) FROM video_views vv WHERE vv.video_id = videos.id AND vv.watched_at >= DATE_SUB(NOW(), INTERVAL ' . . ' HOUR)) * 0.70 +
(SELECT COUNT(*) FROM video_views vv WHERE vv.video_id = videos.id AND vv.watched_at >= DATE_SUB(NOW(), INTERVAL ' . . ' HOUR)) / ' . . ' * 100 * 0.15 +
GREATEST(0, 1 - TIMESTAMPDIFF(HOUR, videos.created_at, NOW()) / 240) * 50 * 0.10 +
(SELECT COUNT(*) FROM video_likes vl WHERE vl.video_id = videos.id) * 0.1 * 0.05
)'))
->limit();
}