视频保存

This commit is contained in:
duoaohui
2026-05-19 18:25:28 +08:00
parent 44c75c9c64
commit 3d7e64dfa9
6 changed files with 28 additions and 8 deletions

View File

@@ -107,17 +107,20 @@ func (c *OpenAICompatibleClient) Transcribe(ctx context.Context, cfg *dao.STTCon
} }
// 3) 构造请求 // 3) 构造请求
endpoint := strings.TrimRight(cfg.Provider.BaseURL, "/") + "/audio/transcriptions" endpoint := buildTranscriptionEndpoint(cfg.Provider.BaseURL)
if endpoint == "" {
return nil, fmt.Errorf("STT provider base_url is empty: provider=%s model=%s", cfg.Provider.ProviderCode, cfg.Model.ModelCode)
}
req, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint, body) req, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint, body)
if err != nil { if err != nil {
return nil, fmt.Errorf("构造 STT 请求失败: %w", err) return nil, fmt.Errorf("构造 STT 请求失败: endpoint=%s provider=%s model=%s: %w", endpoint, cfg.Provider.ProviderCode, cfg.Model.ModelCode, err)
} }
req.Header.Set("Authorization", "Bearer "+cfg.Key.APIKey) req.Header.Set("Authorization", "Bearer "+cfg.Key.APIKey)
req.Header.Set("Content-Type", writer.FormDataContentType()) req.Header.Set("Content-Type", writer.FormDataContentType())
resp, err := c.httpClient.Do(req) resp, err := c.httpClient.Do(req)
if err != nil { if err != nil {
return nil, fmt.Errorf("调用 STT 接口失败: %w", err) return nil, fmt.Errorf("调用 STT 接口失败: endpoint=%s provider=%s model=%s: %w", endpoint, cfg.Provider.ProviderCode, cfg.Model.ModelCode, err)
} }
defer resp.Body.Close() defer resp.Body.Close()
@@ -132,7 +135,7 @@ func (c *OpenAICompatibleClient) Transcribe(ctx context.Context, cfg *dao.STTCon
if len(preview) > 300 { if len(preview) > 300 {
preview = preview[:300] + "..." preview = preview[:300] + "..."
} }
return nil, fmt.Errorf("STT 接口返回 %d: %s", resp.StatusCode, preview) return nil, fmt.Errorf("STT 接口返回 %d: endpoint=%s provider=%s model=%s body=%s", resp.StatusCode, endpoint, cfg.Provider.ProviderCode, cfg.Model.ModelCode, preview)
} }
// 4) 解析 // 4) 解析
@@ -151,6 +154,17 @@ func (c *OpenAICompatibleClient) Transcribe(ctx context.Context, cfg *dao.STTCon
return &result, nil return &result, nil
} }
func buildTranscriptionEndpoint(baseURL string) string {
base := strings.TrimRight(strings.TrimSpace(baseURL), "/")
if base == "" {
return ""
}
if strings.HasSuffix(base, "/audio/transcriptions") {
return base
}
return base + "/audio/transcriptions"
}
// downloadAudio 从给定 URL 拉取音频内容,返回字节流 + 推断的文件名 // downloadAudio 从给定 URL 拉取音频内容,返回字节流 + 推断的文件名
// //
// 文件名仅作为 multipart 的 filename 参数,主要决定 Content-Type 推断; // 文件名仅作为 multipart 的 filename 参数,主要决定 Content-Type 推断;

View File

