} */
const SCHEME_NAMES = {
[SCHEME_DROPBOX]: "Dropbox",
[SCHEME_EPHEMERAL_CACHE]: "Browser storage",
[SCHEME_HTTPS]: "HTTPS",
[SCHEME_ICECAST]: "Icecast",
[SCHEME_LOCAL]: "Local directories & files",
[SCHEME_OPENSUBSONIC]: "OpenSubsonic",
[SCHEME_S3]: "S3",
};
foundation.setup({ title: "Sources | Diffuse" });
////////////////////////////////////////////
// SETUP
////////////////////////////////////////////
const [inputConfigurator, sourcesOrchestrator, outputOrchestrator, processOrchestrator] =
await Promise.all([
foundation.configurator.input(),
foundation.orchestrator.sources(),
foundation.orchestrator.output(),
foundation.orchestrator.processTracks({ disableWhenReady: true }),
]);
await Promise.all([
customElements.whenDefined(inputConfigurator.localName),
customElements.whenDefined(sourcesOrchestrator.localName),
customElements.whenDefined(outputOrchestrator.localName),
]);
////////////////////////////////////////////
// PROCESS BUTTON
////////////////////////////////////////////
const processBtn = /** @type {HTMLButtonElement} */ (document.querySelector("#process-btn"));
const processIcon = /** @type {HTMLElement} */ (document.querySelector("#process-icon"));
const processLabel = /** @type {HTMLElement} */ (document.querySelector("#process-label"));
effect(() => {
const isProcessing = processOrchestrator.isProcessing();
processBtn.disabled = isProcessing;
processIcon.className = isProcessing
? "ph-fill ph-arrows-clockwise animate-spin"
: "ph-fill ph-arrows-clockwise";
processLabel.textContent = isProcessing ? "Processing" : "Process";
});
processBtn.addEventListener("click", async () => {
const output = await foundation.orchestrator.output();
await Output.data(output.tracks);
await processOrchestrator.process();
});
////////////////////////////////////////////
// UI
////////////////////////////////////////////
const list =
/** @type {HTMLElement} */ (document.querySelector("#sources-list"));
const empty =
/** @type {HTMLElement} */ (document.querySelector("#sources-empty"));
/** @param {string} uri */
const trackPrefix = (uri) => { const q = uri.indexOf("?"); return q === -1 ? uri : uri.slice(0, q); };
effect(() => {
const sourcesRecord = sourcesOrchestrator.sources();
const tracksCol = outputOrchestrator.tracks.collection();
const tracks = tracksCol.state === "loaded" ? tracksCol.data : [];
const entries = Object.entries(sourcesRecord).filter(
([, sources]) => sources.length > 0,
);
list.hidden = entries.length === 0;
empty.hidden = entries.length > 0;
litRender(
html`
${entries.map(([scheme, sources]) => {
if (scheme === SCHEME_EPHEMERAL_CACHE) {
const uri = `${SCHEME_EPHEMERAL_CACHE}://`;
const isDisabled = sourcesOrchestrator.isDisabled(uri);
const trackCount = tracks.filter((t) =>
t.uri.startsWith(uri)
).length;
return html`
${SCHEME_NAMES[scheme] ?? scheme}
Files stored in the browser
${trackCount} track${trackCount ===
1
? ""
: "s"}
`;
}
return html`
${SCHEME_NAMES[scheme] ?? scheme}
${sources.map(({ label, uri }) => {
const isDisabled = sourcesOrchestrator.isDisabled(uri);
const trackCount = tracks.filter((t) =>
t.uri.startsWith(trackPrefix(uri))
).length;
return html`
${label}
${trackCount} track${trackCount ===
1
? ""
: "s"}
`;
})}
`;
})}
`,
list,
);
});
////////////////////////////////////////////
// ACTIONS
////////////////////////////////////////////
async function removeEphemeralSources() {
return removeSource(SCHEME_EPHEMERAL_CACHE);
}
/** @param {string} uri */
async function removeSource(uri) {
const tracks = await Output.data(outputOrchestrator.tracks);
const detachedTracks = await inputConfigurator.detach({
fileUriOrScheme: uri,
tracks,
});
if (detachedTracks) await outputOrchestrator.tracks.save(detachedTracks);
}
////////////////////////////////////////////
// 🚀
////////////////////////////////////////////
foundation.ready();