Download injection.js from haraberget/WanGP-Injection: direct link, hf CLI and curl.
- Browser
- Download file 17.3 kB
-
https://huggingface.co/haraberget/WanGP-Injection/resolve/main/injection.js
- Command line
-
hf download hf://haraberget/WanGP-Injection/injection.js
-
curl -L -o injection.js https://huggingface.co/haraberget/WanGP-Injection/resolve/main/injection.js
17.3 kB
| (() => { | |
| // H3 Render Monitor v6 — Kaggle Live Dashboard | |
| if (window.hitRenderMonitorTimer) clearInterval(window.hitRenderMonitorTimer); | |
| if (window.hitResourceMonitorTimer) clearInterval(window.hitResourceMonitorTimer); | |
| document.getElementById("hit-render-monitor")?.remove(); | |
| const POSITION_KEY = "hitRenderMonitorPosition"; | |
| const state = { | |
| prompt: "", | |
| progress: 0, | |
| step: 0, | |
| totalSteps: 0, | |
| checkpoints: new Set(), | |
| completedUrl: "", | |
| minimized: false | |
| }; | |
| const panel = document.createElement("div"); | |
| panel.id = "hit-render-monitor"; | |
| Object.assign(panel.style, { | |
| position: "fixed", | |
| right: "20px", | |
| bottom: "20px", | |
| width: "440px", | |
| maxHeight: "85vh", | |
| overflow: "hidden", | |
| zIndex: "999999", | |
| background: "#17191f", | |
| color: "#eeeeee", | |
| border: "1px solid #555", | |
| borderRadius: "10px", | |
| padding: "0", | |
| font: "13px/1.45 monospace", | |
| boxShadow: "0 5px 30px #0009" | |
| }); | |
| panel.innerHTML = ` | |
| <div id="hit-header" style=" | |
| display:flex;justify-content:space-between;align-items:center; | |
| padding:12px 14px;background:#22252d; | |
| border-radius:10px 10px 0 0;cursor:grab; | |
| user-select:none;touch-action:none"> | |
| <strong style="font-size:15px"> | |
| H3 Monitor <span id="hit-title-progress" style="color:#20b875">· Waiting</span> | |
| </strong> | |
| <div style="display:flex;gap:6px"> | |
| <button id="hit-minimize" title="Minimize" | |
| style="background:#444;color:white;border:0;padding:3px 10px;cursor:pointer;font-size:16px">−</button> | |
| <button id="hit-close" title="Close" | |
| style="background:#744;color:white;border:0;padding:3px 9px;cursor:pointer;font-size:15px">×</button> | |
| </div> | |
| </div> | |
| <div id="hit-content" style="padding:14px;max-height:calc(85vh - 45px);overflow-y:auto"> | |
| <strong>RENDER PROGRESS</strong> | |
| <div style="height:12px;background:#333;border-radius:8px;margin:6px 0;overflow:hidden"> | |
| <div id="hit-bar" style="height:100%;width:0%;background:#20b875;transition:width .3s"></div> | |
| </div> | |
| <div id="hit-progress" style="font-size:18px">Waiting for H3...</div> | |
| <div id="hit-step" style="color:#aaa"></div> | |
| <hr style="border-color:#444;margin:12px 0"> | |
| <strong>KAGGLE SESSION</strong> | |
| <div style="display:grid;grid-template-columns:1fr 1fr;gap:7px;margin-top:8px"> | |
| <div class="hit-stat"><small>ACCELERATOR</small><strong id="hit-accelerator">--</strong></div> | |
| <div class="hit-stat"><small>SESSION TIME</small><strong id="hit-session-time">--</strong></div> | |
| <div class="hit-stat"><small>DISK</small><strong id="hit-disk">--</strong></div> | |
| <div class="hit-stat"><small>QUOTA</small><strong id="hit-quota">--</strong></div> | |
| <div class="hit-stat"><small>PERSISTENCE</small><strong id="hit-persistence">--</strong></div> | |
| <div class="hit-stat"><small>INTERNET</small><strong id="hit-internet">--</strong></div> | |
| <div class="hit-stat" style="grid-column:span 2"><small>ENVIRONMENT</small><strong id="hit-environment">--</strong></div> | |
| </div> | |
| <hr style="border-color:#444;margin:12px 0"> | |
| <div style="display:flex;justify-content:space-between;align-items:center"> | |
| <strong>LIVE RESOURCE USAGE</strong> | |
| <span id="hit-resource-status" style="font-size:10px;color:#aaa">Waiting...</span> | |
| </div> | |
| <div style="display:grid;grid-template-columns:1fr 1fr;gap:7px;margin-top:8px"> | |
| <div class="hit-stat"><small>CPU</small><strong id="hit-cpu">--</strong></div> | |
| <div class="hit-stat"><small>RAM</small><strong id="hit-ram">--</strong></div> | |
| <div class="hit-stat" style="grid-column:span 2"><small>DISK USAGE</small><strong id="hit-disk-live">--</strong></div> | |
| </div> | |
| <div style="margin-top:9px;background:#242730;padding:9px;border-radius:6px"> | |
| <strong style="font-size:12px">GPU 1</strong> | |
| <div style="display:grid;grid-template-columns:1fr 1fr;gap:7px;margin-top:6px"> | |
| <div><small style="color:#aaa">UTILISATION</small><strong id="hit-gpu-1" style="display:block">--</strong></div> | |
| <div><small style="color:#aaa">MEMORY</small><strong id="hit-vram-1" style="display:block">--</strong></div> | |
| </div> | |
| </div> | |
| <div style="margin-top:7px;background:#242730;padding:9px;border-radius:6px"> | |
| <strong style="font-size:12px">GPU 2</strong> | |
| <div style="display:grid;grid-template-columns:1fr 1fr;gap:7px;margin-top:6px"> | |
| <div><small style="color:#aaa">UTILISATION</small><strong id="hit-gpu-2" style="display:block">--</strong></div> | |
| <div><small style="color:#aaa">MEMORY</small><strong id="hit-vram-2" style="display:block">--</strong></div> | |
| </div> | |
| </div> | |
| <hr style="border-color:#444;margin:12px 0"> | |
| <strong>PROMPT INSPECTOR</strong> | |
| <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> | |
| <hr style="border-color:#444;margin:12px 0"> | |
| <div style="display:flex;justify-content:space-between;align-items:center"> | |
| <strong>CALLBACK SIMULATOR</strong> | |
| <label style="font-size:11px"> | |
| <input id="hit-report" type="checkbox" checked> Report to 1hit.no | |
| </label> | |
| </div> | |
| <div style="color:#e6b85c;font-size:11px;margin:4px 0 8px"> | |
| Simulation only — no network requests | |
| </div> | |
| <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> | |
| <hr style="border-color:#444;margin:12px 0"> | |
| <strong>OUTPUT URL</strong> | |
| <div id="hit-url" style="color:#8fcaff;overflow-wrap:anywhere;margin-top:5px">Waiting for video...</div> | |
| <div id="hit-status" style="color:#aaa;font-size:11px;margin-top:10px">Progress scan every 15 seconds · Resources every 2.5 seconds</div> | |
| </div> | |
| `; | |
| document.body.appendChild(panel); | |
| // Style statistic cards. | |
| panel.querySelectorAll(".hit-stat").forEach(card => { | |
| Object.assign(card.style, { | |
| background: "#242730", | |
| padding: "8px", | |
| borderRadius: "6px", | |
| minWidth: "0", | |
| overflowWrap: "anywhere" | |
| }); | |
| const small = card.querySelector("small"); | |
| const strong = card.querySelector("strong"); | |
| if (small) Object.assign(small.style, { | |
| display: "block", | |
| color: "#aaa", | |
| fontSize: "10px", | |
| marginBottom: "3px" | |
| }); | |
| if (strong) Object.assign(strong.style, {fontSize: "12px"}); | |
| }); | |
| const el = id => panel.querySelector("#" + id); | |
| // Restore previous popup position. | |
| try { | |
| const saved = JSON.parse(sessionStorage.getItem(POSITION_KEY) || "null"); | |
| if (saved && Number.isFinite(saved.left) && Number.isFinite(saved.top)) { | |
| panel.style.left = saved.left + "px"; | |
| panel.style.top = saved.top + "px"; | |
| panel.style.right = "auto"; | |
| panel.style.bottom = "auto"; | |
| } | |
| } catch (e) {} | |
| function savePosition() { | |
| const rect = panel.getBoundingClientRect(); | |
| try { | |
| sessionStorage.setItem(POSITION_KEY, JSON.stringify({ | |
| left: Math.round(rect.left), | |
| top: Math.round(rect.top) | |
| })); | |
| } catch (e) {} | |
| } | |
| // Drag popup. | |
| const header = el("hit-header"); | |
| let dragging = false; | |
| let dragOffsetX = 0; | |
| let dragOffsetY = 0; | |
| header.addEventListener("pointerdown", event => { | |
| if (event.target.closest("button")) return; | |
| dragging = true; | |
| const rect = panel.getBoundingClientRect(); | |
| dragOffsetX = event.clientX - rect.left; | |
| dragOffsetY = event.clientY - rect.top; | |
| panel.style.right = "auto"; | |
| panel.style.bottom = "auto"; | |
| header.style.cursor = "grabbing"; | |
| header.setPointerCapture(event.pointerId); | |
| event.preventDefault(); | |
| }); | |
| header.addEventListener("pointermove", event => { | |
| if (!dragging) return; | |
| const left = Math.max(0, Math.min( | |
| window.innerWidth - panel.offsetWidth, | |
| event.clientX - dragOffsetX | |
| )); | |
| const top = Math.max(0, Math.min( | |
| window.innerHeight - panel.offsetHeight, | |
| event.clientY - dragOffsetY | |
| )); | |
| panel.style.left = left + "px"; | |
| panel.style.top = top + "px"; | |
| }); | |
| function stopDragging() { | |
| if (!dragging) return; | |
| dragging = false; | |
| header.style.cursor = "grab"; | |
| savePosition(); | |
| } | |
| header.addEventListener("pointerup", stopDragging); | |
| header.addEventListener("pointercancel", stopDragging); | |
| header.addEventListener("lostpointercapture", stopDragging); | |
| // Minimize and restore. | |
| el("hit-minimize").onclick = () => { | |
| state.minimized = !state.minimized; | |
| el("hit-content").style.display = state.minimized ? "none" : "block"; | |
| el("hit-minimize").textContent = state.minimized ? "+" : "−"; | |
| el("hit-minimize").title = state.minimized ? "Restore" : "Minimize"; | |
| panel.style.width = state.minimized ? "260px" : "440px"; | |
| savePosition(); | |
| }; | |
| // Read a metric from Kaggle's visible metric cards. | |
| function readMetrics() { | |
| const result = []; | |
| const metricNodes = [...document.querySelectorAll('span[tabindex="0"]')]; | |
| for (const node of metricNodes) { | |
| const card = node.querySelector("div"); | |
| if (!card) continue; | |
| const children = [...card.children]; | |
| if (children.length < 2) continue; | |
| const label = (children[0].innerText || "").trim(); | |
| const value = (children[1].innerText || "").trim(); | |
| const maximum = (children[2]?.innerText || "").trim(); | |
| if (!label || !value) continue; | |
| result.push({ | |
| label, | |
| value, | |
| maximum, | |
| container: node.parentElement | |
| }); | |
| } | |
| return result; | |
| } | |
| function updateResourceUsage() { | |
| const metrics = readMetrics(); | |
| const findAll = label => | |
| metrics.filter(item => item.label.toLowerCase() === label.toLowerCase()); | |
| const findOne = label => findAll(label)[0]; | |
| const cpu = findOne("CPU"); | |
| const ram = findOne("RAM"); | |
| const disk = findOne("Disk"); | |
| const session = findOne("Session"); | |
| const gpu = findAll("GPU"); | |
| const gpuMemory = findAll("GPU Memory"); | |
| if (cpu) el("hit-cpu").textContent = cpu.value; | |
| if (ram) el("hit-ram").textContent = | |
| ram.value + (ram.maximum ? " / " + ram.maximum : ""); | |
| if (disk) { | |
| const diskText = disk.value + (disk.maximum ? " / " + disk.maximum : ""); | |
| el("hit-disk").textContent = diskText; | |
| el("hit-disk-live").textContent = diskText; | |
| } | |
| if (session) { | |
| el("hit-session-time").textContent = | |
| session.value + (session.maximum ? " / " + session.maximum : ""); | |
| } | |
| if (gpu[0]) el("hit-gpu-1").textContent = gpu[0].value; | |
| if (gpuMemory[0]) { | |
| el("hit-vram-1").textContent = | |
| gpuMemory[0].value + (gpuMemory[0].maximum ? " / " + gpuMemory[0].maximum : ""); | |
| } | |
| if (gpu[1]) el("hit-gpu-2").textContent = gpu[1].value; | |
| if (gpuMemory[1]) { | |
| el("hit-vram-2").textContent = | |
| gpuMemory[1].value + (gpuMemory[1].maximum ? " / " + gpuMemory[1].maximum : ""); | |
| } | |
| const found = [cpu, ram, disk, session].filter(Boolean).length + | |
| gpu.length + gpuMemory.length; | |
| el("hit-resource-status").textContent = | |
| found ? "Live · " + new Date().toLocaleTimeString() : "Waiting for Kaggle metrics"; | |
| } | |
| // Read Kaggle session options. | |
| function updateKaggleOptions() { | |
| const getValue = label => { | |
| const field = [...document.querySelectorAll('[role="combobox"]')] | |
| .find(node => { | |
| const text = ( | |
| node.getAttribute("aria-label") || | |
| node.getAttribute("aria-labelledby") || | |
| "" | |
| ).toLowerCase(); | |
| return text.includes(label.toLowerCase()); | |
| }); | |
| return field?.innerText?.trim() || ""; | |
| }; | |
| const accelerator = getValue("Accelerator"); | |
| const persistence = getValue("Persistence"); | |
| const environment = getValue("Environment"); | |
| const internetInput = document.querySelector( | |
| '#internet-toggle-switch-label input[type="checkbox"]' | |
| ); | |
| const quota = [...document.querySelectorAll("p")] | |
| .find(node => node.innerText.includes("Quota:")) | |
| ?.innerText.match(/Quota:\s*(.*?)(?:\s*-\s*Link|$)/)?.[1] || ""; | |
| el("hit-accelerator").textContent = accelerator || "Not detected"; | |
| el("hit-quota").textContent = quota || "Not detected"; | |
| el("hit-persistence").textContent = persistence || "Not detected"; | |
| el("hit-environment").textContent = environment || "Not detected"; | |
| el("hit-internet").textContent = internetInput | |
| ? (internetInput.checked ? "On" : "Off") | |
| : "Not detected"; | |
| } | |
| function addEvent(data) { | |
| if (!el("hit-report").checked) return; | |
| const log = el("hit-json"); | |
| log.textContent = | |
| (log.textContent === "Waiting for events..." ? "" : log.textContent + "\n\n") + | |
| JSON.stringify(data, null, 2); | |
| log.scrollTop = log.scrollHeight; | |
| } | |
| function getPrompt() { | |
| const fields = [...document.querySelectorAll("textarea, [contenteditable='true']")]; | |
| const candidates = fields.map(node => ({ | |
| text: (node.value || node.innerText || node.textContent || "").trim(), | |
| hint: [ | |
| node.getAttribute("aria-label"), | |
| node.getAttribute("placeholder"), | |
| node.parentElement?.innerText | |
| ].filter(Boolean).join(" ").toLowerCase() | |
| })).filter(x => x.text.length > 10); | |
| const labelled = candidates | |
| .filter(x => /prompt|description/.test(x.hint)) | |
| .sort((a, b) => b.text.length - a.text.length); | |
| if (labelled.length) return labelled[0].text; | |
| candidates.sort((a, b) => b.text.length - a.text.length); | |
| return candidates[0]?.text || ""; | |
| } | |
| function getVideoUrl() { | |
| const candidates = [ | |
| ...document.querySelectorAll("video"), | |
| ...document.querySelectorAll("video source"), | |
| ...document.querySelectorAll("a[href]") | |
| ]; | |
| for (const node of candidates) { | |
| const url = node.currentSrc || node.src || node.href || ""; | |
| if (url && (/\.mp4(?:[?#]|$)/i.test(url) || url.startsWith("blob:"))) { | |
| return url; | |
| } | |
| } | |
| return ""; | |
| } | |
| function baseEvent(event) { | |
| return { | |
| session_id: "kaggle-" + location.host, | |
| event, | |
| render_url: location.href, | |
| prompt: state.prompt, | |
| progress: state.progress, | |
| step: state.step, | |
| total_steps: state.totalSteps, | |
| timestamp: new Date().toISOString() | |
| }; | |
| } | |
| function poll() { | |
| updateKaggleOptions(); | |
| const prompt = getPrompt(); | |
| if (prompt && prompt !== state.prompt) { | |
| state.prompt = prompt; | |
| state.checkpoints.clear(); | |
| state.completedUrl = ""; | |
| el("hit-prompt").textContent = prompt; | |
| addEvent({...baseEvent("prompt"), message: "New prompt detected"}); | |
| } | |
| const messages = [...document.querySelectorAll('div[type="message"]')]; | |
| const matching = messages | |
| .map(node => node.innerText || "") | |
| .filter(text => text.includes("H3 denoising:")); | |
| if (matching.length) { | |
| const match = matching[matching.length - 1].match( | |
| /H3 denoising:\s*(\d+)%.*?\|\s*(\d+)\/(\d+)/ | |
| ); | |
| if (match) { | |
| const pct = Number(match[1]); | |
| const step = Number(match[2]); | |
| const total = Number(match[3]); | |
| if (pct < state.progress && pct <= 25) { | |
| state.checkpoints.clear(); | |
| state.completedUrl = ""; | |
| } | |
| state.progress = pct; | |
| state.step = step; | |
| state.totalSteps = total; | |
| el("hit-bar").style.width = pct + "%"; | |
| el("hit-progress").textContent = pct + "%"; | |
| el("hit-step").textContent = `Step ${step} / ${total}`; | |
| el("hit-title-progress").textContent = "· " + pct + "%"; | |
| for (const threshold of [25, 50, 75, 100]) { | |
| if (pct >= threshold && !state.checkpoints.has(threshold)) { | |
| state.checkpoints.add(threshold); | |
| addEvent({...baseEvent("progress"), checkpoint: threshold}); | |
| } | |
| } | |
| } | |
| } | |
| const videoUrl = getVideoUrl(); | |
| if (videoUrl) { | |
| el("hit-url").textContent = videoUrl; | |
| if (state.progress >= 100 && videoUrl !== state.completedUrl) { | |
| state.completedUrl = videoUrl; | |
| addEvent({ | |
| ...baseEvent("completed"), | |
| video: { | |
| url: videoUrl, | |
| filename: videoUrl.split("/").pop().split("?")[0] | |
| } | |
| }); | |
| } | |
| } | |
| el("hit-status").textContent = | |
| "Last scan: " + new Date().toLocaleTimeString() + | |
| " · Progress scan every 15 seconds"; | |
| } | |
| // Close and clean up both timers. | |
| el("hit-close").onclick = () => { | |
| clearInterval(window.hitRenderMonitorTimer); | |
| clearInterval(window.hitResourceMonitorTimer); | |
| window.hitRenderMonitorTimer = null; | |
| window.hitResourceMonitorTimer = null; | |
| panel.remove(); | |
| }; | |
| // Start monitoring. | |
| updateResourceUsage(); | |
| poll(); | |
| window.hitResourceMonitorTimer = setInterval(updateResourceUsage, 2500); | |
| window.hitRenderMonitorTimer = setInterval(poll, 15000); | |
| console.log("H3 Render Monitor v6 started — live resource monitoring enabled."); | |
| })(); | |