226 lines
4.9 KiB
JavaScript
226 lines
4.9 KiB
JavaScript
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
|
|
}
|
|
} |