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 | class VideoDanmakus extends Model | ||||||
| { | { | ||||||
|  |     protected $guarded = []; | ||||||
|     protected $table = "video_danmakus"; |     protected $table = "video_danmakus"; | ||||||
|     protected $dateFormat = 'U'; |     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 |     public function video(): \Illuminate\Database\Eloquent\Relations\BelongsTo | ||||||
|     { |     { | ||||||
|         return $this->belongsTo(Videos::class, "video_bvid", "bvid"); |         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", |     "license": "MIT", | ||||||
|     "require": { |     "require": { | ||||||
|         "php": "^7.3|^8.0", |         "php": "^7.3|^8.0", | ||||||
|  |         "ext-dom": "*", | ||||||
|         "ext-json": "*", |         "ext-json": "*", | ||||||
|         "ext-mbstring": "*", |         "ext-mbstring": "*", | ||||||
|         "fruitcake/laravel-cors": "^2.0", |         "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('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> |                         <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") |                         @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 |                         @endauth | ||||||
|                     </div> |                     </div> | ||||||
|                 </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('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> |             <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") |             @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 |             @endauth | ||||||
|         </div> |         </div> | ||||||
|     </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) | @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> |     <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 | @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 | @endauth | ||||||
| @include("common.footer") | @include("common.footer") | ||||||
| </body> | </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::get('/register', ["\\App\\Http\\Controllers\\UserController", "register_page"])->name("register"); | ||||||
| Route::post('/register', ["\\App\\Http\\Controllers\\UserController", "register"])->name("register.submit"); | Route::post('/register', ["\\App\\Http\\Controllers\\UserController", "register"])->name("register.submit"); | ||||||
| Route::get('/logout', ["\\App\\Http\\Controllers\\UserController", "logout"])->name("logout"); | 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('/', ["\\App\\Http\\Controllers\\ProgramConstructController","index"])->name("program.construct.list"); | ||||||
|         $router->get('/add', ["\\App\\Http\\Controllers\\ProgramConstructController","add"])->name("program.construct.add"); |         $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->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"); |         $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) { | 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/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"); |     $router->post("/webauthn/", ["\\App\\Http\\Controllers\\UserWebAuthnController", "register_validate"])->name("user.webauthn.bind.submit"); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user