Merge pull request #7 from zanllp/feat-auto-upload

自动上传实现
pull/8/head
zanllp 2023-04-01 15:58:40 +08:00 committed by GitHub
commit 885f5f701c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 371 additions and 216 deletions

View File

@ -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-0e9928f4.js"></script>
<link rel="stylesheet" href="/baidu_netdisk/fe-static/assets/index-b1903a85.css">
<script type="module" crossorigin src="/baidu_netdisk/fe-static/assets/index-d1574f79.js"></script>
<link rel="stylesheet" href="/baidu_netdisk/fe-static/assets/index-22f3f8f5.css">
</head>
<body>
<div id="zanllp_dev_gradio_fe"></div>

View File

@ -25,7 +25,11 @@ from scripts.tool import get_windows_drives, convert_to_bytes
import functools
from scripts.logger import logger
class AutoUpload:
# 已成等待发送图像的队列
files = []
task_id: Union[None, str] = None
def exec_ops(args: Union[List[str], str]):
args = [args] if isinstance(args, str) else args
res = ""
@ -331,4 +335,27 @@ def baidu_netdisk_api(_: Any, app: FastAPI):
async def image_geninfo(path: str):
from modules import extras
geninfo,_ = extras.images.read_info_from_image(Image.open(path))
return geninfo
return geninfo
class AutoUploadParams(BaseModel):
recv_dir: str
@app.post(pre+"/auto_upload")
async def auto_upload(req: AutoUploadParams):
tick_info = None
if AutoUpload.task_id:
task = BaiduyunTask.get_by_id(AutoUpload.task_id)
tick_info = await task.get_tick()
if not task.running:
AutoUpload.task_id = None
else:
recived_file = AutoUpload.files
AutoUpload.files = []
if len(recived_file):
logger.info(f"创建上传任务 {recived_file} ----> {req.recv_dir}")
task = await BaiduyunTask.create('upload', recived_file, req.recv_dir)
AutoUpload.task_id = task.id
return {
"tick_info": tick_info,
"pending_files": AutoUpload.files
}

View File

@ -31,17 +31,20 @@ def get_matched_summary():
file_name = "BaiduPCS-Go-v3.9.0-windows-x86"
if not file_name:
raise Exception(f"找不到对应的文件,请携带此信息找开发者 machine:{machine} system:{system}")
return file_name, f"https://github.com/qjfoidnh/BaiduPCS-Go/releases/download/v3.9.0/{file_name}.zip"
return file_name, f"https://github.com/qjfoidnh/BaiduPCS-Go/releases/download/v3.9.0/{file_name}.zip", f"http://static.zanllp.cn/{file_name}.zip"
def download_bin_file():
summary, url = get_matched_summary()
summary, url, fallback_url = get_matched_summary()
# 下载文件保存路径
download_path = "BaiduPCS-Go.zip"
try:
# 下载文件并保存
urllib.request.urlretrieve(url, download_path)
urllib.request.urlretrieve(url, download_path)
except:
urllib.request.urlretrieve(fallback_url, download_path)
# 解压缩
with zipfile.ZipFile(download_path, "r") as zip_ref:

View File