@@ -165,6 +165,12 @@ func (s *TranscribeService) runJob(transcriptID int64, rec meetingModel.MeetingR
"model_code": cfg.Model.ModelCode, "model_code": cfg.Model.ModelCode,
"key_id": cfg.Key.ID, "key_id": cfg.Key.ID,
}).Error }).Error
logs.Info(ctx, funcName, "已选择 STT 配置",
zap.Int64("transcript_id", transcriptID),
zap.String("provider_code", cfg.Provider.ProviderCode),
zap.String("model_code", cfg.Model.ModelCode),
zap.String("base_url", cfg.Provider.BaseURL),
zap.String("file_url", rec.FileURL))
result, err := s.sttClient.Transcribe(ctx, cfg, rec.FileURL, language) result, err := s.sttClient.Transcribe(ctx, cfg, rec.FileURL, language)
if err != nil { if err != nil {

View File

@@ -53,7 +53,7 @@ minio:
secret_key: "echochat_2026-1s32dswW@#" # 密钥(与 docker-compose 中的 MINIO_ROOT_PASSWORD 一致) secret_key: "echochat_2026-1s32dswW@#" # 密钥(与 docker-compose 中的 MINIO_ROOT_PASSWORD 一致)
bucket: "echochat" # 存储桶名称 bucket: "echochat" # 存储桶名称
use_ssl: false # 开发环境不使用 HTTPS use_ssl: false # 开发环境不使用 HTTPS
public_base_url: "http://yl.z64.cn/echochat" public_base_url: "https://yl.z64.cn/echochat"
# Node media-server 接入配置Phase 2e-2 Task 7 HTTPMediaOrchestrator # Node media-server 接入配置Phase 2e-2 Task 7 HTTPMediaOrchestrator
# 对应 media-server/.env 的 MEDIA_INTERNAL_TOKEN + HTTP_PORT默认 3300 # 对应 media-server/.env 的 MEDIA_INTERNAL_TOKEN + HTTP_PORT默认 3300

View File

@@ -47,7 +47,7 @@ minio:
secret_key: "echochat_2026-1s32dswW@#" secret_key: "echochat_2026-1s32dswW@#"
bucket: "echochat" bucket: "echochat"
use_ssl: false use_ssl: false
public_base_url: "http://yl.z64.cn/echochat" public_base_url: "https://yl.z64.cn/echochat"
# Node media-server 接入配置Phase 2e-2 Task 7 # Node media-server 接入配置Phase 2e-2 Task 7
media_server: media_server:

View File

@@ -18,7 +18,7 @@ REDIS_PASSWORD=echochat_2026-1s32dswW@#
MINIO_ROOT_USER=echochat MINIO_ROOT_USER=echochat
MINIO_ROOT_PASSWORD=echochat_2026-1s32dswW@# MINIO_ROOT_PASSWORD=echochat_2026-1s32dswW@#
MINIO_PUBLIC_BASE_URL=http://yl.z64.cn/echochat MINIO_PUBLIC_BASE_URL=https://yl.z64.cn/echochat
# ============================================================ # ============================================================
# Go backend # Go backend

View File

@@ -69,7 +69,7 @@ services:
ECHOCHAT_MINIO_ACCESS_KEY: ${MINIO_ROOT_USER:-echochat} ECHOCHAT_MINIO_ACCESS_KEY: ${MINIO_ROOT_USER:-echochat}
ECHOCHAT_MINIO_SECRET_KEY: ${MINIO_ROOT_PASSWORD:-echochat123456} ECHOCHAT_MINIO_SECRET_KEY: ${MINIO_ROOT_PASSWORD:-echochat123456}
ECHOCHAT_MEDIA_SERVER_INTERNAL_TOKEN: ${MEDIA_INTERNAL_TOKEN:-internal-token-22xqs} ECHOCHAT_MEDIA_SERVER_INTERNAL_TOKEN: ${MEDIA_INTERNAL_TOKEN:-internal-token-22xqs}
ECHOCHAT_MINIO_PUBLIC_BASE_URL: ${MINIO_PUBLIC_BASE_URL:-http://yl.z64.cn/echochat} ECHOCHAT_MINIO_PUBLIC_BASE_URL: ${MINIO_PUBLIC_BASE_URL:-https://yl.z64.cn/echochat}
# media-server 失败回调共享密钥:与 media-server 服务的 INTERNAL_WEBHOOK_SECRET 保持一致 # media-server 失败回调共享密钥:与 media-server 服务的 INTERNAL_WEBHOOK_SECRET 保持一致
# 未配置时 go-service 会拒绝所有 webhookForbidden录制失败状态将无法及时落 failed # 未配置时 go-service 会拒绝所有 webhookForbidden录制失败状态将无法及时落 failed
ECHOCHAT_MEETING_INTERNAL_WEBHOOK_SECRET: ${INTERNAL_WEBHOOK_SECRET:-internal-webhook-12xqs} ECHOCHAT_MEETING_INTERNAL_WEBHOOK_SECRET: ${INTERNAL_WEBHOOK_SECRET:-internal-webhook-12xqs}