Skip to content

Commit

Permalink
feat(playlist-plugin): introduce repeat one mode
Browse files Browse the repository at this point in the history
- Introduced a new "repeat one" mode to the playlist plugin.
- Modified the UI to include a toggle button that cycles through three modes: no repeat, repeat all,
  and repeat one.

BREAKING CHANGE:

- Changed the API to toggle the repeat mode and the initialization options.
- The repeat mode is no longer a boolean. It is now a number representing each mode (0: no repeat,
  1: repeat all, 2: repeat one).
  • Loading branch information
jboix committed Jul 12, 2024
1 parent c6bb013 commit 7005629
Show file tree
Hide file tree
Showing 14 changed files with 442 additions and 150 deletions.
51 changes: 38 additions & 13 deletions packages/pillarbox-playlist/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,34 @@ Once the player is installed you can activate the plugin as follows:

```javascript
import Pillarbox from '@srgssr/pillarbox-web';
import '@srgssr/pillarbox-playlist';
import { RepeatMode } from '@srgssr/pillarbox-playlist';
import '@srgssr/pillarbox-playlist/ui';

const player = new Pillarbox('my-player', {
plugins: {
pillarboxPlaylist: { autoadvance: true, repeat: true },
plugins: {
pillarboxPlaylist: {
autoadvance: true,
repeat: RepeatMode.REPEAT_ALL
},
pillarboxPlaylistUI: true
}
});

const playlist = [
{ sources: [{ src: 'video1.mp4', type: 'video/mp4' }], poster: 'poster1.jpg' },
{ sources: [{ src: 'video2.mp4', type: 'video/mp4' }], poster: 'poster2.jpg' }
{
sources: [{
src: 'video1.mp4',
type: 'video/mp4'
}],
poster: 'poster1.jpg'
},
{
sources: [{
src: 'video2.mp4',
type: 'video/mp4'
}],
poster: 'poster2.jpg'
}
];

player.playlistPlugin().load(playlist);
Expand Down Expand Up @@ -71,7 +86,7 @@ The following table outlines the key methods available in the this plugin:
| `previous()` | Navigates to the previous item or restarts the current item based on playback position and threshold. |
| `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. |
| `toggleRepeat(force?)` | Cycles through the repeat mode of the player, or sets it to the specified 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
Expand All @@ -82,7 +97,7 @@ behavior of the plugin. Here are the available options:
| Option | Type | Default | Description |
|-------------------------------|---------|---------|---------------------------------------------------------------------------------------------|
| `playlist` | Array | `[]` | An array of playlist items to be initially loaded into the player. |
| `repeat` | Boolean | `false` | If true, the playlist will start over automatically after the last item ends. |
| `repeat` | Number | 0 | Set the repeat mode of the playlist: 0 - No Repeat, 1 - Repeat All, 2 - Repeat one. |
| `autoadvance` | Boolean | `false` | If enabled, the player will automatically move to the next item after the current one ends. |
| `previousNavigationThreshold` | Number | 3 | Threshold in seconds for determining the behavior when navigating to the previous item. |

Expand All @@ -91,11 +106,11 @@ behavior of the plugin. Here are the available options:
After initializing the plugin, you can modify or read these properties to control playlist behavior
dynamically:

| Property | Type | Description |
|-------------------------------|---------|-----------------------------------------------------------------------------------------------------------------------------------------------|
| `repeat` | Boolean | Enables or disables repeating the playlist once the last item has played. Changes take effect immediately and apply to subsequent operations. |
| `autoadvance` | Boolean | Toggles automatic advancement to the next item when the current item ends. |
| `previousNavigationThreshold` | Number | Threshold in seconds for determining the behavior when navigating to the previous item. |
| Property | Type | Description |
|-------------------------------|---------|----------------------------------------------------------------------------------------------|
| `repeat` | Number | Changes the repeat mode of the playlist: 0 - No Repeat, 1 - Repeat All, 2 - Repeat one. . |
| `autoadvance` | Boolean | Toggles automatic advancement to the next item when the current item ends. |
| `previousNavigationThreshold` | Number | Threshold in seconds for determining the behavior when navigating to the previous item. |

The following properties are read-only:

Expand All @@ -105,6 +120,16 @@ The following properties are read-only:
| `currentItem` | Object | Retrieves the currently playing item. |
| `items` | Array | Retrieves all items in the playlist. Modifications to the returned array will not affect the internal state of the playlist. |

#### Constants

The following table outlines the key constants available in this plugin:

| Constant | Description |
|-------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `RepeatMode.NO_REPEAT` | Disables repeat mode. |
| `RepeatMode.REPEAT_ALL` | Loops the entire playlist. Once the last element of the playlist ends, the next element will be the first one. This mode only works forwards, i.e., when advancing to the next element. |
| `RepeatMode.REPEAT_ONE` | Loops the currently playing item in the playlist. |

#### Events

The following event is emitted by the playlist plugin:
Expand Down Expand Up @@ -158,7 +183,7 @@ import '@srgssr/pillarbox-playlist';
import '@srgssr/pillarbox-playlist/ui';

