ws that starts to work

This commit is contained in:
polv committed 2026-01-25 19:52:12 +07:00
1 parent fc58e68ad7
commit 0f4458912f
7 files changed
+178 -47

No files matched your search

+8 -1
View File
@@ -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
+68 -40
View File
@@ -1,69 +1,97 @@
<template>
<form @submit="onSubmit">
<input type="text" v-model="q">
<button type="submit">Submit</button>
<form @submit="onSubmit">
<input type="text" v-model="q" />
<button type="submit">Submit</button>
<video controls ref="video" :src="videoSrc" @loadeddata="onVideoLoad" @seeked="onSeeked"></video>
</form>
<video
controls
ref="video"
:src="videoSrc"
@loadeddata="onVideoLoad"
@seeked="onSeeked"
></video>
</form>
</template>
<script setup lang="ts">
import { ref, useTemplateRef } from 'vue'
import { invoke } from "@tauri-apps/api/core";
import { listen } from '@tauri-apps/api/event';
import { ref, useTemplateRef } from "vue";
const q = ref('')
const videoSrc = ref('')
const videoEl = useTemplateRef('video')
const q = ref("");
const videoSrc = ref("");
const videoEl = useTemplateRef("video");
const wsRef = ref<WebSocket>();
function onSubmit(ev: SubmitEvent) {
ev.preventDefault()
ev.preventDefault();
if (!q.value) return;
if (!q.value) return
videoSrc.value = '/' + q.value
}
interface SeekQEvent {
t: number
videoSrc.value = "/" + q.value;
}
function onVideoLoad() {
invoke('download_q', { q: q.value.replace(/\.[a-z0-9]+$/, '') })
const ws = new WebSocket("ws://localhost:8080");
wsRef.value = ws;
ws.onmessage = (ev) => {
console.log(ev.data)
const { name, t } = JSON.parse(ev.data);
switch (name) {
case "seek_q":
{
videoEl.value!.currentTime = t;
}
break;
}
};
listen<SeekQEvent>('seek_q', (ev) => {
videoEl.value!.currentTime = ev.payload.t;
})
ws.onopen = async () => {
ws.send(
JSON.stringify({
name: "download_q",
q: q.value.replace(/\.[a-z0-9]+$/, ""),
}),
);
};
ws.onclose = () => {
wsRef.value = undefined;
};
}
function onSeeked() {
const v = videoEl.value!
let c = document.createElement('canvas');
c.height = v.videoHeight || parseInt(v.style.height);
c.width = v.videoWidth || parseInt(v.style.width);
const ctx = c.getContext('2d')!;
ctx.drawImage(v, 0, 0);
const ws = wsRef.value;
if (!ws) return;
invoke('timed_q', { q: q.value.replace(/\.[a-z0-9]+$/, ''), t: videoEl.value!.currentTime, data: c.toDataURL('image/png') })
const v = videoEl.value!;
let c = document.createElement("canvas");
c.height = v.videoHeight || parseInt(v.style.height);
c.width = v.videoWidth || parseInt(v.style.width);
const ctx = c.getContext("2d")!;
ctx.drawImage(v, 0, 0);
ws.send(
JSON.stringify({
name: "timed_q",
q: q.value.replace(/\.[a-z0-9]+$/, ""),
t: videoEl.value!.currentTime,
data: c.toDataURL("image/png"),
}),
);
}
</script>
<style scoped>
form {
margin: 0.5em 0;
margin: 0.5em 0;
}
form * {
border: 1px solid gray;
border: 1px solid gray;
}
button {
margin-left: 0.5em;
cursor: pointer;
padding: 0 0.5em;
border-radius: 5px;
margin-left: 0.5em;
cursor: pointer;
padding: 0 0.5em;
border-radius: 5px;
}
</style>
</style>
Binary file not shown.

After

Width:  |  Height:  |  Size: 765 KiB

+6 -3
View File
@@ -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"
+27
View File
@@ -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: {}
+58
View File
@@ -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 = /^(?<min>\d{1,2}):(?<sec>\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');
});
});
+11 -3
View File
@@ -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]