You've already forked lubo_comment_query
							
							支持通过命令行解析出节目
This commit is contained in:
		
							
								
								
									
										176
									
								
								app/Console/Commands/CreateProgramsFromComments.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										176
									
								
								app/Console/Commands/CreateProgramsFromComments.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,176 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace App\Console\Commands; | ||||
| 
 | ||||
| use App\Models\Programs; | ||||
| use App\Models\ProgramVideos; | ||||
| use App\Models\VideoComments; | ||||
| use App\Models\Videos; | ||||
| use App\Models\VideoParts; | ||||
| use App\Util\ProgramVideoUtil; | ||||
| use Illuminate\Console\Command; | ||||
| use Illuminate\Database\QueryException; | ||||
| use Illuminate\Support\Facades\DB; | ||||
| 
 | ||||
| class CreateProgramsFromComments extends Command | ||||
| { | ||||
|     protected $signature = 'programs:create-from-comments | ||||
|                            {--dry-run : 仅显示结果,不实际创建} | ||||
|                            {--limit=10 : 处理视频数量限制}'; | ||||
| 
 | ||||
|     protected $description = '查询没有关联 programs 的视频,基于置顶的评论自动创建 programs'; | ||||
| 
 | ||||
|     public function handle() | ||||
|     { | ||||
|         $dryRun = $this->option('dry-run'); | ||||
|         $limit = (int) $this->option('limit'); | ||||
| 
 | ||||
|         $this->info('开始查询没有关联 programs 的视频...'); | ||||
| 
 | ||||
|         // 查询没有关联 programs 的视频,且有置顶的评论
 | ||||
|         $videos = Videos::query() | ||||
|             ->whereDoesntHave('program_pivots') | ||||
|             ->whereHas('comments', function ($query) { | ||||
|                 $query->where('is_top', 1); | ||||
|             }) | ||||
|             ->with(['comments' => function ($query) { | ||||
|                 $query->where('is_top', 1)->orderBy('created_at'); | ||||
|             }]) | ||||
|             ->limit($limit) | ||||
|             ->get(); | ||||
| 
 | ||||
|         if ($videos->isEmpty()) { | ||||
|             $this->info('没有找到符合条件的视频。'); | ||||
|             return 0; | ||||
|         } | ||||
| 
 | ||||
|         $this->info("找到 {$videos->count()} 个视频,开始处理..."); | ||||
| 
 | ||||
|         $successCount = 0; | ||||
|         $errorCount = 0; | ||||
| 
 | ||||
|         foreach ($videos as $video) { | ||||
|             $this->line("处理视频: {$video->bvid} - {$video->title}"); | ||||
| 
 | ||||
|             $topComment = $video->comments->first(); | ||||
|             if (!$topComment) { | ||||
|                 $this->warn("  跳过: 没有找到置顶的评论"); | ||||
|                 continue; | ||||
|             } | ||||
| 
 | ||||
|             $this->line("  评论内容: " . mb_substr($topComment->content, 0, 100) . '...'); | ||||
| 
 | ||||
|             if ($dryRun) { | ||||
|                 $this->info("  [DryRun] 将会基于此评论创建 programs"); | ||||
|                 $successCount++; | ||||
|                 continue; | ||||
|             } | ||||
| 
 | ||||
|             try { | ||||
|                 $result = $this->createProgramsFromComment($video->bvid, $topComment->content); | ||||
|                 if ($result['success']) { | ||||
|                     $this->info("  ✅ 成功创建 {$result['count']} 个 programs"); | ||||
|                     $successCount++; | ||||
|                 } else { | ||||
|                     $this->error("  ❌ 创建失败: {$result['error']}"); | ||||
|                     $errorCount++; | ||||
|                 } | ||||
|             } catch (\Exception $e) { | ||||
|                 $this->error("  ❌ 处理异常: " . $e->getMessage()); | ||||
|                 $errorCount++; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         $this->info("\n处理完成:"); | ||||
|         $this->info("成功: {$successCount} 个视频"); | ||||
|         if ($errorCount > 0) { | ||||
|             $this->warn("失败: {$errorCount} 个视频"); | ||||
|         } | ||||
| 
 | ||||
|         return 0; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 基于评论内容创建 programs | ||||
|      * 复用 ProgramConstructController::batch_create 的逻辑 | ||||
|      */ | ||||
|     private function createProgramsFromComment(string $bvid, string $content): array | ||||
|     { | ||||
|         // 检查是否已有关联的 programs
 | ||||
|         $count = ProgramVideos::query()->where("video_bvid", "=", $bvid)->count(); | ||||
|         if ($count > 0) { | ||||
|             return [ | ||||
|                 'success' => false, | ||||
|                 'error' => "该BVID下已有{$count}个节目关联" | ||||
|             ]; | ||||
|         } | ||||
| 
 | ||||
|         // 使用和 batch_create 相同的正则表达式
 | ||||
|         $regex = "/^(p(?P<part>\d{1,2})[-# _:,)]+)?(?P<time>(\d{1,2}[::])?\d{1,3}[::]\d{1,2})[\s-]*(?P<content>.+)$/ui"; | ||||
|         $video_parts = VideoParts::query()->where("bvid", "=", $bvid)->get(); | ||||
| 
 | ||||
|         $createdCount = 0; | ||||
| 
 | ||||
|         DB::beginTransaction(); | ||||
|         try { | ||||
|             foreach (explode("\n", $content) as $line) { | ||||
|                 $line = trim($line); | ||||
|                 if (empty($line)) { | ||||
|                     continue; | ||||
|                 } | ||||
| 
 | ||||
|                 $match = []; | ||||
|                 $match_count = preg_match($regex, $line, $match); | ||||
|                 if ($match_count === 0) { | ||||
|                     $this->line("    跳过行: {$line}"); | ||||
|                     continue; | ||||
|                 } | ||||
| 
 | ||||
|                 $time = $match["time"]; | ||||
|                 $time = str_replace(":", ":", $time); | ||||
|                 while (substr_count($time, ":") < 2) { | ||||
|                     $time = "0:" . $time; | ||||
|                 } | ||||
| 
 | ||||
|                 $program = new Programs(); | ||||
|                 $program->name = trim($match["content"]); | ||||
|                 $program->save(); | ||||
| 
 | ||||
|                 $video_pivot = new ProgramVideos(); | ||||
|                 $video_pivot->video_bvid = $bvid; | ||||
|                 $video_pivot->start_part = empty($match["part"]) ? 1 : $match["part"]; | ||||
|                 $video_pivot->start_time = $time; | ||||
|                 $video_pivot->program_id = $program->id; | ||||
|                 $video_pivot->save(); | ||||
| 
 | ||||
|                 $createdCount++; | ||||
|                 $this->line("    创建: {$program->name} - P{$video_pivot->start_part} {$time}"); | ||||
|             } | ||||
| 
 | ||||
|             if ($createdCount > 0) { | ||||
|                 DB::commit(); | ||||
| 
 | ||||
|                 // 修复创建时间
 | ||||
|                 ProgramVideoUtil::fix_created_at_by_part_info($bvid, true); | ||||
| 
 | ||||
|                 return [ | ||||
|                     'success' => true, | ||||
|                     'count' => $createdCount | ||||
|                 ]; | ||||
|             } else { | ||||
|                 DB::rollBack(); | ||||
|                 return [ | ||||
|                     'success' => false, | ||||
|                     'error' => '没有解析出有效的节目信息' | ||||
|                 ]; | ||||
|             } | ||||
| 
 | ||||
|         } catch (QueryException $e) { | ||||
|             DB::rollBack(); | ||||
|             return [ | ||||
|                 'success' => false, | ||||
|                 'error' => $e->getMessage() | ||||
|             ]; | ||||
|         } | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user