Handle episode metadata (#18)

This commit is contained in:
Ollie Taylor
2023-02-24 15:43:55 +00:00
committed by GitHub
7 changed files with 98 additions and 79 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'svelte-podcast': minor
---
Adds the ability to bind episode metadata to the audio store
+20 -42
View File
@@ -2,44 +2,50 @@
import { import {
audio_autoplay, audio_autoplay,
audio_current_time, audio_current_time,
audio_duration,
audio_element, audio_element,
audio_metadata, audio_ended,
audio_loading,
audio_muted, audio_muted,
audio_paused,
audio_playback_rate, audio_playback_rate,
audio_src, audio_src,
audio_start_at, audio_start_at,
audio_volume, audio_volume,
} from '$lib/context/audio-internals'; } from '$lib/context/audio-internals';
import type { PlayerElement } from '$lib/types/types'; import type { PlayerElement } from '$lib/types/types';
import { isBoolean, isNumber } from '$pkg/type-guards';
// readonly values
let element: PlayerElement; let element: PlayerElement;
let currentTime = $audio_start_at; let current_time = $audio_start_at;
let volume = $audio_volume;
let duration = 0; let duration = 0;
let ended = true; let ended = true;
let muted = false;
let paused = false; let paused = false;
// handler values
let volume = $audio_volume;
let muted = false;
let playbackRate = 1; let playbackRate = 1;
// Update stores based on bindings // readonly - stores will update when bindings change
$: audio_element.set(element); $: audio_element.set(element);
$: audio_current_time.set(currentTime); $: audio_current_time.set(current_time);
$: audio_metadata.update((x) => ({ ...x, duration, ended, paused })); $: audio_duration.set(duration);
$: audio_ended.set(ended);
$: audio_paused.set(paused);
// Update bindings based on stores // handlers - when store value changes, the binding will update the audio element
$: volume = $audio_volume; $: volume = $audio_volume;
$: muted = $audio_muted; $: muted = $audio_muted;
$: playbackRate = $audio_playback_rate; $: playbackRate = $audio_playback_rate;
$: currentTime = $audio_start_at; $: current_time = $audio_start_at;
</script> </script>
<audio <audio
bind:this={element} bind:this={element}
src={$audio_src} src={$audio_src}
autoplay={$audio_autoplay} autoplay={$audio_autoplay}
bind:currentTime bind:currentTime={current_time}
bind:muted bind:muted
bind:duration bind:duration
bind:paused bind:paused
@@ -62,34 +68,6 @@
// TODO: reset player state // TODO: reset player state
// console.log('emptied', e); // console.log('emptied', e);
}} }}
on:loadeddata={() => { on:loadeddata={() => audio_loading.set(false)}
audio_metadata.update((m) => ({ ...m, loading: false })); on:loadstart={() => audio_loading.set(true)}
}}
on:loadstart={() => {
audio_metadata.update((m) => ({ ...m, loading: true }));
}}
on:playing={(e) => {
// @ts-expect-error event is not fully typed
const is_paused = e.target.paused;
if (!isBoolean(is_paused)) return;
audio_metadata.update((m) => ({ ...m, paused: is_paused }));
}}
on:pause={(e) => {
// @ts-expect-error event is not fully typed
const is_paused = e.target.paused;
if (!isBoolean(is_paused)) return;
audio_metadata.update((m) => ({ ...m, paused: is_paused }));
}}
on:ratechange={(e) => {
// @ts-expect-error event is not fully typed
const playbackRate = e.target.playbackRate;
if (!isNumber(playbackRate)) return;
audio_metadata.update((m) => ({ ...m, playbackRate }));
}}
on:durationchange={(e) => {
// @ts-expect-error event is not fully typed
const duration = e.target?.duration;
if (!isNumber(duration)) return;
audio_metadata.update((m) => ({ ...m, duration }));
}}
/> />
+10 -22
View File
@@ -1,30 +1,18 @@
import type { PlayerElement } from '$lib/types/types'; import type { PlayerElement } from '$lib/types/types';
import { writable } from 'svelte/store'; import { writable } from 'svelte/store';
export const audio_element = writable<PlayerElement>(); // readonly - stores will update when bindings change
export const audio_start_at = writable<number>(0);
export const audio_autoplay = writable<boolean>(false);
export const audio_current_time = writable<number>(0); export const audio_current_time = writable<number>(0);
export const audio_element = writable<PlayerElement>();
export const audio_duration = writable<number>(0);
export const audio_ended = writable<boolean>(false);
export const audio_loading = writable<boolean>(false);
export const audio_paused = writable<boolean>(true);
// handlers - when store value changes, the binding will update the audio element
export const audio_start_at = writable<number>(0);
export const audio_autoplay = writable<boolean>(false);
export const audio_muted = writable<boolean>(false); export const audio_muted = writable<boolean>(false);
export const audio_playback_rate = writable<number>(1); export const audio_playback_rate = writable<number>(1);
export const audio_src = writable<string>(''); export const audio_src = writable<string>('');
export const audio_volume = writable<number>(1); export const audio_volume = writable<number>(1);
export type PlayerMetadata = {
duration: number;
// muted: boolean;
paused: boolean;
// playbackRate: number;
loading: boolean;
ended: boolean;
};
export const audio_metadata = writable<PlayerMetadata>({
duration: 0,
ended: false,
loading: false,
paused: true,
// muted: false,
// playbackRate: 1,
});
+8
View File
@@ -0,0 +1,8 @@
import { writable } from 'svelte/store';
export type AudioMetadata = {
title: string;
artwork: string;
};
export const audio_metadata = writable<AudioMetadata | null>(null);
+42 -10
View File
@@ -1,14 +1,18 @@
import { import {
audio_autoplay, audio_autoplay,
audio_current_time, audio_current_time,
audio_duration,
audio_element, audio_element,
audio_metadata, audio_ended,
audio_loading,
audio_muted, audio_muted,
audio_paused,
audio_playback_rate, audio_playback_rate,
audio_src, audio_src,
audio_start_at, audio_start_at,
audio_volume, audio_volume,
} from '$lib/context/audio-internals'; } from '$lib/context/audio-internals';
import { audio_metadata, type AudioMetadata } from '$lib/context/audio-metadata';
import { secondsToTimestamp } from '$lib/utility/seconds-to-timestamp'; import { secondsToTimestamp } from '$lib/utility/seconds-to-timestamp';
import { info, warn } from '$pkg/log'; import { info, warn } from '$pkg/log';
import clamp from 'just-clamp'; import clamp from 'just-clamp';
@@ -16,24 +20,47 @@ import { derived } from 'svelte/store';
const audio_state = derived( const audio_state = derived(
[ [
audio_metadata,
audio_autoplay,
audio_current_time, audio_current_time,
audio_duration,
audio_ended,
audio_loading,
audio_paused,
audio_start_at,
audio_autoplay,
audio_muted, audio_muted,
audio_playback_rate, audio_playback_rate,
audio_src, audio_src,
audio_volume, audio_volume,
audio_metadata,
], ],
([$metadata, $autoplay, $current_time, $muted, $playback_rate, $src, $volume]) => { ([
$current_time,
$duration,
$ended,
$loading,
$paused,
$start_at,
$autoplay,
$muted,
$playback_rate,
$src,
$volume,
$metadata,
]) => {
return { return {
...$metadata,
autoplay: $autoplay,
current_time: $current_time, current_time: $current_time,
timestamp: secondsToTimestamp($current_time), duration: $duration,
ended: $ended,
loading: $loading,
paused: $paused,
start_at: $start_at,
autoplay: $autoplay,
muted: $muted, muted: $muted,
playback_rate: $playback_rate, playback_rate: $playback_rate,
src: $src, src: $src,
volume: $volume, volume: $volume,
metadata: $metadata,
timestamp: secondsToTimestamp($current_time),
}; };
}, },
); );
@@ -80,26 +107,31 @@ function unmute(type: HandleType = 'set') {
} }
} }
type LoadOptions = { export type AudioLoadData = AudioMetadata & { src: string };
export type AudioLoadOptions = {
autoplay: boolean; autoplay: boolean;
start_at?: number; start_at?: number;
}; };
function load(src: string, opts: LoadOptions) { const load = (data: AudioLoadData, opts: AudioLoadOptions) => {
const { src, ...metadata } = data;
info('load: ', src); info('load: ', src);
audio_autoplay.set(opts.autoplay); audio_autoplay.set(opts.autoplay);
audio_volume.set(1); audio_volume.set(1);
audio_src.set(src); audio_src.set(src);
audio_metadata.set(metadata);
audio_start_at.set(opts.start_at || 0); audio_start_at.set(opts.start_at || 0);
// opts.current_time ? seek(opts.current_time) : null; // opts.current_time ? seek(opts.current_time) : null;
//opts.autoplay && play(); //opts.autoplay && play();
} };
function unload() { function unload() {
info('unload: '); info('unload: ');
pause(); pause();
audio_autoplay.set(false); audio_autoplay.set(false);
audio_volume.set(1); audio_volume.set(1);
audio_src.set(''); audio_src.set('');
audio_metadata.set(null);
audio_start_at.set(0); audio_start_at.set(0);
} }
+1 -1
View File
@@ -1,2 +1,2 @@
export { default as AudioLoader } from '$lib/components/audio-loader.svelte'; export { default as AudioLoader } from '$lib/components/audio-loader.svelte';
export { audio } from '$lib/context/audio'; export { audio, type AudioLoadData, type AudioLoadOptions } from '$lib/context/audio';
+12 -4
View File
@@ -1,10 +1,18 @@
<script lang="ts"> <script lang="ts">
import { audio } from '$lib'; import { audio, type AudioLoadData } from '$lib';
const sources = { const sources = {
syntax: '/example-syntax.mp3', syntax: {
knomii: '/example-knomii.mp3', src: `/example-syntax.mp3`,
} as const; title: `Supper Club × Rich Harris, Author of Svelte`,
artwork: `https://ssl-static.libsyn.com/p/assets/b/3/c/d/b3cdf28da11ad39fe5bbc093207a2619/Syntax_-_499.jpg`,
},
knomii: {
src: `/example-knomii.mp3`,
title: `Empowerment starts with letting go of control`,
artwork: `https://ssl-static.libsyn.com/p/assets/f/a/8/d/fa8d56d5226884335f2e77a3093c12a1/ep-6.png`,
},
} satisfies Record<string, AudioLoadData>;
let current_time = 0; let current_time = 0;
$: current_time = $audio.current_time; $: current_time = $audio.current_time;