mirror of
https://github.com/Bigherollc/wticreatorstudio.git
synced 2026-01-16 19:05:08 -05:00
Working livestream module
This commit is contained in:
1
Modules/Livestream/.gitignore
vendored
Normal file
1
Modules/Livestream/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/node_modules
|
||||
0
Modules/Livestream/Config/.gitkeep
Normal file
0
Modules/Livestream/Config/.gitkeep
Normal file
5
Modules/Livestream/Config/config.php
Normal file
5
Modules/Livestream/Config/config.php
Normal file
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'name' => 'Livestream'
|
||||
];
|
||||
0
Modules/Livestream/Console/.gitkeep
Normal file
0
Modules/Livestream/Console/.gitkeep
Normal file
0
Modules/Livestream/Database/Migrations/.gitkeep
Normal file
0
Modules/Livestream/Database/Migrations/.gitkeep
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class CreateRestreamTokensTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('restream_tokens', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('client_id');
|
||||
$table->string('access_token');
|
||||
$table->string('refresh_token');
|
||||
$table->json('scope');
|
||||
$table->string('type');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('restream_tokens');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class CreateRestreamSettingsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('restream_settings', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('client_id', 255);
|
||||
$table->string('client_secret', 255);
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('restream_settings');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class CreateLivestreamMenuAndPermission extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
$sql = [
|
||||
['id' => 240425100, 'module_id' => 18, 'parent_id' => 329, 'name' => 'Livestream Settings', 'route' => 'livestream.index', 'type' => 2 ],
|
||||
];
|
||||
DB::table('permissions')->insert($sql);
|
||||
|
||||
$sql = [
|
||||
['id' => 240425100, 'parent_id' => 133, 'is_admin' => 1,'is_seller' => 0, 'icon' =>null, 'name' => 'Livestream Settings', 'route' => 'livestream.index', 'position' => 1],//Submenu
|
||||
];
|
||||
DB::table('backendmenus')->insert($sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('');
|
||||
}
|
||||
}
|
||||
0
Modules/Livestream/Database/Seeders/.gitkeep
Normal file
0
Modules/Livestream/Database/Seeders/.gitkeep
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Livestream\Database\Seeders;
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class LivestreamDatabaseSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
Model::unguard();
|
||||
|
||||
// $this->call("OthersTableSeeder");
|
||||
}
|
||||
}
|
||||
0
Modules/Livestream/Database/factories/.gitkeep
Normal file
0
Modules/Livestream/Database/factories/.gitkeep
Normal file
0
Modules/Livestream/Entities/.gitkeep
Normal file
0
Modules/Livestream/Entities/.gitkeep
Normal file
16
Modules/Livestream/Entities/RestreamSetting.php
Normal file
16
Modules/Livestream/Entities/RestreamSetting.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Livestream\Entities;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
|
||||
class RestreamSetting extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'client_id',
|
||||
'client_secret',
|
||||
];
|
||||
}
|
||||
19
Modules/Livestream/Entities/RestreamToken.php
Normal file
19
Modules/Livestream/Entities/RestreamToken.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Livestream\Entities;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
|
||||
class RestreamToken extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'client_id',
|
||||
'access_token',
|
||||
'refresh_token',
|
||||
'scope',
|
||||
'type',
|
||||
];
|
||||
}
|
||||
14
Modules/Livestream/Facades/Restream.php
Normal file
14
Modules/Livestream/Facades/Restream.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Livestream\Facades;
|
||||
|
||||
use Illuminate\Support\Facades\Facade;
|
||||
use Modules\Livestream\Services\Restream as RestreamService;
|
||||
|
||||
class Restream extends Facade
|
||||
{
|
||||
protected static function getFacadeAccessor()
|
||||
{
|
||||
return RestreamService::class;
|
||||
}
|
||||
}
|
||||
0
Modules/Livestream/Http/Controllers/.gitkeep
Normal file
0
Modules/Livestream/Http/Controllers/.gitkeep
Normal file
74
Modules/Livestream/Http/Controllers/LivestreamController.php
Normal file
74
Modules/Livestream/Http/Controllers/LivestreamController.php
Normal file
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Livestream\Http\Controllers;
|
||||
|
||||
use GuzzleHttp\Client;
|
||||
use Illuminate\Contracts\Support\Renderable;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Routing\Controller;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Redirect;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
use Illuminate\Support\Str;
|
||||
use Modules\Livestream\Entities\RestreamSetting;
|
||||
use Modules\Livestream\Entities\RestreamToken;
|
||||
use Modules\Livestream\Facades\Restream;
|
||||
|
||||
class LivestreamController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$isConnected = Restream::isConnected();
|
||||
$settings = RestreamSetting::first();
|
||||
|
||||
$connection = session('restream-connect-result');
|
||||
|
||||
if (!empty($connection)) {
|
||||
$message = session('restream-connect-message');
|
||||
Session::forget(['restream-connect-result', 'restream-connect-message']);
|
||||
Session::save();
|
||||
|
||||
return view('livestream::index', compact('connection', 'message', 'settings', 'isConnected'));
|
||||
}
|
||||
|
||||
return view('livestream::index', compact('settings', 'isConnected'));
|
||||
}
|
||||
|
||||
public function platforms(Request $request)
|
||||
{
|
||||
$client = new Client();
|
||||
$response = $client->get('https://api.restream.io/v2/platform/all');
|
||||
$data = json_decode($response->getBody());
|
||||
Log::info($data);
|
||||
|
||||
return redirect()->route('livestream.index');
|
||||
}
|
||||
|
||||
public function servers(Request $request)
|
||||
{
|
||||
$client = new Client();
|
||||
$response = $client->get('https://api.restream.io/v2/server/all');
|
||||
$data = json_decode($response->getBody());
|
||||
Log::info($data);
|
||||
|
||||
return redirect()->route('livestream.index');
|
||||
}
|
||||
|
||||
public function channels(Request $request)
|
||||
{
|
||||
$token = RestreamToken::first();
|
||||
|
||||
if ($token) {
|
||||
$client = new Client();
|
||||
$response = $client->get('https://api.restream.io/v2/user/channel/all', [
|
||||
'headers' => [
|
||||
'Authorization' => "{$token->type} {$token->access_token}",
|
||||
],
|
||||
]);
|
||||
$data = json_decode($response->getBody());
|
||||
Log::info($data);
|
||||
}
|
||||
|
||||
return redirect()->route('livestream.index');
|
||||
}
|
||||
}
|
||||
57
Modules/Livestream/Http/Controllers/RestreamController.php
Normal file
57
Modules/Livestream/Http/Controllers/RestreamController.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Livestream\Http\Controllers;
|
||||
|
||||
use Illuminate\Contracts\Support\Renderable;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Routing\Controller;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Modules\Livestream\Facades\Restream;
|
||||
use Modules\Livestream\Http\Requests\UpdateRestreamSettingsRequest;
|
||||
use Modules\Livestream\Traits\ApiResponse;
|
||||
|
||||
class RestreamController extends Controller
|
||||
{
|
||||
use ApiResponse;
|
||||
|
||||
public function connect()
|
||||
{
|
||||
return Restream::connect();
|
||||
}
|
||||
|
||||
public function callback(Request $request)
|
||||
{
|
||||
Log::info($request);
|
||||
$message = 'Failed to connect Restream.';
|
||||
$stateToken = session('state-token');
|
||||
|
||||
if ($request->state === $stateToken) {
|
||||
$result = Restream::exchange($request->code);
|
||||
|
||||
Log::info('Restream exchange result:');
|
||||
Log::info($result);
|
||||
|
||||
if ($result) {
|
||||
$message = 'Restream has been successfully connected.';
|
||||
}
|
||||
} else {
|
||||
$message = 'Mismatch state token.';
|
||||
Log::info($message);
|
||||
$result = false;
|
||||
}
|
||||
|
||||
session([
|
||||
'restream-connect-result' => $result ? 'success' : 'failed',
|
||||
'restream-connect-message' => $message,
|
||||
]);
|
||||
|
||||
return redirect()->route('livestream.index');
|
||||
}
|
||||
|
||||
public function updateSettings(UpdateRestreamSettingsRequest $request)
|
||||
{
|
||||
Restream::updateSettings($request->safe()->client_id, $request->safe()->client_secret);
|
||||
|
||||
return $this->success('Restream settings has been updated.');
|
||||
}
|
||||
}
|
||||
0
Modules/Livestream/Http/Middleware/.gitkeep
Normal file
0
Modules/Livestream/Http/Middleware/.gitkeep
Normal file
0
Modules/Livestream/Http/Requests/.gitkeep
Normal file
0
Modules/Livestream/Http/Requests/.gitkeep
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Livestream\Http\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class UpdateRestreamSettingsRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'client_id' => ['required', 'string', 'max:255'],
|
||||
'client_secret' => ['required', 'string', 'max:255'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
0
Modules/Livestream/Providers/.gitkeep
Normal file
0
Modules/Livestream/Providers/.gitkeep
Normal file
112
Modules/Livestream/Providers/LivestreamServiceProvider.php
Normal file
112
Modules/Livestream/Providers/LivestreamServiceProvider.php
Normal file
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Livestream\Providers;
|
||||
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Illuminate\Database\Eloquent\Factory;
|
||||
|
||||
class LivestreamServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* @var string $moduleName
|
||||
*/
|
||||
protected $moduleName = 'Livestream';
|
||||
|
||||
/**
|
||||
* @var string $moduleNameLower
|
||||
*/
|
||||
protected $moduleNameLower = 'livestream';
|
||||
|
||||
/**
|
||||
* Boot the application events.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
$this->registerTranslations();
|
||||
$this->registerConfig();
|
||||
$this->registerViews();
|
||||
$this->loadMigrationsFrom(module_path($this->moduleName, 'Database/Migrations'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the service provider.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
$this->app->register(RouteServiceProvider::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register config.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function registerConfig()
|
||||
{
|
||||
$this->publishes([
|
||||
module_path($this->moduleName, 'Config/config.php') => config_path($this->moduleNameLower . '.php'),
|
||||
], 'config');
|
||||
$this->mergeConfigFrom(
|
||||
module_path($this->moduleName, 'Config/config.php'), $this->moduleNameLower
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register views.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function registerViews()
|
||||
{
|
||||
$viewPath = resource_path('views/modules/' . $this->moduleNameLower);
|
||||
|
||||
$sourcePath = module_path($this->moduleName, 'Resources/views');
|
||||
|
||||
$this->publishes([
|
||||
$sourcePath => $viewPath
|
||||
], ['views', $this->moduleNameLower . '-module-views']);
|
||||
|
||||
$this->loadViewsFrom(array_merge($this->getPublishableViewPaths(), [$sourcePath]), $this->moduleNameLower);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register translations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function registerTranslations()
|
||||
{
|
||||
$langPath = resource_path('lang/modules/' . $this->moduleNameLower);
|
||||
|
||||
if (is_dir($langPath)) {
|
||||
$this->loadTranslationsFrom($langPath, $this->moduleNameLower);
|
||||
} else {
|
||||
$this->loadTranslationsFrom(module_path($this->moduleName, 'Resources/lang'), $this->moduleNameLower);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the services provided by the provider.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function provides()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
private function getPublishableViewPaths(): array
|
||||
{
|
||||
$paths = [];
|
||||
foreach (\Config::get('view.paths') as $path) {
|
||||
if (is_dir($path . '/modules/' . $this->moduleNameLower)) {
|
||||
$paths[] = $path . '/modules/' . $this->moduleNameLower;
|
||||
}
|
||||
}
|
||||
return $paths;
|
||||
}
|
||||
}
|
||||
69
Modules/Livestream/Providers/RouteServiceProvider.php
Normal file
69
Modules/Livestream/Providers/RouteServiceProvider.php
Normal file
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Livestream\Providers;
|
||||
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
|
||||
|
||||
class RouteServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* The module namespace to assume when generating URLs to actions.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $moduleNamespace = 'Modules\Livestream\Http\Controllers';
|
||||
|
||||
/**
|
||||
* Called before routes are registered.
|
||||
*
|
||||
* Register any model bindings or pattern based filters.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
parent::boot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the routes for the application.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function map()
|
||||
{
|
||||
$this->mapApiRoutes();
|
||||
|
||||
$this->mapWebRoutes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the "web" routes for the application.
|
||||
*
|
||||
* These routes all receive session state, CSRF protection, etc.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function mapWebRoutes()
|
||||
{
|
||||
Route::middleware('web')
|
||||
->namespace($this->moduleNamespace)
|
||||
->group(module_path('Livestream', '/Routes/web.php'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the "api" routes for the application.
|
||||
*
|
||||
* These routes are typically stateless.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function mapApiRoutes()
|
||||
{
|
||||
Route::prefix('api')
|
||||
->middleware('api')
|
||||
->namespace($this->moduleNamespace)
|
||||
->group(module_path('Livestream', '/Routes/api.php'));
|
||||
}
|
||||
}
|
||||
191
Modules/Livestream/Public/css/base.css
Normal file
191
Modules/Livestream/Public/css/base.css
Normal file
@@ -0,0 +1,191 @@
|
||||
*, ::before, ::after {
|
||||
--tw-border-spacing-x: 0;
|
||||
--tw-border-spacing-y: 0;
|
||||
--tw-translate-x: 0;
|
||||
--tw-translate-y: 0;
|
||||
--tw-rotate: 0;
|
||||
--tw-skew-x: 0;
|
||||
--tw-skew-y: 0;
|
||||
--tw-scale-x: 1;
|
||||
--tw-scale-y: 1;
|
||||
--tw-pan-x: ;
|
||||
--tw-pan-y: ;
|
||||
--tw-pinch-zoom: ;
|
||||
--tw-scroll-snap-strictness: proximity;
|
||||
--tw-gradient-from-position: ;
|
||||
--tw-gradient-via-position: ;
|
||||
--tw-gradient-to-position: ;
|
||||
--tw-ordinal: ;
|
||||
--tw-slashed-zero: ;
|
||||
--tw-numeric-figure: ;
|
||||
--tw-numeric-spacing: ;
|
||||
--tw-numeric-fraction: ;
|
||||
--tw-ring-inset: ;
|
||||
--tw-ring-offset-width: 0px;
|
||||
--tw-ring-offset-color: #fff;
|
||||
--tw-ring-color: rgb(59 130 246 / 0.5);
|
||||
--tw-ring-offset-shadow: 0 0 #0000;
|
||||
--tw-ring-shadow: 0 0 #0000;
|
||||
--tw-shadow: 0 0 #0000;
|
||||
--tw-shadow-colored: 0 0 #0000;
|
||||
--tw-blur: ;
|
||||
--tw-brightness: ;
|
||||
--tw-contrast: ;
|
||||
--tw-grayscale: ;
|
||||
--tw-hue-rotate: ;
|
||||
--tw-invert: ;
|
||||
--tw-saturate: ;
|
||||
--tw-sepia: ;
|
||||
--tw-drop-shadow: ;
|
||||
--tw-backdrop-blur: ;
|
||||
--tw-backdrop-brightness: ;
|
||||
--tw-backdrop-contrast: ;
|
||||
--tw-backdrop-grayscale: ;
|
||||
--tw-backdrop-hue-rotate: ;
|
||||
--tw-backdrop-invert: ;
|
||||
--tw-backdrop-opacity: ;
|
||||
--tw-backdrop-saturate: ;
|
||||
--tw-backdrop-sepia: ;
|
||||
--tw-contain-size: ;
|
||||
--tw-contain-layout: ;
|
||||
--tw-contain-paint: ;
|
||||
--tw-contain-style:
|
||||
}
|
||||
::backdrop {
|
||||
--tw-border-spacing-x: 0;
|
||||
--tw-border-spacing-y: 0;
|
||||
--tw-translate-x: 0;
|
||||
--tw-translate-y: 0;
|
||||
--tw-rotate: 0;
|
||||
--tw-skew-x: 0;
|
||||
--tw-skew-y: 0;
|
||||
--tw-scale-x: 1;
|
||||
--tw-scale-y: 1;
|
||||
--tw-pan-x: ;
|
||||
--tw-pan-y: ;
|
||||
--tw-pinch-zoom: ;
|
||||
--tw-scroll-snap-strictness: proximity;
|
||||
--tw-gradient-from-position: ;
|
||||
--tw-gradient-via-position: ;
|
||||
--tw-gradient-to-position: ;
|
||||
--tw-ordinal: ;
|
||||
--tw-slashed-zero: ;
|
||||
--tw-numeric-figure: ;
|
||||
--tw-numeric-spacing: ;
|
||||
--tw-numeric-fraction: ;
|
||||
--tw-ring-inset: ;
|
||||
--tw-ring-offset-width: 0px;
|
||||
--tw-ring-offset-color: #fff;
|
||||
--tw-ring-color: rgb(59 130 246 / 0.5);
|
||||
--tw-ring-offset-shadow: 0 0 #0000;
|
||||
--tw-ring-shadow: 0 0 #0000;
|
||||
--tw-shadow: 0 0 #0000;
|
||||
--tw-shadow-colored: 0 0 #0000;
|
||||
--tw-blur: ;
|
||||
--tw-brightness: ;
|
||||
--tw-contrast: ;
|
||||
--tw-grayscale: ;
|
||||
--tw-hue-rotate: ;
|
||||
--tw-invert: ;
|
||||
--tw-saturate: ;
|
||||
--tw-sepia: ;
|
||||
--tw-drop-shadow: ;
|
||||
--tw-backdrop-blur: ;
|
||||
--tw-backdrop-brightness: ;
|
||||
--tw-backdrop-contrast: ;
|
||||
--tw-backdrop-grayscale: ;
|
||||
--tw-backdrop-hue-rotate: ;
|
||||
--tw-backdrop-invert: ;
|
||||
--tw-backdrop-opacity: ;
|
||||
--tw-backdrop-saturate: ;
|
||||
--tw-backdrop-sepia: ;
|
||||
--tw-contain-size: ;
|
||||
--tw-contain-layout: ;
|
||||
--tw-contain-paint: ;
|
||||
--tw-contain-style:
|
||||
}
|
||||
.m-0 {
|
||||
margin: 0px
|
||||
}
|
||||
.my-3 {
|
||||
margin-top: 0.75rem;
|
||||
margin-bottom: 0.75rem
|
||||
}
|
||||
.flex {
|
||||
display: flex
|
||||
}
|
||||
.w-1\/3 {
|
||||
width: 33.333333%
|
||||
}
|
||||
.w-1\/4 {
|
||||
width: 25%
|
||||
}
|
||||
.w-full {
|
||||
width: 100%
|
||||
}
|
||||
.flex-col {
|
||||
flex-direction: column
|
||||
}
|
||||
.items-center {
|
||||
align-items: center
|
||||
}
|
||||
.gap-1 {
|
||||
gap: 0.25rem
|
||||
}
|
||||
.gap-4 {
|
||||
gap: 1rem
|
||||
}
|
||||
.gap-8 {
|
||||
gap: 2rem
|
||||
}
|
||||
.gap-2 {
|
||||
gap: 0.5rem
|
||||
}
|
||||
.rounded {
|
||||
border-radius: 0.25rem
|
||||
}
|
||||
.bg-red-500 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(239 68 68 / var(--tw-bg-opacity))
|
||||
}
|
||||
.\!py-\[0\.5rem\] {
|
||||
padding-top: 0.5rem !important;
|
||||
padding-bottom: 0.5rem !important
|
||||
}
|
||||
.px-4 {
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem
|
||||
}
|
||||
.py-1 {
|
||||
padding-top: 0.25rem;
|
||||
padding-bottom: 0.25rem
|
||||
}
|
||||
.py-2 {
|
||||
padding-top: 0.5rem;
|
||||
padding-bottom: 0.5rem
|
||||
}
|
||||
.text-3xl {
|
||||
font-size: 1.875rem;
|
||||
line-height: 2.25rem
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: 700
|
||||
}
|
||||
.leading-none {
|
||||
line-height: 1
|
||||
}
|
||||
.text-white {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(255 255 255 / var(--tw-text-opacity))
|
||||
}
|
||||
.text-red-500 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(239 68 68 / var(--tw-text-opacity))
|
||||
}
|
||||
.text-green-500 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(34 197 94 / var(--tw-text-opacity))
|
||||
}
|
||||
.opacity-75 {
|
||||
opacity: 0.75
|
||||
}
|
||||
1
Modules/Livestream/Public/css/livestream.css
Normal file
1
Modules/Livestream/Public/css/livestream.css
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
1086
Modules/Livestream/Public/css/primeicons.css
Normal file
1086
Modules/Livestream/Public/css/primeicons.css
Normal file
File diff suppressed because it is too large
Load Diff
5731
Modules/Livestream/Public/css/primevue.css
Normal file
5731
Modules/Livestream/Public/css/primevue.css
Normal file
File diff suppressed because it is too large
Load Diff
BIN
Modules/Livestream/Public/fonts/Inter-italic.var.woff2
Normal file
BIN
Modules/Livestream/Public/fonts/Inter-italic.var.woff2
Normal file
Binary file not shown.
BIN
Modules/Livestream/Public/fonts/Inter-roman.var.woff2
Normal file
BIN
Modules/Livestream/Public/fonts/Inter-roman.var.woff2
Normal file
Binary file not shown.
BIN
Modules/Livestream/Public/fonts/vendor/primeicons/primeicons.eot
vendored
Normal file
BIN
Modules/Livestream/Public/fonts/vendor/primeicons/primeicons.eot
vendored
Normal file
Binary file not shown.
292
Modules/Livestream/Public/fonts/vendor/primeicons/primeicons.svg
vendored
Normal file
292
Modules/Livestream/Public/fonts/vendor/primeicons/primeicons.svg
vendored
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 285 KiB |
BIN
Modules/Livestream/Public/fonts/vendor/primeicons/primeicons.ttf
vendored
Normal file
BIN
Modules/Livestream/Public/fonts/vendor/primeicons/primeicons.ttf
vendored
Normal file
Binary file not shown.
BIN
Modules/Livestream/Public/fonts/vendor/primeicons/primeicons.woff
vendored
Normal file
BIN
Modules/Livestream/Public/fonts/vendor/primeicons/primeicons.woff
vendored
Normal file
Binary file not shown.
BIN
Modules/Livestream/Public/fonts/vendor/primeicons/primeicons.woff2
vendored
Normal file
BIN
Modules/Livestream/Public/fonts/vendor/primeicons/primeicons.woff2
vendored
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
33702
Modules/Livestream/Public/js/livestream.js
Normal file
33702
Modules/Livestream/Public/js/livestream.js
Normal file
File diff suppressed because one or more lines are too long
6
Modules/Livestream/Public/mix-manifest.json
Normal file
6
Modules/Livestream/Public/mix-manifest.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"/js/livestream.js": "/js/livestream.js",
|
||||
"/css/base.css": "/css/base.css",
|
||||
"/css/livestream.css": "/css/livestream.css",
|
||||
"/css/primevue.css": "/css/primevue.css"
|
||||
}
|
||||
0
Modules/Livestream/Resources/assets/.gitkeep
Normal file
0
Modules/Livestream/Resources/assets/.gitkeep
Normal file
0
Modules/Livestream/Resources/assets/js/app.js
Normal file
0
Modules/Livestream/Resources/assets/js/app.js
Normal file
0
Modules/Livestream/Resources/assets/sass/app.scss
Normal file
0
Modules/Livestream/Resources/assets/sass/app.scss
Normal file
Binary file not shown.
Binary file not shown.
5
Modules/Livestream/Resources/assets/sass/primevue.scss
Normal file
5
Modules/Livestream/Resources/assets/sass/primevue.scss
Normal file
@@ -0,0 +1,5 @@
|
||||
@use "sass:meta";
|
||||
|
||||
.frontend {
|
||||
@include meta.load-css('theme.css');
|
||||
}
|
||||
3
Modules/Livestream/Resources/assets/sass/tailwind.css
Normal file
3
Modules/Livestream/Resources/assets/sass/tailwind.css
Normal file
@@ -0,0 +1,3 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
5876
Modules/Livestream/Resources/assets/sass/theme.css
Normal file
5876
Modules/Livestream/Resources/assets/sass/theme.css
Normal file
File diff suppressed because it is too large
Load Diff
139
Modules/Livestream/Resources/client/App.vue
Normal file
139
Modules/Livestream/Resources/client/App.vue
Normal file
@@ -0,0 +1,139 @@
|
||||
<template>
|
||||
<div>
|
||||
<h2 class="text-3xl">Livestream</h2>
|
||||
<Group
|
||||
title="Restream"
|
||||
description="Manage Restream connection here."
|
||||
>
|
||||
<div class="flex gap-8 py-2">
|
||||
<div class="w-1/3">
|
||||
<div class="flex flex-col gap-4">
|
||||
<InputField label="Client Id">
|
||||
<InputText
|
||||
v-model="clientId"
|
||||
class="w-full"
|
||||
:pt="{
|
||||
root: 'py-2',
|
||||
}"
|
||||
/>
|
||||
</InputField>
|
||||
<InputField label="Client Secret">
|
||||
<InputText
|
||||
v-model="clientSecret"
|
||||
class="w-full"
|
||||
:pt="{
|
||||
root: 'py-2',
|
||||
}"
|
||||
/>
|
||||
</InputField>
|
||||
<div>
|
||||
<Button
|
||||
label="Save"
|
||||
size="small"
|
||||
:loading="processing"
|
||||
:pt="{
|
||||
root: '!py-[0.5rem]',
|
||||
}"
|
||||
@click="onSave"
|
||||
>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-1/4">
|
||||
<div class="flex flex-col gap-4">
|
||||
<InputField
|
||||
label="Status"
|
||||
>
|
||||
<div class="flex">
|
||||
<div v-if="isConnected" class="flex items-center gap-2 rounded font-bold text-green-500">
|
||||
<i class="pi pi-check-circle"></i>
|
||||
<span>Connected</span>
|
||||
</div>
|
||||
<div v-else class="flex items-center gap-1 rounded font-bold text-red-500">
|
||||
<span>Not connected</span>
|
||||
</div>
|
||||
</div>
|
||||
</InputField>
|
||||
<div>
|
||||
<Button
|
||||
label="Connect"
|
||||
size="small"
|
||||
:pt="{
|
||||
root: '!py-[0.5rem]',
|
||||
}"
|
||||
@click="onConnect"
|
||||
>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Group>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, ref } from "vue";
|
||||
import { toast } from "vue3-toastify";
|
||||
import { useRestream } from "@/api/useRestream";
|
||||
import Group from "@/components/Group";
|
||||
import InputField from "@/components/InputField";
|
||||
|
||||
const props = defineProps({
|
||||
clientId: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
clientSecret: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
isConnected: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
connection: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
message: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
});
|
||||
|
||||
const { updateRestreamSettings } = useRestream();
|
||||
const clientId = ref();
|
||||
const clientSecret = ref();
|
||||
const processing = ref(false);
|
||||
|
||||
onMounted(() => {
|
||||
if (!!props.connection) {
|
||||
if (props.connection === "success") {
|
||||
toast(props.message, {
|
||||
type: toast.TYPE.SUCCESS,
|
||||
position: toast.POSITION.TOP_RIGHT,
|
||||
});
|
||||
} else {
|
||||
toast(props.message, {
|
||||
type: toast.TYPE.ERROR,
|
||||
position: toast.POSITION.TOP_RIGHT,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
clientId.value = props.clientId;
|
||||
clientSecret.value = props.clientSecret;
|
||||
});
|
||||
|
||||
function onSave() {
|
||||
processing.value = true;
|
||||
updateRestreamSettings(clientId, clientSecret)
|
||||
.finally(() => processing.value = false);
|
||||
}
|
||||
|
||||
function onConnect() {
|
||||
window.location.href = route("restream.connect");
|
||||
}
|
||||
</script>
|
||||
17
Modules/Livestream/Resources/client/api/useData.js
Normal file
17
Modules/Livestream/Resources/client/api/useData.js
Normal file
@@ -0,0 +1,17 @@
|
||||
export function useData() {
|
||||
function getData(name, key) {
|
||||
let obj = window[name];
|
||||
|
||||
return key.split(".").reduce((o, k) => {
|
||||
if (o && typeof o === 'object' && k in o) {
|
||||
return o[k];
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}, obj);
|
||||
}
|
||||
|
||||
return {
|
||||
getData,
|
||||
};
|
||||
}
|
||||
42
Modules/Livestream/Resources/client/api/useRestream.js
Normal file
42
Modules/Livestream/Resources/client/api/useRestream.js
Normal file
@@ -0,0 +1,42 @@
|
||||
import { toValue } from "vue";
|
||||
import { toast } from "vue3-toastify";
|
||||
import { request } from "@/helpers/axios";
|
||||
|
||||
export function useRestream() {
|
||||
function updateRestreamSettings(clientId, clientSecret) {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
const response = await request().put(route("restream.update-settings"), {
|
||||
client_id: toValue(clientId),
|
||||
client_secret: toValue(clientSecret),
|
||||
});
|
||||
const { data } = response;
|
||||
const { message, item } = data;
|
||||
|
||||
toast(message, {
|
||||
type: toast.TYPE.SUCCESS,
|
||||
position: toast.POSITION.TOP_RIGHT,
|
||||
});
|
||||
resolve(item);
|
||||
} catch (error) {
|
||||
handleError(error);
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function handleError(error) {
|
||||
const { response } = error;
|
||||
const { data } = response;
|
||||
const { message } = data;
|
||||
|
||||
toast(message, {
|
||||
type: toast.TYPE.ERROR,
|
||||
position: toast.POSITION.TOP_RIGHT,
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
updateRestreamSettings,
|
||||
};
|
||||
}
|
||||
24
Modules/Livestream/Resources/client/components/Group.vue
Normal file
24
Modules/Livestream/Resources/client/components/Group.vue
Normal file
@@ -0,0 +1,24 @@
|
||||
<template>
|
||||
<div class="my-3">
|
||||
<GroupHeader
|
||||
:title="title"
|
||||
:description="description"
|
||||
/>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import GroupHeader from "@/components/GroupHeader";
|
||||
|
||||
const props = defineProps({
|
||||
title: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
description: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,20 @@
|
||||
<template>
|
||||
<div>
|
||||
<h3 class="m-0 leading-none">{{ title }}</h3>
|
||||
<small class="opacity-75">{{ description }}</small>
|
||||
</div>
|
||||
<hr class="m-0">
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
title: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
description: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,17 @@
|
||||
<template>
|
||||
<div class="flex flex-col gap-1">
|
||||
<div>{{ label }}</div>
|
||||
<div>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
label: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
5
Modules/Livestream/Resources/client/helpers/axios.js
Normal file
5
Modules/Livestream/Resources/client/helpers/axios.js
Normal file
@@ -0,0 +1,5 @@
|
||||
import axios from "axios";
|
||||
|
||||
export function request() {
|
||||
return axios.create();
|
||||
}
|
||||
37
Modules/Livestream/Resources/client/helpers/i18n.js
Normal file
37
Modules/Livestream/Resources/client/helpers/i18n.js
Normal file
@@ -0,0 +1,37 @@
|
||||
import { nextTick } from "vue";
|
||||
import { createI18n } from "vue-i18n";
|
||||
import { request } from "@/helpers/axios";
|
||||
|
||||
let instance = null;
|
||||
|
||||
export function setupI18n() {
|
||||
const i18n = createI18n({
|
||||
legacy: false,
|
||||
locale: 'en',
|
||||
});
|
||||
|
||||
instance = i18n;
|
||||
loadTranslations();
|
||||
|
||||
return i18n;
|
||||
}
|
||||
|
||||
export function loadTranslations(i18n = null) {
|
||||
if (window.i18n) {
|
||||
const { locale, messages } = window.i18n;
|
||||
|
||||
loadMessages(instance ?? i18n, locale, messages);
|
||||
}
|
||||
}
|
||||
|
||||
function loadMessages(i18n, locale, messages) {
|
||||
if (i18n) {
|
||||
if (i18n.mode == 'legacy') {
|
||||
i18n.global.locale = locale;
|
||||
} else {
|
||||
i18n.global.locale.value = locale;
|
||||
}
|
||||
|
||||
i18n.global.setLocaleMessage(locale, messages);
|
||||
}
|
||||
}
|
||||
25
Modules/Livestream/Resources/client/main.js
Normal file
25
Modules/Livestream/Resources/client/main.js
Normal file
@@ -0,0 +1,25 @@
|
||||
import { createApp } from "vue";
|
||||
import { createPinia } from "pinia";
|
||||
import PrimeVue from "primevue/config";
|
||||
import Button from "primevue/button";
|
||||
import InputText from "primevue/inputtext";
|
||||
import Vue3Toastify from "vue3-toastify";
|
||||
import App from "@/App";
|
||||
|
||||
import "primevue/resources/primevue.min.css";
|
||||
import "vue3-toastify/dist/index.css";
|
||||
|
||||
const app = createApp({
|
||||
components: {
|
||||
"app": App,
|
||||
},
|
||||
});
|
||||
|
||||
app.use(createPinia());
|
||||
app.use(PrimeVue, { ripple: true });
|
||||
app.use(Vue3Toastify, { autoClose: 3000 });
|
||||
|
||||
app.component("Button", Button);
|
||||
app.component("InputText", InputText);
|
||||
|
||||
app.mount("#livestream");
|
||||
0
Modules/Livestream/Resources/lang/.gitkeep
Normal file
0
Modules/Livestream/Resources/lang/.gitkeep
Normal file
0
Modules/Livestream/Resources/views/.gitkeep
Normal file
0
Modules/Livestream/Resources/views/.gitkeep
Normal file
34
Modules/Livestream/Resources/views/index.blade.php
Normal file
34
Modules/Livestream/Resources/views/index.blade.php
Normal file
@@ -0,0 +1,34 @@
|
||||
@extends('backEnd.master')
|
||||
|
||||
@section('styles')
|
||||
<link rel="stylesheet" href="{{ module_asset_url('Livestream', 'Public/css/base.css') }}">
|
||||
<link rel="stylesheet" href="{{ module_asset_url('Livestream', 'Public/css/primeicons.css') }}">
|
||||
<link rel="stylesheet" href="{{ module_asset_url('Livestream', 'Public/css/livestream.css') }}">
|
||||
@endsection
|
||||
|
||||
@section('mainContent')
|
||||
<link rel="stylesheet" href="{{ module_asset_url('Livestream', 'Public/css/primevue.css') }}">
|
||||
<div id="livestream" class="frontend">
|
||||
<app
|
||||
client-id="{{ $settings->client_id }}"
|
||||
client-secret="{{ $settings->client_secret }}"
|
||||
:is-connected="{{ $isConnected ? 'true' : 'false' }}"
|
||||
connection="{{ isset($connection) ? $connection : null }}"
|
||||
message="{{ isset($message) ? $message : null }}"
|
||||
>
|
||||
</app>
|
||||
</div>
|
||||
|
||||
@if (false)
|
||||
<h2>Livestream Module</h2>
|
||||
<a href="{{ route('livestream.restream.connect') }}">Connect</a>
|
||||
<a href="{{ route('livestream.platforms') }}">List Platforms</a>
|
||||
<a href="{{ route('livestream.servers') }}">Servers</a>
|
||||
<a href="{{ route('livestream.channels') }}">Channels</a>
|
||||
@endif
|
||||
@endsection
|
||||
|
||||
@push('scripts')
|
||||
@routes
|
||||
<script src="{{ module_asset_url('Livestream', 'Public/js/livestream.js') }}"></script>
|
||||
@endpush
|
||||
19
Modules/Livestream/Resources/views/layouts/master.blade.php
Normal file
19
Modules/Livestream/Resources/views/layouts/master.blade.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Module Livestream</title>
|
||||
|
||||
{{-- Laravel Mix - CSS File --}}
|
||||
{{-- <link rel="stylesheet" href="{{ mix('css/livestream.css') }}"> --}}
|
||||
|
||||
</head>
|
||||
<body>
|
||||
@yield('content')
|
||||
|
||||
{{-- Laravel Mix - JS File --}}
|
||||
{{-- <script src="{{ mix('js/livestream.js') }}"></script> --}}
|
||||
</body>
|
||||
</html>
|
||||
0
Modules/Livestream/Routes/.gitkeep
Normal file
0
Modules/Livestream/Routes/.gitkeep
Normal file
14
Modules/Livestream/Routes/api.php
Normal file
14
Modules/Livestream/Routes/api.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| API Routes
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here is where you can register API routes for your application. These
|
||||
| routes are loaded by the RouteServiceProvider within a group which
|
||||
| is assigned the "api" middleware group. Enjoy building your API!
|
||||
|
|
||||
*/
|
||||
32
Modules/Livestream/Routes/web.php
Normal file
32
Modules/Livestream/Routes/web.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------------
|
||||
* Web Routes
|
||||
*--------------------------------------------------------------------------
|
||||
*
|
||||
* Here is where you can register web routes for your application. These
|
||||
* routes are loaded by the RouteServiceProvider within a group which
|
||||
* contains the "web" middleware group. Now create something great!
|
||||
*
|
||||
*/
|
||||
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Modules\Livestream\Http\Controllers\LivestreamController;
|
||||
use Modules\Livestream\Http\Controllers\RestreamController;
|
||||
|
||||
Route::middleware('auth')->group(function () {
|
||||
Route::prefix('livestream')->group(function() {
|
||||
Route::get('/', [LivestreamController::class, 'index'])->name('livestream.index');
|
||||
Route::get('auth/callback', [LivestreamController::class, 'authCallback'])->name('livestream.auth.callback');
|
||||
Route::get('platforms', [LivestreamController::class, 'platforms'])->name('livestream.platforms');
|
||||
Route::get('servers', [LivestreamController::class, 'servers'])->name('livestream.servers');
|
||||
Route::get('channels', [LivestreamController::class, 'channels'])->name('livestream.channels');
|
||||
|
||||
Route::prefix('restream')->name('restream.')->group(function () {
|
||||
Route::get('connect', [RestreamController::class, 'connect'])->name('connect');
|
||||
Route::get('callback', [RestreamController::class, 'callback'])->name('callback');
|
||||
Route::put('update-settings', [RestreamController::class, 'updateSettings'])->name('update-settings');
|
||||
});
|
||||
});
|
||||
});
|
||||
92
Modules/Livestream/Services/Restream.php
Normal file
92
Modules/Livestream/Services/Restream.php
Normal file
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Livestream\Services;
|
||||
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Exception\ClientException;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Redirect;
|
||||
use Illuminate\Support\Str;
|
||||
use Modules\Livestream\Entities\RestreamSetting;
|
||||
use Modules\Livestream\Entities\RestreamToken;
|
||||
|
||||
class Restream
|
||||
{
|
||||
protected $base_url = 'https://api.restream.io';
|
||||
public $client_id;
|
||||
public $client_secret;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$settings = RestreamSetting::first();
|
||||
|
||||
if ($settings) {
|
||||
$this->client_id = $settings->client_id;
|
||||
$this->client_secret = $settings->client_secret;
|
||||
}
|
||||
}
|
||||
|
||||
public function connect()
|
||||
{
|
||||
$redirectUri = route('restream.callback');
|
||||
$stateToken = Str::random();
|
||||
session(['state-token' => $stateToken]);
|
||||
$url = "{$this->base_url}/login?response_type=code&client_id={$this->client_id}&redirect_uri={$redirectUri}&state={$stateToken}";
|
||||
|
||||
return Redirect::away($url);
|
||||
}
|
||||
|
||||
public function exchange($code)
|
||||
{
|
||||
try {
|
||||
$client = new Client();
|
||||
$response = $client->post("{$this->base_url}/oauth/token", [
|
||||
'form_params' => [
|
||||
'grant_type' => 'authorization_code',
|
||||
'redirect_uri' => route('restream.callback'),
|
||||
'code' => $code,
|
||||
'client_id' => $this->client_id,
|
||||
'client_secret' => $this->client_secret,
|
||||
],
|
||||
]);
|
||||
$status = $response->getStatusCode();
|
||||
$data = json_decode($response->getBody());
|
||||
|
||||
if ($status == 200) {
|
||||
$item = RestreamToken::updateOrCreate([ 'client_id' => $this->client_id ], [
|
||||
'access_token' => $data->access_token,
|
||||
'refresh_token' => $data->refresh_token,
|
||||
'scope' => json_encode($data->scope),
|
||||
'type' => $data->token_type,
|
||||
]);
|
||||
Log::info('Restream Connected');
|
||||
|
||||
return true;
|
||||
} else {
|
||||
Log::info("Status code: {$status}");
|
||||
Log::info("Message: {$data->message}");
|
||||
}
|
||||
} catch (ClientException $exception) {
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function isConnected()
|
||||
{
|
||||
$token = RestreamToken::first();
|
||||
|
||||
if ($token) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function updateSettings($clientId, $clientSecret)
|
||||
{
|
||||
RestreamSetting::truncate();
|
||||
RestreamSetting::updateOrCreate([ 'client_id' => $clientId ], [ 'client_secret' => $clientSecret ]);
|
||||
}
|
||||
}
|
||||
0
Modules/Livestream/Tests/Feature/.gitkeep
Normal file
0
Modules/Livestream/Tests/Feature/.gitkeep
Normal file
0
Modules/Livestream/Tests/Unit/.gitkeep
Normal file
0
Modules/Livestream/Tests/Unit/.gitkeep
Normal file
30
Modules/Livestream/Traits/ApiResponse.php
Normal file
30
Modules/Livestream/Traits/ApiResponse.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Livestream\Traits;
|
||||
|
||||
use Illuminate\Http\JsonResponse;
|
||||
|
||||
trait ApiResponse
|
||||
{
|
||||
public function success($message, $item = null): JsonResponse
|
||||
{
|
||||
$response = [
|
||||
'message' => $message,
|
||||
];
|
||||
|
||||
if (is_array($item)) {
|
||||
$response = array_merge($response, $item);
|
||||
} else {
|
||||
$response['item'] = $item;
|
||||
}
|
||||
|
||||
return response()->json($response);
|
||||
}
|
||||
|
||||
public function failed($message, $status = 400): JsonResponse
|
||||
{
|
||||
return response()->json([
|
||||
'message' => $message,
|
||||
], $status);
|
||||
}
|
||||
}
|
||||
23
Modules/Livestream/composer.json
Normal file
23
Modules/Livestream/composer.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"name": "nwidart/livestream",
|
||||
"description": "",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nicolas Widart",
|
||||
"email": "n.widart@gmail.com"
|
||||
}
|
||||
],
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"providers": [],
|
||||
"aliases": {
|
||||
|
||||
}
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Modules\\Livestream\\": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
13
Modules/Livestream/module.json
Normal file
13
Modules/Livestream/module.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "Livestream",
|
||||
"alias": "livestream",
|
||||
"description": "",
|
||||
"keywords": [],
|
||||
"priority": 0,
|
||||
"providers": [
|
||||
"Modules\\Livestream\\Providers\\LivestreamServiceProvider"
|
||||
],
|
||||
"aliases": {},
|
||||
"files": [],
|
||||
"requires": []
|
||||
}
|
||||
10766
Modules/Livestream/package-lock.json
generated
Normal file
10766
Modules/Livestream/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
36
Modules/Livestream/package.json
Normal file
36
Modules/Livestream/package.json
Normal file
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "npm run development",
|
||||
"development": "mix",
|
||||
"watch": "mix watch",
|
||||
"watch-poll": "mix watch -- --watch-options-poll=1000",
|
||||
"hot": "mix watch --hot",
|
||||
"prod": "npm run production",
|
||||
"production": "mix --production"
|
||||
},
|
||||
"devDependencies": {
|
||||
"autoprefixer": "^10.4.16",
|
||||
"axios": "^1.6.1",
|
||||
"cross-env": "^7.0",
|
||||
"dotenv": "^10.0.0",
|
||||
"dotenv-expand": "^5.1.0",
|
||||
"laravel-mix": "^6.0.31",
|
||||
"laravel-mix-merge-manifest": "^2.1.0",
|
||||
"laravel-vite-plugin": "^0.8.0",
|
||||
"lodash": "^4.17.21",
|
||||
"postcss": "^8.4.32",
|
||||
"resolve-url-loader": "^5.0.0",
|
||||
"sass": "^1.69.5",
|
||||
"sass-loader": "^12.1.0",
|
||||
"tailwindcss": "^3.3.5",
|
||||
"vue-loader": "^17.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"pinia": "^2.1.7",
|
||||
"primeicons": "^6.0.1",
|
||||
"primevue": "^3.42.0",
|
||||
"vue": "^3.3.9",
|
||||
"vue3-toastify": "^0.2.1"
|
||||
}
|
||||
}
|
||||
6
Modules/Livestream/postcss.config.js
Normal file
6
Modules/Livestream/postcss.config.js
Normal file
@@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
16
Modules/Livestream/tailwind.config.js
Normal file
16
Modules/Livestream/tailwind.config.js
Normal file
@@ -0,0 +1,16 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: [
|
||||
"./Resources/**/*.blade.php",
|
||||
"./Resources/**/*.js",
|
||||
"./Resources/**/*.vue",
|
||||
],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
corePlugins: {
|
||||
preflight: false,
|
||||
},
|
||||
}
|
||||
|
||||
27
Modules/Livestream/webpack.mix.js
Normal file
27
Modules/Livestream/webpack.mix.js
Normal file
@@ -0,0 +1,27 @@
|
||||
const dotenvExpand = require('dotenv-expand');
|
||||
dotenvExpand(require('dotenv').config({ path: '../../.env'/*, debug: true*/}));
|
||||
|
||||
const mix = require('laravel-mix');
|
||||
require('laravel-mix-merge-manifest');
|
||||
|
||||
mix.setPublicPath('./Public').mergeManifest();
|
||||
mix.webpackConfig({
|
||||
resolve: {
|
||||
extensions: ['*', '.js', '.vue', '.json'],
|
||||
}
|
||||
});
|
||||
|
||||
mix.alias({
|
||||
'@': __dirname + '/Resources/client',
|
||||
'~': __dirname,
|
||||
})
|
||||
|
||||
mix.js(__dirname + '/Resources/client/main.js', 'js/livestream.js')
|
||||
.vue()
|
||||
.sass(__dirname + '/Resources/assets/sass/primevue.scss', 'css/primevue.css')
|
||||
.sass(__dirname + '/Resources/assets/sass/app.scss', 'css/livestream.css')
|
||||
.postCss(__dirname + '/Resources/assets/sass/tailwind.css', 'css/base.css', [require('tailwindcss')]);
|
||||
|
||||
if (mix.inProduction()) {
|
||||
mix.version();
|
||||
}
|
||||
@@ -75,5 +75,6 @@
|
||||
"Theme": true,
|
||||
"Frontend": true,
|
||||
"Staff": true,
|
||||
"LaraBuilder": true
|
||||
"LaraBuilder": true,
|
||||
"Livestream": true
|
||||
}
|
||||
Reference in New Issue
Block a user