Browse Source

Add live stream events

Daniel Supernault 3 years ago
parent
commit
aa498af073

+ 51 - 0
app/Events/LiveStream/BanUser.php

@@ -0,0 +1,51 @@
+<?php
+
+namespace App\Events\LiveStream;
+
+use Illuminate\Broadcasting\Channel;
+use Illuminate\Broadcasting\InteractsWithSockets;
+use Illuminate\Broadcasting\PresenceChannel;
+use Illuminate\Broadcasting\PrivateChannel;
+use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
+use Illuminate\Foundation\Events\Dispatchable;
+use Illuminate\Queue\SerializesModels;
+use App\Models\LiveStream;
+
+class BanUser implements ShouldBroadcast
+{
+    use Dispatchable, InteractsWithSockets, SerializesModels;
+
+    public $livestream;
+    public $profileId;
+
+    /**
+     * Create a new event instance.
+     *
+     * @return void
+     */
+    public function __construct(LiveStream $livestream, $profileId)
+    {
+        $this->livestream = $livestream;
+        $this->profileId = $profileId;
+    }
+
+    /**
+     * Get the channels the event should broadcast on.
+     *
+     * @return \Illuminate\Broadcasting\Channel|array
+     */
+    public function broadcastOn()
+    {
+        return new PrivateChannel('live.chat.' . $this->livestream->profile_id);
+    }
+
+    public function broadcastAs()
+    {
+        return 'chat.ban-user';
+    }
+
+    public function broadcastWith()
+    {
+        return ['id' => $this->profileId];
+    }
+}

+ 51 - 0
app/Events/LiveStream/DeleteChatComment.php

@@ -0,0 +1,51 @@
+<?php
+
+namespace App\Events\LiveStream;
+
+use Illuminate\Broadcasting\Channel;
+use Illuminate\Broadcasting\InteractsWithSockets;
+use Illuminate\Broadcasting\PresenceChannel;
+use Illuminate\Broadcasting\PrivateChannel;
+use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
+use Illuminate\Foundation\Events\Dispatchable;
+use Illuminate\Queue\SerializesModels;
+use App\Models\LiveStream;
+
+class DeleteChatComment implements ShouldBroadcast
+{
+    use Dispatchable, InteractsWithSockets, SerializesModels;
+
+    public $livestream;
+    public $chatmsg;
+
+    /**
+     * Create a new event instance.
+     *
+     * @return void
+     */
+    public function __construct(LiveStream $livestream, $chatmsg)
+    {
+        $this->livestream = $livestream;
+        $this->chatmsg = $chatmsg;
+    }
+
+    /**
+     * Get the channels the event should broadcast on.
+     *
+     * @return \Illuminate\Broadcasting\Channel|array
+     */
+    public function broadcastOn()
+    {
+        return new PrivateChannel('live.chat.' . $this->livestream->profile_id);
+    }
+
+    public function broadcastAs()
+    {
+        return 'chat.delete-message';
+    }
+
+    public function broadcastWith()
+    {
+        return ['id' => $this->chatmsg['id']];
+    }
+}

+ 51 - 0
app/Events/LiveStream/NewChatComment.php

@@ -0,0 +1,51 @@
+<?php
+
+namespace App\Events\LiveStream;
+
+use Illuminate\Broadcasting\Channel;
+use Illuminate\Broadcasting\InteractsWithSockets;
+use Illuminate\Broadcasting\PresenceChannel;
+use Illuminate\Broadcasting\PrivateChannel;
+use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
+use Illuminate\Foundation\Events\Dispatchable;
+use Illuminate\Queue\SerializesModels;
+use App\Models\LiveStream;
+
+class NewChatComment implements ShouldBroadcast
+{
+    use Dispatchable, InteractsWithSockets, SerializesModels;
+
+    public $livestream;
+    public $chatmsg;
+
+    /**
+     * Create a new event instance.
+     *
+     * @return void
+     */
+    public function __construct(LiveStream $livestream, $chatmsg)
+    {
+        $this->livestream = $livestream;
+        $this->chatmsg = $chatmsg;
+    }
+
+    /**
+     * Get the channels the event should broadcast on.
+     *
+     * @return \Illuminate\Broadcasting\Channel|array
+     */
+    public function broadcastOn()
+    {
+        return new PrivateChannel('live.chat.' . $this->livestream->profile_id);
+    }
+
+    public function broadcastAs()
+    {
+        return 'chat.new-message';
+    }
+
+    public function broadcastWith()
+    {
+        return ['msg' => $this->chatmsg];
+    }
+}

