lh 1 месяц назад
Родитель
Сommit
9c8abda980

+ 50 - 8
app/Services/AIGeneration/AIVideoGenerationService.php

@@ -846,26 +846,28 @@ class AIVideoGenerationService
 
     /**
      * 将 Seedance 2.0 参考视频上传为百度AI网关素材
-     * 上传失败的视频直接跳过,不阻断整个任务
+     * 失败处理与参考图一致:删除参考视频的同时清理提示词中的<视频N>标记
      *
      * @param array $referenceVideos 参考视频URL数组
+     * @param string &$fullPrompt 完整提示词(引用传递,用于删除失败视频的标记)
      * @return array 返回素材ID数组(asset://格式)
      */
-    public function processReferenceVideosToSeedance20Assets(array $referenceVideos): array
+    public function processReferenceVideosToSeedance20Assets(array $referenceVideos, string &$fullPrompt): array
     {
-        return $this->processReferenceMediaToSeedance20Assets($referenceVideos, MpAsset::TYPE_VIDEO, '参考视频');
+        return $this->processReferenceMediaToSeedance20Assets($referenceVideos, MpAsset::TYPE_VIDEO, '参考视频', '视频', $fullPrompt);
     }
 
     /**
      * 将 Seedance 2.0 参考音频上传为百度AI网关素材
-     * 上传失败的音频直接跳过,不阻断整个任务
+     * 失败处理与参考图一致:删除参考音频的同时清理提示词中的<音频N>标记
      *
      * @param array $referenceAudios 参考音频URL数组
+     * @param string &$fullPrompt 完整提示词(引用传递,用于删除失败音频的标记)
      * @return array 返回素材ID数组(asset://格式)
      */
-    public function processReferenceAudiosToSeedance20Assets(array $referenceAudios): array
+    public function processReferenceAudiosToSeedance20Assets(array $referenceAudios, string &$fullPrompt): array
     {
-        return $this->processReferenceMediaToSeedance20Assets($referenceAudios, MpAsset::TYPE_AUDIO, '参考音频');
+        return $this->processReferenceMediaToSeedance20Assets($referenceAudios, MpAsset::TYPE_AUDIO, '参考音频', '音频', $fullPrompt);
     }
 
     /**
@@ -874,11 +876,14 @@ class AIVideoGenerationService
      * @param array $urls 参考素材URL数组
      * @param string $assetType 素材类型(Video/Audio)
      * @param string $label 素材中文名(用于日志和素材命名)
+     * @param string $tagName 提示词标记名(视频/音频),用于 <{tagName}N> 标记的清理
+     * @param string &$fullPrompt 完整提示词(引用传递,用于删除失败素材的标记)
      * @return array 返回素材ID数组(asset://格式)
      */
-    private function processReferenceMediaToSeedance20Assets(array $urls, string $assetType, string $label): array
+    private function processReferenceMediaToSeedance20Assets(array $urls, string $assetType, string $label, string $tagName, string &$fullPrompt): array
     {
         $assetIds = [];
+        $failedIndexes = []; // 记录失败的素材序号
 
         foreach ($urls as $index => $url) {
             $mediaIndex = $index + 1; // 序号从1开始
@@ -897,6 +902,7 @@ class AIVideoGenerationService
                         'index' => $mediaIndex,
                         'error' => $createResult['msg']
                     ]);
+                    $failedIndexes[] = $mediaIndex;
                     continue;
                 }
 
@@ -911,10 +917,46 @@ class AIVideoGenerationService
                     'index' => $mediaIndex,
                     'error' => $e->getMessage()
                 ]);
+                $failedIndexes[] = $mediaIndex;
             }
         }
 
-        dLog('generate')->info("Seedance 2.0{$label}转素材完成,成功数量: " . count($assetIds));
+        // 处理失败的素材:从 fullPrompt 中删除对应的标记并调整序号(与参考图处理一致)
+        if (!empty($failedIndexes)) {
+            dLog('generate')->info("开始处理Seedance 2.0失败{$label}的提示词调整,失败序号: " . implode(',', $failedIndexes));
+
+            // 按序号从大到小排序,避免删除时影响后续序号
+            rsort($failedIndexes);
+
+            foreach ($failedIndexes as $failedIndex) {
+                // 删除失败素材的标记,例如 <视频1>、<音频2> 等
+                $pattern = "/<{$tagName}{$failedIndex}>/u";
+                $fullPrompt = preg_replace($pattern, '', $fullPrompt);
+                dLog('generate')->info("已从提示词中删除 <{$tagName}{$failedIndex}>");
+            }
+
+            // 重新调整剩余素材的序号
+            $needAdjust = false;
+            foreach ($failedIndexes as $failedIndex) {
+                $needAdjust = true;
+                // 将所有大于失败序号的素材标记序号减1
+                for ($i = $failedIndex + 1; $i <= count($urls); $i++) {
+                    $oldPattern = "/<{$tagName}{$i}>/u";
+                    $newTag = "<{$tagName}" . ($i - 1) . ">";
+                    $fullPrompt = preg_replace($oldPattern, $newTag, $fullPrompt);
+                }
+            }
+
+            if ($needAdjust) {
+                dLog('generate')->info('Seedance 2.0提示词' . $label . '序号调整完成');
+                logDB('generate', 'info', 'Seedance2.0提示词调整', [
+                    'failed_indexes' => $failedIndexes,
+                    'adjusted_prompt' => $fullPrompt
+                ]);
+            }
+        }
+
+        dLog('generate')->info("Seedance 2.0{$label}转素材完成,成功数量: " . count($assetIds) . ',失败数量: ' . count($failedIndexes));
 
         // 格式化为可直接在API接口中使用的数组
         foreach ($assetIds as &$assetId) {

+ 7 - 3
app/Services/Anime/AnimeService.php

@@ -5263,22 +5263,26 @@ class AnimeService
             if ($reference_images) {
                 // 参考图上传为百度网关素材
                 $reference_image_assets = $this->aiVideoGenerationService->processReferenceImagesToSeedance20Assets($reference_images, $videoParams['prompt']);
-                $videoParams['content'][0]['text'] = $videoParams['prompt'];
                 $videoParams['reference_image_assets'] = $reference_image_assets;
             }
 
             // 参考视频上传为百度网关素材
             if ($reference_videos) {
-                $reference_video_assets = $this->aiVideoGenerationService->processReferenceVideosToSeedance20Assets($reference_videos);
+                $reference_video_assets = $this->aiVideoGenerationService->processReferenceVideosToSeedance20Assets($reference_videos, $videoParams['prompt']);
                 $videoParams['reference_video_assets'] = $reference_video_assets;
             }
 
             // 参考音频上传为百度网关素材
             if ($reference_audios) {
-                $reference_audio_assets = $this->aiVideoGenerationService->processReferenceAudiosToSeedance20Assets($reference_audios);
+                $reference_audio_assets = $this->aiVideoGenerationService->processReferenceAudiosToSeedance20Assets($reference_audios, $videoParams['prompt']);
                 $videoParams['reference_audio_assets'] = $reference_audio_assets;
             }
 
+            // 参考素材上传失败时可能清理提示词中的素材标记,需要同步回content文本
+            if ($reference_images || $reference_videos || $reference_audios) {
+                $videoParams['content'][0]['text'] = $videoParams['prompt'];
+            }
+
             $task = $this->aiVideoGenerationService->createSeedance20Task($videoParams);
         } else {
             // 官方Seedance模型:兼容参考视频和参考音频(content直接透传公网URL)