22 <video @timeupdate =" onTimeUpdate" @ended =" onEnded" @loadedmetadata =" onLoadedMetadata" ref =" mediaRef" autoplay
33 hidden ></video >
44
5- <div v-if =" track" class =" bg-base-200 relative h-24" >
6- <!-- FIXME: ERROR on delete track -->
5+ <div v-if =" track" class =" bg-base-200 relative h-24 w-full" >
76 <!-- Progress Bar and Needle-->
8- <progress class =" progress absolute top-0 h-1.5 w-full rounded-none transition-all hover:-top-1.5 hover:h-3"
9- :value =" playerState.currentTime" :max =" playerState.duration" @click =" onProgressClick" ></progress >
7+ <progress class =" progress absolute top-0 h-1.5 w-full rounded-none transition-all hover:-top-1.5 hover:h-3"
8+ :value =" playerState.currentTime" :max =" playerState.duration || 100 " @click =" onProgressClick" ></progress >
109
1110 <div class =" flex h-full w-full px-4 py-3" >
12- <div class =" flex flex-1/3 gap-2 " >
13- <img :src =" getCoverUrl(track)" alt =" cover" class =" skeleton object-cover" />
14- <div class =" flex flex-col" >
15- <p class =" font-bold" >{{ track.title }}</p >
16- <p class =" text-base-content/70" >{{ getArtist(track) }}</p >
11+ <div class =" flex items-center gap-3 flex-1/3" >
12+ <img :src =" getCoverUrl(track)" alt =" cover" class =" object-cover skeleton size-18 " />
13+ <div class =" flex flex-col overflow-hidden " >
14+ <p class =" font-bold truncate " :title = " track.title " >{{ track.title }}</p >
15+ <p class =" text-base-content/70 truncate " :title = " getArtist(track) " >{{ getArtist(track) }}</p >
1716 </div >
1817 </div >
1918
20- <div class =" flex flex-1/3 items-center justify-center gap-4" >
21- <button class =" btn btn-ghost btn-circle" >
19+ <div class =" flex items-center justify-center gap-4 flex-1/3 " >
20+ <button class =" btn btn-ghost btn-circle" @click = " openListeningWidget " >
2221 <i-mdi-playlist-play />
2322 </button >
2423 <button class =" btn btn-ghost btn-circle" @click =" nextTrack(-1)" >
3433 </button >
3534 <PlayOrderSwitch ></PlayOrderSwitch >
3635 </div >
37- <div class =" flex-1/3" >
38- <div >
39- <button class =" btn btn-ghost btn-circle " @click = " openListeningWidget " >
40- < i-mdi-playlist-play / >
41- </ button >
36+ <div class =" flex items-center justify-end flex -1/3" >
37+ <div class = " flex items-center gap-2 " >
38+ <span class =" text-sm " >{{ formatSecs(playerState.currentTime) }}</ span >
39+ < span class = " text-sm " >/</ span >
40+ <span class = " text-sm " >{{ formatMillis(track.duration) }}</ span >
4241 </div >
4342 </div >
4443 </div >
@@ -55,7 +54,7 @@ import {
5554 setCurrentTrack ,
5655 setTrackUpdateCallback ,
5756} from " @/systems/player/listening-list"
58- import { getArtist , getCoverUrl , replaceImageUrl } from " @/utils/utils"
57+ import { formatMillis , formatSecs , getArtist , getCoverUrl , replaceImageUrl } from " @/utils/utils"
5958import Hls from " hls.js"
6059import type { ErrorData } from " hls.js"
6160import { Track } from " @/utils/types"
@@ -120,10 +119,10 @@ onMounted(() => {
120119 seek (details .seekTime ! )
121120 })
122121 navigator .mediaSession .setActionHandler (" seekforward" , (details ) => {
123- seek (details .seekOffset ?? 10 )
122+ seek (playerState . currentTime + ( details .seekOffset ?? 10 ) )
124123 })
125124 navigator .mediaSession .setActionHandler (" seekbackward" , (details ) => {
126- seek (details .seekOffset ?? - 10 )
125+ seek (playerState . currentTime - ( details .seekOffset ?? 10 ) )
127126 })
128127 navigator .mediaSession .setActionHandler (" stop" , () => {
129128 pause ()
@@ -138,9 +137,10 @@ onMounted(() => {
138137 }
139138
140139 // set track update callbacks
141-
142- setTrackUpdateCallback ((_idx ) => {
143- loadSong ()
140+ setTrackUpdateCallback ((idx ) => {
141+ if (idx >= 0 ) {
142+ loadSong ()
143+ }
144144 })
145145
146146 // Initialize HLS player if supported
@@ -232,7 +232,8 @@ function seek(time: number) {
232232 if (! mediaRef .value || ! isFinite (time )) {
233233 return
234234 }
235- mediaRef .value .currentTime = time
235+ const safeTime = Math .max (0 , Math .min (time , playerState .duration || time ))
236+ mediaRef .value .currentTime = safeTime
236237}
237238
238239// 处理进度条点击事件以实现 Seek
@@ -269,6 +270,7 @@ function pause() {
269270
270271async function loadSong(forceRefreshM3U8 : boolean = false ) {
271272 if (! mediaRef .value || ! track .value ) {
273+ playerState .loading = false
272274 return
273275 }
274276
@@ -297,11 +299,13 @@ async function loadSong(forceRefreshM3U8: boolean = false) {
297299
298300 hlsPlayer .value .once (Hls .Events .MANIFEST_PARSED , async () => {
299301 try {
300- // restore load
301- hlsPlayer .value ! .startLoad (mediaRef .value ! .currentTime )
302- // 等待 HLS 解析完成后再播放
303- await mediaRef .value ! .play ()
304- playerState .paused = false
302+ if (mediaRef .value && track .value ) {
303+ // restore load
304+ hlsPlayer .value ! .startLoad (mediaRef .value ! .currentTime )
305+ // 等待 HLS 解析完成后再播放
306+ await mediaRef .value ! .play ()
307+ playerState .paused = false
308+ }
305309 } catch (error ) {
306310 console .error (" HLS Play Failed:" , error )
307311 playerState .paused = true
@@ -316,7 +320,6 @@ async function loadSong(forceRefreshM3U8: boolean = false) {
316320 })
317321
318322 // automatically loads after a few seconds
319-
320323 setTimeout (async () => {
321324 // TODO: abortable task
322325 await nextTrack ()
0 commit comments