|
|
@@ -0,0 +1,147 @@
|
|
|
+<?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];
|
|
|
+ }
|
|
|
+}
|