diff --git a/app/Http/Controllers/DanmakuConstructController.php b/app/Http/Controllers/DanmakuConstructController.php new file mode 100644 index 0000000..6852eef --- /dev/null +++ b/app/Http/Controllers/DanmakuConstructController.php @@ -0,0 +1,71 @@ +has("video_bvid")) { + $bvid = $request->get("video_bvid"); + $video = Videos::query()->where("bvid", "=", $bvid)->first(); + if ($video == null) { + $view->withErrors([ + "video_bvid" => "系统无此对应视频", + ]); + } else { + $request->session()->flashInput([ + "video_bvid" => $bvid + ]); + } + } + return $view; + } + + public function do_import(Request $request) + { + $request->validate([ + 'video_bvid' => ['required'], + 'platform_id' => ['required', 'int'], + 'file' => ['required'] + ]); + $payload = $request->only(["video_bvid", "platform_id"]); + $files = $request->file("file"); + if (!is_array($files)) { + $files = [$files]; + } + $video = Videos::query()->where("bvid", "=", $payload["video_bvid"])->first(); + if ($video == null) { + return back()->withInput()->withErrors([ + "video_bvid" => "系统无此对应视频", + ]); + } + foreach ($files as $file) { + $danmakus = DanmakuUtil::parse_danmaku($file->getFileInfo()); + DB::beginTransaction(); + try { + foreach ($danmakus as &$danmaku) { + $danmaku['video_bvid'] = $video->bvid; + $danmaku['platform_id'] = $payload["platform_id"]; + unset($danmaku); + } + VideoDanmakus::insert($danmakus); + DB::commit(); + } catch (\Exception $e) { + DB::rollBack(); + return back()->withInput()->withErrors([ + "file" => "文件导入异常:" . $e->getMessage(), + ]); + } + } + return redirect("/danmakus/" . $payload["video_bvid"]); + } +} diff --git a/app/Models/VideoDanmakus.php b/app/Models/VideoDanmakus.php index 41af5ad..851fb77 100644 --- a/app/Models/VideoDanmakus.php +++ b/app/Models/VideoDanmakus.php @@ -6,8 +6,14 @@ use Illuminate\Database\Eloquent\Model; class VideoDanmakus extends Model { + protected $guarded = []; protected $table = "video_danmakus"; protected $dateFormat = 'U'; + public $timestamps = false; + protected $casts = [ + 'created_at' => 'datetime:Y-m-d H:i:s', + ]; + protected $fillable = ["from", "from_mid", "content"]; public function video(): \Illuminate\Database\Eloquent\Relations\BelongsTo { return $this->belongsTo(Videos::class, "video_bvid", "bvid"); diff --git a/app/Util/DanmakuUtil.php b/app/Util/DanmakuUtil.php new file mode 100644 index 0000000..5b59ac2 --- /dev/null +++ b/app/Util/DanmakuUtil.php @@ -0,0 +1,32 @@ +load($file->getRealPath()); + $danmaku_items = $document->getElementsByTagName("d"); + $result = []; + /** @var \DOMNode $item */ + foreach ($danmaku_items as $item) { + $paramsNode = $item->attributes->getNamedItem("p"); + $param_list = mb_split(",", $paramsNode->value); + if (sizeof($param_list) < 7) { + throw new \Exception("弹幕格式异常"); + } + $userNode = $item->attributes->getNamedItem("user"); + $result[] = [ + "from" => $userNode->value, + "from_mid" => $param_list[6], + "content" => $item->textContent, + "created_at" => intval($param_list[4])/1000, + ]; + } + return $result; + } +} diff --git a/composer.json b/composer.json index afcb5ef..a7c93d6 100644 --- a/composer.json +++ b/composer.json @@ -6,6 +6,7 @@ "license": "MIT", "require": { "php": "^7.3|^8.0", + "ext-dom": "*", "ext-json": "*", "ext-mbstring": "*", "fruitcake/laravel-cors": "^2.0", diff --git a/resources/views/common/header.blade.php b/resources/views/common/header.blade.php index 534d931..16c71ca 100644 --- a/resources/views/common/header.blade.php +++ b/resources/views/common/header.blade.php @@ -21,7 +21,7 @@ 稿件查询 节目查询 @auth("web") - 节目建设 + 节目建设 @endauth @@ -50,7 +50,7 @@ 稿件查询 节目查询 @auth("web") - 节目建设 + 节目建设 @endauth diff --git a/resources/views/danmaku/construct/batch_import.blade.php b/resources/views/danmaku/construct/batch_import.blade.php new file mode 100644 index 0000000..b03a2ea --- /dev/null +++ b/resources/views/danmaku/construct/batch_import.blade.php @@ -0,0 +1,35 @@ + + + + 弹幕导入 + + + + + @include("common.header") +
+ @csrf + + + + @include("common.form_error") +
+ +
+
+ @include("common.footer") + + diff --git a/resources/views/video/index.blade.php b/resources/views/video/index.blade.php index 0ca521d..2e6531f 100644 --- a/resources/views/video/index.blade.php +++ b/resources/views/video/index.blade.php @@ -68,6 +68,9 @@ @if(sizeof($video_pivots) === 0 && $comment) $comment->id])) }}" class="px-6 py-2 inline-block rounded-full bg-cyan-600 text-white">一键导入评论中的节目单 @endif +@if($video->danmakus->count() === 0) + $video->bvid])) }}" class="px-6 py-2 inline-block rounded-full bg-cyan-600 text-white">导入直播弹幕 +@endif @endauth @include("common.footer") diff --git a/routes/web.php b/routes/web.php index a88b052..ab61b96 100644 --- a/routes/web.php +++ b/routes/web.php @@ -31,36 +31,44 @@ Route::post("/login/webauthn/", ["\\App\\Http\\Controllers\\UserWebAuthnControll Route::get('/register', ["\\App\\Http\\Controllers\\UserController", "register_page"])->name("register"); Route::post('/register', ["\\App\\Http\\Controllers\\UserController", "register"])->name("register.submit"); Route::get('/logout', ["\\App\\Http\\Controllers\\UserController", "logout"])->name("logout"); +// 弹幕建设 // 建设部分 -Route::prefix("/programs/construct")->middleware("auth:web")->group(function (Router $router) { - // 节目建设 - $router->get('/', ["\\App\\Http\\Controllers\\ProgramConstructController","index"])->name("program.construct.list"); - $router->get('/add', ["\\App\\Http\\Controllers\\ProgramConstructController","add"])->name("program.construct.add"); - $router->post('/add', ["\\App\\Http\\Controllers\\ProgramConstructController","create"])->name("program.construct.create"); - $router->get('/from_comment/{comment}', ["\\App\\Http\\Controllers\\ProgramConstructController","from_comment"])->name("program.construct.from_comment"); - $router->get('/batch', ["\\App\\Http\\Controllers\\ProgramConstructController","batch_add"])->name("program.construct.batch_add"); - $router->post('/batch', ["\\App\\Http\\Controllers\\ProgramConstructController","batch_create"])->name("program.construct.batch_create"); - $router->get('/{program}', ["\\App\\Http\\Controllers\\ProgramConstructController","edit"])->name("program.construct.edit"); - $router->post('/{program}', ["\\App\\Http\\Controllers\\ProgramConstructController", "submit"])->name("program.construct.submit"); - // 节目关联视频建设 - $router->get("/{program}/video", ["\\App\\Http\\Controllers\\ProgramVideoConstructController","index"])->name("program.construct.video.list"); - $router->get("/{program}/video/add", ["\\App\\Http\\Controllers\\ProgramVideoConstructController","add"])->name("program.construct.video.add"); - $router->post("/{program}/video/add", ["\\App\\Http\\Controllers\\ProgramVideoConstructController","create"])->name("program.construct.video.create"); - $router->get("/video/{program_video}", ["\\App\\Http\\Controllers\\ProgramVideoConstructController","edit"])->name("program.construct.video.edit"); - $router->post("/video/{program_video}", ["\\App\\Http\\Controllers\\ProgramVideoConstructController","submit"])->name("program.construct.video.submit"); - $router->get("/video/{program_video}/manual_fix", ["\\App\\Http\\Controllers\\ProgramVideoConstructController","to_fix_created_at"])->name("program.construct.video.manual_fix_created_at.view"); - $router->post("/video/{program_video}/manual_fix", ["\\App\\Http\\Controllers\\ProgramVideoConstructController","fix_created_at_base_on"])->name("program.construct.video.manual_fix_created_at"); - $router->get("/video/fix/{bvid}", ["\\App\\Http\\Controllers\\ProgramVideoConstructController","auto_fix_created_at"])->name("program.construct.video.auto_fix_created_at"); - // 节目关联点播建设 - $router->get('/{program}/append', ["\\App\\Http\\Controllers\\ProgramAppendConstructController","index"])->name("program.construct.append.list"); - $router->get('/{program}/append/add', ["\\App\\Http\\Controllers\\ProgramAppendConstructController","add"])->name("program.construct.append.add"); - $router->post('/{program}/append/add', ["\\App\\Http\\Controllers\\ProgramAppendConstructController","create"])->name("program.construct.append.create"); - $router->get('/append/from_list', ["\\App\\Http\\Controllers\\ProgramAppendConstructController", "from_list"])->name("program.construct.append.from_list"); - $router->get('/{program}/append/copy', ["\\App\\Http\\Controllers\\ProgramAppendConstructController","copy_view"])->name("program.construct.append.copy"); - $router->post('/{program}/append/copy', ["\\App\\Http\\Controllers\\ProgramAppendConstructController","copy_append"])->name("program.construct.append.copy.submit"); - $router->get('/append/broadcast_list', ["\\App\\Http\\Controllers\\ProgramAppendConstructController", "broadcast_list"])->name("program.construct.append.broadcast_list"); - $router->get('/append/{append}', ["\\App\\Http\\Controllers\\ProgramAppendConstructController","edit"])->name("program.construct.append.edit"); - $router->post('/append/{append}', ["\\App\\Http\\Controllers\\ProgramAppendConstructController","submit"])->name("program.construct.append.submit"); +Route::prefix("/construct")->middleware("auth:web")->group(function (Router $router) { + Route::prefix("/programs")->group(function (Router $router) { + // 节目建设 + $router->get('/', ["\\App\\Http\\Controllers\\ProgramConstructController","index"])->name("program.construct.list"); + $router->get('/add', ["\\App\\Http\\Controllers\\ProgramConstructController","add"])->name("program.construct.add"); + $router->post('/add', ["\\App\\Http\\Controllers\\ProgramConstructController","create"])->name("program.construct.create"); + $router->get('/from_comment/{comment}', ["\\App\\Http\\Controllers\\ProgramConstructController","from_comment"])->name("program.construct.from_comment"); + $router->get('/batch', ["\\App\\Http\\Controllers\\ProgramConstructController","batch_add"])->name("program.construct.batch_add"); + $router->post('/batch', ["\\App\\Http\\Controllers\\ProgramConstructController","batch_create"])->name("program.construct.batch_create"); + $router->get('/{program}', ["\\App\\Http\\Controllers\\ProgramConstructController","edit"])->name("program.construct.edit"); + $router->post('/{program}', ["\\App\\Http\\Controllers\\ProgramConstructController", "submit"])->name("program.construct.submit"); + // 节目关联视频建设 + $router->get("/{program}/video", ["\\App\\Http\\Controllers\\ProgramVideoConstructController","index"])->name("program.construct.video.list"); + $router->get("/{program}/video/add", ["\\App\\Http\\Controllers\\ProgramVideoConstructController","add"])->name("program.construct.video.add"); + $router->post("/{program}/video/add", ["\\App\\Http\\Controllers\\ProgramVideoConstructController","create"])->name("program.construct.video.create"); + $router->get("/video/{program_video}", ["\\App\\Http\\Controllers\\ProgramVideoConstructController","edit"])->name("program.construct.video.edit"); + $router->post("/video/{program_video}", ["\\App\\Http\\Controllers\\ProgramVideoConstructController","submit"])->name("program.construct.video.submit"); + $router->get("/video/{program_video}/manual_fix", ["\\App\\Http\\Controllers\\ProgramVideoConstructController","to_fix_created_at"])->name("program.construct.video.manual_fix_created_at.view"); + $router->post("/video/{program_video}/manual_fix", ["\\App\\Http\\Controllers\\ProgramVideoConstructController","fix_created_at_base_on"])->name("program.construct.video.manual_fix_created_at"); + $router->get("/video/fix/{bvid}", ["\\App\\Http\\Controllers\\ProgramVideoConstructController","auto_fix_created_at"])->name("program.construct.video.auto_fix_created_at"); + // 节目关联点播建设 + $router->get('/{program}/append', ["\\App\\Http\\Controllers\\ProgramAppendConstructController","index"])->name("program.construct.append.list"); + $router->get('/{program}/append/add', ["\\App\\Http\\Controllers\\ProgramAppendConstructController","add"])->name("program.construct.append.add"); + $router->post('/{program}/append/add', ["\\App\\Http\\Controllers\\ProgramAppendConstructController","create"])->name("program.construct.append.create"); + $router->get('/append/from_list', ["\\App\\Http\\Controllers\\ProgramAppendConstructController", "from_list"])->name("program.construct.append.from_list"); + $router->get('/{program}/append/copy', ["\\App\\Http\\Controllers\\ProgramAppendConstructController","copy_view"])->name("program.construct.append.copy"); + $router->post('/{program}/append/copy', ["\\App\\Http\\Controllers\\ProgramAppendConstructController","copy_append"])->name("program.construct.append.copy.submit"); + $router->get('/append/broadcast_list', ["\\App\\Http\\Controllers\\ProgramAppendConstructController", "broadcast_list"])->name("program.construct.append.broadcast_list"); + $router->get('/append/{append}', ["\\App\\Http\\Controllers\\ProgramAppendConstructController","edit"])->name("program.construct.append.edit"); + $router->post('/append/{append}', ["\\App\\Http\\Controllers\\ProgramAppendConstructController","submit"])->name("program.construct.append.submit"); + }); + // 弹幕维护 + Route::prefix("/danmaku")->group(function (Router $router) { + $router->get("/batch_import", ["\\App\\Http\\Controllers\\DanmakuConstructController", "page"])->name("danmaku.construct.batch_import.page"); + $router->post("/batch_import", ["\\App\\Http\\Controllers\\DanmakuConstructController", "do_import"])->name("danmaku.construct.batch_import"); + }); }); Route::prefix("/user")->middleware("auth:web")->group(function (Router $router) { $router->post("/webauthn/options", ["\\App\\Http\\Controllers\\UserWebAuthnController", "register_options"])->name("user.webauthn.bind.options");