PK !A A Βvideo-wrappers/netflix.jsPK !< WZIIFvideo-wrappers/nytimes.jsPK !<9"s|Ơvideo-wrappers/piped.jsPK ! { const prefName = `${extensionPrefNameBase}${name}`; const callback = () => { fire.async(name).catch(() => {}); // ignore Message Manager disconnects }; Services.prefs.addObserver(prefName, callback); return () => { Services.prefs.removeObserver(prefName, callback); }; }, }).api(), /** * Calls `Services.prefs.getBoolPref` to get a preference * @param {String} name The name of the preference to get; will be prefixed with this extension's branch * @returns the preference, or undefined */ async getPref(name) { try { return Services.prefs.getBoolPref( `${extensionPrefNameBase}${name}` ); } catch (_) { return undefined; } }, /** * Calls `Services.prefs.setBoolPref` to set a preference * @param {String} name the name of the preference to set; will be prefixed with this extension's branch * @param {String} value the bool value to save in the pref */ async setPref(name, value) { Services.prefs.setBoolPref(`${extensionPrefNameBase}${name}`, value); }, }, }; } }; PK !< #experiment-apis/pictureInPicture.js/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; /* global AppConstants, ChromeUtils, ExtensionAPI, Services */ ChromeUtils.defineESModuleGetters(this, { KEYBOARD_CONTROLS: "resource://gre/modules/PictureInPictureControls.sys.mjs", TOGGLE_POLICIES: "resource://gre/modules/PictureInPictureControls.sys.mjs", }); const TOGGLE_ENABLED_PREF = "media.videocontrols.picture-in-picture.video-toggle.enabled"; /** * This API is expected to be running in the parent process. */ this.pictureInPictureParent = class extends ExtensionAPI { /** * Override ExtensionAPI with PiP override's specific API * Relays the site overrides to this extension's child process * @param {ExtensionContext} context the context of our extension * @returns {Object} returns the necessary API structure required to manage sharedData in PictureInPictureParent */ getAPI(context) { return { pictureInPictureParent: { setOverrides(overrides) { // The Picture-in-Picture toggle is only implemented for Desktop, so make // this a no-op for non-Desktop builds. if (AppConstants.platform == "android") { return; } Services.ppmm.sharedData.set( "PictureInPicture:SiteOverrides", overrides ); }, }, }; } }; /** * This API is expected to be running in a content process - specifically, * the WebExtension content process that the background scripts run in. We * split these out so that they can return values synchronously to the * background scripts. */ this.pictureInPictureChild = class extends ExtensionAPI { /** * Override ExtensionAPI with PiP override's specific API * Clone constants into the Picture-in-Picture child process * @param {ExtensionContext} context the context of our extension * @returns returns the necessary API structure required to get data from PictureInPictureChild */ getAPI(context) { return { pictureInPictureChild: { getKeyboardControls() { // The Picture-in-Picture toggle is only implemented for Desktop, so make // this return nothing for non-Desktop builds. if (AppConstants.platform == "android") { return Cu.cloneInto({}, context.cloneScope); } return Cu.cloneInto(KEYBOARD_CONTROLS, context.cloneScope); }, getPolicies() { // The Picture-in-Picture toggle is only implemented for Desktop, so make // this return nothing for non-Desktop builds. if (AppConstants.platform == "android") { return Cu.cloneInto({}, context.cloneScope); } return Cu.cloneInto(TOGGLE_POLICIES, context.cloneScope); }, }, }; } }; PK !<91 $data/picture_in_picture_overrides.js/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; /* globals browser */ let AVAILABLE_PIP_OVERRIDES; { // See PictureInPictureControls.sys.mjs for these values. // eslint-disable-next-line no-unused-vars const TOGGLE_POLICIES = browser.pictureInPictureChild.getPolicies(); const KEYBOARD_CONTROLS = browser.pictureInPictureChild.getKeyboardControls(); AVAILABLE_PIP_OVERRIDES = { // The keys of this object are match patterns for URLs, as documented in // https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Match_patterns // // Example: // const KEYBOARD_CONTROLS = browser.pictureInPictureChild.getKeyboardControls(); // // // "https://*.youtube.com/*": { // policy: TOGGLE_POLICIES.THREE_QUARTERS, // disabledKeyboardControls: KEYBOARD_CONTROLS.PLAY_PAUSE | KEYBOARD_CONTROLS.VOLUME, // }, // "https://*.twitch.tv/mikeconley_dot_ca/*": { // policy: TOGGLE_POLICIES.TOP, // disabledKeyboardControls: KEYBOARD_CONTROLS.ALL, // }, tests: { // FOR TESTS ONLY! "https://mochitest.youtube.com/*browser/browser/extensions/pictureinpicture/tests/browser/test-mock-wrapper.html": { videoWrapperScriptPath: "video-wrappers/mock-wrapper.js", }, "https://mochitest.youtube.com/*browser/browser/extensions/pictureinpicture/tests/browser/test-toggle-visibility.html": { videoWrapperScriptPath: "video-wrappers/mock-wrapper.js", }, }, abcnews: { "https://*.abcnews.go.com/*": { videoWrapperScriptPath: "video-wrappers/videojsWrapper.js", }, }, airmozilla: { "https://*.mozilla.hosted.panopto.com/*": { videoWrapperScriptPath: "video-wrappers/airmozilla.js", }, }, aol: { "https://*.aol.com/*": { videoWrapperScriptPath: "video-wrappers/yahoo.js", }, }, bbc: { "https://*.bbc.com/*": { videoWrapperScriptPath: "video-wrappers/bbc.js", }, "https://*.bbc.co.uk/*": { videoWrapperScriptPath: "video-wrappers/bbc.js", }, }, brightcove: { "https://*.brightcove.com/*": { videoWrapperScriptPath: "video-wrappers/videojsWrapper.js", }, }, cbc: { "https://*.cbc.ca/*": { videoWrapperScriptPath: "video-wrappers/cbc.js", }, }, dailymotion: { "https://*.dailymotion.com/*": { videoWrapperScriptPath: "video-wrappers/dailymotion.js", }, }, disneyplus: { "https://*.disneyplus.com/*": { videoWrapperScriptPath: "video-wrappers/disneyplus.js", }, }, edx: { "https://*.edx.org/*": { videoWrapperScriptPath: "video-wrappers/edx.js", }, }, frontendMasters: { "https://*.frontendmasters.com/*": { videoWrapperScriptPath: "video-wrappers/videojsWrapper.js", }, }, funimation: { "https://*.funimation.com/*": { videoWrapperScriptPath: "video-wrappers/videojsWrapper.js", }, }, hbomax: { "https://play.hbomax.com/page/*": { policy: TOGGLE_POLICIES.HIDDEN }, "https://play.hbomax.com/player/*": { videoWrapperScriptPath: "video-wrappers/hbomax.js", }, }, hotstar: { "https://*.hotstar.com/*": { videoWrapperScriptPath: "video-wrappers/hotstar.js", }, }, hulu: { "https://www.hulu.com/watch/*": { videoWrapperScriptPath: "video-wrappers/hulu.js", }, }, instagram: { "https://www.instagram.com/*": { policy: TOGGLE_POLICIES.ONE_QUARTER }, }, laracasts: { "https://*.laracasts.com/*": { policy: TOGGLE_POLICIES.ONE_QUARTER }, }, msn: { "https://*.msn.com/*": { visibilityThreshold: 0.7, }, }, mxplayer: { "https://*.mxplayer.in/*": { videoWrapperScriptPath: "video-wrappers/videojsWrapper.js", }, }, nebula: { "https://*.nebula.app/*": { videoWrapperScriptPath: "video-wrappers/videojsWrapper.js", }, }, netflix: { "https://*.netflix.com/*": { videoWrapperScriptPath: "video-wrappers/netflix.js", }, "https://*.netflix.com/browse*": { policy: TOGGLE_POLICIES.HIDDEN }, "https://*.netflix.com/latest*": { policy: TOGGLE_POLICIES.HIDDEN }, "https://*.netflix.com/Kids*": { policy: TOGGLE_POLICIES.HIDDEN }, "https://*.netflix.com/title*": { policy: TOGGLE_POLICIES.HIDDEN }, "https://*.netflix.com/notification*": { policy: TOGGLE_POLICIES.HIDDEN }, "https://*.netflix.com/search*": { policy: TOGGLE_POLICIES.HIDDEN }, }, nytimes: { "https://*.nytimes.com/*": { videoWrapperScriptPath: "video-wrappers/nytimes.js", }, }, pbs: { "https://*.pbs.org/*": { videoWrapperScriptPath: "video-wrappers/videojsWrapper.js", }, "https://*.pbskids.org/*": { videoWrapperScriptPath: "video-wrappers/videojsWrapper.js", }, }, piped: { "https://*.piped.kavin.rocks/*": { videoWrapperScriptPath: "video-wrappers/piped.js", }, "https://*.piped.silkky.cloud/*": { videoWrapperScriptPath: "video-wrappers/piped.js", }, }, radiocanada: { "https://*.ici.radio-canada.ca/*": { videoWrapperScriptPath: "video-wrappers/radiocanada.js", }, }, reddit: { "https://*.reddit.com/*": { policy: TOGGLE_POLICIES.ONE_QUARTER }, }, sonyliv: { "https://*.sonyliv.com/*": { videoWrapperScriptPath: "video-wrappers/sonyliv.js", }, }, ted: { "https://*.ted.com/*": { showHiddenTextTracks: true, }, }, tubi: { "https://*.tubitv.com/live*": { videoWrapperScriptPath: "video-wrappers/tubilive.js", }, "https://*.tubitv.com/movies*": { videoWrapperScriptPath: "video-wrappers/tubi.js", }, "https://*.tubitv.com/tv-shows*": { videoWrapperScriptPath: "video-wrappers/tubi.js", }, }, twitch: { "https://*.twitch.tv/*": { videoWrapperScriptPath: "video-wrappers/twitch.js", policy: TOGGLE_POLICIES.ONE_QUARTER, disabledKeyboardControls: KEYBOARD_CONTROLS.LIVE_SEEK, }, "https://*.twitch.tech/*": { videoWrapperScriptPath: "video-wrappers/twitch.js", policy: TOGGLE_POLICIES.ONE_QUARTER, disabledKeyboardControls: KEYBOARD_CONTROLS.LIVE_SEEK, }, "https://*.twitch.a2z.com/*": { videoWrapperScriptPath: "video-wrappers/twitch.js", policy: TOGGLE_POLICIES.ONE_QUARTER, disabledKeyboardControls: KEYBOARD_CONTROLS.LIVE_SEEK, }, }, udemy: { "https://*.udemy.com/*": { videoWrapperScriptPath: "video-wrappers/udemy.js", policy: TOGGLE_POLICIES.ONE_QUARTER, }, }, voot: { "https://*.voot.com/*": { videoWrapperScriptPath: "video-wrappers/voot.js", }, }, wired: { "https://*.wired.com/*": { videoWrapperScriptPath: "video-wrappers/videojsWrapper.js", }, }, yahoofinance: { "https://*.finance.yahoo.com/*": { videoWrapperScriptPath: "video-wrappers/yahoo.js", }, }, youtube: { /** * The threshold of 0.7 is so that users can click on the "Skip Ads" * button on the YouTube site player without accidentally triggering * PiP. */ "https://*.youtube.com/*": { visibilityThreshold: 0.7, videoWrapperScriptPath: "video-wrappers/youtube.js", }, "https://*.youtube-nocookie.com/*": { visibilityThreshold: 0.9, videoWrapperScriptPath: "video-wrappers/youtube.js", }, }, washingtonpost: { "https://*.washingtonpost.com/*": { videoWrapperScriptPath: "video-wrappers/washingtonpost.js", }, }, primeVideo: { "https://*.primevideo.com/*": { visibilityThreshold: 0.9, videoWrapperScriptPath: "video-wrappers/primeVideo.js", }, "https://*.amazon.com/*": { visibilityThreshold: 0.9, videoWrapperScriptPath: "video-wrappers/primeVideo.js", }, }, }; } PK !<cYY(experiment-apis/aboutConfigPipPrefs.json[ { "namespace": "aboutConfigPipPrefs", "description": "experimental API extension to allow access to about:config preferences", "events": [ { "name": "onPrefChange", "type": "function", "parameters": [ { "name": "name", "type": "string", "description": "The preference which changed" } ], "extraParameters": [ { "name": "name", "type": "string", "description": "The preference to monitor" } ] } ], "functions": [ { "name": "getPref", "type": "function", "description": "Get a preference's value", "parameters": [ { "name": "name", "type": "string", "description": "The preference name" } ], "async": true }, { "name": "setPref", "type": "function", "description": "Set a preference's value", "parameters": [ { "name": "name", "type": "string", "description": "The preference name" }, { "name": "value", "type": "boolean", "description": "The new value" } ], "async": true } ] } ] PK !<ڷ$<<%experiment-apis/pictureInPicture.json[ { "namespace": "pictureInPictureParent", "description": "Parent process methods for controlling the Picture-in-Picture feature.", "functions": [ { "name": "setOverrides", "type": "function", "description": "Set Picture-in-Picture toggle position overrides", "parameters": [ { "name": "overrides", "type": "object", "additionalProperties": { "type": "any" }, "description": "The Picture-in-Picture toggle position overrides to set" } ] } ] }, { "namespace": "pictureInPictureChild", "description": "WebExtension process methods for querying the Picture-in-Picture feature.", "functions": [ { "name": "getKeyboardControls", "type": "function", "description": "Get the Picture-in-Picture keyboard control override constants", "parameters": [], "returns": { "type": "object", "properties": {}, "additionalProperties": { "type": "any" }, "description": "The Picture-in-Picture keyboard control override constants" } }, { "name": "getPolicies", "type": "function", "description": "Get the Picture-in-Picture toggle position override constants", "parameters": [], "returns": { "type": "object", "properties": {}, "additionalProperties": { "type": "any" }, "description": "The Picture-in-Picture toggle position override constants" } } ] } ] PK ! { if (value === false) { this._enabled = false; } else { if (value === undefined) { browser.aboutConfigPipPrefs.setPref(this.pref, true); } this._enabled = true; } }); } /** * Checks the status of a specified override, and updates the set, `this._prefEnabledOverrides`, accordingly * @param {String} id the id of the specific override contained in `this._availableOverrides` * @param {String} pref the specific preference to check, in the form `disabled_picture_in_picture_overrides.${id}` */ async _checkSpecificOverridePref(id, pref) { const isDisabled = await browser.aboutConfigPipPrefs.getPref(pref); if (isDisabled === true) { this._prefEnabledOverrides.delete(id); } else { this._prefEnabledOverrides.add(id); } } /** * The function that `run.js` calls to begin checking for changes to the PiP overrides */ bootup() { const checkGlobal = async () => { await this._checkGlobalPref(); this._onAvailableOverridesChanged(); }; browser.aboutConfigPipPrefs.onPrefChange.addListener( checkGlobal, this.pref ); const bootupPrefCheckPromises = [this._checkGlobalPref()]; for (const id of Object.keys(this._availableOverrides)) { const pref = `disabled_picture_in_picture_overrides.${id}`; const checkSingle = async () => { await this._checkSpecificOverridePref(id, pref); this._onAvailableOverridesChanged(); }; browser.aboutConfigPipPrefs.onPrefChange.addListener(checkSingle, pref); bootupPrefCheckPromises.push(this._checkSpecificOverridePref(id, pref)); } Promise.all(bootupPrefCheckPromises).then(() => { this._onAvailableOverridesChanged(); }); } /** * Sets pictureInPictureParent's overrides */ async _onAvailableOverridesChanged() { const policies = await this.policies; let enabledOverrides = {}; for (const [id, override] of Object.entries(this._availableOverrides)) { const enabled = this._enabled && this._prefEnabledOverrides.has(id); for (const [url, policy] of Object.entries(override)) { enabledOverrides[url] = enabled ? policy : policies.DEFAULT; } } browser.pictureInPictureParent.setOverrides(enabledOverrides); } } PK !<. manifest.json{ "manifest_version": 2, "name": "Picture-In-Picture", "description": "Fixes for web compatibility with Picture-in-Picture", "version": "1.0.0", "browser_specific_settings": { "gecko": { "id": "pictureinpicture@mozilla.org", "strict_min_version": "88.0a1" } }, "experiment_apis": { "aboutConfigPipPrefs": { "schema": "experiment-apis/aboutConfigPipPrefs.json", "parent": { "scopes": ["addon_parent"], "script": "experiment-apis/aboutConfigPipPrefs.js", "paths": [["aboutConfigPipPrefs"]] } }, "pictureInPictureChild": { "schema": "experiment-apis/pictureInPicture.json", "child": { "scopes": ["addon_child"], "script": "experiment-apis/pictureInPicture.js", "paths": [["pictureInPictureChild"]] } }, "pictureInPictureParent": { "schema": "experiment-apis/pictureInPicture.json", "parent": { "scopes": ["addon_parent"], "script": "experiment-apis/pictureInPicture.js", "paths": [["pictureInPictureParent"]] } } }, "background": { "scripts": [ "data/picture_in_picture_overrides.js", "lib/picture_in_picture_overrides.js", "run.js" ] } } PK !<2run.js/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; /* globals AVAILABLE_PIP_OVERRIDES, PictureInPictureOverrides */ const pipOverrides = new PictureInPictureOverrides(AVAILABLE_PIP_OVERRIDES); pipOverrides.bootup(); PK !<'sxxvideo-wrappers/airmozilla.js/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; class PictureInPictureVideoWrapper { play(video) { let playPauseButton = document.querySelector( "#transportControls #playButton" ); if (video.paused) { playPauseButton?.click(); } } pause(video) { let playPauseButton = document.querySelector( "#transportControls #playButton" ); if (!video.paused) { playPauseButton?.click(); } } setMuted(video, shouldMute) { let muteButton = document.querySelector("#transportControls #muteButton"); if (video.muted !== shouldMute && muteButton) { muteButton.click(); } } setCaptionContainerObserver(video, updateCaptionsFunction) { let container = document.querySelector("#absoluteControls"); if (container) { updateCaptionsFunction(""); const callback = function (mutationsList, observer) { let text = container?.querySelector("#overlayCaption").innerText; if (!text) { updateCaptionsFunction(""); return; } updateCaptionsFunction(text); }; // immediately invoke the callback function to add subtitles to the PiP window callback([1], null); let captionsObserver = new MutationObserver(callback); captionsObserver.observe(container, { attributes: false, childList: true, subtree: true, }); } } } this.PictureInPictureVideoWrapper = PictureInPictureVideoWrapper; PK !<dvideo-wrappers/bbc.js/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; class PictureInPictureVideoWrapper { setCaptionContainerObserver(video, updateCaptionsFunction) { let container = document.querySelector(".p_subtitlesContainer"); if (container) { updateCaptionsFunction(""); const callback = function (mutationsList, observer) { let text = container.querySelector(".p_cueDirUniWrapper")?.innerText; updateCaptionsFunction(text); }; callback([1], null); let captionsObserver = new MutationObserver(callback); captionsObserver.observe(container, { attributes: false, childList: true, subtree: true, }); } } } this.PictureInPictureVideoWrapper = PictureInPictureVideoWrapper; PK !<@UEEvideo-wrappers/cbc.js/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; class PictureInPictureVideoWrapper { play(video) { let playPauseButton = document.querySelector(".video-ui .play-button"); if (video.paused) { playPauseButton?.click(); } } pause(video) { let playPauseButton = document.querySelector(".video-ui .pause-button"); if (!video.paused) { playPauseButton?.click(); } } setMuted(video, shouldMute) { let muteButton = document.querySelector(".video-ui .muted-btn"); if (video.muted !== shouldMute && muteButton) { muteButton.click(); } } } this.PictureInPictureVideoWrapper = PictureInPictureVideoWrapper; PK !<Fvideo-wrappers/dailymotion.js/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; class PictureInPictureVideoWrapper { setCaptionContainerObserver(video, updateCaptionsFunction) { let container = document.querySelector("#player"); if (container) { updateCaptionsFunction(""); const callback = function (mutationsList, observer) { let textNodeList = container ?.querySelector(".subtitles") ?.querySelectorAll("div"); if (!textNodeList) { updateCaptionsFunction(""); return; } updateCaptionsFunction( Array.from(textNodeList, x => x.innerText).join("\n") ); }; // immediately invoke the callback function to add subtitles to the PiP window callback([1], null); let captionsObserver = new MutationObserver(callback); captionsObserver.observe(container, { attributes: false, childList: true, subtree: true, }); } } } this.PictureInPictureVideoWrapper = PictureInPictureVideoWrapper; PK ! { let textNodeList = container.querySelectorAll( ".dss-subtitle-renderer-line" ); if (!textNodeList.length) { updateCaptionsFunction(""); return; } updateCaptionsFunction( Array.from(textNodeList, x => x.textContent).join("\n") ); }; // immediately invoke the callback function to add subtitles to the PiP window callback(); let captionsObserver = new MutationObserver(callback); captionsObserver.observe(container, { attributes: false, childList: true, subtree: true, }); } } } this.PictureInPictureVideoWrapper = PictureInPictureVideoWrapper; PK !<=(Gvideo-wrappers/edx.js/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; class PictureInPictureVideoWrapper { setCaptionContainerObserver(video, updateCaptionsFunction) { let container = document.querySelector(".video-wrapper"); if (container) { updateCaptionsFunction(""); const callback = function (mutationsList, observer) { let text = container.querySelector( ".closed-captions.is-visible" )?.innerText; updateCaptionsFunction(text); }; callback([1], null); let captionsObserver = new MutationObserver(callback); captionsObserver.observe(container, { attributes: true, childList: true, subtree: true, }); } } } this.PictureInPictureVideoWrapper = PictureInPictureVideoWrapper; PK !<Η&video-wrappers/hbomax.js/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; class PictureInPictureVideoWrapper { setVolume(video, volume) { video.volume = volume; } isMuted(video) { return video.volume === 0; } setMuted(video, shouldMute) { if (shouldMute) { this.setVolume(video, 0); } else { this.setVolume(video, 1); } } setCaptionContainerObserver(video, updateCaptionsFunction) { let container = document.querySelector( '[data-testid="CueBoxContainer"]' ).parentElement; if (container) { updateCaptionsFunction(""); const callback = function (mutationsList, observer) { let text = container.querySelector( '[data-testid="CueBoxContainer"]' )?.innerText; updateCaptionsFunction(text); }; callback([1], null); let captionsObserver = new MutationObserver(callback); captionsObserver.observe(container, { attributes: false, childList: true, subtree: true, }); } } } this.PictureInPictureVideoWrapper = PictureInPictureVideoWrapper; PK ! x.textContent).join("\n") ); }; // immediately invoke the callback function to add subtitles to the PiP window callback([1], null); let captionsObserver = new MutationObserver(callback); captionsObserver.observe(container, { attributes: false, childList: true, subtree: true, }); } } } this.PictureInPictureVideoWrapper = PictureInPictureVideoWrapper; PK ! div"); if (this.isMuted(video) !== shouldMute) { muteButton.click(); } } setCurrentTime(video, position) { this.player.currentTime = position; } setCaptionContainerObserver(video, updateCaptionsFunction) { let container = document.querySelector(".ClosedCaption"); if (container) { updateCaptionsFunction(""); const callback = function (mutationsList, observer) { // This will get the subtitles for both live and regular playback videos // and combine them to display. liveVideoText should be an empty string // when the video is regular playback and vice versa. If both // liveVideoText and regularVideoText are non empty strings, which // doesn't seem to be the case, they will both show. let liveVideoText = Array.from( container.querySelectorAll( "#inband-closed-caption > div > div > div" ), x => x.textContent.trim() ) .filter(String) .join("\n"); let regularVideoText = container.querySelector(".CaptionBox").innerText; updateCaptionsFunction(liveVideoText + regularVideoText); }; // immediately invoke the callback function to add subtitles to the PiP window callback([1], null); let captionsObserver = new MutationObserver(callback); captionsObserver.observe(container, { attributes: false, childList: true, subtree: true, }); } } getDuration(video) { return this.player.duration; } } this.PictureInPictureVideoWrapper = PictureInPictureVideoWrapper; PK !<+ Uvideo-wrappers/mock-wrapper.js/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; class PictureInPictureVideoWrapper { play(video) { let playPauseButton = document.querySelector("#player .play-pause-button"); playPauseButton.click(); } pause(video) { let invalidSelector = "#player .pause-button"; let playPauseButton = document.querySelector(invalidSelector); playPauseButton.click(); } setMuted(video, shouldMute) { let muteButton = document.querySelector("#player .mute-button"); if (video.muted !== shouldMute && muteButton) { muteButton.click(); } else { video.muted = shouldMute; } } shouldHideToggle() { let video = document.getElementById("mock-video-controls"); return !!video.classList.contains("mock-preview-video"); } } this.PictureInPictureVideoWrapper = PictureInPictureVideoWrapper; PK !<#>A A video-wrappers/netflix.js/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; class PictureInPictureVideoWrapper { constructor() { let netflixPlayerAPI = window.wrappedJSObject.netflix.appContext.state.playerApp.getAPI() .videoPlayer; let sessionId = null; for (let id of netflixPlayerAPI.getAllPlayerSessionIds()) { if (id.startsWith("watch-")) { sessionId = id; break; } } this.player = netflixPlayerAPI.getVideoPlayerBySessionId(sessionId); } /** * The Netflix player returns the current time in milliseconds so we convert * to seconds before returning. * @param {HTMLVideoElement} video The original video element * @returns {Number} The current time in seconds */ getCurrentTime(video) { return this.player.getCurrentTime() / 1000; } /** * The Netflix player returns the duration in milliseconds so we convert to * seconds before returning. * @param {HTMLVideoElement} video The original video element * @returns {Number} The duration in seconds */ getDuration(video) { return this.player.getDuration() / 1000; } play() { this.player.play(); } pause() { this.player.pause(); } setCaptionContainerObserver(video, updateCaptionsFunction) { let container = document.querySelector(".watch-video"); if (container) { updateCaptionsFunction(""); const callback = function (mutationsList, observer) { let text = container.querySelector(".player-timedtext").innerText; updateCaptionsFunction(text); }; // immediately invoke the callback function to add subtitles to the PiP window callback([1], null); let captionsObserver = new MutationObserver(callback); captionsObserver.observe(container, { attributes: false, childList: true, subtree: true, }); } } /** * Set the current time of the video in milliseconds. * @param {HTMLVideoElement} video The original video element * @param {Number} position The new time in seconds */ setCurrentTime(video, position) { this.player.seek(position * 1000); } } this.PictureInPictureVideoWrapper = PictureInPictureVideoWrapper; PK !< WZIIvideo-wrappers/nytimes.js/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; class PictureInPictureVideoWrapper { setCaptionContainerObserver(video, updateCaptionsFunction) { let container = document.querySelector(".react-vhs-player"); if (container) { updateCaptionsFunction(""); const callback = function (mutationsList, observer) { let text = container.querySelector(".cueWrap-2P4Ue4VQ")?.innerText; if (!text) { updateCaptionsFunction(""); return; } updateCaptionsFunction(text); }; // immediately invoke the callback function to add subtitles to the PiP window callback([1], null); let captionsObserver = new MutationObserver(callback); captionsObserver.observe(container, { attributes: false, childList: true, subtree: true, }); } } } this.PictureInPictureVideoWrapper = PictureInPictureVideoWrapper; PK !<9"s|video-wrappers/piped.js/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; class PictureInPictureVideoWrapper { setCaptionContainerObserver(video, updateCaptionsFunction) { let container = document.querySelector(".player-container"); if (container) { updateCaptionsFunction(""); const callback = function (mutationsList, observer) { let textNodeList = container .querySelector(".shaka-text-wrapper") ?.querySelectorAll('span[style="background-color: black;"]'); if (!textNodeList) { updateCaptionsFunction(""); return; } updateCaptionsFunction( Array.from(textNodeList, x => x.textContent).join("\n") ); }; // immediately invoke the callback function to add subtitles to the PiP window callback([1], null); let captionsObserver = new MutationObserver(callback); captionsObserver.observe(container, { attributes: false, childList: true, subtree: true, }); } } } this.PictureInPictureVideoWrapper = PictureInPictureVideoWrapper; PK ! { video.play(); }); } /** * Seeking large amounts of time can cause the video readyState to * HAVE_METADATA (1) and it will throw an error when trying to play the video. * To combat this, after seeking we check if the readyState changed and if so, * we will play to video to "load" the video at the new time and then play or * pause the video depending on if the video was playing before we seeked. * @param {HTMLVideoElement} video * The original video element * @param {Number} position * The new time to set the video to * @param {Boolean} wasPlaying * True if the video was playing before seeking else false */ setCurrentTime(video, position, wasPlaying) { if (wasPlaying === undefined) { this.wasPlaying = !video.paused; } video.currentTime = position; if (video.readyState < video.HAVE_CURRENT_DATA) { video .play() .then(() => { if (!wasPlaying) { video.pause(); } }) .catch(() => { if (wasPlaying) { this.play(video); } }); } } setCaptionContainerObserver(video, updateCaptionsFunction) { let container = document?.querySelector("#dv-web-player"); if (container) { updateCaptionsFunction(""); const callback = function (mutationsList, observer) { // eslint-disable-next-line no-unused-vars for (const mutation of mutationsList) { let text; // windows, mac if (container?.querySelector(".atvwebplayersdk-player-container")) { text = container ?.querySelector(".f35bt6a") ?.querySelector(".atvwebplayersdk-captions-text")?.innerText; } else { // linux text = container ?.querySelector(".persistentPanel") ?.querySelector("span")?.innerText; } if (!text) { updateCaptionsFunction(""); return; } updateCaptionsFunction(text); } }; // immediately invoke the callback function to add subtitles to the PiP window callback([1], null); let captionsObserver = new MutationObserver(callback); captionsObserver.observe(container, { attributes: true, childList: true, subtree: true, }); } } shouldHideToggle(video) { return !!video.classList.contains("tst-video-overlay-player-html5"); } } this.PictureInPictureVideoWrapper = PictureInPictureVideoWrapper; PK ! button" ); if (video.muted !== shouldMute && muteButton) { muteButton.click(); } } } this.PictureInPictureVideoWrapper = PictureInPictureVideoWrapper; PK !<*5video-wrappers/sonyliv.js/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; class PictureInPictureVideoWrapper { setCaptionContainerObserver(video, updateCaptionsFunction) { let container = document.querySelector(".player-ui-main-wrapper"); if (container) { updateCaptionsFunction(""); const callback = function (mutationsList, observer) { let text = container.querySelector( `.text-track-wrapper:not([style*="display: none"])` )?.innerText; if (!text) { updateCaptionsFunction(""); return; } updateCaptionsFunction(text); }; // immediately invoke the callback function to add subtitles to the PiP window callback([1], null); let captionsObserver = new MutationObserver(callback); captionsObserver.observe(container, { attributes: false, childList: true, subtree: true, }); } } } this.PictureInPictureVideoWrapper = PictureInPictureVideoWrapper; PK !<..video-wrappers/tubi.js/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; class PictureInPictureVideoWrapper { setCaptionContainerObserver(video, updateCaptionsFunction) { let container = document.querySelector(`[data-id="hls"]`); if (container) { updateCaptionsFunction(""); const callback = function (mutationsList, observer) { let text = container?.querySelector( `[data-id="captionsComponent"]:not([style="display: none;"])` )?.innerText; updateCaptionsFunction(text); }; // immediately invoke the callback function to add subtitles to the PiP window callback([1], null); let captionsObserver = new MutationObserver(callback); captionsObserver.observe(container, { attributes: true, childList: true, subtree: true, }); } } } this.PictureInPictureVideoWrapper = PictureInPictureVideoWrapper; PK !<"Ǭ//video-wrappers/tubilive.js/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; class PictureInPictureVideoWrapper { setCaptionContainerObserver(video, updateCaptionsFunction) { let container = video.parentElement; if (container) { updateCaptionsFunction(""); const callback = function (mutationsList, observer) { let text = container.querySelector(`.tubi-text-track-container`)?.innerText || container.querySelector(`.subtitleWindow`)?.innerText; updateCaptionsFunction(text); }; // immediately invoke the callback function to add subtitles to the PiP window callback([1], null); let captionsObserver = new MutationObserver(callback); captionsObserver.observe(container, { attributes: true, childList: true, subtree: true, }); } } } this.PictureInPictureVideoWrapper = PictureInPictureVideoWrapper; PK !<_"video-wrappers/twitch.js/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; class PictureInPictureVideoWrapper { isLive(video) { return !document.querySelector(".seekbar-bar"); } getDuration(video) { if (this.isLive(video)) { return Infinity; } return video.duration; } } this.PictureInPictureVideoWrapper = PictureInPictureVideoWrapper; PK !<הvideo-wrappers/udemy.js/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; class PictureInPictureVideoWrapper { setCaptionContainerObserver(video, updateCaptionsFunction) { let container = document.querySelector( ".video-player--video-player--1sfof" ); if (container) { updateCaptionsFunction(""); const callback = function (mutationsList, observer) { let text = container.querySelector( ".captions-display--captions-container--1-aQJ" )?.innerText; if (!text) { updateCaptionsFunction(""); return; } updateCaptionsFunction(text); }; // immediately invoke the callback function to add subtitles to the PiP window callback([1], null); let captionsObserver = new MutationObserver(callback); captionsObserver.observe(container, { attributes: true, childList: true, subtree: true, }); } } } this.PictureInPictureVideoWrapper = PictureInPictureVideoWrapper; PK ! x.textContent).join("\n") ); } }; // immediately invoke the callback function to add subtitles to the PiP window callback([1], null); let captionsObserver = new MutationObserver(callback); captionsObserver.observe(container, { attributes: false, childList: true, subtree: true, }); } } shouldHideToggle(video) { return !!video.closest(".ytd-video-preview"); } } this.PictureInPictureVideoWrapper = PictureInPictureVideoWrapper; PK!!W