A music player that connects to your cloud/distributed storage.
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: v3 import tool (just favourites for now)

+152
+4
src/facets/index.vto
··· 21 21 title: "Tools / Auto Queue Control Panel" 22 22 desc: > 23 23 Everything you need to automatically put tracks into the queue. 24 + - url: "facets/tools/v3-import.html.txt" 25 + title: "Tools / V3.x Import" 26 + desc: > 27 + Import data from Diffuse v3. 24 28 - url: "themes/webamp/browser/facet.html.txt" 25 29 title: "Webamp / Browser" 26 30 desc: >
+147
src/facets/tools/v3-import.html.txt
··· 1 + <main> 2 + <div class="panel"> 3 + <p> 4 + Import data from the previous version of Diffuse (v3). Upload the snapshot JSON file you 5 + exported. 6 + </p> 7 + <hr /> 8 + <div class="row"> 9 + <label class="with-icon" for="file"> 10 + <i class="ph-bold ph-upload"></i> 11 + <input id="file" type="file" accept=".json,application/json" /> 12 + </label> 13 + </div> 14 + <hr /> 15 + <div class="row"> 16 + <button id="import-favourites" disabled> 17 + <span class="with-icon"><i class="ph-fill ph-star"></i> Import favourites</span> 18 + </button> 19 + </div> 20 + <div id="status" class="status" hidden></div> 21 + </div> 22 + </main> 23 + 24 + <style> 25 + @import "./styles/base.css"; 26 + @import "./styles/vendor/phosphor/bold/style.css"; 27 + @import "./styles/vendor/phosphor/fill/style.css"; 28 + @import "./styles/wireframe/ui.css"; 29 + 30 + body { 31 + display: flex; 32 + align-items: center; 33 + justify-content: center; 34 + height: 100dvh; 35 + } 36 + 37 + main { 38 + padding: var(--space-md); 39 + } 40 + 41 + p { 42 + line-height: var(--leading-relaxed); 43 + max-width: 42ch; 44 + } 45 + 46 + label { 47 + flex: 1; 48 + } 49 + 50 + input[type="file"] { 51 + flex: 1; 52 + } 53 + 54 + .status { 55 + margin-top: var(--space-sm); 56 + font-size: var(--text-sm); 57 + } 58 + 59 + .status--success { 60 + color: var(--accent); 61 + 62 + @media (prefers-color-scheme: dark) { 63 + color: var(--accent-twist-4); 64 + } 65 + } 66 + 67 + .status--error { 68 + color: var(--accent-twist-5); 69 + 70 + @media (prefers-color-scheme: dark) { 71 + color: var(--accent-twist-3); 72 + } 73 + } 74 + </style> 75 + 76 + <script type="module"> 77 + import foundation from "./common/facets/foundation.js"; 78 + 79 + // Setup 80 + foundation.features.processInputs(); 81 + 82 + const favourites = foundation.orchestrator.favourites(); 83 + const output = foundation.orchestrator.output(); 84 + 85 + // Elements 86 + const fileInput = document.querySelector("#file"); 87 + const importFavouritesBtn = document.querySelector("#import-favourites"); 88 + const statusEl = document.querySelector("#status"); 89 + 90 + // Parsed data 91 + let json = null; 92 + 93 + /** 94 + * Show a status message. 95 + * @param {string} message 96 + * @param {"success" | "error"} type 97 + */ 98 + function showStatus(message, type) { 99 + statusEl.textContent = message; 100 + statusEl.className = `status status--${type}`; 101 + statusEl.hidden = false; 102 + } 103 + 104 + // Parse file on selection 105 + fileInput.onchange = async () => { 106 + const file = fileInput.files?.[0]; 107 + 108 + json = null; 109 + statusEl.hidden = true; 110 + importFavouritesBtn.disabled = true; 111 + 112 + if (!file) return; 113 + 114 + try { 115 + json = JSON.parse(await file.text()); 116 + } catch (err) { 117 + console.error("Failed to parse JSON:", err); 118 + showStatus(`Failed to parse JSON: ${err.message}`, "error"); 119 + return; 120 + } 121 + 122 + if (json.favourites?.data?.length > 0) { 123 + importFavouritesBtn.disabled = false; 124 + } 125 + }; 126 + 127 + // Import favourites on button click 128 + importFavouritesBtn.onclick = async () => { 129 + const items = json?.favourites?.data; 130 + if (!items || items.length === 0) return; 131 + 132 + try { 133 + const tracks = items.map((item) => ({ 134 + tags: { 135 + artist: item.artist ?? "", 136 + title: item.title ?? "", 137 + }, 138 + })); 139 + 140 + await favourites.include(tracks); 141 + showStatus(`Imported ${tracks.length} favourite(s).`, "success"); 142 + } catch (err) { 143 + console.error("Import failed:", err); 144 + showStatus(`Import failed: ${err.message}`, "error"); 145 + } 146 + }; 147 + </script>
+1
src/styles/wireframe/ui.css
··· 74 74 textarea { 75 75 background: transparent; 76 76 border: 2px solid var(--form-color); 77 + border-radius: var(--radius-md); 77 78 color: inherit; 78 79 font-size: var(--fs-sm); 79 80 padding: var(--space-2xs);