real file with dedup

This commit is contained in:
polv committed 2026-02-06 13:08:50 +07:00
1 parent 963549f22d
commit f143f1e511
4 files changed
+3017 -30

No files matched your search

+2 -2
View File
@@ -1,6 +1,6 @@
# yt2press
A tool to convert YouTube videos into press-ready markdown documents with timestamped screenshots.
A tool to convert YouTube videos into VitePress web app with timestamped screenshots.
## How to Use
@@ -89,7 +89,7 @@ docs/ # VitePress documentation root
components/
Downloader.vue # Vue component for the downloader interface
Video.vue # Vue component to display videos and linked timestamps
public/ # Video storage directory
public/ # Directory to put downloaded videos
*.md # Markdown files with content
[video-filename]/ # Screenshot folders (auto-generated)
*.png # Timestamped screenshots
File diff suppressed because it is too large. Load diff
+50 -28
View File
@@ -6,8 +6,8 @@ import matter from "gray-matter";
import { renderFile } from "./group-similar.js";
const ROOT_DIR = "docs";
const PORT = 8080;
const wss = new WebSocketServer({ port: PORT });
console.log(`WebSocket server running on ws://localhost:${PORT}`);
@@ -16,7 +16,6 @@ wss.on("connection", (ws) => {
console.log("New client connected");
let filename = "";
const ROOT_DIR = "docs";
ws.on("message", (message, isBinary) => {
if (message instanceof Buffer && isBinary) {
@@ -33,61 +32,46 @@ wss.on("connection", (ws) => {
const { q, duration } = payload;
console.log(`Start ${q}`);
const q_stem = q
.replace(/\.[a-z0-9]+$/, "")
.replace(/\[.+?\]/g, "")
.replace(/\s+/g, "_")
.trim();
const md = loadMdFile(q);
try {
mkdirSync(joinPath(ROOT_DIR, q_stem));
mkdirSync(joinPath(ROOT_DIR, md.q_stem));
} catch (e) {}
let isChanged = false;
const ts = [];
const mdFile = joinPath(ROOT_DIR, `${q_stem}.md`);
let { data, content } = matter(readFileSync(mdFile, "utf-8"));
data.video = "/" + q;
md.data.video = "/" + q;
const { dedup, interval } = data;
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);
content += `\n${min}:${sec.toString().padStart(2, "0")}\n`;
md.content += `\n${min}:${sec.toString().padStart(2, "0")}\n`;
}
}
const re = /^(?<min>\d{1,2}):(?<sec>\d{2})\s*$/;
const lines = content.split("\n");
content = "";
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);
content += `\n![${line}](./${q_stem}/${t}.png)\n\n`;
newContent += `\n![${line}](./${md.q_stem}/${t}.png)\n\n`;
isChanged = true;
} else {
content += `${line}\n`;
newContent += `${line}\n`;
}
}
if (isChanged) {
writeFileSync(mdFile, matter.stringify(content, data));
if (dedup) {
renderFile(
mdFile,
mdFile,
typeof dedup === "number" ? dedup : undefined,
).then((stats) => {
// Don't log a thing yet.
});
}
writeFileSync(md.filename, matter.stringify(newContent, md.data));
ws.send(JSON.stringify({ name: "seek_q_ts", q, ts }));
}
@@ -106,6 +90,24 @@ wss.on("connection", (ws) => {
case "done":
{
const { q } = payload;
const md = loadMdFile(q);
console.log(`Done timestamp ${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 duduping ${md.filename}`);
});
}
console.log(`Done ${q}`);
}
break;
@@ -116,3 +118,23 @@ wss.on("connection", (ws) => {
console.log("Client disconnected");
});
});
/**
*
* @param {string} q
* @returns
*/
function loadMdFile(q) {
const q_stem = q
.replace(/\.[a-z0-9]+$/, "")
.replace(/\[.+?\]/g, "")
.trim()
.replace(/\s+/g, "_");
try {
mkdirSync(joinPath(ROOT_DIR, q_stem));
} catch (e) {}
const filename = joinPath(ROOT_DIR, `${q_stem}.md`);
return { q_stem, filename, ...matter(readFileSync(filename, "utf-8")) };
}
+16
View File
@@ -0,0 +1,16 @@
{
"folders": [
{
"path": "."
},
{
"path": "docs"
},
{
"path": "docs/public"
}
],
"settings": {
"json.validate.enable": false
}
}