mirror of
https://github.com/hensm/fx_cast.git
synced 2026-06-12 10:39:57 +00:00
Convert mediaCast sender to typescript
This commit is contained in:
@@ -429,17 +429,17 @@ browser.menus.onClicked.addListener(async (info, tab) => {
|
|||||||
const { frameId } = info;
|
const { frameId } = info;
|
||||||
const mirroringAppId = await options.get("mirroringAppId");
|
const mirroringAppId = await options.get("mirroringAppId");
|
||||||
|
|
||||||
|
switch (info.menuItemId) {
|
||||||
|
case mirrorCastMenuId: {
|
||||||
|
mirrorCastTabId = tab.id;
|
||||||
|
mirrorCastFrameId = frameId;
|
||||||
|
|
||||||
// Load cast setup script
|
// Load cast setup script
|
||||||
await browser.tabs.executeScript(tab.id, {
|
await browser.tabs.executeScript(tab.id, {
|
||||||
file: "shim/content.js"
|
file: "shim/content.js"
|
||||||
, frameId
|
, frameId
|
||||||
});
|
});
|
||||||
|
|
||||||
switch (info.menuItemId) {
|
|
||||||
case mirrorCastMenuId: {
|
|
||||||
mirrorCastTabId = tab.id;
|
|
||||||
mirrorCastFrameId = frameId;
|
|
||||||
|
|
||||||
await browser.tabs.executeScript(tab.id, {
|
await browser.tabs.executeScript(tab.id, {
|
||||||
code: `
|
code: `
|
||||||
var selectedMedia = ${info.pageUrl
|
var selectedMedia = ${info.pageUrl
|
||||||
@@ -456,6 +456,12 @@ browser.menus.onClicked.addListener(async (info, tab) => {
|
|||||||
, frameId
|
, frameId
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Load cast API
|
||||||
|
await browser.tabs.executeScript(tab.id, {
|
||||||
|
file: "shim/bundle.js"
|
||||||
|
, frameId
|
||||||
|
});
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -465,8 +471,10 @@ browser.menus.onClicked.addListener(async (info, tab) => {
|
|||||||
|
|
||||||
// Pass media URL to media sender app
|
// Pass media URL to media sender app
|
||||||
await browser.tabs.executeScript(tab.id, {
|
await browser.tabs.executeScript(tab.id, {
|
||||||
code: `var srcUrl = "${info.srcUrl}";
|
code: `
|
||||||
var targetElementId = ${info.targetElementId};`
|
window.srcUrl = "${info.srcUrl}";
|
||||||
|
window.targetElementId = ${info.targetElementId};
|
||||||
|
`
|
||||||
, frameId
|
, frameId
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -480,12 +488,6 @@ browser.menus.onClicked.addListener(async (info, tab) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load cast API
|
|
||||||
await browser.tabs.executeScript(tab.id, {
|
|
||||||
file: "shim/bundle.js"
|
|
||||||
, frameId
|
|
||||||
});
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,20 +1,28 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
let options;
|
import { Options } from "../defaultOptions";
|
||||||
|
import cast, { init } from "../shim/export";
|
||||||
let chrome;
|
|
||||||
let logMessage;
|
|
||||||
|
|
||||||
|
|
||||||
let session;
|
// Variables passed from background
|
||||||
let currentMedia;
|
const { srcUrl
|
||||||
|
, targetElementId }
|
||||||
|
: { srcUrl: string
|
||||||
|
, targetElementId: number } = (window as any);
|
||||||
|
|
||||||
|
|
||||||
|
let options: Options;
|
||||||
|
|
||||||
|
let session: cast.Session;
|
||||||
|
let currentMedia: cast.media.Media;
|
||||||
|
|
||||||
let ignoreMediaEvents = false;
|
let ignoreMediaEvents = false;
|
||||||
|
|
||||||
|
|
||||||
const isLocalFile = srcUrl.startsWith("file:");
|
const isLocalFile = srcUrl.startsWith("file:");
|
||||||
|
|
||||||
const mediaElement = browser.menus.getTargetElement(targetElementId);
|
const mediaElement = browser.menus.getTargetElement(
|
||||||
|
targetElementId) as HTMLMediaElement;
|
||||||
|
|
||||||
window.addEventListener("beforeunload", () => {
|
window.addEventListener("beforeunload", () => {
|
||||||
browser.runtime.sendMessage({
|
browser.runtime.sendMessage({
|
||||||
@@ -22,7 +30,7 @@ window.addEventListener("beforeunload", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (options.mediaStopOnUnload) {
|
if (options.mediaStopOnUnload) {
|
||||||
session.stop();
|
session.stop(null, null);
|
||||||
/*currentMedia.stop(null
|
/*currentMedia.stop(null
|
||||||
, onMediaStopSuccess
|
, onMediaStopSuccess
|
||||||
, onMediaStopError);*/
|
, onMediaStopError);*/
|
||||||
@@ -47,8 +55,8 @@ function getLocalAddress () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async function onRequestSessionSuccess (session_) {
|
async function onRequestSessionSuccess (session_: cast.Session) {
|
||||||
logMessage("onRequestSessionSuccess");
|
cast.logMessage("onRequestSessionSuccess");
|
||||||
|
|
||||||
session = session_;
|
session = session_;
|
||||||
|
|
||||||
@@ -77,16 +85,16 @@ async function onRequestSessionSuccess (session_) {
|
|||||||
mediaUrl = new URL(`http://${await getLocalAddress()}:${port}/`);
|
mediaUrl = new URL(`http://${await getLocalAddress()}:${port}/`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const mediaInfo = new chrome.cast.media.MediaInfo(mediaUrl.href);
|
const mediaInfo = new cast.media.MediaInfo(mediaUrl.href, null);
|
||||||
|
|
||||||
// Media metadata (title/poster)
|
// Media metadata (title/poster)
|
||||||
mediaInfo.metadata = new chrome.cast.media.GenericMediaMetadata();
|
mediaInfo.metadata = new cast.media.GenericMediaMetadata();
|
||||||
mediaInfo.metadata.metadataType = chrome.cast.media.MetadataType.GENERIC;
|
mediaInfo.metadata.metadataType = cast.media.MetadataType.GENERIC;
|
||||||
mediaInfo.metadata.title = mediaUrl.pathname;
|
mediaInfo.metadata.title = mediaUrl.pathname;
|
||||||
|
|
||||||
if (mediaElement.poster) {
|
if (mediaElement instanceof HTMLVideoElement && mediaElement.poster) {
|
||||||
mediaInfo.metadata.images = [
|
mediaInfo.metadata.images = [
|
||||||
new chrome.cast.Image(mediaElement.poster)
|
new cast.Image(mediaElement.poster)
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,13 +105,13 @@ async function onRequestSessionSuccess (session_) {
|
|||||||
const trackElements = mediaElement.querySelectorAll("track");
|
const trackElements = mediaElement.querySelectorAll("track");
|
||||||
|
|
||||||
let index = 0;
|
let index = 0;
|
||||||
for (const textTrack of mediaElement.textTracks) {
|
for (const textTrack of Array.from(mediaElement.textTracks)) {
|
||||||
const trackElement = trackElements[index];
|
const trackElement = trackElements[index];
|
||||||
|
|
||||||
// Create Track object
|
// Create Track object
|
||||||
const track = new chrome.cast.media.Track(
|
const track = new cast.media.Track(
|
||||||
index // trackId
|
index // trackId
|
||||||
, chrome.cast.media.TrackType.TEXT); // trackType
|
, cast.media.TrackType.TEXT); // trackType
|
||||||
|
|
||||||
// Copy TextTrack properties to Track
|
// Copy TextTrack properties to Track
|
||||||
track.name = textTrack.label;
|
track.name = textTrack.label;
|
||||||
@@ -111,7 +119,7 @@ async function onRequestSessionSuccess (session_) {
|
|||||||
track.trackContentId = trackElement.src;
|
track.trackContentId = trackElement.src;
|
||||||
track.trackContentType = "text/vtt";
|
track.trackContentType = "text/vtt";
|
||||||
|
|
||||||
const { TextTrackType } = chrome.cast.media;
|
const { TextTrackType } = cast.media;
|
||||||
|
|
||||||
switch (textTrack.kind) {
|
switch (textTrack.kind) {
|
||||||
case "subtitles":
|
case "subtitles":
|
||||||
@@ -147,7 +155,7 @@ async function onRequestSessionSuccess (session_) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const loadRequest = new chrome.cast.media.LoadRequest(mediaInfo);
|
const loadRequest = new cast.media.LoadRequest(mediaInfo);
|
||||||
loadRequest.autoplay = false;
|
loadRequest.autoplay = false;
|
||||||
loadRequest.activeTrackIds = activeTrackIds;
|
loadRequest.activeTrackIds = activeTrackIds;
|
||||||
|
|
||||||
@@ -156,31 +164,31 @@ async function onRequestSessionSuccess (session_) {
|
|||||||
, onLoadMediaError);
|
, onLoadMediaError);
|
||||||
}
|
}
|
||||||
function onRequestSessionError () {
|
function onRequestSessionError () {
|
||||||
logMessage("onRequestSessionError");
|
cast.logMessage("onRequestSessionError");
|
||||||
}
|
}
|
||||||
|
|
||||||
function sessionListener (session) {
|
function sessionListener (session: cast.Session) {
|
||||||
logMessage("sessionListener");
|
cast.logMessage("sessionListener");
|
||||||
}
|
}
|
||||||
function receiverListener (availability) {
|
function receiverListener (availability: string) {
|
||||||
logMessage("receiverListener");
|
cast.logMessage("receiverListener");
|
||||||
|
|
||||||
if (availability === chrome.cast.ReceiverAvailability.AVAILABLE) {
|
if (availability === cast.ReceiverAvailability.AVAILABLE) {
|
||||||
chrome.cast.requestSession(
|
cast.requestSession(
|
||||||
onRequestSessionSuccess
|
onRequestSessionSuccess
|
||||||
, onRequestSessionError);
|
, onRequestSessionError);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onInitializeSuccess () {
|
function onInitializeSuccess () {
|
||||||
logMessage("onInitializeSuccess");
|
cast.logMessage("onInitializeSuccess");
|
||||||
}
|
}
|
||||||
function onInitializeError () {
|
function onInitializeError () {
|
||||||
logMessage("onInitializeError");
|
cast.logMessage("onInitializeError");
|
||||||
}
|
}
|
||||||
|
|
||||||
function onLoadMediaSuccess (media) {
|
function onLoadMediaSuccess (media: cast.media.Media) {
|
||||||
logMessage("onLoadMediaSuccess");
|
cast.logMessage("onLoadMediaSuccess");
|
||||||
|
|
||||||
currentMedia = media;
|
currentMedia = media;
|
||||||
|
|
||||||
@@ -219,7 +227,7 @@ function onLoadMediaSuccess (media) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const seekRequest = new chrome.cast.media.SeekRequest();
|
const seekRequest = new cast.media.SeekRequest();
|
||||||
seekRequest.currentTime = mediaElement.currentTime;
|
seekRequest.currentTime = mediaElement.currentTime;
|
||||||
|
|
||||||
currentMedia.seek(seekRequest
|
currentMedia.seek(seekRequest
|
||||||
@@ -228,21 +236,21 @@ function onLoadMediaSuccess (media) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
mediaElement.addEventListener("ratechange", () => {
|
mediaElement.addEventListener("ratechange", () => {
|
||||||
currentMedia._sendMediaMessage({
|
(currentMedia as any)._sendMediaMessage({
|
||||||
type: "SET_PLAYBACK_RATE"
|
type: "SET_PLAYBACK_RATE"
|
||||||
, playbackRate: mediaElement.playbackRate
|
, playbackRate: mediaElement.playbackRate
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
mediaElement.addEventListener("volumechange", () => {
|
mediaElement.addEventListener("volumechange", () => {
|
||||||
const newVolume = new chrome.cast.Volume(
|
const newVolume = new cast.Volume(
|
||||||
currentMedia.volume
|
currentMedia.volume.level
|
||||||
, currentMedia.muted);
|
, currentMedia.volume.muted);
|
||||||
|
|
||||||
const volumeRequest =
|
const volumeRequest =
|
||||||
new chrome.cast.media.VolumeRequest(newVolume);
|
new cast.media.VolumeRequest(newVolume);
|
||||||
|
|
||||||
logMessage("Volume change");
|
cast.logMessage("Volume change");
|
||||||
currentMedia.setVolume(volumeRequest);
|
currentMedia.setVolume(volumeRequest);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -254,17 +262,17 @@ function onLoadMediaSuccess (media) {
|
|||||||
|
|
||||||
// PlayerState
|
// PlayerState
|
||||||
const localPlayerState = mediaElement.paused
|
const localPlayerState = mediaElement.paused
|
||||||
? chrome.cast.media.PlayerState.PAUSED
|
? cast.media.PlayerState.PAUSED
|
||||||
: chrome.cast.media.PlayerState.PLAYING;
|
: cast.media.PlayerState.PLAYING;
|
||||||
|
|
||||||
if (localPlayerState !== currentMedia.playerState) {
|
if (localPlayerState !== currentMedia.playerState) {
|
||||||
ignoreMediaEvents = true;
|
ignoreMediaEvents = true;
|
||||||
switch (currentMedia.playerState) {
|
switch (currentMedia.playerState) {
|
||||||
case chrome.cast.media.PlayerState.PLAYING:
|
case cast.media.PlayerState.PLAYING:
|
||||||
mediaElement.play();
|
mediaElement.play();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case chrome.cast.media.PlayerState.PAUSED:
|
case cast.media.PlayerState.PAUSED:
|
||||||
mediaElement.pause();
|
mediaElement.pause();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -272,17 +280,17 @@ function onLoadMediaSuccess (media) {
|
|||||||
|
|
||||||
// RepeatMode
|
// RepeatMode
|
||||||
const localRepeatMode = mediaElement.loop
|
const localRepeatMode = mediaElement.loop
|
||||||
? chrome.cast.media.RepeatMode.SINGLE
|
? cast.media.RepeatMode.SINGLE
|
||||||
: chrome.cast.media.RepeatMode.OFF;
|
: cast.media.RepeatMode.OFF;
|
||||||
|
|
||||||
if (localRepeatMode !== currentMedia.repeatMode) {
|
if (localRepeatMode !== currentMedia.repeatMode) {
|
||||||
ignoreMediaEvents = true;
|
ignoreMediaEvents = true;
|
||||||
switch (currentMedia.repeatMode) {
|
switch (currentMedia.repeatMode) {
|
||||||
case chrome.cast.media.RepeatMode.SINGLE:
|
case cast.media.RepeatMode.SINGLE:
|
||||||
mediaElement.loop = true;
|
mediaElement.loop = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case chrome.cast.media.RepeatMode.OFF:
|
case cast.media.RepeatMode.OFF:
|
||||||
mediaElement.loop = false;
|
mediaElement.loop = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -298,70 +306,64 @@ function onLoadMediaSuccess (media) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
function onLoadMediaError () {
|
function onLoadMediaError () {
|
||||||
logMessage("onLoadMediaError");
|
cast.logMessage("onLoadMediaError");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* play */
|
/* play */
|
||||||
function onMediaPlaySuccess () {
|
function onMediaPlaySuccess () {
|
||||||
logMessage("onMediaPlaySuccess");
|
cast.logMessage("onMediaPlaySuccess");
|
||||||
}
|
}
|
||||||
function onMediaPlayError (err) {
|
function onMediaPlayError (err: cast.Error) {
|
||||||
logMessage("onMediaPlayError");
|
cast.logMessage("onMediaPlayError");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* pause */
|
/* pause */
|
||||||
function onMediaPauseSuccess () {
|
function onMediaPauseSuccess () {
|
||||||
logMessage("onMediaPauseSuccess");
|
cast.logMessage("onMediaPauseSuccess");
|
||||||
}
|
}
|
||||||
function onMediaPauseError (err) {
|
function onMediaPauseError (err: cast.Error) {
|
||||||
logMessage("onMediaPauseError");
|
cast.logMessage("onMediaPauseError");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* stop */
|
/* stop */
|
||||||
function onMediaStopSuccess () {
|
function onMediaStopSuccess () {
|
||||||
logMessage("onMediaStopSuccess");
|
cast.logMessage("onMediaStopSuccess");
|
||||||
}
|
}
|
||||||
function onMediaStopError (err) {
|
function onMediaStopError (err: cast.Error) {
|
||||||
logMessage("onMediaStopError");
|
cast.logMessage("onMediaStopError");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* seek */
|
/* seek */
|
||||||
function onMediaSeekSuccess () {
|
function onMediaSeekSuccess () {
|
||||||
logMessage("onMediaSeekSuccess");
|
cast.logMessage("onMediaSeekSuccess");
|
||||||
}
|
}
|
||||||
function onMediaSeekError (err) {
|
function onMediaSeekError (err: cast.Error) {
|
||||||
logMessage("onMediaSeekError");
|
cast.logMessage("onMediaSeekError");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
window.__onGCastApiAvailable = async function (loaded, errorInfo) {
|
init().then(async bridgeInfo => {
|
||||||
if (!loaded) {
|
if (!bridgeInfo.isVersionCompatible) {
|
||||||
console.error("__onGCastApiAvailable error");
|
console.error("__onGCastApiAvailable error");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
chrome = window.chrome;
|
|
||||||
logMessage = chrome.cast.logMessage;
|
|
||||||
|
|
||||||
logMessage("__onGCastApiAvailable success");
|
|
||||||
|
|
||||||
|
|
||||||
options = (await browser.storage.sync.get("options")).options;
|
options = (await browser.storage.sync.get("options")).options;
|
||||||
|
|
||||||
if (isLocalFile && !options.localMediaEnabled) {
|
if (isLocalFile && !options.localMediaEnabled) {
|
||||||
logMessage("Local media casting not enabled");
|
cast.logMessage("Local media casting not enabled");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const sessionRequest = new chrome.cast.SessionRequest(
|
const sessionRequest = new cast.SessionRequest(
|
||||||
chrome.cast.media.DEFAULT_MEDIA_RECEIVER_APP_ID);
|
cast.media.DEFAULT_MEDIA_RECEIVER_APP_ID);
|
||||||
|
|
||||||
const apiConfig = new chrome.cast.ApiConfig(sessionRequest
|
const apiConfig = new cast.ApiConfig(sessionRequest
|
||||||
, sessionListener
|
, sessionListener
|
||||||
, receiverListener);
|
, receiverListener);
|
||||||
|
|
||||||
chrome.cast.initialize(apiConfig
|
cast.initialize(apiConfig
|
||||||
, onInitializeSuccess
|
, onInitializeSuccess
|
||||||
, onInitializeError);
|
, onInitializeError);
|
||||||
};
|
});
|
||||||
34
ext/src/shim/export.ts
Normal file
34
ext/src/shim/export.ts
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
import * as cast from "./cast";
|
||||||
|
|
||||||
|
import { BridgeInfo } from "../lib/getBridgeInfo";
|
||||||
|
import { Message } from "../types";
|
||||||
|
import { onMessage } from "./messageBridge";
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To support exporting an API from a module, we need to
|
||||||
|
* retain the event-based message passing despite not
|
||||||
|
* actually crossing any context boundaries. The shim listens
|
||||||
|
* for and emits these messages, and changing that behavior
|
||||||
|
* is too messy.
|
||||||
|
*/
|
||||||
|
export function init (): Promise<BridgeInfo> {
|
||||||
|
return new Promise(async (resolve, reject) => {
|
||||||
|
|
||||||
|
// Trigger message port setup side-effects
|
||||||
|
import("./content");
|
||||||
|
|
||||||
|
onMessage(message => {
|
||||||
|
switch (message.subject) {
|
||||||
|
case "shim:/initialized": {
|
||||||
|
const bridgeInfo: BridgeInfo = message.data;
|
||||||
|
resolve(bridgeInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export default cast;
|
||||||
@@ -2,9 +2,8 @@
|
|||||||
|
|
||||||
import * as cast from "./cast";
|
import * as cast from "./cast";
|
||||||
|
|
||||||
import { onMessage } from "./messageBridge";
|
|
||||||
|
|
||||||
import { loadScript } from "../lib/utils";
|
import { loadScript } from "../lib/utils";
|
||||||
|
import { onMessage } from "./messageBridge";
|
||||||
|
|
||||||
|
|
||||||
const _window = (window as any);
|
const _window = (window as any);
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ module.exports = (env) => ({
|
|||||||
, "ui/updater/bundle": `${env.includePath}/ui/updater/index.tsx`
|
, "ui/updater/bundle": `${env.includePath}/ui/updater/index.tsx`
|
||||||
|
|
||||||
// Sender apps
|
// Sender apps
|
||||||
, "senders/mediaCast": `${env.includePath}/senders/mediaCast.js`
|
, "senders/mediaCast": `${env.includePath}/senders/mediaCast.ts`
|
||||||
, "senders/mirroringCast": `${env.includePath}/senders/mirroringCast.js`
|
, "senders/mirroringCast": `${env.includePath}/senders/mirroringCast.js`
|
||||||
|
|
||||||
// Shim entries
|
// Shim entries
|
||||||
|
|||||||
Reference in New Issue
Block a user