// @ts-check import { WebSocketServer } from "ws"; import { readFileSync, mkdirSync, writeFileSync } from "fs"; import { join as joinPath } from "path"; import matter from "gray-matter"; import { renderFile } from "./group-similar.js"; const MD_ROOT_DIR = "docs"; const IMG_ROOT_DIR = "docs/public/images"; 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"); let filename = ""; ws.on("message", (message, isBinary) => { if (message instanceof Buffer && isBinary) { writeFileSync(joinPath(IMG_ROOT_DIR, filename), message); return; } const { name, ...payload } = JSON.parse(message.toString("utf-8")); switch (name) { case "download_q": { const { q, duration } = payload; console.log(`Start ${q}`); const md = loadMdFile(q); const q_stem = make_q_stem(q); try { mkdirSync(joinPath(IMG_ROOT_DIR, q_stem)); } catch (e) {} let isChanged = false; const ts = []; md.data.video = "/" + q; const { interval } = md.data; if (typeof interval === "number") { for (let t = 0; t < duration; t += interval) { const min = Math.floor(t / 60); const sec = Math.floor(t - min * 60); md.content += `\n${min}:${sec.toString().padStart(2, "0")}\n`; } } const re = /^(?\d{1,2}):(?\d{2})\s*$/; const lines = md.content.split("\n"); let newContent = ""; for (const line of lines) { const m = re.exec(line); if (m?.groups) { const { min, sec } = m.groups; const t = `${min}_${sec}`; ts.push(t); newContent += `![${line}](/${q_stem}/${t}.webp)\n`; newContent += `${line}\n`; newContent += `\n`; isChanged = true; } else { newContent += `${line}\n`; } } if (isChanged) { writeFileSync(md.filename, matter.stringify(newContent, md.data)); ws.send(JSON.stringify({ name: "seek_q_ts", q, ts })); } } break; case "timed_q": { const { q, t } = payload; const q_stem = make_q_stem(q); filename = t ? joinPath(q_stem, `${t}.webp`) : ""; } break; case "done": { const { q } = payload; const md = loadMdFile(q); console.log(`Done basic parsing ${q}`); const { dedup } = md.data; if (dedup) { console.log(`Deduping ${md.filename}`); renderFile( md.filename, md.filename, typeof dedup === "number" ? dedup : undefined, ).then((stats) => { // Don't log a thing yet. console.log(`Done deduping ${md.filename}`); }); } console.log(`Done ${q}`); } break; } }); ws.on("close", () => { console.log("Client disconnected"); }); }); /** * * @param {string} q * @returns */ function loadMdFile(q) { const q_stem = make_q_stem(q); try { mkdirSync(joinPath(MD_ROOT_DIR, q_stem)); } catch (e) {} const filename = joinPath(MD_ROOT_DIR, `${q_stem}.md`); try { // create a blank md file if not exists readFileSync(filename, { flag: "a" }); } catch (e) {} return { filename, ...matter(readFileSync(filename, "utf-8")) }; } /** * * @param {string} q * @returns */ function make_q_stem(q) { return q .replace(/\.[a-z0-9]+$/, "") .replace(/\[.+?\]/g, "") .trim() .replace(/\s+/g, "_"); }