const view = window.view ??= {} const app = window.app ??= {} import * as mode from "./panels.js" import * as queue from "./queue.js" import * as entry from "../entry.js" export const states = { EMPTY: 0, PLAYING: 1, PAUSED: 2 } export const Init = () => { app.player = { currentTime: 0 } } export const View = async () => { view.player = { cover: document.getElementById('track-cover'), title: document.getElementById('track-title'), artist: document.getElementById('track-artist'), timestamp: document.getElementById('timestamp'), trackLength: document.getElementById('track-length'), progress: document.getElementById('playback-progress') } if(app.player.current) { Switch(await Load(app.player.current, app.player.currentTime), app.player.current) } } export const Open = () => { mode.Display('player') } export const TogglePausePlay = () => { if(State() === states.PLAYING) { Pause() } else { Play() } } export const OnKeydown = (event) => { switch(event.key) { case 'ArrowUp': navigator.volumeManager?.requestUp() break case 'ArrowDown': navigator.volumeManager?.requestDown() break case ' ': case 'Enter': if(State() === states.PLAYING) { Pause() } else { Play() } break case ']': case 'SoftRight': Next() break default: return true } } export const Load = async (eTrack, fromTime) => { let f = await device.Open(eTrack.handle) let u = URL.createObjectURL(f) // if(fromTime) { // u = new URL(u) // u.hash = '#t=' + fromTime // } let a = new Audio(u) if(fromTime) { a.currentTime = fromTime } return a } export const Switch = (aTrack, eTrack) => { if(State() === states.PLAYING) { view.playback.pause() } view.playback = aTrack app.player.current = eTrack attach() Render() } export const attach = () => { let a = view.playback a.addEventListener('playing', onStart) a.addEventListener('timeupdate', onTimeUpdate) a.addEventListener('ended', onEnded) storeTime() } export const onStart = () => { storeTime() renderDuration() onTimeUpdate() } export const onTimeUpdate = () => { let s = parseInt(view.playback.currentTime) if(s > app.player.currentTime) { storeTime() renderTimestamp() } renderProgress() } export const storeTime = () => { app.player.currentTime = view.playback ? parseInt(view.playback.currentTime) : 0 } export const formatTime = currentTime => { let minutes = Math.floor(currentTime / 60).toString() let seconds = Math.floor(currentTime % 60).toString() return minutes.padStart(2, '0') + ':' + seconds.padStart(2, '0') } export const onEnded = () => { view.playback = null Next() } /* Rendering */ export const Render = async () => { marqueeableText(view.player.title, app.player.current.name) marqueeableText(view.player.artist, app.player.current.artist) renderCover() renderTimestamp() renderDuration() } export const marqueeableText = (eElement, sName) => { while(eElement.firstChild) { eElement.lastChild.remove() } eElement.classList = [] let s = textContainer(eElement, sName) if(s.offsetWidth > eElement.offsetWidth) { eElement.classList = [ 'marquee' ] textContainer(eElement, sName) } } export const textContainer = (eElement, sText) => { let s = document.createElement('span') s.innerText = sText eElement.appendChild(s) return s } export const renderProgress = () => { let t = view.playback.currentTime / view.playback.duration t = parseInt(t * 100) view.player.progress.style.strokeDasharray = `${t}, ${100 - t}` } export const renderTimestamp = (iTimestamp = app.player.currentTime) => { view.player.timestamp.innerText = formatTime(iTimestamp) } export const renderDuration = (iDuration = view.playback.duration) => { view.player.trackLength.innerText = formatTime(iDuration) } export const renderCover = async () => { view.player.cover.src = app.player.current.coverId ? URL.createObjectURL(await entry.GetCover(app.player.current)): '' } /* Actions */ export const Play = async () => { if(view.playback) { view.playback.play() return } await Next() } export const Next = async () => { let e = queue.Shift() if(e) { Switch(await Load(e), e) view.playback.play() } } export const Pause = () => { view.playback.pause() } export const State = () => { if(view.playback == null) { return states.EMPTY } else { return view.playback.paused ? states.PAUSED : states.PLAYING } }