@ -1,4 +1,5 @@
from scripts.api import baidu_netdisk_api, send_img_path
from fastapi import FastAPI
from scripts.api import baidu_netdisk_api, send_img_path, AutoUpload
from modules import script_callbacks, generation_parameters_copypaste as send, extras
from scripts.bin import (
bin_file_name,
@ -6,8 +7,9 @@ from scripts.bin import (
check_bin_exists,
download_bin_file,
)
from scripts.tool import cwd
from scripts.tool import cwd, debounce
from PIL import Image
from scripts.logger import logger
"""
@ -15,7 +17,7 @@ api函数声明和启动分离方便另外一边被外部调用
"""
not_exists_msg = (
f"找不到{bin_file_name},尝试手动从 {get_matched_summary()[1]} 下载,下载后放到 {cwd} 文件夹下,重启界面"
f"找不到{bin_file_name},尝试手动从 {get_matched_summary()[1]} 或者 {get_matched_summary()[2]} 下载,下载后放到 {cwd} 文件夹下,重启界面"
)
@ -70,5 +72,13 @@ def on_ui_tabs():
return ((baidu_netdisk, "百度云", "baiduyun"),)
def on_img_saved(params: script_callbacks.ImageSaveParams):
AutoUpload.files.append(params.filename)
script_callbacks.on_ui_tabs(on_ui_tabs)
script_callbacks.on_app_started(baidu_netdisk_api)
script_callbacks.on_image_saved(on_img_saved)

View File

@ -46,6 +46,29 @@ def convert_to_bytes(file_size_str):
return int(size)
else:
raise ValueError(f"Invalid file size string '{file_size_str}'")
import asyncio
def debounce(delay):
"""用于优化高频事件的装饰器"""
def decorator(func):
from typing import Union
task: Union[None, asyncio.Task] = None
async def debounced(*args, **kwargs):
nonlocal task
if task:
task.cancel()
task = asyncio.create_task(asyncio.sleep(delay))
await task
return await func(*args, **kwargs)
return debounced
return decorator
is_dev = "APP_ENV" in os.environ and os.environ["APP_ENV"] == "dev"
cwd = os.path.normpath(os.path.join(__file__, "../../"))

3
vue/components.d.ts vendored
View File

@ -13,6 +13,7 @@ declare module '@vue/runtime-core' {
ABreadcrumb: typeof import('ant-design-vue/es')['Breadcrumb']
ABreadcrumbItem: typeof import('ant-design-vue/es')['BreadcrumbItem']
AButton: typeof import('ant-design-vue/es')['Button']
ACol: typeof import('ant-design-vue/es')['Col']
ADropdown: typeof import('ant-design-vue/es')['Dropdown']
AForm: typeof import('ant-design-vue/es')['Form']
AFormItem: typeof import('ant-design-vue/es')['FormItem']
@ -23,8 +24,10 @@ declare module '@vue/runtime-core' {
AMenuItem: typeof import('ant-design-vue/es')['MenuItem']
AModal: typeof import('ant-design-vue/es')['Modal']
AProgress: typeof import('ant-design-vue/es')['Progress']
ARow: typeof import('ant-design-vue/es')['Row']
ASelect: typeof import('ant-design-vue/es')['Select']
ASkeleton: typeof import('ant-design-vue/es')['Skeleton']
AStatistic: typeof import('ant-design-vue/es')['Statistic']
ASwitch: typeof import('ant-design-vue/es')['Switch']
ATabPane: typeof import('ant-design-vue/es')['TabPane']
ATabs: typeof import('ant-design-vue/es')['Tabs']

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

201
vue/dist/assets/index-d1574f79.js vendored Normal file

File diff suppressed because one or more lines are too long

4
vue/dist/index.html vendored
View File

@ -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-0e9928f4.js"></script>
<link rel="stylesheet" href="/baidu_netdisk/fe-static/assets/index-b1903a85.css">
<script type="module" crossorigin src="/baidu_netdisk/fe-static/assets/index-d1574f79.js"></script>
<link rel="stylesheet" href="/baidu_netdisk/fe-static/assets/index-22f3f8f5.css">
</head>
<body>
<div id="zanllp_dev_gradio_fe"></div>

View File

@ -13,6 +13,7 @@ import { getAutoCompletedTagList } from './taskRecord/autoComplete'
import { useTaskListStore } from './store/useTaskListStore'
import TaskOperation from './taskRecord/taskOperation.vue'
import { useIntervalFn } from '@vueuse/core'
import autoUpload from './autoUpload/autoUpload.vue'
const user = ref<UserInfo>()
const bduss = ref('')
@ -108,6 +109,9 @@ useIntervalFn(() => {
</template>
<task-operation />
</a-tab-pane>
<a-tab-pane key="3" tab="自动上传">
<auto-upload/>
</a-tab-pane>
</a-tabs>
</a-skeleton>
</template>

View File

@ -168,3 +168,14 @@ export const genInfoCompleted = async () => {
export const getImageGenerationInfo = async (path: string) => {
return (await axiosInst.get(`/image_geninfo?path=${encodeURIComponent(path)}`)).data as string
}
export const autoUploadOutput = async (recv_dir: string) => {
const resp = await axiosInst.post(`/auto_upload`, { recv_dir })
return resp.data as {
pending_files: string[]
tick_info?: {
tasks: UploadTaskTickStatus[],
task_summary: UploadTaskSummary
}
}
}

View File

@ -0,0 +1,72 @@
<script setup lang="ts">
import { computed, reactive, ref, watchEffect } from 'vue'
import { autoUploadOutput, type UploadTaskSummary } from '@/api/index'
import { delay, Task } from 'vue3-ts-util'
import { useGlobalStore } from '@/store/useGlobalStore'
const emit = defineEmits<{ (e: 'runningChange', v: boolean): void }>()
const global = useGlobalStore()
const pendingFiles = ref<string[]>([])
const task = ref<ReturnType<typeof runPollTask>>()
const running = computed(() => !!(task.value || pendingFiles.value.length))
watchEffect(() => emit('runningChange', running.value))
const taskLog = reactive(new Map<string, UploadTaskSummary>())
const taskLogList = computed(() => Array.from(taskLog.values()))
const completedFiles = computed(() => taskLogList.value.reduce((p, c) => p + c.n_success_files, 0))
const failededFiles = computed(() => taskLogList.value.reduce((p, c) => p + c.n_failed_files, 0))
// const allFiles = computed(() => taskLogList.value.reduce((p, c) => p + c.n_files, 0) + pendingFiles.value.length)
const runPollTask = () => {
return Task.run({
action: async () => {
const res = await autoUploadOutput(global.autoUploadRecvDir)
if (res.tick_info) {
taskLog.set(res.tick_info.task_summary.id, res.tick_info.task_summary)
}
pendingFiles.value = res.pending_files
await delay(10000 * Math.random())
return res
},
pollInterval: 30_000
})
}
const onStart = async () => {
if (task.value) {
task.value.clearTask()
task.value = undefined
pendingFiles.value = []
} else {
task.value = runPollTask()
}
}
</script>
<template>
<div class="container">
<AInput v-model:value="global.autoUploadRecvDir"></AInput>
<AButton @click="onStart" :loading="running">{{ task ? '暂停' : '开始' }}</AButton>
<a-row>
<a-col :span="12">
<a-statistic title="等待上传数量" :value="pendingFiles.length" style="margin-right: 50px" />
</a-col>
<a-col :span="12">
<a-statistic title="上传失败数量" :value="failededFiles" />
</a-col>
</a-row>
<a-row>
<a-col :span="12">
<a-statistic title="已完成数量" :value="completedFiles" style="margin-right: 50px" />
</a-col>
</a-row>
</div>
</template>
<style lang="scss" scoped>
.container {
margin: 16px;
&>* {
margin: 8px;
}
}
</style>

View File

@ -495,7 +495,7 @@ function useFileItemActions () {
v-if="props.target === 'local' && viewMode !== 'line' && isImageFile(file.name)"
:src="global.enableThumbnail ? toImageThumbnailUrl(file, viewMode === 'grid' ? void 0 : '512,512') : toRawFileUrl(file)"
:fallback="fallbackImage"
:preview="{ src: toRawFileUrl(sortedFiles[previewIdx]), onVisibleChange: onPreviewVisibleChange }">
:preview="{ src: sortedFiles[previewIdx] ? toRawFileUrl(sortedFiles[previewIdx]) : '', onVisibleChange: onPreviewVisibleChange }">
</a-image>
<template v-else>
<file-outlined class="icon" v-if="file.type === 'file'" />

View File

@ -9,15 +9,17 @@ export const useGlobalStore = defineStore('useGlobalStore', () => {
const autoCompletedDirList = ref([] as ReturnType<typeof getAutoCompletedTagList>)
const enableThumbnail = ref(true)
const stackViewSplit = ref(50)
const autoUploadRecvDir = ref('/')
return {
conf,
autoCompletedDirList,
enableThumbnail,
stackViewSplit,
autoUploadRecvDir,
...typedEventEmitter<{ createNewTask: Partial<UploadTaskSummary> }>()
}
}, {
persist: {
paths: ['enableThumbnail', 'stackViewSplit']
paths: ['enableThumbnail', 'stackViewSplit', 'autoUploadRecvDir']
}
})