Przeglądaj źródła

文生视频接口兼容参考音频及音频截断规则

lh 1 dzień temu
rodzic
commit
351f14f89b
1 zmienionych plików z 89 dodań i 24 usunięć
  1. 89 24
      app/Services/Anime/AnimeService.php

+ 89 - 24
app/Services/Anime/AnimeService.php

@@ -5422,7 +5422,7 @@ class AnimeService
 
 
         $model = getProp($data, 'model');
         $model = getProp($data, 'model');
         if (!$model) {
         if (!$model) {
-            $model = 'seed-2';
+            $model = 'baidu-doubao-seedance-2-0-260128';
         }
         }
         // 验证模型是否在可用模型表中
         // 验证模型是否在可用模型表中
         if (!DB::table('mp_video_models')->where('model', $model)->where('is_enabled', 1)->exists()) {
         if (!DB::table('mp_video_models')->where('model', $model)->where('is_enabled', 1)->exists()) {
@@ -5463,7 +5463,7 @@ class AnimeService
         }
         }
         $reference_videos = array_slice($reference_videos, 0, 3);
         $reference_videos = array_slice($reference_videos, 0, 3);
 
 
-        // 参考音频(支持数组或JSON字符串,最多3个)
+        // 参考音频:以传入的 reference_audios 为准,有值即视为启用,无值即关闭
         $reference_audios = getProp($data, 'reference_audios', []);
         $reference_audios = getProp($data, 'reference_audios', []);
         if (is_string($reference_audios)) {
         if (is_string($reference_audios)) {
             $reference_audios = json_decode($reference_audios, true) ?: [];
             $reference_audios = json_decode($reference_audios, true) ?: [];
@@ -5472,10 +5472,13 @@ class AnimeService
             Utils::throwError('20003:参考音频格式不正确');
             Utils::throwError('20003:参考音频格式不正确');
         }
         }
         $reference_audios = array_values(array_unique(array_filter($reference_audios)));
         $reference_audios = array_values(array_unique(array_filter($reference_audios)));
-        if (count($reference_audios) > 3) {
-            Utils::throwError('20003:参考音频最多选择3个');
+        $audioLimits = $this->getActAudioLimits($model);
+        if ($audioLimits) {
+            $reference_audios = $this->filterActReferenceAudios($reference_audios, $audioLimits);
+        } else {
+            // 非多模态参考音频模型:保留原有最多3段的限制
+            $reference_audios = array_slice($reference_audios, 0, 3);
         }
         }
-        $reference_audios = array_slice($reference_audios, 0, 3);
 
 
         // 余额预检:按预估时长计算所需积分,不足直接报错
         // 余额预检:按预估时长计算所需积分,不足直接报错
         $chargeDuration = (int)ceil((float)$video_duration);
         $chargeDuration = (int)ceil((float)$video_duration);
@@ -11830,53 +11833,115 @@ class AnimeService
             }
             }
         }
         }
 
 
