- Created complete documentation in docs/ directory - Added PROJECT_OVERVIEW.md with feature highlights and getting started guide - Added ARCHITECTURE.md with system design and technical details - Added SECURITY.md with comprehensive security implementation guide - Added DEVELOPMENT.md with development workflows and best practices - Added DEPLOYMENT.md with production deployment instructions - Added API.md with complete REST API documentation - Added CONTRIBUTING.md with contribution guidelines - Added CHANGELOG.md with version history and migration notes - Reorganized all documentation files into docs/ directory for better organization - Updated README.md with proper documentation links and quick navigation - Enhanced project structure with professional documentation standards
500 lines
11 KiB
JavaScript
500 lines
11 KiB
JavaScript
/**
|
|
* @file Dailymotion.js
|
|
* Dailymotion Media Controller - Wrapper for HTML5 Media API
|
|
*/
|
|
//import videojs from 'video.js';
|
|
|
|
const Component = videojs.getComponent('Component');
|
|
const Tech = videojs.getComponent('Tech');
|
|
|
|
/**
|
|
* Dailymotion Media Controller - Wrapper for HTML5 Media API
|
|
*
|
|
* @param {Object=} options Object of option names and values
|
|
* @param {Function=} ready Ready callback function
|
|
* @extends Tech
|
|
* @class Dailymotion
|
|
*/
|
|
|
|
class Dailymotion extends Tech {
|
|
constructor(options, ready) {
|
|
super(options, ready);
|
|
|
|
this.params = {
|
|
id: this.options_.techId,
|
|
autoplay: (this.player_.options_.autoplay) ? 1 : 0,
|
|
chromeless: (this.player_.options_.dmControls) ? 0 : 1,
|
|
html: 1,
|
|
info: 1,
|
|
logo: 1,
|
|
controls: 'html',
|
|
wmode: 'opaque',
|
|
format: 'json',
|
|
url: options.source.src
|
|
};
|
|
|
|
// If we are not on a server, don't specify the origin (it will crash)
|
|
if (window.location.protocol !== 'file:') {
|
|
this.params.origin = window.location.protocol + '//' + window.location.hostname;
|
|
}
|
|
|
|
this.videoId = this.parseSrc(options.source.src);
|
|
|
|
if (typeof this.videoId !== 'undefined') {
|
|
this.setTimeout(() => {
|
|
this.setPoster('//api.dailymotion.com/video/' + this.videoId + '?fields=poster_url&ads=false');
|
|
}, 100);
|
|
}
|
|
|
|
if (Dailymotion.isApiReady) {
|
|
this.loadApi();
|
|
} else {
|
|
// Add to the queue because the Dailymotion API is not ready
|
|
Dailymotion.apiReadyQueue.push(this);
|
|
}
|
|
|
|
}
|
|
|
|
createEl() {
|
|
|
|
let el = videojs.createEl('iframe', {
|
|
id: this.options_.techId,
|
|
className: 'vjs-tech vjs-tech-dailymotion'
|
|
});
|
|
|
|
let iframeContainer = videojs.createEl('iframe', {
|
|
scrolling: 'no',
|
|
marginWidth: 0,
|
|
marginHeight: 0,
|
|
frameBorder: 0,
|
|
webkitAllowFullScreen: '',
|
|
mozallowfullscreen: '',
|
|
allowFullScreen: '',
|
|
});
|
|
|
|
el.appendChild(iframeContainer);
|
|
|
|
if (/MSIE (\d+\.\d+);/.test(navigator.userAgent) || !/(iPad|iPhone|iPod|Android)/g.test(navigator.userAgent)) {
|
|
let divBlocker = videojs.createEl('div',
|
|
{
|
|
className: 'vjs-iframe-blocker',
|
|
style: 'position:absolute;top:0;left:0;width:100%;height:100%'
|
|
});
|
|
|
|
// In case the blocker is still there and we want to pause
|
|
divBlocker.onclick = function () {
|
|
this.pause();
|
|
}.bind(this);
|
|
|
|
el.appendChild(divBlocker);
|
|
}
|
|
|
|
return el;
|
|
}
|
|
|
|
loadApi() {
|
|
this.dmPlayer = new DM.player(this.options_.techId, {
|
|
video: this.videoId,
|
|
width: this.options_.width,
|
|
height: this.options_.height,
|
|
params: this.params
|
|
});
|
|
|
|
this.setupTriggers();
|
|
|
|
this.dmPlayer.vjsTech = this;
|
|
}
|
|
|
|
parseSrc(src) {
|
|
if (src) {
|
|
// Regex that parse the video ID for any Dailymotion URL
|
|
var regExp = /^.+dailymotion.com\/((video|hub)\/([^_]+))?[^#]*(#video=([^_&]+))?/;
|
|
var match = src.match(regExp);
|
|
|
|
return match ? match[5] || match[3] : null;
|
|
}
|
|
}
|
|
|
|
setupTriggers() {
|
|
this.dmPlayer.listeners = [];
|
|
for (var i = Dailymotion.Events.length - 1; i >= 0; i--) {
|
|
//videojs.on(this.dmPlayer, Dailymotion.Events[i], videojs.bind(this, this.eventHandler));
|
|
var listener = videojs.bind(this, this.eventHandler);
|
|
this.dmPlayer.listeners.push({event: Dailymotion.Events[i], func: listener});
|
|
this.dmPlayer.addEventListener(Dailymotion.Events[i], listener);
|
|
}
|
|
}
|
|
|
|
eventHandler(e) {
|
|
this.onStateChange(e);
|
|
this.trigger(e);
|
|
}
|
|
|
|
onStateChange(event) {
|
|
let state = event.type;
|
|
if (state !== this.lastState) {
|
|
switch (state) {
|
|
case -1:
|
|
break;
|
|
|
|
case 'apiready':
|
|
this.triggerReady();
|
|
break;
|
|
|
|
case 'video_end':
|
|
this.trigger('ended');
|
|
break;
|
|
|
|
case 'ad_play':
|
|
this.trigger('play');
|
|
break;
|
|
|
|
case 'video_start':
|
|
case 'ad_start':
|
|
this.trigger('playing');
|
|
this.trigger('play');
|
|
break;
|
|
|
|
case 'play':
|
|
break;
|
|
|
|
case 'playing':
|
|
break;
|
|
|
|
case 'pause':
|
|
break;
|
|
case 'durationchange':
|
|
break;
|
|
|
|
case 'timeupdate':
|
|
break;
|
|
case 'progress':
|
|
break;
|
|
|
|
}
|
|
|
|
this.lastState = state;
|
|
}
|
|
}
|
|
|
|
poster() {
|
|
return this.poster_;
|
|
}
|
|
|
|
setPoster(poster) {
|
|
this.poster_ = poster;
|
|
this.trigger('posterchange');
|
|
}
|
|
|
|
/**
|
|
* Set video
|
|
*
|
|
* @param {Object=} src Source object
|
|
* @method setSrc
|
|
*/
|
|
src(src) {
|
|
if (typeof src !== 'undefined') {
|
|
this.src_ = this.parseSrc(src);
|
|
this.dmPlayer.load(this.src_);
|
|
}
|
|
return this.src_;
|
|
}
|
|
|
|
currentSrc() {
|
|
return this.src_;
|
|
}
|
|
|
|
play() {
|
|
if (this.isReady_) {
|
|
this.dmPlayer.play();
|
|
} else {
|
|
if (!this.player_.options_.dmControls) {
|
|
// Keep the big play button until it plays for real
|
|
this.player_.bigPlayButton.show();
|
|
}
|
|
}
|
|
}
|
|
|
|
ended() {
|
|
|
|
if (this.isReady_) {
|
|
var stateId = this.dmPlayer.getPlayerState();
|
|
return stateId === 0;
|
|
} else {
|
|
// We will play it when the API will be ready
|
|
return false;
|
|
}
|
|
}
|
|
|
|
pause() {
|
|
this.dmPlayer.pause(!this.dmPlayer.paused);
|
|
}
|
|
|
|
paused() {
|
|
return this.dmPlayer.paused;
|
|
}
|
|
|
|
currentTime() {
|
|
return (this.dmPlayer && this.dmPlayer.currentTime) ? this.dmPlayer.currentTime : 0;
|
|
}
|
|
|
|
setCurrentTime(position) {
|
|
this.dmPlayer.seek(position);
|
|
}
|
|
|
|
duration() {
|
|
return (this.dmPlayer && this.dmPlayer.duration) ? this.dmPlayer.duration : 0;
|
|
}
|
|
|
|
volume() {
|
|
if (isNaN(this.volume_)) {
|
|
this.volume_ = this.dmPlayer.volume;
|
|
}
|
|
|
|
return this.volume_;
|
|
}
|
|
|
|
/**
|
|
* Request to enter fullscreen
|
|
*
|
|
* @method enterFullScreen
|
|
*/
|
|
enterFullScreen() {
|
|
this.dmPlayer.setFullscreen(true);
|
|
}
|
|
|
|
/**
|
|
* Request to exit fullscreen
|
|
*
|
|
* @method exitFullScreen
|
|
*/
|
|
exitFullScreen() {
|
|
this.dmPlayer.setFullscreen(false);
|
|
}
|
|
|
|
|
|
setVolume(percentAsDecimal) {
|
|
if (typeof(percentAsDecimal) !== 'undefined' && percentAsDecimal !== this.volume_) {
|
|
this.dmPlayer.setVolume(percentAsDecimal);
|
|
this.volume_ = percentAsDecimal;
|
|
this.player_.trigger('volumechange');
|
|
}
|
|
}
|
|
|
|
buffered() {
|
|
return [];
|
|
}
|
|
|
|
controls() {
|
|
return false;
|
|
}
|
|
|
|
muted() {
|
|
return this.dmPlayer.muted;
|
|
}
|
|
|
|
setMuted(muted) {
|
|
this.dmPlayer.setMuted(muted);
|
|
|
|
this.setTimeout(function () {
|
|
this.player_.trigger('volumechange');
|
|
});
|
|
}
|
|
|
|
supportsFullScreen() {
|
|
return true;
|
|
}
|
|
|
|
|
|
resetSrc_(callback) {
|
|
callback();
|
|
}
|
|
|
|
dispose() {
|
|
this.resetSrc_(Function.prototype);
|
|
super.dispose(this);
|
|
}
|
|
|
|
}
|
|
|
|
Dailymotion.prototype.options_ = {};
|
|
|
|
Dailymotion.apiReadyQueue = [];
|
|
|
|
Dailymotion.makeQueryString = function (args) {
|
|
let querys = [];
|
|
for (var key in args) {
|
|
if (args.hasOwnProperty(key)) {
|
|
querys.push(encodeURIComponent(key) + '=' + encodeURIComponent(args[key]));
|
|
}
|
|
}
|
|
|
|
return querys.join('&');
|
|
};
|
|
|
|
const injectJs = function () {
|
|
let tag = document.createElement('script');
|
|
tag.src = '//api.dmcdn.net/all.js';
|
|
let firstScriptTag = document.getElementsByTagName('script')[0];
|
|
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
|
|
}
|
|
|
|
/* Dailymotion Support Testing -------------------------------------------------------- */
|
|
|
|
Dailymotion.isSupported = function () {
|
|
return true;
|
|
};
|
|
|
|
// Add Source Handler pattern functions to this tech
|
|
Tech.withSourceHandlers(Dailymotion);
|
|
|
|
/*
|
|
* The default native source handler.
|
|
* This simply passes the source to the video element. Nothing fancy.
|
|
*
|
|
* @param {Object} source The source object
|
|
* @param {Flash} tech The instance of the Flash tech
|
|
*/
|
|
Dailymotion.nativeSourceHandler = {};
|
|
|
|
/**
|
|
* Check if Flash can play the given videotype
|
|
* @param {String} type The mimetype to check
|
|
* @return {String} 'probably', 'maybe', or '' (empty string)
|
|
*/
|
|
Dailymotion.nativeSourceHandler.canPlayType = function (source) {
|
|
|
|
const dashExtRE = /^video\/(dailymotion)/i;
|
|
|
|
if (dashExtRE.test(source)) {
|
|
return 'maybe';
|
|
} else {
|
|
return '';
|
|
}
|
|
|
|
};
|
|
|
|
/*
|
|
* Check Flash can handle the source natively
|
|
*
|
|
* @param {Object} source The source object
|
|
* @return {String} 'probably', 'maybe', or '' (empty string)
|
|
*/
|
|
Dailymotion.nativeSourceHandler.canHandleSource = function (source) {
|
|
|
|
// If a type was provided we should rely on that
|
|
if (source.type) {
|
|
return Dailymotion.nativeSourceHandler.canPlayType(source.type);
|
|
} else if (source.src) {
|
|
return Dailymotion.nativeSourceHandler.canPlayType(source.src);
|
|
}
|
|
|
|
return '';
|
|
};
|
|
|
|
/*
|
|
* Pass the source to the flash object
|
|
* Adaptive source handlers will have more complicated workflows before passing
|
|
* video data to the video element
|
|
*
|
|
* @param {Object} source The source object
|
|
* @param {Flash} tech The instance of the Flash tech
|
|
*/
|
|
Dailymotion.nativeSourceHandler.handleSource = function (source, tech) {
|
|
tech.src(source.src);
|
|
};
|
|
|
|
/*
|
|
* Clean up the source handler when disposing the player or switching sources..
|
|
* (no cleanup is needed when supporting the format natively)
|
|
*/
|
|
Dailymotion.nativeSourceHandler.dispose = function () {
|
|
};
|
|
|
|
// Register the native source handler
|
|
Dailymotion.registerSourceHandler(Dailymotion.nativeSourceHandler);
|
|
|
|
|
|
/*
|
|
* Set the tech's volume control support status
|
|
*
|
|
* @type {Boolean}
|
|
*/
|
|
Dailymotion.prototype['featuresVolumeControl'] = true;
|
|
|
|
/*
|
|
* Set the tech's playbackRate support status
|
|
*
|
|
* @type {Boolean}
|
|
*/
|
|
Dailymotion.prototype['featuresPlaybackRate'] = false;
|
|
|
|
/*
|
|
* Set the tech's status on moving the video element.
|
|
* In iOS, if you move a video element in the DOM, it breaks video playback.
|
|
*
|
|
* @type {Boolean}
|
|
*/
|
|
Dailymotion.prototype['movingMediaElementInDOM'] = false;
|
|
|
|
/*
|
|
* Set the the tech's fullscreen resize support status.
|
|
* HTML video is able to automatically resize when going to fullscreen.
|
|
* (No longer appears to be used. Can probably be removed.)
|
|
*/
|
|
Dailymotion.prototype['featuresFullscreenResize'] = false;
|
|
|
|
/*
|
|
* Set the tech's timeupdate event support status
|
|
* (this disables the manual timeupdate events of the Tech)
|
|
*/
|
|
Dailymotion.prototype['featuresTimeupdateEvents'] = false;
|
|
|
|
/*
|
|
* Set the tech's progress event support status
|
|
* (this disables the manual progress events of the Tech)
|
|
*/
|
|
Dailymotion.prototype['featuresProgressEvents'] = false;
|
|
|
|
/*
|
|
* Sets the tech's status on native text track support
|
|
*
|
|
* @type {Boolean}
|
|
*/
|
|
Dailymotion.prototype['featuresNativeTextTracks'] = true;
|
|
|
|
/*
|
|
* Sets the tech's status on native audio track support
|
|
*
|
|
* @type {Boolean}
|
|
*/
|
|
Dailymotion.prototype['featuresNativeAudioTracks'] = true;
|
|
|
|
/*
|
|
* Sets the tech's status on native video track support
|
|
*
|
|
* @type {Boolean}
|
|
*/
|
|
Dailymotion.prototype['featuresNativeVideoTracks'] = false;
|
|
|
|
Dailymotion.Events = 'apiready,ad_play,ad_start,ad_timeupdate,ad_pause,ad_end,video_start,video_end,play,playing,pause,ended,canplay,canplaythrough,timeupdate,progress,seeking,seeked,volumechange,durationchange,fullscreenchange,error'.split(',');
|
|
|
|
videojs.options.Dailymotion = {};
|
|
|
|
Component.registerComponent('Dailymotion', Dailymotion);
|
|
Tech.registerTech('Dailymotion', Dailymotion);
|
|
|
|
injectJs();
|
|
|
|
// Called when Dailymotion API is ready to be used
|
|
window.dmAsyncInit = function () {
|
|
var dm;
|
|
while ((dm = Dailymotion.apiReadyQueue.shift())) {
|
|
dm.loadApi();
|
|
}
|
|
Dailymotion.apiReadyQueue = [];
|
|
Dailymotion.isApiReady = true;
|
|
};
|
|
|
|
//export default Dailymotion;
|