Last active
          September 4, 2025 09:52 
        
      - 
      
- 
        Save qubodup/a4ed2f9a50d4b554ba605607ad638499 to your computer and use it in GitHub Desktop. 
    Fix broken audio embeds on Fandom Special:NewFiles and in Categories + enhance NewFiles gallery with filenames and repositioned captions
  
        
  
    
      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
    
  
  
    
  | // ==UserScript== | |
| // @name Fandom Audio Browser | |
| // @namespace https://tampermonkey.net/ | |
| // @version 2025-09-04 | |
| // @description Fix broken audio embeds on Fandom Special:NewFiles and in Categories + enhance NewFiles gallery with filenames and repositioned captions | |
| // @author Some BODY once TOLD me | |
| // @license CC0 1.0 Universal | |
| // @match https://*.fandom.com/wiki/* | |
| // @grant none | |
| // ==/UserScript== | |
| (function () { | |
| 'use strict'; | |
| const path = location.pathname; | |
| // Special:NewFiles or Category pages | |
| const isNewFiles = path.startsWith("/wiki/Special:NewFiles"); | |
| const isCategory = path.startsWith("/wiki/Category:"); | |
| if (isNewFiles || isCategory) { | |
| let currentAudio = null; | |
| let currentButton = null; | |
| function makeSimplePlayer(src, btn) { | |
| const audio = new Audio(src); | |
| audio.addEventListener("ended", () => { | |
| if (currentAudio === audio) { | |
| currentAudio = null; | |
| if (currentButton) currentButton.textContent = "▶"; | |
| } | |
| }); | |
| return audio; | |
| } | |
| function replaceWithToggle(container, src, title) { | |
| // Hide existing children instead of nuking innerHTML | |
| for (const child of Array.from(container.children)) { | |
| child.style.setProperty("display", "none", "important"); | |
| child.style.setProperty("visibility", "hidden", "important"); | |
| } | |
| // Only add once | |
| if (container.querySelector("button[data-added-by-userscript]")) return; | |
| const btn = document.createElement("button"); | |
| btn.textContent = "▶"; | |
| btn.title = "Play " + (title || "audio"); | |
| btn.dataset.addedByUserscript = "true"; | |
| btn.style.cursor = "pointer"; | |
| btn.style.width = "28px"; | |
| btn.style.height = "28px"; | |
| btn.style.fontSize = "16px"; | |
| btn.style.display = "block"; | |
| btn.style.margin = "0 auto"; | |
| let audio = null; | |
| btn.addEventListener("click", ev => { | |
| ev.preventDefault(); | |
| ev.stopPropagation(); | |
| if (!audio) audio = makeSimplePlayer(src, btn); | |
| if (currentAudio && currentAudio !== audio) { | |
| currentAudio.pause(); | |
| currentAudio.currentTime = 0; | |
| if (currentButton) currentButton.textContent = "▶"; | |
| } | |
| if (audio.paused) { | |
| audio.play().catch(console.error); | |
| currentAudio = audio; | |
| currentButton = btn; | |
| btn.textContent = "■"; // stop symbol | |
| } else { | |
| audio.pause(); | |
| audio.currentTime = 0; | |
| currentAudio = null; | |
| currentButton = null; | |
| btn.textContent = "▶"; | |
| } | |
| }); | |
| container.appendChild(btn); | |
| } | |
| function replaceWithAudio(container, src, title) { | |
| // Hide existing content instead of deleting it | |
| for (const child of Array.from(container.children)) { | |
| child.style.setProperty("display", "none", "important"); | |
| child.style.setProperty("visibility", "hidden", "important"); | |
| } | |
| // Make sure container uses flexbox for centering | |
| container.style.display = "flex"; | |
| container.style.alignItems = "center"; | |
| container.style.justifyContent = "center"; | |
| // Only add our player once | |
| if (container.querySelector("audio[data-added-by-userscript]")) return; | |
| // Create and append audio element | |
| const audio = document.createElement("audio"); | |
| audio.controls = true; | |
| audio.src = src; | |
| audio.title = title || "Audio"; | |
| audio.dataset.addedByUserscript = "true"; | |
| audio.style.width = "180px"; | |
| audio.style.height = "30px"; | |
| container.appendChild(audio); | |
| } | |
| function enhanceNewFilesItem(item) { | |
| if (item.classList.contains("filename-enhanced")) return; | |
| const link = item.querySelector("a.image.lightbox"); | |
| const caption = item.querySelector(".lightbox-caption"); | |
| const timeEl = caption?.querySelector("time"); | |
| const usernameLink = caption?.querySelector("a"); | |
| if (link && caption && timeEl && usernameLink) { | |
| const fileName = link.getAttribute("title")?.split(" (")[0] || ""; | |
| const filePageUrl = link.getAttribute("href"); | |
| // New filename link | |
| const fileLink = document.createElement("a"); | |
| fileLink.href = filePageUrl; | |
| fileLink.textContent = fileName; | |
| fileLink.title = fileName; | |
| fileLink.style.display = "inline-block"; | |
| fileLink.style.maxWidth = "100%"; | |
| fileLink.style.whiteSpace = "nowrap"; | |
| fileLink.style.overflow = "hidden"; | |
| fileLink.style.textOverflow = "ellipsis"; | |
| // Replace username with filename link | |
| usernameLink.replaceWith(fileLink); | |
| // Move username next to the date | |
| const movedUsername = usernameLink.cloneNode(true); | |
| const spanWrapper = document.createElement("span"); | |
| spanWrapper.appendChild(document.createTextNode(" – ")); | |
| spanWrapper.appendChild(movedUsername); | |
| timeEl.parentNode.appendChild(spanWrapper); | |
| item.classList.add("filename-enhanced"); | |
| } | |
| } | |
| function handleNode(node) { | |
| // Special:NewFiles (gallery) - audio player replacement | |
| if (node.matches?.(".wikia-gallery-item .thumbimage")) { | |
| const src = node.getAttribute("src") || node.getAttribute("data-src"); | |
| if (src && /\.(mp3|ogg|wav)/i.test(src)) { | |
| const container = node.closest(".gallery-image-wrapper"); | |
| const title = node.getAttribute("data-image-name") || "audio"; | |
| replaceWithAudio(container, src, title); | |
| } | |
| } | |
| // Special:NewFiles (gallery) - caption enhancements | |
| if (location.pathname.startsWith("/wiki/Special:NewFiles") && node.matches?.(".wikia-gallery-item")) { | |
| enhanceNewFilesItem(node); | |
| } | |
| // Category pages (toggle) | |
| if (node.matches?.(".category-page__member-thumbnail")) { | |
| const src = node.getAttribute("src") || node.getAttribute("data-src"); | |
| if (src && /\.(mp3|ogg|wav)/i.test(src)) { | |
| const container = node.parentElement; // <a> wrapper | |
| const title = node.getAttribute("alt") || "audio"; | |
| replaceWithToggle(container, src.replace(/\/smart\/.*$/, ""), title); | |
| } | |
| } | |
| } | |
| // Initial scan | |
| document.querySelectorAll(".wikia-gallery-item .thumbimage, .category-page__member-thumbnail, .wikia-gallery-item") | |
| .forEach(handleNode); | |
| // Scoped observers | |
| const roots = document.querySelectorAll(".wikia-gallery, .category-page__members, body.page-MediaWiki_File"); | |
| roots.forEach(root => { | |
| const observer = new MutationObserver(mutations => { | |
| for (const m of mutations) { | |
| if (m.type === "attributes" && m.target.matches?.(".wikia-gallery-item .thumbimage, .category-page__member-thumbnail")) { | |
| handleNode(m.target); | |
| } | |
| for (const node of m.addedNodes) { | |
| if (node.nodeType === 1) { | |
| if (node.matches(".wikia-gallery-item .thumbimage, .category-page__member-thumbnail, .wikia-gallery-item")) { | |
| handleNode(node); | |
| } | |
| node.querySelectorAll?.(".wikia-gallery-item .thumbimage, .category-page__member-thumbnail, .wikia-gallery-item") | |
| .forEach(handleNode); | |
| } | |
| } | |
| //catch caption changes | |
| if (m.type === "childList" && m.target.matches(".lightbox-caption")) { | |
| const item = m.target.closest(".wikia-gallery-item"); | |
| if (item) enhanceNewFilesItem(item); | |
| } | |
| } | |
| }); | |
| observer.observe(root, { | |
| childList: true, | |
| subtree: true, | |
| attributes: true, | |
| attributeFilter: ["src", "data-src"] | |
| }); | |
| }); | |
| } | |
| })(); | 
  
    Sign up for free
    to join this conversation on GitHub.
    Already have an account?
    Sign in to comment