"use strict"; const TARGET_SELECTOR = ".playbar-playback-info-bar.playback-row-section.svelte-11gjrk3"; const INJECTED_CONTAINER_ID = "s3-audio-capture-controls"; const SPLICE_DOMAIN_RE = /^https?:\/\/([a-z0-9-]+\.)*splice\.com(\/|$)/i; const PROBE_EVENT_SOURCE = "S3_CAPTURE_PROBE"; let knownUrls = new Set(); let knownPerformanceUrls = new Set(); let observer = null; let captureObserver = null; let performanceObserver = null; let fetchPatched = false; let xhrPatched = false; let probeListenerInstalled = false; let currentPlaybackFilename = ""; let currentPlaybackKey = ""; let currentPlaybackBpm = ""; let currentPlaybackTags = []; let currentPlaybackCoverImageUrl = ""; let lastPlaybackMetaSignature = ""; let lastPlaybackMetaApplied = false; function isDomainAllowed(url) { return typeof url === "string" && SPLICE_DOMAIN_RE.test(url); } function isS3Url(url) { if (typeof url !== "string") { return false; } try { const parsed = new URL(url); if (parsed.protocol !== "https:") { return false; } const host = parsed.hostname.toLowerCase(); const isAwsHost = host.endsWith(".amazonaws.com") || host.endsWith(".amazonaws.com.cn"); const isS3Host = host.includes(".s3.") || host.startsWith("s3."); if (!isAwsHost || !isS3Host) { return false; } const path = parsed.pathname || ""; if (!path.includes("/audio_samples/")) { return false; } if (path.endsWith(".wv.json")) { return false; } return true; } catch (error) { return false; } } function normalizeUrl(rawUrl) { try { return new URL(rawUrl, window.location.href).toString(); } catch (error) { return ""; } } function normalizeFileName(raw) { if (typeof raw !== "string") { return ""; } const trimmed = raw.trim(); if (!trimmed) { return ""; } const parts = trimmed.split("/"); return parts[parts.length - 1] || trimmed; } function getPlaybackRoot() { const playbar = document.querySelector(TARGET_SELECTOR); if (playbar) { return playbar; } const filenameNode = document.querySelector('[data-qa="filename"]'); if (filenameNode && typeof filenameNode.closest === "function") { return filenameNode.closest(".playbar-playback-info-bar"); } return null; } function queryPlaybackNode(selector) { const root = getPlaybackRoot(); if (!root) { return null; } return root.querySelector(selector); } function getPlaybackFilenameFromDom() { const el = queryPlaybackNode('[data-qa="filename"]'); if (!el) { return ""; } return normalizeFileName(el.textContent || ""); } function getPlaybackKeyFromDom() { const el = queryPlaybackNode('[data-qa="key"]'); return el ? String(el.textContent || "").trim() : ""; } function getPlaybackBpmFromDom() { const el = queryPlaybackNode('[data-qa="bpm"]'); return el ? String(el.textContent || "").trim() : ""; } function getPlaybackTagsFromDom() { const root = queryPlaybackNode('[data-qa="tags"]'); if (!root) { return []; } const tags = []; const spans = root.querySelectorAll("span.tag"); for (const span of spans) { const value = String(span.textContent || "").trim(); if (value) { tags.push(value); } } return Array.from(new Set(tags)); } function getPlaybackCoverImageFromDom() { const img = queryPlaybackNode('[data-qa="cover-art-image"]'); if (!img) { return ""; } const src = img.getAttribute("src") || img.currentSrc || ""; return normalizeUrl(src); } function extractCoverImageFromGraphqlItem(item) { if (!item || typeof item !== "object") { return ""; } const ownFiles = Array.isArray(item.files) ? item.files : []; for (const file of ownFiles) { if (!file || typeof file !== "object") { continue; } const typeSlug = String(file.asset_file_type_slug || "").toLowerCase(); if (typeSlug !== "cover_image") { continue; } const url = normalizeUrl(file.url || ""); if (url) { return url; } } if (!item.parents || !Array.isArray(item.parents.items)) { return ""; } for (const parent of item.parents.items) { const files = parent && Array.isArray(parent.files) ? parent.files : []; for (const file of files) { if (!file || typeof file !== "object") { continue; } const typeSlug = String(file.asset_file_type_slug || "").toLowerCase(); if (typeSlug !== "cover_image") { continue; } const url = normalizeUrl(file.url || ""); if (url) { return url; } } } return ""; } function extractPackNameFromGraphqlItem(item) { if (!item || typeof item !== "object" || !item.parents || !Array.isArray(item.parents.items)) { return ""; } for (const parent of item.parents.items) { if (!parent || typeof parent !== "object") { continue; } const name = typeof parent.name === "string" ? parent.name.trim() : ""; if (name) { return name; } } return ""; } function collectProbeCandidateUrls(payload) { if (!payload || typeof payload !== "object") { return []; } const candidates = []; const keys = ["requestUrl", "responseUrl", "linkedUrl", "url", "blobUrl"]; for (const key of keys) { if (typeof payload[key] === "string" && payload[key]) { candidates.push(payload[key]); } } return candidates; } function extractSampleIdFromUrl(url) { try { const parsed = new URL(url); const marker = "/audio_samples/"; const idx = parsed.pathname.indexOf(marker); if (idx < 0) { return ""; } const rest = parsed.pathname.slice(idx + marker.length); const parts = rest.split("/").filter(Boolean); const first = (parts[0] || "").replace(/-scrambled$/i, ""); const second = (parts[1] || "").split(".")[0]; const hashRe = /^[a-f0-9]{64}$/i; if (hashRe.test(first)) { return first.toLowerCase(); } if (hashRe.test(second)) { return second.toLowerCase(); } return ""; } catch (error) { return ""; } } function recursivelyCollectAudioUrls(value, output) { if (typeof value === "string") { const normalized = normalizeUrl(value); if (normalized && isS3Url(normalized)) { output.add(normalized); } return; } if (Array.isArray(value)) { for (const item of value) { recursivelyCollectAudioUrls(item, output); } return; } if (value && typeof value === "object") { for (const nested of Object.values(value)) { recursivelyCollectAudioUrls(nested, output); } } } function extractPreviewContextsFromGraphql(json) { const contexts = []; const items = json && json.data && json.data.assetsSearch && Array.isArray(json.data.assetsSearch.items) ? json.data.assetsSearch.items : []; for (const item of items) { if (!item || typeof item !== "object") { continue; } const sampleId = typeof item.uuid === "string" ? item.uuid.toLowerCase() : ""; const itemName = normalizeFileName(String(item.name || "")); const itemKey = typeof item.key === "string" ? item.key.trim().toUpperCase() : ""; const itemBpm = item.bpm == null ? "" : String(item.bpm).trim(); const itemTags = Array.isArray(item.tags) ? Array.from(new Set(item.tags.map((tag) => (tag && tag.label ? String(tag.label).trim() : "")).filter(Boolean))) : []; const itemCoverImageUrl = extractCoverImageFromGraphqlItem(item); const itemPackName = extractPackNameFromGraphqlItem(item); const files = Array.isArray(item.files) ? item.files : []; for (const file of files) { if (!file || typeof file !== "object") { continue; } const typeSlug = String(file.asset_file_type_slug || "").toLowerCase(); const fileUrl = normalizeUrl(file.url || ""); if (typeSlug !== "preview_mp3" || !fileUrl) { continue; } contexts.push({ sampleId: sampleId || extractSampleIdFromUrl(fileUrl), previewUrl: fileUrl, fileName: itemName, key: itemKey, bpm: itemBpm, tags: itemTags, coverImageUrl: itemCoverImageUrl, packName: itemPackName }); } } return contexts; } function sendPreviewContexts(contexts) { if (!contexts.length) { return; } browser.runtime.sendMessage({ type: "CAPTURE_PREVIEW_CONTEXT", contexts }).catch(() => { // Ignore transient extension messaging failures. }); } function captureUrls(inputUrls) { if (!isDomainAllowed(window.location.href)) { return; } const newUrls = []; for (const rawUrl of inputUrls) { const normalized = normalizeUrl(rawUrl); if (!normalized || !isS3Url(normalized) || knownUrls.has(normalized)) { continue; } knownUrls.add(normalized); newUrls.push(normalized); } if (!newUrls.length) { return; } browser.runtime.sendMessage({ type: "CAPTURE_URLS", urls: newUrls, source: "content_capture", fileName: getPlaybackFilenameFromDom() }).catch(() => { // Ignore transient extension context failures. }); } function scanNodeForUrls(root) { if (!(root instanceof Element || root instanceof Document)) { return; } const urlCandidates = []; const selectors = [ "a[href]", "audio[src]", "source[src]", "[data-url]", "[data-src]" ]; for (const selector of selectors) { const nodes = root.querySelectorAll(selector); for (const node of nodes) { const attrs = ["href", "src", "data-url", "data-src"]; for (const attr of attrs) { const value = node.getAttribute(attr); if (value) { urlCandidates.push(value); } } } } captureUrls(urlCandidates); } function patchFetch() { if (fetchPatched || typeof window.fetch !== "function") { return; } try { const originalFetch = window.fetch.bind(window); window.fetch = function patchedFetch(input, init) { const candidate = typeof input === "string" ? input : input && input.url; if (candidate) { captureUrls([candidate]); } if (init && typeof init === "object" && typeof init.url === "string") { captureUrls([init.url]); } return originalFetch(input, init); }; fetchPatched = true; } catch (error) { // Firefox may expose fetch as read-only in content script worlds. fetchPatched = false; } } function patchXhr() { if (xhrPatched || !window.XMLHttpRequest) { return; } try { const originalOpen = window.XMLHttpRequest.prototype.open; window.XMLHttpRequest.prototype.open = function patchedOpen(method, url) { this.__s3CaptureRequestUrl = typeof url === "string" ? url : ""; if (typeof url === "string") { captureUrls([url]); } return originalOpen.apply(this, arguments); }; xhrPatched = true; } catch (error) { xhrPatched = false; } } function capturePerformanceResourceUrls() { if (typeof performance === "undefined" || typeof performance.getEntriesByType !== "function") { return; } const resources = performance.getEntriesByType("resource"); const candidates = []; for (const entry of resources) { if (!entry || typeof entry.name !== "string") { continue; } if (knownPerformanceUrls.has(entry.name)) { continue; } knownPerformanceUrls.add(entry.name); candidates.push(entry.name); } if (candidates.length) { captureUrls(candidates); } } function startPerformanceObserver() { if (performanceObserver || typeof PerformanceObserver !== "function") { return; } try { performanceObserver = new PerformanceObserver((list) => { const candidates = []; for (const entry of list.getEntries()) { if (!entry || typeof entry.name !== "string") { continue; } if (knownPerformanceUrls.has(entry.name)) { continue; } knownPerformanceUrls.add(entry.name); candidates.push(entry.name); } if (candidates.length) { captureUrls(candidates); } }); performanceObserver.observe({ type: "resource", buffered: true }); } catch (error) { performanceObserver = null; } } function createIcon(kind) { const ns = "http://www.w3.org/2000/svg"; const svg = document.createElementNS(ns, "svg"); svg.setAttribute("viewBox", "0 0 24 24"); svg.setAttribute("width", "14"); svg.setAttribute("height", "14"); svg.setAttribute("aria-hidden", "true"); if (kind === "download") { const arrow = document.createElementNS(ns, "path"); arrow.setAttribute("d", "M12 3v10m0 0l4-4m-4 4l-4-4"); arrow.setAttribute("fill", "none"); arrow.setAttribute("stroke", "currentColor"); arrow.setAttribute("stroke-width", "2"); arrow.setAttribute("stroke-linecap", "round"); arrow.setAttribute("stroke-linejoin", "round"); const tray = document.createElementNS(ns, "path"); tray.setAttribute("d", "M4 17v2a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-2"); tray.setAttribute("fill", "none"); tray.setAttribute("stroke", "currentColor"); tray.setAttribute("stroke-width", "2"); tray.setAttribute("stroke-linecap", "round"); tray.setAttribute("stroke-linejoin", "round"); svg.appendChild(arrow); svg.appendChild(tray); return svg; } const circle = document.createElementNS(ns, "circle"); circle.setAttribute("cx", "12"); circle.setAttribute("cy", "12"); circle.setAttribute("r", "9"); circle.setAttribute("fill", "none"); circle.setAttribute("stroke", "currentColor"); circle.setAttribute("stroke-width", "2"); const hand = document.createElementNS(ns, "path"); hand.setAttribute("d", "M12 7v5l3 2"); hand.setAttribute("fill", "none"); hand.setAttribute("stroke", "currentColor"); hand.setAttribute("stroke-width", "2"); hand.setAttribute("stroke-linecap", "round"); hand.setAttribute("stroke-linejoin", "round"); svg.appendChild(circle); svg.appendChild(hand); return svg; } function createButtonMetaItem(iconType, ariaLabel, onClick) { const wrapper = document.createElement("div"); wrapper.className = "bordered-item meta-item svelte-11gjrk3"; const inner = document.createElement("div"); inner.className = "meta-wrap svelte-11gjrk3"; const button = document.createElement("button"); button.type = "button"; button.className = "meta-value svelte-11gjrk3 s3-capture-button"; button.setAttribute("aria-label", ariaLabel); button.setAttribute("title", ariaLabel); button.appendChild(createIcon(iconType)); button.addEventListener("click", onClick); inner.appendChild(button); wrapper.appendChild(inner); return wrapper; } function ensureInlineStyles() { if (document.getElementById("s3-audio-capture-style")) { return; } const style = document.createElement("style"); style.id = "s3-audio-capture-style"; style.textContent = ` .s3-capture-button { border: 1px solid rgba(255, 255, 255, 0.25); border-radius: 4px; background: rgba(255, 255, 255, 0.08); color: inherit; cursor: pointer; padding: 4px 8px; font: inherit; display: inline-flex; align-items: center; justify-content: center; } .s3-capture-button:hover { background: rgba(255, 255, 255, 0.16); } .s3-capture-button svg { display: block; } `; document.head.appendChild(style); } function injectControls() { const target = document.querySelector(TARGET_SELECTOR); if (!target) { return; } if (target.querySelector("#" + INJECTED_CONTAINER_ID)) { return; } ensureInlineStyles(); const controlsRoot = document.createElement("div"); controlsRoot.id = INJECTED_CONTAINER_ID; controlsRoot.style.display = "contents"; const currentButton = createButtonMetaItem("download", "Download current audio", () => { browser.runtime.sendMessage({ type: "DOWNLOAD_LATEST" }).then((result) => { if (!result || !result.ok) { window.alert((result && result.error) || "No URL available for download."); } }).catch(() => { window.alert("Extension message failed."); }); }); const historyButton = createButtonMetaItem("history", "Open captured history", () => { browser.runtime.sendMessage({ type: "OPEN_HISTORY" }).catch(() => { window.alert("Unable to open history."); }); }); controlsRoot.appendChild(currentButton); controlsRoot.appendChild(historyButton); target.appendChild(controlsRoot); } function startUiObserver() { if (observer) { observer.disconnect(); } observer = new MutationObserver(() => { injectControls(); syncPlaybackMeta(); }); observer.observe(document.documentElement, { childList: true, subtree: true }); } function startCaptureObserver() { if (captureObserver) { captureObserver.disconnect(); } captureObserver = new MutationObserver((mutations) => { for (const mutation of mutations) { for (const addedNode of mutation.addedNodes) { scanNodeForUrls(addedNode); } if (mutation.type === "attributes" && mutation.target instanceof Element) { scanNodeForUrls(mutation.target); } } }); captureObserver.observe(document.documentElement, { childList: true, subtree: true, attributes: true, attributeFilter: ["href", "src", "data-url", "data-src"] }); } function syncPlaybackMeta() { const fileName = getPlaybackFilenameFromDom(); const key = getPlaybackKeyFromDom(); const bpm = getPlaybackBpmFromDom(); const tags = getPlaybackTagsFromDom(); const coverImageUrl = getPlaybackCoverImageFromDom(); const sameTags = tags.length === currentPlaybackTags.length && tags.every((tag, idx) => tag === currentPlaybackTags[idx]); if ( (!fileName || fileName === currentPlaybackFilename) && key === currentPlaybackKey && bpm === currentPlaybackBpm && sameTags && coverImageUrl === currentPlaybackCoverImageUrl ) { const signature = JSON.stringify({ fileName: currentPlaybackFilename, key: currentPlaybackKey, bpm: currentPlaybackBpm, tags: currentPlaybackTags, coverImageUrl: currentPlaybackCoverImageUrl }); if (lastPlaybackMetaApplied && signature === lastPlaybackMetaSignature) { return; } } if (fileName) { currentPlaybackFilename = fileName; } currentPlaybackKey = key; currentPlaybackBpm = bpm; currentPlaybackTags = tags; currentPlaybackCoverImageUrl = coverImageUrl; const payload = { type: "UPDATE_ACTIVE_PLAYBACK_META", fileName: currentPlaybackFilename, key: currentPlaybackKey, bpm: currentPlaybackBpm, tags: currentPlaybackTags, coverImageUrl: currentPlaybackCoverImageUrl }; const signature = JSON.stringify(payload); lastPlaybackMetaSignature = signature; lastPlaybackMetaApplied = false; browser.runtime.sendMessage(payload).then((result) => { // Keep retrying on the next interval until background confirms attachment. lastPlaybackMetaApplied = Boolean(result && result.ok); }).catch(() => { lastPlaybackMetaApplied = false; // Ignore transient extension messaging failures. }); } function installPageProbeListener() { if (probeListenerInstalled) { return; } window.addEventListener("message", (event) => { if (event.source !== window) { return; } const data = event.data; if (!data || data.source !== PROBE_EVENT_SOURCE) { return; } const kind = typeof data.kind === "string" ? data.kind : "unknown"; const payload = data.payload && typeof data.payload === "object" ? data.payload : {}; if (kind === "graphql-contexts") { const contexts = Array.isArray(payload.contexts) ? payload.contexts : []; if (contexts.length) { sendPreviewContexts(contexts); } return; } const candidateUrls = collectProbeCandidateUrls(payload); if (candidateUrls.length) { captureUrls(candidateUrls); } }); probeListenerInstalled = true; } function injectPageProbeScript() { if (document.getElementById("s3-capture-page-probe")) { return; } const root = document.head || document.documentElement; if (!root) { return; } const script = document.createElement("script"); script.id = "s3-capture-page-probe"; script.src = browser.runtime.getURL("page-probe.js"); script.async = false; script.onload = () => { script.remove(); }; root.appendChild(script); } function init() { if (!isDomainAllowed(window.location.href)) { return; } installPageProbeListener(); injectPageProbeScript(); currentPlaybackFilename = getPlaybackFilenameFromDom(); currentPlaybackKey = getPlaybackKeyFromDom(); currentPlaybackBpm = getPlaybackBpmFromDom(); currentPlaybackTags = getPlaybackTagsFromDom(); currentPlaybackCoverImageUrl = getPlaybackCoverImageFromDom(); lastPlaybackMetaSignature = ""; lastPlaybackMetaApplied = false; scanNodeForUrls(document); patchFetch(); patchXhr(); // Avoid buffered resource sweeps; they can re-capture stale previews. injectControls(); startUiObserver(); startCaptureObserver(); window.setInterval(syncPlaybackMeta, 700); } init();