I need to keep track of all history of which songs have been played with audio player. The difficult part is when user continuously clicks on next and previous button.
Testing data (songs):
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',
}
];
Some variables used and their usage behind them:
// 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
Initialize tracks (working as intended):
const initializeTracks = (tracks) => {
const { path, name, id } = tracks[0];
const playingTrack = {
path,
name,
id,
};
return {
playingTrack,
currentPlayingTrackIndex: 0,
trackQueIndex: 0,
trackStartPoint: 0,
playedTrackIndexes: [0]
}
};
Get next track (when next button is clicked or track ends):
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]
}
};
Get previous track (when previous button is clicked on):
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]
}
};
Failing test case for getPreviousTrack. Obviously it doesn’t work with use case like this anymore. I think the whole logic needs to be rewritten to keep track of the history correctly, but I am not sure how exactly. Maybe using uuid for each played track, but how does that help me exactly? I am actually baffled how complex can a simple audio player logic be (if you really want to keep track of the whole play history correctly and not just reset it at some point for a simplified logic).
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]
})
});
There is also an option to select tracks from the library as well. This is an separate function, which I have not yet implemented. So lets say user plays indexes [0, 1, 2], then selects tracksData[7] then the playedTrackIndexes would be [0, 1, 2, 7].
Working more simple test case:
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],
})
});
Can anyone please point me in the right direction how to implement it better? Obviously this logic only works if user doesn’t click on previous/next buttons continuously. Seriously stuck here.
2
Answers
The (very subtle) issue I see here is confusion of the meanings of
currentPlayingTrackIndex
andtrackQueIndex
.currentPlayingTrackIndex
is a bit like the ID of the playing track – it is the index of the playing track inside thetracksData
array.Meanwhile
trackQueIndex
is the index of the playing track within the history array. They won’t always be the same as each other, which you’veexpected
in your test function.So actually, your in your expectation these variables are wrong.
So in your test function you should define the
tracksQueData
differently, so that thetrackQueIndex
points to the element stored inside ofcurrentPlayingTrackIndex
within theplayedTrackIndexes
array, and usetrackQueIndex
to keep track of where in the history you are.I.e.,
currentPlayingTrackIndex
should always be given bytracksQueData.playedTrackIndexes[tracksQueData.trackQueIndex]
.However you have defined both as 5 when the history is actually longer.
To know when to start playing new tracks, you cleverly use the
trackStartPoint
variable.Here is this in action:
Note – as I stated in the comments, I think it would be a good idea to convert this code into classes as that would make it more readable. This is the perfect use case for OOP: you have a couple entities (the player and tracks) that interract with each other.
As far as I understood, you have your set of data, which contains a playlist. You want to be able to keep a record of the playing history. You also want to be able to click on a song and play it, aswell using the next and previous buttons to navigate through the history if possible, otherwise you want to go through the playlist.
Use the browser’s console to have a better view of the console logs.
To make it as simple as possible, I would do the following:
The explanation of the code is also included in comments.