diff --git a/server/index.ts b/server/index.ts index 7d0125f..c37c8fa 100644 --- a/server/index.ts +++ b/server/index.ts @@ -53,7 +53,7 @@ import main from './main' process.exit() }, 5000) - wss.on('connection', ws => { + wss.on('connection',async ws => { connection_count++ if (connection_count > 1) return ws.close() // 忽略連線 if (timeout) clearTimeout(timeout) diff --git a/server/main.ts b/server/main.ts index 5ca5258..cad420f 100644 --- a/server/main.ts +++ b/server/main.ts @@ -7,23 +7,11 @@ import { exec } from "child_process" // 監聽並回傳 export default function (ws: _ws) { - ws.on("build", async ($data) => { - console.log("清空output") - const output = { - path: path("output"), - files: [] as string[], - wait: [] as Promise[], - } - output.wait.push( - fs.readdir(output.path) - .then((files) => { - for (const file of files) { output.wait.push(fs.unlink(path("output", file))) } - }) - .catch(() => { - output.wait.push(fs.mkdir(output.path)) - }) - ) + ws.on("upload_config", async ($data) => config($data.data)) + ws.on("config", async () => { return config() }) + ws.on("build", async ($data) => { + const wait_clear = clear_output() console.log("設置構建") const data = $data.data @@ -41,6 +29,10 @@ export default function (ws: _ws) { return img }).catch(() => { console.warn("下載圖片失敗") + ws.send("logger", { + title: "下載圖片失敗", + content: "請檢查網址是否正確", + }) return false }) }) @@ -76,7 +68,7 @@ export default function (ws: _ws) { } console.log("構建選項:", data.option) - await Promise.all(output.wait) //等待 output 刪除檔案 + await wait_clear //等待 output 刪除檔案 await worker("build", data.option, data.imgs.map(img => { return { name: img.name, base64img: img.data, } })) console.log("構建完成") @@ -85,6 +77,37 @@ export default function (ws: _ws) { return true }) - ws.on("upload_config", async ($data) => config($data.data)) - ws.on("config", async () => { return config() }) + ws.on("compression", async ($data) => { + const wait_clear = clear_output() + const data = $data.data + + + console.log("壓縮選項:", data.option) + await wait_clear //等待 output 刪除檔案 + await worker("compression", data.option, data.imgs) + + console.log("壓縮完成") + exec(`start explorer "${path("output")}"`) + + return true + }) +} + +function clear_output() { + console.log("清空output") + const output = { + path: path("output"), + files: [] as string[], + wait: [] as Promise[], + } + output.wait.push( + fs.readdir(output.path) + .then((files) => { + for (const file of files) { output.wait.push(fs.unlink(path("output", file))) } + }) + .catch(() => { + output.wait.push(fs.mkdir(output.path)) + }) + ) + return Promise.all(output.wait) } \ No newline at end of file diff --git a/server/threads.ts b/server/threads.ts index 9c82222..8b741e0 100644 --- a/server/threads.ts +++ b/server/threads.ts @@ -1,15 +1,16 @@ import { workerData } from 'worker_threads' import { WorkerDataType } from 'VS/protocol' -import { path } from './utils' +import { hash, path } from './utils' import { extname, basename } from "path" import Jimp from 'jimp' +import { exec as $exec } from 'child_process' // 多線程運算 -(async()=>{ - const $type = workerData.type as keyof WorkerDataType - const { option:$option, data:$data } = workerData.data as WorkerDataType[typeof $type] +(async($type:keyof WorkerDataType)=>{ + const worker_data = workerData.data as unknown switch ($type) { case "build":{ + const { option:$option, data:$data } = worker_data as WorkerDataType[typeof $type] for (const data of $data) { let img = await decode_image(data.base64img).catch(e=>{throw e}) img.resize($option.size,Jimp.AUTO) @@ -39,8 +40,39 @@ import Jimp from 'jimp' } break; } + case "compression":{ + const { option:$option, data:$data } = worker_data as WorkerDataType["compression"] + const pngquant = path("lib","pngquant.exe") + for (const data of $data) { + const tmp = path("tmp","compression$" + data.name) + const out = path("output",data.name ) + + const img = await decode_image(data.data).catch(e=>{throw e}) + if(img.getMIME() === Jimp.MIME_JPEG){ + await img.quality($option.quality[1]).writeAsync(out) + }else{ + await img.writeAsync(tmp) + await exec(tmp,out) + } + } + + function exec(tmp_path:string,output_path:string):Promise { + const quality = $option.quality[0] + "-" + $option.quality[1] + const speed = $option.speed + return new Promise(resolve=>{ + const cmd = `\"${pngquant}\" --quality ${quality} --speed ${speed} - < \"${tmp_path}\" > \"${output_path}\"` + console.log(cmd) + $exec(cmd,{ encoding: 'utf8' }, + (e,s,stderr)=>{ + if(e) console.error(e,stderr) + resolve() + }) + }) + } + break; + } } -})() +})(workerData.type) diff --git a/server/utils.ts b/server/utils.ts index 2f00248..1e7f800 100644 --- a/server/utils.ts +++ b/server/utils.ts @@ -74,4 +74,6 @@ async function worker return Promise.all(worker_threads) } -export { config, path, worker } \ No newline at end of file +function hash(){ let result = ''; const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; for (let i = 0; i < 12; i++)result += characters.charAt(Math.floor(Math.random() * 62)); return result } + +export { config, path, worker, hash } \ No newline at end of file diff --git a/server/websocket.ts b/server/websocket.ts index 7c5d8b3..8a713ce 100644 --- a/server/websocket.ts +++ b/server/websocket.ts @@ -1,3 +1,4 @@ +import { hash as $hash } from "./utils" import { DataType } from "VS/protocol" import SocketServer = require('ws') @@ -63,7 +64,7 @@ export default class { get(key: T, data: DataType[T]["req"]): Promise["receiver"]> async get(key: string, data: any) { - const hash = this._hash() + const hash = $hash() const req = JSON.stringify({ key: key, data: data, type: "get", hash } as WebSocketEvent["sender"]) this.ws.send(req) return new Promise((resolve, reject) => this._sync_list[hash] = resolve) @@ -73,7 +74,6 @@ export default class { event: { [key: string]: (enevt: WebSocketEvent["sender"]) => any } = {} private _info = "|websocket|" private _sync_list: { [hash: string]: (req: WebSocketEvent["receiver"]) => void } = {} - private _hash() { let result = ''; const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; for (let i = 0; i < 12; i++)result += characters.charAt(Math.floor(Math.random() * 62)); return result } } interface WebSocketEvent { diff --git a/types/protocol/index.d.ts b/types/protocol/index.d.ts index aa67dca..eaa8ae0 100644 --- a/types/protocol/index.d.ts +++ b/types/protocol/index.d.ts @@ -5,8 +5,8 @@ export interface DataType req : { option:DataType["build"]["option"], imgs:{ - name: string, - data: string, + name: string, // 圖片名稱 + data: string, // 圖片base64 }[] }, option:{ @@ -20,6 +20,21 @@ export interface DataType }, resolve:boolean, }, + compression:{ + req : { + option:DataType["compression"]["option"], + imgs:{ + name: string, // 圖片名稱 + data: string, // 圖片base64 + }[] + }, + option:{ + quality:[number,number], + speed:number + }, + resolve:boolean, + }, + // config:{ req:undefined, resolve:config @@ -32,6 +47,13 @@ export interface DataType }[] resolve:void } + logger:{ + req:{ + title: string; + content: string; + }, + resolve:void + } } export interface WorkerDataType @@ -43,4 +65,11 @@ export interface WorkerDataType base64img: string; }[] } + compression:{ + option:DataType["compression"]["option"], + data:{ + name: string; + data: string; + }[] + } } \ No newline at end of file diff --git a/view/src/components/logger.vue b/view/src/components/logger.vue index b9eff6f..bc9f947 100644 --- a/view/src/components/logger.vue +++ b/view/src/components/logger.vue @@ -20,6 +20,11 @@ export default defineComponent({ logger: toRef(useStore().state, "logger"), }; }, + created() { + this.$ws.on("logger", (log) => { + this.logger.push(log.data); + }); + }, }); diff --git a/view/src/components/view_files.vue b/view/src/components/view_files.vue index 1e51d50..20923b1 100644 --- a/view/src/components/view_files.vue +++ b/view/src/components/view_files.vue @@ -48,7 +48,9 @@ + + diff --git a/view/src/views/home.vue b/view/src/views/home.vue index d43cec9..d14729e 100644 --- a/view/src/views/home.vue +++ b/view/src/views/home.vue @@ -103,7 +103,6 @@ export default defineComponent({ }; return { imgs: [] as { name: string; link_url: string; file: File }[], - dragenter: 0, show_build_type_list: false, edit_setting, build_type: [ @@ -135,7 +134,7 @@ export default defineComponent({ get_bg_url(event: Event) { let url = (event.target as HTMLInputElement).value?.trim(); - if (!/\.(gif|jpe?g|png)$/i.test(url) || url.indexOf("http") !== 0) + if (!/\.(jpe?g|png)$/i.test(url) || url.indexOf("http") !== 0) return (this.edit_setting.background.url = ""); const index = url.indexOf("steam.design/#"); // length === 14 @@ -149,7 +148,7 @@ export default defineComponent({ const files = Array.from($files); //轉換成陣列 for (const file of files) { //驗證檔案類型 - if (!/\.(gif|jpe?g|png)$/i.test(file.name)) { + if (!/\.(jpe?g|png)$/i.test(file.name)) { this.log(`錯誤類型`, ""); return; } diff --git a/view/src/views/settings.vue b/view/src/views/settings.vue index f683d60..0a92b14 100644 --- a/view/src/views/settings.vue +++ b/view/src/views/settings.vue @@ -102,7 +102,9 @@ export default defineComponent({ }, }, async created() { - this.$ws.get("config", undefined).then((res) => (this.config = res.data)); + this.$ws + .get("config", undefined) + .then((res) => (this.config = Object.assign(this.config, res.data))); }, });