You've already forked lubo_comment_query
							
							支持主动导入弹幕
This commit is contained in:
		
							
								
								
									
										71
									
								
								app/Http/Controllers/DanmakuConstructController.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								app/Http/Controllers/DanmakuConstructController.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,71 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace App\Http\Controllers; | ||||
| 
 | ||||
| use App\Models\VideoDanmakus; | ||||
| use App\Models\Videos; | ||||
| use App\Util\DanmakuUtil; | ||||
| use Illuminate\Http\Request; | ||||
| use Illuminate\Routing\Controller as BaseController; | ||||
| use Illuminate\Support\Facades\DB; | ||||
| 
 | ||||
| class DanmakuConstructController extends BaseController | ||||
| { | ||||
|     public function page(Request $request) | ||||
|     { | ||||
|         $view = view("danmaku.construct.batch_import"); | ||||
|         if ($request->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"]); | ||||
|     } | ||||
| } | ||||
| @@ -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"); | ||||
|   | ||||
							
								
								
									
										32
									
								
								app/Util/DanmakuUtil.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								app/Util/DanmakuUtil.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace App\Util; | ||||
| 
 | ||||
| use SplFileInfo; | ||||
| 
 | ||||
| class DanmakuUtil | ||||
| { | ||||
|     public static function parse_danmaku(SplFileInfo $file): array | ||||
|     { | ||||
|         $document = new \DOMDocument(); | ||||
|         $document->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; | ||||
|     } | ||||
| } | ||||
| @@ -6,6 +6,7 @@ | ||||
|     "license": "MIT", | ||||
|     "require": { | ||||
|         "php": "^7.3|^8.0", | ||||
|         "ext-dom": "*", | ||||
|         "ext-json": "*", | ||||
|         "ext-mbstring": "*", | ||||
|         "fruitcake/laravel-cors": "^2.0", | ||||
|   | ||||
| @@ -21,7 +21,7 @@ | ||||
|                         <a class="{{ (request()->is('danmakus', 'danmakus/*')) ? 'bg-gray-900 text-white' : 'text-gray-700 hover:bg-gray-700 hover:text-white' }} px-3 py-2 rounded-md text-sm font-medium" href="/danmakus">稿件查询</a> | ||||
|                         <a class="{{ (request()->is('programs', '/', 'programs/*/video')) ? 'bg-gray-900 text-white' : 'text-gray-700 hover:bg-gray-700 hover:text-white' }} px-3 py-2 rounded-md text-sm font-medium" href="/programs" title="数据不全,待补充">节目查询</a> | ||||
|                         @auth("web") | ||||
|                             <a class="{{ (request()->is('programs/construct', 'programs/construct/*')) ? 'bg-gray-900 text-white' : 'text-gray-700 hover:bg-gray-700 hover:text-white' }} px-3 py-2 rounded-md text-sm font-medium" href="/programs/construct">节目建设</a> | ||||
|                             <a class="{{ (request()->is('construct/*')) ? 'bg-gray-900 text-white' : 'text-gray-700 hover:bg-gray-700 hover:text-white' }} px-3 py-2 rounded-md text-sm font-medium" href="/construct/programs">节目建设</a> | ||||
|                         @endauth | ||||
|                     </div> | ||||
|                 </div> | ||||
| @@ -50,7 +50,7 @@ | ||||
|             <a class="{{ (request()->is('danmakus', 'danmakus/*')) ? 'bg-gray-900 text-white' : 'text-gray-700 hover:bg-gray-700 hover:text-white' }} block px-3 py-2 rounded-md text-base font-medium" href="/danmakus">稿件查询</a> | ||||
|             <a class="{{ (request()->is('programs', '/', 'programs/*/video')) ? 'bg-gray-900 text-white' : 'text-gray-700 hover:bg-gray-700 hover:text-white' }} block px-3 py-2 rounded-md text-base font-medium" href="/programs" title="数据不全,待补充">节目查询</a> | ||||
|             @auth("web") | ||||
|                 <a class="{{ (request()->is('programs/construct', 'programs/construct/*')) ? 'bg-gray-900 text-white' : 'text-gray-700 hover:bg-gray-700 hover:text-white' }} block px-3 py-2 rounded-md text-base font-medium" href="/programs/construct">节目建设</a> | ||||
|                 <a class="{{ (request()->is('construct/*')) ? 'bg-gray-900 text-white' : 'text-gray-700 hover:bg-gray-700 hover:text-white' }} block px-3 py-2 rounded-md text-base font-medium" href="/construct/programs">节目建设</a> | ||||
|             @endauth | ||||
|         </div> | ||||
|     </div> | ||||
|   | ||||
							
								
								
									
										35
									
								
								resources/views/danmaku/construct/batch_import.blade.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								resources/views/danmaku/construct/batch_import.blade.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | ||||
| <html lang="zh"> | ||||
| <head> | ||||
|     <meta charset="UTF-8"> | ||||
|     <title>弹幕导入</title> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||||
|     <link href="{{ mix('/css/app.css') }}" rel="stylesheet"/> | ||||
| </head> | ||||
| <body> | ||||
|     @include("common.header") | ||||
|     <form class="w-full lg:w-1/2 lg:ml-6 border-2" action="" method="post" enctype="multipart/form-data"> | ||||
|         @csrf | ||||
|         <label class="block my-2"> | ||||
|             BVID | ||||
|             <input class="form-input border-0 border-b-2 w-full" type="text" name="video_bvid" required value="{{ old('video_bvid') }}"> | ||||
|         </label> | ||||
|         <label class="block my-2"> | ||||
|             弹幕类型 | ||||
|             <span class="block form-input border-0 border-b-2 w-full"> | ||||
|                 <input type="radio" value="1" name="platform_id" checked>B站 | ||||
|                 <input type="radio" value="2" name="platform_id" disabled>西瓜 | ||||
|                 <input type="radio" value="3" name="platform_id" disabled>抖音 | ||||
|             </span> | ||||
|         </label> | ||||
|         <label class="block my-2"> | ||||
|             弹幕文件 | ||||
|             <input class="form-input border-0 border-b-2 w-full" multiple type="file" name="file[]" accept="text/xml"> | ||||
|         </label> | ||||
|         @include("common.form_error") | ||||
|         <div class="block my-2 text-center"> | ||||
|             <input class="px-6 py-2 inline-block rounded-full bg-cyan-600 text-white" type="submit"> | ||||
|         </div> | ||||
|     </form> | ||||
|     @include("common.footer") | ||||
| </body> | ||||
| </html> | ||||
| @@ -68,6 +68,9 @@ | ||||
| @if(sizeof($video_pivots) === 0 && $comment) | ||||
|     <a href="{{ url(route("program.construct.from_comment", ["comment"=>$comment->id])) }}" class="px-6 py-2 inline-block rounded-full bg-cyan-600 text-white">一键导入评论中的节目单</a> | ||||
| @endif | ||||
| @if($video->danmakus->count() === 0) | ||||
|     <a href="{{ url(route("danmaku.construct.batch_import", ["video_bvid"=>$video->bvid])) }}" class="px-6 py-2 inline-block rounded-full bg-cyan-600 text-white">导入直播弹幕</a> | ||||
| @endif | ||||
| @endauth | ||||
| @include("common.footer") | ||||
| </body> | ||||
|   | ||||
| @@ -31,8 +31,10 @@ 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) { | ||||
| 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"); | ||||
| @@ -62,6 +64,12 @@ Route::prefix("/programs/construct")->middleware("auth:web")->group(function (Ro | ||||
|         $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"); | ||||
|     $router->post("/webauthn/", ["\\App\\Http\\Controllers\\UserWebAuthnController", "register_validate"])->name("user.webauthn.bind.submit"); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user