| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147 |
- <?php
- namespace App\Libs;
- use Illuminate\Support\Facades\DB;
- /**
- * act_show_content 处理:把动漫画风提示词插入每个「画面:[…]」块
- *
- * 规则:
- * 1. 取 mp_animes.art_style_type,查 mp_art_styles(is_deleted=0)的 prompt;
- * 2. prompt 非空时,插入到每个「画面:」后第一个闭合 ] 之前,用中文逗号分隔;
- * 3. 找不到闭合 ] 的块、已包含该画风提示词的块跳过。
- *
- * 受 config('anime.act_show_art_style_enabled') 总开关控制,默认关闭;
- * 只处理 act_show_content 字段,不修改 act_content。
- */
- class ActShowContent
- {
- /** 画风提示词缓存(anime_id => prompt),同一次请求内避免重复查库 */
- private static $stylePromptCache = [];
- /**
- * 把画风提示词插入 act_show_content 中每个「画面:[…]」块
- *
- * @param string $content act_show_content 内容
- * @param int $animeId 所属动漫ID
- * @return string 处理后的内容(开关关闭、无画风提示词时原样返回)
- */
- public static function insertArtStylePrompt($content, $animeId)
- {
- if (!config('anime.act_show_art_style_enabled', false)) {
- return $content;
- }
- if (!is_string($content) || $content === '' || strpos($content, '画面:') === false) {
- return $content;
- }
- $prompt = self::getArtStylePrompt($animeId);
- if ($prompt === '') {
- return $content;
- }
- $huamianLen = strlen('画面:');
- $insertPositions = [];
- $skipped = 0;
- $offset = 0;
- while (($pos = strpos($content, '画面:', $offset)) !== false) {
- $searchStart = $pos + $huamianLen;
- $closePos = strpos($content, ']', $searchStart);
- if ($closePos === false) {
- // 该「画面:」块找不到闭合 ],跳过
- $skipped++;
- break;
- }
- // 闭合 ] 之前已经出现下一个「画面:」,说明当前块没有闭合 ],跳过当前块
- $nextPos = strpos($content, '画面:', $searchStart);
- if ($nextPos !== false && $nextPos < $closePos) {
- $skipped++;
- $offset = $nextPos;
- continue;
- }
- $inner = substr($content, $searchStart, $closePos - $searchStart);
- // 画面块内没有 [,] 或已经跨到其它字段行,说明不是标准的「画面:[…]」,跳过
- if (strpos($inner, '[') === false
- || preg_match('/(^|\n)\s*(运镜|配音台词|背景音效|出场角色|场景|出场道具|【(?:镜头|分镜))/u', $inner)) {
- $skipped++;
- $offset = $closePos + 1;
- continue;
- }
- if (strpos($inner, $prompt) !== false) {
- // 已包含画风提示词,避免重复插入
- $skipped++;
- } else {
- $insertPositions[] = [
- 'pos' => $closePos,
- 'needComma' => !preg_match('/[,,、]\s*$/u', $inner),
- ];
- }
- $offset = $closePos + 1;
- }
- if ($insertPositions) {
- // 从后往前插入,避免前面的插入影响后面的位置
- foreach (array_reverse($insertPositions) as $item) {
- $insertText = ($item['needComma'] ? ',' : '') . $prompt;
- $content = substr($content, 0, $item['pos']) . $insertText . substr($content, $item['pos']);
- }
- dLog('anime')->debug('act_show_content 插入画风提示词', [
- 'anime_id' => (int)$animeId,
- 'inserted' => count($insertPositions),
- 'skipped' => $skipped,
- ]);
- } elseif ($skipped > 0) {
- dLog('anime')->warning('act_show_content 未找到可插入的「画面:[…]」块', [
- 'anime_id' => (int)$animeId,
- 'skipped' => $skipped,
- ]);
- }
- return $content;
- }
- /**
- * 按动漫ID取画风提示词(mp_animes.art_style_type -> mp_art_styles.prompt)
- *
- * @param int $animeId
- * @return string 取不到时返回空字符串
- */
- private static function getArtStylePrompt($animeId)
- {
- $animeId = (int)$animeId;
- if ($animeId <= 0) {
- return '';
- }
- if (!array_key_exists($animeId, self::$stylePromptCache)) {
- $prompt = '';
- $anime = DB::table('mp_animes')->where('id', $animeId)->first(['art_style_type']);
- $artStyleType = $anime ? trim((string)$anime->art_style_type) : '';
- if ($artStyleType !== '') {
- $styleRow = DB::table('mp_art_styles')
- ->where('art_style', $artStyleType)
- ->where('is_deleted', 0)
- ->first(['prompt']);
- $prompt = $styleRow ? trim((string)$styleRow->prompt) : '';
- }
- self::$stylePromptCache[$animeId] = $prompt;
- }
- return self::$stylePromptCache[$animeId];
- }
- }
|