我需要跟踪音频播放器播放过的歌曲的所有历史记录。困难的部分是当用户连续单击下一个和上一个按钮时。
测试数据(歌曲):
export const tracksData = [
{
name: 'song-1',
path: 'path.mp3',
id: 'song-1-id',
}, {
name: 'song-2',
path: 'path.mp3',
id: 'song-2-id',
}, {
name: 'song-3',
path: 'path.mp3',
id: 'song-3-id',
}, {
name: 'song-4',
path: 'path.mp3',
id: 'song-4-id',
}, {
name: 'song-5',
path: 'path.mp3',
id: 'song-5-id',
}, {
name: 'song-6',
path: 'path.mp3',
id: 'song-6-id',
}, {
name: 'song-7',
path: 'path.mp3',
id: 'song-7-id',
}, {
name: 'song-8',
path: 'path.mp3',
id: 'song-8-id',
}, {
name: 'song-9',
path: 'path.mp3',
id: 'song-9-id',
}, {
name: 'song-10',
path: 'path.mp3',
id: 'song-10-id',
}
];
使用的一些变量及其背后的用法:
// currentPlayingTrackIndex = index of tracks array
// trackQueIndex = position of playedTrackedIndexes
// trackStartPoint === 0 then at the the start of the que and should take just next indexes
初始化轨道(按预期工作):
const initializeTracks = (tracks) => {
const { path, name, id } = tracks[0];
const playingTrack = {
path,
name,
id,
};
return {
playingTrack,
currentPlayingTrackIndex: 0,
trackQueIndex: 0,
trackStartPoint: 0,
playedTrackIndexes: [0]
}
};
获取下一首曲目(当单击下一个按钮或曲目结束时):
const getNextTrack = (tracks, tracksQueData) => {
const { currentPlayingTrackIndex, trackQueIndex, trackStartPoint, playedTrackIndexes } = tracksQueData;
const isCurrentTrackTheLastOfPlayQue = trackStartPoint === 0;
const nextTrackIndex = isCurrentTrackTheLastOfPlayQue ? currentPlayingTrackIndex + 1 : playedTrackIndexes[trackQueIndex + 1];
const { path, name, id } = tracks[nextTrackIndex];
const nextTrack = {
path,
name,
id,
};
const nextTrackQueIndex = trackQueIndex + 1;
const startPoint = trackStartPoint === 0 ? trackStartPoint : trackStartPoint - 1;
return {
playingTrack: nextTrack,
currentPlayingTrackIndex: nextTrackIndex,
trackQueIndex: nextTrackQueIndex,
trackStartPoint: startPoint,
playedTrackIndexes: [...playedTrackIndexes, nextTrackIndex]
}
};
获取上一首曲目(单击上一首按钮时):
const getPreviousTrack = (tracks, tracksQueData) => {
const { currentPlayingTrackIndex, trackQueIndex, trackStartPoint, playedTrackIndexes } = tracksQueData;
const previousTrackQueIndex = trackQueIndex - 1;
const previousTrackIndex = playedTrackIndexes[previousTrackQueIndex];
const { path, name, id } = tracks[previousTrackIndex];
const previousTrack = {
path,
name,
id,
};
const startPoint = trackStartPoint + 1;
return {
playingTrack: previousTrack,
currentPlayingTrackIndex: previousTrackIndex,
trackQueIndex: previousTrackQueIndex,
trackStartPoint: startPoint,
playedTrackIndexes: [...playedTrackIndexes, previousTrackIndex]
}
};
getPreviousTrack 测试用例失败。显然它不再适用于这样的用例。我认为需要重写整个逻辑以正确跟踪历史,但我不确定具体如何。也许对每个播放的曲目使用 uuid,但这对我有什么帮助呢?实际上,我很困惑简单的音频播放器逻辑有多复杂(如果您真的想正确跟踪整个播放历史记录,而不只是在某个时刻重置它以简化逻辑)。
it('Should return correct track object when getting previous track and the same tracks have been played multiple times, more complex', () => {
const tracksQueData = {
currentPlayingTrackIndex: 5,
trackQueIndex: 5,
trackStartPoint: 0,
playedTrackIndexes: [0, 1, 2, 1, 0, 1, 2, 1, 0, 1, 2, 1, 0, 1, 2, 1, 0, 1, 2, 3, 4, 5]
};
const track = getPreviousTrack(tracksTestData, tracksQueData);
expect(track).toEqual(
{
playingTrack: {
name: 'song-4',
path: 'path.mp3',
id: 'song-4-id',
},
currentPlayingTrackIndex: 4,
trackQueIndex: 4,
trackStartPoint: 1,
playedTrackIndexes: [0, 1, 2, 1, 0, 1, 2, 1, 0, 1, 2, 1, 0, 1, 2, 1, 0, 1, 2, 3, 4, 5, 4]
})
});
还有一个选项可以从库中选择曲目。这是一个单独的函数,我还没有实现。因此,假设用户播放索引 [0, 1, 2],然后选择 trackData[7],那么 PlayedTrackIndexes 将是 [0, 1, 2, 7]。
工作更简单的测试用例:
it('Should return correct track object when getting previous track and the same tracks have been played multiple times', () => {
const tracksQueData = {
currentPlayingTrackIndex: 4,
trackQueIndex: 4,
trackStartPoint: 4,
playedTrackIndexes: [0, 1, 2, 3, 2, 1, 2, 1, 2, 3, 4, 3, 2, 3, 4, 5, 4, 3, 2, 3, 2, 3, 4]
};
const track = getPreviousTrack(tracksTestData, tracksQueData);
expect(track).toEqual(
{
playingTrack: {
name: 'song-4',
path: 'path.mp3',
id: 'song-4-id',
},
currentPlayingTrackIndex: 3,
trackQueIndex: 3,
trackStartPoint: 5,
playedTrackIndexes: [0, 1, 2, 3, 2, 1, 2, 1, 2, 3, 4, 3, 2, 3, 4, 5, 4, 3, 2, 3, 2, 3, 4, 3],
})
});
任何人都可以为我指明如何更好地实施它的正确方向吗?显然,只有当用户不连续单击上一个/下一个按钮时,此逻辑才有效。严重卡在这里。
我在这里看到的(非常微妙的)问题是
currentPlayingTrackIndex
和trackQueIndex
的含义混淆。
currentPlayingTrackIndex
有点像播放曲目的ID - 它是tracksData
数组中播放曲目的索引。
同时
trackQueIndex
是历史数组中播放曲目的索引。它们并不总是彼此相同,您在测试函数中已expected
。
所以实际上,您的期望是错误的,这两个变量/属性的用法也是错误的。
因此,在测试函数中,您应该以不同的方式定义
tracksQueData
,以便 trackQueIndex
指向存储在 currentPlayingTrackIndex
数组中 playedTrackIndexes
内部的元素,并使用 trackQueIndex
来跟踪你就是历史。
即,
currentPlayingTrackIndex
应始终由 tracksQueData.playedTrackIndexes[tracksQueData.trackQueIndex]
给出。
要知道何时开始播放新曲目,您可以巧妙地使用
trackStartPoint
变量。
这是实际操作:
var tracksData = [
{
name: 'song-1',
path: 'path.mp3',
id: 'song-1-id',
}, {
name: 'song-2',
path: 'path.mp3',
id: 'song-2-id',
}, {
name: 'song-3',
path: 'path.mp3',
id: 'song-3-id',
}, {
name: 'song-4',
path: 'path.mp3',
id: 'song-4-id',
}, {
name: 'song-5',
path: 'path.mp3',
id: 'song-5-id',
}, {
name: 'song-6',
path: 'path.mp3',
id: 'song-6-id',
}, {
name: 'song-7',
path: 'path.mp3',
id: 'song-7-id',
}, {
name: 'song-8',
path: 'path.mp3',
id: 'song-8-id',
}, {
name: 'song-9',
path: 'path.mp3',
id: 'song-9-id',
}, {
name: 'song-10',
path: 'path.mp3',
id: 'song-10-id',
}
];
function initializeTracks (tracks) {
const { path, name, id } = tracks[0];
const playingTrack = {
path,
name,
id,
};
return {
playingTrack,
currentPlayingTrackIndex: 0,
trackQueIndex: 0,
trackStartPoint: 0,
playedTrackIndexes: [0]
}
}
function getNextTrack (tracks, tracksQueData) {
const { currentPlayingTrackIndex, trackQueIndex, trackStartPoint, playedTrackIndexes } = tracksQueData;
const isCurrentTrackTheLastOfPlayQue = trackStartPoint === 0;
const nextTrackIndex = isCurrentTrackTheLastOfPlayQue ? currentPlayingTrackIndex + 1 : playedTrackIndexes[trackQueIndex + 1];
const { path, name, id } = tracks[nextTrackIndex];
const nextTrack = {
path,
name,
id,
};
const nextTrackQueIndex = trackQueIndex + 1;
const startPoint = trackStartPoint === 0 ? trackStartPoint : trackStartPoint - 1;
return {
playingTrack: nextTrack,
currentPlayingTrackIndex: nextTrackIndex,
trackQueIndex: nextTrackQueIndex,
trackStartPoint: startPoint,
playedTrackIndexes: [...playedTrackIndexes, nextTrackIndex]
}
}
function getPreviousTrack (tracks, tracksQueData) {
const { currentPlayingTrackIndex, trackQueIndex, trackStartPoint, playedTrackIndexes } = tracksQueData;
const previousTrackQueIndex = trackQueIndex-1;
const previousTrackIndex = playedTrackIndexes[previousTrackQueIndex];
const { path, name, id } = tracks[previousTrackIndex];
const previousTrack = {
path,
name,
id,
};
const startPoint = trackStartPoint + 1;
return {
playingTrack: previousTrack,
currentPlayingTrackIndex: previousTrackIndex,
trackQueIndex: previousTrackQueIndex,
trackStartPoint: startPoint,
playedTrackIndexes: [...playedTrackIndexes, previousTrackIndex],
}
}
var test = (tracksData) => {
const tracksQueData = {
currentPlayingTrackIndex: 5, // a bit like track id
trackQueIndex: 7, // the index of the track in history
trackStartPoint: 0,
playedTrackIndexes: [0,1,0,1,2,3,4,5],
};
console.log("Original is:", ...[0,1,0,1,2,3,4,5]);
console.log("Currently playing:", tracksQueData.playedTrackIndexes[tracksQueData.trackQueIndex])
console.log("go to previous a few times:");
var track = getPreviousTrack(tracksData, tracksQueData);
console.log(track.currentPlayingTrackIndex);
var track = getPreviousTrack(tracksData, track);
console.log(track.currentPlayingTrackIndex);
var track = getPreviousTrack(tracksData, track);
console.log(track.currentPlayingTrackIndex);
console.log("and go once forward:");
var track = getNextTrack(tracksData, track);
console.log(track.currentPlayingTrackIndex);
console.log("and back again:");
var track = getPreviousTrack(tracksData, track);
console.log(track.currentPlayingTrackIndex);
console.log("go to next a few times:");
var track = getNextTrack(tracksData, track);
console.log(track.currentPlayingTrackIndex);
var track = getNextTrack(tracksData, track);
console.log(track.currentPlayingTrackIndex);
var track = getNextTrack(tracksData, track);
console.log(track.currentPlayingTrackIndex);
var track = getNextTrack(tracksData, track);
console.log(track.currentPlayingTrackIndex);
var track = getNextTrack(tracksData, track);
console.log(track.currentPlayingTrackIndex);
var track = getNextTrack(tracksData, track);
console.log(track.currentPlayingTrackIndex);
console.log("resulting history from this manipulation:"); // expected: 0 1 0 1 2 3 4 5 4 3 2 3 2 3 4 5 6 7 8
console.log(...track.playedTrackIndexes);
return track;
};
console.log("Track order is as expected:")
test(tracksData);
据我了解,您有一组数据,其中包含一个播放列表。您希望能够保留播放历史记录。您还希望能够单击一首歌曲并播放它,如果可能的话,还可以使用下一个和上一个按钮浏览历史记录,否则您想要浏览播放列表。
为了使其尽可能简单,我会执行以下操作:
const playlist = [{
name: 'song-1',
path: 'path.mp3',
id: 'song-1-id',
}, {
name: 'song-2',
path: 'path.mp3',
id: 'song-2-id',
}, {
name: 'song-3',
path: 'path.mp3',
id: 'song-3-id',
}, {
name: 'song-4',
path: 'path.mp3',
id: 'song-4-id',
}, {
name: 'song-5',
path: 'path.mp3',
id: 'song-5-id',
}, {
name: 'song-6',
path: 'path.mp3',
id: 'song-6-id',
}, {
name: 'song-7',
path: 'path.mp3',
id: 'song-7-id',
}, {
name: 'song-8',
path: 'path.mp3',
id: 'song-8-id',
}, {
name: 'song-9',
path: 'path.mp3',
id: 'song-9-id',
}, {
name: 'song-10',
path: 'path.mp3',
id: 'song-10-id',
}];
const history = [];
let historyIndex;
let currentSong = {};
function playSong(song, fromHistory = false) {
// if a song is not played from the history, it shall be added to the history and historyIndex will be set as the latest song
if (!fromHistory && song !== currentSong){
history.push(song);
historyIndex = history.length - 1;
}
currentSong = song;
console.log("current: ", currentSong.name);
console.log("playing from history: ", fromHistory);
console.log("history: ", history);
console.log("historyIndex: ", historyIndex);
}
// a general function for the NEXT button
function next() {
if (currentSong === {}) return; // do nothing if no song is selected
// checks whether there is a history or not
if (history.length === 0) {
nextInPlaylist();
} else {
nextInHistory();
}
}
// plays next song in the playlist
function nextInPlaylist() {
// finding the currentIndex of the selected song
const currentIndex = playlist.findIndex(s => s.id === currentSong.id);
// checks if it is the last song on the playlist. If yes, it should just start from the beginning again
if (currentIndex === playlist.length - 1) {
playSong(playlist.at(0));
} else {
playSong(playlist.at(currentIndex + 1));
}
}
// plays next song in the history
function nextInHistory() {
// checks if it is the last song on the history. If yes, it should just play the next song in the playlist
if (historyIndex === history.length - 1) {
nextInPlaylist();
} else {
historyIndex++;
playSong(history.at(historyIndex), true);
}
}
function back() {
if (currentSong === {}) return; // do nothing if no song is selected
// checks whether there is a history or not
if (history.length === 0) {
backInPlaylist();
} else {
backInHistory();
}
}
// plays previous song in the playlist
function backInPlaylist() {
const currentIndex = playlist.findIndex(s => s.id === currentSong.id);
// checks if it is the first song on the playlist. If yes, it should just start from the end
if (currentIndex === 0) {
playSong(playlist.at(-1));
} else {
playSong(playlist.at(currentIndex - 1));
}
}
// plays previous song in the history
function backInHistory() {
// checks if it is the first song on the history. If yes, it should just play the previous song in the playlist
if (historyIndex === 0) {
backInPlaylist();
} else {
historyIndex--;
playSong(history.at(historyIndex), true);
}
}
function selectRandomSong() {
const maxSongIndex = playlist.length - 1;
const randomNum = Math.floor(Math.random() * maxSongIndex);
playSong(playlist.at(randomNum));
}
<button onClick="selectRandomSong()">Select random</button><br>
<button onClick="next()">Next</button><br>
<button onClick="back()">Back</button>
代码的解释也包含在注释中。