mirror of
https://github.com/owntone/owntone-server.git
synced 2025-11-22 02:35:32 -05:00
[web] Improve the initialisation of stores
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<button :class="{ 'is-dark': playerStore.lyrics }" @click="toggle">
|
||||
<button :class="{ 'is-dark': playerStore.showLyrics }" @click="toggle">
|
||||
<mdicon
|
||||
class="icon"
|
||||
:name="icon"
|
||||
@@ -21,14 +21,14 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
icon() {
|
||||
return this.playerStore.lyrics
|
||||
return this.playerStore.showLyrics
|
||||
? 'script-text-play'
|
||||
: 'script-text-outline'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toggle() {
|
||||
this.playerStore.lyrics = !this.playerStore.lyrics
|
||||
this.playerStore.showLyrics = !this.playerStore.showLyrics
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
<template>
|
||||
<div class="lyrics is-overlay">
|
||||
<div v-for="(verse, index) in visibleLyrics" :key="index">
|
||||
<div
|
||||
class="lyrics is-overlay"
|
||||
@wheel.prevent="onScroll"
|
||||
@touchmove.prevent="onScroll"
|
||||
>
|
||||
<div v-for="(verse, index) in visibleVerses" :key="index">
|
||||
<div v-if="verse">
|
||||
<div v-if="index === 3" class="title is-5 my-5 lh-2">
|
||||
<div v-if="index === MIDDLE_POSITION" class="title is-5 my-5 lh-2">
|
||||
<span
|
||||
v-for="(word, wordIndex) in verse.words"
|
||||
:key="wordIndex"
|
||||
@@ -19,36 +23,40 @@
|
||||
|
||||
<script>
|
||||
import { usePlayerStore } from '@/stores/player'
|
||||
import { useQueueStore } from '@/stores/queue'
|
||||
|
||||
const VISIBLE_VERSES = 7
|
||||
const MIDDLE_POSITION = Math.floor(VISIBLE_VERSES / 2)
|
||||
|
||||
export default {
|
||||
name: 'PaneLyrics',
|
||||
setup() {
|
||||
return { playerStore: usePlayerStore(), queueStore: useQueueStore() }
|
||||
const VISIBLE_VERSES = 7
|
||||
const MIDDLE_POSITION = Math.floor(VISIBLE_VERSES / 2)
|
||||
const SCROLL_THRESHOLD = 10
|
||||
return {
|
||||
MIDDLE_POSITION,
|
||||
playerStore: usePlayerStore(),
|
||||
SCROLL_THRESHOLD,
|
||||
VISIBLE_VERSES
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
lastIndex: -1,
|
||||
time: 0,
|
||||
timerId: null,
|
||||
lastProgress: 0,
|
||||
lastUpdateTime: 0,
|
||||
lyrics: []
|
||||
lyrics: { synchronised: false, verses: [] },
|
||||
scrollIndex: 0,
|
||||
scrollDelta: 0,
|
||||
time: 0,
|
||||
timerId: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
verseIndex() {
|
||||
const currentTime = this.time
|
||||
const { lyrics } = this
|
||||
const { verses } = this.lyrics
|
||||
let start = 0
|
||||
let end = lyrics.length - 1
|
||||
let end = verses.length - 1
|
||||
while (start <= end) {
|
||||
const mid = Math.floor((start + end) / 2)
|
||||
const midTime = lyrics[mid].time
|
||||
const nextTime = lyrics[mid + 1]?.time
|
||||
const midTime = verses[mid].time
|
||||
const nextTime = verses[mid + 1]?.time
|
||||
if (midTime <= currentTime && (!nextTime || nextTime > currentTime)) {
|
||||
return mid
|
||||
} else if (midTime < currentTime) {
|
||||
@@ -59,13 +67,13 @@ export default {
|
||||
}
|
||||
return -1
|
||||
},
|
||||
visibleLyrics() {
|
||||
const current = this.verseIndex
|
||||
const total = this.lyrics.length
|
||||
return Array.from({ length: VISIBLE_VERSES }, (_, i) => {
|
||||
const index = current - MIDDLE_POSITION + i
|
||||
return index >= 0 && index < total ? this.lyrics[index] : null
|
||||
})
|
||||
visibleVerses() {
|
||||
const { verses, synchronised } = this.lyrics
|
||||
const index = synchronised ? this.verseIndex : this.scrollIndex
|
||||
return Array.from(
|
||||
{ length: this.VISIBLE_VERSES },
|
||||
(_, i) => verses[index - this.MIDDLE_POSITION + i] ?? null
|
||||
)
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@@ -78,7 +86,6 @@ export default {
|
||||
}
|
||||
},
|
||||
'playerStore.item_progress_ms'(progress) {
|
||||
this.lastProgress = progress
|
||||
this.lastUpdateTime = Date.now()
|
||||
if (!this.playerStore.isPlaying) {
|
||||
this.time = progress
|
||||
@@ -86,13 +93,10 @@ export default {
|
||||
},
|
||||
'playerStore.lyricsContent'() {
|
||||
this.lyrics = this.parseLyrics()
|
||||
},
|
||||
verseIndex(index) {
|
||||
this.lastIndex = index
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.lastProgress = this.playerStore.item_progress_ms
|
||||
this.playerStore.initialise()
|
||||
this.lastUpdateTime = Date.now()
|
||||
this.lyrics = this.parseLyrics()
|
||||
this.updateTime()
|
||||
@@ -101,11 +105,24 @@ export default {
|
||||
this.stopTimer()
|
||||
},
|
||||
methods: {
|
||||
onScroll(event) {
|
||||
if (this.verseIndex >= 0) {
|
||||
return
|
||||
}
|
||||
this.scrollDelta += event.deltaY
|
||||
if (Math.abs(this.scrollDelta) >= this.SCROLL_THRESHOLD) {
|
||||
const newIndex = this.scrollIndex + Math.sign(this.scrollDelta)
|
||||
if (newIndex >= -1 && newIndex < this.lyrics.verses.length) {
|
||||
this.scrollIndex = newIndex
|
||||
}
|
||||
this.scrollDelta = 0
|
||||
}
|
||||
},
|
||||
isWordHighlighted(word) {
|
||||
return this.time >= word.start && this.time < word.end
|
||||
},
|
||||
parseLyrics() {
|
||||
const verses = []
|
||||
const lyrics = { synchronised: false, verses: [] }
|
||||
const regex =
|
||||
/(?:\[(?<minutes>\d+):(?<seconds>\d+)(?:\.(?<hundredths>\d+))?\])?\s*(?<text>\S.*\S)?\s*/u
|
||||
this.playerStore.lyricsContent.split('\n').forEach((line) => {
|
||||
@@ -117,25 +134,25 @@ export default {
|
||||
const time =
|
||||
(Number(minutes) * 60 + Number(`${seconds}.${hundredths ?? 0}`)) *
|
||||
1000
|
||||
verses.push({ text: verse, time })
|
||||
lyrics.synchronised = !isNaN(time)
|
||||
lyrics.verses.push({ text: verse, time })
|
||||
}
|
||||
}
|
||||
})
|
||||
verses.forEach((verse, index, lyrics) => {
|
||||
const nextTime = lyrics[index + 1]?.time ?? verse.time + 3000
|
||||
lyrics.verses.forEach((verse, index, verses) => {
|
||||
const nextTime = verses[index + 1]?.time ?? verse.time + 3000
|
||||
const totalDuration = nextTime - verse.time
|
||||
const words = verse.text.match(/\S+\s*/gu) || []
|
||||
const totalLength = words.reduce((sum, word) => sum + word.length, 0)
|
||||
let currentTime = verse.time
|
||||
verse.words = words.map((text) => {
|
||||
const duration = totalDuration * (text.length / totalLength)
|
||||
const start = currentTime
|
||||
const end = start + duration
|
||||
const end = start + totalDuration * (text.length / totalLength)
|
||||
currentTime = end
|
||||
return { text, start, end }
|
||||
})
|
||||
})
|
||||
return verses
|
||||
return lyrics
|
||||
},
|
||||
startTimer() {
|
||||
if (this.timerId) {
|
||||
@@ -150,13 +167,15 @@ export default {
|
||||
}
|
||||
},
|
||||
tick() {
|
||||
this.time = this.lastProgress + Date.now() - this.lastUpdateTime
|
||||
this.time =
|
||||
this.playerStore.item_progress_ms + Date.now() - this.lastUpdateTime
|
||||
},
|
||||
updateTime() {
|
||||
this.lastUpdateTime = Date.now()
|
||||
if (this.playerStore.isPlaying) {
|
||||
this.startTimer()
|
||||
} else {
|
||||
this.time = this.lastProgress
|
||||
this.time = this.playerStore.item_progress_ms
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user