feat: play-by-id and play-playlist endpoints, fix lyrics sync timing

- New endpoints: play-by-id, add-by-id, play-playlist (load all songs at once)
- Playlist 'Play All' now uses server-side bulk load (no N sequential searches)
- Search/Home/Playlist views use playById instead of search-by-name
- Fixed updateBotStatus timing: capture prev state before mutation
- Early return on song change prevents stale pause/resume logic

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
saopig1andClaude Opus 4.6 committed 2026-03-30 02:55:15 +08:00
1 parent 2c87f1cff9
commit 2e4d6d8d40
6 files changed
+163 -20

No files matched your search

+34 -6
View File
@@ -76,10 +76,14 @@ export const usePlayerStore = defineStore('player', {
},
updateBotStatus(botId: string, status: BotStatus) {
// Capture previous state BEFORE updating
const prev = this.bots.find((b) => b.id === botId);
const prevSongId = prev?.currentSong?.id;
const prevPaused = prev?.paused ?? false;
const prevPlaying = prev?.playing ?? false;
const newSongId = status.currentSong?.id;
// Update the bot status
const index = this.bots.findIndex((b) => b.id === botId);
if (index >= 0) {
this.bots[index] = status;
@@ -87,19 +91,24 @@ export const usePlayerStore = defineStore('player', {
this.bots.push(status);
}
// Reset play timer when song changes
// Only adjust timing for the active bot
if (botId !== (this.activeBotId ?? this.bots[0]?.id)) return;
// Song changed — reset timer
if (newSongId && newSongId !== prevSongId) {
this.playStartedAt = Date.now();
this.pausedElapsed = 0;
return;
}
// Track pause/resume
if (status.playing && !status.paused && prev?.paused) {
// Resumed — adjust start time
// Resumed from pause
if (status.playing && !status.paused && prevPaused) {
this.playStartedAt = Date.now() - this.pausedElapsed * 1000;
return;
}
if (status.paused && !prev?.paused) {
// Paused — save elapsed
// Paused
if (status.paused && prevPlaying && !prevPaused) {
this.pausedElapsed = this.playStartedAt > 0
? (Date.now() - this.playStartedAt) / 1000
: 0;
@@ -154,11 +163,30 @@ export const usePlayerStore = defineStore('player', {
this.pausedElapsed = 0;
},
async playById(songId: string, platform = 'netease') {
if (!this.activeBotId) return;
await axios.post(`/api/player/${this.activeBotId}/play-by-id`, { songId, platform });
this.playStartedAt = Date.now();
this.pausedElapsed = 0;
},
async addToQueue(query: string, platform = 'netease') {
if (!this.activeBotId) return;
await axios.post(`/api/player/${this.activeBotId}/add`, { query, platform });
},
async addToQueueById(songId: string, platform = 'netease') {
if (!this.activeBotId) return;
await axios.post(`/api/player/${this.activeBotId}/add-by-id`, { songId, platform });
},
async playPlaylist(playlistId: string, platform = 'netease') {
if (!this.activeBotId) return;
await axios.post(`/api/player/${this.activeBotId}/play-playlist`, { playlistId, platform });
this.playStartedAt = Date.now();
this.pausedElapsed = 0;
},
async pause() {
if (!this.activeBotId) return;
this.pausedElapsed = this.playStartedAt > 0
+1 -1
View File
@@ -54,7 +54,7 @@
v-for="song in store.dailySongs.slice(0, 12)"
:key="song.id"
class="daily-card hover-scale"
@click="store.play(song.name, song.platform)"
@click="store.playById(song.id, song.platform)"
>
<CoverArt :url="song.coverUrl" :size="120" :radius="10" :show-shadow="true" />
<div class="daily-name">{{ song.name }}</div>
+5 -9
View File
@@ -31,8 +31,8 @@
:song="song"
:index="i + 1"
:active="store.currentSong?.id === song.id"
@play="store.play(song.name, song.platform)"
@add="store.addToQueue(song.name, song.platform)"
@play="store.playById(song.id, song.platform)"
@add="store.addToQueueById(song.id, song.platform)"
/>
</div>
</template>
@@ -76,13 +76,9 @@ const songs = ref<SongItem[]>([]);
const loading = ref(true);
async function playAll() {
if (songs.value.length > 0) {
const first = songs.value[0];
await store.play(first.name, first.platform);
for (let i = 1; i < songs.value.length; i++) {
await store.addToQueue(songs.value[i].name, songs.value[i].platform);
}
}
const id = route.params.id as string;
const platform = (route.query.platform as string) || 'netease';
await store.playPlaylist(id, platform);
}
onMounted(async () => {
+2 -2
View File
@@ -27,8 +27,8 @@
:song="song"
:index="i + 1"
:active="store.currentSong?.id === song.id"
@play="store.play(song.name, song.platform)"
@add="store.addToQueue(song.name, song.platform)"
@play="store.playById(song.id, song.platform)"
@add="store.addToQueueById(song.id, song.platform)"
/>
</div>