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

Configure Feed

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

feat(theme/winamp): artwork window

+195
+171
src/facets/themes/winamp/artwork/element.js
··· 1 + import { 2 + defineElement, 3 + DiffuseElement, 4 + query, 5 + whenElementsDefined, 6 + } from "~/common/element.js"; 7 + import { signal, untracked } from "~/common/signal.js"; 8 + 9 + /** 10 + * @import {RenderArg} from "~/common/element.d.ts" 11 + * 12 + * @import ArtworkOrchestrator from "~/components/orchestrator/artwork/element.js" 13 + * @import ControllerOrchestrator from "~/components/orchestrator/controller/element.js" 14 + */ 15 + 16 + class ArtworkWindowElement extends DiffuseElement { 17 + constructor() { 18 + super(); 19 + this.attachShadow({ mode: "open" }); 20 + } 21 + 22 + // SIGNALS 23 + 24 + #artworkUrl = signal(/** @type {string | null} */ (null)); 25 + 26 + /** @type {string | null} */ 27 + #prevUrl = null; 28 + 29 + // SIGNALS - DEPENDENCIES 30 + 31 + $artwork = signal(/** @type {ArtworkOrchestrator | undefined} */ (undefined)); 32 + $controller = signal( 33 + /** @type {ControllerOrchestrator | undefined} */ (undefined), 34 + ); 35 + 36 + // COMPUTED 37 + 38 + currentTrack = () => this.$controller.value?.currentTrack(); 39 + 40 + // LIFECYCLE 41 + 42 + /** 43 + * @override 44 + */ 45 + connectedCallback() { 46 + super.connectedCallback(); 47 + 48 + /** @type {ArtworkOrchestrator} */ 49 + const artwork = query(this, "artwork-selector"); 50 + 51 + /** @type {ControllerOrchestrator} */ 52 + const controller = query(this, "controller-orchestrator-selector"); 53 + 54 + whenElementsDefined({ artwork, controller }).then(() => { 55 + this.$artwork.value = artwork; 56 + this.$controller.value = controller; 57 + 58 + this.effect(() => { 59 + const track = this.currentTrack(); 60 + untracked(() => this.#fetchArtwork(track)); 61 + }); 62 + }); 63 + } 64 + 65 + /** 66 + * @override 67 + */ 68 + disconnectedCallback() { 69 + if (this.#prevUrl) { 70 + URL.revokeObjectURL(this.#prevUrl); 71 + this.#prevUrl = null; 72 + } 73 + } 74 + 75 + /** 76 + * @param {ReturnType<ControllerOrchestrator["currentTrack"]>} track 77 + */ 78 + async #fetchArtwork(track) { 79 + const bytes = track 80 + ? ((await this.$artwork.value?.get(track)) ?? null) 81 + : null; 82 + 83 + if (this.$controller.value?.$queue.value?.now()?.id !== track?.id) return; 84 + 85 + if (this.#prevUrl) { 86 + URL.revokeObjectURL(this.#prevUrl); 87 + this.#prevUrl = null; 88 + } 89 + 90 + if (bytes) { 91 + const mime = detectMime(bytes); 92 + const url = URL.createObjectURL( 93 + new Blob([/** @type {ArrayBuffer} */ (bytes.buffer)], { type: mime }), 94 + ); 95 + this.#prevUrl = url; 96 + this.#artworkUrl.value = url; 97 + } else { 98 + this.#artworkUrl.value = null; 99 + } 100 + } 101 + 102 + /** 103 + * @param {RenderArg} _ 104 + */ 105 + render({ html }) { 106 + const url = this.#artworkUrl.value; 107 + 108 + return html` 109 + <style> 110 + :host { 111 + background: #000; 112 + display: block; 113 + height: 100%; 114 + width: 100%; 115 + } 116 + 117 + .art { 118 + align-items: center; 119 + background: #000; 120 + display: flex; 121 + height: 100%; 122 + justify-content: center; 123 + min-height: 240px; 124 + width: 100%; 125 + } 126 + 127 + img { 128 + display: block; 129 + max-height: 100%; 130 + max-width: 100%; 131 + object-fit: contain; 132 + } 133 + 134 + .no-artwork { 135 + color: #808080; 136 + font-family: "Pixelated MS Sans Serif", sans-serif; 137 + font-size: 11px; 138 + } 139 + </style> 140 + 141 + <div class="art"> 142 + ${url 143 + ? html`<img src="${url}" alt="" />` 144 + : html`<span class="no-artwork">No artwork</span>`} 145 + </div> 146 + `; 147 + } 148 + } 149 + 150 + export default ArtworkWindowElement; 151 + 152 + //////////////////////////////////////////// 153 + // REGISTER 154 + //////////////////////////////////////////// 155 + 156 + export const CLASS = ArtworkWindowElement; 157 + export const NAME = "dtw-winamp-artwork"; 158 + 159 + defineElement(NAME, ArtworkWindowElement); 160 + 161 + /** 162 + * @param {Uint8Array} bytes 163 + * @returns {string} 164 + */ 165 + function detectMime(bytes) { 166 + if (bytes[0] === 0xff && bytes[1] === 0xd8) return "image/jpeg"; 167 + if (bytes[0] === 0x89 && bytes[1] === 0x50) return "image/png"; 168 + if (bytes[0] === 0x47 && bytes[1] === 0x49) return "image/gif"; 169 + if (bytes[0] === 0x52 && bytes[1] === 0x49) return "image/webp"; 170 + return "image/jpeg"; 171 + }
+22
src/facets/themes/winamp/facet/index.html
··· 82 82 ></dtw-browser> 83 83 </dtw-window> 84 84 85 + <!-- 🖼️ --> 86 + <dtw-window 87 + id="artwork-window" 88 + window-style="min-width: 0; width: 275px; height: 275px;" 89 + > 90 + <span slot="title-icon" 91 + ><img src="images/icons/windows_98/imagwmf-1.png" height="14" 92 + /></span> 93 + <span slot="title">Artwork</span> 94 + 95 + <dtw-winamp-artwork 96 + artwork-selector="do-artwork" 97 + controller-orchestrator-selector="do-controller" 98 + ></dtw-winamp-artwork> 99 + </dtw-window> 100 + 85 101 <!-- ⚡️ --> 86 102 <dtw-winamp 87 103 controller-orchestrator-selector="do-controller" ··· 118 134 <a class="button desktop__item"> 119 135 <img src="images/icons/windows_98/directory_explorer-5.png" height="32" /> 120 136 <label for="browser-window">Browse collection</label> 137 + </a> 138 + 139 + <!-- ARTWORK --> 140 + <a class="button desktop__item"> 141 + <img src="images/icons/windows_98/imagwmf-1.png" height="32" /> 142 + <label for="artwork-window">Artwork</label> 121 143 </a> 122 144 123 145 <!-- INSERT -->
+2
src/facets/themes/winamp/facet/index.inline.js
··· 18 18 await foundation.orchestrator.processTracks({ disableWhenReady: true }); 19 19 await foundation.orchestrator.queueAudio(); 20 20 await foundation.orchestrator.controller(); 21 + await foundation.orchestrator.artwork(); 21 22 22 23 await import("~/facets/themes/winamp/browser/element.js"); 23 24 await import("~/facets/themes/winamp/window/element.js"); 25 + await import("~/facets/themes/winamp/artwork/element.js"); 24 26 25 27 const { default: WinampElement } = await import( 26 28 "~/facets/themes/winamp/winamp/element.js"
src/images/icons/windows_98/imagwmf-1.png

This is a binary file and will not be displayed.

src/images/icons/windows_98/wia_img_color_sound-0.png

This is a binary file and will not be displayed.