ws that starts to work
This commit is contained in:
1 parent
fc58e68ad7
commit
0f4458912f
7 files changed
+177
-46
No files matched your search
@@ -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
|
||||
|
||||
@@ -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>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 765 KiB |
+6
-3
@@ -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"
|
||||
|
||||
Generated
+27
@@ -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: {}
|
||||
@@ -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
@@ -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]
|
||||
|
||||
Reference in new issue
Block a user