mirror of
https://github.com/videojs/video.js.git
synced 2025-01-02 06:32:07 +02:00
feat: Add methods to add and remove <source> elements (#8886)
## Description It is useful to have methods for appending and removing `<source>` elements to the `<video>` element, as they are sometimes required to enable certain playback features, for example, using [Airplay with MSE](https://webkit.org/blog/15036/how-to-use-media-source-extensions-with-airplay). ## Specific Changes proposed Add new methods-- `addSourceElement()` and `removeSourceElement()` to the player and tech. The former will take a source object and create and append a new `<source>` element to the `<video>` element, and the latter will take a source url and remove any `<source>` element with a matching `src`. ## Requirements Checklist - [ ] Feature implemented / Bug fixed - [ ] If necessary, more likely in a feature request than a bug fix - [ ] Change has been verified in an actual browser (Chrome, Firefox, IE) - [ ] Unit Tests updated or fixed - [ ] Docs/guides updated - [ ] Example created ([starter template on JSBin](https://codepen.io/gkatsev/pen/GwZegv?editors=1000#0)) - [ ] Has no DOM changes which impact accessiblilty or trigger warnings (e.g. Chrome issues tab) - [ ] Has no changes to JSDoc which cause `npm run docs:api` to error - [ ] Reviewed by Two Core Contributors
This commit is contained in:
parent
077077b00a
commit
eddda97eeb
@ -3710,6 +3710,43 @@ class Player extends Component {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a <source> element to the <video> element.
|
||||||
|
*
|
||||||
|
* @param {string} srcUrl
|
||||||
|
* The URL of the video source.
|
||||||
|
*
|
||||||
|
* @param {string} [mimeType]
|
||||||
|
* The MIME type of the video source. Optional but recommended.
|
||||||
|
*
|
||||||
|
* @return {boolean}
|
||||||
|
* Returns true if the source element was successfully added, false otherwise.
|
||||||
|
*/
|
||||||
|
addSourceElement(srcUrl, mimeType) {
|
||||||
|
if (!this.tech_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.tech_.addSourceElement(srcUrl, mimeType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a <source> element from the <video> element by its URL.
|
||||||
|
*
|
||||||
|
* @param {string} srcUrl
|
||||||
|
* The URL of the source to remove.
|
||||||
|
*
|
||||||
|
* @return {boolean}
|
||||||
|
* Returns true if the source element was successfully removed, false otherwise.
|
||||||
|
*/
|
||||||
|
removeSourceElement(srcUrl) {
|
||||||
|
if (!this.tech_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.tech_.removeSourceElement(srcUrl);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Begin loading the src data.
|
* Begin loading the src data.
|
||||||
*/
|
*/
|
||||||
|
@ -783,6 +783,67 @@ class Html5 extends Tech {
|
|||||||
this.setSrc(src);
|
this.setSrc(src);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a <source> element to the <video> element.
|
||||||
|
*
|
||||||
|
* @param {string} srcUrl
|
||||||
|
* The URL of the video source.
|
||||||
|
*
|
||||||
|
* @param {string} [mimeType]
|
||||||
|
* The MIME type of the video source. Optional but recommended.
|
||||||
|
*
|
||||||
|
* @return {boolean}
|
||||||
|
* Returns true if the source element was successfully added, false otherwise.
|
||||||
|
*/
|
||||||
|
addSourceElement(srcUrl, mimeType) {
|
||||||
|
if (!srcUrl) {
|
||||||
|
log.error('Invalid source URL.');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const sourceAttributes = { src: srcUrl };
|
||||||
|
|
||||||
|
if (mimeType) {
|
||||||
|
sourceAttributes.type = mimeType;
|
||||||
|
}
|
||||||
|
|
||||||
|
const sourceElement = Dom.createEl('source', {}, sourceAttributes);
|
||||||
|
|
||||||
|
this.el_.appendChild(sourceElement);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a <source> element from the <video> element by its URL.
|
||||||
|
*
|
||||||
|
* @param {string} srcUrl
|
||||||
|
* The URL of the source to remove.
|
||||||
|
*
|
||||||
|
* @return {boolean}
|
||||||
|
* Returns true if the source element was successfully removed, false otherwise.
|
||||||
|
*/
|
||||||
|
removeSourceElement(srcUrl) {
|
||||||
|
if (!srcUrl) {
|
||||||
|
log.error('Source URL is required to remove the source element.');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const sourceElements = this.el_.querySelectorAll('source');
|
||||||
|
|
||||||
|
for (const sourceElement of sourceElements) {
|
||||||
|
if (sourceElement.src === srcUrl) {
|
||||||
|
this.el_.removeChild(sourceElement);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.warn(`No matching source element found with src: ${srcUrl}`);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reset the tech by removing all sources and then calling
|
* Reset the tech by removing all sources and then calling
|
||||||
* {@link Html5.resetMediaElement}.
|
* {@link Html5.resetMediaElement}.
|
||||||
|
@ -3611,3 +3611,57 @@ QUnit.test('smooth seeking set to true should update the display time components
|
|||||||
player.dispose();
|
player.dispose();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
QUnit.test('addSourceElement calls tech method with correct args', function(assert) {
|
||||||
|
const player = TestHelpers.makePlayer();
|
||||||
|
const addSourceElementSpy = sinon.spy(player.tech_, 'addSourceElement');
|
||||||
|
const srcUrl = 'http://example.com/video.mp4';
|
||||||
|
const mimeType = 'video/mp4';
|
||||||
|
|
||||||
|
player.addSourceElement(srcUrl, mimeType);
|
||||||
|
|
||||||
|
assert.ok(addSourceElementSpy.calledOnce, 'addSourceElement method called');
|
||||||
|
assert.ok(addSourceElementSpy.calledWith(srcUrl, mimeType), 'addSourceElement called with correct arguments');
|
||||||
|
|
||||||
|
addSourceElementSpy.restore();
|
||||||
|
player.dispose();
|
||||||
|
});
|
||||||
|
|
||||||
|
QUnit.test('addSourceElement returns false if no tech', function(assert) {
|
||||||
|
const player = TestHelpers.makePlayer();
|
||||||
|
const srcUrl = 'http://example.com/video.mp4';
|
||||||
|
const mimeType = 'video/mp4';
|
||||||
|
|
||||||
|
player.tech_ = undefined;
|
||||||
|
|
||||||
|
const added = player.addSourceElement(srcUrl, mimeType);
|
||||||
|
|
||||||
|
assert.notOk(added, 'Returned false');
|
||||||
|
player.dispose();
|
||||||
|
});
|
||||||
|
|
||||||
|
QUnit.test('removeSourceElement calls tech method with correct args', function(assert) {
|
||||||
|
const player = TestHelpers.makePlayer();
|
||||||
|
const removeSourceElementSpy = sinon.spy(player.tech_, 'removeSourceElement');
|
||||||
|
const srcUrl = 'http://example.com/video.mp4';
|
||||||
|
|
||||||
|
player.removeSourceElement(srcUrl);
|
||||||
|
|
||||||
|
assert.ok(removeSourceElementSpy.calledOnce, 'removeSourceElement method called');
|
||||||
|
assert.ok(removeSourceElementSpy.calledWith(srcUrl), 'removeSourceElement called with correct arguments');
|
||||||
|
|
||||||
|
removeSourceElementSpy.restore();
|
||||||
|
player.dispose();
|
||||||
|
});
|
||||||
|
|
||||||
|
QUnit.test('removeSourceElement returns false if no tech', function(assert) {
|
||||||
|
const player = TestHelpers.makePlayer();
|
||||||
|
const srcUrl = 'http://example.com/video.mp4';
|
||||||
|
const mimeType = 'video/mp4';
|
||||||
|
|
||||||
|
player.tech_ = undefined;
|
||||||
|
|
||||||
|
const removed = player.removeSourceElement(srcUrl, mimeType);
|
||||||
|
|
||||||
|
assert.notOk(removed, 'Returned false');
|
||||||
|
player.dispose();
|
||||||
|
});
|
||||||
|
@ -905,3 +905,79 @@ QUnit.test('supportsFullScreen is always with `webkitEnterFullScreen`', function
|
|||||||
|
|
||||||
tech.el_ = oldEl;
|
tech.el_ = oldEl;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
QUnit.test('addSourceElement adds a valid source element with srcUrl and mimeType', function(assert) {
|
||||||
|
const videoEl = document.createElement('video');
|
||||||
|
|
||||||
|
tech.el_ = videoEl;
|
||||||
|
|
||||||
|
const srcUrl = 'http://example.com/video.mp4';
|
||||||
|
const mimeType = 'video/mp4';
|
||||||
|
|
||||||
|
const added = tech.addSourceElement(srcUrl, mimeType);
|
||||||
|
const sourceElement = videoEl.querySelector('source');
|
||||||
|
|
||||||
|
assert.ok(added, 'Returned true');
|
||||||
|
assert.ok(sourceElement, 'A source element was added');
|
||||||
|
assert.equal(sourceElement.src, srcUrl, 'Source element has correct src');
|
||||||
|
assert.equal(sourceElement.type, mimeType, 'Source element has correct type');
|
||||||
|
});
|
||||||
|
|
||||||
|
QUnit.test('addSourceElement adds a valid source element without a mimeType', function(assert) {
|
||||||
|
const videoEl = document.createElement('video');
|
||||||
|
|
||||||
|
tech.el_ = videoEl;
|
||||||
|
|
||||||
|
const srcUrl = 'http://example.com/video2.mp4';
|
||||||
|
|
||||||
|
const added = tech.addSourceElement(srcUrl);
|
||||||
|
const sourceElement = videoEl.querySelector('source');
|
||||||
|
|
||||||
|
assert.ok(added, 'Returned true');
|
||||||
|
assert.ok(sourceElement, 'A source element was added even without a type');
|
||||||
|
assert.equal(sourceElement.src, srcUrl, 'Source element has correct src');
|
||||||
|
assert.notOk(sourceElement.type, 'Source element does not have a type attribute');
|
||||||
|
});
|
||||||
|
|
||||||
|
QUnit.test('addSourceElement does not add a source element for invalid source URL', function(assert) {
|
||||||
|
const videoEl = document.createElement('video');
|
||||||
|
|
||||||
|
tech.el_ = videoEl;
|
||||||
|
|
||||||
|
const added = tech.addSourceElement('');
|
||||||
|
const sourceElement = videoEl.querySelector('source');
|
||||||
|
|
||||||
|
assert.notOk(added, 'Returned false');
|
||||||
|
assert.notOk(sourceElement, 'No source element was added for missing src');
|
||||||
|
});
|
||||||
|
|
||||||
|
QUnit.test('removeSourceElement removes a source element by its URL', function(assert) {
|
||||||
|
const videoEl = document.createElement('video');
|
||||||
|
|
||||||
|
tech.el_ = videoEl;
|
||||||
|
|
||||||
|
tech.addSourceElement('http://example.com/video1.mp4');
|
||||||
|
tech.addSourceElement('http://example.com/video2.mp4');
|
||||||
|
|
||||||
|
let sourceElement = videoEl.querySelector('source[src="http://example.com/video1.mp4"]');
|
||||||
|
|
||||||
|
assert.ok(sourceElement, 'Source element for video1.mp4 was added');
|
||||||
|
|
||||||
|
const removed = tech.removeSourceElement('http://example.com/video1.mp4');
|
||||||
|
|
||||||
|
assert.ok(removed, 'Source element was successfully removed');
|
||||||
|
sourceElement = videoEl.querySelector('source[src="http://example.com/video1.mp4"]');
|
||||||
|
assert.notOk(sourceElement, 'Source element for video1.mp4 was removed');
|
||||||
|
});
|
||||||
|
|
||||||
|
QUnit.test('removeSourceElement does not remove a source element if URL does not match', function(assert) {
|
||||||
|
const videoEl = document.createElement('video');
|
||||||
|
|
||||||
|
tech.el_ = videoEl;
|
||||||
|
|
||||||
|
tech.addSourceElement('http://example.com/video.mp4');
|
||||||
|
|
||||||
|
const removed = tech.removeSourceElement('http://example.com/invalid.mp4');
|
||||||
|
|
||||||
|
assert.notOk(removed, 'No source element was removed for non-matching URL');
|
||||||
|
});
|
||||||
|
@ -111,6 +111,8 @@ class TechFaker extends Tech {
|
|||||||
}
|
}
|
||||||
return 'movie.mp4';
|
return 'movie.mp4';
|
||||||
}
|
}
|
||||||
|
addSourceElement() {}
|
||||||
|
removeSourceElement() {}
|
||||||
load() {
|
load() {
|
||||||
}
|
}
|
||||||
currentSrc() {
|
currentSrc() {
|
||||||
|
Loading…
Reference in New Issue
Block a user