"use strict"; const TAB_CAPTURED = new Map(); const FULL_URL_BY_SAMPLE = new Map(); const HISTORY_KEY = "capturedS3History"; const MAX_HISTORY_ITEMS = 500; const TARGET_HOST = "splice.com"; const TARGET_PATH_PREFIX = "/sounds"; const AUDIO_SAMPLE_PATH_RE = /\/audio_samples\//i; const WAVEFORM_PATH_RE = /\.wv\.json$/i; const SCRAMBLED_PATH_RE = /-scrambled\//i; const HASH_RE = /^[a-f0-9]{64}$/i; const GRAPHQL_URL = "https://surfaces-graphql.splice.com/graphql"; function isDomainAllowed(pageUrl) { if (typeof pageUrl !== "string") { return false; } try { const parsed = new URL(pageUrl); const path = parsed.pathname || ""; const inSoundsPath = path === TARGET_PATH_PREFIX || path.startsWith(TARGET_PATH_PREFIX + "/"); return parsed.protocol === "https:" && parsed.hostname === TARGET_HOST && inSoundsPath; } catch (error) { return false; } } 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."); return isAwsHost && isS3Host; } catch (error) { return false; } } function isAudioSampleUrl(url) { if (!isS3Url(url)) { return false; } try { const parsed = new URL(url); return AUDIO_SAMPLE_PATH_RE.test(parsed.pathname || ""); } catch (error) { return false; } } function isWaveformUrl(url) { try { const parsed = new URL(url); return WAVEFORM_PATH_RE.test(parsed.pathname || ""); } catch (error) { return false; } } function isPreviewUrl(url) { try { const parsed = new URL(url); return SCRAMBLED_PATH_RE.test(parsed.pathname || ""); } catch (error) { return false; } } function isFullUrl(url) { return isAudioSampleUrl(url) && !isWaveformUrl(url) && !isPreviewUrl(url); } function normalizeUrl(url) { try { return new URL(url).toString(); } catch (error) { return ""; } } function normalizeName(value) { return typeof value === "string" ? value.trim().toLowerCase() : ""; } function extractSampleIdFromUrl(url) { try { const parsed = new URL(url); const path = parsed.pathname || ""; const marker = "/audio_samples/"; const idx = path.indexOf(marker); if (idx < 0) { return ""; } const rest = path.slice(idx + marker.length); const parts = rest.split("/").filter(Boolean); const first = parts[0] || ""; const second = parts[1] || ""; const firstNormalized = first.replace(/-scrambled$/i, ""); if (HASH_RE.test(firstNormalized)) { return firstNormalized.toLowerCase(); } const secondStem = second.split(".")[0]; if (HASH_RE.test(secondStem)) { return secondStem.toLowerCase(); } if (HASH_RE.test(first)) { return first.toLowerCase(); } return ""; } catch (error) { return ""; } } function ensureTabState(tabId) { if (!TAB_CAPTURED.has(tabId)) { TAB_CAPTURED.set(tabId, { latestSampleId: "", entriesBySample: new Map() }); } return TAB_CAPTURED.get(tabId); } function tabIdFromSender(sender) { return sender && sender.tab && typeof sender.tab.id === "number" ? sender.tab.id : -1; } function maybeClearTabState(tabId) { if (typeof tabId === "number" && tabId >= 0) { TAB_CAPTURED.delete(tabId); } } function mergeEntry(tabState, inputEntry, source, options) { const setLatest = !options || options.setLatest !== false; const sampleId = inputEntry.sampleId || extractSampleIdFromUrl(inputEntry.previewUrl || inputEntry.fullUrl || ""); if (!sampleId) { return null; } const existing = tabState.entriesBySample.get(sampleId) || { sampleId, previewUrl: "", fullUrl: "", fileName: "", packName: "", key: "", bpm: "", tags: [], coverImageUrl: "", source: source || "unknown", capturedAt: Date.now() }; const merged = { ...existing, sampleId, source: source || existing.source || "unknown", capturedAt: Date.now() }; if (inputEntry.previewUrl) { merged.previewUrl = inputEntry.previewUrl; } if (inputEntry.fullUrl) { merged.fullUrl = inputEntry.fullUrl; FULL_URL_BY_SAMPLE.set(sampleId, inputEntry.fullUrl); } else if (!merged.fullUrl && FULL_URL_BY_SAMPLE.has(sampleId)) { merged.fullUrl = FULL_URL_BY_SAMPLE.get(sampleId); } if (inputEntry.fileName) { merged.fileName = String(inputEntry.fileName).trim(); } if (typeof inputEntry.packName === "string" && inputEntry.packName.trim()) { merged.packName = inputEntry.packName.trim(); } if (typeof inputEntry.key === "string" && inputEntry.key.trim()) { merged.key = inputEntry.key.trim(); } if (typeof inputEntry.bpm === "string" && inputEntry.bpm.trim()) { merged.bpm = inputEntry.bpm.trim(); } if (Array.isArray(inputEntry.tags) && inputEntry.tags.length) { merged.tags = Array.from(new Set(inputEntry.tags.map((x) => String(x).trim()).filter(Boolean))); } if (typeof inputEntry.coverImageUrl === "string" && inputEntry.coverImageUrl.trim()) { merged.coverImageUrl = inputEntry.coverImageUrl.trim(); } tabState.entriesBySample.set(sampleId, merged); if (setLatest) { tabState.latestSampleId = sampleId; } return merged; } function upsertPreviewContext(tabId, context, source) { const tabState = ensureTabState(tabId); const previewUrl = normalizeUrl(context.previewUrl || ""); if (!previewUrl || !isAudioSampleUrl(previewUrl) || isWaveformUrl(previewUrl)) { return null; } const sampleId = (context.sampleId || extractSampleIdFromUrl(previewUrl)).toLowerCase(); if (!sampleId) { return null; } return mergeEntry( tabState, { sampleId, previewUrl, fullUrl: context.fullUrl ? normalizeUrl(context.fullUrl) : "", fileName: context.fileName || "", packName: context.packName || "", key: context.key || "", bpm: context.bpm || "", tags: Array.isArray(context.tags) ? context.tags : [], coverImageUrl: context.coverImageUrl || "" }, source || "graphql_preview", { setLatest: false } ); } function upsertUrl(tabId, url, source) { const sourceTag = source && typeof source === "object" ? source.tag || "" : source || ""; const sourceFileName = source && typeof source === "object" ? source.fileName || "" : ""; const sourcePackName = source && typeof source === "object" ? source.packName || "" : ""; const sourceKey = source && typeof source === "object" ? source.key || "" : ""; const sourceBpm = source && typeof source === "object" ? source.bpm || "" : ""; const sourceTags = source && typeof source === "object" && Array.isArray(source.tags) ? source.tags : []; const sourceCoverImageUrl = source && typeof source === "object" ? source.coverImageUrl || "" : ""; const normalized = normalizeUrl(url); if (!normalized || !isAudioSampleUrl(normalized) || isWaveformUrl(normalized)) { return null; } const sampleId = extractSampleIdFromUrl(normalized); if (!sampleId) { return null; } const entry = { sampleId, previewUrl: isPreviewUrl(normalized) ? normalized : "", fullUrl: isFullUrl(normalized) ? normalized : "", fileName: sourceFileName, packName: sourcePackName, key: sourceKey, bpm: sourceBpm, tags: sourceTags, coverImageUrl: sourceCoverImageUrl }; if (typeof tabId === "number" && tabId >= 0) { return mergeEntry(ensureTabState(tabId), entry, sourceTag); } if (entry.fullUrl) { FULL_URL_BY_SAMPLE.set(sampleId, entry.fullUrl); } return null; } async function appendHistoryRows(rows) { if (!rows.length) { return; } const data = await browser.storage.local.get(HISTORY_KEY); const current = Array.isArray(data[HISTORY_KEY]) ? data[HISTORY_KEY] : []; const combined = [...current]; const seenMap = new Map(); current.forEach((item, idx) => { seenMap.set(`${item.sampleId}|${item.url}`, idx); }); for (const row of rows) { const key = `${row.sampleId}|${row.url}`; if (!row.url) { continue; } if (seenMap.has(key)) { const existingIdx = seenMap.get(key); const existing = combined[existingIdx] || {}; const hasCoverImageUrl = Object.prototype.hasOwnProperty.call(row, "coverImageUrl"); combined[existingIdx] = { ...existing, ...row, fileName: row.fileName || existing.fileName || "", packName: row.packName || existing.packName || "", key: row.key || existing.key || "", bpm: row.bpm || existing.bpm || "", tags: Array.isArray(row.tags) && row.tags.length ? row.tags : (existing.tags || []), coverImageUrl: hasCoverImageUrl ? String(row.coverImageUrl || "") : (existing.coverImageUrl || "") }; continue; } seenMap.set(key, combined.length); combined.push(row); } await browser.storage.local.set({ [HISTORY_KEY]: combined.slice(-MAX_HISTORY_ITEMS) }); } async function appendDownloadHistory(entry, result) { const resolvedEntry = result && result.resolvedEntry ? result.resolvedEntry : entry; const historyRow = { sampleId: resolvedEntry.sampleId || "", previewUrl: resolvedEntry.previewUrl || "", fullUrl: resolvedEntry.fullUrl || "", fileName: resolvedEntry.fileName || "", packName: resolvedEntry.packName || "", key: resolvedEntry.key || "", bpm: resolvedEntry.bpm || "", tags: Array.isArray(resolvedEntry.tags) ? resolvedEntry.tags : [], coverImageUrl: resolvedEntry.coverImageUrl || "", url: result.historyUrl || resolvedEntry.fullUrl || "", source: result.mode === "full" ? "full_download" : "decoded_preview", capturedAt: Date.now(), mode: result.mode }; await appendHistoryRows([historyRow]); } function makeDecodedPlaceholderRow(entry, source) { const sampleId = entry.sampleId || ""; if (!sampleId) { return null; } return { sampleId, previewUrl: entry.previewUrl || "", fullUrl: entry.fullUrl || "", fileName: entry.fileName || "", packName: entry.packName || "", key: entry.key || "", bpm: entry.bpm || "", tags: Array.isArray(entry.tags) ? entry.tags : [], coverImageUrl: entry.coverImageUrl || "", url: `decoded://${sampleId}`, source: source || entry.source || "captured_preview", capturedAt: entry.capturedAt || Date.now(), mode: "pending_decoded" }; } function decodePass(startIdx, arr, encodeBlk, endIdx) { let blockIdx = 0; for (let i = startIdx; i < endIdx; i += 1) { if (blockIdx > encodeBlk.length - 1) { blockIdx = 0; } arr[i] = arr[i] ^ encodeBlk.charCodeAt(blockIdx); blockIdx += 1; } return endIdx; } function decodeSpliceAudio(data) { const sizeData = Array.from(data.subarray(2, 10)); let size = 0; for (let i = sizeData.length - 1; i >= 0; i -= 1) { size = (256 * size) + sizeData[i]; } const encodingData = data.subarray(10, 28); const chunks = []; for (let i = 0; i < encodingData.length; i += 32768) { chunks.push(String.fromCharCode(...Array.from(encodingData.subarray(i, i + 32768)))); } const encodeBlk = chunks.join(""); const audioData = data.slice(28); const passIdx = decodePass(0, audioData, encodeBlk, size) + size; decodePass(passIdx, audioData, encodeBlk, passIdx + size); return audioData; } function deriveFilename(entry, mode) { const sampleBase = entry.sampleId || `splice-${Date.now()}`; const rawName = typeof entry.fileName === "string" ? entry.fileName.trim() : ""; const cleanName = rawName ? rawName.replace(/[<>:"/\\|?*\u0000-\u001f]/g, "_") : ""; const hasExt = cleanName.lastIndexOf(".") > 0; const stem = hasExt ? cleanName.slice(0, cleanName.lastIndexOf(".")) : cleanName; const ext = hasExt ? cleanName.slice(cleanName.lastIndexOf(".")).toLowerCase() : ""; const safeStem = stem || sampleBase; if (mode === "full") { if (ext === ".wav" || ext === ".mp3" || ext === ".aif" || ext === ".aiff" || ext === ".flac") { return `${safeStem}${ext}`; } const full = (entry.fullUrl || "").toLowerCase(); if (full.endsWith(".wav") || full.includes(".wav?")) { return `${safeStem}.wav`; } if (full.endsWith(".mp3") || full.includes(".mp3?")) { return `${safeStem}.mp3`; } return `${safeStem}.wav`; } return `${safeStem}.mp3`; } async function downloadDirectUrl(url, entry) { const filename = deriveFilename(entry, "full"); await browser.downloads.download({ url, saveAs: true, filename, conflictAction: "uniquify" }); return { ok: true, mode: "full", filename, historyUrl: url }; } async function downloadDecodedPreview(previewUrl, entry) { const response = await fetch(previewUrl, { credentials: "include" }); if (!response.ok) { throw new Error(`Preview fetch failed (${response.status}).`); } const raw = new Uint8Array(await response.arrayBuffer()); const decoded = decodeSpliceAudio(raw); const blob = new Blob([decoded], { type: "audio/mpeg" }); const objectUrl = URL.createObjectURL(blob); const filename = deriveFilename(entry, "decoded"); try { await browser.downloads.download({ url: objectUrl, saveAs: true, filename, conflictAction: "uniquify" }); } finally { window.setTimeout(() => URL.revokeObjectURL(objectUrl), 30000); } return { ok: true, mode: "decoded", filename, historyUrl: `decoded://${entry.sampleId || Date.now()}` }; } function extractAudioUrlsFromObject(input, output) { if (!input) { return; } if (typeof input === "string") { const normalized = normalizeUrl(input); if (normalized && isFullUrl(normalized)) { output.add(normalized); } return; } if (Array.isArray(input)) { for (const item of input) { extractAudioUrlsFromObject(item, output); } return; } if (typeof input === "object") { for (const value of Object.values(input)) { extractAudioUrlsFromObject(value, output); } } } async function queryBackendForFullUrl(entry) { const sampleId = entry.sampleId || ""; if (!sampleId) { return ""; } if (FULL_URL_BY_SAMPLE.has(sampleId)) { return FULL_URL_BY_SAMPLE.get(sampleId); } const body = { operationName: "SampleLookupForDownload", query: "query SampleLookupForDownload($query: String!, $limit: Int = 5) {" + " assetsSearch(filter: {legacy: true, published: true, asset_type_slug: sample, query: $query}," + " pagination: {page: 1, limit: $limit}, sort: {sort: relevance, order: DESC}) {" + " items { ... on IAsset { uuid files { asset_file_type_slug url path } } } } }", variables: { query: sampleId, limit: 5 } }; try { const response = await fetch(GRAPHQL_URL, { method: "POST", credentials: "include", headers: { "content-type": "application/json" }, body: JSON.stringify(body) }); if (!response.ok) { return ""; } const json = await response.json(); const urls = new Set(); extractAudioUrlsFromObject(json, urls); for (const url of urls) { const id = extractSampleIdFromUrl(url); if (id === sampleId && isFullUrl(url)) { FULL_URL_BY_SAMPLE.set(sampleId, url); return url; } } return ""; } catch (error) { return ""; } } function findEntryInTabStates(sampleId) { for (const state of TAB_CAPTURED.values()) { if (!state || !state.entriesBySample) { continue; } const entry = state.entriesBySample.get(sampleId); if (entry && (entry.previewUrl || entry.fullUrl)) { return { sampleId: entry.sampleId || sampleId, previewUrl: normalizeUrl(entry.previewUrl || ""), fullUrl: normalizeUrl(entry.fullUrl || ""), fileName: entry.fileName || "", packName: entry.packName || "", key: entry.key || "", bpm: entry.bpm || "", tags: Array.isArray(entry.tags) ? entry.tags : [], coverImageUrl: entry.coverImageUrl || "" }; } } return null; } async function hydrateEntryFromHistory(sampleId) { const data = await browser.storage.local.get(HISTORY_KEY); const rows = Array.isArray(data[HISTORY_KEY]) ? data[HISTORY_KEY] : []; for (let i = rows.length - 1; i >= 0; i -= 1) { const row = rows[i]; if (!row || row.sampleId !== sampleId) { continue; } const previewUrl = normalizeUrl(row.previewUrl || ""); const fullUrl = normalizeUrl(row.fullUrl || ""); if (previewUrl || fullUrl) { return { sampleId, previewUrl, fullUrl, fileName: row.fileName || "", packName: row.packName || "", key: row.key || "", bpm: row.bpm || "", tags: Array.isArray(row.tags) ? row.tags : [], coverImageUrl: row.coverImageUrl || "" }; } } return null; } async function resolveAndDownloadEntry(entryInput) { const entry = entryInput || {}; const sampleId = entry.sampleId || extractSampleIdFromUrl(entry.previewUrl || entry.fullUrl || ""); let normalizedEntry = { sampleId, previewUrl: normalizeUrl(entry.previewUrl || ""), fullUrl: normalizeUrl(entry.fullUrl || ""), fileName: entry.fileName || "", packName: entry.packName || "", key: entry.key || "", bpm: entry.bpm || "", tags: Array.isArray(entry.tags) ? entry.tags : [], coverImageUrl: entry.coverImageUrl || "" }; if (normalizedEntry.sampleId && !normalizedEntry.previewUrl && !normalizedEntry.fullUrl) { const fromTabState = findEntryInTabStates(normalizedEntry.sampleId); if (fromTabState) { normalizedEntry = fromTabState; } else { const fromHistory = await hydrateEntryFromHistory(normalizedEntry.sampleId); if (fromHistory) { normalizedEntry = fromHistory; } } } if (normalizedEntry.fullUrl && isFullUrl(normalizedEntry.fullUrl)) { const result = await downloadDirectUrl(normalizedEntry.fullUrl, normalizedEntry); return { ...result, resolvedEntry: normalizedEntry }; } const discoveredFull = await queryBackendForFullUrl(normalizedEntry); if (discoveredFull) { normalizedEntry.fullUrl = discoveredFull; const result = await downloadDirectUrl(discoveredFull, normalizedEntry); return { ...result, resolvedEntry: normalizedEntry }; } if (normalizedEntry.previewUrl) { const result = await downloadDecodedPreview(normalizedEntry.previewUrl, normalizedEntry); return { ...result, resolvedEntry: normalizedEntry }; } throw new Error("No preview or full URL available for this sample."); } function getLatestEntryForTab(tabId) { const state = TAB_CAPTURED.get(tabId); if (!state || !state.latestSampleId) { return null; } return state.entriesBySample.get(state.latestSampleId) || null; } function serializeEntry(entry) { return { sampleId: entry.sampleId || "", previewUrl: entry.previewUrl || "", fullUrl: entry.fullUrl || "", fileName: entry.fileName || "", packName: entry.packName || "", key: entry.key || "", bpm: entry.bpm || "", tags: Array.isArray(entry.tags) ? entry.tags : [], coverImageUrl: entry.coverImageUrl || "", source: entry.source || "", capturedAt: entry.capturedAt || Date.now() }; } function serializeTabEntries(state) { return Array.from(state.entriesBySample.values()) .sort((a, b) => (b.capturedAt || 0) - (a.capturedAt || 0)) .map(serializeEntry); } async function clearAllCapturedState() { TAB_CAPTURED.clear(); FULL_URL_BY_SAMPLE.clear(); await browser.storage.local.remove(HISTORY_KEY); } browser.runtime.onMessage.addListener((message, sender) => { if (!message || typeof message.type !== "string") { return undefined; } if (message.type === "CAPTURE_PREVIEW_CONTEXT") { const tabId = tabIdFromSender(sender); if (tabId < 0) { return Promise.resolve({ ok: false, added: 0 }); } const senderUrl = sender && sender.tab ? sender.tab.url : ""; if (!isDomainAllowed(senderUrl)) { return Promise.resolve({ ok: false, added: 0 }); } const contexts = Array.isArray(message.contexts) ? message.contexts : []; for (const context of contexts) { upsertPreviewContext(tabId, context, "graphql_preview"); } return Promise.resolve({ ok: true, added: contexts.length }); } if (message.type === "CAPTURE_URLS") { const tabId = tabIdFromSender(sender); const urls = Array.isArray(message.urls) ? message.urls : []; const canAttachPlaybackMeta = urls.length === 1; const rows = []; for (const raw of urls) { const merged = upsertUrl(tabId, raw, { tag: message.source || "network_preview", fileName: canAttachPlaybackMeta ? (message.fileName || "") : "", packName: "", key: "", bpm: "", tags: [], coverImageUrl: "" }); if (merged && merged.previewUrl && canAttachPlaybackMeta) { const row = makeDecodedPlaceholderRow(merged, message.source || "network_preview"); if (row) { rows.push(row); } } } return appendHistoryRows(rows).then(() => ({ ok: true, added: urls.length })); } if (message.type === "GET_TAB_URLS") { const tabId = typeof message.tabId === "number" ? message.tabId : -1; const state = TAB_CAPTURED.get(tabId); if (!state) { return Promise.resolve({ ok: true, entries: [] }); } return Promise.resolve({ ok: true, entries: serializeTabEntries(state) }); } if (message.type === "GET_HISTORY") { return browser.storage.local.get(HISTORY_KEY).then((data) => { const rows = Array.isArray(data[HISTORY_KEY]) ? data[HISTORY_KEY] : []; const filtered = rows.filter((row) => { const url = typeof row.url === "string" ? row.url : ""; if (url.startsWith("decoded://")) { return true; } if (typeof row.fullUrl === "string" && isFullUrl(row.fullUrl)) { return true; } return isFullUrl(url); }); filtered.sort((a, b) => (b.capturedAt || 0) - (a.capturedAt || 0)); return { ok: true, history: filtered }; }); } if (message.type === "CLEAR_CAPTURED_STATE") { return clearAllCapturedState().then(() => ({ ok: true })); } if (message.type === "DOWNLOAD_ENTRY") { return resolveAndDownloadEntry(message.entry) .then(async (result) => { if (result && result.ok) { const entry = message.entry || {}; await appendDownloadHistory(entry, result); } return result; }) .catch((error) => ({ ok: false, error: error.message || "Download failed." })); } if (message.type === "UPDATE_ACTIVE_PLAYBACK_META" || message.type === "UPDATE_ACTIVE_FILENAME") { const tabId = tabIdFromSender(sender); const state = TAB_CAPTURED.get(tabId); if (!state || !state.latestSampleId) { return Promise.resolve({ ok: false }); } const latest = state.entriesBySample.get(state.latestSampleId); if (!latest) { return Promise.resolve({ ok: false }); } const fileName = typeof message.fileName === "string" ? message.fileName.trim() : ""; const packName = typeof message.packName === "string" ? message.packName.trim() : ""; const key = typeof message.key === "string" ? message.key.trim() : ""; const bpm = typeof message.bpm === "string" ? message.bpm.trim() : ""; const tags = Array.isArray(message.tags) ? message.tags.map((x) => String(x).trim()).filter(Boolean) : []; const coverImageUrl = typeof message.coverImageUrl === "string" ? message.coverImageUrl.trim() : ""; const entries = Array.from(state.entriesBySample.values()); let targetEntry = null; if (fileName) { const wantedName = normalizeName(fileName); const matches = entries .filter((entry) => normalizeName(entry.fileName) === wantedName) .sort((a, b) => (b.capturedAt || 0) - (a.capturedAt || 0)); if (matches.length) { targetEntry = matches[0]; } } if (!targetEntry && state.latestSampleId) { const latestEntry = state.entriesBySample.get(state.latestSampleId); const freshEnough = latestEntry && (Date.now() - (latestEntry.capturedAt || 0) < 15000); const canHydrate = latestEntry && !latestEntry.fileName; if (freshEnough && canHydrate) { targetEntry = latestEntry; } } if (!targetEntry) { return Promise.resolve({ ok: false }); } if (fileName) { targetEntry.fileName = fileName; } if (packName) { targetEntry.packName = packName; } if (key) { targetEntry.key = key; } if (bpm) { targetEntry.bpm = bpm; } if (tags.length) { targetEntry.tags = Array.from(new Set(tags)); } if (coverImageUrl) { targetEntry.coverImageUrl = coverImageUrl; } targetEntry.capturedAt = Date.now(); state.entriesBySample.set(targetEntry.sampleId, targetEntry); state.latestSampleId = targetEntry.sampleId; const row = makeDecodedPlaceholderRow(targetEntry, targetEntry.source || "playback_meta"); if (!row) { return Promise.resolve({ ok: true }); } return appendHistoryRows([row]).then(() => ({ ok: true })); } if (message.type === "DOWNLOAD_LATEST") { const tabId = tabIdFromSender(sender); const latest = getLatestEntryForTab(tabId); if (!latest) { return Promise.resolve({ ok: false, error: "No captured sample is available yet." }); } return resolveAndDownloadEntry(latest) .then(async (result) => { if (result && result.ok) { await appendDownloadHistory(latest, result); } return result; }) .catch((error) => ({ ok: false, error: error.message || "Download failed." })); } if (message.type === "OPEN_HISTORY") { const historyUrl = browser.runtime.getURL("history.html"); return browser.tabs.create({ url: historyUrl }).then(() => ({ ok: true })); } return undefined; }); function handleWebRequestCapture(details) { const url = typeof details.url === "string" ? details.url : ""; if (!isAudioSampleUrl(url) || isWaveformUrl(url)) { return; } const tabId = typeof details.tabId === "number" ? details.tabId : -1; if (tabId < 0) { return; } browser.tabs.get(tabId).then((tab) => { const tabUrl = tab && typeof tab.url === "string" ? tab.url : ""; if (!isDomainAllowed(tabUrl)) { return; } const merged = upsertUrl(tabId, url, "webrequest"); void merged; }).catch(() => { // Ignore tab lookup failures. }); } browser.webRequest.onBeforeRequest.addListener( handleWebRequestCapture, { urls: ["*://*.amazonaws.com/*", "*://*.amazonaws.com.cn/*"] } ); browser.tabs.onRemoved.addListener((tabId) => { maybeClearTabState(tabId); }); browser.webNavigation.onCommitted.addListener((details) => { if (details.frameId !== 0) { return; } maybeClearTabState(details.tabId); }); browser.runtime.onStartup.addListener(() => { void clearAllCapturedState(); });