implement powerful viewer count system
This commit is contained in:
@@ -7,12 +7,13 @@ use App\Models\Broadcast;
|
||||
use App\Models\Channel;
|
||||
use App\Models\User;
|
||||
use App\Models\Vod;
|
||||
use App\Services\Streaming\ViewerCountStore;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class AdminModerationController extends Controller
|
||||
{
|
||||
public function suspendChannel(Request $request, Channel $channel): RedirectResponse
|
||||
public function suspendChannel(Request $request, Channel $channel, ViewerCountStore $viewerCounts): RedirectResponse
|
||||
{
|
||||
$channel->forceFill([
|
||||
'suspended_at' => now(),
|
||||
@@ -20,6 +21,7 @@ class AdminModerationController extends Controller
|
||||
'live_broadcast_id' => null,
|
||||
'viewer_count' => 0,
|
||||
])->save();
|
||||
$viewerCounts->forget($channel);
|
||||
|
||||
$this->audit($request, $channel, 'channel.suspended');
|
||||
|
||||
@@ -35,7 +37,7 @@ class AdminModerationController extends Controller
|
||||
return back()->with('success', 'Channel restored.');
|
||||
}
|
||||
|
||||
public function suspendUser(Request $request, User $user): RedirectResponse
|
||||
public function suspendUser(Request $request, User $user, ViewerCountStore $viewerCounts): RedirectResponse
|
||||
{
|
||||
$user->forceFill(['suspended_at' => now()])->save();
|
||||
$user->channel?->forceFill([
|
||||
@@ -44,12 +46,16 @@ class AdminModerationController extends Controller
|
||||
'viewer_count' => 0,
|
||||
])->save();
|
||||
|
||||
if ($user->channel) {
|
||||
$viewerCounts->forget($user->channel);
|
||||
}
|
||||
|
||||
$this->audit($request, $user, 'user.suspended');
|
||||
|
||||
return back()->with('success', 'User suspended.');
|
||||
}
|
||||
|
||||
public function stopBroadcast(Request $request, Broadcast $broadcast): RedirectResponse
|
||||
public function stopBroadcast(Request $request, Broadcast $broadcast, ViewerCountStore $viewerCounts): RedirectResponse
|
||||
{
|
||||
$broadcast->forceFill([
|
||||
'status' => Broadcast::STATUS_ENDED,
|
||||
@@ -61,6 +67,7 @@ class AdminModerationController extends Controller
|
||||
'live_broadcast_id' => null,
|
||||
'viewer_count' => 0,
|
||||
])->save();
|
||||
$viewerCounts->forget($broadcast->channel);
|
||||
|
||||
$this->audit($request, $broadcast, 'broadcast.stopped');
|
||||
|
||||
|
||||
@@ -5,6 +5,8 @@ namespace App\Http\Controllers;
|
||||
use App\Models\Channel;
|
||||
use App\Models\ChatMessage;
|
||||
use App\Models\Vod;
|
||||
use App\Services\Streaming\PlaybackViewerSigner;
|
||||
use App\Services\Streaming\ViewerCountStore;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Inertia\Inertia;
|
||||
@@ -12,8 +14,12 @@ use Inertia\Response;
|
||||
|
||||
class ChannelController extends Controller
|
||||
{
|
||||
public function show(Request $request, Channel $channel): Response
|
||||
{
|
||||
public function show(
|
||||
Request $request,
|
||||
Channel $channel,
|
||||
ViewerCountStore $viewerCounts,
|
||||
PlaybackViewerSigner $playbackViewers,
|
||||
): Response {
|
||||
abort_if($channel->isSuspended(), 404);
|
||||
|
||||
$channel->load(['category', 'liveBroadcast', 'user']);
|
||||
@@ -22,7 +28,7 @@ class ChannelController extends Controller
|
||||
$currentBroadcast = $channel->liveBroadcast;
|
||||
|
||||
return Inertia::render('channels/show', [
|
||||
'channel' => [
|
||||
'channel' => fn () => [
|
||||
'id' => $channel->id,
|
||||
'slug' => $channel->slug,
|
||||
'display_name' => $channel->display_name,
|
||||
@@ -31,7 +37,7 @@ class ChannelController extends Controller
|
||||
'banner_url' => $this->mediaUrl($channel->banner_path),
|
||||
'thumbnail_url' => $this->mediaUrl($channel->thumbnail_path),
|
||||
'is_live' => $channel->is_live,
|
||||
'viewer_count' => $channel->viewer_count,
|
||||
'viewer_count' => $channel->is_live ? $viewerCounts->current($channel) : 0,
|
||||
'followers_count' => $channel->follows_count,
|
||||
'category' => $channel->category ? [
|
||||
'id' => $channel->category->id,
|
||||
@@ -43,14 +49,23 @@ class ChannelController extends Controller
|
||||
'name' => $channel->user->name,
|
||||
],
|
||||
],
|
||||
'currentBroadcast' => $currentBroadcast ? [
|
||||
'currentBroadcast' => fn () => $currentBroadcast ? [
|
||||
'id' => $currentBroadcast->id,
|
||||
'title' => $currentBroadcast->title,
|
||||
'hls_url' => $currentBroadcast->hls_url,
|
||||
'hls_url' => $currentBroadcast->hls_url
|
||||
? $playbackViewers->signedPlaybackUrl($currentBroadcast->hls_url, $channel, $currentBroadcast, $request)
|
||||
: null,
|
||||
'recording_enabled' => $currentBroadcast->recording_enabled,
|
||||
'started_at' => $currentBroadcast->started_at?->toIso8601String(),
|
||||
] : null,
|
||||
'vods' => $channel->vods()
|
||||
'viewerStats' => fn () => [
|
||||
'current' => $channel->is_live ? $viewerCounts->current($channel) : 0,
|
||||
'peak' => $currentBroadcast && $channel->is_live
|
||||
? max($currentBroadcast->viewer_peak, $viewerCounts->current($channel))
|
||||
: 0,
|
||||
'refresh_interval_ms' => max(5000, (int) config('streaming.viewer_stats_poll_interval', 30000)),
|
||||
],
|
||||
'vods' => fn () => $channel->vods()
|
||||
->where(function ($query) {
|
||||
$query->whereNull('expires_at')->orWhere('expires_at', '>', now());
|
||||
})
|
||||
@@ -65,7 +80,7 @@ class ChannelController extends Controller
|
||||
'published_at' => $vod->published_at?->toIso8601String(),
|
||||
'expires_at' => $vod->expires_at?->toIso8601String(),
|
||||
]),
|
||||
'chatMessages' => ChatMessage::query()
|
||||
'chatMessages' => fn () => ChatMessage::query()
|
||||
->where('channel_id', $channel->id)
|
||||
->when($currentBroadcast, fn ($query) => $query->where('broadcast_id', $currentBroadcast->id))
|
||||
->with('user')
|
||||
@@ -84,9 +99,7 @@ class ChannelController extends Controller
|
||||
'avatar_url' => $message->user->avatar,
|
||||
],
|
||||
]),
|
||||
'isFollowing' => $request->user()
|
||||
? $channel->follows()->where('user_id', $request->user()->id)->exists()
|
||||
: false,
|
||||
'isFollowing' => fn () => $request->user() && $channel->follows()->where('user_id', $request->user()->id)->exists(),
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ use App\Models\Broadcast;
|
||||
use App\Models\Category;
|
||||
use App\Models\Channel;
|
||||
use App\Services\Streaming\StreamKeyManager;
|
||||
use App\Services\Streaming\ViewerCountStore;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
@@ -154,7 +155,7 @@ class CreatorDashboardController extends Controller
|
||||
return back()->with('success', 'Broadcast is ready. Start streaming from OBS.');
|
||||
}
|
||||
|
||||
public function stopBroadcast(Request $request, Broadcast $broadcast): RedirectResponse
|
||||
public function stopBroadcast(Request $request, Broadcast $broadcast, ViewerCountStore $viewerCounts): RedirectResponse
|
||||
{
|
||||
abort_unless($broadcast->channel->user_id === $request->user()->id, 403);
|
||||
|
||||
@@ -168,6 +169,7 @@ class CreatorDashboardController extends Controller
|
||||
'viewer_count' => 0,
|
||||
'live_broadcast_id' => null,
|
||||
])->save();
|
||||
$viewerCounts->forget($broadcast->channel);
|
||||
|
||||
return back()->with('success', 'Broadcast ended.');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user