+ 51 - 0
app/Events/LiveStream/PinChatMessage.php

@@ -0,0 +1,51 @@
+<?php
+
+namespace App\Events\LiveStream;
+
+use Illuminate\Broadcasting\Channel;
+use Illuminate\Broadcasting\InteractsWithSockets;
+use Illuminate\Broadcasting\PresenceChannel;
+use Illuminate\Broadcasting\PrivateChannel;
+use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
+use Illuminate\Foundation\Events\Dispatchable;
+use Illuminate\Queue\SerializesModels;
+use App\Models\LiveStream;
+
+class PinChatMessage implements ShouldBroadcast
+{
+    use Dispatchable, InteractsWithSockets, SerializesModels;
+
+    public $livestream;
+    public $chatmsg;
+
+    /**
+     * Create a new event instance.
+     *
+     * @return void
+     */
+    public function __construct(LiveStream $livestream, $chatmsg)
+    {
+        $this->livestream = $livestream;
+        $this->chatmsg = $chatmsg;
+    }
+
+    /**
+     * Get the channels the event should broadcast on.
+     *
+     * @return \Illuminate\Broadcasting\Channel|array
+     */
+    public function broadcastOn()
+    {
+        return new PrivateChannel('live.chat.' . $this->livestream->profile_id);
+    }
+
+    public function broadcastAs()
+    {
+        return 'chat.pin-message';
+    }
+
+    public function broadcastWith()
+    {
+        return $this->chatmsg;
+    }
+}

+ 51 - 0
app/Events/LiveStream/UnpinChatMessage.php

@@ -0,0 +1,51 @@
+<?php
+
+namespace App\Events\LiveStream;
+
+use Illuminate\Broadcasting\Channel;
+use Illuminate\Broadcasting\InteractsWithSockets;
+use Illuminate\Broadcasting\PresenceChannel;
+use Illuminate\Broadcasting\PrivateChannel;
+use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
+use Illuminate\Foundation\Events\Dispatchable;
+use Illuminate\Queue\SerializesModels;
+use App\Models\LiveStream;
+
+class UnpinChatMessage implements ShouldBroadcast
+{
+    use Dispatchable, InteractsWithSockets, SerializesModels;
+
+    public $livestream;
+    public $chatmsg;
+
+    /**
+     * Create a new event instance.
+     *
+     * @return void
+     */
+    public function __construct(LiveStream $livestream, $chatmsg)
+    {
+        $this->livestream = $livestream;
+        $this->chatmsg = $chatmsg;
+    }
+
+    /**
+     * Get the channels the event should broadcast on.
+     *
+     * @return \Illuminate\Broadcasting\Channel|array
+     */
+    public function broadcastOn()
+    {
+        return new PrivateChannel('live.chat.' . $this->livestream->profile_id);
+    }
+
+    public function broadcastAs()
+    {
+        return 'chat.unpin-message';
+    }
+
+    public function broadcastWith()
+    {
+        return $this->chatmsg;
+    }
+}

+ 10 - 0
app/Http/Controllers/Api/ApiV1Controller.php

@@ -101,6 +101,16 @@ class ApiV1Controller extends Controller
 		return response()->json($res, $code, $headers, JSON_UNESCAPED_SLASHES);
 		return response()->json($res, $code, $headers, JSON_UNESCAPED_SLASHES);
 	}
 	}
 
 
