invidious/assets/js/invidious_iframe_api.js

419 lines
16 KiB
JavaScript
Raw Normal View History

2023-08-29 15:52:58 +00:00
class invidious_embed {
2023-08-18 02:40:50 +00:00
static widgetid = 0;
2023-08-29 15:52:58 +00:00
static eventname_table = { onPlaybackRateChange: 'ratechange', onStateChange: 'statechange', onerror: 'error' };
static available_event_name = ['ended', 'error', 'ratechange', 'volumechange', 'waiting', 'timeupdate', 'loadedmetadata', 'play', 'seeking', 'seeked', 'playerresize', 'pause'];
2023-08-18 02:40:50 +00:00
static api_promise = false;
2023-08-29 15:52:58 +00:00
static invidious_instance = '';
addEventListener(eventname, func) {
if (eventname in invidious_embed.eventname_table) {
2023-08-18 02:40:50 +00:00
this.eventobject[invidious_embed.eventname_table[eventname]].push(func);
}
2023-08-29 15:52:58 +00:00
else {
this.eventobject[eventname].push(func);
2023-08-18 02:40:50 +00:00
}
}
2023-08-29 15:52:58 +00:00
removeEventListner(eventname, func) {
let internal_eventname;
2023-08-29 15:52:58 +00:00
if (eventname in invidious_embed.eventname_table) {
2023-08-18 02:40:50 +00:00
internal_eventname = invidious_embed.eventname_table[eventname];
}
2023-08-29 15:52:58 +00:00
else if (invidious_embed.available_event_name.includes(eventname)) {
2023-08-18 02:40:50 +00:00
internal_eventname = eventname;
}
2023-08-29 15:52:58 +00:00
this.eventobject[internal_eventname] = this.eventobject[internal_eventname].fillter(x => x !== func);
2023-08-18 02:40:50 +00:00
}
2023-08-29 15:52:58 +00:00
Player(element, options) {
2023-08-18 02:40:50 +00:00
this.player_status = -1;
this.error_code = 0;
2023-08-25 15:48:41 +00:00
this.volume = 100;
2023-08-29 15:52:58 +00:00
this.eventobject = { ready: [], ended: [], error: [], ratechange: [], volumechange: [], waiting: [], timeupdate: [], loadedmetadata: [], play: [], seeking: [], seeked: [], playerresize: [], pause: [], statechange: [] };
let replace_elemnt;
if (element === undefined || element === null) {
throw 'Please, pass element id or HTMLElement as first argument';
2023-08-18 02:40:50 +00:00
}
2023-08-29 15:52:58 +00:00
else if (typeof element === 'string') {
2023-08-18 02:40:50 +00:00
replace_elemnt = document.getElementById(element);
}
2023-08-29 15:52:58 +00:00
else {
2023-08-18 02:40:50 +00:00
replace_elemnt = element;
}
2023-08-29 15:52:58 +00:00
let iframe_src = '';
if (options.host !== undefined && options.host !== "") {
2023-08-18 02:40:50 +00:00
iframe_src = new URL(options.host).origin;
}
2023-08-29 15:52:58 +00:00
else if (invidious_embed.invidious_instance !== '') {
iframe_src = invidious_embed.invidious_instance;
}
else {
2023-08-18 02:40:50 +00:00
iframe_src = 'https://vid.puffyan.us';//I set most hot instanse but this may need discuss or change ay to default instanse
}
this.target_origin = iframe_src.slice();
iframe_src += '/embed/';
2023-08-29 15:52:58 +00:00
if (typeof options.videoId === 'string' && options.videoId.length === 11) {
2023-08-18 02:40:50 +00:00
iframe_src += options.videoId;
this.videoId = options.videoId;
}
2023-08-29 15:52:58 +00:00
else {
2023-08-18 02:40:50 +00:00
this.error_code = 2;
this.event_executor('error');
return;
2023-08-18 02:40:50 +00:00
}
2023-08-29 15:52:58 +00:00
let search_params = new URLSearchParams('');
search_params.append('widgetid', invidious_embed.widgetid);
2023-08-18 02:40:50 +00:00
this.widgetid = invidious_embed.widgetid;
invidious_embed.widgetid++;
2023-08-29 15:52:58 +00:00
search_params.append('origin', location.origin);
search_params.append('enablejsapi', '1');
if (typeof options.playerVars === 'object') {
2023-08-18 02:40:50 +00:00
this.option_playerVars = options.playerVars;
2023-08-29 15:52:58 +00:00
for (let x in options.playerVars) {
if (typeof x === 'string' && typeof options.playerVars[x] === 'string') {
search_params.append(x, options.playerVars[x]);
2023-08-18 02:40:50 +00:00
}
}
2023-08-29 15:52:58 +00:00
if (options.playerVars.autoplay === undefined) {
search_params.append('autoplay', '0');
2023-08-18 02:40:50 +00:00
}
}
iframe_src += "?" + search_params.toString();
2023-08-29 15:52:58 +00:00
if (typeof options.events === 'object') {
for (let x in options.events) {
this.addEventListener(x, options.events[x]);
2023-08-18 02:40:50 +00:00
}
}
this.player_iframe = document.createElement("iframe");
this.loaded = false;
2023-08-29 15:52:58 +00:00
this.addEventListener('loadedmetadata', () => { this.event_executor('ready'); this.loaded = true });
this.addEventListener('loadedmetadata', () => { this.setVolume(this.volume) });
2023-08-18 02:40:50 +00:00
this.player_iframe.src = iframe_src;
2023-08-29 15:52:58 +00:00
if (typeof options.width === 'number') {
2023-08-18 02:40:50 +00:00
this.player_iframe.width = options.width;
}
2023-08-29 15:52:58 +00:00
else {
if (document.body.clientWidth < 640) {
2023-08-18 02:40:50 +00:00
this.player_iframe.width = document.body.clientWidth;
}
2023-08-29 15:52:58 +00:00
else {
2023-08-18 02:40:50 +00:00
this.player_iframe.width = 640;
}
}
2023-08-29 15:52:58 +00:00
if (typeof options.width === 'number') {
2023-08-18 02:40:50 +00:00
this.player_iframe.width = options.width;
}
2023-08-29 15:52:58 +00:00
else {
this.player_iframe.height = this.player_iframe.width * (9 / 16);
2023-08-18 02:40:50 +00:00
}
this.player_iframe.style.border = "none";
replace_elemnt.replaceWith(this.player_iframe);
this.eventdata = {};
return this;
}
2023-08-29 15:52:58 +00:00
postMessage(data) {
const additionalInfo = { 'origin': location.origin, 'widgetid': this.widgetid.toString(), 'target': 'invidious_control' };
data = Object.assign(additionalInfo, data);
this.player_iframe.contentWindow.postMessage(data, this.target_origin);
}
event_executor(eventname) {
const execute_functions = this.eventobject[eventname];
let return_data = { data: undefined, target: this };
if (eventname === 'statechange') {
2023-08-18 02:40:50 +00:00
return_data.data = this.getPlayerState();
}
2023-08-29 15:52:58 +00:00
for (let x = 0; x < execute_functions.length; x++) {
try {
2023-08-18 02:40:50 +00:00
execute_functions[x](return_data);
}
2023-08-29 15:52:58 +00:00
catch { }
2023-08-18 02:40:50 +00:00
}
}
2023-08-29 15:52:58 +00:00
receiveMessage(message) {
if (message.data.from === 'invidious_control' && message.data.widgetid === String(this.widgetid)) {
switch (message.data.message_kind) {
2023-08-18 02:40:50 +00:00
case 'info_return':
2023-08-29 15:52:58 +00:00
const promise_array = this.message_wait[message.data.command];
promise_array.forEach(element => {
if (message.data.command === 'getvolume') {
element(message.data.value * 100);
2023-08-18 02:40:50 +00:00
}
2023-08-29 15:52:58 +00:00
else {
element(message.data.value);
2023-08-18 02:40:50 +00:00
}
2023-08-29 15:52:58 +00:00
});
2023-08-18 02:40:50 +00:00
this.message_wait[message.data.command] = [];
break;
case 'event':
if (typeof message.data.eventname === 'string') {
2023-08-18 02:40:50 +00:00
this.event_executor(message.data.eventname);
2023-08-29 15:52:58 +00:00
const previous_status = this.player_status;
switch (message.data.eventname) {
2023-08-18 02:40:50 +00:00
case 'ended':
this.player_status = 0;
break;
case 'play':
this.player_status = 1;
break;
case 'timeupdate':
this.player_status = 1;
2023-08-29 15:52:58 +00:00
this.eventdata = Object.assign({}, this.eventdata, message.data.value);
2023-08-18 02:40:50 +00:00
break;
case 'pause':
this.player_status = 2;
break;
case 'waiting':
this.player_status = 3;
break;
case 'loadedmetadata':
2023-08-29 15:52:58 +00:00
this.eventdata = Object.assign({}, this.eventdata, message.data.value);
2023-08-18 02:40:50 +00:00
break;
}
2023-08-29 15:52:58 +00:00
if (previous_status !== this.player_status) {
2023-08-18 02:40:50 +00:00
this.event_executor('statechange');
}
}
}
}
}
2023-08-29 15:52:58 +00:00
promise_send_event(event_name) {
if (invidious_embed.api_promise) {
const promise_object = new Promise((resolve, reject) => { this.message_wait[event_name].push(resolve) });
this.postMessage({ eventname: event_name });
return promise_object;
2023-08-18 02:40:50 +00:00
}
2023-08-29 15:52:58 +00:00
else {
2023-08-18 02:40:50 +00:00
return this.eventdata[event_name];
}
}
2023-08-29 15:52:58 +00:00
getPlayerState() {
2023-08-18 02:40:50 +00:00
return this.player_status;
}
2023-08-29 15:52:58 +00:00
playVideo() {
this.postMessage({ eventname: 'play' });
2023-08-18 02:40:50 +00:00
}
2023-08-29 15:52:58 +00:00
pauseVideo() {
this.postMessage({ eventname: 'pause' });
2023-08-18 02:40:50 +00:00
}
2023-08-29 15:52:58 +00:00
getVolume() {
2023-08-18 02:40:50 +00:00
return this.promise_send_event('getvolume');
}
2023-08-29 15:52:58 +00:00
setVolume(volume) {
if (typeof volume === 'number') {
this.volume = volume;
if (volume !== NaN && volume != undefined && volume >= 0 && volume <= 100) {
this.postMessage({ eventname: 'setvolume', value: volume / 100 });
}
}
else {
console.warn("setVolume first argument must be number");
2023-08-18 02:40:50 +00:00
}
}
2023-08-29 15:52:58 +00:00
getIframe() {
2023-08-18 02:40:50 +00:00
return this.player_iframe;
}
2023-08-29 15:52:58 +00:00
destroy() {
2023-08-18 02:40:50 +00:00
this.player_iframe.remove();
}
2023-08-29 15:52:58 +00:00
mute() {
this.postMessage({ eventname: 'setmutestatus', value: true });
2023-08-18 02:40:50 +00:00
}
2023-08-29 15:52:58 +00:00
unMute() {
this.postMessage({ eventname: 'setmutestatus', value: false });
2023-08-18 02:40:50 +00:00
}
2023-08-29 15:52:58 +00:00
isMuted() {
2023-08-18 02:40:50 +00:00
return this.promise_send_event('getmutestatus');
}
2023-08-29 15:52:58 +00:00
seekTo(seconds, allowSeekAhead) {//seconds must be a number and allowSeekAhead is ignore
2023-08-18 02:40:50 +00:00
seconds = Number(seconds);
2023-08-29 15:52:58 +00:00
if (seconds !== NaN && seconds !== undefined) {
this.postMessage({ eventname: 'seek', value: seconds });
2023-08-18 02:40:50 +00:00
}
}
2023-08-29 15:52:58 +00:00
setSize(width, height) {//width and height must be Number
2023-08-18 02:40:50 +00:00
this.player_iframe.width = Number(width);
this.player_iframe.height = Number(height);
}
2023-08-29 15:52:58 +00:00
getPlaybackRate() {
2023-08-18 02:40:50 +00:00
return this.promise_send_event('getplaybackrate');
}
2023-08-29 15:52:58 +00:00
setPlaybackRate(suggestedRate) {//suggestedRate must be number.this player allow not available playback rate such as 1.4
2023-08-18 02:40:50 +00:00
suggestedRate = Number(suggestedRate);
2023-08-29 15:52:58 +00:00
if (suggestedRate !== NaN && suggestedRate !== undefined) {
this.postMessage({ eventname: 'setplaybackrate', value: suggestedRate });
2023-08-18 02:40:50 +00:00
}
}
2023-08-29 15:52:58 +00:00
getAvailablePlaybackRates() {
2023-08-18 02:40:50 +00:00
return this.promise_send_event('getavailableplaybackrates');
}
2023-08-29 15:52:58 +00:00
playOtherVideoById(option, autoplay, startSeconds_arg) {//internal fuction
2023-08-18 02:40:50 +00:00
let videoId = '';
2023-08-29 15:52:58 +00:00
let startSeconds = 0;
2023-08-18 02:40:50 +00:00
let endSeconds = -1;
let mediaContetUrl = '';
2023-08-29 15:52:58 +00:00
if (typeof option === 'string') {
if (option.length === 11) {
2023-08-18 02:40:50 +00:00
videoId = option
}
2023-08-29 15:52:58 +00:00
else {
2023-08-18 02:40:50 +00:00
mediaContetUrl = option;
}
if (typeof startSeconds_arg === 'number') {
2023-08-18 02:40:50 +00:00
startSeconds = startSeconds_arg;
}
}
2023-08-29 15:52:58 +00:00
else if (typeof option === 'object') {
if (typeof option.videoId === 'string') {
if (option.videoId.length == 11) {
2023-08-18 02:40:50 +00:00
videoId = option.videoId;
}
2023-08-29 15:52:58 +00:00
else {
2023-08-18 02:40:50 +00:00
this.error_code = 2;
this.event_executor('error');
2023-08-29 15:52:58 +00:00
return;
2023-08-18 02:40:50 +00:00
}
}
else if (typeof option.mediaContentUrl === 'string') {
2023-08-18 02:40:50 +00:00
mediaContetUrl = option.mediaContentUrl;
}
2023-08-29 15:52:58 +00:00
else {
2023-08-18 02:40:50 +00:00
this.error_code = 2;
this.event_executor('error');
2023-08-29 15:52:58 +00:00
return;
2023-08-18 02:40:50 +00:00
}
2023-08-29 15:52:58 +00:00
if (typeof option.startSeconds === 'number' && option.startSeconds >= 0) {
2023-08-18 02:40:50 +00:00
startSeconds = option.startSeconds;
}
2023-08-29 15:52:58 +00:00
if (typeof option.endSeconds === 'number' && option.endSeconds >= 0) {
2023-08-25 15:48:41 +00:00
endSeconds = option.endSeconds;
2023-08-18 02:40:50 +00:00
}
}
2023-08-29 15:52:58 +00:00
if (mediaContetUrl.length > 0) {
2023-08-18 02:40:50 +00:00
var tmp_videoId = '';
2023-08-29 15:52:58 +00:00
if (mediaContetUrl.indexOf('/v/') !== -1) {
var end_pos = mediaContetUrl.length - 1;
if (mediaContetUrl.indexOf('?') !== -1) {
2023-08-18 02:40:50 +00:00
end_pos = mediaContetUrl.indexOf('?');
}
2023-08-29 15:52:58 +00:00
tmp_videoId = mediaContetUrl.substring(mediaContetUrl.indexOf('/v/'), end_pos);
2023-08-18 02:40:50 +00:00
}
2023-08-29 15:52:58 +00:00
else {
2023-08-18 02:40:50 +00:00
tmp_videoId = new URL(mediaContetUrl).searchParams.get('v');
}
2023-08-29 15:52:58 +00:00
if (tmp_videoId === null || tmp_videoId.length !== 11) {
2023-08-18 02:40:50 +00:00
this.error_code = 2;
this.event_executor('error');
2023-08-29 15:52:58 +00:00
return;
2023-08-18 02:40:50 +00:00
}
videoId = tmp_videoId;
}
2023-08-29 15:52:58 +00:00
let iframe_sorce = this.target_origin.slice();
2023-08-18 02:40:50 +00:00
iframe_sorce += "/embed/" + videoId;
this.videoId = videoId;
2023-08-29 15:52:58 +00:00
let search_params = new URLSearchParams('');
search_params.append('origin', location.origin);
search_params.append('enablejsapi', '1');
search_params.append('widgetid', invidious_embed.widgetid);
2023-08-18 02:40:50 +00:00
this.widgetid = invidious_embed.widgetid;
invidious_embed.widgetid++;
search_params.append('autoplay', Number(autoplay));
2023-08-29 15:52:58 +00:00
if (this.option_playerVars !== undefined) {
Object.keys(this.option_playerVars).forEach(key => {
if (key !== 'autoplay' && key !== 'start' && key !== 'end') {
search_params.append(key, this.option_playerVars[key]);
2023-08-18 02:40:50 +00:00
}
2023-08-29 15:52:58 +00:00
})
2023-08-18 02:40:50 +00:00
}
2023-08-29 15:52:58 +00:00
if (startSeconds > 0) {
search_params.append('start', startSeconds);
2023-08-18 02:40:50 +00:00
}
2023-08-29 15:52:58 +00:00
if (endSeconds !== -1 && endSeconds >= 0) {
if (endSeconds > startSeconds) {
search_params.append('end', endSeconds);
2023-08-18 02:40:50 +00:00
}
2023-08-29 15:52:58 +00:00
else {
throw 'Invalid end seconds because end seconds before start seconds';
2023-08-18 02:40:50 +00:00
}
}
iframe_sorce += "?" + search_params.toString();
this.player_iframe.src = iframe_sorce;
2023-08-29 15:52:58 +00:00
if (autoplay) {
2023-08-18 02:40:50 +00:00
this.player_status = 5;
}
this.eventdata = {};
}
2023-08-29 15:52:58 +00:00
loadVideoById(option, startSeconds) {
this.playOtherVideoById(option, true, startSeconds);
2023-08-18 02:40:50 +00:00
}
2023-08-29 15:52:58 +00:00
cueVideoById(option, startSeconds) {
this.playOtherVideoById(option, false, startSeconds);
2023-08-18 02:40:50 +00:00
}
2023-08-29 15:52:58 +00:00
cueVideoByUrl(option, startSeconds) {
this.playOtherVideoById(option, false, startSeconds);
2023-08-18 02:40:50 +00:00
}
2023-08-29 15:52:58 +00:00
loadVideoByUrl(option, startSeconds) {
this.playOtherVideoById(option, true, startSeconds);
2023-08-18 02:40:50 +00:00
}
2023-08-29 15:52:58 +00:00
getDuration() {
2023-08-18 02:40:50 +00:00
return this.promise_send_event('getduration');
}
2023-08-29 15:52:58 +00:00
getVideoUrl() {
2023-08-18 02:40:50 +00:00
return this.target_origin + "/watch?v=" + this.videoId;
}
2023-08-29 15:52:58 +00:00
async getVideoEmbedCode() {
2023-08-18 02:40:50 +00:00
var title = await this.getVideoTitle();
2023-08-29 15:52:58 +00:00
return '<iframe width="560" height="315" src="' + this.target_origin + '/embed/' + this.videoId + '" title="' + title.replace('"', "'") + '" frameborder="0" allow="autoplay;encrypted-media;picture-in-picture;web-share" allowfullscreen></iframe>';
2023-08-18 02:40:50 +00:00
}
2023-08-29 15:52:58 +00:00
getCurrentTime() {
2023-08-18 02:40:50 +00:00
return this.promise_send_event('getcurrenttime');
}
2023-08-29 15:52:58 +00:00
constructor(element, options) {
this.Player(element, options);
window.addEventListener('message', (ms) => { this.receiveMessage(ms) });
this.message_wait = { getvolume: [], getmutestatus: [], getduration: [], getcurrenttime: [], getplaybackrate: [], getavailableplaybackrates: [], gettitle: [] };
2023-08-18 02:40:50 +00:00
}
2023-08-29 15:52:58 +00:00
async getVideoData() {
return { video_id: this.videoId, title: await this.promise_send_event('gettitle') };
2023-08-25 15:48:41 +00:00
}
2023-08-18 02:40:50 +00:00
}
2023-08-29 15:52:58 +00:00
function invidious_ready(func) {
if (typeof func === 'function') {
2023-08-18 02:40:50 +00:00
func();
}
}
2023-08-29 15:52:58 +00:00
invidious_embed.invidious_instance = new URL(document.currentScript.src).origin;
const invidious = { Player: invidious_embed, PlayerState: { ENDED: 0, PLAYING: 1, PAUSED: 2, BUFFERING: 3, CUED: 5 }, ready: invidious_ready };
try {
2023-08-18 02:40:50 +00:00
onInvidiousIframeAPIReady();
}
2023-08-29 15:52:58 +00:00
catch { }