real file with dedup
This commit is contained in:
1 parent
963549f22d
commit
f143f1e511
4 files changed
+3017
-30
No files matched your search
@@ -1,6 +1,6 @@
|
|||||||
# yt2press
|
# 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
|
## How to Use
|
||||||
|
|
||||||
@@ -89,7 +89,7 @@ docs/ # VitePress documentation root
|
|||||||
components/
|
components/
|
||||||
Downloader.vue # Vue component for the downloader interface
|
Downloader.vue # Vue component for the downloader interface
|
||||||
Video.vue # Vue component to display videos and linked timestamps
|
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
|
*.md # Markdown files with content
|
||||||
[video-filename]/ # Screenshot folders (auto-generated)
|
[video-filename]/ # Screenshot folders (auto-generated)
|
||||||
*.png # Timestamped screenshots
|
*.png # Timestamped screenshots
|
||||||
|
|||||||
File diff suppressed because it is too large.
Load diff
@@ -6,8 +6,8 @@ import matter from "gray-matter";
|
|||||||
|
|
||||||
import { renderFile } from "./group-similar.js";
|
import { renderFile } from "./group-similar.js";
|
||||||
|
|
||||||
|
const ROOT_DIR = "docs";
|
||||||
const PORT = 8080;
|
const PORT = 8080;
|
||||||
|
|
||||||
const wss = new WebSocketServer({ port: PORT });
|
const wss = new WebSocketServer({ port: PORT });
|
||||||
|
|
||||||
console.log(`WebSocket server running on ws://localhost:${PORT}`);
|
console.log(`WebSocket server running on ws://localhost:${PORT}`);
|
||||||
@@ -16,7 +16,6 @@ wss.on("connection", (ws) => {
|
|||||||
console.log("New client connected");
|
console.log("New client connected");
|
||||||
|
|
||||||
let filename = "";
|
let filename = "";
|
||||||
const ROOT_DIR = "docs";
|
|
||||||
|
|
||||||
ws.on("message", (message, isBinary) => {
|
ws.on("message", (message, isBinary) => {
|
||||||
if (message instanceof Buffer && isBinary) {
|
if (message instanceof Buffer && isBinary) {
|
||||||
@@ -33,61 +32,46 @@ wss.on("connection", (ws) => {
|
|||||||
const { q, duration } = payload;
|
const { q, duration } = payload;
|
||||||
console.log(`Start ${q}`);
|
console.log(`Start ${q}`);
|
||||||
|
|
||||||
const q_stem = q
|
const md = loadMdFile(q);
|
||||||
.replace(/\.[a-z0-9]+$/, "")
|
|
||||||
.replace(/\[.+?\]/g, "")
|
|
||||||
.replace(/\s+/g, "_")
|
|
||||||
.trim();
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
mkdirSync(joinPath(ROOT_DIR, q_stem));
|
mkdirSync(joinPath(ROOT_DIR, md.q_stem));
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|
||||||
let isChanged = false;
|
let isChanged = false;
|
||||||
const ts = [];
|
const ts = [];
|
||||||
|
|
||||||
const mdFile = joinPath(ROOT_DIR, `${q_stem}.md`);
|
md.data.video = "/" + q;
|
||||||
let { data, content } = matter(readFileSync(mdFile, "utf-8"));
|
|
||||||
data.video = "/" + q;
|
|
||||||
|
|
||||||
const { dedup, interval } = data;
|
const { interval } = md.data;
|
||||||
if (typeof interval === "number") {
|
if (typeof interval === "number") {
|
||||||
for (let t = 0; t < duration; t += interval) {
|
for (let t = 0; t < duration; t += interval) {
|
||||||
const min = Math.floor(t / 60);
|
const min = Math.floor(t / 60);
|
||||||
const sec = Math.floor(t - min * 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 re = /^(?<min>\d{1,2}):(?<sec>\d{2})\s*$/;
|
||||||
const lines = content.split("\n");
|
const lines = md.content.split("\n");
|
||||||
content = "";
|
|
||||||
|
let newContent = "";
|
||||||
for (const line of lines) {
|
for (const line of lines) {
|
||||||
const m = re.exec(line);
|
const m = re.exec(line);
|
||||||
if (m?.groups) {
|
if (m?.groups) {
|
||||||
const { min, sec } = m.groups;
|
const { min, sec } = m.groups;
|
||||||
const t = `${min}_${sec}`;
|
const t = `${min}_${sec}`;
|
||||||
ts.push(t);
|
ts.push(t);
|
||||||
content += `\n\n\n`;
|
newContent += `\n\n\n`;
|
||||||
isChanged = true;
|
isChanged = true;
|
||||||
} else {
|
} else {
|
||||||
content += `${line}\n`;
|
newContent += `${line}\n`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isChanged) {
|
if (isChanged) {
|
||||||
writeFileSync(mdFile, matter.stringify(content, data));
|
writeFileSync(md.filename, matter.stringify(newContent, md.data));
|
||||||
|
|
||||||
if (dedup) {
|
|
||||||
renderFile(
|
|
||||||
mdFile,
|
|
||||||
mdFile,
|
|
||||||
typeof dedup === "number" ? dedup : undefined,
|
|
||||||
).then((stats) => {
|
|
||||||
// Don't log a thing yet.
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
ws.send(JSON.stringify({ name: "seek_q_ts", q, ts }));
|
ws.send(JSON.stringify({ name: "seek_q_ts", q, ts }));
|
||||||
}
|
}
|
||||||
@@ -106,6 +90,24 @@ wss.on("connection", (ws) => {
|
|||||||
case "done":
|
case "done":
|
||||||
{
|
{
|
||||||
const { q } = payload;
|
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}`);
|
console.log(`Done ${q}`);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -116,3 +118,23 @@ wss.on("connection", (ws) => {
|
|||||||
console.log("Client disconnected");
|
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")) };
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"folders": [
|
||||||
|
{
|
||||||
|
"path": "."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "docs"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "docs/public"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"settings": {
|
||||||
|
"json.validate.enable": false
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user