+	public function getWebsocketConfig()
+	{
+		return config('broadcasting.default') === 'pusher' ? [
+			'host' => config('broadcasting.connections.pusher.options.host'),
+			'port' => config('broadcasting.connections.pusher.options.port'),
+			'key' => config('broadcasting.connections.pusher.key'),
+			'cluster' => config('broadcasting.connections.pusher.options.cluster')
+		] : [];
+	}
+
 	public function getApp(Request $request)
 	public function getApp(Request $request)
 	{
 	{
 		if(!$request->user()) {
 		if(!$request->user()) {

+ 129 - 19
app/Http/Controllers/LiveStreamController.php

@@ -9,6 +9,12 @@ use Illuminate\Support\Facades\Storage;
 use App\Services\AccountService;
 use App\Services\AccountService;
 use App\Services\FollowerService;
 use App\Services\FollowerService;
 use App\Services\LiveStreamService;
 use App\Services\LiveStreamService;
+use App\User;
+use App\Events\LiveStream\NewChatComment;
+use App\Events\LiveStream\DeleteChatComment;
+use App\Events\LiveStream\BanUser;
+use App\Events\LiveStream\PinChatMessage;
+use App\Events\LiveStream\UnpinChatMessage;
 
 
 class LiveStreamController extends Controller
 class LiveStreamController extends Controller
 {
 {
@@ -63,32 +69,48 @@ class LiveStreamController extends Controller
 		abort_if(!config('livestreaming.enabled'), 400);
 		abort_if(!config('livestreaming.enabled'), 400);
 		abort_if(!$request->user(), 403);
 		abort_if(!$request->user(), 403);
 
 
-		$stream = LiveStream::whereProfileId($request->input('profile_id'))->first();
+		$stream = LiveStream::whereProfileId($request->input('profile_id'))
+			->whereNotNull('live_at')
+			->orderByDesc('live_at')
+			->first();
 
 
 		if(!$stream) {
 		if(!$stream) {
 			return [];
 			return [];
 		}
 		}
 
 
 		$res = [];
 		$res = [];
-		$owner = $stream->profile_id == $request->user()->profile_id;
+		$owner = $request->user() ? $stream->profile_id == $request->user()->profile_id : false;
 
 
 		if($stream->visibility === 'private') {
 		if($stream->visibility === 'private') {
 			abort_if(!$owner && !FollowerService::follows($request->user()->profile_id, $stream->profile_id), 403, 'LSE:011');
 			abort_if(!$owner && !FollowerService::follows($request->user()->profile_id, $stream->profile_id), 403, 'LSE:011');
 		}
 		}
 
 
-		if($owner) {
-			$res['stream_key'] = $stream->stream_key;
-			$res['stream_id'] = $stream->stream_id;
-			$res['stream_url'] = $stream->getStreamKeyUrl();
-		}
+		$res = [
+			'hls_url' => $stream->getHlsUrl(),
+			'name' => $stream->name,
+			'description' => $stream->description
+		];
+
+		return response()->json($res, 200, [], JSON_UNESCAPED_SLASHES);
+	}
+
+
+	public function getUserStreamAsGuest(Request $request)
+	{
+		abort_if(!config('livestreaming.enabled'), 400);
+
+		$stream = LiveStream::whereProfileId($request->input('profile_id'))
+			->whereVisibility('public')
+			->whereNotNull('live_at')
+			->orderByDesc('live_at')
+			->first();
 
 
-		if($stream->live_at == null) {
-			$res['hls_url'] = null;
-			$res['name'] = $stream->name;
-			$res['description'] = $stream->description;
-			return $res;
+		if(!$stream) {
+			return [];
 		}
 		}
 
 
+		$res = [];
+
 		$res = [
 		$res = [
 			'hls_url' => $stream->getHlsUrl(),
 			'hls_url' => $stream->getHlsUrl(),
 			'name' => $stream->name,
 			'name' => $stream->name,
@@ -98,6 +120,21 @@ class LiveStreamController extends Controller
 		return response()->json($res, 200, [], JSON_UNESCAPED_SLASHES);
 		return response()->json($res, 200, [], JSON_UNESCAPED_SLASHES);
 	}
 	}
 
 
+	public function showProfilePlayer(Request $request, $username)
+	{
+		abort_if(!config('livestreaming.enabled'), 400);
+
+		$user = User::whereUsername($username)->firstOrFail();
+		$id = (string) $user->profile_id;
+		$stream = LiveStream::whereProfileId($id)
+			->whereNotNull('live_at')
+			->first();
+
+		abort_if(!$request->user() && $stream->visibility !== 'public', 404);
+
+		return view('live.player', compact('id'));
+	}
+
 	public function deleteStream(Request $request)
 	public function deleteStream(Request $request)
 	{
 	{
 		abort_if(!config('livestreaming.enabled'), 400);
 		abort_if(!config('livestreaming.enabled'), 400);
@@ -118,7 +155,7 @@ class LiveStreamController extends Controller
 		abort_if(!config('livestreaming.enabled'), 400);
 		abort_if(!config('livestreaming.enabled'), 400);
 		abort_if(!$request->user(), 403);
 		abort_if(!$request->user(), 403);
 
 
-		return LiveStream::whereVisibility('local')->whereNotNull('live_at')->get()->map(function($stream) {
+		return LiveStream::whereIn('visibility', ['local', 'public'])->whereNotNull('live_at')->get()->map(function($stream) {
 			return [
 			return [
 				'account' => AccountService::get($stream->profile_id),
 				'account' => AccountService::get($stream->profile_id),
 				'stream_id' => $stream->stream_id
 				'stream_id' => $stream->stream_id
@@ -162,22 +199,30 @@ class LiveStreamController extends Controller
 			'message' => 'required|max:140'
 			'message' => 'required|max:140'
 		]);
 		]);
 
 
-		$stream = LiveStream::whereProfileId($request->input('profile_id'))->firstOrFail();
+		$stream = LiveStream::whereProfileId($request->input('profile_id'))
+			->whereNotNull('live_at')
+			->firstOrFail();
 
 
 		$owner = $stream->profile_id == $request->user()->profile_id;
 		$owner = $stream->profile_id == $request->user()->profile_id;
 		if($stream->visibility === 'private') {
 		if($stream->visibility === 'private') {
-			abort_if(!$owner && !FollowerService::follows($request->user()->profile_id, $stream->profile_id), 403, 'LSE:022');
+			abort_if(!$owner && !FollowerService::follows($request->user()->profile_id, $stream->profile_id), 403);
 		}
 		}
 
 
+		$user = AccountService::get($request->user()->profile_id);
+
+		abort_if(!$user, 422);
+
 		$res = [
 		$res = [
+			'id' => (string) Str::uuid(),
 			'pid' => (string) $request->user()->profile_id,
 			'pid' => (string) $request->user()->profile_id,
-			'username' => $request->user()->username,
+			'avatar' => $user['avatar'],
+			'username' => $user['username'],
 			'text' => $request->input('message'),
 			'text' => $request->input('message'),
 			'ts' => now()->timestamp
 			'ts' => now()->timestamp
 		];
 		];
 
 
 		LiveStreamService::addComment($stream->profile_id, json_encode($res, JSON_UNESCAPED_SLASHES));
 		LiveStreamService::addComment($stream->profile_id, json_encode($res, JSON_UNESCAPED_SLASHES));
-
+		NewChatComment::dispatch($stream, $res);
 		return $res;
 		return $res;
 	}
 	}
 
 
@@ -209,14 +254,79 @@ class LiveStreamController extends Controller
 			'message' => 'required'
 			'message' => 'required'
 		]);
 		]);
 
 
-		abort_if($request->user()->profile_id != $request->input('profile_id'), 403);
+		$uid = $request->user()->profile_id;
+		$pid = $request->input('profile_id');
+		$msg = $request->input('message');
+		$admin = $uid == $request->input('profile_id');
+		$owner = $uid == $msg['pid'];
+		abort_if(!$admin && !$owner, 403);
 
 
-		$stream = LiveStream::whereProfileId($request->user()->profile_id)->firstOrFail();
+		$stream = LiveStream::whereProfileId($pid)->firstOrFail();
 
 
 		$payload = $request->input('message');
 		$payload = $request->input('message');
+		broadcast(new DeleteChatComment($stream, $payload))->toOthers();
 		$payload = json_encode($payload, JSON_UNESCAPED_SLASHES);
 		$payload = json_encode($payload, JSON_UNESCAPED_SLASHES);
 		LiveStreamService::deleteComment($stream->profile_id, $payload);
 		LiveStreamService::deleteComment($stream->profile_id, $payload);
+		return;
+	}
 
 
+	public function banChatUser(Request $request)
+	{
+		abort_if(!config('livestreaming.enabled'), 400);
+		abort_if(!$request->user(), 403);
+
+		$this->validate($request, [
+			'profile_id' => 'required|exists:profiles,id',
+		]);
+
+		abort_if($request->user()->profile_id == $request->input('profile_id'), 403);
+
+		$stream = LiveStream::whereProfileId($request->user()->profile_id)->firstOrFail();
+		$pid = $request->input('profile_id');
+
+		BanUser::dispatch($stream, $pid);
+		return;
+	}
+
+	public function pinChatComment(Request $request)
+	{
+		abort_if(!config('livestreaming.enabled'), 400);
+		abort_if(!$request->user(), 403);
+
+		$this->validate($request, [
+			'profile_id' => 'required|exists:profiles,id',
+			'message' => 'required'
+		]);
+
+		$uid = $request->user()->profile_id;
+		$pid = $request->input('profile_id');
+		$msg = $request->input('message');
+
+		abort_if($uid != $pid, 403);
+
+		$stream = LiveStream::whereProfileId($request->user()->profile_id)->firstOrFail();
+		PinChatMessage::dispatch($stream, $msg);
+		return;
+	}
+
+	public function unpinChatComment(Request $request)
+	{
+		abort_if(!config('livestreaming.enabled'), 400);
+		abort_if(!$request->user(), 403);
+
+		$this->validate($request, [
+			'profile_id' => 'required|exists:profiles,id',
+			'message' => 'required'
+		]);
+
+		$uid = $request->user()->profile_id;
+		$pid = $request->input('profile_id');
+		$msg = $request->input('message');
+
+		abort_if($uid != $pid, 403);
+
+		$stream = LiveStream::whereProfileId($request->user()->profile_id)->firstOrFail();
+		UnpinChatMessage::dispatch($stream, $msg);
 		return;
 		return;
 	}
 	}
 
 

+ 2 - 1
config/app.php

@@ -154,10 +154,11 @@ return [
          */
          */
         App\Providers\AppServiceProvider::class,
         App\Providers\AppServiceProvider::class,
         App\Providers\AuthServiceProvider::class,
         App\Providers\AuthServiceProvider::class,
-        // App\Providers\BroadcastServiceProvider::class,
+        App\Providers\BroadcastServiceProvider::class,
         App\Providers\HorizonServiceProvider::class,
         App\Providers\HorizonServiceProvider::class,
         App\Providers\EventServiceProvider::class,
         App\Providers\EventServiceProvider::class,
         App\Providers\RouteServiceProvider::class,
         App\Providers\RouteServiceProvider::class,
+        App\Providers\TelescopeServiceProvider::class,
         App\Providers\PassportServiceProvider::class,
         App\Providers\PassportServiceProvider::class,
 
 
     ],
     ],

+ 4 - 8
config/broadcasting.php

@@ -37,14 +37,10 @@ return [
             'app_id'  => env('PUSHER_APP_ID'),
             'app_id'  => env('PUSHER_APP_ID'),
             'options' => [
             'options' => [
                 'cluster'   => env('PUSHER_APP_CLUSTER'),
                 'cluster'   => env('PUSHER_APP_CLUSTER'),
-                'encrypted' => true,
-                'host' => env('APP_DOMAIN'),
-                'port' => 6001,
-                'scheme' => 'https',
-                'curl_options' => [
-                    CURLOPT_SSL_VERIFYHOST => 0,
-                    CURLOPT_SSL_VERIFYPEER => 0,
-                ]
+                'encrypted' => env('PUSHER_APP_ENCRYPTED', false),
+                'host' => env('PUSHER_HOST', env('APP_DOMAIN')),
+                'port' => env('PUSHER_PORT', 443),
+                'scheme' => env('PUSHER_SCHEME', 'https')
             ],
             ],
         ],
         ],
 
 