-        $usedUrls = [];
-        $totalDuration = 0;
+        // 收集候选音频(同一音频可能被多个角色复用,只保留首次出现的URL用于筛选)
+        $orderedUrls = [];
+        foreach (array_keys($orderedNames) as $name) {
+            $timbre = $timbreMap[$name] ?? '';
+            if ($timbre !== '' && !in_array($timbre, $orderedUrls, true)) {
+                $orderedUrls[] = $timbre;
+            }
+        }
+        if (empty($orderedUrls)) {
+            return $result;
+        }
+
+        // 复用统一的筛选逻辑:单个音频时长 → 段数 → 总时长(与通用接口 generateVideo 共用)
+        $filtered = $this->filterActReferenceAudiosWithDetail($orderedUrls, $limits);
+        $result['total_duration'] = $filtered['total_duration'];
+
+        // 按角色在正文中的出现顺序回填结果,保证键顺序与提示词编号顺序一致
         foreach (array_keys($orderedNames) as $name) {
         foreach (array_keys($orderedNames) as $name) {
             $timbre = $timbreMap[$name] ?? '';
             $timbre = $timbreMap[$name] ?? '';
             if ($timbre === '') {
             if ($timbre === '') {
                 continue;
                 continue;
             }
             }
-
-            // 相同音频复用同一段素材,不重复计数
-            if (isset($usedUrls[$timbre])) {
+            if (isset($filtered['durations'][$timbre])) {
                 $result['allowed'][$name] = $timbre;
                 $result['allowed'][$name] = $timbre;
+            } elseif (isset($filtered['rejected'][$timbre])) {
+                $result['excluded'][$name] = $filtered['rejected'][$timbre];
+            }
+        }
+
+        if (!empty($result['excluded'])) {
+            dLog('anime')->info('参考音频超出模型限制,已截断', [
+                'model' => $model,
+                'limits' => $limits,
+                'allowed' => array_keys($result['allowed']),
+                'excluded' => $result['excluded'],
+                'total_duration' => $result['total_duration'],
+            ]);
+        }
+
+        return $result;
+    }
+
+    /**
+     * 按模型限制逐条筛选参考音频(片段转视频与通用文生视频共用的判断逻辑)
+     * 顺序:单个音频时长 → 段数上限 → 总时长上限;相同URL复用同一段素材,不重复计数
+     *
+     * @param array $referenceAudios 参考音频URL数组
+     * @param array $limits 限制,如 ['max_count'=>3,'max_single'=>15,'max_total'=>15]
+     * @return array ['allowed' => [url...], 'durations' => [url=>秒], 'rejected' => [url=>原因], 'total_duration' => 秒]
+     */
+    public function filterActReferenceAudiosWithDetail(array $referenceAudios, array $limits) {
+        $allowed = [];
+        $durations = [];
+        $rejected = [];
+        $totalDuration = 0;
+
+        foreach ($referenceAudios as $url) {
+            if (!is_string($url) || trim($url) === '' || isset($durations[$url]) || isset($rejected[$url])) {
                 continue;
                 continue;
             }
             }
 
 
-            $duration = $this->getRemoteAudioDurationSeconds($timbre);
+            $duration = $this->getRemoteAudioDurationSeconds($url);
 
 
             if ($duration > 0 && $duration > $limits['max_single']) {
             if ($duration > 0 && $duration > $limits['max_single']) {
-                $result['excluded'][$name] = '单个音频时长超限(' . $duration . 's > ' . $limits['max_single'] . 's)';
+                $rejected[$url] = '单个音频时长超限(' . $duration . 's > ' . $limits['max_single'] . 's)';
                 continue;
                 continue;
             }
             }
-            if (count($usedUrls) >= $limits['max_count']) {
-                $result['excluded'][$name] = '超过参考音频段数上限(' . $limits['max_count'] . '段)';
+            if (count($allowed) >= $limits['max_count']) {
+                $rejected[$url] = '超过参考音频段数上限(' . $limits['max_count'] . '段)';
                 continue;
                 continue;
             }
             }
             if ($duration > 0 && ($totalDuration + $duration) > $limits['max_total']) {
             if ($duration > 0 && ($totalDuration + $duration) > $limits['max_total']) {
-                $result['excluded'][$name] = '超过参考音频总时长上限(' . $limits['max_total'] . 's)';
+                $rejected[$url] = '超过参考音频总时长上限(' . $limits['max_total'] . 's)';
                 continue;
                 continue;
             }
             }
 
 
-            $usedUrls[$timbre] = true;
+            $allowed[] = $url;
+            $durations[$url] = max(0, $duration);
             $totalDuration += max(0, $duration);
             $totalDuration += max(0, $duration);
-            $result['allowed'][$name] = $timbre;
         }
         }
 
 
-        $result['total_duration'] = round($totalDuration, 2);
+        return [
+            'allowed' => $allowed,
+            'durations' => $durations,
+            'rejected' => $rejected,
+            'total_duration' => round($totalDuration, 2),
+        ];
+    }
+
+    /**
+     * 按模型限制过滤参考音频(通用接口用,仅返回通过的URL数组)
+     *
+     * @param array $referenceAudios 参考音频URL数组
+     * @param array $limits 限制
+     * @return array 过滤后的音频URL数组
+     */
+    public function filterActReferenceAudios(array $referenceAudios, array $limits) {
+        $filtered = $this->filterActReferenceAudiosWithDetail($referenceAudios, $limits);
 
 
-        if (!empty($result['excluded'])) {
+        if (!empty($filtered['rejected'])) {
             dLog('anime')->info('参考音频超出模型限制,已截断', [
             dLog('anime')->info('参考音频超出模型限制,已截断', [
-                'model' => $model,
                 'limits' => $limits,
                 'limits' => $limits,
-                'allowed' => array_keys($result['allowed']),
-                'excluded' => $result['excluded'],
-                'total_duration' => $result['total_duration'],
+                'allowed' => $filtered['allowed'],
+                'rejected' => $filtered['rejected'],
+                'total_duration' => $filtered['total_duration'],
             ]);
             ]);
         }
         }
 
 
-        return $result;
+        return $filtered['allowed'];
     }
     }
 
 
     /**
     /**