Remove experimental media overlay

This commit is contained in:
hensm
2022-04-15 07:50:57 +01:00
parent 25549ac1df
commit 59dc806d31
17 changed files with 0 additions and 725 deletions

View File

@@ -74,8 +74,6 @@ const buildOpts = {
`${srcPath}/background/background.ts`,
// Media sender
`${srcPath}/senders/media/index.ts`,
`${srcPath}/senders/media/overlay/overlayContent.ts`,
`${srcPath}/senders/media/overlay/overlayContentLoader.ts`,
// Mirroring sender
`${srcPath}/senders/mirroring.ts`,
// Cast

View File

@@ -92,18 +92,6 @@
}
}
, "mediaOverlayTitle": {
"message": "Wiedergabe auf $receiverName$"
, "description": "Main title for overlay displayed on media elements whilst casting."
, "placeholders": {
"receiverName": {
"content": "$1"
, "example": "Living Room TV"
}
}
}
, "optionsBridgeLoading": {
"message": "Lade Bridge-Informationen..."
, "description": "Loading placeholder text for bridge section on options page."
@@ -226,18 +214,6 @@
"message": "Streamen von Medien aktivieren"
, "description": "Media casting enabled checkbox label."
}
, "optionsMediaOverlayEnabled": {
"message": "Aktiviere Medien-Overlay"
, "description": "Media element overlay checkbox label."
}
, "optionsMediaOverlayEnabledTemp": {
"message": "Aktiviere Medien-Overlay (experimentell)"
, "description": "Experimental-labelled version of above."
}
, "optionsMediaOverlayEnabledDescription": {
"message": "Overlay auf Medien, das, wenn verbunden, Informationen über die aktuelle Sitzung anzeigt."
, "description": "Media element overlay option description."
}
, "optionsMediaSyncElement": {
"message": "Empfängerstatus mit Media-Element synchronisieren"
, "description": "Media casting sync checkbox label."

View File

@@ -97,18 +97,6 @@
}
, "mediaOverlayTitle": {
"message": "Playing on $receiverName$"
, "description": "Main title for overlay displayed on media elements whilst casting."
, "placeholders": {
"receiverName": {
"content": "$1"
, "example": "Living Room TV"
}
}
}
, "optionsBridgeLoading": {
"message": "Loading bridge info..."
, "description": "Loading placeholder text for bridge section on options page."
@@ -231,18 +219,6 @@
"message": "Enable media casting"
, "description": "Media casting enabled checkbox label."
}
, "optionsMediaOverlayEnabled": {
"message": "Enable media element overlay"
, "description": "Media element overlay checkbox label."
}
, "optionsMediaOverlayEnabledTemp": {
"message": "Enable media element overlay (experimental)"
, "description": "Experimental-labelled version of above."
}
, "optionsMediaOverlayEnabledDescription": {
"message": "Overlay on media elements displaying information about the current session if connected."
, "description": "Media element overlay option description."
}
, "optionsMediaSyncElement": {
"message": "Sync receiver state with media element"
, "description": "Media casting sync checkbox label."

View File

@@ -97,18 +97,6 @@
}
, "mediaOverlayTitle": {
"message": "Reproduciendo en $receiverName$"
, "description": "Main title for overlay displayed on media elements whilst casting."
, "placeholders": {
"receiverName": {
"content": "$1"
, "example": "Living Room TV"
}
}
}
, "optionsBridgeLoading": {
"message": "Cargando información de la aplicación puente..."
, "description": "Loading placeholder text for bridge section on options page."
@@ -231,18 +219,6 @@
"message": "Activar transmisión de contenidos"
, "description": "Media casting enabled checkbox label."
}
, "optionsMediaOverlayEnabled": {
"message": "Activar sobreposición en el elemento de medios"
, "description": "Media element overlay checkbox label."
}
, "optionsMediaOverlayEnabledTemp": {
"message": "Activar sobreposición en el elemento de medios (experimental)"
, "description": "Experimental-labelled version of above."
}
, "optionsMediaOverlayEnabledDescription": {
"message": "Sobreposición en elementos de medios mostrando información acerca de la sesión actual si está conectado."
, "description": "Media element overlay option description."
}
, "optionsMediaSyncElement": {
"message": "Sincronizar estado del receptor con el contenido"
, "description": "Media casting sync checkbox label."

View File

@@ -85,16 +85,6 @@
}
}
},
"mediaOverlayTitle": {
"message": "Speelt af op $receiverName$",
"description": "Main title for overlay displayed on media elements whilst casting.",
"placeholders": {
"receiverName": {
"content": "$1",
"example": "Living Room TV"
}
}
},
"optionsBridgeLoading": {
"message": "Bezig met laden van bridge-informatie...",
"description": "Loading placeholder text for bridge section on options page."
@@ -209,18 +199,6 @@
"message": "Mediacasten ingeschakeld",
"description": "Media casting enabled checkbox label."
},
"optionsMediaOverlayEnabled": {
"message": "Inschakelen overlay media element",
"description": "Media element overlay checkbox label."
},
"optionsMediaOverlayEnabledTemp": {
"message": "Inschakelen overlay media element (Experimenteel)",
"description": "Experimental-labelled version of above."
},
"optionsMediaOverlayEnabledDescription": {
"message": "Een overlay over media elementen die informatie weergeeft over de huidige sessie indien verbonden.",
"description": "Media element overlay option description."
},
"optionsMediaSyncElement": {
"message": "Ontvangerstatus synchroniseren met media-element",
"description": "Media casting sync checkbox label."

View File

@@ -97,17 +97,6 @@
}
, "mediaOverlayTitle": {
"message": "Spiller på $receiverName$"
, "description": "Main title for overlay displayed on media elements whilst casting."
, "placeholders": {
"receiverName": {
"content": "$1"
, "example": "Living Room TV"
}
}
}
, "optionsBridgeLoading": {
"message": "Laster bro-info"
, "description": "Loading placeholder text for bridge section on options page."
@@ -230,18 +219,6 @@
"message": "Skru på media-casting"
, "description": "Media casting enabled checkbox label."
}
, "optionsMediaOverlayEnabled": {
"message": "Skru på element-overlegg (eksperimentell)"
, "description": "Media element overlay checkbox label."
}
, "optionsMediaOverlayEnabledTemp": {
"message": "Skru på element-overlegg (eksperimentell)"
, "description": "Experimental-labelled version of above."
}
, "optionsMediaOverlayEnabledDescription": {
"message": "mediaelementer "
, "description": "Media element overlay option description."
}
, "optionsMediaSyncElement": {
"message": "Synkroniser mottager med mediaelement"
, "description": "Media casting sync checkbox label."

View File

@@ -41,48 +41,6 @@ browser.runtime.onInstalled.addListener(async details => {
}
});
/**
* Sets up media overlay content script and handles toggling
* on options change.
*/
async function initMediaOverlay() {
logger.info("init (media overlay)");
let contentScript: browser.contentScripts.RegisteredContentScript;
async function registerMediaOverlayContentScript() {
if (!(await options.get("mediaOverlayEnabled"))) {
return;
}
try {
contentScript = await browser.contentScripts.register({
allFrames: true,
js: [{ file: "senders/media/overlay/overlayContentLoader.js" }],
matches: ["<all_urls>"],
runAt: "document_start"
});
} catch (err) {
logger.error("Failed to register media overlay");
}
}
async function unregisterMediaOverlayContentScript() {
await contentScript?.unregister();
}
registerMediaOverlayContentScript();
// Update if toggled
options.addEventListener("changed", async ev => {
const alteredOpts = ev.detail;
if (alteredOpts.includes("mediaOverlayEnabled")) {
await unregisterMediaOverlayContentScript();
await registerMediaOverlayContentScript();
}
});
}
/**
* Checks whether the bridge can be reached and is compatible
@@ -153,7 +111,6 @@ async function init() {
await initMenus();
await initWhitelist();
await initMediaOverlay();
/**
* When the browser action is clicked, open a receiver

View File

@@ -8,7 +8,6 @@ export default {
bridgeBackupHost: "localhost",
bridgeBackupPort: 9556,
mediaEnabled: true,
mediaOverlayEnabled: false,
mediaSyncElement: false,
mediaStopOnUnload: false,
localMediaEnabled: true,

View File

@@ -17,7 +17,6 @@ export interface Options {
bridgeBackupHost: string;
bridgeBackupPort: number;
mediaEnabled: boolean;
mediaOverlayEnabled: boolean;
mediaSyncElement: boolean;
mediaStopOnUnload: boolean;
localMediaEnabled: boolean;

View File

@@ -66,8 +66,5 @@
]
, "web_accessible_resources": [
"cast/index.js"
, "senders/media/overlay/overlayContent.js"
, "senders/media/overlay/AirPlay_Audio.svg"
, "senders/media/overlay/AirPlay_Video.svg"
]
}

View File

@@ -362,10 +362,6 @@ export async function init(opts: InitOptions) {
if (targetElement instanceof HTMLMediaElement) {
registerMediaElementListeners(targetElement);
if (await options.get("mediaOverlayEnabled")) {
// TODO: Un-hide overlay here
}
}
window.addEventListener("beforeunload", async () => {

View File

@@ -1,6 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 125 125" fill="white">
<path d="M43.5 84.1l1.3-1.5c.3-.3.3-.8 0-1.1-10.5-9.7-11.2-26.2-1.4-36.7s26.2-11.2 36.7-1.4 11.2 26.2 1.4 36.7c-.5.5-.9 1-1.4 1.4-.3.3-.3.8 0 1.1l1.3 1.5c.3.3.8.3 1.1.1 12-11.1 12.7-29.7 1.7-41.7-11.1-12-29.7-12.7-41.7-1.7s-12.7 29.7-1.7 41.7c.5.6 1.1 1.1 1.7 1.7.3.2.7.2 1-.1z"/>
<path d="M44.8 62.5c0-9.7 7.9-17.6 17.6-17.6S80 52.9 80 62.6c0 4.8-2 9.5-5.5 12.8-.3.3-.3.8 0 1.1l1.3 1.5c.3.3.8.4 1.1.1 8.5-8 8.9-21.3 1-29.8s-21.3-8.9-29.8-1-9 21.2-1.1 29.7c.3.3.6.7 1 1 .3.3.8.3 1.1 0l1.3-1.5c.3-.3.3-.8 0-1.1-3.5-3.3-5.6-8-5.6-12.9z"/>
<path d="M53.2 62.5c0-5.1 4.1-9.2 9.2-9.2s9.2 4.1 9.2 9.2c0 2.5-1 4.8-2.8 6.6-.3.3-.3.8 0 1.1l1.3 1.5c.3.3.8.3 1.1 0 5-4.9 5.2-12.9.3-18s-12.9-5.2-18-.3-5.2 12.9-.3 18l.3.3c.3.3.8.3 1.1 0l1.3-1.5c.3-.3.3-.8 0-1.1-1.7-1.7-2.7-4.1-2.7-6.6z"/>
<path d="M80.9 89.1L63.5 69.3c-.5-.6-1.3-.6-1.9-.1l-.1.1-17.6 19.8c-.4.5-.4 1.2.1 1.7.2.2.5.3.7.3H80c.6 0 1.2-.5 1.2-1.2 0-.3-.1-.6-.3-.8z"/>
</svg>

Before

Width:  |  Height:  |  Size: 1016 B

View File

@@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 125 125" fill="white">
<path d="M81 88.8c.4.5.4 1.3-.1 1.7-.2.2-.5.3-.8.3H44.9c-.7 0-1.2-.5-1.2-1.2 0-.3.1-.6.3-.8l17.5-20.1c.5-.6 1.3-.6 1.9-.1l.1.1L81 88.8zm-4.1-11.1l-2.8-3.3h10.5c.9.1 1.7-.1 2.5-.4.5-.3 1-.7 1.2-1.2.4-.8.5-1.7.4-2.5V45.8c.1-.9-.1-1.7-.4-2.5-.3-.5-.7-1-1.2-1.2-.8-.4-1.7-.5-2.5-.4h-44c-.9-.1-1.7.1-2.5.4-.5.3-1 .7-1.2 1.2-.4.8-.5 1.7-.4 2.5v24.4c-.1.9.1 1.7.4 2.5.3.5.7 1 1.2 1.2.8.4 1.7.5 2.5.4h10.5l-2.8 3.3h-6.7c-3 0-4-.3-5-.9-1.1-.6-1.9-1.4-2.5-2.5-.6-1.1-.9-2.1-.9-5V46.7c0-3 .3-4 .9-5.1.6-1.1 1.4-1.9 2.5-2.5 1.1-.6 2.1-.9 5-.9h42.2c3 0 4 .3 5.1.9 1.1.6 1.9 1.4 2.5 2.5.6 1.1.9 2.1.9 5.1v22.5c0 3-.3 4-.9 5-.6 1.1-1.4 1.9-2.5 2.5-1.1.6-2.1.9-5.1.9l-6.9.1z"/>
</svg>

Before

Width:  |  Height:  |  Size: 749 B

View File

@@ -1,75 +0,0 @@
"use strict";
/**
* Walk up the prototype chain until the specified property
* descriptor is found, otherwise return undefined.
*/
export function getPropertyDescriptor(
target: any,
prop: string | number | symbol
): PropertyDescriptor | undefined {
let desc: PropertyDescriptor | undefined;
while (!desc && target !== null) {
desc = Object.getOwnPropertyDescriptor(target, prop);
if (!desc) target = Object.getPrototypeOf(target);
}
return desc;
}
/**
* Bind either the getter/setter functions or the value function
* to a target object.
*/
export function bindPropertyDescriptor(
desc: PropertyDescriptor,
target: any
): PropertyDescriptor {
if (typeof desc.value === "function") {
desc.value = desc.value.bind(target);
} else {
if (desc.get) desc.get = desc.get.bind(target);
if (desc.set) desc.set = desc.set.bind(target);
}
return desc;
}
/**
* For each attribute handler, fetch the property descriptor (which may
* be further up in the prototype chain), re-bind it to the target
* element and collect them into a property descriptor map.
*/
export function clonePropsDescriptor<T>(
target: T,
props: any[]
): PropertyDescriptorMap {
return props.reduce<PropertyDescriptorMap>((descriptorMap, prop) => {
const desc = getPropertyDescriptor(target, prop);
if (desc) {
bindPropertyDescriptor(desc, target);
descriptorMap[prop as any] = desc;
}
return descriptorMap;
}, {});
}
export function makeGetterDescriptor(val: any): PropertyDescriptor {
return {
enumerable: true,
configurable: true,
get() {
return val;
}
};
}
export function makeValueDescriptor(val: any): PropertyDescriptor {
return {
enumerable: true,
configurable: true,
writable: true,
value: val
};
}

View File

@@ -1,418 +0,0 @@
"use strict";
import logger from "../../../lib/logger";
import {
bindPropertyDescriptor,
clonePropsDescriptor,
getPropertyDescriptor,
makeGetterDescriptor,
makeValueDescriptor
} from "./descriptorUtils";
// Injected by content loader
declare const iconAirPlayAudio: string;
declare const iconAirPlayVideo: string;
declare const mediaOverlayTitle: string;
/**
* Intercept and store references to shadow root nodes created by
* calls to `attachShadow`. Used to reference shadow roots, even when
* created in closed mode without exposing them to other page scripts.
*/
const internalShadowRoots = new WeakMap<Element, ShadowRoot>();
const _attachShadow = Element.prototype.attachShadow;
Element.prototype.attachShadow = function (init) {
const shadowRoot = _attachShadow.call(this, init);
internalShadowRoots.set(this, shadowRoot);
return shadowRoot;
};
function getShadowRootFromNode(node: Node): ShadowRoot | undefined {
// Don't touch our custom element
if (node instanceof PlayerElement) {
return;
}
return internalShadowRoots.get(node as Element);
}
const DQS_XPATH_EXPRESSION = `//*[contains(name(), "-")]`;
/**
* Return the first matching querySelector result on any ShadowRoot
* nodes present in the document.
*/
function deepQuerySelector(selector: string): Element | null {
const result = document.evaluate(
DQS_XPATH_EXPRESSION,
document,
null,
XPathResult.ORDERED_NODE_ITERATOR_TYPE
);
let node: Node | null;
// eslint-disable-next-line no-cond-assign
while ((node = result.iterateNext())) {
const shadowRoot = getShadowRootFromNode(node);
if (!shadowRoot) {
continue;
}
const queryResult = shadowRoot.querySelector(selector);
if (queryResult) {
return queryResult;
}
}
return null;
}
/**
* Collect and return the results of querySelectorAll on any
* ShadowRoot nodes present in the document.
*/
function deepQuerySelectorAll(selector: string): Node[] {
const result = document.evaluate(
DQS_XPATH_EXPRESSION,
document,
null,
XPathResult.ORDERED_NODE_ITERATOR_TYPE
);
const nodes: Node[] = [];
let node: Node | null;
// eslint-disable-next-line no-cond-assign
while ((node = result.iterateNext())) {
const shadowRoot = getShadowRootFromNode(node);
if (shadowRoot) {
nodes.push(...shadowRoot.querySelectorAll(selector));
}
}
return nodes;
}
const mediaElementTypes = [
HTMLMediaElement,
HTMLVideoElement,
HTMLAudioElement
];
const mediaElementEvents = [
"abort",
"canplay",
"canplaythrough",
"durationchange",
"emptied",
"encrypted",
"ended",
"error",
"interruptbegin",
"interruptend",
"loadeddata",
"loadedmetadata",
"loadstart",
"mozaudioavailable",
"pause",
"play",
"playing",
"progress",
"ratechange",
"seeked",
"seeking",
"stalled",
"suspend",
"timeupdate",
"volumechange",
"waiting"
];
const mediaElementAttributes = mediaElementTypes
.flatMap(type => Object.getOwnPropertyNames(type.prototype))
.concat(mediaElementEvents.map(ev => `on${ev}`));
/**
* Opaque wrapper around the media element to provide an overlay without
* author interference. Relevant properties, attributes, events and
* functions are proxied to the internal media element.
*/
class PlayerElement extends HTMLElement {
constructor() {
super();
const shadowRoot = this.attachShadow({ mode: "closed" });
const { host } = shadowRoot;
let iconUrl;
switch (this.constructor) {
// URL variables injected ahead of current script
case AudioPlayerElement: {
iconUrl = iconAirPlayAudio;
break;
}
case VideoPlayerElement: {
iconUrl = iconAirPlayVideo;
break;
}
}
shadowRoot.innerHTML = `
<style>
:host {
display: inline-flex;
font: menu;
position: relative;
}
:host[hidden],
.overlay[hidden] {
display: none;
}
video {
width: 100%;
}
.overlay {
align-items: center;
background-color: rgba(0, 0, 0, 0.85);
color: white;
display: flex;
flex-direction: column;
height: 100%;
justify-content: center;
left: 0;
pointer-events: none;
position: absolute;
top: 0;
width: 100%;
}
.overlay__icon {
background-image: url("${iconUrl}");
height: 125px;
width: 125px;
}
.overlay__text {
font-size: 18px;
}
</style>
<div class="overlay" hidden>
<div class="overlay__icon"></div>
<div class="overlay__text">
${mediaOverlayTitle}
</div>
</div>
`;
const videoElement = _createElement.call(document, "video");
for (const attr of mediaElementAttributes) {
if (host.hasOwnProperty(attr)) {
// @ts-ignore
videoElement[attr] = host[attr];
}
}
/**
* Page scripts need to be able to read/write attributes, event
* listeners, etc... on the media element, but since it's hidden
* within the shadow DOM, these properties must be proxied.
*/
Object.defineProperties(
host,
clonePropsDescriptor(videoElement, [
"attributes",
"setAttribute",
"removeAttribute",
"setAttribute",
"addEventListener",
"removeEventListener",
"hasEventListener",
...(mediaElementAttributes as any)
])
);
shadowRoot.prepend(videoElement);
}
}
class AudioPlayerElement extends PlayerElement {}
class VideoPlayerElement extends PlayerElement {
set overlayHidden(val: boolean) {
const shadowRoot = internalShadowRoots.get(this);
(shadowRoot?.querySelector(".overlay") as HTMLDivElement).hidden = val;
}
get overlayHidden() {
const shadowRoot = internalShadowRoots.get(this);
return (shadowRoot?.querySelector(".overlay") as HTMLDivElement).hidden;
}
}
try {
customElements.define("audio-player-element", AudioPlayerElement);
customElements.define("video-player-element", VideoPlayerElement);
} catch (err) {
if (
err instanceof DOMException &&
err.code === DOMException.NOT_SUPPORTED_ERR
) {
// Script already injected
}
}
// Original functions
const _createElement = document.createElement;
const _createElementNS = document.createElementNS;
/**
* Intercepts `<audio>`/`<video>` element creation and returns a wrapped
* custom element version that imitates the original. Otherwise, returns
* the result of the original.
*/
function createElement(tagName: string, options?: ElementCreationOptions) {
// Normalize formatting
const lowerTagName = tagName.toLowerCase();
const upperTagName = tagName.toUpperCase();
if (lowerTagName === "audio" || lowerTagName === "video") {
const fakeElement = _createElement.call(
document,
`${lowerTagName}-player-element`
) as HTMLMediaElement;
// Ensure all references to the element name match tagName
Object.defineProperties(fakeElement, {
tagName: makeGetterDescriptor(upperTagName),
nodeName: makeGetterDescriptor(upperTagName),
localName: makeGetterDescriptor(lowerTagName)
});
return fakeElement;
}
return _createElement.call(document, tagName, options);
}
/**
* If the namespace matches the current document, redirect to the
* patched `createElement` function, otherwise return the result of the
* original.
*/
function createElementNS(
namespaceURI: string,
qualifiedName: string,
options?: ElementCreationOptions
) {
if (namespaceURI === document.namespaceURI) {
return createElement(qualifiedName, options);
}
return _createElementNS.call(
document,
namespaceURI,
qualifiedName,
options
);
}
/**
* Attempt to hide function source from page scripts by returning the
* toString/toSource values of the native function.
*/
Object.defineProperties(
createElement,
clonePropsDescriptor(_createElement, ["toString", "toSource"])
);
Object.defineProperties(
createElementNS,
clonePropsDescriptor(_createElementNS, ["toString", "toSource"])
);
// Re-define element creation functions
Object.defineProperties(document, {
createElement: makeValueDescriptor(createElement),
createElementNS: makeValueDescriptor(createElementNS)
});
/**
* Takes a media element, creates a `PlayerElement` via the patched
* `createElement` function, fetches the shadow root and copies any
* attributes before swapping with the original element in-place.
*/
function wrapMediaElement(mediaElement: HTMLMediaElement) {
const wrappedMedia = document.createElement(mediaElement.tagName);
const shadowRoot = internalShadowRoots.get(wrappedMedia);
if (!shadowRoot) {
logger.error("Failed to fetch shadow root!");
return;
}
/**
* Copy attributes, any non-media specific attributes are set to the
* wrapper element for identification (id, class, etc...) or styling.
*/
for (const attr of mediaElement.attributes) {
if (mediaElementAttributes.includes(attr.name)) {
wrappedMedia.setAttribute(attr.name, attr.value);
} else {
/**
* Since the wrapped element has a patched `setAttribute`
* method, need to call the original from the `HTMLElement`
* prototype, otherwise attributes will be set on the
* internal media element instead.
*/
HTMLElement.prototype.setAttribute.call(
wrappedMedia,
attr.name,
attr.value
);
}
}
/**
* Clone and append any HTMLSourceElement children to the
* internal media element within the wrapped media shadow root.
*/
for (const source of mediaElement.getElementsByTagName("source")) {
const internalMedia = shadowRoot.querySelector("audio,video");
if (!internalMedia) {
logger.error("Failed to fetch internal video element!");
return;
}
internalMedia.appendChild(source.cloneNode());
}
// Replace media element on page with wrapped media
mediaElement.replaceWith(wrappedMedia);
}
/*function* joinIterables (...iterables: Array<Iterable<any>>) {
for (const iterable of iterables) {
for (const item of iterable) {
yield item;
}
}
}*/
/**
* Find all media elements (both in the main DOM and any shadow DOMs)
* and wrap them.
*/
document.addEventListener("DOMContentLoaded", () => {
const mediaSelector = "audio,video";
setTimeout(() => {
const mediaElements = document.querySelectorAll(mediaSelector);
const deepMediaElements = deepQuerySelectorAll(mediaSelector);
for (const mediaElement of [...mediaElements, ...deepMediaElements]) {
wrapMediaElement(mediaElement as HTMLMediaElement);
}
});
});

View File

@@ -1,37 +0,0 @@
"use strict";
const _ = browser.i18n.getMessage;
/**
* Make synchronous request for another script to keep other page
* scripts from loading before its execution.
*/
const req = new XMLHttpRequest();
req.open(
"GET",
browser.runtime.getURL("senders/media/overlay/overlayContent.js"),
false
);
req.send();
if (req.status === 200) {
// TODO: Replace with cast icons until AirPlay support is ready
const iconAirPlayAudio = browser.runtime.getURL(
"senders/media/overlay/AirPlay_Audio.svg"
);
const iconAirPlayVideo = browser.runtime.getURL(
"senders/media/overlay/AirPlay_Audio.svg"
);
const scriptElement = document.createElement("script");
scriptElement.textContent = `(function(){
const iconAirPlayAudio = "${iconAirPlayAudio}";
const iconAirPlayVideo = "${iconAirPlayVideo}";
const mediaOverlayTitle = "${_("mediaOverlayTitle", "X")}";
${req.responseText}
})();`;
// <head> probably doesn't exist yet
(document.head || document.documentElement).append(scriptElement);
}

View File

@@ -189,21 +189,6 @@ class OptionsApp extends Component<
</div>
</label>
<label className="option option--inline">
<div className="option__control">
<input name="mediaOverlayEnabled"
type="checkbox"
checked={ this.state.options?.mediaOverlayEnabled }
onChange={ this.handleInputChange } />
</div>
<div className="option__label">
{ _("optionsMediaOverlayEnabledTemp") }
</div>
<div className="option__description">
{ _("optionsMediaOverlayEnabledDescription") }
</div>
</label>
<hr />
<label className="option option--inline">