+ 5 - 0
routes/api.php

@@ -94,6 +94,7 @@ Route::group(['prefix' => 'api'], function() use($middleware) {
 	Route::group(['prefix' => 'v2'], function() use($middleware) {
 	Route::group(['prefix' => 'v2'], function() use($middleware) {
 		Route::get('search', 'Api\ApiV1Controller@searchV2')->middleware($middleware);
 		Route::get('search', 'Api\ApiV1Controller@searchV2')->middleware($middleware);
 		Route::post('media', 'Api\ApiV1Controller@mediaUploadV2')->middleware($middleware);
 		Route::post('media', 'Api\ApiV1Controller@mediaUploadV2')->middleware($middleware);
+		Route::get('streaming/config', 'Api\ApiV1Controller@getWebsocketConfig');
 	});
 	});
 
 
 	Route::group(['prefix' => 'live'], function() use($middleware) {
 	Route::group(['prefix' => 'live'], function() use($middleware) {
@@ -101,10 +102,14 @@ Route::group(['prefix' => 'api'], function() use($middleware) {
 		Route::post('stream/edit', 'LiveStreamController@editStream')->middleware($middleware);
 		Route::post('stream/edit', 'LiveStreamController@editStream')->middleware($middleware);
 		Route::get('active/list', 'LiveStreamController@getActiveStreams')->middleware($middleware);
 		Route::get('active/list', 'LiveStreamController@getActiveStreams')->middleware($middleware);
 		Route::get('accounts/stream', 'LiveStreamController@getUserStream')->middleware($middleware);
 		Route::get('accounts/stream', 'LiveStreamController@getUserStream')->middleware($middleware);
+		Route::get('accounts/stream/guest', 'LiveStreamController@getUserStreamAsGuest');
 		Route::delete('accounts/stream', 'LiveStreamController@deleteStream')->middleware($middleware);
 		Route::delete('accounts/stream', 'LiveStreamController@deleteStream')->middleware($middleware);
 		Route::get('chat/latest', 'LiveStreamController@getLatestChat')->middleware($middleware);
 		Route::get('chat/latest', 'LiveStreamController@getLatestChat')->middleware($middleware);
 		Route::post('chat/message', 'LiveStreamController@addChatComment')->middleware($middleware);
 		Route::post('chat/message', 'LiveStreamController@addChatComment')->middleware($middleware);
 		Route::post('chat/delete', 'LiveStreamController@deleteChatComment')->middleware($middleware);
 		Route::post('chat/delete', 'LiveStreamController@deleteChatComment')->middleware($middleware);
+		Route::post('chat/ban-user', 'LiveStreamController@banChatUser')->middleware($middleware);
+		Route::post('chat/pin', 'LiveStreamController@pinChatComment')->middleware($middleware);
+		Route::post('chat/unpin', 'LiveStreamController@unpinChatComment')->middleware($middleware);
 		Route::get('config', 'LiveStreamController@getConfig')->middleware($middleware);
 		Route::get('config', 'LiveStreamController@getConfig')->middleware($middleware);
 		Route::post('broadcast/publish', 'LiveStreamController@clientBroadcastPublish');
 		Route::post('broadcast/publish', 'LiveStreamController@clientBroadcastPublish');
 		Route::post('broadcast/finish', 'LiveStreamController@clientBroadcastFinish');
 		Route::post('broadcast/finish', 'LiveStreamController@clientBroadcastFinish');

+ 8 - 0
routes/channels.php

@@ -14,3 +14,11 @@
 Broadcast::channel('App.User.{id}', function ($user, $id) {
 Broadcast::channel('App.User.{id}', function ($user, $id) {
     return (int) $user->id === (int) $id;
     return (int) $user->id === (int) $id;
 });
 });
+
+Broadcast::channel('live.chat.{id}', function ($user, $id) {
+    return true;
+}, ['guards' => ['web', 'api']]);
+
+Broadcast::channel('live.presence.{id}', function ($user, $id) {
+    return [ $user->profile_id ];
+}, ['guards' => ['web', 'api']]);