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.

chore: reorganise

+90 -77
+1 -1
deno.jsonc
··· 9 9 10 10 // Source 11 11 "@common/": "./src/common/", 12 - "@elements/": "./src/elements/", 12 + "@component/": "./src/component/", 13 13 }, 14 14 "tasks": { 15 15 "build": "deno task lume",
+45
src/component/engine/audio/types.d.ts
··· 1 + import { Signal } from "@common/signal.d.ts"; 2 + 3 + export interface Actions { 4 + pause: (_: { audioId: string }) => void; 5 + play: (_: { audioId: string; volume?: number }) => void; 6 + reload: (_: { audioId: string; play: boolean; progress?: number }) => void; 7 + seek: (_: { audioId: string; percentage: number }) => void; 8 + yield: ( 9 + _: { audio: Audio[]; play?: { audioId: string; volume?: number } }, 10 + ) => void; 11 + } 12 + 13 + export interface Audio { 14 + id: string; 15 + isPreload: boolean; 16 + mimeType?: string; 17 + progress?: number; 18 + url: string; 19 + } 20 + 21 + export interface AudioState { 22 + duration: number; 23 + id: string; 24 + hasEnded: boolean; 25 + loadingState: 26 + | "initialisation" 27 + | "loading" 28 + | "loaded" 29 + | { 30 + error: { code: number }; 31 + }; 32 + isPlaying: boolean; 33 + isPreload: boolean; 34 + mimeType?: string; 35 + progress: number; 36 + url: string; 37 + } 38 + 39 + export interface Signals { 40 + isPlaying: Signal<boolean>; 41 + items: Signal<Audio[]>; 42 + volume: Signal<number>; 43 + } 44 + 45 + export type State = Signals;
+1 -13
src/elements/constituent/blur/browser-list/index.js src/component/constituent/blur/browser-list/element.js
··· 4 4 /** 5 5 * @import {RenderArg} from "@common/element.d.ts" 6 6 * @import {State} from "./types.d.ts" 7 - * @import {Track} from "@elements/core/types.d.ts" 7 + * @import {Track} from "@component/core/types.d.ts" 8 8 */ 9 9 10 10 //////////////////////////////////////////// ··· 20 20 return { 21 21 tracks: this.tracks, 22 22 }; 23 - } 24 - 25 - // LIFECYCLE 26 - 27 - /** 28 - * @override 29 - * 30 - * TODO: Remove, just an example. 31 - */ 32 - connectedCallback() { 33 - super.connectedCallback(); 34 - this.effect(() => {}); 35 23 } 36 24 37 25 // RENDER
src/elements/constituent/blur/browser-list/types.d.ts src/component/constituent/blur/browser-list/types.d.ts
src/elements/core/types.d.ts src/component/core/types.d.ts
+42 -29
src/elements/engine/audio/index.js src/component/engine/audio/element.js
··· 1 1 import DiffuseElement from "@common/element.js"; 2 - import { effect, signal } from "@common/signal.js"; 2 + import { signal } from "@common/signal.js"; 3 3 4 4 /** 5 - * @import {Audio, AudioState, State} from "./types.d.ts" 5 + * @import {Actions, Audio, AudioState, Signals, State} from "./types.d.ts" 6 6 * @import {RenderArg} from "@common/element.d.ts" 7 - * @import {Signal} from "@common/signal.d.ts" 8 7 */ 9 8 10 9 //////////////////////////////////////////// 11 10 // CONSTANTS 12 11 //////////////////////////////////////////// 13 - const SILENT_MP3 = 12 + const _SILENT_MP3 = 14 13 "data:audio/mp3;base64,SUQzBAAAAAAAI1RTU0UAAAAPAAADTGF2ZjU2LjM2LjEwMAAAAAAAAAAAAAAA//OEAAAAAAAAAAAAAAAAAAAAAAAASW5mbwAAAA8AAAAEAAABIADAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV6urq6urq6urq6urq6urq6urq6urq6urq6v////////////////////////////////8AAAAATGF2YzU2LjQxAAAAAAAAAAAAAAAAJAAAAAAAAAAAASDs90hvAAAAAAAAAAAAAAAAAAAA//MUZAAAAAGkAAAAAAAAA0gAAAAATEFN//MUZAMAAAGkAAAAAAAAA0gAAAAARTMu//MUZAYAAAGkAAAAAAAAA0gAAAAAOTku//MUZAkAAAGkAAAAAAAAA0gAAAAANVVV"; 15 14 16 15 //////////////////////////////////////////// 17 16 // ELEMENT 18 17 //////////////////////////////////////////// 19 18 19 + /** 20 + * @implements {Actions} 21 + * @implements {Signals} 22 + */ 20 23 class AudioEngine extends DiffuseElement { 21 24 static observedAttributes = ["is-playing", "volume"]; 22 25 ··· 30 33 31 34 // SIGNALS 32 35 33 - defaultVolume = signal(0.5); 36 + volume = signal(0.5); 34 37 isPlaying = signal(false); 35 38 items = signal(/** @type {Audio[]} */ ([])); 36 39 37 40 // STATE 38 41 42 + /** 43 + * @type {State} 44 + */ 39 45 get state() { 40 46 return { 41 47 isPlaying: this.isPlaying, 42 48 items: this.items, 43 - volume: { default: this.defaultVolume() }, 49 + volume: this.volume, 44 50 }; 45 51 } 46 52 53 + // LIFECYCLE 54 + 55 + /** 56 + * @override 57 + */ 58 + connectedCallback() { 59 + super.connectedCallback(); 60 + 61 + this.effect(() => { 62 + // NOTE: Support different volume levels for audio elements? 63 + 64 + Array.from(this.querySelectorAll("de-audio-item audio")).forEach( 65 + (node) => { 66 + const audio = /** @type {HTMLAudioElement} */ (node); 67 + if (audio.hasAttribute("preload")) return; 68 + audio.volume = this.volume(); 69 + }, 70 + ); 71 + }); 72 + } 73 + 47 74 // ACTIONS 48 75 49 76 /** 50 - * @param {{ audioId: string }} _ 77 + * @type {Actions["pause"]} 51 78 */ 52 79 pause({ audioId }) { 53 80 this.withAudioNode(audioId, (audio) => audio.pause()); 54 81 } 55 82 56 83 /** 57 - * @param {{ audioId: string; volume?: number }} _ 84 + * @type {Actions["play"]} 58 85 */ 59 86 play({ audioId, volume }) { 60 87 this.withAudioNode(audioId, (audio, item) => { 61 - audio.volume = volume ?? this.state.volume.default; 88 + audio.volume = volume ?? this.state.volume(); 62 89 audio.muted = false; 63 90 64 91 if (audio.readyState === 0) audio.load(); ··· 80 107 } 81 108 82 109 /** 83 - * @param {{ audioId: string; play: boolean; progress?: number }} args 110 + * @type {Actions["reload"]} 84 111 */ 85 112 reload(args) { 86 113 this.withAudioNode(args.audioId, (audio, item) => { ··· 102 129 } 103 130 104 131 /** 105 - * @param {{ audioId: string; percentage: number }} _ 132 + * @type {Actions["seek"]} 106 133 */ 107 134 seek({ audioId, percentage }) { 108 135 this.withAudioNode(audioId, (audio) => { ··· 113 140 } 114 141 115 142 /** 116 - * @param {{ audioId?: string; volume: number }} args 117 - */ 118 - volume(args) { 119 - // TODO: 120 - // if (!args.audioId) update({ volume: { default: args.volume } }); 121 - 122 - Array.from(this.querySelectorAll("de-audio-item audio")).forEach((node) => { 123 - const audio = /** @type {HTMLAudioElement} */ (node); 124 - if (audio.hasAttribute("preload")) return; 125 - if (args.audioId === undefined || args.audioId === audio.id) { 126 - audio.volume = args.volume; 127 - } 128 - }); 129 - } 130 - 131 - /** 132 - * @param {{ audio: Audio[]; play?: { audioId: string; volume?: number } }} args 143 + * @type {Actions["yield"]} 133 144 */ 134 145 yield(args) { 135 146 this.items(args.audio); ··· 202 213 // ITEM ELEMENT 203 214 //////////////////////////////////////////// 204 215 205 - export class AudioEngineItem extends HTMLElement { 216 + class AudioEngineItem extends HTMLElement { 206 217 /** 207 218 * @type {AudioState} 208 219 */ ··· 373 384 initiateLoading(event); 374 385 } 375 386 } 387 + 388 + export { AudioEngineItem }; 376 389 377 390 //////////////////////////////////////////// 378 391 // 🛠️
-33
src/elements/engine/audio/types.d.ts
··· 1 - import { Signal } from "@common/signal.d.ts"; 2 - 3 - export interface State { 4 - isPlaying: boolean; 5 - items: Signal<Audio[]>; 6 - volume: { default: number }; 7 - } 8 - 9 - export interface Audio { 10 - id: string; 11 - isPreload: boolean; 12 - mimeType?: string; 13 - progress?: number; 14 - url: string; 15 - } 16 - 17 - export interface AudioState { 18 - duration: number; 19 - id: string; 20 - hasEnded: boolean; 21 - loadingState: 22 - | "initialisation" 23 - | "loading" 24 - | "loaded" 25 - | { 26 - error: { code: number }; 27 - }; 28 - isPlaying: boolean; 29 - isPreload: boolean; 30 - mimeType?: string; 31 - progress: number; 32 - url: string; 33 - }
+1 -1
src/theme/blur/index.vto
··· 4 4 <link rel="stylesheet" href="../../styles/theme/blur/index.css" /> 5 5 6 6 <script type="module"> 7 - import "../../elements/engine/audio/index.js"; 7 + import "../../component/engine/audio/element.js"; 8 8 </script> 9 9 </head> 10 10 <body>