Spaces:
Running
Running
| // assets/js/category.js | |
| const CSV_PATH = "cleaned_data.csv"; | |
| /* >>> DATASET VIDEO BASE <<< */ | |
| const VIDEO_DATASET_BASE = | |
| "https://huggingface.co/datasets/therarelab/codebook-videos/resolve/main/videos/"; | |
| const params = new URLSearchParams(window.location.search); | |
| const cat = (params.get("cat") || "").trim(); | |
| const titleEl = document.getElementById("catTitle"); | |
| const defEl = document.getElementById("catDef"); | |
| const repMetaEl = document.getElementById("repMeta"); | |
| const repGrid = document.getElementById("repGrid"); | |
| const repEmpty = document.getElementById("repEmpty"); | |
| const instCountEl = document.getElementById("instCount"); | |
| const instVideoCountEl = document.getElementById("instVideoCount"); | |
| const tbody = document.querySelector("#catTable tbody"); | |
| function parseCSV(text) { | |
| const lines = text.trim().split("\n"); | |
| const header = lines[0].split(",").map(s => s.trim()); | |
| const rows = []; | |
| for (let i = 1; i < lines.length; i++) { | |
| const cols = lines[i].split(","); | |
| const obj = {}; | |
| for (let j = 0; j < header.length; j++) { | |
| obj[header[j]] = (cols[j] ?? "").trim(); | |
| } | |
| rows.push(obj); | |
| } | |
| return rows; | |
| } | |
| function setHeader(failureInfo) { | |
| if (!cat) { | |
| titleEl.textContent = "No category specified"; | |
| defEl.textContent = "Return to Home and select a category."; | |
| return; | |
| } | |
| titleEl.textContent = failureInfo?.name || cat; | |
| defEl.textContent = failureInfo?.definition || ""; | |
| document.title = `${cat} — Failure Category`; | |
| } | |
| const failureInfo = | |
| window.FAILURE_MAP?.[cat] || | |
| window.FAILURES?.find(f => f.name === cat); | |
| setHeader(failureInfo); | |
| /* ---------------- Representative Video ---------------- */ | |
| function renderRep(instance) { | |
| repGrid.innerHTML = ""; | |
| if (!instance) { | |
| repEmpty.hidden = false; | |
| repMetaEl.textContent = ""; | |
| return; | |
| } | |
| repEmpty.hidden = true; | |
| const filename = instance.filename; | |
| const label = instance.label; | |
| const start = instance.start; | |
| const end = instance.end; | |
| const video_url = | |
| VIDEO_DATASET_BASE + encodeURIComponent(filename); | |
| repMetaEl.textContent = | |
| `Example: ${filename} (frames ${start}-${end})`; | |
| const wrap = document.createElement("div"); | |
| wrap.className = "video-item"; | |
| wrap.innerHTML = ` | |
| <video preload="metadata" controls> | |
| <source src="${video_url}" type="video/mp4"> | |
| </video> | |
| <div class="video-label">${label}</div> | |
| `; | |
| const videoEl = wrap.querySelector("video"); | |
| attachLabeledLoop(videoEl, { | |
| label, | |
| startFrame: start, | |
| endFrame: end, | |
| fps: DEFAULT_FPS | |
| }); | |
| repGrid.appendChild(wrap); | |
| } | |
| /* ---------------- Table ---------------- */ | |
| function renderTable(instances) { | |
| tbody.innerHTML = ""; | |
| const uniqueVideos = new Set(); | |
| for (const r of instances) { | |
| uniqueVideos.add(r.filename); | |
| const video_url = | |
| VIDEO_DATASET_BASE + encodeURIComponent(r.filename); | |
| const tr = document.createElement("tr"); | |
| tr.innerHTML = ` | |
| <td>${r.filename}</td> | |
| <td>${r.start}-${r.end}</td> | |
| <td class="video-cell"> | |
| <video preload="metadata" controls> | |
| <source src="${video_url}" type="video/mp4"> | |
| </video> | |
| </td> | |
| `; | |
| const videoEl = tr.querySelector("video"); | |
| attachLabeledLoop(videoEl, { | |
| label: r.label, | |
| startFrame: r.start, | |
| endFrame: r.end, | |
| fps: DEFAULT_FPS | |
| }); | |
| tbody.appendChild(tr); | |
| } | |
| instCountEl.textContent = `${instances.length} segments`; | |
| instVideoCountEl.textContent = `${uniqueVideos.size} videos`; | |
| } | |
| /* ---------------- Load CSV ---------------- */ | |
| fetch(CSV_PATH) | |
| .then(r => r.text()) | |
| .then(text => { | |
| const rows = parseCSV(text); | |
| const instances = rows | |
| .filter(r => String(r.label || "").trim() === cat) | |
| .filter(r => r.filename && r.start !== "" && r.end !== ""); | |
| instances.sort((a, b) => { | |
| if (a.filename !== b.filename) | |
| return a.filename.localeCompare(b.filename); | |
| return Number(a.start) - Number(b.start); | |
| }); | |
| renderRep(instances[0]); | |
| renderTable(instances); | |
| }) | |
| .catch(err => { | |
| console.error(err); | |
| repEmpty.hidden = false; | |
| repEmpty.textContent = "Could not load cleaned_data.csv."; | |
| tbody.innerHTML = | |
| `<tr><td colspan="3">Could not load cleaned_data.csv</td></tr>`; | |
| }); | |