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 {Signal, signal, WritableSignal} from '@angular/core';
enum STATE {
Playing,
@@ -7,46 +8,71 @@ enum STATE {
}
export class PlaylistItem {
protected element: HTMLAudioElement;
protected element: HTMLAudioElement | null = null;
protected state: STATE = STATE.Stopped;
protected initialised: boolean = false;
public currentTime: WritableSignal<number> = signal(0);
public duration: WritableSignal<number> = signal(0);
constructor(
public track: Track,
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) {
const element = document.createElement('audio');
public initialise(): void {
if (this.initialised) return;
this.initialised = true;
this.element = document.createElement('audio');
const source = document.createElement('source');
source.src = filename;
source.type = type;
element.appendChild(source);
return element;
source.src = this.track.file.filepath;
source.type = this.track.getMimeType();
this.element.appendChild(source);
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() {
this.initialise();
if (this.state === STATE.Stopped || this.state === STATE.Paused) {
await this.element.play();
await this.element?.play();
this.state = STATE.Playing;
return;
}
}
public async stop() {
this.element.currentTime = 0;
this.element.pause();
this.initialise();
this.element?.pause();
this.setTime();
this.state = STATE.Stopped;
}
protected setTime() {
if (this.element) {
this.element.currentTime = 0;
}
}
public async pause() {
this.initialise();
if (this.state === STATE.Paused) {
return this.play();
}
if (this.state === STATE.Playing) {
this.element.pause();
this.element?.pause();
this.state = STATE.Paused;
return;
}
+2
View File
@@ -16,3 +16,5 @@ Home
{{ item.track.file.filename }}
</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 {Signal} from '@angular/core';
import {computed, Signal} from '@angular/core';
import {IndexedArray} from '../lib';
export class AudioService {
@@ -8,6 +8,12 @@ export class AudioService {
public list: Signal<PlaylistItem[]> = this.playlist.playlist;
public current: Signal<PlaylistItem|null> = this.playlist.current;
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() {
this.context = new AudioContext();