import { createEffect, createMemo, createSignal, on, onMount, Show } from 'solid-js' import { PlayerHeader } from './PlayerHeader' import { PlayerPlaylist } from './PlayerPlaylist' import styles from './AudioPlayer.module.scss' import { MediaItem } from '../../../pages/types' type Props = { media: MediaItem[] articleSlug?: string body?: string editorMode?: boolean onMediaItemFieldChange?: (index: number, field: keyof MediaItem, value: string) => void onChangeMediaIndex?: (direction: 'up' | 'down', index) => void } const getFormattedTime = (point) => new Date(point * 1000).toISOString().slice(14, -5) export const AudioPlayer = (props: Props) => { const audioRef: { current: HTMLAudioElement } = { current: null } const gainNodeRef: { current: GainNode } = { current: null } const progressRef: { current: HTMLDivElement } = { current: null } const audioContextRef: { current: AudioContext } = { current: null } const mouseDownRef: { current: boolean } = { current: false } const [currentTrackDuration, setCurrentTrackDuration] = createSignal(0) const [currentTime, setCurrentTime] = createSignal(0) const [currentTrackIndex, setCurrentTrackIndex] = createSignal(0) const [isPlaying, setIsPlaying] = createSignal(false) const currentTack = createMemo(() => props.media[currentTrackIndex()]) createEffect( on( () => currentTrackIndex(), () => { setCurrentTrackDuration(0) }, { defer: true } ) ) const handlePlayMedia = async (trackIndex: number) => { setIsPlaying(!isPlaying() || trackIndex !== currentTrackIndex()) setCurrentTrackIndex(trackIndex) if (audioContextRef.current.state === 'suspended') { await audioContextRef.current.resume() } if (isPlaying()) { await audioRef.current.play() } else { audioRef.current.pause() } } const handleVolumeChange = (volume: number) => { gainNodeRef.current.gain.value = volume } const handleAudioEnd = () => { if (currentTrackIndex() < props.media.length - 1) { playNextTrack() return } audioRef.current.currentTime = 0 setIsPlaying(false) setCurrentTrackIndex(0) } const handleAudioTimeUpdate = () => { setCurrentTime(audioRef.current.currentTime) } onMount(() => { audioContextRef.current = new AudioContext() gainNodeRef.current = audioContextRef.current.createGain() const track = audioContextRef.current.createMediaElementSource(audioRef.current) track.connect(gainNodeRef.current).connect(audioContextRef.current.destination) }) const playPrevTrack = () => { let newCurrentTrackIndex = currentTrackIndex() - 1 if (newCurrentTrackIndex < 0) { newCurrentTrackIndex = 0 } setCurrentTrackIndex(newCurrentTrackIndex) } const playNextTrack = () => { let newCurrentTrackIndex = currentTrackIndex() + 1 if (newCurrentTrackIndex > props.media.length - 1) { newCurrentTrackIndex = props.media.length - 1 } setCurrentTrackIndex(newCurrentTrackIndex) } const handleMediaItemFieldChange = (index: number, field: keyof MediaItem, value) => { props.onMediaItemFieldChange(index, field, value) } const scrub = (event) => { audioRef.current.currentTime = (event.offsetX / progressRef.current.offsetWidth) * currentTrackDuration() } return (
handlePlayMedia(currentTrackIndex())} playPrevTrack={playPrevTrack} playNextTrack={playNextTrack} onVolumeChange={handleVolumeChange} isPlaying={isPlaying()} currentTrack={currentTack()} />
(progressRef.current = el)} onClick={(e) => scrub(e)} onMouseMove={(e) => mouseDownRef.current && scrub(e)} onMouseDown={() => (mouseDownRef.current = true)} onMouseUp={() => (mouseDownRef.current = false)} >
{getFormattedTime(currentTime())} 0}> {getFormattedTime(currentTrackDuration())}
props.onChangeMediaIndex(direction, index)} isPlaying={isPlaying()} media={props.media} currentTrackIndex={currentTrackIndex()} articleSlug={props.articleSlug} body={props.body} onMediaItemFieldChange={handleMediaItemFieldChange} />
) }