Merge pull request #256 from 11cafe/feat/upload-thumbnail

Feat/upload thumbnail
This commit is contained in:
Weixuan Fu
2024-03-13 18:12:50 +08:00
committed by GitHub
5 changed files with 87 additions and 14 deletions
+31 -4
View File
@@ -1,4 +1,5 @@
import asyncio
import folder_paths
from datetime import datetime
import platform
import tempfile
@@ -241,9 +242,6 @@ def count_files_sync(reqJson):
if file.is_file():
file_count += 1
return {"success": True, "count": file_count}
# Begin: Copy all files to the new directory when switching the save directory
@server.PromptServer.instance.routes.post('/workspace/copy_json_files')
async def copy_json_files(request):
@@ -281,4 +279,33 @@ async def get_unique_dir_name(path):
path = f"{original_path}_{counter}"
counter += 1
return path
# End: Copy all files to the new directory when switching the save directory
# End: Copy all files to the new directory when switching the save directory
@server.PromptServer.instance.routes.post('/workspace/images/save')
async def save_images(request):
reader = await request.multipart()
subfolder = "workspace_manager" # 默认子文件夹,实际上你可能会从表单中动态获取
output_path = folder_paths.get_output_directory()
safe_dir = Path(os.path.join(output_path, subfolder))
if not safe_dir.exists():
safe_dir.mkdir(parents=True, exist_ok=True)
# 用于存储保存的文件路径
files_saved = []
# 处理multipart/form-data中的每一部分
async for part in reader:
if part.name.startswith('images'):
filename = part.filename
if not filename:
continue
file_path = safe_dir / filename
loop = asyncio.get_running_loop()
# 异步写入文件
with open(file_path, 'wb') as f:
while True:
chunk = await part.read_chunk() # 默认8192 bytes
if not chunk:
break
await loop.run_in_executor(None, f.write, chunk)
files_saved.append(str(file_path))
return web.Response(text=json.dumps({"imgPaths": files_saved}), content_type='application/json')
+50 -7
View File
@@ -1,28 +1,30 @@
import { useState, useContext } from "react";
import { ChangeEvent, useContext, useRef, useState } from "react";
import {
IconButton,
Menu,
MenuButton,
MenuList,
MenuItem,
useToast,
MenuList,
Tooltip,
useToast,
} from "@chakra-ui/react";
import {
IconDotsVertical,
IconLockOpen,
IconLock,
IconCopy,
IconDotsVertical,
IconLock,
IconLockOpen,
IconUpload,
} from "@tabler/icons-react";
import AddTagToWorkflowPopover from "./AddTagToWorkflowPopover";
import { Workflow } from "../types/dbTypes";
import { workflowsTable } from "../db-tables/WorkspaceDB";
import { mediaTable, workflowsTable } from "../db-tables/WorkspaceDB";
import { WorkspaceContext } from "../WorkspaceContext";
type Props = {
workflow: Workflow;
};
export default function MoreActionMenu({ workflow }: Props) {
const fileInputRef = useRef<HTMLInputElement>(null);
const toast = useToast();
const { onDuplicateWorkflow } = useContext(WorkspaceContext);
@@ -41,6 +43,28 @@ export default function MoreActionMenu({ workflow }: Props) {
});
};
const handleFileChange = async (event: ChangeEvent<HTMLInputElement>) => {
const files = event.target.files;
if (!files) return;
const body = new FormData();
Array.from(files).forEach((file, index) => {
body.append(`images[${index}]`, file, file.name);
});
body.append("subfolder", "workspace_manager");
await fetch("/workspace/images/save", {
method: "POST",
body,
});
Array.from(files).forEach((file) => {
mediaTable?.create({
workflowID: workflow.id,
localPath: `workspace_manager/${file.name}`,
});
});
};
return (
<>
<Menu isLazy closeOnSelect={false}>
@@ -78,8 +102,27 @@ export default function MoreActionMenu({ workflow }: Props) {
{isLocked ? "Unlock" : "Lock"}
</MenuItem>
</Tooltip>
<MenuItem
icon={<IconUpload />}
onClick={() => {
fileInputRef.current?.click();
}}
>
Upload Thumbnail Image
</MenuItem>
</MenuList>
</Menu>
<input
style={{ display: "none" }}
ref={fileInputRef}
type="file"
accept=".json,image/png"
multiple
onChange={handleFileChange}
onClick={(e) => {
e.stopPropagation();
}}
/>
</>
);
}
@@ -57,9 +57,11 @@ export default memo(function WorkflowListItem({ workflow }: Props) {
}}
>
<HStack>
{workflow.coverMediaPath != null && (
{(workflow.coverMediaPath != null || workflow.latestImage !== null) && (
<MediaPreview
mediaLocalPath={workflow.coverMediaPath}
mediaLocalPath={
workflow.coverMediaPath ?? workflow.latestImage ?? ""
}
size={60}
hideBrokenImage
isPreview
+1 -1
View File
@@ -36,7 +36,7 @@ export class MediaTable extends TableBase<Media> {
const newMedia = new Set(workflow?.mediaIDs ?? []).add(md.id);
await workflowsTable?.updateMetaInfo(input.workflowID, {
mediaIDs: Array.from(newMedia),
coverMediaPath: md.localPath,
latestImage: md.localPath,
});
// save indexdb
indexdb.media.add(md);
+1
View File
@@ -20,6 +20,7 @@ export interface Workflow extends SortableItem {
cloudID?: string; // TODO will be deprecated
cloudOrigin?: string;
saveLock?: boolean;
latestImage?: string;
}
export interface TableBaseModel {