haraberget commited on
Commit
98731fc
·
verified ·
1 Parent(s): c58e051

Create injection.js

Browse files
Files changed (1) hide show
  1. injection.js +375 -0
injection.js ADDED
@@ -0,0 +1,375 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ (() => {
3
+ // Stop the previous monitor before replacing its window.
4
+ if (window.hitRenderMonitorTimer) {
5
+ clearInterval(window.hitRenderMonitorTimer);
6
+ }
7
+ document.getElementById("hit-render-monitor")?.remove();
8
+
9
+ const POSITION_KEY = "hitRenderMonitorPosition";
10
+
11
+ const state = {
12
+ prompt: "",
13
+ progress: 0,
14
+ step: 0,
15
+ totalSteps: 0,
16
+ checkpoints: new Set(),
17
+ completedUrl: "",
18
+ lastReport: null,
19
+ minimized: false
20
+ };
21
+
22
+ const panel = document.createElement("div");
23
+ panel.id = "hit-render-monitor";
24
+
25
+ Object.assign(panel.style, {
26
+ position: "fixed",
27
+ right: "20px",
28
+ bottom: "20px",
29
+ width: "440px",
30
+ maxHeight: "85vh",
31
+ overflow: "hidden",
32
+ zIndex: "999999",
33
+ background: "#17191f",
34
+ color: "#eeeeee",
35
+ border: "1px solid #555",
36
+ borderRadius: "10px",
37
+ padding: "0",
38
+ font: "13px/1.45 monospace",
39
+ boxShadow: "0 5px 30px #0009"
40
+ });
41
+
42
+ panel.innerHTML = `
43
+ <div id="hit-header" style="
44
+ display:flex;
45
+ justify-content:space-between;
46
+ align-items:center;
47
+ padding:12px 14px;
48
+ background:#22252d;
49
+ border-radius:10px 10px 0 0;
50
+ cursor:grab;
51
+ user-select:none;
52
+ touch-action:none;
53
+ ">
54
+ <strong style="font-size:15px">
55
+ H3 Monitor <span id="hit-title-progress" style="color:#20b875">· Waiting</span>
56
+ </strong>
57
+ <div style="display:flex;gap:6px">
58
+ <button id="hit-minimize" title="Minimize" style="
59
+ background:#444;color:white;border:0;
60
+ padding:3px 10px;cursor:pointer;font-size:16px
61
+ ">−</button>
62
+ <button id="hit-close" title="Close" style="
63
+ background:#744;color:white;border:0;
64
+ padding:3px 9px;cursor:pointer;font-size:15px
65
+ ">×</button>
66
+ </div>
67
+ </div>
68
+ <div id="hit-content" style="
69
+ padding:14px;
70
+ max-height:calc(85vh - 45px);
71
+ overflow-y:auto;
72
+ ">
73
+ <div style="margin-top:2px">RENDER PROGRESS</div>
74
+ <div style="height:12px;background:#333;border-radius:8px;margin:6px 0;overflow:hidden">
75
+ <div id="hit-bar" style="height:100%;width:0%;background:#20b875;transition:width .3s"></div>
76
+ </div>
77
+ <div id="hit-progress" style="font-size:18px">Waiting for H3...</div>
78
+ <div id="hit-step" style="color:#aaa"></div>
79
+ <hr style="border-color:#444;margin:12px 0">
80
+ <div><strong>PROMPT INSPECTOR</strong></div>
81
+ <div id="hit-prompt" style="background:#242730;padding:8px;margin-top:6px;max-height:125px;overflow:auto;white-space:pre-wrap;overflow-wrap:anywhere;color:#ddd">Waiting for prompt...</div>
82
+ <hr style="border-color:#444;margin:12px 0">
83
+ <div style="display:flex;justify-content:space-between;align-items:center">
84
+ <strong>CALLBACK SIMULATOR</strong>
85
+ <label style="font-size:11px">
86
+ <input id="hit-report" type="checkbox" checked>
87
+ Report to 1hit.no
88
+ </label>
89
+ </div>
90
+ <div style="color:#e6b85c;font-size:11px;margin:4px 0 8px">
91
+ Simulation only — no network requests
92
+ </div>
93
+ <div id="hit-json" style="background:#090b0e;padding:9px;max-height:230px;overflow:auto;white-space:pre-wrap;overflow-wrap:anywhere;font-size:11px;color:#9fe5b2">Waiting for events...</div>
94
+ <hr style="border-color:#444;margin:12px 0">
95
+ <div><strong>OUTPUT URL</strong></div>
96
+ <div id="hit-url" style="color:#8fcaff;overflow-wrap:anywhere;margin-top:5px">Waiting for video...</div>
97
+ <div id="hit-status" style="color:#aaa;font-size:11px;margin-top:10px">Polling every 15 seconds</div>
98
+ </div>
99
+ `;
100
+
101
+ document.body.appendChild(panel);
102
+
103
+ const el = id => panel.querySelector("#" + id);
104
+
105
+ // Restore the previous position, if available.
106
+ try {
107
+ const saved = JSON.parse(sessionStorage.getItem(POSITION_KEY) || "null");
108
+
109
+ if (saved && Number.isFinite(saved.left) && Number.isFinite(saved.top)) {
110
+ panel.style.left = saved.left + "px";
111
+ panel.style.top = saved.top + "px";
112
+ panel.style.right = "auto";
113
+ panel.style.bottom = "auto";
114
+ }
115
+ } catch (e) {
116
+ console.warn("Could not restore monitor position:", e);
117
+ }
118
+
119
+ function savePosition() {
120
+ const rect = panel.getBoundingClientRect();
121
+
122
+ try {
123
+ sessionStorage.setItem(POSITION_KEY, JSON.stringify({
124
+ left: Math.round(rect.left),
125
+ top: Math.round(rect.top)
126
+ }));
127
+ } catch (e) {}
128
+ }
129
+
130
+ // Drag the monitor using its title bar.
131
+ const header = el("hit-header");
132
+ let dragging = false;
133
+ let dragOffsetX = 0;
134
+ let dragOffsetY = 0;
135
+
136
+ header.addEventListener("pointerdown", event => {
137
+ if (event.target.closest("button")) return;
138
+
139
+ dragging = true;
140
+
141
+ const rect = panel.getBoundingClientRect();
142
+ dragOffsetX = event.clientX - rect.left;
143
+ dragOffsetY = event.clientY - rect.top;
144
+
145
+ panel.style.right = "auto";
146
+ panel.style.bottom = "auto";
147
+ header.style.cursor = "grabbing";
148
+
149
+ header.setPointerCapture(event.pointerId);
150
+ event.preventDefault();
151
+ });
152
+
153
+ header.addEventListener("pointermove", event => {
154
+ if (!dragging) return;
155
+
156
+ const width = panel.offsetWidth;
157
+ const height = panel.offsetHeight;
158
+
159
+ const left = Math.max(
160
+ 0,
161
+ Math.min(window.innerWidth - width, event.clientX - dragOffsetX)
162
+ );
163
+
164
+ const top = Math.max(
165
+ 0,
166
+ Math.min(window.innerHeight - height, event.clientY - dragOffsetY)
167
+ );
168
+
169
+ panel.style.left = left + "px";
170
+ panel.style.top = top + "px";
171
+ });
172
+
173
+ function stopDragging() {
174
+ if (!dragging) return;
175
+
176
+ dragging = false;
177
+ header.style.cursor = "grab";
178
+ savePosition();
179
+ }
180
+
181
+ header.addEventListener("pointerup", stopDragging);
182
+ header.addEventListener("pointercancel", stopDragging);
183
+ header.addEventListener("lostpointercapture", stopDragging);
184
+
185
+ // Minimize and restore without stopping the monitor.
186
+ el("hit-minimize").onclick = () => {
187
+ state.minimized = !state.minimized;
188
+
189
+ el("hit-content").style.display =
190
+ state.minimized ? "none" : "block";
191
+
192
+ el("hit-minimize").textContent =
193
+ state.minimized ? "+" : "−";
194
+
195
+ el("hit-minimize").title =
196
+ state.minimized ? "Restore" : "Minimize";
197
+
198
+ panel.style.width = state.minimized ? "260px" : "440px";
199
+
200
+ savePosition();
201
+ };
202
+
203
+ function addEvent(data) {
204
+ if (!el("hit-report").checked) return;
205
+
206
+ const json = JSON.stringify(data, null, 2);
207
+ const log = el("hit-json");
208
+
209
+ log.textContent =
210
+ (log.textContent === "Waiting for events..." ? "" : log.textContent + "\n\n")
211
+ + json;
212
+
213
+ log.scrollTop = log.scrollHeight;
214
+ }
215
+
216
+ function getPrompt() {
217
+ const fields = [
218
+ ...document.querySelectorAll("textarea, [contenteditable='true']")
219
+ ];
220
+
221
+ const candidates = fields.map(node => ({
222
+ text: (node.value || node.innerText || node.textContent || "").trim(),
223
+ hint: [
224
+ node.getAttribute("aria-label"),
225
+ node.getAttribute("placeholder"),
226
+ node.parentElement?.innerText
227
+ ].filter(Boolean).join(" ").toLowerCase()
228
+ })).filter(x => x.text.length > 10);
229
+
230
+ const labelled = candidates
231
+ .filter(x => /prompt|description/.test(x.hint))
232
+ .sort((a, b) => b.text.length - a.text.length);
233
+
234
+ if (labelled.length) return labelled[0].text;
235
+
236
+ candidates.sort((a, b) => b.text.length - a.text.length);
237
+ return candidates[0]?.text || "";
238
+ }
239
+
240
+ function getVideoUrl() {
241
+ const candidates = [
242
+ ...document.querySelectorAll("video"),
243
+ ...document.querySelectorAll("video source"),
244
+ ...document.querySelectorAll("a[href]")
245
+ ];
246
+
247
+ for (const node of candidates) {
248
+ const url =
249
+ node.currentSrc ||
250
+ node.src ||
251
+ node.href ||
252
+ "";
253
+
254
+ if (url && (
255
+ /\.mp4(?:[?#]|$)/i.test(url) ||
256
+ url.startsWith("blob:")
257
+ )) {
258
+ return url;
259
+ }
260
+ }
261
+
262
+ return "";
263
+ }
264
+
265
+ function baseEvent(event) {
266
+ return {
267
+ session_id: "kaggle-" + location.host,
268
+ event,
269
+ render_url: location.href,
270
+ prompt: state.prompt,
271
+ progress: state.progress,
272
+ step: state.step,
273
+ total_steps: state.totalSteps,
274
+ timestamp: new Date().toISOString()
275
+ };
276
+ }
277
+
278
+ function poll() {
279
+ const prompt = getPrompt();
280
+
281
+ if (prompt && prompt !== state.prompt) {
282
+ state.prompt = prompt;
283
+ state.checkpoints.clear();
284
+ state.completedUrl = "";
285
+
286
+ el("hit-prompt").textContent = prompt;
287
+
288
+ addEvent({
289
+ ...baseEvent("prompt"),
290
+ message: "New prompt detected"
291
+ });
292
+ }
293
+
294
+ const messages = [
295
+ ...document.querySelectorAll('div[type="message"]')
296
+ ];
297
+
298
+ const matching = messages
299
+ .map(node => node.innerText || "")
300
+ .filter(text => text.includes("H3 denoising:"));
301
+
302
+ if (matching.length) {
303
+ const latest = matching[matching.length - 1];
304
+
305
+ const match = latest.match(
306
+ /H3 denoising:\s*(\d+)%.*?\|\s*(\d+)\/(\d+)/
307
+ );
308
+
309
+ if (match) {
310
+ const pct = Number(match[1]);
311
+ const step = Number(match[2]);
312
+ const total = Number(match[3]);
313
+
314
+ if (pct < state.progress && pct <= 25) {
315
+ state.checkpoints.clear();
316
+ state.completedUrl = "";
317
+ }
318
+
319
+ state.progress = pct;
320
+ state.step = step;
321
+ state.totalSteps = total;
322
+
323
+ el("hit-bar").style.width = pct + "%";
324
+ el("hit-progress").textContent = pct + "%";
325
+ el("hit-step").textContent = `Step ${step} / ${total}`;
326
+
327
+ el("hit-title-progress").textContent = "· " + pct + "%";
328
+
329
+ for (const threshold of [25, 50, 75, 100]) {
330
+ if (pct >= threshold && !state.checkpoints.has(threshold)) {
331
+ state.checkpoints.add(threshold);
332
+
333
+ addEvent({
334
+ ...baseEvent("progress"),
335
+ checkpoint: threshold
336
+ });
337
+ }
338
+ }
339
+ }
340
+ }
341
+
342
+ const videoUrl = getVideoUrl();
343
+
344
+ if (videoUrl) {
345
+ el("hit-url").textContent = videoUrl;
346
+
347
+ if (state.progress >= 100 && videoUrl !== state.completedUrl) {
348
+ state.completedUrl = videoUrl;
349
+
350
+ addEvent({
351
+ ...baseEvent("completed"),
352
+ video: {
353
+ url: videoUrl,
354
+ filename: videoUrl.split("/").pop().split("?")[0]
355
+ }
356
+ });
357
+ }
358
+ }
359
+
360
+ el("hit-status").textContent =
361
+ "Last scan: " + new Date().toLocaleTimeString() +
362
+ " · Polling every 15 seconds";
363
+ }
364
+
365
+ el("hit-close").onclick = () => {
366
+ clearInterval(window.hitRenderMonitorTimer);
367
+ window.hitRenderMonitorTimer = null;
368
+ panel.remove();
369
+ };
370
+
371
+ poll();
372
+ window.hitRenderMonitorTimer = setInterval(poll, 15000);
373
+
374
+ console.log("H3 Render Monitor v3 started.");
375
+ })();