system HF Staff commited on
Commit
972ea65
·
verified ·
1 Parent(s): 1157f09

Deploy Tactiq Playwright from V21 e512f9f

Browse files
__pycache__/app.cpython-312.pyc CHANGED
Binary files a/__pycache__/app.cpython-312.pyc and b/__pycache__/app.cpython-312.pyc differ
 
transcript-backend/server.mjs ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import http from "node:http";
2
+ import { URL } from "node:url";
3
+ import { fetchTranscript } from "youtube-transcript-plus";
4
+
5
+ const PORT = Number(process.env.TRANSCRIPT_PORT || 8765);
6
+ const HOST = process.env.TRANSCRIPT_HOST || "127.0.0.1";
7
+ const ALLOW_ORIGIN = process.env.ALLOW_ORIGIN || "*";
8
+
9
+ function json(res, status, payload) {
10
+ res.writeHead(status, {
11
+ "Content-Type": "application/json; charset=utf-8",
12
+ "Access-Control-Allow-Origin": ALLOW_ORIGIN,
13
+ "Access-Control-Allow-Methods": "GET, OPTIONS",
14
+ "Access-Control-Allow-Headers": "Content-Type",
15
+ "Cache-Control": "no-store",
16
+ });
17
+ res.end(JSON.stringify(payload));
18
+ }
19
+
20
+ function extractVideoId(value) {
21
+ const s = String(value || "").trim();
22
+ if (/^[A-Za-z0-9_-]{11}$/.test(s)) return s;
23
+ try {
24
+ const u = new URL(s);
25
+ if (u.hostname === "youtu.be") return u.pathname.split("/").filter(Boolean)[0] || null;
26
+ const v = u.searchParams.get("v");
27
+ if (v && /^[A-Za-z0-9_-]{11}$/.test(v)) return v;
28
+ const parts = u.pathname.split("/").filter(Boolean);
29
+ if (parts.length >= 2 && ["embed", "shorts", "live"].includes(parts[0])) return parts[1];
30
+ } catch {}
31
+ return null;
32
+ }
33
+
34
+ function normalize(items) {
35
+ return (Array.isArray(items) ? items : []).map((x, i) => ({
36
+ index: i + 1,
37
+ start: Number(x.offset ?? x.start ?? x.startTime ?? 0),
38
+ duration: Number(x.duration ?? 0),
39
+ text: String(x.text ?? x.content ?? "").replace(/\s+/g, " ").trim(),
40
+ })).filter(x => x.text);
41
+ }
42
+
43
+ const server = http.createServer(async (req, res) => {
44
+ if (req.method === "OPTIONS") return json(res, 204, {});
45
+ const u = new URL(req.url, `http://${req.headers.host || "localhost"}`);
46
+
47
+ if (u.pathname === "/health") return json(res, 200, { ok: true, service: "english-lab-transcript-backend" });
48
+ if (u.pathname !== "/transcript") return json(res, 404, { ok: false, error: "not_found" });
49
+
50
+ const video = extractVideoId(u.searchParams.get("video") || u.searchParams.get("url"));
51
+ const lang = u.searchParams.get("lang") || undefined;
52
+ if (!video) return json(res, 400, { ok: false, error: "invalid_youtube_video_id" });
53
+
54
+ try {
55
+ const config = lang ? { lang, retries: 2, retryDelay: 500 } : { retries: 2, retryDelay: 500 };
56
+ const raw = await fetchTranscript(video, config);
57
+ const segments = normalize(raw);
58
+ return json(res, 200, {
59
+ ok: true,
60
+ videoId: video,
61
+ language: lang || null,
62
+ count: segments.length,
63
+ segments,
64
+ });
65
+ } catch (error) {
66
+ return json(res, 502, {
67
+ ok: false,
68
+ videoId: video,
69
+ error: "transcript_fetch_failed",
70
+ message: String(error?.message || error),
71
+ });
72
+ }
73
+ });
74
+
75
+ server.listen(PORT, HOST, () => {
76
+ console.log(`English Lab transcript backend listening on http://${HOST}:${PORT}`);
77
+ });