@muhkoo/theater-transcoder 0.3.1 → 0.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +4 -3
- package/src/client.js +10 -4
- package/src/worker.js +44 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@muhkoo/theater-transcoder",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.3",
|
|
4
4
|
"description": "Drain a Muhkoo Theater transcode queue with NATIVE ffmpeg. Run it on any machine signed in as you to add transcoding power to your library.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -25,10 +25,11 @@
|
|
|
25
25
|
"access": "public"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@muhkoo/connect": "0.10.
|
|
28
|
+
"@muhkoo/connect": "^0.10.3-alpha.0",
|
|
29
29
|
"ffmpeg-static": "^5.2.0",
|
|
30
30
|
"node-datachannel": "^0.12.0",
|
|
31
31
|
"snarkjs": "^0.7.5",
|
|
32
32
|
"ws": "^8.18.0"
|
|
33
|
-
}
|
|
33
|
+
},
|
|
34
|
+
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
|
|
34
35
|
}
|
package/src/client.js
CHANGED
|
@@ -55,10 +55,16 @@ export async function connect({ baseUrl, appKey, username, password }) {
|
|
|
55
55
|
apiKey: appKey,
|
|
56
56
|
circuits,
|
|
57
57
|
offline: { enabled: false },
|
|
58
|
-
// P2P on: fetch raw shards from a browser peer over WebRTC
|
|
59
|
-
//
|
|
60
|
-
//
|
|
61
|
-
|
|
58
|
+
// P2P on: fetch raw shards from a browser peer over WebRTC instead of
|
|
59
|
+
// round-tripping R2. We must let the default STUN server apply (do NOT pass
|
|
60
|
+
// an empty iceServers — that disables STUN entirely, so WebRTC can't gather
|
|
61
|
+
// server-reflexive candidates and the browser↔Node connection never forms).
|
|
62
|
+
// Origin stays the fallback; the block engine runs main-thread in Node.
|
|
63
|
+
p2p: {
|
|
64
|
+
enabled: true,
|
|
65
|
+
...(process.env.MUHKOO_STUN ? { iceServers: [{ urls: process.env.MUHKOO_STUN }] } : {}),
|
|
66
|
+
debug: process.env.MUHKOO_P2P_DEBUG === "1",
|
|
67
|
+
},
|
|
62
68
|
});
|
|
63
69
|
const user = await client.auth.zk.login(username, password);
|
|
64
70
|
return { client, user };
|
package/src/worker.js
CHANGED
|
@@ -38,6 +38,33 @@ export function startWorker({ client, space, appKey, baseUrl }) {
|
|
|
38
38
|
|
|
39
39
|
const patch = (id, values) => jobs().update(id, { ...values, updated_at: Date.now() });
|
|
40
40
|
|
|
41
|
+
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
42
|
+
|
|
43
|
+
// Fetch the raw by manifest, retrying while the uploader's background upload
|
|
44
|
+
// finishes landing on origin (or a P2P peer appears). A missing-shard error is
|
|
45
|
+
// transient here — the bytes are still arriving — so we back off and retry
|
|
46
|
+
// instead of failing the job. Only a genuine, persistent gap eventually throws.
|
|
47
|
+
async function fetchRawWithRetry(space, manifest, job) {
|
|
48
|
+
const deadline = Date.now() + 8 * 60_000; // headroom for a slow big-file upload
|
|
49
|
+
let attempt = 0;
|
|
50
|
+
for (;;) {
|
|
51
|
+
try {
|
|
52
|
+
const { data } = await space.getFile(manifest);
|
|
53
|
+
return data;
|
|
54
|
+
} catch (e) {
|
|
55
|
+
const msg = String(e?.message || e);
|
|
56
|
+
const transient = /shard|unrecoverable|missing|404|network|fetch|econn|timeout/i.test(msg);
|
|
57
|
+
if (!transient || Date.now() > deadline) throw e;
|
|
58
|
+
attempt++;
|
|
59
|
+
const wait = Math.min(8000, 1500 * attempt);
|
|
60
|
+
log(`raw not fully available yet (${msg.slice(0, 70)}) — retry ${attempt} in ${wait}ms`);
|
|
61
|
+
await patch(job._id, { phase: "Fetching raw… (uploading)", heartbeat_at: Date.now() }).catch(() => {});
|
|
62
|
+
await postSignal({ kind: "progress", jobId: job._id, progress: 0, phase: "Fetching raw… (uploading)", worker: WORKER_LABEL });
|
|
63
|
+
await sleep(wait);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
41
68
|
async function claimNext() {
|
|
42
69
|
const { rows } = await jobs().query({
|
|
43
70
|
where: [{ column: "owner", op: "eq", value: commitment }],
|
|
@@ -75,11 +102,25 @@ export function startWorker({ client, space, appKey, baseUrl }) {
|
|
|
75
102
|
}
|
|
76
103
|
};
|
|
77
104
|
try {
|
|
78
|
-
log(`claim job ${job._id} "${job.title}" —
|
|
105
|
+
log(`claim job ${job._id} "${job.title}" — fetching raw (P2P → origin)…`);
|
|
106
|
+
// Surface the fetch stage so the uploader's UI shows "Fetching raw… on the
|
|
107
|
+
// transcoder" instead of a silent stall while the bytes transfer.
|
|
108
|
+
await patch(job._id, { phase: "Fetching raw…", progress: 0 }).catch(() => {});
|
|
109
|
+
await postSignal({ kind: "progress", jobId: job._id, progress: 0, phase: "Fetching raw…", worker: WORKER_LABEL });
|
|
79
110
|
// The DB json column can hand back the manifest as a STRING — parse it, or
|
|
80
|
-
//
|
|
111
|
+
// getFile chokes ("manifest.chunks is not iterable").
|
|
81
112
|
const manifest = typeof job.input_manifest === "string" ? JSON.parse(job.input_manifest) : job.input_manifest;
|
|
82
|
-
|
|
113
|
+
// Read through the Space's getFile (NOT client.storage.readByManifest):
|
|
114
|
+
// only the Space file surface has the P2P peer source wired, so this pulls
|
|
115
|
+
// shards from the uploader's browser over WebRTC first, origin as fallback.
|
|
116
|
+
//
|
|
117
|
+
// Because the uploader returns the manifest as soon as shards are cached +
|
|
118
|
+
// announced (background upload), origin may not hold every shard yet when we
|
|
119
|
+
// first ask — and if P2P can't reach the uploader, a shard 404s and getFile
|
|
120
|
+
// fails "N shards missing". That's transient: the upload is still landing, so
|
|
121
|
+
// retry (with backoff) until we can reassemble or we time out. The heartbeat
|
|
122
|
+
// ticker keeps our claim alive throughout.
|
|
123
|
+
const data = await fetchRawWithRetry(space, manifest, job);
|
|
83
124
|
log(`transcoding ${(data.length / 1e6).toFixed(1)}MB…`);
|
|
84
125
|
const { hls_index, duration, size } = await transcodeToHls({
|
|
85
126
|
rawBytes: data, filename: job.filename || "input.mp4", space, onProgress, signal: abort.signal,
|