@muhkoo/theater-transcoder 0.1.1 → 0.2.1

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@muhkoo/theater-transcoder",
3
- "version": "0.1.1",
3
+ "version": "0.2.1",
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": {
@@ -27,6 +27,7 @@
27
27
  "dependencies": {
28
28
  "@muhkoo/connect": "0.10.1-alpha.0",
29
29
  "ffmpeg-static": "^5.2.0",
30
+ "node-datachannel": "^0.12.0",
30
31
  "snarkjs": "^0.7.5",
31
32
  "ws": "^8.18.0"
32
33
  }
package/src/client.js CHANGED
@@ -11,11 +11,19 @@ import { mkdtemp, writeFile } from "node:fs/promises";
11
11
  import { tmpdir } from "node:os";
12
12
  import { join } from "node:path";
13
13
  import { WebSocket } from "ws";
14
+ import * as ndc from "node-datachannel/polyfill";
14
15
  import { Client } from "@muhkoo/connect";
15
16
 
16
17
  // Node 20's global WebSocket is flag-gated; ensure the Space socket has one.
17
18
  if (!globalThis.WebSocket) globalThis.WebSocket = WebSocket;
18
19
 
20
+ // WebRTC polyfill so connect's P2P (browser-only by default) runs in Node — the
21
+ // worker then pulls raw shards directly from the uploader's browser over the LAN.
22
+ // `isP2pCapable()` only checks for RTCPeerConnection; set the whole surface.
23
+ for (const k of ["RTCPeerConnection", "RTCDataChannel", "RTCSessionDescription", "RTCIceCandidate"]) {
24
+ if (!globalThis[k] && ndc[k]) globalThis[k] = ndc[k];
25
+ }
26
+
19
27
  /** snarkjs (Node) reads circuit assets off disk — download them to a temp dir. */
20
28
  async function downloadCircuits(baseUrl) {
21
29
  const dir = await mkdtemp(join(tmpdir(), "muhkoo-circuits-"));
@@ -47,7 +55,10 @@ export async function connect({ baseUrl, appKey, username, password }) {
47
55
  apiKey: appKey,
48
56
  circuits,
49
57
  offline: { enabled: false },
50
- p2p: { enabled: false },
58
+ // P2P on: fetch raw shards from a browser peer over WebRTC. Empty iceServers
59
+ // → direct LAN (host) candidates, no STUN — ideal same-network transcoding;
60
+ // origin remains the fallback. The block engine runs main-thread in Node.
61
+ p2p: { enabled: true, iceServers: [], debug: process.env.MUHKOO_P2P_DEBUG === "1" },
51
62
  });
52
63
  const user = await client.auth.zk.login(username, password);
53
64
  return { client, user };
package/src/worker.js CHANGED
@@ -5,11 +5,14 @@
5
5
  * set) are claimable here — a browser's freshly-created job is stored in the
6
6
  * background, and the browser can also process it locally from memory.
7
7
  */
8
- import { hostname } from "node:os";
8
+ import { hostname, cpus } from "node:os";
9
9
  import { transcodeToHls } from "./transcode.js";
10
10
 
11
11
  const WORKER_ID = `node-${Math.random().toString(36).slice(2, 10)}`;
12
12
  const WORKER_LABEL = `the transcoder (${hostname()})`;
13
+ // Capability score: native ffmpeg on many cores far outranks a browser's wasm
14
+ // (cores×~30), so browsers defer manifest-ready jobs to this machine.
15
+ const MY_SCORE = cpus().length * 30;
13
16
  const STALE_MS = 90_000;
14
17
  const POLL_MS = 4_000;
15
18
 
@@ -104,9 +107,13 @@ export function startWorker({ client, space, appKey, baseUrl }) {
104
107
 
105
108
  let stopped = false;
106
109
  (async () => {
107
- log(`worker ${WORKER_ID} draining queue as ${client.auth.zk.user.username} (${WORKER_LABEL})`);
108
- // Real-time wake on new jobs; the poll below is the durable fallback.
109
- space.onMessage((e) => { const b = e?.message?.body; if (b?._t === "jobsig" && b.kind === "new" && b.src !== WORKER_ID) tick(); });
110
+ log(`worker ${WORKER_ID} draining queue as ${client.auth.zk.user.username} (${WORKER_LABEL}, score ${MY_SCORE})`);
111
+ // Announce capability so browsers defer manifest-ready jobs to us.
112
+ const hello = () => void postSignal({ kind: "hello", score: MY_SCORE, worker: WORKER_LABEL });
113
+ hello(); setInterval(hello, 4_000);
114
+ // Real-time wake on new jobs; also re-announce presence right away so the
115
+ // uploader learns a strong worker is here and defers to us. Poll is fallback.
116
+ space.onMessage((e) => { const b = e?.message?.body; if (b?._t === "jobsig" && b.kind === "new" && b.src !== WORKER_ID) { hello(); tick(); } });
110
117
  let ticking = false;
111
118
  async function tick() {
112
119
  if (ticking || stopped) return;