diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index b33247e2..97d85ac4 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -29,7 +29,14 @@ export default defineConfig({ }, vite: { clearScreen: false, - server: { + server: { + proxy: { + '/socket': { + target: 'ws://localhost:8080', + ws: true, + rewriteWsOrigin: true, + } + }, // make sure this port matches the devUrl port in tauri.conf.json file port: 5173, // Tauri expects a fixed port, fail if that port is not available diff --git a/docs/.vitepress/theme/components/Downloader.vue b/docs/.vitepress/theme/components/Downloader.vue index d1889d41..359a77a7 100644 --- a/docs/.vitepress/theme/components/Downloader.vue +++ b/docs/.vitepress/theme/components/Downloader.vue @@ -1,69 +1,97 @@ \ No newline at end of file + diff --git a/docs/qVVhBGT0K7Y/2636.png b/docs/qVVhBGT0K7Y/2636.png new file mode 100644 index 00000000..7a8a702b Binary files /dev/null and b/docs/qVVhBGT0K7Y/2636.png differ diff --git a/package.json b/package.json index 98f6ab83..a7ff74ed 100644 --- a/package.json +++ b/package.json @@ -1,21 +1,24 @@ { "name": "yt2press", "version": "1.0.0", - "main": "index.js", + "main": "server.js", + "type": "module", "license": "MIT", "dependencies": { "@tauri-apps/api": "^2", - "@tauri-apps/plugin-opener": "^2" + "@tauri-apps/plugin-opener": "^2", + "ws": "^8.19.0" }, "devDependencies": { "@tauri-apps/cli": "^2.9.6", "@types/node": "^20.19.30", + "@types/ws": "^8.18.1", "vite": "^7.3.1", "vitepress": "2.0.0-alpha.15", "vue": "^3.5.27" }, "scripts": { - "start": "tauri dev", + "start": "node .", "dev": "vitepress dev docs", "build": "vitepress build docs", "preview": "vitepress preview docs" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8dc140db..a8722171 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,6 +14,9 @@ importers: '@tauri-apps/plugin-opener': specifier: ^2 version: 2.5.3 + ws: + specifier: ^8.19.0 + version: 8.19.0 devDependencies: '@tauri-apps/cli': specifier: ^2.9.6 @@ -21,6 +24,9 @@ importers: '@types/node': specifier: ^20.19.30 version: 20.19.30 + '@types/ws': + specifier: ^8.18.1 + version: 8.18.1 vite: specifier: ^7.3.1 version: 7.3.1(@types/node@20.19.30) @@ -611,6 +617,9 @@ packages: '@types/web-bluetooth@0.0.21': resolution: {integrity: sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==} + '@types/ws@8.18.1': + resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} + '@ungap/structured-clone@1.3.0': resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} @@ -1031,6 +1040,18 @@ packages: typescript: optional: true + ws@8.19.0: + resolution: {integrity: sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + zod@4.3.5: resolution: {integrity: sha512-k7Nwx6vuWx1IJ9Bjuf4Zt1PEllcwe7cls3VNzm4CQ1/hgtFUK2bRNG3rvnpPUhFjmqJKAKtjV576KnUkHocg/g==} @@ -1504,6 +1525,10 @@ snapshots: '@types/web-bluetooth@0.0.21': {} + '@types/ws@8.18.1': + dependencies: + '@types/node': 20.19.30 + '@ungap/structured-clone@1.3.0': {} '@vercel/oidc@3.1.0': {} @@ -1994,6 +2019,8 @@ snapshots: '@vue/server-renderer': 3.5.27(vue@3.5.27) '@vue/shared': 3.5.27 + ws@8.19.0: {} + zod@4.3.5: {} zwitch@2.0.4: {} diff --git a/server.js b/server.js new file mode 100644 index 00000000..c9a5c050 --- /dev/null +++ b/server.js @@ -0,0 +1,58 @@ +// @ts-check +import { WebSocketServer } from 'ws'; +import { readFileSync, mkdirSync, writeFileSync } from "fs"; + +const PORT = 8080 + +const wss = new WebSocketServer({ port: PORT }); + +console.log(`WebSocket server running on ws://localhost:${PORT}`); + +wss.on('connection', (ws) => { + console.log('New client connected'); + + ws.on('message', (message) => { + const { name, ...payload } = JSON.parse(message.toString('utf-8')); + console.log(name, payload) + switch (name) { + case 'download_q': { + const { q } = payload; + + try { + mkdirSync(`docs/${q}`); + } catch (e) {} + + let outMd = ""; + + const re = /^(?\d{1,2}):(?\d{2})\s*$/; + + for (const line of readFileSync(`docs/${q}.md`, "utf-8").trimEnd().split("\n")) { + const m = re.exec(line); + if (m?.groups) { + const { min, sec } = m.groups; + const t = parseInt(min) * 60 + parseInt(sec); + ws.send(JSON.stringify({ name: "seek_q", t })); + outMd += `\n[${line}](./${q}/${t}.png)\n\n`; + } else { + outMd += `${line}\n`; + } + } + + writeFileSync(`docs/${q}.md`, outMd); + } break; + case 'timed_q' : { + const { q, t, data } = payload; + + writeFileSync( + `docs/${q}/${t}.png`, + data.replace(/^data:image\/png;base64,/, ""), + "base64", + ); + } break; + } + }); + + ws.on('close', () => { + console.log('Client disconnected'); + }); +}); diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index c03488fd..47d3cbd1 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -1,9 +1,17 @@ -use tauri::Listener; -use serde::Deserialize; +use tauri::{Listener, AppHandle, Emitter}; +use serde::{Serialize, Deserialize}; + +#[derive(Clone, Serialize)] +#[serde(rename_all = "camelCase")] +struct DownloadProgress { + t: u16, +} #[tauri::command] -fn download_q(q: String) { +fn download_q(app: AppHandle, q: String) { println!("Download q! {q}"); + + app.emit("seek_q", DownloadProgress { t: 0 }).unwrap(); } #[tauri::command]