import { createEffect, createSignal, onMount, Show } from 'solid-js' import { PlayerHeader } from './PlayerHeader' import { PlayerPlaylist } from './PlayerPlaylist' import styles from './AudioPlayer.module.scss' export type MediaItem = { id?: number body: string pic: string title: string url: string isCurrent: boolean isPlaying: boolean } const prepareMedia = (media: MediaItem[]) => media.map((item, index) => ({ ...item, id: index, isCurrent: false, isPlaying: false })) const progressUpdate = (audioRef, progressFilledRef, duration) => { progressFilledRef.style.width = `${(audioRef.currentTime / duration) * 100 || 0}%` } const scrub = (event, progressRef, duration, audioRef) => { audioRef.currentTime = (event.offsetX / progressRef.offsetWidth) * duration } const getFormattedTime = (point) => new Date(point * 1000).toISOString().slice(14, -5) export default (props: { media: MediaItem[]; articleSlug: string; body: string }) => { let audioRef: HTMLAudioElement let progressRef: HTMLDivElement let progressFilledRef: HTMLDivElement const [audioContext, setAudioContext] = createSignal() const [gainNode, setGainNode] = createSignal() const [tracks, setTracks] = createSignal(prepareMedia(props.media)) const [duration, setDuration] = createSignal(0) const [currentTimeContent, setCurrentTimeContent] = createSignal('00:00') const [currentDurationContent, setCurrentDurationContent] = createSignal('00:00') const [mousedown, setMousedown] = createSignal(false) const getCurrentTrack = () => tracks().find((track) => track.isCurrent) || (() => { setTracks( tracks().map((track, index) => ({ ...track, isCurrent: index === 0 })) ) return tracks()[0] })() createEffect(() => { if (audioRef.src !== getCurrentTrack().url) { audioRef.src = getCurrentTrack().url audioRef.load() } }) createEffect(() => { if (getCurrentTrack() && duration()) { setCurrentDurationContent(getFormattedTime(duration())) } }) const playMedia = async (m: MediaItem) => { setTracks( tracks().map((track) => ({ ...track, isCurrent: track.id === m.id ? true : false, isPlaying: track.id === m.id ? !track.isPlaying : false })) ) progressUpdate(audioRef, progressFilledRef, duration()) if (audioContext().state === 'suspended') audioContext().resume() if (getCurrentTrack().isPlaying) { await audioRef.play() } else { audioRef.pause() } } const setTimes = () => { setCurrentTimeContent(getFormattedTime(audioRef.currentTime)) } const handleAudioEnd = () => { progressFilledRef.style.width = '0%' audioRef.currentTime = 0 } const handleAudioTimeUpdate = () => { progressUpdate(audioRef, progressFilledRef, duration()) setTimes() } onMount(() => { setAudioContext(new AudioContext()) setGainNode(audioContext().createGain()) setTimes() const track = audioContext().createMediaElementSource(audioRef) track.connect(gainNode()).connect(audioContext().destination) }) const playPrevTrack = () => { const { id } = getCurrentTrack() const currIndex = tracks().findIndex((track) => track.id === id) const getUpdatedStatus = (trackId) => currIndex === 0 ? trackId === tracks()[tracks().length - 1].id : trackId === tracks()[currIndex - 1].id setTracks( tracks().map((track) => ({ ...track, isCurrent: getUpdatedStatus(track.id), isPlaying: getUpdatedStatus(track.id) })) ) } const playNextTrack = () => { const { id } = getCurrentTrack() const currIndex = tracks().findIndex((track) => track.id === id) const getUpdatedStatus = (trackId) => currIndex === tracks().length - 1 ? trackId === tracks()[0].id : trackId === tracks()[currIndex + 1].id setTracks( tracks().map((track) => ({ ...track, isCurrent: getUpdatedStatus(track.id), isPlaying: getUpdatedStatus(track.id) })) ) } const handleOnAudioMetadataLoad = ({ target }) => { setDuration(target.duration) } return (
playMedia(getCurrentTrack())} getCurrentTrack={getCurrentTrack} playPrevTrack={playPrevTrack} playNextTrack={playNextTrack} gainNode={gainNode()} />
scrub(e, progressRef, duration(), audioRef)} onMouseMove={(e) => mousedown() && scrub(e, progressRef, duration(), audioRef)} onMouseDown={() => setMousedown(true)} onMouseUp={() => setMousedown(false)} >
{currentTimeContent()} {currentDurationContent()}
) }