支持快速添加一些常用的文件夹
parent
a7804c5102
commit
510a00db32
|
|
@ -6,8 +6,8 @@
|
|||
<link rel="icon" href="/favicon.ico">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Vite App</title>
|
||||
<script type="module" crossorigin src="/baidu_netdisk/fe-static/assets/index-9de6dddf.js"></script>
|
||||
<link rel="stylesheet" href="/baidu_netdisk/fe-static/assets/index-83870f85.css">
|
||||
<script type="module" crossorigin src="/baidu_netdisk/fe-static/assets/index-521b6f7a.js"></script>
|
||||
<link rel="stylesheet" href="/baidu_netdisk/fe-static/assets/index-3fdcc1a6.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="zanllp_dev_gradio_fe"></div>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import asyncio
|
||||
import datetime
|
||||
import os
|
||||
from typing import List, Dict, Union, Literal
|
||||
import uuid
|
||||
import subprocess
|
||||
|
|
@ -72,7 +73,7 @@ class BaiduyunTask:
|
|||
process = await asyncio.create_subprocess_exec(
|
||||
bin_file_path,
|
||||
type,
|
||||
*str(send_dirs).split(","),
|
||||
*process_path_arr(str(send_dirs).split(",")),
|
||||
recv_dir,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
|
|
@ -92,3 +93,18 @@ class BaiduyunTask:
|
|||
|
||||
|
||||
baiduyun_task_cache: Dict[str, BaiduyunTask] = {}
|
||||
|
||||
def process_path_arr(path_arr):
|
||||
"""
|
||||
处理路径
|
||||
如果是绝对路径直接返回,
|
||||
如果是相对路径则与当前工作目录拼接返回。
|
||||
"""
|
||||
cwd = os.getcwd()
|
||||
result = []
|
||||
for path in path_arr:
|
||||
if os.path.isabs(path):
|
||||
result.append(path)
|
||||
else:
|
||||
result.append(os.path.join(cwd, path))
|
||||
return result
|
||||
|
|
@ -212,7 +212,7 @@ def get_default_conf():
|
|||
)
|
||||
upload_dir = "/stable-diffusion-upload"
|
||||
return {
|
||||
"output_dirs": outputs_dirs,
|
||||
#"output_dirs": outputs_dirs,
|
||||
"upload_dir": upload_dir,
|
||||
}
|
||||
|
||||
|
|
@ -273,6 +273,13 @@ def baidu_netdisk_api(_: gr.Blocks, app: FastAPI):
|
|||
@app.get(f"{pre}/hello")
|
||||
async def greeting():
|
||||
return "hello"
|
||||
|
||||
@app.get(f'{pre}/global_setting')
|
||||
async def global_setting():
|
||||
return {
|
||||
"global_setting": opts.data,
|
||||
"default_conf": get_default_conf()
|
||||
}
|
||||
|
||||
class BaiduyunUploadDownloadReq(BaseModel):
|
||||
type: Literal["upload", "download"]
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ declare module '@vue/runtime-core' {
|
|||
ASelect: typeof import('ant-design-vue/es')['Select']
|
||||
ATag: typeof import('ant-design-vue/es')['Tag']
|
||||
ATextarea: typeof import('ant-design-vue/es')['Textarea']
|
||||
ATooltip: typeof import('ant-design-vue/es')['Tooltip']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
RouterView: typeof import('vue-router')['RouterView']
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -5,8 +5,8 @@
|
|||
<link rel="icon" href="/favicon.ico">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Vite App</title>
|
||||
<script type="module" crossorigin src="/baidu_netdisk/fe-static/assets/index-9de6dddf.js"></script>
|
||||
<link rel="stylesheet" href="/baidu_netdisk/fe-static/assets/index-83870f85.css">
|
||||
<script type="module" crossorigin src="/baidu_netdisk/fe-static/assets/index-521b6f7a.js"></script>
|
||||
<link rel="stylesheet" href="/baidu_netdisk/fe-static/assets/index-3fdcc1a6.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="zanllp_dev_gradio_fe"></div>
|
||||
|
|
|
|||
|
|
@ -24,8 +24,9 @@ const percent = computed(() => !store.splitView.open ? 100 : store.splitView.per
|
|||
</split-view>
|
||||
</div>
|
||||
</template>
|
||||
<style scoped>
|
||||
<style scoped lang="scss">
|
||||
.split-view-container {
|
||||
height: 70vh;
|
||||
height: 95vh; // todo 暂时这样,这个不重要
|
||||
width: 95%;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
import axios from 'axios'
|
||||
import type { GlobalSettingPart } from './type'
|
||||
const axiosInst = axios.create({
|
||||
baseURL: '/baidu_netdisk'
|
||||
})
|
||||
|
|
@ -118,3 +119,13 @@ export const getUploadTasks = async () => {
|
|||
tasks: UploadTaskSummary[]
|
||||
}
|
||||
}
|
||||
|
||||
export const getGlobalSetting = async () => {
|
||||
const resp = await axiosInst.get('/global_setting')
|
||||
return resp.data as {
|
||||
global_setting: GlobalSettingPart,
|
||||
default_conf: {
|
||||
upload_dir: string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
export interface GlobalSettingPart {
|
||||
outdir_samples: string;
|
||||
outdir_txt2img_samples: string;
|
||||
outdir_img2img_samples: string;
|
||||
outdir_extras_samples: string;
|
||||
outdir_grids: string;
|
||||
outdir_txt2img_grids: string;
|
||||
outdir_img2img_grids: string;
|
||||
outdir_save: string;
|
||||
dataset_filename_word_regex: string;
|
||||
dataset_filename_join_string: string;
|
||||
sd_model_checkpoint: string;
|
||||
sd_vae: string;
|
||||
img2img_background_color: string;
|
||||
deepbooru_filter_tags: string;
|
||||
sd_hypernetwork: string;
|
||||
font: string;
|
||||
quicksettings: string;
|
||||
ui_reorder: string;
|
||||
ui_extra_networks_tab_reorder: string;
|
||||
localization: string;
|
||||
show_progress_type: string;
|
||||
live_preview_content: string;
|
||||
ddim_discretize: string;
|
||||
sd_checkpoint_hash: string;
|
||||
sd_lora: string;
|
||||
control_net_model_config: string;
|
||||
control_net_model_adapter_config: string;
|
||||
control_net_models_path: string;
|
||||
additional_networks_extra_lora_path: string;
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
import type { getGlobalSetting } from '@/api'
|
||||
import { pick, type ReturnTypeAsync } from '@/util'
|
||||
|
||||
export const getAutoCompletedTagList = ({ global_setting }: ReturnTypeAsync<typeof getGlobalSetting>) => {
|
||||
const picked = pick(global_setting,
|
||||
'additional_networks_extra_lora_path',
|
||||
'outdir_grids',
|
||||
'outdir_extras_samples',
|
||||
'outdir_img2img_grids',
|
||||
'outdir_img2img_samples',
|
||||
'outdir_grids',
|
||||
'outdir_extras_samples',
|
||||
'outdir_samples',
|
||||
'outdir_txt2img_grids',
|
||||
'outdir_txt2img_samples',
|
||||
'outdir_save'
|
||||
)
|
||||
const allTag = {
|
||||
...picked,
|
||||
'embeddings': 'embeddings',
|
||||
'hypernetworks': 'models/hypernetworks'
|
||||
}
|
||||
type Keys = keyof (typeof allTag)
|
||||
const cnMap: Record<Keys, string> = {
|
||||
additional_networks_extra_lora_path: '扫描 LoRA 模型的附加目录',
|
||||
outdir_grids: '宫格图的输出目录',
|
||||
outdir_extras_samples: '附加功能选项卡的输出目录',
|
||||
outdir_img2img_grids: '图生图网格文件夹',
|
||||
outdir_img2img_samples: '图生图的输出目录',
|
||||
outdir_samples: '图像的输出目录',
|
||||
outdir_txt2img_samples: '文生图的输出目录',
|
||||
outdir_txt2img_grids: '文生图宫格的输出目录',
|
||||
hypernetworks: '超网络模型的路径',
|
||||
outdir_save: '使用“保存”按钮保存图像的目录',
|
||||
embeddings: 'Embedding的文件夹'
|
||||
}
|
||||
return Object.keys(cnMap).map((k) => {
|
||||
const key = k as Keys
|
||||
return {
|
||||
key,
|
||||
zh: cnMap[key],
|
||||
dir: allTag[key]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -1,17 +1,23 @@
|
|||
<script setup lang="ts">
|
||||
import { key } from '@/util'
|
||||
import { key, pick } from '@/util'
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { SearchSelect, type WithId, typedID, Task } from 'vue3-ts-util'
|
||||
import { type WithId, typedID, Task } from 'vue3-ts-util'
|
||||
import { PlusOutlined, SyncOutlined } from '@/icon'
|
||||
import { createBaiduYunTask, getUploadTasks, getUploadTaskTickStatus, type UploadTaskSummary } from '@/api'
|
||||
import { createBaiduYunTask, getGlobalSetting, getUploadTasks, getUploadTaskTickStatus, type UploadTaskSummary } from '@/api'
|
||||
import { message } from 'ant-design-vue'
|
||||
import { pick } from 'lodash-es'
|
||||
import { useTaskListStore } from '@/store/useTaskListStore'
|
||||
import { getAutoCompletedTagList } from './autoComplete'
|
||||
|
||||
const tasks = ref<WithId<UploadTaskSummary>[]>([])
|
||||
const ID = typedID<UploadTaskSummary>(true)
|
||||
const store = useTaskListStore()
|
||||
const autoCompletedDirList = ref([] as ReturnType<typeof getAutoCompletedTagList>)
|
||||
const showDirAutoCompletedIdx = ref(-1)
|
||||
|
||||
onMounted(async () => {
|
||||
getGlobalSetting().then((resp) => {
|
||||
autoCompletedDirList.value = getAutoCompletedTagList(resp).filter(v => v.dir.trim())
|
||||
})
|
||||
const resp = await getUploadTasks()
|
||||
tasks.value = resp.tasks.map(ID)
|
||||
const runningTasks = tasks.value.filter(v => v.running)
|
||||
|
|
@ -43,6 +49,11 @@ const addEmptyTask = () => {
|
|||
|
||||
const createNewTask = async (idx: number) => {
|
||||
const task = tasks.value[idx]
|
||||
task.send_dirs = task.send_dirs.split(/,,\n/).map(v => v.trim()).filter(v => v).join()
|
||||
task.recv_dir = task.recv_dir.trim()
|
||||
if (!task.recv_dir.startsWith('/')) {
|
||||
return message.error('百度云接收位置必须以 “/” 开头')
|
||||
}
|
||||
task.running = true
|
||||
task.n_files = 100
|
||||
const resp = await createBaiduYunTask(task)
|
||||
|
|
@ -72,7 +83,7 @@ const copyFrom = (idx: number) => {
|
|||
const prevTask = tasks.value[idx]
|
||||
tasks.value.unshift({
|
||||
...getEmptyTask(),
|
||||
...pick(prevTask, ['send_dirs', 'type', 'recv_dir'])
|
||||
...pick(prevTask, 'send_dirs', 'type', 'recv_dir')
|
||||
})
|
||||
message.success('复制完成,已添加到最前端')
|
||||
}
|
||||
|
|
@ -81,10 +92,19 @@ const openLogDetail = (idx: number) => {
|
|||
store.currLogDetailId = tasks.value[idx].id
|
||||
store.splitView.open = true
|
||||
}
|
||||
const colors = ['#f5222d', '#1890ff', '#ff3125', '#d46b08', '#007bff', '#52c41a', '#13c2c2', '#fa541c', '#eb2f96', '#2f54eb']
|
||||
const addDir2task = (idx: number, dir: string) => {
|
||||
const task = tasks.value[idx]
|
||||
if (/[,,\n]$/.test(task.send_dirs) || !task.send_dirs.trim()) {
|
||||
task.send_dirs += dir
|
||||
} else {
|
||||
task.send_dirs += ` , ${dir}`
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="wrapper">
|
||||
<div class="wrapper" @click="showDirAutoCompletedIdx = -1">
|
||||
<a-select style="display: none" />
|
||||
<a-button @click="addEmptyTask">
|
||||
<template>
|
||||
|
|
@ -105,17 +125,22 @@ const openLogDetail = (idx: number) => {
|
|||
</div>
|
||||
</div>
|
||||
<a-form layout="vertical" label-align="left">
|
||||
<a-form-item label="发送的文件夹">
|
||||
<a-form-item label="发送的文件夹" @click.stop="showDirAutoCompletedIdx = idx">
|
||||
<a-textarea auto-size :disabled="task.running" v-model:value="task.send_dirs"
|
||||
placeholder="发送文件的文件夹,多个文件夹使用逗号分隔"></a-textarea>
|
||||
placeholder="发送文件的文件夹,多个文件夹使用逗号或者换行分隔"></a-textarea>
|
||||
<div v-if="idx === showDirAutoCompletedIdx" class="auto-completed-dirs">
|
||||
<a-tooltip v-for="item, tagIdx in autoCompletedDirList" :key="item.dir" :title="item.dir+ ' 点击添加'">
|
||||
<a-tag :visible="!task.send_dirs.includes(item.dir)" :color="colors[tagIdx % colors.length]" @click="addDir2task(idx, item.dir)">{{ item.zh}}</a-tag>
|
||||
</a-tooltip>
|
||||
</div>
|
||||
</a-form-item>
|
||||
<a-form-item label="百度云文件夹">
|
||||
<a-input v-model:value="task.recv_dir" :disabled="task.running" placeholder="用于接收的文件夹,可以使用占位符进行动态生成"></a-input>
|
||||
</a-form-item>
|
||||
<!--a-form-item label="任务类型">
|
||||
<search-select v-model:value="task.type" :disabled="task.running" :options="['upload', 'download']"
|
||||
:conv="{ value: (v) => v, text: (v) => (v === 'upload' ? '上传' : '下载') }"></search-select>
|
||||
</a-form-item-->
|
||||
<search-select v-model:value="task.type" :disabled="task.running" :options="['upload', 'download']"
|
||||
:conv="{ value: (v) => v, text: (v) => (v === 'upload' ? '上传' : '下载') }"></search-select>
|
||||
</a-form-item-->
|
||||
</a-form>
|
||||
<div class="action-bar">
|
||||
<a-button @click="openLogDetail(idx)" v-if="store.taskLogMap.get(task.id)">查看详细日志</a-button>
|
||||
|
|
@ -140,6 +165,7 @@ const openLogDetail = (idx: number) => {
|
|||
height: 100%;
|
||||
overflow: auto;
|
||||
padding: 8px;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
|
@ -166,6 +192,10 @@ const openLogDetail = (idx: number) => {
|
|||
margin-bottom: 16px;
|
||||
|
||||
}
|
||||
|
||||
.auto-completed-dirs {
|
||||
margin-top: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -22,4 +22,22 @@ export const asyncCheck = async<T> (getter: () => T, checkSize = 100, timeout =
|
|||
});
|
||||
}
|
||||
|
||||
export const key = (obj: UniqueId) => obj[idKey]
|
||||
export const key = (obj: UniqueId) => obj[idKey]
|
||||
export type Dict<T = any> = Record<string, T>
|
||||
/**
|
||||
* 推导比loadsh好
|
||||
* @param v
|
||||
* @param keys
|
||||
*/
|
||||
export const pick = <T extends Dict, keys extends Array<keyof T>> (v: T, ...keys: keys) => {
|
||||
return keys.reduce((p, c) => {
|
||||
p[c] = v?.[c]
|
||||
return p
|
||||
}, {} as Pick<T, keys[number]>)
|
||||
}
|
||||
/**
|
||||
* 获取一个异步函数的返回类型,
|
||||
*
|
||||
* ReturnTypeAsync\<typeof fn\>
|
||||
*/
|
||||
export type ReturnTypeAsync<T extends (...arg: any) => Promise<any>> = Awaited<ReturnType<T>>
|
||||
Loading…
Reference in New Issue