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:
1 parent
59870862db
commit
9ad842dcd5
5 files changed
+327
-2
No files matched your search
@@ -395,4 +395,47 @@ class VideoController extends Controller
|
||||
|
||||
return response()->download($path, $filename);
|
||||
}
|
||||
|
||||
// Trending videos page
|
||||
public function trending(Request $request)
|
||||
{
|
||||
$hours = $request->get('hours', 48); // Default: 48 hours
|
||||
$limit = $request->get('limit', 50);
|
||||
|
||||
// Validate parameters
|
||||
$hours = min(max($hours, 24), 168); // Between 24h and 7 days
|
||||
$limit = min(max($limit, 10), 100);
|
||||
|
||||
// Get trending videos using the scope with raw score calculation
|
||||
$trendingVideos = \DB::table('videos')
|
||||
->select('videos.*',
|
||||
\DB::raw('(
|
||||
(SELECT COUNT(*) FROM video_views vv WHERE vv.video_id = videos.id AND vv.watched_at >= DATE_SUB(NOW(), INTERVAL ' . $hours . ' HOUR)) * 0.70 +
|
||||
(SELECT COUNT(*) FROM video_views vv WHERE vv.video_id = videos.id AND vv.watched_at >= DATE_SUB(NOW(), INTERVAL ' . $hours . ' HOUR)) / ' . $hours . ' * 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
|
||||
) as trending_score')
|
||||
)
|
||||
->where('visibility', 'public')
|
||||
->where('status', 'ready')
|
||||
->where('created_at', '>=', now()->subDays(10))
|
||||
->having('trending_score', '>', 0)
|
||||
->orderByDesc('trending_score')
|
||||
->limit($limit)
|
||||
->get();
|
||||
|
||||
// Load user relationship
|
||||
$trendingVideos = $trendingVideos->map(function ($video) {
|
||||
$video->user = \App\Models\User::find($video->user_id);
|
||||
$video->view_count = \DB::table('video_views')->where('video_id', $video->id)->count();
|
||||
$video->like_count = \DB::table('video_likes')->where('video_id', $video->id)->count();
|
||||
return $video;
|
||||
});
|
||||
|
||||
return view('videos.trending', [
|
||||
'videos' => $trendingVideos,
|
||||
'hours' => $hours,
|
||||
'limit' => $limit,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
Reference in new issue
Block a user