mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-09-16 01:56:55 +00:00
4.14.6
* Fixed: Rewrite our player code to support the Highwind player. This removes support for the old player, but to our knowledge everyone should be using Highwind now. * Removed: The Reset Player button. At this time we just don't know how to properly implement it for the new player. The new player is more tightly integrated into the Twitch web application. * Removed: Closed Captioning position settings. Twitch appears to have native position controls for closed captions now.
This commit is contained in:
parent
29414beaa0
commit
6547497d02
10 changed files with 433 additions and 475 deletions
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "frankerfacez",
|
"name": "frankerfacez",
|
||||||
"author": "Dan Salvato LLC",
|
"author": "Dan Salvato LLC",
|
||||||
"version": "4.14.5",
|
"version": "4.14.6",
|
||||||
"description": "FrankerFaceZ is a Twitch enhancement suite.",
|
"description": "FrankerFaceZ is a Twitch enhancement suite.",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
|
@ -155,6 +155,27 @@ export default class Metadata extends Module {
|
||||||
skippedFrames: temp.dropped_frames,
|
skippedFrames: temp.dropped_frames,
|
||||||
videoResolution: `${temp.vid_width}x${temp.vid_height}`
|
videoResolution: `${temp.vid_width}x${temp.vid_height}`
|
||||||
}
|
}
|
||||||
|
} else if ( player.stats ) {
|
||||||
|
const videoHeight = maybe_call(player.getVideoHeight, player) || 0,
|
||||||
|
videoWidth = maybe_call(player.getVideoWidth, player) || 0,
|
||||||
|
displayHeight = maybe_call(player.getDisplayHeight, player) || 0,
|
||||||
|
displayWidth = maybe_call(player.getDisplayWidth, player) || 0;
|
||||||
|
|
||||||
|
stats = {
|
||||||
|
backendVersion: maybe_call(player.getVersion, player),
|
||||||
|
bufferSize: maybe_call(player.getBufferDuration, player),
|
||||||
|
displayResolution: `${displayWidth}x${displayHeight}`,
|
||||||
|
videoResolution: `${videoWidth}x${videoHeight}`,
|
||||||
|
videoHeight,
|
||||||
|
videoWidth,
|
||||||
|
displayHeight,
|
||||||
|
displayWidth,
|
||||||
|
fps: (maybe_call(player.getVideoFrameRate, player) || 0).toFixed(2),
|
||||||
|
hlsLatencyBroadcaster: player.stats?.broadcasterLatency,
|
||||||
|
hlsLatencyEncoder: player.stats?.transcoderLatency,
|
||||||
|
playbackRate: (maybe_call(player.getVideoBitRate, player) || 0) / 1000,
|
||||||
|
skippedFrames: maybe_call(player.getDroppedFrames, player),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ! stats || stats.hlsLatencyBroadcaster < -100 )
|
if ( ! stats || stats.hlsLatencyBroadcaster < -100 )
|
||||||
|
@ -192,19 +213,12 @@ export default class Metadata extends Module {
|
||||||
|
|
||||||
click() {
|
click() {
|
||||||
const Player = this.resolve('site.player'),
|
const Player = this.resolve('site.player'),
|
||||||
internal = Player.getInternalPlayer();
|
ui = Player.playerUI;
|
||||||
|
|
||||||
if ( ! internal )
|
if ( ! ui )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const store = internal.context.store,
|
ui.setStatsOverlay(ui.statsOverlay === 1 ? 0 : 1);
|
||||||
state = store.getState(),
|
|
||||||
displayed = state && state.stats && state.stats.displayState === 'DISPLAY_VIDEO_STATS';
|
|
||||||
|
|
||||||
store.dispatch({
|
|
||||||
type: 'display stats',
|
|
||||||
displayState: displayed ? 'DISPLAY_NONE' : 'DISPLAY_VIDEO_STATS'
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
color(data) {
|
color(data) {
|
||||||
|
|
|
@ -22,8 +22,8 @@ const CLASSES = {
|
||||||
|
|
||||||
'prime-offers': '.top-nav__prime',
|
'prime-offers': '.top-nav__prime',
|
||||||
|
|
||||||
'player-ext': '.player .extension-taskbar,.player .extension-container,.player .extensions-dock__layout,.player .extensions-notifications,.player .extensions-video-overlay-size-container',
|
'player-ext': '.highwind-video-player .extension-taskbar,.highwind-video-player .extension-container,.highwind-video-player .extensions-dock__layout,.highwind-video-player .extensions-notifications,.highwind-video-player .extensions-video-overlay-size-container',
|
||||||
'player-ext-hover': '.player:not([data-controls="true"]) .extension-container,.player:not([data-controls="true"]) .extensions-dock__layout,.player:not([data-controls="true"]) .extensions-notifications,.player:not([data-controls="true"]) .extensions-video-overlay-size-container',
|
'player-ext-hover': '.highwind-video-player__overlay[data-controls="false"] .extension-taskbar,.highwind-video-player__overlay[data-controls="false"] .extension-container,.highwind-video-player__overlay[data-controls="false"] .extensions-dock__layout,.highwind-video-player__overlay[data-controls="false"] .extensions-notifications,.highwind-video-player__overlay[data-controls="false"] .extensions-video-overlay-size-container',
|
||||||
|
|
||||||
'player-event-bar': '.channel-root .live-event-banner-ui__header',
|
'player-event-bar': '.channel-root .live-event-banner-ui__header',
|
||||||
'player-rerun-bar': '.channel-root__player-container div.tw-c-text-overlay:not([data-a-target="hosting-ui-header"])',
|
'player-rerun-bar': '.channel-root__player-container div.tw-c-text-overlay:not([data-a-target="hosting-ui-header"])',
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
html body {
|
||||||
|
font-family: var(--ffz-global-font) !important;
|
||||||
|
}
|
|
@ -1,4 +1,7 @@
|
||||||
.player .extension-overlay__iframe,
|
.player .extension-overlay__iframe,
|
||||||
.player .extension-overlay {
|
.player .extension-overlay,
|
||||||
|
.highwind-video-player .extension-overlay__iframe,
|
||||||
|
.highwind-video-player .extension-overlay,
|
||||||
|
.highwind-video-player .extension-view__iframe {
|
||||||
pointer-events: none !important;
|
pointer-events: none !important;
|
||||||
}
|
}
|
|
@ -1,3 +1,4 @@
|
||||||
|
.highwind-video-player__overlay[data-controls="false"][data-paused="false"][data-ended="false"],
|
||||||
.player:hover:not([data-controls="true"]):not([data-paused="true"]):not([data-ended="true"]) {
|
.player:hover:not([data-controls="true"]):not([data-paused="true"]):not([data-ended="true"]) {
|
||||||
cursor: none;
|
cursor: none;
|
||||||
}
|
}
|
|
@ -1,3 +1,4 @@
|
||||||
|
.highwind-video-player .volume-slider__slider-container,
|
||||||
.player .player-volume__slider-container {
|
.player .player-volume__slider-container {
|
||||||
opacity: 1 !important;
|
opacity: 1 !important;
|
||||||
}
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
|
.highwind-video-player__container--theatre-whispers,
|
||||||
.video-player--theatre.video-player--logged-in .player.video-player__container {
|
.video-player--theatre.video-player--logged-in .player.video-player__container {
|
||||||
bottom: 0;
|
bottom: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.whispers.whispers--theatre-mode { display: none !important }
|
.whispers.whispers--theatre-mode { display: none !important }
|
|
@ -17,26 +17,19 @@ export default class Player extends Module {
|
||||||
|
|
||||||
this.should_enable = true;
|
this.should_enable = true;
|
||||||
|
|
||||||
|
|
||||||
|
// Dependency Injection
|
||||||
|
|
||||||
|
this.inject('i18n');
|
||||||
this.inject('settings');
|
this.inject('settings');
|
||||||
|
|
||||||
this.inject('site.fine');
|
this.inject('site.fine');
|
||||||
this.inject('site.web_munch');
|
this.inject('site.web_munch');
|
||||||
this.inject('site.css_tweaks');
|
this.inject('site.css_tweaks');
|
||||||
this.inject('site.router');
|
this.inject('site.router');
|
||||||
this.inject('i18n');
|
|
||||||
|
|
||||||
this.Player = this.fine.define(
|
|
||||||
'twitch-player',
|
|
||||||
n => n.player && n.onPlayerReady,
|
|
||||||
PLAYER_ROUTES
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
// TODO: Better way to reposition player on demand.
|
// React Components
|
||||||
this.PersistentPlayer = this.fine.define(
|
|
||||||
'twitch-player-persistent',
|
|
||||||
n => n.renderMiniHoverControls && n.togglePause,
|
|
||||||
PLAYER_ROUTES
|
|
||||||
);
|
|
||||||
|
|
||||||
this.SquadStreamBar = this.fine.define(
|
this.SquadStreamBar = this.fine.define(
|
||||||
'squad-stream-bar',
|
'squad-stream-bar',
|
||||||
|
@ -44,6 +37,21 @@ export default class Player extends Module {
|
||||||
PLAYER_ROUTES
|
PLAYER_ROUTES
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.Player = this.fine.define(
|
||||||
|
'highwind-player',
|
||||||
|
n => n.setPlayerActive && n.props?.playerEvents && n.props?.mediaPlayerInstance,
|
||||||
|
PLAYER_ROUTES
|
||||||
|
);
|
||||||
|
|
||||||
|
this.TheatreHost = this.fine.define(
|
||||||
|
'theatre-host',
|
||||||
|
n => n.toggleTheatreMode && n.props && n.props.onTheatreModeEnabled,
|
||||||
|
['user', 'video', 'user-video', 'user-clip']
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
// Settings
|
||||||
|
|
||||||
this.settings.add('player.volume-scroll', {
|
this.settings.add('player.volume-scroll', {
|
||||||
default: false,
|
default: false,
|
||||||
ui: {
|
ui: {
|
||||||
|
@ -59,7 +67,7 @@ export default class Player extends Module {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.settings.add('player.button.reset', {
|
/*this.settings.add('player.button.reset', {
|
||||||
default: true,
|
default: true,
|
||||||
ui: {
|
ui: {
|
||||||
path: 'Player > General >> General',
|
path: 'Player > General >> General',
|
||||||
|
@ -71,7 +79,7 @@ export default class Player extends Module {
|
||||||
for(const inst of this.Player.instances)
|
for(const inst of this.Player.instances)
|
||||||
this.addResetButton(inst);
|
this.addResetButton(inst);
|
||||||
}
|
}
|
||||||
});
|
});*/
|
||||||
|
|
||||||
if ( document.pictureInPictureEnabled )
|
if ( document.pictureInPictureEnabled )
|
||||||
this.settings.add('player.button.pip', {
|
this.settings.add('player.button.pip', {
|
||||||
|
@ -126,7 +134,7 @@ export default class Player extends Module {
|
||||||
changed: () => this.updateCaptionsCSS()
|
changed: () => this.updateCaptionsCSS()
|
||||||
});
|
});
|
||||||
|
|
||||||
this.settings.add('player.captions.custom-position', {
|
/*this.settings.add('player.captions.custom-position', {
|
||||||
default: false,
|
default: false,
|
||||||
ui: {
|
ui: {
|
||||||
path: 'Player > Closed Captioning >> Position',
|
path: 'Player > Closed Captioning >> Position',
|
||||||
|
@ -178,7 +186,7 @@ export default class Player extends Module {
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
changed: () => this.updateCaptionsCSS()
|
changed: () => this.updateCaptionsCSS()
|
||||||
});
|
});*/
|
||||||
|
|
||||||
this.settings.add('player.theatre.no-whispers', {
|
this.settings.add('player.theatre.no-whispers', {
|
||||||
default: false,
|
default: false,
|
||||||
|
@ -292,7 +300,7 @@ export default class Player extends Module {
|
||||||
},
|
},
|
||||||
changed: val => {
|
changed: val => {
|
||||||
this.css_tweaks.toggleHide('player-event-bar', val);
|
this.css_tweaks.toggleHide('player-event-bar', val);
|
||||||
this.PersistentPlayer.forceUpdate();
|
this.repositionPlayer();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -306,7 +314,7 @@ export default class Player extends Module {
|
||||||
},
|
},
|
||||||
changed: val => {
|
changed: val => {
|
||||||
this.css_tweaks.toggleHide('player-rerun-bar', val);
|
this.css_tweaks.toggleHide('player-rerun-bar', val);
|
||||||
this.PersistentPlayer.forceUpdate();
|
this.repositionPlayer();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -331,6 +339,264 @@ export default class Player extends Module {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onEnable() {
|
||||||
|
this.css_tweaks.toggle('player-volume', this.settings.get('player.volume-always-shown'));
|
||||||
|
this.css_tweaks.toggle('player-ext-mouse', !this.settings.get('player.ext-interaction'));
|
||||||
|
this.css_tweaks.toggle('theatre-no-whispers', this.settings.get('player.theatre.no-whispers'));
|
||||||
|
this.css_tweaks.toggle('theatre-metadata', this.settings.get('player.theatre.metadata'));
|
||||||
|
this.css_tweaks.toggle('player-hide-mouse', this.settings.get('player.hide-mouse'));
|
||||||
|
this.css_tweaks.toggleHide('player-event-bar', this.settings.get('player.hide-event-bar'));
|
||||||
|
this.css_tweaks.toggleHide('player-rerun-bar', this.settings.get('player.hide-rerun-bar'));
|
||||||
|
|
||||||
|
this.updateCaptionsCSS();
|
||||||
|
this.updateHideExtensions();
|
||||||
|
this.installVisibilityHook();
|
||||||
|
|
||||||
|
const t = this;
|
||||||
|
|
||||||
|
|
||||||
|
this.SquadStreamBar.ready(cls => {
|
||||||
|
const old_should_render = cls.prototype.shouldRenderSquadBanner;
|
||||||
|
|
||||||
|
cls.prototype.shouldRenderSquadBanner = function(...args) {
|
||||||
|
if ( t.settings.get('player.hide-squad-banner') )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return old_should_render.call(this, ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.SquadStreamBar.forceUpdate();
|
||||||
|
this.updateSquadContext();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.SquadStreamBar.on('mount', this.updateSquadContext, this);
|
||||||
|
this.SquadStreamBar.on('update', this.updateSquadContext, this);
|
||||||
|
this.SquadStreamBar.on('unmount', this.updateSquadContext, this);
|
||||||
|
|
||||||
|
|
||||||
|
this.Player.ready((cls, instances) => {
|
||||||
|
const old_attach = cls.prototype.maybeAttachDomEventListeners;
|
||||||
|
|
||||||
|
cls.prototype.ffzInstall = function() {
|
||||||
|
if ( this._ffz_installed )
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._ffz_installed = true;
|
||||||
|
|
||||||
|
if ( ! this._ffzUpdateState )
|
||||||
|
this._ffzUpdateState = this.ffzUpdateState.bind(this);
|
||||||
|
|
||||||
|
const inst = this,
|
||||||
|
old_active = this.setPlayerActive,
|
||||||
|
old_inactive = this.setPlayerInactive;
|
||||||
|
|
||||||
|
this.setPlayerActive = function() {
|
||||||
|
inst.ffzScheduleState();
|
||||||
|
return old_active.call(inst);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setPlayerInactive = function() {
|
||||||
|
inst.ffzScheduleState();
|
||||||
|
return old_inactive.call(inst);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.ffzOnEnded = () => {
|
||||||
|
if ( t.settings.get('player.vod.autoplay') )
|
||||||
|
return;
|
||||||
|
|
||||||
|
t.parent.awaitElement(
|
||||||
|
'.autoplay-vod__content-container button',
|
||||||
|
this.props.containerRef || t.fine.getChildNode(this),
|
||||||
|
1000
|
||||||
|
).then(el => el.click());
|
||||||
|
}
|
||||||
|
|
||||||
|
const events = this.props.playerEvents;
|
||||||
|
if ( events ) {
|
||||||
|
on(events, 'Playing', this._ffzUpdateState);
|
||||||
|
on(events, 'PlayerError', this._ffzUpdateState);
|
||||||
|
on(events, 'Ended', this._ffzUpdateState);
|
||||||
|
on(events, 'Ended', this.ffzOnEnded);
|
||||||
|
on(events, 'Idle', this._ffzUpdateState);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.ffzStopAutoplay();
|
||||||
|
}
|
||||||
|
|
||||||
|
cls.prototype.ffzUninstall = function() {
|
||||||
|
if ( this._ffz_state_raf )
|
||||||
|
cancelAnimationFrame(this._ffz_state_raf);
|
||||||
|
|
||||||
|
const events = this.props.playerEvents;
|
||||||
|
if ( events && this._ffzUpdateState ) {
|
||||||
|
off(events, 'Playing', this._ffzUpdateState);
|
||||||
|
off(events, 'PlayerError', this._ffzUpdateState);
|
||||||
|
off(events, 'Ended', this._ffzUpdateState);
|
||||||
|
off(events, 'Ended', this.ffzOnEnded);
|
||||||
|
off(events, 'Idle', this._ffzUpdateState);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.ffzRemoveListeners();
|
||||||
|
|
||||||
|
this._ffz_state_raf = null;
|
||||||
|
this._ffzUpdateState = null;
|
||||||
|
this.ffzOnEnded = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
cls.prototype.ffzStopAutoplay = function() {
|
||||||
|
if ( t.settings.get('player.no-autoplay') || (! t.settings.get('player.home.autoplay') && t.router.current.name === 'front-page') ) {
|
||||||
|
const player = this.props.mediaPlayerInstance,
|
||||||
|
events = this.props.playerEvents;
|
||||||
|
|
||||||
|
if ( player && player.pause )
|
||||||
|
player.pause();
|
||||||
|
else if ( events ) {
|
||||||
|
const immediatePause = () => {
|
||||||
|
if ( this.props.mediaPlayerInstance?.pause ) {
|
||||||
|
this.props.mediaPlayerInstance.pause();
|
||||||
|
off(events, 'Playing', immediatePause);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
t.log.info('Unable to immediately pause. Listening for playing event.');
|
||||||
|
on(events, 'Playing', immediatePause);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cls.prototype.ffzScheduleState = function() {
|
||||||
|
if ( ! this._ffzUpdateState )
|
||||||
|
this._ffzUpdateState = this.ffzUpdateState.bind(this);
|
||||||
|
|
||||||
|
if ( ! this._ffz_state_raf )
|
||||||
|
this._ffz_state_raf = requestAnimationFrame(this._ffzUpdateState);
|
||||||
|
}
|
||||||
|
|
||||||
|
cls.prototype.ffzUpdateState = function() {
|
||||||
|
this._ffz_state_raf = null;
|
||||||
|
const cont = this.props.containerRef,
|
||||||
|
player = this.props.mediaPlayerInstance;
|
||||||
|
if ( ! cont )
|
||||||
|
return;
|
||||||
|
|
||||||
|
const ds = cont.dataset;
|
||||||
|
ds.controls = this.state?.active || false;
|
||||||
|
|
||||||
|
ds.ended = player?.state?.playerState === 'Ended';
|
||||||
|
ds.paused = player?.state?.playerState === 'Idle';
|
||||||
|
}
|
||||||
|
|
||||||
|
cls.prototype.ffzAttachListeners = function() {
|
||||||
|
const cont = this.props.containerRef;
|
||||||
|
if ( ! cont || this._ffz_listeners )
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._ffz_listeners = true;
|
||||||
|
if ( ! this._ffz_scroll_handler )
|
||||||
|
this._ffz_scroll_handler = this.ffzScrollHandler.bind(this);
|
||||||
|
on(cont, 'wheel', this._ffz_scroll_handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
cls.prototype.ffzRemoveListeners = function() {
|
||||||
|
const cont = this.props.containerRef;
|
||||||
|
if ( ! cont || ! this._ffz_listeners )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if ( this._ffz_scroll_handler ) {
|
||||||
|
off(cont, 'wheel', this._ffz_scroll_handler);
|
||||||
|
this._ffz_scroll_handler = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._ffz_listeners = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
cls.prototype.ffzScrollHandler = function(event) {
|
||||||
|
if ( ! t.settings.get('player.volume-scroll') )
|
||||||
|
return;
|
||||||
|
|
||||||
|
const delta = event.wheelDelta || -(event.deltaY || event.detail || 0),
|
||||||
|
player = this.props?.mediaPlayerInstance;
|
||||||
|
|
||||||
|
if ( ! player?.getVolume )
|
||||||
|
return;
|
||||||
|
|
||||||
|
const amount = t.settings.get('player.volume-scroll-steps'),
|
||||||
|
volume = Math.max(0, Math.min(1, player.getVolume() + (delta > 0 ? amount : -amount)));
|
||||||
|
|
||||||
|
player.setVolume(volume);
|
||||||
|
if ( volume !== 0 )
|
||||||
|
player.setMuted(false);
|
||||||
|
|
||||||
|
event.preventDefault();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
cls.prototype.ffzMaybeRemoveNativeListeners = function() {
|
||||||
|
const cont = this.props.containerRef;
|
||||||
|
if ( cont && this.listenersAttached ) {
|
||||||
|
off(cont, 'mouseleave', this.setPlayerInactive);
|
||||||
|
off(cont, 'mouseenter', this.setPlayerActive);
|
||||||
|
off(cont, 'mousemove', this.onMouseMove);
|
||||||
|
this.listenersAttached = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cls.prototype.maybeAttachDomEventListeners = function() {
|
||||||
|
try {
|
||||||
|
this.ffzInstall();
|
||||||
|
this.ffzAttachListeners();
|
||||||
|
} catch(err) {
|
||||||
|
t.log.error('Error attaching event listener.', err);
|
||||||
|
}
|
||||||
|
|
||||||
|
return old_attach.call(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
for(const inst of instances) {
|
||||||
|
const events = inst.props?.playerEvents;
|
||||||
|
if ( events ) {
|
||||||
|
off(events, 'Playing', inst.setPlayerActive);
|
||||||
|
off(events, 'PlayerSeekCompleted', inst.setPlayerActive);
|
||||||
|
}
|
||||||
|
|
||||||
|
inst.ffzMaybeRemoveNativeListeners();
|
||||||
|
inst.maybeAttachDomEventListeners();
|
||||||
|
inst.ffzScheduleState();
|
||||||
|
|
||||||
|
if ( events ) {
|
||||||
|
on(events, 'Playing', inst.setPlayerActive);
|
||||||
|
on(events, 'PlayerSeekCompleted', inst.setPlayerActive);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.updateGUI(inst);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.Player.on('mount', inst => {
|
||||||
|
this.updateGUI(inst);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.Player.on('unmount', inst => {
|
||||||
|
inst.ffzUninstall();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
this.TheatreHost.on('mount', this.tryTheatreMode, this);
|
||||||
|
this.TheatreHost.ready((cls, instances) => {
|
||||||
|
for(const inst of instances)
|
||||||
|
this.tryTheatreMode(inst);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
this.on('i18n:update', () => {
|
||||||
|
for(const inst of this.Player.instances) {
|
||||||
|
this.updateGUI(inst);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
updateHideExtensions(val) {
|
updateHideExtensions(val) {
|
||||||
if ( val === undefined )
|
if ( val === undefined )
|
||||||
val = this.settings.get('player.ext-hide');
|
val = this.settings.get('player.ext-hide');
|
||||||
|
@ -339,6 +605,7 @@ export default class Player extends Module {
|
||||||
this.css_tweaks.toggleHide('player-ext', val === 2);
|
this.css_tweaks.toggleHide('player-ext', val === 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
updateCaptionsCSS() {
|
updateCaptionsCSS() {
|
||||||
// Font
|
// Font
|
||||||
const font_size = this.settings.get('player.captions.font-size');
|
const font_size = this.settings.get('player.captions.font-size');
|
||||||
|
@ -359,14 +626,14 @@ export default class Player extends Module {
|
||||||
font_out.push(`font-size: ${STYLE_VALIDATOR.style.fontSize} !important;`);
|
font_out.push(`font-size: ${STYLE_VALIDATOR.style.fontSize} !important;`);
|
||||||
|
|
||||||
if ( font_out.length )
|
if ( font_out.length )
|
||||||
this.css_tweaks.set('captions-font', `.player-captions {
|
this.css_tweaks.set('captions-font', `.player-captions-container__caption-line {
|
||||||
${font_out.join('\n\t')}
|
${font_out.join('\n\t')}
|
||||||
}`)
|
}`)
|
||||||
else
|
else
|
||||||
this.css_tweaks.delete('captions-font');
|
this.css_tweaks.delete('captions-font');
|
||||||
|
|
||||||
// Position
|
// Position
|
||||||
const enabled = this.settings.get('player.captions.custom-position'),
|
/*const enabled = this.settings.get('player.captions.custom-position'),
|
||||||
vertical = this.settings.get('player.captions.vertical'),
|
vertical = this.settings.get('player.captions.vertical'),
|
||||||
horizontal = this.settings.get('player.captions.horizontal'),
|
horizontal = this.settings.get('player.captions.horizontal'),
|
||||||
alignment = this.settings.get('player.captions.alignment');
|
alignment = this.settings.get('player.captions.alignment');
|
||||||
|
@ -376,7 +643,7 @@ export default class Player extends Module {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const out = [],
|
const out = [], align_out = [],
|
||||||
align_horizontal = alignment % 10,
|
align_horizontal = alignment % 10,
|
||||||
align_vertical = Math.floor(alignment / 10);
|
align_vertical = Math.floor(alignment / 10);
|
||||||
|
|
||||||
|
@ -394,6 +661,11 @@ export default class Player extends Module {
|
||||||
STYLE_VALIDATOR.style.top = '';
|
STYLE_VALIDATOR.style.top = '';
|
||||||
STYLE_VALIDATOR.style.top = horizontal;
|
STYLE_VALIDATOR.style.top = horizontal;
|
||||||
if ( STYLE_VALIDATOR.style.top ) {
|
if ( STYLE_VALIDATOR.style.top ) {
|
||||||
|
if ( align_horizontal === 1 )
|
||||||
|
align_out.push(`align-items: flex-start !important;`);
|
||||||
|
else if ( align_horizontal === 3 )
|
||||||
|
align_out.push(`align-items: flex-end !important;`);
|
||||||
|
|
||||||
out.push(`${align_horizontal === 3 ? 'right' : 'left'}: ${STYLE_VALIDATOR.style.top} !important;`);
|
out.push(`${align_horizontal === 3 ? 'right' : 'left'}: ${STYLE_VALIDATOR.style.top} !important;`);
|
||||||
out.push(`${align_horizontal === 3 ? 'left' : 'right'}: unset !important;`);
|
out.push(`${align_horizontal === 3 ? 'left' : 'right'}: unset !important;`);
|
||||||
custom_left = true;
|
custom_left = true;
|
||||||
|
@ -406,74 +678,11 @@ export default class Player extends Module {
|
||||||
|
|
||||||
this.css_tweaks.set('captions-position', `.player-captions-container {
|
this.css_tweaks.set('captions-position', `.player-captions-container {
|
||||||
${out.join('\n\t')};
|
${out.join('\n\t')};
|
||||||
}`);
|
}${align_out.length ? `.player-captions-container__caption-window {
|
||||||
|
${align_out.join('\n\t')}
|
||||||
|
}` : ''}`);*/
|
||||||
}
|
}
|
||||||
|
|
||||||
onEnable() {
|
|
||||||
this.css_tweaks.toggle('player-volume', this.settings.get('player.volume-always-shown'));
|
|
||||||
this.css_tweaks.toggle('player-ext-mouse', !this.settings.get('player.ext-interaction'));
|
|
||||||
this.css_tweaks.toggle('theatre-no-whispers', this.settings.get('player.theatre.no-whispers'));
|
|
||||||
this.css_tweaks.toggle('theatre-metadata', this.settings.get('player.theatre.metadata'));
|
|
||||||
this.css_tweaks.toggle('player-hide-mouse', this.settings.get('player.hide-mouse'));
|
|
||||||
this.css_tweaks.toggleHide('player-event-bar', this.settings.get('player.hide-event-bar'));
|
|
||||||
this.css_tweaks.toggleHide('player-rerun-bar', this.settings.get('player.hide-rerun-bar'));
|
|
||||||
this.updateHideExtensions();
|
|
||||||
this.updateCaptionsCSS();
|
|
||||||
this.installVisibilityHook();
|
|
||||||
|
|
||||||
const t = this;
|
|
||||||
|
|
||||||
this.SquadStreamBar.ready(cls => {
|
|
||||||
const old_should_render = cls.prototype.shouldRenderSquadBanner;
|
|
||||||
|
|
||||||
cls.prototype.shouldRenderSquadBanner = function(...args) {
|
|
||||||
if ( t.settings.get('player.hide-squad-banner') )
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return old_should_render.call(this, ...args);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.SquadStreamBar.forceUpdate();
|
|
||||||
this.updateSquadContext();
|
|
||||||
});
|
|
||||||
|
|
||||||
this.SquadStreamBar.on('mount', this.updateSquadContext, this);
|
|
||||||
this.SquadStreamBar.on('update', this.updateSquadContext, this);
|
|
||||||
this.SquadStreamBar.on('unmount', this.updateSquadContext, this);
|
|
||||||
|
|
||||||
this.Player.on('mount', this.onMount, this);
|
|
||||||
this.Player.on('unmount', this.onUnmount, this);
|
|
||||||
|
|
||||||
this.Player.ready((cls, instances) => {
|
|
||||||
const old_init = cls.prototype.initializePlayer;
|
|
||||||
|
|
||||||
if ( old_init ) {
|
|
||||||
cls.prototype.initializePlayer = function(...args) {
|
|
||||||
const ret = old_init.call(this, ...args);
|
|
||||||
t.process(this);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
this.Player.on('will-mount', this.overrideInitialize, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
for(const inst of instances) {
|
|
||||||
if ( ! old_init )
|
|
||||||
this.overrideInitialize(inst);
|
|
||||||
|
|
||||||
this.onMount(inst);
|
|
||||||
this.process(inst);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.on('i18n:update', () => {
|
|
||||||
for(const inst of this.Player.instances) {
|
|
||||||
this.addPiPButton(inst);
|
|
||||||
this.addResetButton(inst);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
installVisibilityHook() {
|
installVisibilityHook() {
|
||||||
if ( ! document.pictureInPictureEnabled ) {
|
if ( ! document.pictureInPictureEnabled ) {
|
||||||
|
@ -500,290 +709,14 @@ export default class Player extends Module {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
updateSquadContext() {
|
updateGUI(inst) {
|
||||||
this.settings.updateContext({
|
|
||||||
squad_bar: this.hasSquadBar()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
hasSquadBar() {
|
|
||||||
const inst = this.SquadStreamBar.first;
|
|
||||||
return inst ? inst.shouldRenderSquadBanner(inst.props) : false
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
overrideInitialize(inst) {
|
|
||||||
const t = this,
|
|
||||||
old_init = inst.initializePlayer;
|
|
||||||
|
|
||||||
inst.initializePlayer = function(...args) {
|
|
||||||
const ret = old_init.call(inst, ...args);
|
|
||||||
t.process(inst);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
onMount(inst) {
|
|
||||||
if ( this.settings.get('player.theatre.auto-enter') && inst.onTheatreChange )
|
|
||||||
inst.onTheatreChange(true);
|
|
||||||
|
|
||||||
if ( this.settings.get('player.no-autoplay') || (! this.settings.get('player.home.autoplay') && this.router.current.name === 'front-page') ) {
|
|
||||||
if ( inst.player )
|
|
||||||
this.disableAutoplay(inst);
|
|
||||||
else {
|
|
||||||
const wrapped = inst.onPlayerReady;
|
|
||||||
inst.onPlayerReady = () => {
|
|
||||||
const ret = wrapped.call(inst);
|
|
||||||
this.disableAutoplay(inst);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
onUnmount(inst) { // eslint-disable-line class-methods-use-this
|
|
||||||
this.cleanup(inst);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
process(inst) {
|
|
||||||
this.addResetButton(inst);
|
|
||||||
this.addPiPButton(inst);
|
this.addPiPButton(inst);
|
||||||
this.addEndedListener(inst);
|
|
||||||
this.addStateTags(inst);
|
|
||||||
this.addControlVisibility(inst);
|
|
||||||
this.updateVolumeScroll(inst);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
cleanup(inst) { // eslint-disable-line class-methods-use-this
|
|
||||||
const p = inst.player,
|
|
||||||
pr = inst.playerRef,
|
|
||||||
video = pr && pr.querySelector('video'),
|
|
||||||
reset = pr && pr.querySelector('.ffz--player-reset'),
|
|
||||||
pip = pr && pr.querySelector('.ffz--player-pip');
|
|
||||||
|
|
||||||
if ( reset )
|
|
||||||
reset.remove();
|
|
||||||
|
|
||||||
if ( pip )
|
|
||||||
pip.remove();
|
|
||||||
|
|
||||||
if ( video && video._ffz_pip_enter ) {
|
|
||||||
video.removeEventListener('enterpictureinpicture', video._ffz_pip_enter);
|
|
||||||
video.removeEventListener('leavepictureinpicture', video._ffz_pip_exit);
|
|
||||||
video._ffz_pip_enter = null;
|
|
||||||
video._ffz_pip_exit = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( inst._ffz_on_ended ) {
|
|
||||||
p && off(p, 'ended', inst._ffz_on_ended);
|
|
||||||
inst._ffz_on_ended = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( inst._ffz_visibility_handler ) {
|
|
||||||
if ( pr ) {
|
|
||||||
off(pr, 'mousemove', inst._ffz_visibility_handler);
|
|
||||||
off(pr, 'mouseleave', inst._ffz_visibility_handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
inst._ffz_visibility_handler = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( inst._ffz_scroll_handler ) {
|
|
||||||
pr && off(pr, 'wheel', inst._ffz_scroll_handler);
|
|
||||||
inst._ffz_scroll_handler = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( inst._ffz_autoplay_handler ) {
|
|
||||||
if ( p ) {
|
|
||||||
off(p, 'play', inst._ffz_autoplay_handler);
|
|
||||||
off(p, 'playing', inst._ffz_autoplay_handler);
|
|
||||||
off(p, 'contentShowing', inst._ffz_autoplay_handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
inst._ffz_autoplay_handler = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( inst._ffz_on_state ) {
|
|
||||||
if ( p ) {
|
|
||||||
off(p, 'ended', inst._ffz_on_state);
|
|
||||||
off(p, 'pause', inst._ffz_on_state);
|
|
||||||
off(p, 'playing', inst._ffz_on_state);
|
|
||||||
off(p, 'error', inst._ffz_on_state);
|
|
||||||
}
|
|
||||||
|
|
||||||
inst._ffz_on_state = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
addEndedListener(inst) {
|
|
||||||
let p = inst.player;
|
|
||||||
if ( ! p )
|
|
||||||
return;
|
|
||||||
|
|
||||||
if ( p.player )
|
|
||||||
p = p.player;
|
|
||||||
|
|
||||||
if ( inst._ffz_on_ended )
|
|
||||||
off(p, 'ended', inst._ffz_on_ended);
|
|
||||||
|
|
||||||
on(p, 'ended', inst._ffz_on_ended = async () => {
|
|
||||||
if ( this.settings.get('player.vod.autoplay') )
|
|
||||||
return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
(await this.parent.awaitElement('.pl-rec__cancel', inst.playerRef, 1000)).click();
|
|
||||||
} catch(err) { /* do nothing~ */ }
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
addControlVisibility(inst) { // eslint-disable-line class-methods-use-this
|
|
||||||
const p = inst.playerRef;
|
|
||||||
if ( ! p )
|
|
||||||
return;
|
|
||||||
|
|
||||||
if ( inst._ffz_visibility_handler ) {
|
|
||||||
off(p, 'mousemove', inst._ffz_visibility_handler);
|
|
||||||
off(p, 'mouseleave', inst._ffz_visibility_handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
let timer;
|
|
||||||
|
|
||||||
const c = () => { p.dataset.controls = false };
|
|
||||||
const f = inst._ffz_visibility_handler = e => {
|
|
||||||
clearTimeout(timer);
|
|
||||||
if ( e.type === 'mouseleave' )
|
|
||||||
return c();
|
|
||||||
|
|
||||||
timer = setTimeout(c, 5000);
|
|
||||||
p.dataset.controls = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
on(p, 'mousemove', f);
|
|
||||||
on(p, 'mouseleave', f);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
addStateTags(inst) {
|
|
||||||
let p = inst.player;
|
|
||||||
if ( ! p )
|
|
||||||
return;
|
|
||||||
|
|
||||||
if ( p.player )
|
|
||||||
p = p.player;
|
|
||||||
|
|
||||||
if ( inst._ffz_on_state ) {
|
|
||||||
off(p, 'ended', inst._ffz_on_state);
|
|
||||||
off(p, 'pause', inst._ffz_on_state);
|
|
||||||
off(p, 'playing', inst._ffz_on_state);
|
|
||||||
off(p, 'error', inst._ffz_on_state);
|
|
||||||
}
|
|
||||||
|
|
||||||
const f = inst._ffz_on_state = () => this.updateStateTags(inst);
|
|
||||||
|
|
||||||
on(p, 'ended', f);
|
|
||||||
on(p, 'pause', f);
|
|
||||||
on(p, 'playing', f);
|
|
||||||
on(p, 'error', f);
|
|
||||||
|
|
||||||
f();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
updateStateTags(inst) { // eslint-disable-line class-methods-use-this
|
|
||||||
const p = inst.playerRef;
|
|
||||||
let player = inst.player;
|
|
||||||
if ( ! p || ! player )
|
|
||||||
return;
|
|
||||||
|
|
||||||
if ( player.player )
|
|
||||||
player = player.player;
|
|
||||||
|
|
||||||
p.dataset.ended = player.ended;
|
|
||||||
p.dataset.paused = player.paused;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
disableAutoplay(inst) {
|
|
||||||
let p = inst.player;
|
|
||||||
if ( ! p )
|
|
||||||
return this.log.warn('disableAutoplay() called without Player');
|
|
||||||
|
|
||||||
if ( p.player )
|
|
||||||
p = p.player;
|
|
||||||
|
|
||||||
if ( p.readyState > 0 ) {
|
|
||||||
this.log.info('Player already playing. Pausing.');
|
|
||||||
return p.pause();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( ! inst._ffz_autoplay_handler ) {
|
|
||||||
const listener = inst._ffz_autoplay_handler = () => {
|
|
||||||
setTimeout(() => {
|
|
||||||
this.log.info('Pausing due to playback.');
|
|
||||||
inst._ffz_autoplay_handler = null;
|
|
||||||
p.pause();
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
off(p, 'play', listener);
|
|
||||||
off(p, 'playing', listener);
|
|
||||||
off(p, 'contentShowing', listener);
|
|
||||||
}, 250);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
on(p, 'play', listener);
|
|
||||||
on(p, 'playing', listener);
|
|
||||||
on(p, 'contentShowing', listener);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
updateVolumeScroll(inst, enabled) {
|
|
||||||
if ( enabled === undefined )
|
|
||||||
enabled = this.settings.get('player.volume-scroll');
|
|
||||||
|
|
||||||
const pr = inst.playerRef;
|
|
||||||
if ( ! pr )
|
|
||||||
return;
|
|
||||||
|
|
||||||
if ( ! enabled && inst._ffz_scroll_handler ) {
|
|
||||||
off(pr, 'wheel', inst._ffz_scroll_handler);
|
|
||||||
inst._ffz_scroll_handler = null;
|
|
||||||
|
|
||||||
} else if ( enabled && ! inst._ffz_scroll_handler ) {
|
|
||||||
on(pr, 'wheel', inst._ffz_scroll_handler = e => {
|
|
||||||
const delta = e.wheelDelta || -(e.deltaY || e.detail || 0);
|
|
||||||
let player = inst.player;
|
|
||||||
if ( player.player )
|
|
||||||
player = player.player;
|
|
||||||
|
|
||||||
if ( player ) {
|
|
||||||
const amount = this.settings.get('player.volume-scroll-steps'),
|
|
||||||
volume = Math.max(0, Math.min(1, player.getVolume() + (delta > 0 ? amount : -amount)));
|
|
||||||
|
|
||||||
player.setVolume(volume);
|
|
||||||
if ( volume !== 0 )
|
|
||||||
player.setMuted(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
e.preventDefault();
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
addPiPButton(inst, tries = 0) {
|
addPiPButton(inst, tries = 0) {
|
||||||
const t = this,
|
const outer = inst.props.containerRef || this.fine.getChildNode(inst),
|
||||||
el = inst.playerRef && inst.playerRef.querySelector('.player-buttons-right .pl-flex'),
|
container = outer && outer.querySelector('.player-controls__right-control-group'),
|
||||||
container = el && el.parentElement,
|
|
||||||
has_pip = document.pictureInPictureEnabled && this.settings.get('player.button.pip');
|
has_pip = document.pictureInPictureEnabled && this.settings.get('player.button.pip');
|
||||||
|
|
||||||
if ( ! container ) {
|
if ( ! container ) {
|
||||||
|
@ -791,42 +724,54 @@ export default class Player extends Module {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if ( tries < 5 )
|
if ( tries < 5 )
|
||||||
return setTimeout(this.addPiPButton.bind(this, inst, (tries||0) + 1), 250);
|
return setTimeout(this.addPiPButton.bind(this, inst, (tries || 0) + 1), 250);
|
||||||
|
|
||||||
return this.log.warn('Unable to find container element for PiP Button');
|
return this.log.warn('Unable to find container element for PiP button.');
|
||||||
}
|
}
|
||||||
|
|
||||||
let tip, btn = container.querySelector('.ffz--player-pip');
|
let icon, tip, btn, cont = container.querySelector('.ffz--player-pip');
|
||||||
if ( ! has_pip ) {
|
if ( ! has_pip ) {
|
||||||
if ( btn )
|
if ( cont )
|
||||||
btn.remove();
|
cont.remove();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ! btn ) {
|
if ( ! cont ) {
|
||||||
btn = (<button
|
cont = (<div class="ffz--player-pip tw-inline-flex tw-relative tw-tooltip-wrapper">
|
||||||
class="player-button player-button--pip ffz--player-pip ffz-i-window-restore"
|
{btn = (<button
|
||||||
type="button"
|
class="tw-align-items-center tw-align-middle tw-border-bottom-left-radius-medium tw-border-bottom-right-radius-medium tw-border-top-left-radius-medium tw-border-top-right-radius-medium tw-button-icon tw-button-icon--overlay tw-core-button tw-core-button--border tw-core-button--overlay tw-inline-flex tw-interactive tw-justify-content-center tw-overflow-hidden tw-relative"
|
||||||
onClick={t.pipPlayer.bind(t, inst)} // eslint-disable-line react/jsx-no-bind
|
type="button"
|
||||||
>
|
data-a-target="ffz-player-pip-button"
|
||||||
{tip = <span class="player-tip js-control-tip" />}
|
onClick={this.pipPlayer.bind(this, inst)} // eslint-disable-line react/jsx-no-bind
|
||||||
</button>);
|
>
|
||||||
|
<div class="tw-align-items-center tw-flex tw-flex-grow-0">
|
||||||
|
<div class="tw-button-icon__icon">
|
||||||
|
{icon = (<figure />)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</button>)}
|
||||||
|
{tip = (<div class="tw-tooltip tw-tooltip--align-right tw-tooltip--up" role="tooltip" />)}
|
||||||
|
</div>);
|
||||||
|
|
||||||
container.insertBefore(btn, el.nextSibling);
|
container.appendChild(cont);
|
||||||
|
|
||||||
} else
|
} else {
|
||||||
tip = btn.querySelector('.player-tip');
|
icon = cont.querySelector('figure');
|
||||||
|
btn = cont.querySelector('button');
|
||||||
|
tip = cont.querySelector('.tw-tooltip');
|
||||||
|
}
|
||||||
|
|
||||||
const pip_active = !!document.pictureInPictureElement
|
const pip_active = !!document.pictureInPictureElement
|
||||||
|
|
||||||
btn.classList.toggle('ffz-i-window-restore', ! pip_active);
|
icon.classList.toggle('ffz-i-window-restore', ! pip_active);
|
||||||
btn.classList.toggle('ffz-i-window-maximize', pip_active);
|
icon.classList.toggle('ffz-i-window-maximize', pip_active);
|
||||||
|
|
||||||
tip.dataset.tip = this.i18n.t('player.pip_button', 'Click to Toggle Picture-in-Picture');
|
btn.setAttribute('aria-label', tip.textContent = this.i18n.t('player.pip_button', 'Click to Toggle Picture-in-Picture'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pipPlayer(inst) {
|
pipPlayer(inst) {
|
||||||
const video = inst.playerRef && inst.playerRef.querySelector('video');
|
const video = inst.props.mediaPlayerInstance?.mediaSinkManager?.video;
|
||||||
if ( ! video || ! document.pictureInPictureEnabled )
|
if ( ! video || ! document.pictureInPictureEnabled )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -847,84 +792,51 @@ export default class Player extends Module {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
addResetButton(inst, tries = 0) {
|
tryTheatreMode(inst) {
|
||||||
const t = this,
|
if ( ! this.settings.get('player.theatre.auto-enter') )
|
||||||
el = inst.playerRef && inst.playerRef.querySelector('.player-buttons-right .pl-flex'),
|
|
||||||
container = el && el.parentElement;
|
|
||||||
|
|
||||||
if ( ! this.settings.get('player.button.reset') ) {
|
|
||||||
if ( container ) {
|
|
||||||
const btn = container.querySelector('.ffz--player-reset');
|
|
||||||
if ( btn )
|
|
||||||
btn.remove();
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if ( inst?.props?.onTheatreModeEnabled )
|
||||||
|
inst.props.onTheatreModeEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries to reposition the player, using a method exposed on the
|
||||||
|
* Squad Streaming bar.
|
||||||
|
*
|
||||||
|
* @memberof Player
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
repositionPlayer() {
|
||||||
|
for(const inst of this.SquadStreamBar.instances) {
|
||||||
|
if ( inst?.props?.triggerPlayerReposition ) {
|
||||||
|
inst.props.triggerPlayerReposition();
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ! container ) {
|
|
||||||
if ( tries < 5 )
|
|
||||||
return setTimeout(this.addResetButton.bind(this, inst, (tries||0) + 1), 250);
|
|
||||||
|
|
||||||
return this.log.warn('Unable to find container element for Reset Button');
|
|
||||||
}
|
|
||||||
|
|
||||||
let tip = container.querySelector('.ffz--player-reset .player-tip');
|
|
||||||
|
|
||||||
if ( ! tip )
|
|
||||||
container.insertBefore(<button
|
|
||||||
class="player-button player-button--reset ffz--player-reset ffz-i-cancel"
|
|
||||||
type="button"
|
|
||||||
onDblClick={t.resetPlayer.bind(t, inst)} // eslint-disable-line react/jsx-no-bind
|
|
||||||
>
|
|
||||||
{tip = <span class="player-tip js-control-tip" />}
|
|
||||||
</button>, el.nextSibling);
|
|
||||||
|
|
||||||
tip.dataset.tip = this.i18n.t('player.reset_button', 'Double-Click to Reset Player');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateSquadContext() {
|
||||||
resetPlayer(inst) {
|
this.settings.updateContext({
|
||||||
// Player shutdown logic copied from componentWillUnmount
|
squad_bar: this.hasSquadBar
|
||||||
inst.checkPlayerDependencyAnimationFrame && cancelAnimationFrame(inst.checkPlayerDependencyAnimationFrame);
|
});
|
||||||
inst.checkPlayerDependencyAnimationFrame = null;
|
|
||||||
inst.maybeDetachFromWindow();
|
|
||||||
|
|
||||||
const ES = this.web_munch.getModule('extension-service');
|
|
||||||
if ( ES )
|
|
||||||
ES.extensionService.unregisterPlayer();
|
|
||||||
|
|
||||||
this.cleanup(inst);
|
|
||||||
|
|
||||||
const klass = inst.player.constructor;
|
|
||||||
|
|
||||||
inst.player.destroy();
|
|
||||||
inst.playerRef.innerHTML = '';
|
|
||||||
|
|
||||||
inst.initializePlayer(klass);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get hasSquadBar() {
|
||||||
getInternalPlayer(inst) {
|
const inst = this.SquadStreamBar.first;
|
||||||
if ( ! inst )
|
return inst ? inst.shouldRenderSquadBanner(inst.props) : false
|
||||||
inst = this.Player.first;
|
|
||||||
|
|
||||||
const node = this.fine.getChildNode(inst),
|
|
||||||
el = node && node.querySelector('.player-ui');
|
|
||||||
|
|
||||||
if ( ! el || ! el._reactRootContainer )
|
|
||||||
return null;
|
|
||||||
|
|
||||||
return this.fine.searchTree(el, n => n.props && n.props.player && n.context && n.context.store);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get playerUI() {
|
||||||
|
const container = this.fine.searchTree(this.Player.first, n => n.props && n.props.uiContext, 150);
|
||||||
|
return container?.props?.uiContext;
|
||||||
|
}
|
||||||
|
|
||||||
get current() {
|
get current() {
|
||||||
// There should only ever be one player instance, but might change
|
|
||||||
// when they re-add support for the mini player.
|
|
||||||
for(const inst of this.Player.instances)
|
for(const inst of this.Player.instances)
|
||||||
if ( inst && inst.player )
|
if ( inst?.props?.mediaPlayerInstance )
|
||||||
return inst.player;
|
return inst.props.mediaPlayerInstance;
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,8 +77,8 @@ export default class Fine extends Module {
|
||||||
return instance.return;
|
return instance.return;
|
||||||
}
|
}
|
||||||
|
|
||||||
getParentNode(instance) {
|
getParentNode(instance, max_depth = 100, traverse_roots = false) {
|
||||||
if ( instance._reactInternalFiber )
|
/*if ( instance._reactInternalFiber )
|
||||||
instance = instance._reactInternalFiber;
|
instance = instance._reactInternalFiber;
|
||||||
else if ( instance instanceof Node )
|
else if ( instance instanceof Node )
|
||||||
instance = this.getReactInstance(instance);
|
instance = this.getReactInstance(instance);
|
||||||
|
@ -87,11 +87,13 @@ export default class Fine extends Module {
|
||||||
if ( instance.stateNode instanceof Node )
|
if ( instance.stateNode instanceof Node )
|
||||||
return instance.stateNode
|
return instance.stateNode
|
||||||
else
|
else
|
||||||
instance = instance.parent;
|
instance = instance.parent;*/
|
||||||
|
|
||||||
|
return this.searchParent(instance, n => n instanceof Node, max_depth, 0, traverse_roots);
|
||||||
}
|
}
|
||||||
|
|
||||||
getChildNode(instance, max_depth = 100) {
|
getChildNode(instance, max_depth = 100, traverse_roots = false) {
|
||||||
if ( instance._reactInternalFiber )
|
/*if ( instance._reactInternalFiber )
|
||||||
instance = instance._reactInternalFiber;
|
instance = instance._reactInternalFiber;
|
||||||
else if ( instance instanceof Node )
|
else if ( instance instanceof Node )
|
||||||
instance = this.getReactInstance(instance);
|
instance = this.getReactInstance(instance);
|
||||||
|
@ -104,7 +106,9 @@ export default class Fine extends Module {
|
||||||
if ( max_depth < 0 )
|
if ( max_depth < 0 )
|
||||||
return null;
|
return null;
|
||||||
instance = instance.child;
|
instance = instance.child;
|
||||||
}
|
}*/
|
||||||
|
|
||||||
|
return this.searchTree(instance, n => n instanceof Node, max_depth, 0, traverse_roots);
|
||||||
}
|
}
|
||||||
|
|
||||||
getHostNode(instance, max_depth = 100) {
|
getHostNode(instance, max_depth = 100) {
|
||||||
|
@ -243,6 +247,25 @@ export default class Fine extends Module {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
findAllMatching(node, criteria, max_depth=15, parents=false, depth=0, traverse_roots=true) {
|
||||||
|
const matches = new Set,
|
||||||
|
crit = n => ! matches.has(n) && criteria(n);
|
||||||
|
|
||||||
|
while(true) {
|
||||||
|
const match = parents ?
|
||||||
|
this.searchParent(node, crit, max_depth, depth, traverse_roots) :
|
||||||
|
this.searchTree(node, crit, max_depth, depth, traverse_roots);
|
||||||
|
|
||||||
|
if ( ! match )
|
||||||
|
break;
|
||||||
|
|
||||||
|
matches.add(match);
|
||||||
|
}
|
||||||
|
|
||||||
|
return matches;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
searchAll(node, criterias, max_depth=15, depth=0, data, traverse_roots = true) {
|
searchAll(node, criterias, max_depth=15, depth=0, data, traverse_roots = true) {
|
||||||
if ( ! node )
|
if ( ! node )
|
||||||
node = this.react;
|
node = this.react;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue