This commit is contained in:
2026-02-28 10:41:38 +00:00
parent 3116bfb681
commit 1e7e8dfb91
3 changed files with 48 additions and 14 deletions
+39 -13
View File
@@ -1,4 +1,5 @@
import {Track} from './Track'; import {Track} from './Track';
import {Signal, signal, WritableSignal} from '@angular/core';
enum STATE { enum STATE {
Playing, Playing,
@@ -7,46 +8,71 @@ enum STATE {
} }
export class PlaylistItem { export class PlaylistItem {
protected element: HTMLAudioElement; protected element: HTMLAudioElement | null = null;
protected state: STATE = STATE.Stopped; protected state: STATE = STATE.Stopped;
protected initialised: boolean = false;
public currentTime: WritableSignal<number> = signal(0);
public duration: WritableSignal<number> = signal(0);
constructor( constructor(
public track: Track, public track: Track,
protected context: AudioContext protected context: AudioContext
) { ) {
this.element = this.createElement(this.track.file.filepath, this.track.getMimeType())
context.createMediaElementSource(this.element).connect(context.destination);
} }
private createElement(filename: string, type: string) { public initialise(): void {
const element = document.createElement('audio'); if (this.initialised) return;
this.initialised = true;
this.element = document.createElement('audio');
const source = document.createElement('source'); const source = document.createElement('source');
source.src = filename; source.src = this.track.file.filepath;
source.type = type; source.type = this.track.getMimeType();
element.appendChild(source); this.element.appendChild(source);
return element; for (const eventType of ['progress', 'durationchange', 'ended', 'loadeddata', 'pause', 'play', 'seeking', 'seeked', 'stalled', 'volumechange']) {
this.element.addEventListener(eventType, (event) => {
console.log(eventType, event);
});
}
this.element.addEventListener('timeupdate', () => {
this.currentTime.set(this.element?.currentTime || 0);
})
this.element.addEventListener('durationchange', () => {
this.duration.set(this.element?.duration || 0);
})
this.context.createMediaElementSource(this.element).connect(this.context.destination);
} }
public async play() { public async play() {
this.initialise();
if (this.state === STATE.Stopped || this.state === STATE.Paused) { if (this.state === STATE.Stopped || this.state === STATE.Paused) {
await this.element.play(); await this.element?.play();
this.state = STATE.Playing; this.state = STATE.Playing;
return; return;
} }
} }
public async stop() { public async stop() {
this.element.currentTime = 0; this.initialise();
this.element.pause(); this.element?.pause();
this.setTime();
this.state = STATE.Stopped; this.state = STATE.Stopped;
} }
protected setTime() {
if (this.element) {
this.element.currentTime = 0;
}
}
public async pause() { public async pause() {
this.initialise();
if (this.state === STATE.Paused) { if (this.state === STATE.Paused) {
return this.play(); return this.play();
} }
if (this.state === STATE.Playing) { if (this.state === STATE.Playing) {
this.element.pause(); this.element?.pause();
this.state = STATE.Paused; this.state = STATE.Paused;
return; return;
} }
+2
View File
@@ -16,3 +16,5 @@ Home
{{ item.track.file.filename }} {{ item.track.file.filename }}
</div> </div>
} }
<div>{{ audio.current()?.currentTime()}} / {{audio.current()?.duration()}} -> {{ audio.progress()}} %</div>
+7 -1
View File
@@ -1,5 +1,5 @@
import {FileSystemFile, Track, TrackMeta, PlaylistItem} from '../models'; import {FileSystemFile, Track, TrackMeta, PlaylistItem} from '../models';
import {Signal} from '@angular/core'; import {computed, Signal} from '@angular/core';
import {IndexedArray} from '../lib'; import {IndexedArray} from '../lib';
export class AudioService { export class AudioService {
@@ -8,6 +8,12 @@ export class AudioService {
public list: Signal<PlaylistItem[]> = this.playlist.playlist; public list: Signal<PlaylistItem[]> = this.playlist.playlist;
public current: Signal<PlaylistItem|null> = this.playlist.current; public current: Signal<PlaylistItem|null> = this.playlist.current;
public index: Signal<number> = this.playlist.index; public index: Signal<number> = this.playlist.index;
public progress: Signal<number> = computed(() => {
const current = this.current();
if (!current) return 0;
if (current.duration() === 0) return 0;
return current.currentTime() / current.duration();
})
constructor() { constructor() {
this.context = new AudioContext(); this.context = new AudioContext();