9 Commits

Author SHA1 Message Date
Hayden
37be9a0b0d prepare release 2.3.4 2025-02-18 16:01:49 +08:00
Hayden
fcea052dde fix: resolve path (#132) 2025-02-10 17:00:08 +08:00
Hayden
9e95e7bd74 style: optimize style (#131) 2025-02-10 16:42:53 +08:00
Hayden
7e58d0a82d fix(setting): no modified value saved (#130)
* fix: save setting value

* prepare release 2.3.3
2025-02-10 13:51:45 +08:00
Hayden
55a4eff01b prepare releas 2.3.2 2025-02-10 12:42:29 +08:00
Hayden
45cf18299f feat: optimize resize card size (#129) 2025-02-10 12:41:00 +08:00
Hayden
c7898c47f1 fix: unpack folder_names_and_paths error (#128) 2025-02-10 10:59:36 +08:00
Hayden
17ab373b9c fix: change model size type to float (#126) 2025-02-06 12:02:00 +08:00
boeto
f6368fe20b fix: model preview path (#120) 2025-02-04 20:27:00 +08:00
8 changed files with 224 additions and 83 deletions

View File

@@ -61,7 +61,7 @@ class TaskContent:
description: str
downloadPlatform: str
downloadUrl: str
sizeBytes: int
sizeBytes: float
hashes: Optional[dict[str, str]] = None
def __init__(self, **kwargs):
@@ -71,7 +71,7 @@ class TaskContent:
self.description = kwargs.get("description", None)
self.downloadPlatform = kwargs.get("downloadPlatform", None)
self.downloadUrl = kwargs.get("downloadUrl", None)
self.sizeBytes = int(kwargs.get("sizeBytes", 0))
self.sizeBytes = float(kwargs.get("sizeBytes", 0))
self.hashes = kwargs.get("hashes", None)
def to_dict(self):
@@ -382,7 +382,7 @@ async def download_model_file(
# When parsing model information from HuggingFace API,
# the file size was not found and needs to be obtained from the response header.
if total_size == 0:
total_size = int(response.headers.get("content-length", 0))
total_size = float(response.headers.get("content-length", 0))
task_content.sizeBytes = total_size
task_status.totalSize = total_size
set_task_content(task_id, task_content)

View File

@@ -386,7 +386,7 @@ class Information:
model_base_paths = utils.resolve_model_base_paths()
for model_type in model_base_paths:
folders, extensions = folder_paths.folder_names_and_paths[model_type]
folders, *others = folder_paths.folder_names_and_paths[model_type]
for path_index, base_path in enumerate(folders):
files = utils.recursive_search_files(base_path, request)

View File

@@ -117,10 +117,14 @@ class ModelManager:
result = []
include_hidden_files = utils.get_setting_value(request, "scan.include_hidden_files", False)
folders, extensions = folder_paths.folder_names_and_paths[folder]
folders, *others = folder_paths.folder_names_and_paths[folder]
def get_file_info(entry: os.DirEntry[str], base_path: str, path_index: int):
fullname = entry.path.replace(base_path, "")
prefix_path = utils.normalize_path(base_path)
if not prefix_path.endswith("/"):
prefix_path = f"{prefix_path}/"
fullname = utils.normalize_path(entry.path).replace(prefix_path, "")
basename = os.path.splitext(fullname)[0]
extension = os.path.splitext(fullname)[1]

View File

@@ -1,7 +1,7 @@
[project]
name = "comfyui-model-manager"
description = "Manage models: browsing, download and delete."
version = "2.3.1"
version = "2.3.4"
license = { file = "LICENSE" }
dependencies = ["markdownify"]

View File

@@ -20,15 +20,18 @@
<div class="flex items-center justify-between gap-4 overflow-hidden">
<ResponseSelect
class="flex-1"
v-model="currentType"
:items="typeOptions"
></ResponseSelect>
<ResponseSelect
class="flex-1"
v-model="sortOrder"
:items="sortOrderOptions"
></ResponseSelect>
<ResponseSelect
v-model="currentCardSize"
class="flex-1"
v-model="cardSizeFlag"
:items="cardSizeOptions"
></ResponseSelect>
</div>
@@ -77,7 +80,14 @@ import { genModelKey } from 'utils/model'
import { computed, ref } from 'vue'
import { useI18n } from 'vue-i18n'
const { isMobile, gutter, cardSize } = useConfig()
const {
isMobile,
gutter,
cardSize,
cardSizeMap,
cardSizeFlag,
dialog: settings,
} = useConfig()
const { data, folders } = useModels()
const { t } = useI18n()
@@ -90,7 +100,8 @@ const { $lg: $content_lg } = useContainerQueries(contentContainer)
const searchContent = ref<string>()
const currentType = ref('all')
const allType = 'All'
const currentType = ref(allType)
const typeOptions = computed(() => {
const excludeScanTypes = app.ui?.settings.getSettingValue<string>(
configSetting.excludeScanTypes,
@@ -101,7 +112,7 @@ const typeOptions = computed(() => {
.map((type) => type.trim())
.filter(Boolean) ?? []
return [
'all',
allType,
...Object.keys(folders.value).filter(
(folder) => !customBlackList.includes(folder),
),
@@ -156,7 +167,7 @@ const list = computed(() => {
const mergedList = Object.values(data.value).flat()
const filterList = mergedList.filter((model) => {
const showAllModel = currentType.value === 'all'
const showAllModel = currentType.value === allType
const matchType = showAllModel || model.type === currentType.value
const matchName = model.fullname
@@ -198,48 +209,24 @@ const contentStyle = computed(() => ({
paddingRight: `1rem`,
}))
const currentCardSize = computed({
get: () => {
const options = cardSizeOptions.value.map((item) => item.value)
const current = [cardSize.value.width, cardSize.value.height].join('x')
if (options.includes(current)) {
return current
}
return 'custom'
},
set: (val) => {
if (val === 'custom') {
app.ui?.settings.show(t('size.customTip'))
return
}
const [width, height] = val.split('x')
app.ui?.settings.setSettingValue(
'ModelManager.UI.CardWidth',
parseInt(width),
)
app.ui?.settings.setSettingValue(
'ModelManager.UI.CardHeight',
parseInt(height),
)
},
})
const cardSizeOptions = computed(() => {
const defineOptions = {
extraLarge: '240x320',
large: '180x240',
medium: '120x160',
small: '80x120',
custom: 'custom',
const customSize = 'size.custom'
const customOptionMap = {
...cardSizeMap.value,
[customSize]: 'custom',
}
return Object.entries(defineOptions).map(([key, value]) => {
return Object.keys(customOptionMap).map((key) => {
return {
label: t(`size.${key}`),
value,
command() {
currentCardSize.value = value
label: t(key),
value: key,
command: () => {
if (key === customSize) {
settings.showCardSizeSetting()
} else {
cardSizeFlag.value = key
}
},
}
})

View File

@@ -0,0 +1,110 @@
<template>
<div class="flex h-full flex-col">
<div class="flex-1 px-4">
<DataTable :value="sizeList">
<Column field="name" :header="$t('name')">
<template #body="{ data, field }">
{{ $t(data[field]) }}
</template>
</Column>
<Column field="width" :header="$t('width')" class="min-w-36">
<template #body="{ data, field }">
<span class="flex items-center gap-4">
<Slider
v-model="data[field]"
class="flex-1"
v-bind="sizeStint"
></Slider>
<span>{{ data[field] }}</span>
</span>
</template>
</Column>
<Column field="height" :header="$t('height')" class="min-w-36">
<template #body="{ data, field }">
<span class="flex items-center gap-4">
<Slider
v-model="data[field]"
class="flex-1"
v-bind="sizeStint"
></Slider>
<span>{{ data[field] }}</span>
</span>
</template>
</Column>
</DataTable>
</div>
<div class="flex justify-between px-4">
<div></div>
<div class="flex gap-2">
<Button
icon="pi pi-refresh"
:label="$t('reset')"
@click="handleReset"
></Button>
<Button :label="$t('cancel')" @click="handleCancelEditor"></Button>
<Button :label="$t('save')" @click="handleSaveSizeMap"></Button>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { useConfig } from 'hooks/config'
import { useDialog } from 'hooks/dialog'
import Button from 'primevue/button'
import Column from 'primevue/column'
import DataTable from 'primevue/datatable'
import Slider from 'primevue/slider'
import { onMounted, ref } from 'vue'
const { cardSizeMap, defaultCardSizeMap } = useConfig()
const dialog = useDialog()
const sizeList = ref()
const sizeStint = {
step: 10,
min: 80,
max: 320,
}
const resolveSizeMap = (sizeMap: Record<string, string>) => {
return Object.entries(sizeMap).map(([key, value]) => {
const [width, height] = value.split('x')
return {
id: key,
name: key,
width: parseInt(width),
height: parseInt(height),
}
})
}
const resolveSizeList = (
sizeList: { name: string; width: number; height: number }[],
) => {
return Object.fromEntries(
sizeList.map(({ name, width, height }) => {
return [name, [width, height].join('x')]
}),
)
}
onMounted(() => {
sizeList.value = resolveSizeMap(cardSizeMap.value)
})
const handleReset = () => {
sizeList.value = resolveSizeMap(defaultCardSizeMap)
}
const handleCancelEditor = () => {
sizeList.value = resolveSizeMap(cardSizeMap.value)
dialog.close()
}
const handleSaveSizeMap = () => {
cardSizeMap.value = resolveSizeList(sizeList.value)
dialog.close()
}
</script>

View File

@@ -1,11 +1,14 @@
import SettingCardSize from 'components/SettingCardSize.vue'
import { request } from 'hooks/request'
import { defineStore } from 'hooks/store'
import { $el, app, ComfyDialog } from 'scripts/comfyAPI'
import { onMounted, onUnmounted, ref } from 'vue'
import { computed, onMounted, onUnmounted, readonly, ref, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import { useToast } from './toast'
export const useConfig = defineStore('config', (store) => {
const { t } = useI18n()
const mobileDeviceBreakPoint = 759
const isMobile = ref(window.innerWidth < mobileDeviceBreakPoint)
@@ -21,23 +24,59 @@ export const useConfig = defineStore('config', (store) => {
window.removeEventListener('resize', checkDeviceType)
})
const cardSize = ref({
width:
app.ui?.settings.getSettingValue<number>('ModelManager.UI.CardWidth') ??
240,
height:
app.ui?.settings.getSettingValue<number>('ModelManager.UI.CardHeight') ??
310,
const defaultCardSizeMap = readonly({
'size.extraLarge': '240x320',
'size.large': '180x240',
'size.medium': '120x160',
'size.small': '80x120',
})
const cardSizeMap = ref<Record<string, string>>({ ...defaultCardSizeMap })
const cardSizeFlag = ref('size.extraLarge')
const cardSize = computed(() => {
const size = cardSizeMap.value[cardSizeFlag.value]
const [width = '120', height = '240'] = size.split('x')
return {
width: parseInt(width),
height: parseInt(height),
}
})
const config = {
isMobile,
gutter: 16,
cardSize,
defaultCardSizeMap: defaultCardSizeMap,
cardSizeMap: cardSizeMap,
cardSizeFlag: cardSizeFlag,
cardSize: cardSize,
cardWidth: 240,
aspect: 7 / 9,
dialog: {
showCardSizeSetting: () => {
store.dialog.open({
key: 'setting.cardSize',
title: t('setting.cardSize'),
content: SettingCardSize,
defaultSize: {
width: 500,
height: 390,
},
})
},
},
}
watch(cardSizeFlag, (val) => {
app.ui?.settings.setSettingValue('ModelManager.UI.CardSize', val)
})
watch(cardSizeMap, (val) => {
app.ui?.settings.setSettingValue(
'ModelManager.UI.CardSizeMap',
JSON.stringify(val),
)
})
useAddConfigSettings(store)
return config
@@ -109,36 +148,27 @@ function useAddConfigSettings(store: import('hooks/store').StoreProvider) {
defaultValue: undefined,
})
// UI settings
const defaultCardSize = store.config.defaultCardSizeMap
app.ui?.settings.addSetting({
id: 'ModelManager.UI.CardWidth',
category: [t('modelManager'), t('setting.ui'), 'CardWidth'],
name: t('setting.cardWidth'),
type: 'slider',
defaultValue: 240,
attrs: {
min: 80,
max: 320,
step: 10,
},
onChange(value) {
store.config.cardSize.value.width = value
id: 'ModelManager.UI.CardSize',
category: [t('modelManager'), t('setting.ui'), 'CardSize'],
name: t('setting.cardSize'),
defaultValue: 'size.extraLarge',
type: 'hidden',
onChange: (val) => {
store.config.cardSizeFlag.value = val
},
})
app.ui?.settings.addSetting({
id: 'ModelManager.UI.CardHeight',
category: [t('modelManager'), t('setting.ui'), 'CardHeight'],
name: t('setting.cardHeight'),
type: 'slider',
defaultValue: 320,
attrs: {
min: 80,
max: 320,
step: 10,
},
id: 'ModelManager.UI.CardSizeMap',
category: [t('modelManager'), t('setting.ui'), 'CardSizeMap'],
name: t('setting.cardSize'),
defaultValue: JSON.stringify(defaultCardSize),
type: 'hidden',
onChange(value) {
store.config.cardSize.value.height = value
store.config.cardSizeMap.value = JSON.parse(value)
},
})

View File

@@ -25,6 +25,10 @@ const messages = {
none: 'None',
uploadFile: 'Upload File',
tapToChange: 'Tap description to change content',
name: 'Name',
width: 'Width',
height: 'Height',
reset: 'Reset',
sort: {
name: 'Name',
size: 'Largest',
@@ -57,6 +61,7 @@ const messages = {
includeHiddenFiles: 'Include hidden files(start with .)',
excludeScanTypes: 'Exclude scan types (separate with commas)',
ui: 'UI',
cardSize: 'Card Size',
},
},
zh: {
@@ -82,6 +87,10 @@ const messages = {
none: '无',
uploadFile: '上传文件',
tapToChange: '点击描述可更改内容',
name: '名称',
width: '宽度',
height: '高度',
reset: '重置',
sort: {
name: '名称',
size: '最大',
@@ -114,6 +123,7 @@ const messages = {
includeHiddenFiles: '包含隐藏文件(以 . 开头的文件或文件夹)',
excludeScanTypes: '排除扫描类型(使用英文逗号隔开)',
ui: '外观',
cardSize: '卡片尺寸',
},
},
}