diff --git a/README.md b/README.md index 2b710aed..3de57eaa 100644 --- a/README.md +++ b/README.md @@ -269,6 +269,7 @@ new Vlitejs('#player', { | `progress` | Sent periodically to inform interested parties of progress downloading the media. | | `timeupdate` | Sent when the `currentTime` of the media has changed | | `volumechange` | Sent when audio volume changes | +| `sourcechange` | Sent when source changes | | `enterfullscreen`¹ | Sent when the video switches to fullscreen mode | | `exitfullscreen`¹ | Sent when the video exits fullscreen mode | | `ended` | Sent when playback completes | @@ -303,6 +304,7 @@ The player instance exposes the following methods, accessible when the player is | `getDuration()` | - | `Promise` | Get the duration | | `mute()` | - | - | Mute the volume | | `unMute()` | - | - | Unmute the volume | +| `setSource(source)` | `String` | - | Set the new media source | | `seekTo(time)` | `Number` | - | Seek to a current time in seconds | | `requestFullscreen()` | - | - | Request the fullscreen | | `exitFullscreen()` | - | - | Exit the fullscreen | diff --git a/biome.json b/biome.json index f85c032a..0938baf7 100644 --- a/biome.json +++ b/biome.json @@ -7,7 +7,7 @@ }, "files": { "ignoreUnknown": false, - "includes": ["**"] + "includes": ["**", "!examples/**/*.html"] }, "formatter": { "enabled": true, diff --git a/examples/audio/config.js b/examples/audio/config.js index e35e1f8f..6dbdd7d6 100644 --- a/examples/audio/config.js +++ b/examples/audio/config.js @@ -2,6 +2,7 @@ import '../../dist/vlite.css' import '../../dist/plugins/volume-bar.css' import VlitejsVolumeBar from '../../dist/plugins/volume-bar.js' import Vlitejs from '../../dist/vlite.js' +import { changeSourceEvent } from '../shared/utils.js' Vlitejs.registerPlugin('volume-bar', VlitejsVolumeBar) @@ -24,6 +25,9 @@ new Vlitejs('#player', { player.on('progress', () => console.log('progress')) player.on('timeupdate', () => console.log('timeupdate')) player.on('volumechange', () => console.log('volumechange')) + player.on('sourcechange', () => console.log('sourcechange')) player.on('ended', () => console.log('ended')) + + changeSourceEvent({ player }) } }) diff --git a/examples/audio/index.html b/examples/audio/index.html index 69e6eaa2..67f47549 100644 --- a/examples/audio/index.html +++ b/examples/audio/index.html @@ -8,5 +8,6 @@ + <%= require('html-loader!../shared/change-source.html').default %> diff --git a/examples/dailymotion/config.js b/examples/dailymotion/config.js index ce17d844..c9284206 100644 --- a/examples/dailymotion/config.js +++ b/examples/dailymotion/config.js @@ -3,6 +3,7 @@ import '../../dist/plugins/volume-bar.css' import VlitejsVolumeBar from '../../dist/plugins/volume-bar.js' import VlitejsDailymotion from '../../dist/providers/dailymotion.js' import Vlitejs from '../../dist/vlite.js' +import { changeSourceEvent } from '../shared/utils.js' Vlitejs.registerProvider('dailymotion', VlitejsDailymotion, { playerId: 'x9scg' @@ -35,8 +36,11 @@ new Vlitejs('#player', { player.on('progress', () => console.log('progress')) player.on('timeupdate', () => console.log('timeupdate')) player.on('volumechange', () => console.log('volumechange')) + player.on('sourcechange', () => console.log('sourcechange')) player.on('enterfullscreen', () => console.log('enterfullscreen')) player.on('exitfullscreen', () => console.log('exitfullscreen')) player.on('ended', () => console.log('ended')) + + changeSourceEvent({ player }) } }) diff --git a/examples/dailymotion/index.html b/examples/dailymotion/index.html index 1781a5e4..df0f318a 100644 --- a/examples/dailymotion/index.html +++ b/examples/dailymotion/index.html @@ -1,4 +1,4 @@ - + @@ -8,5 +8,6 @@
+ <%= require('html-loader!../shared/change-source.html').default %> diff --git a/examples/html5/config.js b/examples/html5/config.js index 03bd60b2..23a81e3b 100644 --- a/examples/html5/config.js +++ b/examples/html5/config.js @@ -11,6 +11,7 @@ import VlitejsPip from '../../dist/plugins/pip.js' import VlitejsSubtitle from '../../dist/plugins/subtitle.js' import VlitejsVolumeBar from '../../dist/plugins/volume-bar.js' import Vlitejs from '../../dist/vlite.js' +import { changeSourceEvent } from '../shared/utils.js' Vlitejs.registerPlugin('subtitle', VlitejsSubtitle) Vlitejs.registerPlugin('pip', VlitejsPip) @@ -55,6 +56,7 @@ new Vlitejs('#player', { player.on('progress', () => console.log('progress')) player.on('timeupdate', () => console.log('timeupdate')) player.on('volumechange', () => console.log('volumechange')) + player.on('sourcechange', () => console.log('sourcechange')) player.on('enterfullscreen', () => console.log('enterfullscreen')) player.on('exitfullscreen', () => console.log('exitfullscreen')) player.on('enterpip', () => console.log('enterpip')) @@ -64,5 +66,7 @@ new Vlitejs('#player', { player.on('ended', () => console.log('ended')) player.on('castsessionstarted', () => console.log('castsessionstarted')) player.on('castsessionended', () => console.log('castsessionended')) + + changeSourceEvent({ player }) } }) diff --git a/examples/html5/index.html b/examples/html5/index.html index 245a1abc..c8e24ab7 100644 --- a/examples/html5/index.html +++ b/examples/html5/index.html @@ -11,5 +11,6 @@ + <%= require('html-loader!../shared/change-source.html').default %> diff --git a/examples/shared/change-source.html b/examples/shared/change-source.html new file mode 100644 index 00000000..f781d775 --- /dev/null +++ b/examples/shared/change-source.html @@ -0,0 +1,8 @@ +
+
+
+ Change media source + +
+ +
diff --git a/examples/shared/utils.js b/examples/shared/utils.js new file mode 100644 index 00000000..dbdde31b --- /dev/null +++ b/examples/shared/utils.js @@ -0,0 +1,8 @@ +export function changeSourceEvent({ player }) { + document.querySelector('.change-source').addEventListener('submit', (e) => { + e.preventDefault() + + const formData = new FormData(e.target) + player.setSource(formData.get('url')) + }) +} diff --git a/examples/vimeo/config.js b/examples/vimeo/config.js index a7e9ae49..0f335065 100644 --- a/examples/vimeo/config.js +++ b/examples/vimeo/config.js @@ -3,6 +3,7 @@ import '../../dist/plugins/volume-bar.css' import VlitejsVolumeBar from '../../dist/plugins/volume-bar.js' import VlitejsVimeo from '../../dist/providers/vimeo.js' import Vlitejs from '../../dist/vlite.js' +import { changeSourceEvent } from '../shared/utils.js' Vlitejs.registerProvider('vimeo', VlitejsVimeo) Vlitejs.registerPlugin('volume-bar', VlitejsVolumeBar) @@ -34,8 +35,11 @@ new Vlitejs('#player', { player.on('progress', () => console.log('progress')) player.on('timeupdate', () => console.log('timeupdate')) player.on('volumechange', () => console.log('volumechange')) + player.on('sourcechange', () => console.log('sourcechange')) player.on('enterfullscreen', () => console.log('enterfullscreen')) player.on('exitfullscreen', () => console.log('exitfullscreen')) player.on('ended', () => console.log('ended')) + + changeSourceEvent({ player }) } }) diff --git a/examples/vimeo/index.html b/examples/vimeo/index.html index b2234d98..f8e03374 100644 --- a/examples/vimeo/index.html +++ b/examples/vimeo/index.html @@ -8,5 +8,6 @@
+ <%= require('html-loader!../shared/change-source.html').default %> diff --git a/examples/youtube/config.js b/examples/youtube/config.js index ec8d9c84..720fbc9d 100644 --- a/examples/youtube/config.js +++ b/examples/youtube/config.js @@ -3,6 +3,7 @@ import '../../dist/plugins/volume-bar.css' import VlitejsVolumeBar from '../../dist/plugins/volume-bar.js' import VlitejsYoutube from '../../dist/providers/youtube.js' import Vlitejs from '../../dist/vlite.js' +import { changeSourceEvent } from '../shared/utils.js' Vlitejs.registerProvider('youtube', VlitejsYoutube) Vlitejs.registerPlugin('volume-bar', VlitejsVolumeBar) @@ -34,6 +35,7 @@ new Vlitejs('#player', { player.on('progress', () => console.log('progress')) player.on('timeupdate', () => console.log('timeupdate')) player.on('volumechange', () => console.log('volumechange')) + player.on('sourcechange', () => console.log('sourcechange')) player.on('enterfullscreen', () => console.log('enterfullscreen')) player.on('exitfullscreen', () => console.log('exitfullscreen')) player.on('enterpip', () => console.log('enterpip')) @@ -41,5 +43,7 @@ new Vlitejs('#player', { player.on('trackenabled', () => console.log('trackenabled')) player.on('trackdisabled', () => console.log('trackdisabled')) player.on('ended', () => console.log('ended')) + + changeSourceEvent({ player }) } }) diff --git a/examples/youtube/index.html b/examples/youtube/index.html index eeb97eea..557c8796 100644 --- a/examples/youtube/index.html +++ b/examples/youtube/index.html @@ -7,6 +7,7 @@ -
+
+ <%= require('html-loader!../shared/change-source.html').default %> diff --git a/package-lock.json b/package-lock.json index 95878161..168d28da 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,6 +23,7 @@ "babel-loader": "^10.0.0", "css-loader": "^7.1.2", "css-minimizer-webpack-plugin": "^7.0.2", + "html-loader": "^5.1.0", "html-webpack-plugin": "^5.6.4", "mini-css-extract-plugin": "^2.9.4", "postcss": "^8.5.6", @@ -5375,6 +5376,72 @@ "safe-buffer": "~5.1.0" } }, + "node_modules/html-loader": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/html-loader/-/html-loader-5.1.0.tgz", + "integrity": "sha512-Jb3xwDbsm0W3qlXrCZwcYqYGnYz55hb6aoKQTlzyZPXsPpi6tHXzAfqalecglMQgNvtEfxrCQPaKT90Irt5XDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "html-minifier-terser": "^7.2.0", + "parse5": "^7.1.2" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/html-loader/node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/html-loader/node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/html-loader/node_modules/html-minifier-terser": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-7.2.0.tgz", + "integrity": "sha512-tXgn3QfqPIpGl9o+K5tpcj3/MN4SfLtsx2GWwBC3SSd0tXQGyF3gsSqad8loJgKZGM3ZxbYDd5yhiBIdWpmvLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "camel-case": "^4.1.2", + "clean-css": "~5.3.2", + "commander": "^10.0.0", + "entities": "^4.4.0", + "param-case": "^3.0.4", + "relateurl": "^0.2.7", + "terser": "^5.15.1" + }, + "bin": { + "html-minifier-terser": "cli.js" + }, + "engines": { + "node": "^14.13.1 || >=16.0.0" + } + }, "node_modules/html-minifier-terser": { "version": "6.1.0", "dev": true, @@ -6602,6 +6669,32 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5/node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/parseurl": { "version": "1.3.3", "dev": true, diff --git a/package.json b/package.json index 953e43f9..831c50c4 100644 --- a/package.json +++ b/package.json @@ -93,6 +93,7 @@ "babel-loader": "^10.0.0", "css-loader": "^7.1.2", "css-minimizer-webpack-plugin": "^7.0.2", + "html-loader": "^5.1.0", "html-webpack-plugin": "^5.6.4", "mini-css-extract-plugin": "^2.9.4", "postcss": "^8.5.6", diff --git a/src/core/player.ts b/src/core/player.ts index 567eddf1..3c428d57 100644 --- a/src/core/player.ts +++ b/src/core/player.ts @@ -168,6 +168,13 @@ export default abstract class Player { */ public abstract methodUnMute(): void + /** + * methodSetSource + * Extends by the provider + * @abstract + */ + public abstract methodSetSource(videoId: string): void + /** * On the player is ready */ @@ -464,6 +471,23 @@ export default abstract class Player { this.dispatchEvent('volumechange') } + /** + * Set the new source of the player + * @param videoId Video ID + */ + setSource(videoId: string) { + // Prevent onMediaEnded to play + this.options.loop = false + + this.pause() + this.onMediaEnded() + this.methodSetSource(videoId) + this.controlBar.onReady() + this.play() + + this.dispatchEvent('sourcechange') + } + /** * Update the current time of the media element * @param newTime New current time of the media element diff --git a/src/providers/dailymotion/dailymotion.ts b/src/providers/dailymotion/dailymotion.ts index 2e6bcc98..85a755fc 100644 --- a/src/providers/dailymotion/dailymotion.ts +++ b/src/providers/dailymotion/dailymotion.ts @@ -163,6 +163,14 @@ export default function DailymotionProvider(Player: any, options: interfaceProvi }) } + /** + * Change source method of the player + * {@link https://developers.dailymotion.com/sdk/player-sdk/web/#player-methods} + */ + methodSetSource(id: string) { + this.instance.loadContent({ video: id }) + } + /** * Play method of the player */ diff --git a/src/providers/html5/html5.ts b/src/providers/html5/html5.ts index 789eb526..b1b772f1 100644 --- a/src/providers/html5/html5.ts +++ b/src/providers/html5/html5.ts @@ -17,12 +17,12 @@ export default function Html5Provider(Player: any) { super(props) this.events = [ - { type: 'timeupdate', listener: super.onTimeUpdate }, - { type: 'ended', listener: super.onMediaEnded }, - { type: 'playing', listener: this.onPlaying }, - { type: 'waiting', listener: this.onWaiting }, - { type: 'seeking', listener: this.onSeeking }, - { type: 'seeked', listener: this.onSeeked } + { type: 'timeupdate', listener: super.onTimeUpdate.bind(this) }, + { type: 'ended', listener: super.onMediaEnded.bind(this) }, + { type: 'playing', listener: this.onPlaying.bind(this) }, + { type: 'waiting', listener: this.onWaiting.bind(this) }, + { type: 'seeking', listener: this.onSeeking.bind(this) }, + { type: 'seeked', listener: this.onSeeked.bind(this) } ] } @@ -137,6 +137,17 @@ export default function Html5Provider(Player: any) { this.media.removeAttribute('muted') } + /** + * Set the media source + * @param source New media source URL + */ + methodSetSource(source: string) { + this.media.src = source + this.media.load() + this.removeSpecificEvents() + this.init() + } + /** * Set the new current time for the player * @param Current time video diff --git a/src/providers/vimeo/vimeo.ts b/src/providers/vimeo/vimeo.ts index 53c84287..27f53422 100644 --- a/src/providers/vimeo/vimeo.ts +++ b/src/providers/vimeo/vimeo.ts @@ -147,6 +147,14 @@ export default function VimeoProvider(Player: any) { }) } + /** + * Change source method of the player + * {@link https://developer.vimeo.com/player/sdk/reference#load-a-new-video-into-a-player} + */ + methodSetSource(id: string) { + this.instance.loadVideo(id) + } + /** * Play method of the player */ diff --git a/src/providers/youtube/youtube.ts b/src/providers/youtube/youtube.ts index cf152fa0..03f512ad 100644 --- a/src/providers/youtube/youtube.ts +++ b/src/providers/youtube/youtube.ts @@ -178,6 +178,14 @@ export default function YoutubeProvider(Player: any) { return new window.Promise((resolve) => resolve(this.instance.getDuration())) } + /** + * Change source method of the player + * {@link https://developers.google.com/youtube/iframe_api_reference#loadVideoById} + */ + methodSetSource(id: string) { + this.instance.loadVideoById(id) + } + /** * Play method of the player */