跟踪音频播放器的所有历史记录(上一个、下一个按钮和从浏览器中随机选择)

问题描述 投票:0回答:2

我需要跟踪音频播放器播放过的歌曲的所有历史记录。困难的部分是当用户连续单击下一个和上一个按钮时。

测试数据(歌曲):

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],
  })
});

任何人都可以为我指明如何更好地实施它的正确方向吗?显然,只有当用户不连续单击上一个/下一个按钮时,此逻辑才有效。严重卡在这里。

javascript audio-player
2个回答
0
投票

我在这里看到的(非常微妙的)问题是

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);


0
投票

据我了解,您有一组数据,其中包含一个播放列表。您希望能够保留播放历史记录。您还希望能够单击一首歌曲并播放它,如果可能的话,还可以使用下一个和上一个按钮浏览历史记录,否则您想要浏览播放列表。

为了使其尽可能简单,我会执行以下操作:

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>

代码的解释也包含在注释中。

© www.soinside.com 2019 - 2024. All rights reserved.