|
|
@@ -3,8 +3,7 @@ import delay from 'delay'; |
|
|
|
async function tryUntilItWorks(func, delayBetweenTries = 1, maxTries = 10) { |
|
|
|
for (let i = 0; i < maxTries; i++) { |
|
|
|
try { |
|
|
|
await func(); |
|
|
|
return; |
|
|
|
return await func(); |
|
|
|
} catch (err) { |
|
|
|
await delay(delayBetweenTries); |
|
|
|
continue; |
|
|
@@ -12,33 +11,38 @@ async function tryUntilItWorks(func, delayBetweenTries = 1, maxTries = 10) { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
async function insertOurAudioPlayer(tabId) { |
|
|
|
// Avoid any short glitch of sound (observed in Firefox) before our injected script executes. |
|
|
|
await browser.tabs.update(tabId, { muted: true }); |
|
|
|
|
|
|
|
// Too quickly executing the script may fail (bug in Firefox), so we retry if necessary. |
|
|
|
await tryUntilItWorks(() => browser.tabs.executeScript(tabId, { |
|
|
|
runAt: 'document_end', |
|
|
|
file: '/scripts/contentscript.js', |
|
|
|
})); |
|
|
|
|
|
|
|
// Unmute again. |
|
|
|
await browser.tabs.update(tabId, { muted: false }); |
|
|
|
|
|
|
|
await tryUntilItWorks(() => browser.tabs.insertCSS(tabId, { |
|
|
|
runAt: 'document_end', |
|
|
|
file: '/assets/waveform-playlist.css', |
|
|
|
})); |
|
|
|
|
|
|
|
await tryUntilItWorks(() => browser.tabs.insertCSS(tabId, { |
|
|
|
runAt: 'document_end', |
|
|
|
file: '/assets/main.css', |
|
|
|
})); |
|
|
|
} |
|
|
|
|
|
|
|
// Load the player on any file with an audio/* mime type. |
|
|
|
async function onHeadersReceived({ responseHeaders, url, tabId }) { |
|
|
|
const isAudio = responseHeaders.some(header => |
|
|
|
header.name.toLowerCase() === 'content-type' && header.value.toLowerCase().startsWith('audio/') |
|
|
|
); |
|
|
|
|
|
|
|
if (isAudio) { |
|
|
|
// Avoid any short glitch of sound (observed in Firefox) before our injected script executes. |
|
|
|
await browser.tabs.update(tabId, { muted: true }); |
|
|
|
|
|
|
|
// Too quickly executing the script may fail (bug in Firefox), so we retry if necessary. |
|
|
|
await tryUntilItWorks(() => browser.tabs.executeScript(tabId, { |
|
|
|
runAt: 'document_end', |
|
|
|
file: '/scripts/contentscript.js', |
|
|
|
})); |
|
|
|
|
|
|
|
// Unmute again. |
|
|
|
await browser.tabs.update(tabId, { muted: false }); |
|
|
|
|
|
|
|
await tryUntilItWorks(() => browser.tabs.insertCSS(tabId, { |
|
|
|
runAt: 'document_end', |
|
|
|
file: '/assets/waveform-playlist.css', |
|
|
|
})); |
|
|
|
|
|
|
|
await tryUntilItWorks(() => browser.tabs.insertCSS(tabId, { |
|
|
|
runAt: 'document_end', |
|
|
|
file: '/assets/main.css', |
|
|
|
})); |
|
|
|
insertOurAudioPlayer(tabId); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@@ -50,3 +54,27 @@ browser.webRequest.onHeadersReceived.addListener( |
|
|
|
}, |
|
|
|
['responseHeaders'], |
|
|
|
); |
|
|
|
|
|
|
|
// As files loaded through file://… URLs do not have headers, we run a separate check for these. |
|
|
|
async function onVisitFileUrl({ url, tabId }) { |
|
|
|
// I don’t know how we could neatly test whether the file is an audio file. This expression is a |
|
|
|
// pragmatic solution working for (at least) the current versions of Firefox and Chromium. |
|
|
|
const isAudioPlayerExpression = |
|
|
|
`document.body.childElementCount === 1 |
|
|
|
&& document.body.firstElementChild instanceof HTMLVideoElement |
|
|
|
&& document.body.firstElementChild.videoHeight === 0 |
|
|
|
&& document.body.firstElementChild.videoWidth === 0 |
|
|
|
`; |
|
|
|
const [isAudio] = await tryUntilItWorks(() => browser.tabs.executeScript(tabId, { |
|
|
|
code: isAudioPlayerExpression, |
|
|
|
runAt: 'document_idle', // (at document_end it’s not clear yet whether it’s audio or video..) |
|
|
|
})); |
|
|
|
if (isAudio) { |
|
|
|
insertOurAudioPlayer(tabId); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
browser.webNavigation.onCommitted.addListener( |
|
|
|
onVisitFileUrl, |
|
|
|
{ url: [{ schemes: ['file'] }] }, |
|
|
|
); |