diff --git a/frontend/src/components/canvas/CanvasNodeView.vue b/frontend/src/components/canvas/CanvasNodeView.vue
index e151fe1..801149d 100644
--- a/frontend/src/components/canvas/CanvasNodeView.vue
+++ b/frontend/src/components/canvas/CanvasNodeView.vue
@@ -24,7 +24,7 @@ const editing = ref(false)
let imgController = null
async function loadSrc() {
- if (props.node.type !== 'image' || !props.node.src) return
+ if (!['image', 'video'].includes(props.node.type) || !props.node.src) return
imgController?.abort()
imgController = new AbortController()
imgLoaded.value = false
@@ -79,6 +79,19 @@ function onImgLoad(e) {
})
}
}
+function onVidMeta(e) {
+ imgLoaded.value = true
+ imgBroken.value = false
+ const v = e.target
+ if (v.videoWidth && v.videoHeight) {
+ const long = Math.max(v.videoWidth, v.videoHeight)
+ const k = 512 / long
+ emit('imgload', {
+ w: Math.round(v.videoWidth * k),
+ h: Math.round(v.videoHeight * k),
+ })
+ }
+}
function startEdit() {
if (props.node.type !== 'note') return
@@ -164,6 +177,23 @@ defineExpose({ startEdit, commitNote })
runningGens.value > 0)
const genMsg = ref('')
-const imageModels = computed(() =>
- (allModels.value || []).filter((m) => m.enabled !== false && m.type === 'image'),
+const kindModels = computed(() =>
+ (allModels.value || []).filter((m) => m.enabled !== false && m.type === gen.kind),
)
-const model = computed(() => imageModels.value.find((m) => m.id === gen.modelId) || null)
+const model = computed(() => kindModels.value.find((m) => m.id === gen.modelId) || null)
const ratios = computed(() => model.value?.ratios?.length ? model.value.ratios : ['1:1'])
const resolutions = computed(() => model.value?.resolutions?.length
? model.value.resolutions
: Object.keys(model.value?.prices || {}))
+const durations = computed(() => model.value?.durations?.length ? model.value.durations : ['5s'])
const maxRefs = computed(() => Math.min(8, Math.max(
Number(model.value?.max_reference_images || 0), model.value?.image_to_image ? 1 : 0,
)))
-// 换模型时重置参数默认值(和画图台一致)
+// 换类型/模型时重置参数默认值(和画图台一致)
+watch(() => gen.kind, () => {
+ const first = kindModels.value[0]
+ gen.modelId = first?.id || ''
+})
watch(model, (m) => {
if (!m) return
gen.ratio = m.ratios?.[0] || '1:1'
const res = m.resolutions?.length ? m.resolutions : Object.keys(m.prices || {})
gen.resolution = res[0] || '1K'
+ gen.duration = m.durations?.[0] || '5s'
})
const selectedNotes = computed(() =>
selectionIds.value
@@ -551,7 +557,7 @@ async function generate() {
}
genMsg.value = ''
- const n = Math.max(1, Math.min(4, Number(gen.count) || 1))
+ const n = gen.kind === 'image' ? Math.max(1, Math.min(4, Number(gen.count) || 1)) : 1
const size = ratioToSize(gen.ratio || ratios.value[0])
const sources = [...selectedNotes.value]
// 落点:参与源节点的包围盒右侧;无源则视口中心。多张时横向排开。
@@ -599,6 +605,7 @@ async function runGeneration(node, prompt) {
model: gen.modelId, prompt, ratio: gen.ratio || ratios.value[0],
resolution: gen.resolution || resolutions.value[0] || '1K',
}
+ if (gen.kind === 'video') payload.duration = gen.duration || durations.value[0]
if (refImages.value.length) {
payload.reference_images = refImages.value.map((r) => r.dataUrl.replace(/^data:[^,]*,/, ''))
}
@@ -606,7 +613,7 @@ async function runGeneration(node, prompt) {
opts.headers['Idempotency-Key'] = node.id
const r = await api('/generate', opts)
if (r.ok && r.data?.url) {
- node.type = 'image'
+ node.type = gen.kind === 'video' ? 'video' : 'image'
node.src = r.data.url.replace(/^\/images\//, '')
node.prompt = prompt
node.model = model.value?.alias || model.value?.name || model.value?.id
@@ -746,7 +753,7 @@ onMounted(async () => {
document.addEventListener('visibilitychange', onVisibility)
const mm = await api('/managed-models')
allModels.value = mm.data?.data || []
- const first = imageModels.value[0]
+ const first = kindModels.value[0]
if (first) {
gen.modelId = first.id
gen.ratio = (first.ratios && first.ratios[0]) || '1:1'
@@ -874,44 +881,54 @@ const saveLabel = computed(() => ({
placeholder="描述你要生成的画面…(留空则使用选中的笔记)"
/>
+
+
-
⚠ 还没有可用的图像模型,请到管理后台添加
-
-
-
-
-
+
+
+
+ ⚠ 还没有可用的{{ gen.kind === 'video' ? '视频' : '图像' }}模型