const player = new Pillarbox('my-player', {
plugins: {
plugins: {
// Include the playlist plugin
pillarboxPlaylist: true,
// Include the playlist UI plugin
Expand Down
3 changes: 2 additions & 1 deletion packages/pillarbox-playlist/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import { default as pillarbox, SrgSsr} from '@srgssr/pillarbox-web';
import './src/pillarbox-playlist.js';
import './src/pillarbox-playlist-ui.js';
import { RepeatMode } from './src/pillarbox-playlist.js';

// Handle URL parameters
const searchParams = new URLSearchParams(location.search);
Expand All @@ -40,7 +41,7 @@
autoplay: true,
srgOptions: { dataProviderHost: ilHost },
plugins: {
pillarboxPlaylist: { repeat: true },
pillarboxPlaylist: { autoadvance: true, repeat: RepeatMode.REPEAT_ALL },
pillarboxPlaylistUI: true
}
});
Expand Down
18 changes: 18 additions & 0 deletions packages/pillarbox-playlist/scss/pillarbox-playlist.scss
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,22 @@
border-radius: 0.5em;
}
}

.pbw-repeat-one {
position: relative;
}

.pbw-repeat-one::after {
position: absolute;
right: 0.5em;
bottom: 0.5em;
display: flex;
align-items: center;
justify-content: center;
width: 1em;
height: 1em;
font-size: 1em;
border-radius: 50%;
content: "1";
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import videojs from 'video.js';
import { RepeatMode } from '../../pillarbox-playlist.js';

/**
* @ignore
Expand All @@ -14,6 +15,7 @@ class PillarboxPlaylistRepeatButton extends Button {
constructor(player, options) {
options = videojs.mergeOptions({ controlText: 'Repeat' }, options);
super(player, options);
this.controlText(this.repeatModeAsString());
this.setIcon('repeat');
}

Expand All @@ -30,6 +32,17 @@ class PillarboxPlaylistRepeatButton extends Button {
this.$('.vjs-icon-placeholder').classList.toggle(`vjs-icon-repeat`, true);
}

repeatModeAsString() {
switch (this.playlist().repeat) {
case RepeatMode.NO_REPEAT:
return 'No Repeat';
case RepeatMode.REPEAT_ALL:
return 'Repeat All';
case RepeatMode.REPEAT_ONE:
return 'Repeat One';
}
}

/**
* Builds the CSS class string for the button.
*
Expand All @@ -47,7 +60,10 @@ class PillarboxPlaylistRepeatButton extends Button {
handleClick(event) {
super.handleClick(event);
this.playlist().toggleRepeat();
this.toggleClass('vjs-selected', this.playlist().repeat);
this.toggleClass('vjs-selected', !this.playlist().isNoRepeatMode());
this.toggleClass('pbw-repeat-one', this.playlist().isRepeatOneMode());
this.controlText(this.repeatModeAsString());
this.setAttribute('aria-pressed', !this.playlist().isNoRepeatMode());
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import videojs from 'video.js';
import './modal/pillarbox-playlist-modal.js';
import '../lang/index.js';

/**
* @ignore
Expand Down
6 changes: 4 additions & 2 deletions packages/pillarbox-playlist/src/lang/de.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
{
"Next Item": "Nächstes Element",
"Next Item": "Nächstes element",
"No Repeat": "Keine Wiederholung",
"Playlist": "Wiedergabeliste",
"Previous Item": "Vorheriges Element",
"Repeat": "Wiederholen",
"Repeat All": "Alle Wiederholen",
"Repeat One": "Einzelnes Wiederholen",
"Shuffle": "Mischen"
}
4 changes: 3 additions & 1 deletion packages/pillarbox-playlist/src/lang/en.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
{
"Next Item": "Next Item",
"No Repeat": "No Repeat",
"Playlist": "Playlist",
"Previous Item": "Previous Item",
"Repeat": "Repeat",
"Repeat All": "Repeat All",
"Repeat One": "Repeat One",
"Shuffle": "Shuffle"
}
4 changes: 3 additions & 1 deletion packages/pillarbox-playlist/src/lang/fr.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
{
"Next Item": "Élément suivant",
"No Repeat": "Pas de Répétition",
"Playlist": "Liste de lecture",
"Previous Item": "Élément précédent",
"Repeat": "Répéter",
"Repeat All": "Répéter Tout",
"Repeat One": "Répéter Un",
"Shuffle": "Mélanger"
}
4 changes: 3 additions & 1 deletion packages/pillarbox-playlist/src/lang/it.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
{
"Next Item": "Elemento successivo",
"No Repeat": "Nessuna Ripetizione",
"Playlist": "Playlist",
"Previous Item": "Elemento precedente",
"Repeat": "Ripeti",
"Repeat All": "Ripeti Tutto",
"Repeat One": "Ripeti Uno",
"Shuffle": "Mescola"
}
4 changes: 3 additions & 1 deletion packages/pillarbox-playlist/src/lang/rm.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
{
"Next Item": "Element proxim",
"No Repeat": "Nagins Repeter",
"Playlist": "Glista da reprodukziun",
"Previous Item": "Element precedent",
"Repeat": "Repeter",
"Repeat All": "Repeter Tut",
"Repeat One": "Repeter In",
"Shuffle": "Maschadar"
}
2 changes: 2 additions & 0 deletions packages/pillarbox-playlist/src/pillarbox-playlist-ui.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import videojs from 'video.js';
import './components/pillarbox-playlist-button.js';
import './components/modal/pillarbox-playlist-modal.js';
import './lang';

/**
* @ignore
Expand Down
78 changes: 65 additions & 13 deletions packages/pillarbox-playlist/src/pillarbox-playlist.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,32 @@ import videojs from 'video.js';
const Plugin = videojs.getPlugin('plugin');
const log = videojs.log.createLogger('pillarbox-playlist');

/**
* Defines the available repeat modes for the playlist.
*
* @enum {number}
*/
export const RepeatMode = {
/**
* Disables repeat mode.
*/
NO_REPEAT: 0,
/**
* Loops the entire playlist. Once the last element of the playlist ends the n
* ext element will be the first one. This mode only works forwards,
* i.e. when advancing to the next element.
*/
REPEAT_ALL: 1,
/**
* Loops the currently playing item in the playlist.
*/
REPEAT_ONE: 2,
};

/**
* Represents a Plugin that allows control over a playlist.
*/
class PillarboxPlaylist extends Plugin {
export class PillarboxPlaylist extends Plugin {
/**
* The items in the playlist.
*
Expand Down Expand Up @@ -45,22 +67,48 @@ class PillarboxPlaylist extends Plugin {
previousNavigationThreshold = 3;

/**
* Whether the repeat is enabled or not. If repeat is enabled once the last
* element of the playlist ends the next element will be the first one. This
* mode only works forwards, i.e. when advancing to the next element.
* The current repeat mode of the player. By default, repeat is disabled.
*
* @type boolean
* @type {RepeatMode}
*/
repeat = false;
repeat = RepeatMode.NO_REPEAT;

/**
* Toggles the repeat mode of the player to the opposite of its current state.
*
* @param {boolean} [force] Optional. If provided, sets the repeat mode to the specified boolean value (true or false).
* If omitted, the repeat mode will toggle to the opposite of its current state.
* @param {RepeatMode} [force] Optional.
* If provided, sets the repeat mode to the specified state.
* If omitted, the repeat mode will cycle in order through: no repeat, repeat all and repeat one.
*/
toggleRepeat(force = undefined) {
this.repeat = force ?? !this.repeat;
this.repeat = force ?? (this.repeat + 1) % 3;
}

/**
* Checks if the repeat mode is set to {@link RepeatMode.REPEAT_ONE}.
*
* @returns {boolean} True if the repeat mode is {@link RepeatMode.REPEAT_ONE}, false otherwise.
*/
isRepeatOneMode() {
return this.repeat === RepeatMode.REPEAT_ONE;
}

/**
* Checks if the repeat mode is set to {@link RepeatMode.REPEAT_ALL}.
*
* @returns {boolean} True if the repeat mode is {@link RepeatMode.REPEAT_ALL}, false otherwise.
*/
isRepeatAllMode() {
return this.repeat === RepeatMode.REPEAT_ALL;
}

/**
* Checks if the repeat mode is set to {@link RepeatMode.NO_REPEAT}.
*
* @returns {boolean} True if the repeat mode is {@link RepeatMode.NO_REPEAT}, false otherwise.
*/
isNoRepeatMode() {
return this.repeat === RepeatMode.NO_REPEAT;
}

/**
Expand Down Expand Up @@ -110,7 +158,7 @@ class PillarboxPlaylist extends Plugin {
}

this.autoadvance = Boolean(options.autoadvance);
this.repeat = Boolean(options.repeat);
this.repeat = options.repeat ?? this.repeat;
this.previousNavigationThreshold =
Number.isFinite(options.previousNavigationThreshold) ?
options.previousNavigationThreshold :
Expand Down Expand Up @@ -312,7 +360,7 @@ class PillarboxPlaylist extends Plugin {
return;
}

if (this.repeat) this.select(0);
if (this.repeat === RepeatMode.REPEAT_ALL) this.select(0);
}

/**
Expand Down Expand Up @@ -369,6 +417,12 @@ class PillarboxPlaylist extends Plugin {
* will be played, otherwise nothing happens.
*/
handleEnded() {
if (this.repeat === RepeatMode.REPEAT_ONE) {
this.player.play().then(() => {});

return;
}

if (!this.autoadvance) {
return;
}
Expand Down Expand Up @@ -422,8 +476,6 @@ PillarboxPlaylist.prototype.options_ = {

videojs.registerPlugin('pillarboxPlaylist', PillarboxPlaylist);

export default PillarboxPlaylist;

/**
* Represents a single item in the playlist.
*
Expand Down
Loading

0 comments on commit 7005629

Please sign in to comment.