Something went wrong. Try again.
a simple but full-featured web player for OpenSubsonic music servers (Navidrome, Gonic, Ampache, etc) devins.page/tinysub
svelte typescript javascript subsonic gonic navidrome
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133<script lang="ts">import { api } from "../api/client.svelte.js";import { t } from "../languages/translate.svelte.js";import { loadShares, recursiveSongs } from "../library/store.svelte.js";import { ui } from "../ui/state.svelte.js";import Modal from "./Modal.svelte";
let description = $state(""), expires = $state("0"), busy = $state(false), shareUrl = $state("");
$effect(() => { if (!ui.shareItemId) return; description = ui.shareItemLabel; expires = "0"; shareUrl = ""; busy = false;});
async function create() { const id = ui.shareItemId; if (!id) return; busy = true; try { let ids: string[]; if (ui.shareItemType === "song") { ids = [id]; } else if (ui.shareItemType === "artist") { const songs = await recursiveSongs( await api.getArtistAlbums(id), (item) => () => api.getAlbum(item.id as string), ); ids = songs.map((s) => s.id); } else { const items = ui.shareItemType === "album" ? await api.getAlbum(id) : await api.getPlaylist(id); const songs = await recursiveSongs(items); ids = songs.map((s) => s.id); } const expDate = expires === "0" ? undefined : Date.now() + parseInt(expires, 10) * 24 * 60 * 60 * 1000;
const res = await api.createShare(ids, description, expDate); shareUrl = res.shares.share[0].url; await loadShares(); } catch (err) { console.error(err); } finally { busy = false; }}
async function share() { try { await navigator.share({ title: description || t("common.share"), url: shareUrl, }); } catch (err) { if (!(err instanceof DOMException && err.name === "AbortError")) console.error("failed to share link:", err); }}</script>
<Modal open={!!ui.shareItemId} onclose={() => (ui.shareItemId = null)} title={t("common.share")}> <section> {#if shareUrl} <div class="share-result"> <input id="share-url" name="shareUrl" readonly value={shareUrl}> <div class="actions"> {#if typeof navigator !== "undefined" && typeof navigator.share === "function"} <button class="native" type="button" onclick={share}> {t("common.share")} </button> {/if} <button class="native" type="button" onclick={() => void navigator.clipboard.writeText(shareUrl)} > {t("common.copy")} </button> </div> </div> {:else} <form onsubmit={(e) => { e.preventDefault(); create(); }} > <input id="share-desc" name="shareDesc" bind:value={description} placeholder={t("common.description")} > <select id="share-expires" name="shareExpires" bind:value={expires}> <option value="0">{t("common.never")}</option> <option value="1">1 {t("common.day")}</option> <option value="7">7 {t("common.days")}</option> <option value="30">30 {t("common.days")}</option> </select> <button class="native" type="submit" disabled={busy}> {busy ? t("common.busy") : t("common.share")} </button> </form> {/if} </section></Modal>
<style>section { padding: 1rem;}.share-result { display: flex; flex-direction: column; gap: 0.5rem;}</style>