{"id":334717,"date":"2022-06-19T21:00:18","date_gmt":"2022-06-19T21:00:18","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=334717"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=334717","title":{"rendered":"<span>\u0412\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u0432\u0438\u0434\u0435\u043e\u0442\u0440\u0430\u043d\u0441\u043b\u044f\u0446\u0438\u0439 \u043d\u0430 Laravel<\/span>"},"content":{"rendered":"<div><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u041f\u0440\u0438\u0432\u0435\u0442, \u0425\u0430\u0431\u0440. \u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u044f \u0445\u043e\u0447\u0443 \u043f\u043e\u0434\u0435\u043b\u0438\u0442\u044c\u0441\u044f \u0441\u0432\u043e\u0438\u043c \u043e\u043f\u044b\u0442\u043e\u043c \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043d\u0430 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0435 Laravel \u043f\u043e \u0442\u0440\u0430\u043d\u0441\u043b\u044f\u0446\u0438\u0438 \u0432\u0438\u0434\u0435\u043e\u043a\u043e\u043d\u0442\u0435\u043d\u0442\u0430. \u0418\u0442\u0430\u043a \u043d\u0430\u0447\u043d\u0451\u043c.<\/p>\n<p><a href=\"https:\/\/github.com\/deyen01\/dlvp\" rel=\"noopener noreferrer nofollow\"><em>\u041f\u0440\u043e\u0435\u043a\u0442 \u043e\u043f\u0443\u0431\u043b\u0438\u043a\u043e\u0432\u0430\u043d \u043a\u0430\u043a \u0441\u0432\u043e\u0431\u043e\u0434\u043d\u043e\u0435 \u041f\u041e<\/em><\/a><\/p>\n<h2>\u0417\u0430\u0434\u0430\u0447\u0430<\/h2>\n<p>\u0421\u0434\u0435\u043b\u0430\u0442\u044c \u0441\u0435\u0440\u0432\u0438\u0441, \u0441\u043e\u0432\u043c\u0435\u0441\u0442\u0438\u043c\u044b\u0439 \u0441 \u0431\u0438\u0437\u043d\u0435\u0441-\u043c\u043e\u0434\u0435\u043b\u044c\u044e SaaS , \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u044e\u0449\u0438\u0439 \u0434\u0430\u043d\u043d\u044b\u0435 \u043f\u043e \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u0443 RTMP \u043e\u0442 \u0440\u0430\u0437\u043d\u044b\u0445 \u043f\u043e\u0441\u0442\u0430\u0432\u0449\u0438\u043a\u043e\u0432 \u043a\u043e\u043d\u0442\u0435\u043d\u0442\u0430 \u0438 \u0440\u0430\u0437\u0434\u0430\u044e\u0449\u0438\u0439 \u044d\u0442\u043e\u0442 \u043a\u043e\u043d\u0442\u0435\u043d\u0442 \u043f\u043e HLS \u043a\u043e\u043d\u0435\u0447\u043d\u044b\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c \u0437\u0430 \u043f\u043b\u0430\u0442\u0443 \u0438\u043b\u0438 \u0431\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u043e, \u0442.\u0435. \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c Live-\u0442\u0440\u0430\u043d\u0441\u043b\u044f\u0446\u0438\u0438.<\/p>\n<h2>\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b<\/h2>\n<p>\u0411\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0441\u0432\u043e\u0431\u043e\u0434\u043d\u043e\u0435 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u043d\u043e\u0435 \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0435\u043d\u0438\u0435. \u0414\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 RTMP \u0438 HLS \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c nginx \u0441 <a href=\"https:\/\/github.com\/arut\/nginx-rtmp-module\" rel=\"noopener noreferrer nofollow\">nginx-rtmp-module<\/a>. \u0414\u043b\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c apache2, php, \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445 MariaDB. \u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0430 Laravel \u0441 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430\u043c\u0438 LiveWire \u0434\u043b\u044f \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u0434\u043b\u044f \u043f\u043e\u0441\u0442\u0440\u043e\u0435\u043d\u0438\u044f html \u0441\u0442\u0440\u0430\u043d\u0438\u0446 \u0448\u0430\u0431\u043b\u043e\u043d\u044b Blade. \u0414\u043b\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0437\u0430\u043f\u0438\u0441\u0430\u043d\u043d\u044b\u0445 \u0442\u0440\u0430\u043d\u0441\u043b\u044f\u0446\u0438\u0439 \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <a href=\"https:\/\/ffmpeg.org\/\" rel=\"noopener noreferrer nofollow\">FFMPEG<\/a>. \u0412\u0441\u0451 \u044d\u0442\u043e \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435 Ubuntu 20.04 LTS.<\/p>\n<h2>\u041f\u0440\u0438\u0441\u0442\u0443\u043f\u0430\u0435\u043c<\/h2>\n<p>\u0421\u043e\u0437\u0434\u0430\u043b\u0438 \u043f\u0440\u043e\u0435\u043a\u0442 Laravel 8. \u0421\u043e\u0437\u0434\u0430\u0451\u043c \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u0438 \u0434\u043b\u044f \u0431\u0430\u0437 \u0434\u0430\u043d\u043d\u044b\u0445. \u0423 \u043d\u0430\u0441 \u0431\u0443\u0434\u0443\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438, \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0446\u0438\u0438 (\u043f\u043e\u0441\u0442\u0430\u0432\u0449\u0438\u043a\u0438 \u043a\u043e\u043d\u0442\u0435\u043d\u0442\u0430), \u043f\u043e\u0441\u0442\u044b (\u0442\u043e \u0435\u0441\u0442\u044c \u0437\u0430\u043f\u0438\u0441\u0438 \u043e \u0431\u0443\u0434\u0443\u0449\u0438\u0445, \u0442\u0435\u043a\u0443\u0449\u0438\u0445 \u0438 \u043f\u0440\u043e\u0448\u0435\u0434\u0448\u0438\u0445 \u0442\u0440\u0430\u043d\u0441\u043b\u044f\u0446\u0438\u044f\u0445) \u0438 \u0442\u0430\u043a \u0434\u0430\u043b\u0435\u0435.<\/p>\n<p>\u0422\u0430\u0431\u043b\u0438\u0446\u0430 Users:<\/p>\n<pre><code class=\"php\">&lt;?php  use Illuminate\\Database\\Migrations\\Migration; use Illuminate\\Database\\Schema\\Blueprint; use Illuminate\\Support\\Facades\\Schema;  class CreateUsersTable extends Migration {     \/**      * Run the migrations.      *      * @return void      *\/     public function up()     {         Schema::create('users', function (Blueprint $table) {             $table->id();             $table->string('name');             $table->string('email')->unique();             $table->unsignedTinyInteger('access_level')->default(0); \/\/ 0 - user, 1 - editor, 2 - finmanager, 3 - admin, 4 - root(global admin)             $table->timestamp('email_verified_at')->nullable();             $table->string('password')->nullable();             $table->rememberToken();             $table->string('google_id')->nullable();             $table->string('google_token')->nullable();             $table->string('google_refresh_token')->nullable();             $table->string('instagram_id')->nullable();             $table->string('instagram_token')->nullable();             $table->string('instagram_refresh_token')->nullable();             $table->string('yandex_id')->nullable();             $table->string('yandex_token')->nullable();             $table->string('yandex_refresh_token')->nullable();             $table->string('vk_id')->nullable();             $table->string('vk_token')->nullable();             $table->string('vk_refresh_token')->nullable();             $table->foreignId('org_id')                 ->nullable()                 ->constrained()                 ->onUpdate('cascade')                 ->onDelete('restrict');             $table->timestamps();         });     }      \/**      * Reverse the migrations.      *      * @return void      *\/     public function down()     {         Schema::dropIfExists('users');     } }<\/code><\/pre>\n<p>\u0422\u0430\u0431\u043b\u0438\u0446\u0430 Orgs:<\/p>\n<pre><code class=\"php\">&lt;?php  use Illuminate\\Database\\Migrations\\Migration; use Illuminate\\Database\\Schema\\Blueprint; use Illuminate\\Support\\Facades\\Schema;  class CreateOrgsTable extends Migration {     \/**      * Run the migrations.      *      * @return void      *\/     public function up()     {         Schema::create('orgs', function (Blueprint $table) {             $table->id();             $table->string('fulltitle', 512)->nullable();             $table->string('title', 128);             $table->string('brandtitle', 128);             $table->string('ogrn', 15);             $table->string('inn', 12);             $table->string('kpp', 9)->nullable();             $table->string('address', 255);             $table->string('drawer_status', 2)->nullable();             $table->string('fintitle', 255);             $table->string('personal_acc', 20);             $table->string('bank_name', 128);             $table->string('bic', 9);             $table->string('corresp_acc', 20);             $table->string('kbk', 20)->nullable();             $table->string('titlekbk', 128)->nullable();             $table->string('oktmo', 11)->nullable();             $table->string('purpose', 255)->nullable();             $table->string('email', 255);             $table->string('tel', 10);;             $table->timestamps();         });     }      \/**      * Reverse the migrations.      *      * @return void      *\/     public function down()     {         Schema::dropIfExists('orgs');     } }<\/code><\/pre>\n<p>\u0422\u0430\u0431\u043b\u0438\u0446\u0430 \u043f\u043e\u0441\u0442\u043e\u0432:<\/p>\n<pre><code class=\"php\">&lt;?php ... Schema::create('posts', function (Blueprint $table) {             $table->id();             $table->foreignId('org_id')                 ->nullable()                 ->constrained()                 ->cascadeOnUpdate()                 ->nullOnDelete();             $table->boolean('record')->default(FALSE);             $table->boolean('autorecord')->default(FALSE);             $table->boolean('file_preparation')->default(FALSE);             $table->boolean('rtmp_status')->default(FALSE);             $table->ipAddress('rtmp_ip_sender')->nullable();             $table->boolean('allow_comment')->default(FALSE);             $table->string('title1', 64);             $table->string('title2', 64)->nullable();             $table->string('body', 2048)->nullable();             $table->uuid('stream_name')->unique();             $table->string('stream_token', 32);             $table->dateTime('dt_begin');             $table->dateTime('dt_end');             $table->unsignedDecimal('price', 14, 2)->nullable();             $table->unsignedBigInteger('timeleft')->nullable();             $table->unsignedBigInteger('timepass')->nullable();             $table->char('color', 4)->charset('binary')->nullable();             $table->foreignId('picture_id')->nullable()->constrained('mediafiles')->cascadeOnUpdate()->nullOnDelete();             $table->foreignId('videopreview_id')->nullable()->constrained('mediafiles')->cascadeOnUpdate()->nullOnDelete();             $table->foreignId('video_id')->nullable()->constrained('mediafiles')->cascadeOnUpdate()->nullOnDelete();             $table->foreignId('user_id') \/\/author                 ->nullable()                 ->constrained()                 ->cascadeOnUpdate()                 ->nullOnDelete();             $table->unsignedBigInteger('cv_before')->default(0);             $table->unsignedBigInteger('cv_live')->default(0);             $table->unsignedBigInteger('cv_after')->default(0);             $table->timestamps();         });<\/code><\/pre>\n<h2>\u041d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u043c \u043c\u043e\u0434\u0435\u043b\u0438 Eloquent<\/h2>\n<p>\u041c\u043e\u0434\u0435\u043b\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f:<\/p>\n<pre><code class=\"php\">&lt;?php  namespace App\\Models;  use Illuminate\\Contracts\\Auth\\MustVerifyEmail; use Illuminate\\Database\\Eloquent\\Factories\\HasFactory; use Illuminate\\Foundation\\Auth\\User as Authenticatable; use Illuminate\\Notifications\\Notifiable; use Laravel\\Sanctum\\HasApiTokens; use Illuminate\\Support\\Facades\\Auth;  class User extends Authenticatable {     use HasApiTokens, HasFactory, Notifiable;      const AAL = [         0 => '\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c',         1 => '\u0420\u0435\u0434\u0430\u043a\u0442\u043e\u0440',         2 => '\u0424\u0438\u043d\u0430\u043d\u0441\u043e\u0432\u044b\u0439 \u043c\u0435\u043d\u0435\u0434\u0436\u0435\u0440',         3 => '\u0410\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440',         4 => 'root'     ];      \/**      * The attributes that are mass assignable.      *      * @var array&lt;int, string>      *\/     protected $fillable = [         'name',         'email',         'password',         'google_id',         'google_token',         'google_refresh_token',         'instagram_id',         'instagram_token',         'instagram_refresh_token',         'vk_id',         'vk_token',         'vk_refresh_token',         'yandex_id',         'yandex_token',         'yandex_refresh_token',     ];      protected $attributes = ['access_level' => 0];      \/**      * The attributes that should be hidden for serialization.      *      * @var array&lt;int, string>      *\/     protected $hidden = [         'password',         'remember_token',         'google_id',         'google_token',         'google_refresh_token',         'instagram_id',         'instagram_token',         'instagram_refresh_token',         'vk_id',         'vk_token',         'vk_refresh_token',         'yandex_id',         'yandex_token',         'yandex_refresh_token',     ];      \/**      * The attributes that should be cast.      *      * @var array&lt;string, string>      *\/     protected $casts = [         'email_verified_at' => 'datetime',     ];      public function getALAttribute()     {         return self::AAL[$this->access_level];     }      public function org() {         return $this->belongsTo(Org::class);     }      public function scopeLimitAL($query){         $ac = Auth::user()->access_level;         if ($ac == 0) {             return $query->where('id', Auth::id());         } else {             return $query->where('access_level', '&lt;=', $ac);         }     } }<\/code><\/pre>\n<p>\u041c\u043e\u0434\u0435\u043b\u044c \u043f\u043e\u0441\u0442\u0430:<\/p>\n<pre><code class=\"php\">&lt;?php  namespace App\\Models;  use Illuminate\\Database\\Eloquent\\Factories\\HasFactory; use Illuminate\\Database\\Eloquent\\Model; use App\\Models\\Mediafile; use App\\Models\\User; use App\\Models\\Org; use App\\Models\\Ticket; use Illuminate\\Support\\Facades\\Auth; use Illuminate\\Support\\Str;  class Post extends Model {     use HasFactory;      public function picture() {         return $this->belongsTo(Mediafile::class);     }      public function video() {         return $this->belongsTo(Mediafile::class);     }      public function videopriview() {         return $this->belongsTo(Mediafile::class);     }      public function getStreamStringAttribute() {         return \"{$this->stream_name}\/{$this->stream_token}\";     }      public function getCvAttribute() {         return $this->cv_before + $this->cv_live + $this->cv_after;     }      public function tickets()     {         return $this->hasMany(Ticket::class);     }      public function user() {         return $this->belongsTo(User::class);     }      public function org() {         return $this->belongsTo(Org::class);     }      protected static function booted()     {         static::creating(function (Post $post) {             $post->user_id = Auth::id();             $post->org_id = Auth::user()->org_id;             $post->stream_name = Str::uuid();             $post->stream_token = Str::random(32);         });     } }<\/code><\/pre>\n<h2>\u041f\u0438\u0448\u0435\u043c \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u044b<\/h2>\n<p>\u041f\u043e \u0441\u0443\u0442\u0438 \u0432\u0441\u044f \u043b\u043e\u0433\u0438\u043a\u0430 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043f\u0438\u0448\u0435\u0442\u0441\u044f \u0432 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u0430\u0445. \u0421 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c\u0438 \u0438 \u0434\u0440\u0443\u0433\u0438\u043c\u0438 \u043c\u043e\u0434\u0435\u043b\u044f\u043c\u0438 \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0432\u0441\u0451 \u0442\u0440\u0438\u0432\u0438\u0430\u043b\u044c\u043d\u043e. \u0420\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440 \u043f\u043e\u0441\u0442\u043e\u0432 \u0438 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440 \u0432\u0437\u0430\u0438\u043c\u043e\u0440\u0430\u0441\u0447\u0435\u0442\u043e\u0432 \u043c\u0435\u0436\u0434\u0443 \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0446\u0438\u044f\u043c\u0438 \u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c\u0438.<\/p>\n<p>\u041a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440 \u043f\u043e\u0441\u0442\u043e\u0432:<\/p>\n<pre><code class=\"php\">&lt;?php  namespace App\\Http\\Controllers;  use Illuminate\\Http\\Request; use Illuminate\\Support\\Facades\\Http; use Illuminate\\Support\\Facades\\Auth; use App\\Models\\Mediafile; use App\\Models\\Post; use App\\Jobs\\StartRec; use App\\Jobs\\StopRec; use App\\Jobs\\StopRTMP;  class PostController extends Controller {      public function show($id = 0)     {         if ($id > 0) {             return view('post', ['edit' => 0, 'posts' => [Post::findOrFail($id)]]);         } else {             return view('post', ['edit' => 0, 'posts' => Post::orderByDesc('id')->paginate(10)]);         }     }      public function index()     {         $lp = Post::select('id')->orderByDesc('id')->take(1)->get();         if (isset($lp[0])) { $lpid = $lp[0]['id']; } else { $lpid = 0; }         return view('home', [             'posts' => Post::orderByDesc('id')->paginate(32),             'postsfuture' => Post::where('dt_begin', '>', now())->orderByDesc('id')->paginate(32),             'postspast' => Post::where('dt_end', '&lt;', now())->orderByDesc('id')->paginate(32),             'postsnow' => Post::where('dt_end', '>', now())->where('dt_begin', '&lt;', now())->orderByDesc('id')->paginate(32),             'lpid' => $lpid         ]);     }      public function edit($id = 0)     {         if (($id > 0) &amp;&amp; (in_array(Auth::user()->access_level, [1, 3, 4]))) {             return view('post', ['edit' => 1, 'post' => Post::findOrFail($id)]);         } elseif (in_array(Auth::user()->access_level, [1, 3, 4])) {             return view('post', ['edit' => 2]);         }     }      public function rtmp_on(Request $request) {         $ar = [             'stream_name' => $request->input('name'),             'stream_token' => $request->input('token')         ];         $post = Post::where($ar)->firstOr(function () { return false; });         if ($post) {             $post->rtmp_status = true;             $post->rtmp_ip_sender = $request->input('addr');             $post->save();             if (($post->autorecord == true) &amp;&amp; ($post->record == false)) {                 StartRec::dispatch($post);             }             return response()->noContent(); \/\/ allow         } else {             return response(null, 403); \/\/ forbidden         }     }      public function rtmp_off(Request $request) {         $ar = [             'stream_name' => $request->input('name'),             'rtmp_ip_sender' => $request->input('addr')         ];         $post = Post::where($ar)->firstOr(function () { return false; });         if ($post) {             $post->rtmp_status = false;             $post->save();             if ($post->record == true) {                 StopRec::dispatch($post);             }         }         return response()->noContent();     }      public function rtmp_update(Request $request) {         $ar = [             'stream_name' => $request->input('name'),             'stream_token' => $request->input('token')         ];         $post = Post::where($ar)->firstOr(function () { return false; });         if ($post) {             $post->rtmp_status = true;             $post->rtmp_ip_sender = $request->input('addr');             $post->timepass = $request->input('time');             $post->save();             return response()->noContent(); \/\/ allow         } else {             return response(null, 403); \/\/ forbidden         }     } }<\/code><\/pre>\n<p>\u042d\u0442\u043e\u0442 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440 \u0443 \u043d\u0430\u0441 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0443\u0435\u0442 \u0438 \u043a\u043e\u043d\u0435\u0447\u043d\u044b\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u043c \u0438 \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u043c nginx. \u041c\u0430\u0440\u0448\u0440\u0443\u0442\u044b \u043a \u044d\u0442\u043e\u043c\u0443 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u0443 \u0434\u043b\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043c\u044b \u043d\u0430\u043f\u0438\u0448\u0435\u043c \u0432 web.php:<\/p>\n<pre><code class=\"php\">&lt;?php ... Route::prefix('posts')->middleware('auth')->group(function () {     Route::get('\/{id?}', [PostController::class, 'show'])->where('id', '[0-9]+')->name('posts');     Route::get('\/{id}\/edit', [PostController::class, 'edit'])->where('id', '[0-9]+')->name('editpost');     Route::get('\/add', [PostController::class, 'edit'])->name('addpost'); });<\/code><\/pre>\n<p>\u0410 \u0434\u043b\u044f \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0432 \u0444\u0430\u0439\u043b\u0435 api.php:<\/p>\n<pre><code class=\"php\">&lt;?php ...   Route::post('stream\/on_publish', [PostController::class, 'rtmp_on'])->name('rtmp_on'); Route::post('stream\/on_publish_done', [PostController::class, 'rtmp_off'])->name('rtmp_off'); Route::post('stream\/on_update', [PostController::class, 'rtmp_update'])->name('rtmp_update');<\/code><\/pre>\n<p>\u041b\u043e\u0433\u0438\u043a\u0430 \u0442\u0430\u043a\u0430\u044f: \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0441\u043e\u0437\u0434\u0430\u0451\u0442 \u043f\u043e\u0441\u0442: \u043f\u0438\u0448\u0435\u0442 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435, \u0434\u0430\u0442\u0430 \u0438 \u0432\u0440\u0435\u043c\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u0438 \u043a\u043e\u043d\u0446\u0430, \u043f\u0440\u0438\u043a\u043b\u0430\u0434\u044b\u0432\u0430\u0435\u0442 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0443, \u043f\u0440\u0438 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435 \u043c\u043e\u0434\u0435\u043b\u044c \u0441\u043e\u0437\u0434\u0430\u0451\u0442 \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0435 <code>stream_name<\/code> \u0438 <code>stream_token<\/code>. <code>stream_name<\/code> \u0432\u0438\u0434\u044f\u0442 \u0432\u0441\u0435, \u0430 <code>stream_token<\/code> \u0442\u043e\u043b\u044c\u043a\u043e \u0430\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440\u044b \u0438 \u0430\u0432\u0442\u043e\u0440 \u043f\u043e\u0441\u0442\u0430. \u0417\u0430\u043f\u0438\u0441\u044c \u0432\u043d\u0435\u0441\u0435\u043d\u0430 \u0432 \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445. \u0417\u0430\u0442\u0435\u043c \u0430\u0432\u0442\u043e\u0440 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u0442\u0440\u0430\u043d\u0441\u043b\u044f\u0446\u0438\u0438 \u043a\u043e\u043d\u0442\u0435\u043d\u0442\u0430 \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 <a href=\"https:\/\/obsproject.com\/\" rel=\"noopener noreferrer nofollow\">OBS<\/a>. \u0423\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 rtmp \u0430\u0434\u0440\u0435\u0441 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442.<\/p>\n<p>\u0414\u0430\u043d\u043d\u044b\u0435 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u0441\u0435\u0440\u0432\u0435\u0440 nginx \u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0437\u0430\u043f\u0440\u043e\u0441 \u043d\u0430 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435, \u043a\u0430\u043a \u044d\u0442\u043e \u0443\u043a\u0430\u0437\u0430\u043d\u043e \u0432 \u0435\u0433\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u0445:<\/p>\n<pre><code class=\"nginx\">rtmp {     server {         listen 1935; # Listen on standard RTMP port         chunk_size 8192;         max_streams 32;          application show {             on_publish \"http:\/\/live.example.org:80\/api\/stream\/on_publish\";             live on;             recorder rec1 {                 record all manual;                 record_suffix _rec.flv;                 record_path \/var\/www\/live.example.org\/storage\/app\/public\/rec;                 record_unique on;             }             hls on;             hls_path \/var\/www\/live.example.org-hls\/public_html\/hls;             hls_fragment 5;             hls_cleanup on;             hls_playlist_length 30;             hls_nested on;             deny play all;             on_publish_done \"http:\/\/live.example.org:80\/api\/stream\/on_publish_done\";             notify_update_timeout 2s;             on_update \"http:\/\/live.example.org:80\/api\/stream\/on_update\";         }     } }<\/code><\/pre>\n<p>\u0422\u043e \u0435\u0441\u0442\u044c \u0441\u043e\u0431\u044b\u0442\u0438\u0435 <code>on_publish<\/code> \u0432 \u0441\u0435\u0440\u0432\u0435\u0440\u0435 nginx \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442 \u043c\u0435\u0442\u043e\u0434 <code>rtmp_on <\/code>\u0443 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u0430 \u043f\u043e\u0441\u0442\u0430. \u0412\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442 <code>stream_name<\/code> \u0438 <code>stream_token<\/code> \u0442.\u0435. \u0435\u0441\u0442\u044c \u043b\u0438 \u0432\u043e\u043e\u0431\u0449\u0435 \u0442\u0430\u043a\u043e\u0439 \u043f\u043e\u0441\u0442 \u0438 \u0441\u043e\u0432\u043f\u0430\u0434\u0430\u0435\u0442 \u043b\u0438 \u0442\u043e\u043a\u0435\u043d, \u0435\u0441\u043b\u0438 \u0434\u0430, \u0442\u043e \u043e\u0442\u0432\u0435\u0442 \u0434\u043b\u044f \u0441\u0435\u0440\u0432\u0435\u0440\u0430 HTTP 204, \u0438 \u0441\u0435\u0440\u0432\u0435\u0440 \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0430\u0435\u0442 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435 RTMP, \u0430 \u0435\u0441\u043b\u0438 \u043d\u0435\u0442, \u0442\u043e HTTP 403, \u0441\u0435\u0440\u0432\u0435\u0440 \u043e\u0442\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 \u0432 \u043f\u0440\u0438\u0451\u043c\u0435 \u0434\u0430\u043d\u043d\u044b\u0445, \u0432 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0435 OBS \u0432\u044b\u0439\u0434\u0435\u0442 \u043e\u0448\u0438\u0431\u043a\u0430 I\/O error. rtmp_off &#8212; \u0432\u043d\u043e\u0441\u0438\u0442 \u043c\u0435\u043d\u044f\u0435\u0442 \u0441\u0442\u0430\u0442\u0443\u0441 \u043f\u043e\u0441\u0442\u0430 \u043d\u0430 &#171;\u0442\u0440\u0430\u043d\u0441\u043b\u044f\u0446\u0438\u044f \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0430&#187;. rtmp_update &#8212; \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u0442 \u0441\u0432\u0435\u0434\u0435\u043d\u0438\u044f \u043e \u0442\u0440\u0430\u043d\u0441\u043b\u044f\u0446\u0438\u0438. \u0422\u0430\u043a\u0436\u0435 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440 \u043f\u043e\u0441\u0442\u0430 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442 \u043d\u0430\u0434\u043e \u043b\u0438 \u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c \u0442\u0440\u0430\u043d\u0441\u043b\u044f\u0446\u0438\u044e, \u043f\u0440\u0438\u0447\u0451\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043c\u043e\u0436\u0435\u0442 \u0432 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u043c \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u043d\u0430\u0447\u0438\u043d\u0430\u0442\u044c \u0438 \u043e\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0442\u044c \u0437\u0430\u043f\u0438\u0441\u044c. \u0414\u043b\u044f \u0442\u0430\u043a\u0438\u0445 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439 \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043e\u0447\u0435\u0440\u0435\u0434\u044c Laravel \u0434\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u043e\u043d\u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u043b\u0438\u0441\u044c \u0432 \u043e\u0434\u043d\u043e\u043c \u043f\u043e\u0442\u043e\u043a\u0435. \u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0441\u043b\u0443\u0436\u0431\u0443 \u0434\u043b\u044f \u043e\u0447\u0435\u0440\u0435\u0434\u0435\u0439 Laravel:<\/p>\n<pre><code class=\"bash\">[Unit] Description=The Deyen Live Video Platform Laravel Queue Worker Daemon  [Service] User=www-data Group=www-data Restart=on-failure ExecStart=\/usr\/bin\/php \/var\/www\/live.example.org\/artisan queue:work ExecReload=\/usr\/bin\/php \/var\/www\/live.example.org\/artisan queue:restart  [Install] WantedBy=multi-user.target<\/code><\/pre>\n<p>\u0434\u043b\u044f \u043f\u0440\u0438\u0451\u043c\u0430 \u043a\u043e\u043c\u0430\u043d\u0434 \u043e\u0442 \u0441\u043b\u0443\u0436\u0431\u044b \u043e\u0447\u0435\u0440\u0435\u0434\u0435\u0439 laravel \u043d\u0430 \u0441\u0442\u043e\u0440\u043e\u043d\u0435 nginx \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u0439 \u0445\u043e\u0441\u0442 \u043d\u0430 82 \u043f\u043e\u0440\u0442\u0443:<\/p>\n<pre><code class=\"nginx\">server { listen 127.0.1.2:82; root \/var\/www\/live.example.org-hls\/public_html; index index.html index.m3u8; server_name live.example.org;  location \/ { try_files $uri $uri\/ =404; }  location \/control {         rtmp_control all; add_header Access-Control-Allow-Origin \"*\";     } }<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043c\u043e\u0436\u043d\u043e \u0432\u043e\u0432\u0440\u0435\u043c\u044f \u0442\u0440\u0430\u043d\u0441\u043b\u044f\u0446\u0438\u0438 \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u0435\u043c \u0437\u0430\u043f\u0438\u0441\u0438, \u0430 \u0442\u0430\u043a\u0436\u0435 \u043a\u0438\u043a\u0430\u0442\u044c \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u0432-\u0432\u0435\u0449\u0430\u0442\u0435\u043b\u0435\u0439.<\/p>\n<p>\u0417\u0430\u0434\u0430\u043d\u0438\u0435 \u043d\u0430 \u0441\u0442\u0430\u0440\u0442 \u0437\u0430\u043f\u0438\u0441\u0438:<\/p>\n<pre><code class=\"php\">&lt;?php  namespace App\\Jobs;  use Illuminate\\Bus\\Queueable; use Illuminate\\Contracts\\Queue\\ShouldBeUnique; use Illuminate\\Contracts\\Queue\\ShouldQueue; use Illuminate\\Foundation\\Bus\\Dispatchable; use Illuminate\\Queue\\InteractsWithQueue; use Illuminate\\Queue\\SerializesModels; use Illuminate\\Support\\Facades\\Http; use App\\Models\\Mediafile; use App\\Models\\Post;  class StartRec implements ShouldQueue {     use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;      public $post;      public function __construct(Post $post)     {         $this->post = $post;     }      \/**      * Execute the job.      *      * @return void      *\/     public function handle()     {         if ($this->post->record == false) {             $this->post->record = true;             $r = Http::get(env('APP_URL').\":82\/control\/record\/start?rec=rec1&amp;app=show&amp;name={$this->post->stream_name}\");             $v = new Mediafile;             $v->org_id = $this->post->org_id;             $v->user_id = $this->post->user_id;             $m = [0 => \"\"];             preg_match('\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}-[0-9]+_rec.flv\/i', $r, $m);             $v->uri = 'public\/rec\/'.$m[0];             $v->sha256checksum = hash('sha256', $v->uri, true);             $v->save();             $this->post->video_id = $v->id;             Post::where('stream_name', $this->post->stream_name)->update(['video_id' => $v->id, 'record' => true]);         }     } }<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043f\u0440\u043e \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440 \u0432\u0437\u0430\u0438\u043c\u043e\u0440\u0430\u0441\u0447\u0451\u0442\u043e\u0432:<\/p>\n<pre><code class=\"php\">&lt;?php  namespace App\\Http\\Controllers;  use Illuminate\\Support\\Facades\\Auth; use Illuminate\\Http\\Request; use Barryvdh\\DomPDF\\Facade\\Pdf; use App\\Models\\Inout; use App\\Models\\Org; use App\\Models\\User;  class InoutController extends Controller {      public function show($id = 0)     {         if ($id > 0) {             return view('inout', ['inouts' => [Inout::limitByUser()->findOrFail($id)]]);         } else {             return view('inout', ['inouts' => Inout::limitByUser()->orderByDesc('id')->paginate(10)]);         }     }      public function show_balance()     {         return view('inout-balance', ['inouts'=> Inout::getBalances()->limitByUser()->paginate(10)]);     }      public function getkvit(Request $request)     {         $validatedData = $request->validate([             'user_id' => ['required', 'numeric'],             'org_id' => ['required', 'numeric']         ]);         $org = Org::findOrFail($validatedData['org_id']);         $user = User::findOrFail($validatedData['user_id']);         $qs = [\"ST00012\", \"Name={$org->fintitle}\",\"PersonalAcc={$org->personal_acc}\", \"BankName={$org->bank_name}\", \"BIC={$org->bic}\", \"CorrespAcc={$org->corresp_acc}\", \"PayeeINN={$org->inn}\", \"KPP={$org->kpp}\", \"CBC={$org->kbk}\", \"OKTMO={$org->oktmo}\", \"Purpose=ID {$user->id} {$org->purpose}\", \"DrawerStatus={$org->drawer_status}\", \"PersAcc={$user->id}\"];         $ms = implode('|',$qs);         $pdf = PDF::loadView('pdf\/kvit', ['org' => $org, 'user' => $user, 'ms' => $ms]);         return $pdf->download('kvit.pdf');     }      public function edit()     {         if (in_array(Auth::user()->access_level, [2, 3, 4])) {             return view('inout-add');         }     }      public function store(Request $request)     {         if (in_array(Auth::user()->access_level, [2, 3, 4])) {             $inout = new Inout;             $validatedData = $request->validate([                 'title_doc' => ['required', 'string', 'max:64'],                 'number_doc' => ['required', 'string', 'max:64'],                 'date_doc' => ['required', 'date'],                 'user_id' => ['required', 'numeric'],                 'sum' => ['required', 'numeric']             ]);             $inout->fill($validatedData);             $inout->org_id = Auth::user()->org_id;             $inout->total = $inout->balance;             $inout->save();             return redirect()->route('inouts');         }     } }<\/code><\/pre>\n<p>\u0423 \u043d\u0430\u0441 \u043f\u043e \u043a\u0430\u0436\u0434\u043e\u043c\u0443 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e \u043f\u043e \u043a\u0430\u0436\u0434\u043e\u0439 \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0446\u0438\u0438 \u0432\u0435\u0434\u0435\u0442\u0441\u044f \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u043b\u0438\u0446\u0435\u0432\u043e\u0439 \u0441\u0447\u0451\u0442 \u0432\u0437\u0430\u0438\u043c\u043e\u0440\u0430\u0441\u0447\u0451\u0442\u043e\u0432. \u0421\u0430\u043c\u043e\u0433\u043e \u043b\u0438\u0446\u0435\u0432\u043e\u0433\u043e \u0441\u0447\u0451\u0442\u0430 \u043a\u0430\u043a \u0431\u044b \u043d\u0435\u0442, \u044d\u0442\u043e \u043b\u0438\u0448\u044c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u043e\u0442\u043d\u043e\u0448\u0435\u043d\u0438\u0439 \u043c\u0435\u0436\u0434\u0443 \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0446\u0438\u0435\u0439 \u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u043c. \u042d\u0442\u043e \u043f\u043e\u043b\u0435\u0437\u043d\u043e \u043a\u043e\u0433\u0434\u0430 \u043c\u044b \u0441 \u044e\u0440\u0438\u0434\u0438\u0447\u0435\u0441\u043a\u043e\u0439 \u0442\u043e\u0447\u043a\u0438 \u0437\u0440\u0435\u043d\u0438\u044f \u043d\u0435 \u0445\u043e\u0442\u0438\u043c \u0431\u044b\u0442\u044c \u043f\u043b\u0430\u0442\u0435\u0436\u043d\u044b\u043c \u0430\u0433\u0435\u043d\u0442\u043e\u043c \u0438 \u0432\u0441\u0435 \u043f\u043b\u0430\u0442\u0435\u0436\u0438 \u043c\u0435\u0436\u0434\u0443 \u043a\u043b\u0438\u0435\u043d\u0442\u0430\u043c\u0438 \u0438 \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0446\u0438\u044f\u043c\u0438 \u043f\u0440\u043e\u0445\u043e\u0434\u044f\u0442 \u043d\u0430 \u043f\u0440\u044f\u043c\u0443\u044e. \u0422\u0430\u043a \u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043c\u0435\u0442\u043e\u0434 \u043e\u043f\u043b\u0430\u0442\u044b \u0432 \u043f\u043e\u043b\u044c\u0437\u0443 \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u043e \u0440\u0435\u043a\u0432\u0438\u0437\u0438\u0442\u0430\u043c \u0441 \u0444\u043e\u0440\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u043a\u0432\u0438\u0442\u0430\u043d\u0446\u0438\u0438 \u0432 PDF \u0441 QR-\u043a\u043e\u0434\u043e\u043c. \u0417\u0430 \u044d\u0442\u043e \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0444\u0443\u043d\u043a\u0446\u0438\u044f getkvit. \u041d\u0435 \u0441\u043e\u0441\u0442\u0430\u0432\u0438\u0442 \u0442\u0440\u0443\u0434\u0430 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0438 \u0434\u0440\u0443\u0433\u0438\u0435 \u043c\u0435\u0442\u043e\u0434\u044b \u043e\u043f\u043b\u0430\u0442\u044b. \u041d\u0435 \u0431\u0443\u0434\u0443 \u0432\u0441\u0442\u0430\u0432\u043b\u044f\u0442\u044c \u043a\u043e\u0434\u044b Blade \u0448\u0430\u0431\u043b\u043e\u043d\u043e\u0432, \u0438\u043d\u0430\u0447\u0435 \u0441\u0442\u0430\u0442\u044c\u044f \u0441\u0442\u0430\u043d\u0435\u0442 \u0441\u043b\u0438\u0448\u043a\u043e\u043c \u0434\u043b\u0438\u043d\u043d\u043e\u0439.<\/p>\n<p>\u041f\u0440\u043e\u0435\u043a\u0442 \u043e\u043f\u0443\u0431\u043b\u0438\u043a\u043e\u0432\u0430\u043d \u043d\u0430 Github <a href=\"https:\/\/github.com\/deyen01\/dlvp\" rel=\"noopener noreferrer nofollow\">https:\/\/github.com\/deyen01\/dlvp<\/a> \u043a\u0430\u043a \u0441\u0432\u043e\u0431\u043e\u0434\u043d\u043e\u0435 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u043d\u043e\u0435 \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0435\u043d\u0438\u0435.<\/p>\n<\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"v-portal\" style=\"display:none;\"><\/div>\n<\/div>\n<p> <!----> <!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/post\/672276\/\"> https:\/\/habr.com\/ru\/post\/672276\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u041f\u0440\u0438\u0432\u0435\u0442, \u0425\u0430\u0431\u0440. \u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u044f \u0445\u043e\u0447\u0443 \u043f\u043e\u0434\u0435\u043b\u0438\u0442\u044c\u0441\u044f \u0441\u0432\u043e\u0438\u043c \u043e\u043f\u044b\u0442\u043e\u043c \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043d\u0430 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0435 Laravel \u043f\u043e \u0442\u0440\u0430\u043d\u0441\u043b\u044f\u0446\u0438\u0438 \u0432\u0438\u0434\u0435\u043e\u043a\u043e\u043d\u0442\u0435\u043d\u0442\u0430. \u0418\u0442\u0430\u043a \u043d\u0430\u0447\u043d\u0451\u043c.<\/p>\n<p><a href=\"https:\/\/github.com\/deyen01\/dlvp\" rel=\"noopener noreferrer nofollow\"><em>\u041f\u0440\u043e\u0435\u043a\u0442 \u043e\u043f\u0443\u0431\u043b\u0438\u043a\u043e\u0432\u0430\u043d \u043a\u0430\u043a \u0441\u0432\u043e\u0431\u043e\u0434\u043d\u043e\u0435 \u041f\u041e<\/em><\/a><\/p>\n<h2>\u0417\u0430\u0434\u0430\u0447\u0430<\/h2>\n<p>\u0421\u0434\u0435\u043b\u0430\u0442\u044c \u0441\u0435\u0440\u0432\u0438\u0441, \u0441\u043e\u0432\u043c\u0435\u0441\u0442\u0438\u043c\u044b\u0439 \u0441 \u0431\u0438\u0437\u043d\u0435\u0441-\u043c\u043e\u0434\u0435\u043b\u044c\u044e SaaS , \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u044e\u0449\u0438\u0439 \u0434\u0430\u043d\u043d\u044b\u0435 \u043f\u043e \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u0443 RTMP \u043e\u0442 \u0440\u0430\u0437\u043d\u044b\u0445 \u043f\u043e\u0441\u0442\u0430\u0432\u0449\u0438\u043a\u043e\u0432 \u043a\u043e\u043d\u0442\u0435\u043d\u0442\u0430 \u0438 \u0440\u0430\u0437\u0434\u0430\u044e\u0449\u0438\u0439 \u044d\u0442\u043e\u0442 \u043a\u043e\u043d\u0442\u0435\u043d\u0442 \u043f\u043e HLS \u043a\u043e\u043d\u0435\u0447\u043d\u044b\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c \u0437\u0430 \u043f\u043b\u0430\u0442\u0443 \u0438\u043b\u0438 \u0431\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u043e, \u0442.\u0435. \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c Live-\u0442\u0440\u0430\u043d\u0441\u043b\u044f\u0446\u0438\u0438.<\/p>\n<h2>\u0418\u043d\u0433\u0440\u0435\u0434\u0438\u0435\u043d\u0442\u044b<\/h2>\n<p>\u0411\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0441\u0432\u043e\u0431\u043e\u0434\u043d\u043e\u0435 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u043d\u043e\u0435 \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0435\u043d\u0438\u0435. \u0414\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 RTMP \u0438 HLS \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c nginx \u0441 <a href=\"https:\/\/github.com\/arut\/nginx-rtmp-module\" rel=\"noopener noreferrer nofollow\">nginx-rtmp-module<\/a>. \u0414\u043b\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c apache2, php, \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445 MariaDB. \u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0430 Laravel \u0441 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430\u043c\u0438 LiveWire \u0434\u043b\u044f \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u0434\u043b\u044f \u043f\u043e\u0441\u0442\u0440\u043e\u0435\u043d\u0438\u044f html \u0441\u0442\u0440\u0430\u043d\u0438\u0446 \u0448\u0430\u0431\u043b\u043e\u043d\u044b Blade. \u0414\u043b\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0437\u0430\u043f\u0438\u0441\u0430\u043d\u043d\u044b\u0445 \u0442\u0440\u0430\u043d\u0441\u043b\u044f\u0446\u0438\u0439 \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <a href=\"https:\/\/ffmpeg.org\/\" rel=\"noopener noreferrer nofollow\">FFMPEG<\/a>. \u0412\u0441\u0451 \u044d\u0442\u043e \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435 Ubuntu 20.04 LTS.<\/p>\n<h2>\u041f\u0440\u0438\u0441\u0442\u0443\u043f\u0430\u0435\u043c<\/h2>\n<p>\u0421\u043e\u0437\u0434\u0430\u043b\u0438 \u043f\u0440\u043e\u0435\u043a\u0442 Laravel 8. \u0421\u043e\u0437\u0434\u0430\u0451\u043c \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u0438 \u0434\u043b\u044f \u0431\u0430\u0437 \u0434\u0430\u043d\u043d\u044b\u0445. \u0423 \u043d\u0430\u0441 \u0431\u0443\u0434\u0443\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438, \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0446\u0438\u0438 (\u043f\u043e\u0441\u0442\u0430\u0432\u0449\u0438\u043a\u0438 \u043a\u043e\u043d\u0442\u0435\u043d\u0442\u0430), \u043f\u043e\u0441\u0442\u044b (\u0442\u043e \u0435\u0441\u0442\u044c \u0437\u0430\u043f\u0438\u0441\u0438 \u043e \u0431\u0443\u0434\u0443\u0449\u0438\u0445, \u0442\u0435\u043a\u0443\u0449\u0438\u0445 \u0438 \u043f\u0440\u043e\u0448\u0435\u0434\u0448\u0438\u0445 \u0442\u0440\u0430\u043d\u0441\u043b\u044f\u0446\u0438\u044f\u0445) \u0438 \u0442\u0430\u043a \u0434\u0430\u043b\u0435\u0435.<\/p>\n<p>\u0422\u0430\u0431\u043b\u0438\u0446\u0430 Users:<\/p>\n<pre><code class=\"php\">&lt;?php  use Illuminate\\Database\\Migrations\\Migration; use Illuminate\\Database\\Schema\\Blueprint; use Illuminate\\Support\\Facades\\Schema;  class CreateUsersTable extends Migration {     \/**      * Run the migrations.      *      * @return void      *\/     public function up()     {         Schema::create('users', function (Blueprint $table) {             $table->id();             $table->string('name');             $table->string('email')->unique();             $table->unsignedTinyInteger('access_level')->default(0); \/\/ 0 - user, 1 - editor, 2 - finmanager, 3 - admin, 4 - root(global admin)             $table->timestamp('email_verified_at')->nullable();             $table->string('password')->nullable();             $table->rememberToken();             $table->string('google_id')->nullable();             $table->string('google_token')->nullable();             $table->string('google_refresh_token')->nullable();             $table->string('instagram_id')->nullable();             $table->string('instagram_token')->nullable();             $table->string('instagram_refresh_token')->nullable();             $table->string('yandex_id')->nullable();             $table->string('yandex_token')->nullable();             $table->string('yandex_refresh_token')->nullable();             $table->string('vk_id')->nullable();             $table->string('vk_token')->nullable();             $table->string('vk_refresh_token')->nullable();             $table->foreignId('org_id')                 ->nullable()                 ->constrained()                 ->onUpdate('cascade')                 ->onDelete('restrict');             $table->timestamps();         });     }      \/**      * Reverse the migrations.      *      * @return void      *\/     public function down()     {         Schema::dropIfExists('users');     } }<\/code><\/pre>\n<p>\u0422\u0430\u0431\u043b\u0438\u0446\u0430 Orgs:<\/p>\n<pre><code class=\"php\">&lt;?php  use Illuminate\\Database\\Migrations\\Migration; use Illuminate\\Database\\Schema\\Blueprint; use Illuminate\\Support\\Facades\\Schema;  class CreateOrgsTable extends Migration {     \/**      * Run the migrations.      *      * @return void      *\/     public function up()     {         Schema::create('orgs', function (Blueprint $table) {             $table->id();             $table->string('fulltitle', 512)->nullable();             $table->string('title', 128);             $table->string('brandtitle', 128);             $table->string('ogrn', 15);             $table->string('inn', 12);             $table->string('kpp', 9)->nullable();             $table->string('address', 255);             $table->string('drawer_status', 2)->nullable();             $table->string('fintitle', 255);             $table->string('personal_acc', 20);             $table->string('bank_name', 128);             $table->string('bic', 9);             $table->string('corresp_acc', 20);             $table->string('kbk', 20)->nullable();             $table->string('titlekbk', 128)->nullable();             $table->string('oktmo', 11)->nullable();             $table->string('purpose', 255)->nullable();             $table->string('email', 255);             $table->string('tel', 10);;             $table->timestamps();         });     }      \/**      * Reverse the migrations.      *      * @return void      *\/     public function down()     {         Schema::dropIfExists('orgs');     } }<\/code><\/pre>\n<p>\u0422\u0430\u0431\u043b\u0438\u0446\u0430 \u043f\u043e\u0441\u0442\u043e\u0432:<\/p>\n<pre><code class=\"php\">&lt;?php ... Schema::create('posts', function (Blueprint $table) {             $table->id();             $table->foreignId('org_id')                 ->nullable()                 ->constrained()                 ->cascadeOnUpdate()                 ->nullOnDelete();             $table->boolean('record')->default(FALSE);             $table->boolean('autorecord')->default(FALSE);             $table->boolean('file_preparation')->default(FALSE);             $table->boolean('rtmp_status')->default(FALSE);             $table->ipAddress('rtmp_ip_sender')->nullable();             $table->boolean('allow_comment')->default(FALSE);             $table->string('title1', 64);             $table->string('title2', 64)->nullable();             $table->string('body', 2048)->nullable();             $table->uuid('stream_name')->unique();             $table->string('stream_token', 32);             $table->dateTime('dt_begin');             $table->dateTime('dt_end');             $table->unsignedDecimal('price', 14, 2)->nullable();             $table->unsignedBigInteger('timeleft')->nullable();             $table->unsignedBigInteger('timepass')->nullable();             $table->char('color', 4)->charset('binary')->nullable();             $table->foreignId('picture_id')->nullable()->constrained('mediafiles')->cascadeOnUpdate()->nullOnDelete();             $table->foreignId('videopreview_id')->nullable()->constrained('mediafiles')->cascadeOnUpdate()->nullOnDelete();             $table->foreignId('video_id')->nullable()->constrained('mediafiles')->cascadeOnUpdate()->nullOnDelete();             $table->foreignId('user_id') \/\/author                 ->nullable()                 ->constrained()                 ->cascadeOnUpdate()                 ->nullOnDelete();             $table->unsignedBigInteger('cv_before')->default(0);             $table->unsignedBigInteger('cv_live')->default(0);             $table->unsignedBigInteger('cv_after')->default(0);             $table->timestamps();         });<\/code><\/pre>\n<h2>\u041d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u043c \u043c\u043e\u0434\u0435\u043b\u0438 Eloquent<\/h2>\n<p>\u041c\u043e\u0434\u0435\u043b\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f:<\/p>\n<pre><code class=\"php\">&lt;?php  namespace App\\Models;  use Illuminate\\Contracts\\Auth\\MustVerifyEmail; use Illuminate\\Database\\Eloquent\\Factories\\HasFactory; use Illuminate\\Foundation\\Auth\\User as Authenticatable; use Illuminate\\Notifications\\Notifiable; use Laravel\\Sanctum\\HasApiTokens; use Illuminate\\Support\\Facades\\Auth;  class User extends Authenticatable {     use HasApiTokens, HasFactory, Notifiable;      const AAL = [         0 => '\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c',         1 => '\u0420\u0435\u0434\u0430\u043a\u0442\u043e\u0440',         2 => '\u0424\u0438\u043d\u0430\u043d\u0441\u043e\u0432\u044b\u0439 \u043c\u0435\u043d\u0435\u0434\u0436\u0435\u0440',         3 => '\u0410\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440',         4 => 'root'     ];      \/**      * The attributes that are mass assignable.      *      * @var array&lt;int, string>      *\/     protected $fillable = [         'name',         'email',         'password',         'google_id',         'google_token',         'google_refresh_token',         'instagram_id',         'instagram_token',         'instagram_refresh_token',         'vk_id',         'vk_token',         'vk_refresh_token',         'yandex_id',         'yandex_token',         'yandex_refresh_token',     ];      protected $attributes = ['access_level' => 0];      \/**      * The attributes that should be hidden for serialization.      *      * @var array&lt;int, string>      *\/     protected $hidden = [         'password',         'remember_token',         'google_id',         'google_token',         'google_refresh_token',         'instagram_id',         'instagram_token',         'instagram_refresh_token',         'vk_id',         'vk_token',         'vk_refresh_token',         'yandex_id',         'yandex_token',         'yandex_refresh_token',     ];      \/**      * The attributes that should be cast.      *      * @var array&lt;string, string>      *\/     protected $casts = [         'email_verified_at' => 'datetime',     ];      public function getALAttribute()     {         return self::AAL[$this->access_level];     }      public function org() {         return $this->belongsTo(Org::class);     }      public function scopeLimitAL($query){         $ac = Auth::user()->access_level;         if ($ac == 0) {             return $query->where('id', Auth::id());         } else {             return $query->where('access_level', '&lt;=', $ac);         }     } }<\/code><\/pre>\n<p>\u041c\u043e\u0434\u0435\u043b\u044c \u043f\u043e\u0441\u0442\u0430:<\/p>\n<pre><code class=\"php\">&lt;?php  namespace App\\Models;  use Illuminate\\Database\\Eloquent\\Factories\\HasFactory; use Illuminate\\Database\\Eloquent\\Model; use App\\Models\\Mediafile; use App\\Models\\User; use App\\Models\\Org; use App\\Models\\Ticket; use Illuminate\\Support\\Facades\\Auth; use Illuminate\\Support\\Str;  class Post extends Model {     use HasFactory;      public function picture() {         return $this->belongsTo(Mediafile::class);     }      public function video() {         return $this->belongsTo(Mediafile::class);     }      public function videopriview() {         return $this->belongsTo(Mediafile::class);     }      public function getStreamStringAttribute() {         return \"{$this->stream_name}\/{$this->stream_token}\";     }      public function getCvAttribute() {         return $this->cv_before + $this->cv_live + $this->cv_after;     }      public function tickets()     {         return $this->hasMany(Ticket::class);     }      public function user() {         return $this->belongsTo(User::class);     }      public function org() {         return $this->belongsTo(Org::class);     }      protected static function booted()     {         static::creating(function (Post $post) {             $post->user_id = Auth::id();             $post->org_id = Auth::user()->org_id;             $post->stream_name = Str::uuid();    <\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-334717","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/334717","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=334717"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/334717\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=334717"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=334717"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=334717"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}