mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-26 17:54:14 +00:00
## Summary - When `Comfy.Assets.UseAssetAPI` is enabled, `getAssetModelFolders()` only discovers folders containing assets. Empty folders (e.g. `text_encoders`, `vae`) were falsely flagged as invalid, showing "Invalid directory specified" on every missing model dialog. - Removes the `directory_invalid` concept entirely — the existing `!paths` check via `getFolderPaths()` already correctly validates all registered directories including empty ones, making `directory_invalid` redundant. ## Before <img width="1841" height="954" alt="Screenshot 2026-02-18 at 21 09 55" src="https://github.com/user-attachments/assets/09cf4f28-5175-4ff6-aa9d-916568c6d9b3" /> ## After <img width="1134" height="738" alt="Screenshot 2026-02-18 at 21 23 29" src="https://github.com/user-attachments/assets/578d2fa5-3fb8-401a-beee-0fd74667f08b" /> ## Test plan - [ ] Enable `Comfy.Assets.UseAssetAPI` setting - [ ] Open a template referencing models in empty folders (e.g. "Image Editing (New)") - [ ] Verify the missing models dialog shows download buttons instead of "Invalid directory specified" error - [ ] Disable `Comfy.Assets.UseAssetAPI` and verify behavior is unchanged Fixes #8583
178 lines
5.3 KiB
Vue
178 lines
5.3 KiB
Vue
<template>
|
|
<NoResultsPlaceholder
|
|
class="pb-0"
|
|
icon="pi pi-exclamation-circle"
|
|
:title="t('missingModelsDialog.missingModels')"
|
|
:message="t('missingModelsDialog.missingModelsMessage')"
|
|
/>
|
|
<div class="mb-4 flex flex-col gap-1">
|
|
<div class="flex gap-1">
|
|
<input
|
|
id="doNotAskAgain"
|
|
v-model="doNotAskAgain"
|
|
type="checkbox"
|
|
class="h-4 w-4 cursor-pointer"
|
|
/>
|
|
<label for="doNotAskAgain">{{
|
|
t('missingModelsDialog.doNotAskAgain')
|
|
}}</label>
|
|
</div>
|
|
<i18n-t
|
|
v-if="doNotAskAgain"
|
|
keypath="missingModelsDialog.reEnableInSettings"
|
|
tag="span"
|
|
class="text-sm text-muted-foreground ml-6"
|
|
>
|
|
<template #link>
|
|
<Button
|
|
variant="textonly"
|
|
class="underline cursor-pointer p-0 text-sm text-muted-foreground hover:bg-transparent"
|
|
@click="openShowMissingModelsSetting"
|
|
>
|
|
{{ t('missingModelsDialog.reEnableInSettingsLink') }}
|
|
</Button>
|
|
</template>
|
|
</i18n-t>
|
|
</div>
|
|
<ListBox :options="missingModels" class="comfy-missing-models">
|
|
<template #option="{ option }">
|
|
<Suspense v-if="isDesktop">
|
|
<ElectronFileDownload
|
|
:url="option.url"
|
|
:label="option.label"
|
|
:error="option.error"
|
|
/>
|
|
</Suspense>
|
|
<FileDownload
|
|
v-else
|
|
:url="option.url"
|
|
:label="option.label"
|
|
:error="option.error"
|
|
/>
|
|
</template>
|
|
</ListBox>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import ListBox from 'primevue/listbox'
|
|
import { computed, onBeforeUnmount, ref } from 'vue'
|
|
import { useI18n } from 'vue-i18n'
|
|
|
|
import Button from '@/components/ui/button/Button.vue'
|
|
import ElectronFileDownload from '@/components/common/ElectronFileDownload.vue'
|
|
import FileDownload from '@/components/common/FileDownload.vue'
|
|
import NoResultsPlaceholder from '@/components/common/NoResultsPlaceholder.vue'
|
|
import { isDesktop } from '@/platform/distribution/types'
|
|
import { useSettingStore } from '@/platform/settings/settingStore'
|
|
import { useSettingsDialog } from '@/platform/settings/composables/useSettingsDialog'
|
|
import { useDialogStore } from '@/stores/dialogStore'
|
|
|
|
// TODO: Read this from server internal API rather than hardcoding here
|
|
// as some installations may wish to use custom sources
|
|
const allowedSources = [
|
|
'https://civitai.com/',
|
|
'https://huggingface.co/',
|
|
'http://localhost:' // Included for testing usage only
|
|
]
|
|
const allowedSuffixes = ['.safetensors', '.sft']
|
|
// Models that fail above conditions but are still allowed
|
|
const whiteListedUrls = new Set([
|
|
'https://huggingface.co/stabilityai/stable-zero123/resolve/main/stable_zero123.ckpt',
|
|
'https://huggingface.co/TencentARC/T2I-Adapter/resolve/main/models/t2iadapter_depth_sd14v1.pth?download=true',
|
|
'https://github.com/xinntao/Real-ESRGAN/releases/download/v0.1.0/RealESRGAN_x4plus.pth'
|
|
])
|
|
|
|
interface ModelInfo {
|
|
name: string
|
|
directory: string
|
|
url: string
|
|
downloading?: boolean
|
|
completed?: boolean
|
|
progress?: number
|
|
error?: string
|
|
folder_path?: string
|
|
}
|
|
|
|
const props = defineProps<{
|
|
missingModels: ModelInfo[]
|
|
paths: Record<string, string[]>
|
|
}>()
|
|
|
|
const { t } = useI18n()
|
|
|
|
const doNotAskAgain = ref(false)
|
|
|
|
function openShowMissingModelsSetting() {
|
|
useDialogStore().closeDialog({ key: 'global-missing-models-warning' })
|
|
useSettingsDialog().show(undefined, 'Comfy.Workflow.ShowMissingModelsWarning')
|
|
}
|
|
|
|
const modelDownloads = ref<Record<string, ModelInfo>>({})
|
|
const missingModels = computed(() => {
|
|
return props.missingModels.map((model) => {
|
|
const paths = props.paths[model.directory]
|
|
if (!paths) {
|
|
return {
|
|
label: `${model.directory} / ${model.name}`,
|
|
url: model.url,
|
|
error: 'Invalid directory specified (does this require custom nodes?)'
|
|
}
|
|
}
|
|
const downloadInfo: ModelInfo = modelDownloads.value[model.name] ?? {
|
|
downloading: false,
|
|
completed: false,
|
|
progress: 0,
|
|
error: null,
|
|
name: model.name,
|
|
directory: model.directory,
|
|
url: model.url,
|
|
folder_path: paths[0]
|
|
}
|
|
modelDownloads.value[model.name] = downloadInfo
|
|
if (!whiteListedUrls.has(model.url)) {
|
|
if (!allowedSources.some((source) => model.url.startsWith(source))) {
|
|
return {
|
|
label: `${model.directory} / ${model.name}`,
|
|
url: model.url,
|
|
error: `Download not allowed from source '${model.url}', only allowed from '${allowedSources.join("', '")}'`
|
|
}
|
|
}
|
|
if (!allowedSuffixes.some((suffix) => model.name.endsWith(suffix))) {
|
|
return {
|
|
label: `${model.directory} / ${model.name}`,
|
|
url: model.url,
|
|
error: `Only allowed suffixes are: '${allowedSuffixes.join("', '")}'`
|
|
}
|
|
}
|
|
}
|
|
return {
|
|
url: model.url,
|
|
label: `${model.directory} / ${model.name}`,
|
|
downloading: downloadInfo.downloading,
|
|
completed: downloadInfo.completed,
|
|
progress: downloadInfo.progress,
|
|
error: downloadInfo.error,
|
|
name: model.name,
|
|
paths: paths,
|
|
folderPath: downloadInfo.folder_path
|
|
}
|
|
})
|
|
})
|
|
|
|
onBeforeUnmount(async () => {
|
|
if (doNotAskAgain.value) {
|
|
await useSettingStore().set(
|
|
'Comfy.Workflow.ShowMissingModelsWarning',
|
|
false
|
|
)
|
|
}
|
|
})
|
|
</script>
|
|
|
|
<style scoped>
|
|
.comfy-missing-models {
|
|
max-height: 300px;
|
|
overflow-y: auto;
|
|
}
|
|
</style>
|