From a1a37b82ab0b2eaac6549206da3d04fffe7c3b8f Mon Sep 17 00:00:00 2001 From: Josep Boix Requesens Date: Mon, 3 Jun 2024 13:42:58 +0200 Subject: [PATCH] feat(playlist-plugin): add reverse and sort functionality Resolves #18 by adding `reverse` and `sort` methods to the playlist plugin. - The `reverse` method reverses the order of the playlist items and updates the current index accordingly. - The `sort` method sorts the playlist items based on a provided comparison function and updates the current index accordingly. --- packages/pillarbox-playlist/README.md | 6 +- .../src/pillarbox-playlist.js | 45 +++++++- .../test/pillarbox-playlist.spec.js | 101 +++++++++++++++++- 3 files changed, 147 insertions(+), 5 deletions(-) diff --git a/packages/pillarbox-playlist/README.md b/packages/pillarbox-playlist/README.md index da87381..5e2c51d 100644 --- a/packages/pillarbox-playlist/README.md +++ b/packages/pillarbox-playlist/README.md @@ -78,12 +78,14 @@ The following table outlines the key methods available in the this plugin: | `push(...items)` | Adds new items to the end of the current playlist. | | `splice(start, deleteCount, ...items)` | Modifies the playlist by adding, removing, or replacing items. Adjusts the current index if necessary. | | `clear()` | Clears the internal playlist. Does not stop or unload the currently playing media. | +| `reverse()` | Reverses the order of the items in the playlist. Adjusts the current index if necessary. | +| `sort(compareFn?)` | Sorts the items in the playlist using the provided compare function. Adjusts the current index if necessary. | | `next()` | Advances to the next item in the playlist, with support for repeat mode. | | `previous()` | Moves to the previous item in the playlist. | | `shuffle()` | Randomizes the order of the playlist items using the Fisher-Yates shuffle algorithm. | | `select(index)` | Selects and plays the item at the specified index in the playlist. | -| `toggleRepeat(force)` | Toggles the repeat mode of the player to the opposite of its current state, or sets it to the specified boolean value if provided. | -| `toggleAutoadvance(force)` | Toggles the auto-advance mode of the player to the opposite of its current state, or sets it to the specified boolean value if provided. | +| `toggleRepeat(force?)` | Toggles the repeat mode of the player to the opposite of its current state, or sets it to the specified boolean value if provided. | +| `toggleAutoadvance(force?)` | Toggles the auto-advance mode of the player to the opposite of its current state, or sets it to the specified boolean value if provided. | #### Options diff --git a/packages/pillarbox-playlist/src/pillarbox-playlist.js b/packages/pillarbox-playlist/src/pillarbox-playlist.js index 405a671..34aff91 100644 --- a/packages/pillarbox-playlist/src/pillarbox-playlist.js +++ b/packages/pillarbox-playlist/src/pillarbox-playlist.js @@ -113,6 +113,8 @@ class PillarboxPlaylist extends Plugin { * running. * * @param {...PlaylistItem} items the items to add to the playlist. + * + * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/push */ push(...items) { this.items_.push(...items); @@ -136,6 +138,8 @@ class PillarboxPlaylist extends Plugin { * @param {...PlaylistItem} items The items to add to the playlist. * * @return {PlaylistItem[]} An array containing the deleted elements. + * + * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice */ splice(start, deleteCount, ...items) { const itemsAddedCount = items.length; @@ -143,7 +147,7 @@ class PillarboxPlaylist extends Plugin { const deletedElementsCount = deletedElements.length; if (this.currentIndex_ >= start && - this.currentIndex_ < start + deletedElementsCount) { + this.currentIndex_ < start + deletedElementsCount) { // Current item was removed, set currentIndex to -1 this.currentIndex_ = -1; } else if (this.currentIndex_ >= start) { @@ -169,6 +173,45 @@ class PillarboxPlaylist extends Plugin { this.updateState_(); } + /** + * Reverses the order of the items in the playlist and updates the current index to reflect + * the new position of the previously current item after reversal. + * + * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reverse + */ + reverse() { + if (!this.items_.length) return; + + this.items_.reverse(); + this.currentIndex_ = this.items_.length - 1 - this.currentIndex_; + this.updateState_(); + } + + /** + * Sorts the items in the playlist using the provided compare function and updates the current + * index to reflect the new position of the previously current item after sorting. + * + * @method sort + * @param {Function} compareFn - A function that defines the sort order. + * The return value should be a number whose sign indicates the relative order + * of the two elements: negative if a is less than b, positive if a is + * greater than b, and zero if they are equal. NaN is treated as 0. If omitted, + * the array elements are converted to strings, then sorted according to each + * character's Unicode code point value. + * + * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort + */ + sort(compareFn) { + if (!this.items_.length) return; + + const currentItem = this.currentItem; + + this.items_.sort(compareFn); + this.currentIndex_ = this.items_.indexOf(currentItem); + this.updateState_(); + } + + /** * Get the currently playing index. * diff --git a/packages/pillarbox-playlist/test/pillarbox-playlist.spec.js b/packages/pillarbox-playlist/test/pillarbox-playlist.spec.js index 99a5820..4f1adb3 100644 --- a/packages/pillarbox-playlist/test/pillarbox-playlist.spec.js +++ b/packages/pillarbox-playlist/test/pillarbox-playlist.spec.js @@ -7,7 +7,7 @@ const playlist = [ { sources: [{ src: 'first-source', type: 'test' }], poster: 'first-poster', - data: { title: 'first-source', duration: 120 } + data: { title: 'first-source', duration: 180 } }, { sources: [{ src: 'second-source', type: 'test' }], @@ -17,7 +17,7 @@ const playlist = [ { sources: [{ src: 'third-source', type: 'test' }], poster: 'third-poster', - data: { title: 'third-source', duration: 180 } + data: { title: 'third-source', duration: 120 } }, { sources: [{ src: 'fourth-source', type: 'test' }], @@ -389,6 +389,103 @@ describe('PillarboxPlaylist', () => { }); }); + describe('reverse', () => { + it('should reverse the order of items and update currentIndex correctly', () => { + // Given + pillarboxPlaylist.load(playlist); + pillarboxPlaylist.select(1); + + // When + pillarboxPlaylist.reverse(); + + // Then + expect(pillarboxPlaylist.currentIndex).toBe(2); + expect(pillarboxPlaylist.currentItem).toBe(playlist[1]); + }); + + it('should reverse an empty playlist without errors', () => { + // Given + pillarboxPlaylist.load([]); + + // When + pillarboxPlaylist.reverse(); + + // Then + expect(pillarboxPlaylist.items_.length).toBe(0); + expect(pillarboxPlaylist.currentIndex).toBe(-1); + expect(pillarboxPlaylist.currentItem).toBeUndefined(); + }); + + it('should reverse a single-item playlist without changing the index', () => { + // Given + const items = [{ + sources: [{ src: 'first-source', type: 'test' }], + poster: 'first-poster', + data: { title: 'first-source', duration: 120 } + }]; + + pillarboxPlaylist.load(items); + + // When + pillarboxPlaylist.reverse(); + + // Then + expect(pillarboxPlaylist.currentIndex).toBe(0); + expect(pillarboxPlaylist.currentItem).toBe(items[0]); + }); + }); + + + describe('sort', () => { + it('should sort items by duration and update currentIndex correctly', () => { + // Given + pillarboxPlaylist.load(playlist); + pillarboxPlaylist.select(2); + + // When + pillarboxPlaylist.sort((a, b) => a.data.duration - b.data.duration); + + // Then + const durations = pillarboxPlaylist.items_.map(item => item.data.duration); + + for (let i = 0; i < durations.length - 1; i++) { + expect(durations[i]).toBeLessThanOrEqual(durations[i + 1]); + } + expect(pillarboxPlaylist.currentIndex).toBe(0); + expect(pillarboxPlaylist.currentItem).toBe(playlist[2]); + }); + + it('should handle sorting an empty playlist without errors', () => { + // Given + pillarboxPlaylist.load([]); + + // When + pillarboxPlaylist.sort((a, b) => a.data.duration - b.data.duration); + + // Then + expect(pillarboxPlaylist.items_.length).toBe(0); + expect(pillarboxPlaylist.currentIndex).toBe(-1); + }); + + it('should sort a single-item playlist without changing the index', () => { + // Given + const items = [{ + sources: [{ src: 'first-source', type: 'test' }], + poster: 'first-poster', + data: { title: 'first-source', duration: 120 } + }]; + + pillarboxPlaylist.load(items); + + // When + pillarboxPlaylist.sort((a, b) => a.data.duration - b.data.duration); + + // Then + expect(pillarboxPlaylist.currentIndex).toBe(0); + expect(pillarboxPlaylist.currentItem).toBe(items[0]); + }); + }); + describe('clear', () => { it('should clear all the items of the playlist', () => { // Given