diff --git a/backend/internal/service/channel.go b/backend/internal/service/channel.go index 154dd67..7f3000a 100644 --- a/backend/internal/service/channel.go +++ b/backend/internal/service/channel.go @@ -299,6 +299,13 @@ func normalizeRemoteModel(m upstream.RemoteModel) (ChannelModelSpec, error) { default: return spec, fmt.Errorf("未知类型 %q", spec.Kind) } + if spec.Duration != nil { + switch spec.Duration.Mode { + case "options", "range": + default: + return spec, errors.New("时长模式仅支持 options / range") + } + } return spec, nil } @@ -432,6 +439,9 @@ func (s *ChannelService) ImportModels(ctx context.Context, channelID string, spe if upID == "" { continue } + if spec.Duration != nil && spec.Duration.Mode != "options" && spec.Duration.Mode != "range" { + return fmt.Errorf("模型 %s:时长模式仅支持 options / range", upID) + } alias := strings.TrimSpace(spec.Alias) if alias == upID { return fmt.Errorf("「%s」别名不能与上游模型同名,留空即可", upID) @@ -501,9 +511,6 @@ func modelPatchFromSpec(spec ChannelModelSpec, existing *model.ModelConfig) map[ } if d := spec.Duration; d != nil { switch d.Mode { - case "fixed": - patch["durations"] = jsonArray([]string{fmt.Sprintf("%ds", d.Seconds)}) - patch["duration_min"], patch["duration_max"] = int64(0), int64(0) case "options": opts := make([]string, 0, len(d.Options)) for _, s := range d.Options { diff --git a/backend/internal/service/v1.go b/backend/internal/service/v1.go index 27f79db..63114d7 100644 --- a/backend/internal/service/v1.go +++ b/backend/internal/service/v1.go @@ -342,8 +342,9 @@ type V1ModelItem struct { RefAudioSeconds int64 `json:"refAudioSeconds,omitempty"` } -// V1ModelDur exposes a video model's duration capability: fixed=locked seconds, -// options=discrete list, range=continuous interval. +// V1ModelDur exposes a video model's duration capability: options=discrete +// list (single-choice models too — "fixed" is never emitted), range=continuous +// interval. type V1ModelDur struct { Mode string `json:"mode"` Seconds int64 `json:"seconds,omitempty"` @@ -433,20 +434,19 @@ func modelDurationSpec(item *model.ModelConfig) *V1ModelDur { return &V1ModelDur{Mode: "range", Min: int64(item.DurationMin), Max: int64(item.DurationMax)} } opts := repo.JSONStrings(item.Durations) - switch len(opts) { - case 0: + if len(opts) == 0 { return nil - case 1: - return &V1ModelDur{Mode: "fixed", Seconds: int64(parseDurationSeconds(opts[0]))} - default: - vals := make([]int64, 0, len(opts)) - for _, d := range opts { - if n := parseDurationSeconds(d); n > 0 { - vals = append(vals, int64(n)) - } - } - return &V1ModelDur{Mode: "options", Options: vals} } + vals := make([]int64, 0, len(opts)) + for _, d := range opts { + if n := parseDurationSeconds(d); n > 0 { + vals = append(vals, int64(n)) + } + } + if len(vals) == 0 { + return nil + } + return &V1ModelDur{Mode: "options", Options: vals} } // UserBalance — GET /v1/balance. Re-reads the user row so the number is live. diff --git a/backend/internal/upstream/client.go b/backend/internal/upstream/client.go index 2810fcf..7cbf2b6 100644 --- a/backend/internal/upstream/client.go +++ b/backend/internal/upstream/client.go @@ -102,7 +102,7 @@ type RemoteModel struct { // RemoteDuration mirrors the duration spec of a vivid model item. type RemoteDuration struct { - Mode string `json:"mode"` // fixed | options | range + Mode string `json:"mode"` // options | range Seconds int64 `json:"seconds,omitempty"` Min int64 `json:"min,omitempty"` Max int64 `json:"max,omitempty"` diff --git a/frontend/src/views/ChannelsView.vue b/frontend/src/views/ChannelsView.vue index 7617625..94663fa 100644 --- a/frontend/src/views/ChannelsView.vue +++ b/frontend/src/views/ChannelsView.vue @@ -126,8 +126,7 @@ function specText(s) { const parts = [] if (s.qualities?.length) parts.push(s.qualities.join('/')) if (s.duration) { - if (s.duration.mode === 'fixed') parts.push(`固定 ${s.duration.seconds}s`) - else if (s.duration.mode === 'range') parts.push(`${s.duration.min}-${s.duration.max}s`) + if (s.duration.mode === 'range') parts.push(`${s.duration.min}-${s.duration.max}s`) else if (s.duration.mode === 'options') parts.push((s.duration.options || []).map((d) => `${d}s`).join('/')) } const refs = []