Created
June 9, 2026 13:03
-
-
Save nbogie/4ff4bf38522ae7411e891d7987451fd3 to your computer and use it in GitHub Desktop.
sort miro videos numerically - script for console
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| * Sort Miro recording-library videos by their <h2> titles. | |
| * | |
| * Run by pasting the whole thing into the browser console on the | |
| * authenticated Miro page that shows the video list. | |
| * | |
| * Strategy (resilient to Miro's hashed CSS class names, which change | |
| * between sessions): | |
| * 1. Find the <h2> titles that look like video titles (most start | |
| * with a number, e.g. "22. Straw Poll"). | |
| * 2. Their lowest common ancestor is the list container. | |
| * 3. Each video item is the direct child of that container holding one h2. | |
| * 4. Sort: unnumbered intros first (alphabetically), then numbered | |
| * items in numeric order. Re-append in that order. | |
| */ | |
| (function sortMiroVideos() { | |
| const allH2 = Array.from(document.querySelectorAll('h2')) | |
| .filter((h) => h.textContent.trim().length > 0); | |
| // h2s that clearly belong to the video list: leading number like "22." / "00." | |
| const numbered = allH2.filter((h) => /^\s*\d+[.)\s]/.test(h.textContent)); | |
| if (numbered.length < 2) { | |
| console.warn('[sort-miro] Could not find numbered video titles — aborting.'); | |
| return; | |
| } | |
| // Lowest common ancestor of the numbered titles = the list container. | |
| const chain = (el) => { const a = []; for (; el; el = el.parentElement) a.push(el); return a; }; | |
| const firstChain = chain(numbered[0]); | |
| const container = firstChain.find((cand) => numbered.every((h) => cand.contains(h))); | |
| if (!container) { | |
| console.warn('[sort-miro] Could not locate a common list container — aborting.'); | |
| return; | |
| } | |
| // Direct child of `container` that holds a given descendant. | |
| const itemFor = (h) => { | |
| let el = h; | |
| while (el.parentElement && el.parentElement !== container) el = el.parentElement; | |
| return el.parentElement === container ? el : null; | |
| }; | |
| // All h2s living inside the container (numbered + unnumbered intros), one item each. | |
| const seen = new Set(); | |
| const items = []; | |
| for (const h of allH2) { | |
| if (!container.contains(h)) continue; | |
| const item = itemFor(h); | |
| if (!item || seen.has(item)) continue; | |
| seen.add(item); | |
| const title = h.textContent.trim(); | |
| const m = title.match(/^\s*(\d+)/); | |
| items.push({ item, title, hasNum: !!m, num: m ? parseInt(m[1], 10) : 0 }); | |
| } | |
| // Unnumbered intros first (alphabetical), then numbered ascending. | |
| items.sort((a, b) => { | |
| if (a.hasNum !== b.hasNum) return a.hasNum ? 1 : -1; // unnumbered first | |
| if (!a.hasNum) return a.title.localeCompare(b.title); // alpha among intros | |
| return a.num - b.num || a.title.localeCompare(b.title); // numeric among rest | |
| }); | |
| // appendChild moves an existing node, so appending in order reorders in place. | |
| items.forEach(({ item }) => container.appendChild(item)); | |
| console.log(`[sort-miro] Reordered ${items.length} videos:`); | |
| console.table(items.map((x, i) => ({ position: i + 1, title: x.title }))); | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment