mirror of
https://github.com/hensm/fx_cast.git
synced 2026-06-12 18:39:58 +00:00
Implement additional options
This commit is contained in:
@@ -17,6 +17,17 @@
|
|||||||
"message": "Cast..."
|
"message": "Cast..."
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
, "options_category_media": {
|
||||||
|
"message": "Media casting"
|
||||||
|
}
|
||||||
|
, "options_category_media_description": {
|
||||||
|
"message": "HTML5 video/audio media casting."
|
||||||
|
}
|
||||||
|
, "options_option_mediaEnabled": {
|
||||||
|
"message": "Enabled"
|
||||||
|
}
|
||||||
|
|
||||||
, "options_category_localMedia": {
|
, "options_category_localMedia": {
|
||||||
"message": "Local media casting"
|
"message": "Local media casting"
|
||||||
}
|
}
|
||||||
@@ -40,7 +51,7 @@
|
|||||||
"message": "Enabled"
|
"message": "Enabled"
|
||||||
}
|
}
|
||||||
, "options_option_uaWhitelist": {
|
, "options_option_uaWhitelist": {
|
||||||
"message": "Match matterns (newline-separated)"
|
"message": "Match patterns (newline-separated)"
|
||||||
}
|
}
|
||||||
, "options_option_uaWhitelistBasicView": {
|
, "options_option_uaWhitelistBasicView": {
|
||||||
"message": "Basic View"
|
"message": "Basic View"
|
||||||
@@ -64,6 +75,22 @@
|
|||||||
"message": "Invalid match pattern $1"
|
"message": "Invalid match pattern $1"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
, "options_category_mirroring": {
|
||||||
|
"message": "Screen mirroring"
|
||||||
|
}
|
||||||
|
, "options_category_mirroring_description": {
|
||||||
|
"message": "Screen/Tab mirroring to a Chromecast receiver app."
|
||||||
|
}
|
||||||
|
, "options_option_mirroringEnabled": {
|
||||||
|
"message": "Enabled"
|
||||||
|
}
|
||||||
|
, "options_option_mirroringAppId": {
|
||||||
|
"message": "Receiver app ID"
|
||||||
|
}
|
||||||
|
|
||||||
|
, "options_reset": {
|
||||||
|
"message": "Reset to defaults"
|
||||||
|
}
|
||||||
, "options_submit": {
|
, "options_submit": {
|
||||||
"message": "Submit"
|
"message": "Submit"
|
||||||
}
|
}
|
||||||
|
|||||||
129
ext/src/main.js
129
ext/src/main.js
@@ -1,26 +1,18 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
import defaultOptions from "./options/defaultOptions";
|
||||||
import messageRouter from "./messageRouter";
|
import messageRouter from "./messageRouter";
|
||||||
|
|
||||||
const _ = browser.i18n.getMessage;
|
const _ = browser.i18n.getMessage;
|
||||||
|
|
||||||
|
|
||||||
browser.runtime.onInstalled.addListener(async details => {
|
browser.runtime.onInstalled.addListener(async details => {
|
||||||
const initialOptions = {
|
|
||||||
option_localMediaEnabled: true
|
|
||||||
, option_localMediaServerPort: 9555
|
|
||||||
, option_uaWhitelistEnabled: true
|
|
||||||
, option_uaWhitelist: [
|
|
||||||
"https://www.netflix.com/*"
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
switch (details.reason) {
|
switch (details.reason) {
|
||||||
|
|
||||||
// Set initial options
|
// Set default options
|
||||||
case "install":
|
case "install":
|
||||||
browser.storage.sync.set({
|
await browser.storage.sync.set({
|
||||||
options: initialOptions
|
options: defaultOptions
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -30,14 +22,14 @@ browser.runtime.onInstalled.addListener(async details => {
|
|||||||
const newOptions = {};
|
const newOptions = {};
|
||||||
|
|
||||||
// Find options not already in storage
|
// Find options not already in storage
|
||||||
for (const [ key, val ] of Object.entries(initialOptions)) {
|
for (const [ key, val ] of Object.entries(defaultOptions)) {
|
||||||
if (!options.hasOwnProperty(key)) {
|
if (!options.hasOwnProperty(key)) {
|
||||||
newOptions[key] = val;
|
newOptions[key] = val;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update storage with default values of new options
|
// Update storage with default values of new options
|
||||||
browser.storage.sync.set({
|
await browser.storage.sync.set({
|
||||||
options: {
|
options: {
|
||||||
...options
|
...options
|
||||||
, ...newOptions
|
, ...newOptions
|
||||||
@@ -46,9 +38,55 @@ browser.runtime.onInstalled.addListener(async details => {
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Call after default options have been set
|
||||||
|
createMenus();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// Menu IDs
|
||||||
|
let mirrorCastMenuId;
|
||||||
|
let mediaCastMenuId;
|
||||||
|
|
||||||
|
const mediaCastTargetUrlPatterns = new Set([
|
||||||
|
"http://*/*"
|
||||||
|
, "https://*/*"
|
||||||
|
]);
|
||||||
|
|
||||||
|
const LOCAL_MEDIA_URL_PATTERN = "file://*/*";
|
||||||
|
|
||||||
|
async function createMenus () {
|
||||||
|
const { options } = await browser.storage.sync.get("options");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If options aren't set or menus have already been
|
||||||
|
* created, return.
|
||||||
|
*/
|
||||||
|
if (!options || mirrorCastMenuId || mediaCastMenuId) return;
|
||||||
|
|
||||||
|
if (options.option_localMediaEnabled) {
|
||||||
|
mediaCastTargetUrlPatterns.add(LOCAL_MEDIA_URL_PATTERN);
|
||||||
|
}
|
||||||
|
|
||||||
|
// <video>/<audio> "Cast..." context menu item
|
||||||
|
mediaCastMenuId = await browser.menus.create({
|
||||||
|
contexts: [ "audio", "video" ]
|
||||||
|
, id: "contextCastMedia"
|
||||||
|
, targetUrlPatterns: Array.from(mediaCastTargetUrlPatterns)
|
||||||
|
, title: _("context_media_cast")
|
||||||
|
, visible: options.option_mediaEnabled
|
||||||
|
});
|
||||||
|
|
||||||
|
// Screen/Tab mirroring "Cast..." context menu item
|
||||||
|
mirrorCastMenuId = await browser.menus.create({
|
||||||
|
contexts: [ "browser_action", "page" ]
|
||||||
|
, id: "contextCast"
|
||||||
|
, title: _("context_media_cast")
|
||||||
|
, visible: options.option_mirroringEnabled
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Google-hosted API loader script
|
// Google-hosted API loader script
|
||||||
const SENDER_SCRIPT_URL =
|
const SENDER_SCRIPT_URL =
|
||||||
"https://www.gstatic.com/cv/js/sender/v1/cast_sender.js";
|
"https://www.gstatic.com/cv/js/sender/v1/cast_sender.js";
|
||||||
@@ -142,7 +180,7 @@ async function onBeforeSendHeaders (details) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async function registerWebRequestListeners (alteredOptions) {
|
async function onOptionsUpdated (alteredOptions) {
|
||||||
const { options } = await browser.storage.sync.get("options");
|
const { options } = await browser.storage.sync.get("options");
|
||||||
|
|
||||||
// If options aren't set yet, return
|
// If options aren't set yet, return
|
||||||
@@ -159,34 +197,52 @@ async function registerWebRequestListeners (alteredOptions) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
if (!alteredOptions) {
|
if (!alteredOptions) {
|
||||||
// If no altered properties specified, register all listeners
|
// If no altered properties specified, register all listeners
|
||||||
for (const func of Object.values(registerFunctions)) {
|
for (const func of Object.values(registerFunctions)) {
|
||||||
func();
|
func();
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (alteredOptions.includes("option_uaWhitelist")
|
if (alteredOptions.includes("option_uaWhitelist")
|
||||||
|| alteredOptions.includes("option_uaWhitelistEnabled")) {
|
|| alteredOptions.includes("option_uaWhitelistEnabled")) {
|
||||||
browser.webRequest.onBeforeSendHeaders.removeListener(onBeforeSendHeaders);
|
browser.webRequest.onBeforeSendHeaders.removeListener(onBeforeSendHeaders);
|
||||||
registerFunctions.onBeforeSendHeaders();
|
registerFunctions.onBeforeSendHeaders();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
if (alteredOptions.includes("option_mirroringEnabled")) {
|
||||||
|
browser.menus.update(mirrorCastMenuId, {
|
||||||
|
visible: options.option_mirroringEnabled
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
registerWebRequestListeners();
|
if (alteredOptions.includes("option_mediaEnabled")) {
|
||||||
|
browser.menus.update(mediaCastMenuId, {
|
||||||
|
visible: options.option_mediaEnabled
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (alteredOptions.includes("option_localMediaEnabled")) {
|
||||||
|
if (options.option_localMediaEnabled) {
|
||||||
|
mediaCastTargetUrlPatterns.add(LOCAL_MEDIA_URL_PATTERN);
|
||||||
|
} else {
|
||||||
|
mediaCastTargetUrlPatterns.delete(LOCAL_MEDIA_URL_PATTERN);
|
||||||
|
}
|
||||||
|
|
||||||
|
browser.menus.update(mediaCastMenuId, {
|
||||||
|
targetUrlPatterns: Array.from(mediaCastTargetUrlPatterns)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
browser.runtime.onMessage.addListener(message => {
|
browser.runtime.onMessage.addListener(message => {
|
||||||
switch (message.subject) {
|
switch (message.subject) {
|
||||||
case "optionsUpdated":
|
case "optionsUpdated":
|
||||||
const { alteredOptions } = message.data;
|
onOptionsUpdated(message.data.alteredOptions);
|
||||||
registerWebRequestListeners(alteredOptions);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
// Defines window.chrome for site compatibility
|
// Defines window.chrome for site compatibility
|
||||||
browser.contentScripts.register({
|
browser.contentScripts.register({
|
||||||
allFrames: true
|
allFrames: true
|
||||||
@@ -196,26 +252,6 @@ browser.contentScripts.register({
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
// Screen/Tab mirroring "Cast..." context menu item
|
|
||||||
browser.menus.create({
|
|
||||||
contexts: [ "browser_action", "page" ]
|
|
||||||
, id: "contextCast"
|
|
||||||
, title: _("context_media_cast")
|
|
||||||
});
|
|
||||||
|
|
||||||
// <video>/<audio> "Cast..." context menu item
|
|
||||||
browser.menus.create({
|
|
||||||
contexts: [ "audio", "video" ]
|
|
||||||
, id: "contextCastMedia"
|
|
||||||
, targetUrlPatterns: [
|
|
||||||
"http://*/*"
|
|
||||||
, "https://*/*"
|
|
||||||
, "file://*/*"
|
|
||||||
]
|
|
||||||
, title: _("context_media_cast")
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
let mediaCastTabId;
|
let mediaCastTabId;
|
||||||
let mediaCastFrameId;
|
let mediaCastFrameId;
|
||||||
|
|
||||||
@@ -225,6 +261,7 @@ let mirrorCastFrameId;
|
|||||||
|
|
||||||
browser.menus.onClicked.addListener(async (info, tab) => {
|
browser.menus.onClicked.addListener(async (info, tab) => {
|
||||||
const { frameId } = info;
|
const { frameId } = info;
|
||||||
|
const { options } = await browser.storage.sync.get("options");
|
||||||
|
|
||||||
// Load cast setup script
|
// Load cast setup script
|
||||||
await browser.tabs.executeScript(tab.id, {
|
await browser.tabs.executeScript(tab.id, {
|
||||||
@@ -238,7 +275,8 @@ browser.menus.onClicked.addListener(async (info, tab) => {
|
|||||||
mirrorCastFrameId = frameId;
|
mirrorCastFrameId = frameId;
|
||||||
|
|
||||||
await browser.tabs.executeScript(tab.id, {
|
await browser.tabs.executeScript(tab.id, {
|
||||||
code: `let selectedMedia = "${info.pageUrl ? "tab" : "screen"}";`
|
code: `let selectedMedia = "${info.pageUrl ? "tab" : "screen"}";
|
||||||
|
let FX_CAST_RECEIVER_APP_ID = ${options.option_mirroringAppId};`
|
||||||
, frameId
|
, frameId
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -422,3 +460,8 @@ messageRouter.register("mediaCast", message => {
|
|||||||
browser.runtime.onMessage.addListener((message, sender) => {
|
browser.runtime.onMessage.addListener((message, sender) => {
|
||||||
messageRouter.handleMessage(message, sender);
|
messageRouter.handleMessage(message, sender);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// Misc init
|
||||||
|
createMenus();
|
||||||
|
onOptionsUpdated();
|
||||||
|
|||||||
@@ -3,9 +3,6 @@
|
|||||||
let chrome;
|
let chrome;
|
||||||
let logMessage;
|
let logMessage;
|
||||||
|
|
||||||
const FX_CAST_RECEIVER_APP_ID = typeof MIRROR_CAST_APP_ID !== "undefined"
|
|
||||||
? MIRROR_CAST_APP_ID
|
|
||||||
: "19A6F4AE";
|
|
||||||
const FX_CAST_NAMESPACE = "urn:x-cast:fx_cast";
|
const FX_CAST_NAMESPACE = "urn:x-cast:fx_cast";
|
||||||
|
|
||||||
let session;
|
let session;
|
||||||
|
|||||||
11
ext/src/options/defaultOptions.js
Normal file
11
ext/src/options/defaultOptions.js
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
export default {
|
||||||
|
option_mediaEnabled: true
|
||||||
|
, option_localMediaEnabled: true
|
||||||
|
, option_localMediaServerPort: 9555
|
||||||
|
, option_uaWhitelistEnabled: true
|
||||||
|
, option_uaWhitelist: [
|
||||||
|
"https://www.netflix.com/*"
|
||||||
|
]
|
||||||
|
, option_mirroringEnabled: false
|
||||||
|
, option_mirroringAppId: MIRROR_CAST_APP_ID || "19A6F4AE"
|
||||||
|
}
|
||||||
@@ -5,7 +5,8 @@
|
|||||||
grid-row-gap: 5px;
|
grid-row-gap: 5px;
|
||||||
}
|
}
|
||||||
.category-name {}
|
.category-name {}
|
||||||
.category-description {
|
.category-description,
|
||||||
|
.category .category {
|
||||||
color: graytext;
|
color: graytext;
|
||||||
grid-column: span 2;
|
grid-column: span 2;
|
||||||
}
|
}
|
||||||
@@ -48,6 +49,7 @@
|
|||||||
.editable-list__items {
|
.editable-list__items {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
padding: initial;
|
||||||
}
|
}
|
||||||
.editable-list__item {
|
.editable-list__item {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
import React, { Component } from "react";
|
import React, { Component } from "react";
|
||||||
import ReactDOM from "react-dom";
|
import ReactDOM from "react-dom";
|
||||||
|
|
||||||
|
import defaultOptions from "./defaultOptions";
|
||||||
|
|
||||||
|
|
||||||
const _ = browser.i18n.getMessage;
|
const _ = browser.i18n.getMessage;
|
||||||
|
|
||||||
@@ -272,6 +274,7 @@ class OptionsApp extends Component {
|
|||||||
, isFormValid: true
|
, isFormValid: true
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.handleReset = this.handleReset.bind(this);
|
||||||
this.handleFormSubmit = this.handleFormSubmit.bind(this);
|
this.handleFormSubmit = this.handleFormSubmit.bind(this);
|
||||||
this.handleFormChange = this.handleFormChange.bind(this);
|
this.handleFormChange = this.handleFormChange.bind(this);
|
||||||
this.handleInputChange = this.handleInputChange.bind(this);
|
this.handleInputChange = this.handleInputChange.bind(this);
|
||||||
@@ -285,14 +288,26 @@ class OptionsApp extends Component {
|
|||||||
setStorage () {
|
setStorage () {
|
||||||
return browser.storage.sync.set({
|
return browser.storage.sync.set({
|
||||||
options: {
|
options: {
|
||||||
option_localMediaEnabled: this.state.option_localMediaEnabled
|
option_mediaEnabled: this.state.option_mediaEnabled
|
||||||
|
, option_localMediaEnabled: this.state.option_localMediaEnabled
|
||||||
, option_localMediaServerPort: this.state.option_localMediaServerPort
|
, option_localMediaServerPort: this.state.option_localMediaServerPort
|
||||||
, option_uaWhitelistEnabled: this.state.option_uaWhitelistEnabled
|
, option_uaWhitelistEnabled: this.state.option_uaWhitelistEnabled
|
||||||
, option_uaWhitelist: this.state.option_uaWhitelist
|
, option_uaWhitelist: this.state.option_uaWhitelist
|
||||||
|
, option_mirroringEnabled: this.state.option_mirroringEnabled
|
||||||
|
, option_mirroringAppId: this.state.option_mirroringAppId
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleReset () {
|
||||||
|
this.setState({
|
||||||
|
...defaultOptions
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO: Propagate state properly
|
||||||
|
this.form.submit();
|
||||||
|
}
|
||||||
|
|
||||||
async handleFormSubmit (ev) {
|
async handleFormSubmit (ev) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
|
|
||||||
@@ -358,6 +373,24 @@ class OptionsApp extends Component {
|
|||||||
<form id="form" ref={ form => { this.form = form; }}
|
<form id="form" ref={ form => { this.form = form; }}
|
||||||
onSubmit={ this.handleFormSubmit }
|
onSubmit={ this.handleFormSubmit }
|
||||||
onChange={ this.handleFormChange }>
|
onChange={ this.handleFormChange }>
|
||||||
|
<fieldset className="category">
|
||||||
|
<legend className="category-name">
|
||||||
|
{ _("options_category_media") }
|
||||||
|
</legend>
|
||||||
|
<p className="category-description">
|
||||||
|
{ _("options_category_media_description") }
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<label className="option">
|
||||||
|
<div className="option-label">
|
||||||
|
{ _("options_option_mediaEnabled") }
|
||||||
|
</div>
|
||||||
|
<input name="option_mediaEnabled"
|
||||||
|
type="checkbox"
|
||||||
|
checked={ this.state.option_mediaEnabled }
|
||||||
|
onChange={ this.handleInputChange } />
|
||||||
|
</label>
|
||||||
|
|
||||||
<fieldset className="category">
|
<fieldset className="category">
|
||||||
<legend className="category-name">
|
<legend className="category-name">
|
||||||
{ _("options_category_localMedia") }
|
{ _("options_category_localMedia") }
|
||||||
@@ -389,6 +422,37 @@ class OptionsApp extends Component {
|
|||||||
onChange={ this.handleInputChange } />
|
onChange={ this.handleInputChange } />
|
||||||
</label>
|
</label>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<fieldset className="category">
|
||||||
|
<legend className="category-name">
|
||||||
|
{ _("options_category_mirroring") }
|
||||||
|
</legend>
|
||||||
|
<p className="category-description">
|
||||||
|
{ _("options_category_mirroring_description") }
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<label className="option">
|
||||||
|
<div className="option-label">
|
||||||
|
{ _("options_option_mirroringEnabled") }
|
||||||
|
</div>
|
||||||
|
<input name="option_mirroringEnabled"
|
||||||
|
type="checkbox"
|
||||||
|
checked={ this.state.option_mirroringEnabled }
|
||||||
|
onChange={ this.handleInputChange } />
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label className="option">
|
||||||
|
<div className="option-label">
|
||||||
|
{ _("options_option_mirroringAppId") }
|
||||||
|
</div>
|
||||||
|
<input name="option_mirroringAppId"
|
||||||
|
type="text"
|
||||||
|
required
|
||||||
|
value={ this.state.option_mirroringAppId }
|
||||||
|
onChange={ this.handleInputChange } />
|
||||||
|
</label>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
<fieldset className="category">
|
<fieldset className="category">
|
||||||
<legend className="category-name">
|
<legend className="category-name">
|
||||||
@@ -420,6 +484,9 @@ class OptionsApp extends Component {
|
|||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<div id="buttons">
|
<div id="buttons">
|
||||||
|
<button onClick={ this.handleReset }>
|
||||||
|
{ _("options_reset") }
|
||||||
|
</button>
|
||||||
<button type="submit"
|
<button type="submit"
|
||||||
disabled={ !this.state.isFormValid }>
|
disabled={ !this.state.isFormValid }>
|
||||||
{ _("options_submit") }
|
{ _("options_submit") }
|
||||||
|
|||||||
Reference in New Issue
Block a user