mirror of
https://github.com/hensm/fx_cast.git
synced 2026-06-08 08:39:59 +00:00
Add media context menu items to receiver popup
This commit is contained in:
@@ -78,7 +78,7 @@
|
||||
"description": "Alternate action button text displayed instead of popupCastButtonTitle."
|
||||
},
|
||||
"popupCastMenuTitle": {
|
||||
"message": "Cast to \"$deviceName$\"",
|
||||
"message": "Cast to $deviceName$",
|
||||
"description": "Menu text for cast item in context menu for receivers in receiver selector.",
|
||||
"placeholders": {
|
||||
"deviceName": {
|
||||
@@ -88,7 +88,7 @@
|
||||
}
|
||||
},
|
||||
"popupStopMenuTitle": {
|
||||
"message": "Stop \"$appName$\" on \"$deviceName$\"",
|
||||
"message": "Stop \"$appName$\" on $deviceName$",
|
||||
"description": "Menu text for stop item in context menu for receivers in receiver selector.",
|
||||
"placeholders": {
|
||||
"appName": {
|
||||
@@ -133,11 +133,11 @@
|
||||
"message": "Seek forwards",
|
||||
"description": "Media controls seek forward button title."
|
||||
},
|
||||
"popupMediaSubtitlesClosedCaptions": {
|
||||
"message": "Subtitles/closed captions",
|
||||
"popupMediaSubtitlesCaptions": {
|
||||
"message": "Subtitles/captions",
|
||||
"description": "Media controls subtitles/cc button title."
|
||||
},
|
||||
"popupMediaSubtitlesClosedCaptionsOff": {
|
||||
"popupMediaSubtitlesCaptionsOff": {
|
||||
"message": "Off",
|
||||
"description": "Media controls subtitles/cc button title."
|
||||
},
|
||||
|
||||
@@ -9,6 +9,8 @@ import { ReceiverSelectorMediaType } from "../types";
|
||||
import ReceiverSelector, { ReceiverSelection } from "./ReceiverSelector";
|
||||
import castManager from "./castManager";
|
||||
|
||||
import * as menuIds from "../menuIds";
|
||||
|
||||
const _ = browser.i18n.getMessage;
|
||||
|
||||
const URL_PATTERN_HTTP = "http://*/*";
|
||||
@@ -69,16 +71,51 @@ export async function initMenus() {
|
||||
});
|
||||
|
||||
// Popup context menus
|
||||
const popupUrlPattern = `${browser.runtime.getURL("ui/popup")}/*`;
|
||||
browser.menus.create({
|
||||
id: "popup_cast",
|
||||
title: _("popupCastButtonTitle"),
|
||||
documentUrlPatterns: [popupUrlPattern]
|
||||
const createPopupMenu = (props: browser.menus._CreateCreateProperties) =>
|
||||
browser.menus.create({
|
||||
visible: false,
|
||||
documentUrlPatterns: [`${browser.runtime.getURL("ui/popup")}/*`],
|
||||
...props
|
||||
});
|
||||
|
||||
createPopupMenu({
|
||||
id: menuIds.POPUP_MEDIA_PLAY_PAUSE,
|
||||
title: _("popupMediaPlay")
|
||||
});
|
||||
browser.menus.create({
|
||||
id: "popup_stop",
|
||||
title: _("popupStopButtonTitle"),
|
||||
documentUrlPatterns: [popupUrlPattern]
|
||||
createPopupMenu({
|
||||
id: menuIds.POPUP_MEDIA_MUTE,
|
||||
type: "checkbox",
|
||||
title: _("popupMediaMute")
|
||||
});
|
||||
createPopupMenu({
|
||||
id: menuIds.POPUP_MEDIA_SKIP_PREVIOUS,
|
||||
title: _("popupMediaSkipPrevious")
|
||||
});
|
||||
createPopupMenu({
|
||||
id: menuIds.POPUP_MEDIA_SKIP_NEXT,
|
||||
title: _("popupMediaSkipNext")
|
||||
});
|
||||
createPopupMenu({
|
||||
id: menuIds.POPUP_MEDIA_CC,
|
||||
title: _("popupMediaSubtitlesCaptions")
|
||||
});
|
||||
createPopupMenu({
|
||||
id: menuIds.POPUP_MEDIA_CC_OFF,
|
||||
parentId: menuIds.POPUP_MEDIA_CC,
|
||||
type: "radio",
|
||||
title: _("popupMediaSubtitlesCaptionsOff")
|
||||
});
|
||||
|
||||
createPopupMenu({ id: menuIds.POPUP_MEDIA_SEPARATOR, type: "separator" });
|
||||
|
||||
createPopupMenu({
|
||||
id: menuIds.POPUP_CAST,
|
||||
title: _("popupCastButtonTitle"),
|
||||
icons: { 16: "icons/icon.svg" }
|
||||
});
|
||||
createPopupMenu({
|
||||
id: menuIds.POPUP_STOP,
|
||||
title: _("popupStopButtonTitle")
|
||||
});
|
||||
|
||||
browser.menus.onShown.addListener(onMenuShown);
|
||||
|
||||
30
ext/src/menuIds.ts
Normal file
30
ext/src/menuIds.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
export const POPUP_CAST = "popupCastMenuId";
|
||||
export const POPUP_STOP = "popupStopMenuId";
|
||||
|
||||
export const POPUP_MEDIA_SEPARATOR = "popupMediaSeparatorMenuId";
|
||||
export const POPUP_MEDIA_PLAY_PAUSE = "popupMediaPlayPauseMenuId";
|
||||
export const POPUP_MEDIA_MUTE = "popupMediaMuteMenuId";
|
||||
export const POPUP_MEDIA_SKIP_PREVIOUS = "popupMediaSkipPreviousMenuId";
|
||||
export const POPUP_MEDIA_SKIP_NEXT = "popupMediaSkipNextMenuId";
|
||||
export const POPUP_MEDIA_CC = "popupMediaSubtitlesCaptionsMenuId";
|
||||
export const POPUP_MEDIA_CC_OFF = "popupMediaSubtitlesCaptionsOffMenuId";
|
||||
|
||||
export const receiverMenuIds = [
|
||||
POPUP_CAST,
|
||||
POPUP_STOP,
|
||||
POPUP_MEDIA_SEPARATOR,
|
||||
POPUP_MEDIA_PLAY_PAUSE,
|
||||
POPUP_MEDIA_MUTE,
|
||||
POPUP_MEDIA_SKIP_PREVIOUS,
|
||||
POPUP_MEDIA_SKIP_NEXT,
|
||||
POPUP_MEDIA_CC
|
||||
];
|
||||
|
||||
export const mediaMenuIds = [
|
||||
POPUP_MEDIA_SEPARATOR,
|
||||
POPUP_MEDIA_PLAY_PAUSE,
|
||||
POPUP_MEDIA_MUTE,
|
||||
POPUP_MEDIA_SKIP_PREVIOUS,
|
||||
POPUP_MEDIA_SKIP_NEXT,
|
||||
POPUP_MEDIA_CC
|
||||
];
|
||||
@@ -5,6 +5,8 @@
|
||||
import options, { Options } from "../../lib/options";
|
||||
import { RemoteMatchPattern } from "../../lib/matchPattern";
|
||||
|
||||
import { receiverMenuIds } from "../../menuIds";
|
||||
|
||||
import {
|
||||
ReceiverDevice,
|
||||
ReceiverDeviceCapabilities,
|
||||
@@ -128,8 +130,6 @@
|
||||
|
||||
resizeObserver.observe(document.documentElement);
|
||||
|
||||
window.addEventListener("contextmenu", onContextMenu);
|
||||
browser.menus.onClicked.addListener(onMenuClicked);
|
||||
browser.menus.onShown.addListener(onMenuShown);
|
||||
});
|
||||
|
||||
@@ -137,8 +137,6 @@
|
||||
port?.disconnect();
|
||||
resizeObserver.disconnect();
|
||||
|
||||
window.removeEventListener("contextmenu", onContextMenu);
|
||||
browser.menus.onClicked.removeListener(onMenuClicked);
|
||||
browser.menus.onShown.removeListener(onMenuShown);
|
||||
});
|
||||
|
||||
@@ -247,93 +245,27 @@
|
||||
}
|
||||
}
|
||||
|
||||
function onContextMenu(ev: MouseEvent) {
|
||||
if (!(ev.target instanceof Element)) return;
|
||||
|
||||
const receiverElement = ev.target.closest(".receiver");
|
||||
if (receiverElement) {
|
||||
browser.menus.overrideContext({
|
||||
showDefaults: false
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function getDeviceFromElement(target: Element) {
|
||||
const receiverElement = target.closest(".receiver");
|
||||
if (!receiverElement) return;
|
||||
|
||||
const receiverElementIndex = [
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
...receiverElement.parentElement!.children
|
||||
].indexOf(receiverElement);
|
||||
|
||||
// Match by index rendered receiver element to device array
|
||||
if (receiverElementIndex > -1) {
|
||||
return devices[receiverElementIndex];
|
||||
}
|
||||
}
|
||||
/** Device ID associated with the last receiver menu that was shown. */
|
||||
let lastMenuShownDeviceId: string;
|
||||
|
||||
/** Handle show events for receiver context menus. */
|
||||
function onMenuShown(info: browser.menus._OnShownInfo) {
|
||||
if (!info.targetElementId) return;
|
||||
const target = browser.menus.getTargetElement(info.targetElementId);
|
||||
if (!target) return;
|
||||
|
||||
const device = getDeviceFromElement(target);
|
||||
if (!device) {
|
||||
browser.menus.update("popup_cast", { visible: false });
|
||||
browser.menus.update("popup_stop", { visible: false });
|
||||
} else {
|
||||
const app = device.status?.applications?.[0];
|
||||
const isAppRunning = !!(app && !app.isIdleScreen);
|
||||
|
||||
browser.menus.update("popup_cast", {
|
||||
visible: true,
|
||||
title: _("popupCastMenuTitle", device.friendlyName),
|
||||
enabled:
|
||||
// Not already connecting to a receiver
|
||||
!isConnecting &&
|
||||
// Selected media type available
|
||||
isMediaTypeAvailable
|
||||
});
|
||||
|
||||
browser.menus.update("popup_stop", {
|
||||
visible: isAppRunning,
|
||||
title: isAppRunning
|
||||
? _("popupStopMenuTitle", [
|
||||
app.displayName,
|
||||
device.friendlyName
|
||||
])
|
||||
: ""
|
||||
});
|
||||
}
|
||||
|
||||
browser.menus.refresh();
|
||||
}
|
||||
|
||||
/** Handle click events for receiver context menus. */
|
||||
function onMenuClicked(info: browser.menus.OnClickData) {
|
||||
if (
|
||||
info.menuItemId !== "popup_cast" &&
|
||||
info.menuItemId !== "popup_stop"
|
||||
) {
|
||||
return;
|
||||
}
|
||||
// Only handle menu events on this page
|
||||
if (info.pageUrl !== window.location.href) return;
|
||||
|
||||
if (!info.targetElementId) return;
|
||||
const target = browser.menus.getTargetElement(info.targetElementId);
|
||||
if (!target) return;
|
||||
const targetElement = browser.menus.getTargetElement(
|
||||
info.targetElementId
|
||||
);
|
||||
if (!targetElement) return;
|
||||
|
||||
const device = getDeviceFromElement(target);
|
||||
if (!device) return;
|
||||
const receiverElement = targetElement.closest(".receiver");
|
||||
if (!receiverElement) {
|
||||
for (const menuId of receiverMenuIds) {
|
||||
browser.menus.update(menuId, { visible: false });
|
||||
}
|
||||
|
||||
switch (info.menuItemId) {
|
||||
case "popup_cast":
|
||||
onReceiverCast(device);
|
||||
break;
|
||||
case "popup_stop":
|
||||
onReceiverStop(device);
|
||||
break;
|
||||
browser.menus.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -434,6 +366,7 @@
|
||||
ReceiverSelectorMediaType.None &&
|
||||
isDeviceCompatible(device)}
|
||||
isAnyConnecting={isConnecting}
|
||||
bind:lastMenuShownDeviceId
|
||||
on:cast={ev => onReceiverCast(ev.detail.device)}
|
||||
on:stop={ev => onReceiverStop(ev.detail.device)}
|
||||
/>
|
||||
|
||||
@@ -4,10 +4,14 @@
|
||||
import { ReceiverDevice, ReceiverDeviceCapabilities } from "../../types";
|
||||
import type { Port } from "../../messaging";
|
||||
|
||||
import { PlayerState } from "../../cast/sdk/media/enums";
|
||||
import type {
|
||||
import * as menuIds from "../../menuIds";
|
||||
|
||||
import type { Volume } from "../../cast/sdk/classes";
|
||||
import { PlayerState, TrackType } from "../../cast/sdk/media/enums";
|
||||
import {
|
||||
SenderMediaMessage,
|
||||
SenderMessage
|
||||
SenderMessage,
|
||||
_MediaCommand
|
||||
} from "../../cast/sdk/types";
|
||||
|
||||
import LoadingIndicator from "../LoadingIndicator.svelte";
|
||||
@@ -37,6 +41,42 @@
|
||||
/** Current media status (if available) */
|
||||
$: mediaStatus = device.mediaStatus;
|
||||
|
||||
export let lastMenuShownDeviceId: string;
|
||||
$: if (lastMenuShownDeviceId === device.id) {
|
||||
void device.mediaStatus;
|
||||
updateMediaMenus();
|
||||
browser.menus.refresh();
|
||||
}
|
||||
|
||||
const languageNames = new Intl.DisplayNames(
|
||||
[browser.i18n.getUILanguage()],
|
||||
{ type: "language" }
|
||||
);
|
||||
|
||||
// Subtitle/caption tracks
|
||||
$: textTracks = mediaStatus?.media?.tracks
|
||||
?.filter(track => track.type === TrackType.TEXT)
|
||||
.map(track => {
|
||||
/**
|
||||
* If track has no name, but does have a language, get a
|
||||
* display name for the language.
|
||||
*/
|
||||
if (!track.name && track.language) {
|
||||
try {
|
||||
const displayName = languageNames.of(track.language);
|
||||
if (displayName) {
|
||||
track.name = displayName;
|
||||
}
|
||||
// eslint-disable-next-line no-empty
|
||||
} catch (err) {}
|
||||
}
|
||||
|
||||
return track;
|
||||
});
|
||||
$: activeTextTrackId = mediaStatus?.activeTrackIds?.find(trackId =>
|
||||
textTracks?.find(track => track.trackId === trackId)
|
||||
);
|
||||
|
||||
/** Whether media controls are shown. */
|
||||
let isExpanded = false;
|
||||
$: if (!device.mediaStatus) {
|
||||
@@ -79,14 +119,252 @@
|
||||
});
|
||||
}
|
||||
|
||||
let receiverElement: HTMLLIElement;
|
||||
function isTarget(
|
||||
info?: browser.menus._OnShownInfo | browser.menus.OnClickData
|
||||
) {
|
||||
// Only handle menu events on this page
|
||||
if (info?.pageUrl !== window.location.href) return false;
|
||||
|
||||
if (!info.targetElementId) return false;
|
||||
const targetElement = browser.menus.getTargetElement(
|
||||
info.targetElementId
|
||||
);
|
||||
if (!targetElement) return false;
|
||||
|
||||
return (
|
||||
targetElement === receiverElement ||
|
||||
receiverElement.contains(targetElement)
|
||||
);
|
||||
}
|
||||
|
||||
// Map of menu IDs to track IDs
|
||||
const captionSubmenus = new Map<number | string, number>();
|
||||
|
||||
function onMenuShown(info: browser.menus._OnShownInfo) {
|
||||
if (!isTarget(info)) {
|
||||
return;
|
||||
}
|
||||
|
||||
lastMenuShownDeviceId = device.id;
|
||||
|
||||
browser.menus.update(menuIds.POPUP_CAST, {
|
||||
visible: true,
|
||||
title: _("popupCastMenuTitle", device.friendlyName),
|
||||
enabled:
|
||||
// Not already connecting to a receiver
|
||||
!isConnecting &&
|
||||
// Selected media type available
|
||||
isMediaTypeAvailable
|
||||
});
|
||||
|
||||
browser.menus.update(menuIds.POPUP_STOP, {
|
||||
visible: !!application && !application.isIdleScreen,
|
||||
title: application?.displayName
|
||||
? _("popupStopMenuTitle", [
|
||||
application.displayName,
|
||||
device.friendlyName
|
||||
])
|
||||
: ""
|
||||
});
|
||||
|
||||
updateMediaMenus();
|
||||
browser.menus.refresh();
|
||||
}
|
||||
|
||||
function handleMediaPlayPause() {
|
||||
switch (mediaStatus?.playerState) {
|
||||
case PlayerState.PLAYING:
|
||||
sendMediaMessage({ type: "PAUSE" });
|
||||
break;
|
||||
case PlayerState.PAUSED:
|
||||
sendMediaMessage({ type: "PLAY" });
|
||||
break;
|
||||
}
|
||||
}
|
||||
function handleMediaSkipPrevious() {
|
||||
sendMediaMessage({
|
||||
type: "QUEUE_UPDATE",
|
||||
jump: -1
|
||||
});
|
||||
}
|
||||
function handleMediaSkipNext() {
|
||||
sendMediaMessage({
|
||||
type: "QUEUE_UPDATE",
|
||||
jump: 1
|
||||
});
|
||||
}
|
||||
function handleMediaTrackChange(activeTrackIds: number[]) {
|
||||
sendMediaMessage({
|
||||
type: "EDIT_TRACKS_INFO",
|
||||
activeTrackIds: activeTrackIds
|
||||
});
|
||||
}
|
||||
function handleVolumeChange(volume: Partial<Volume>) {
|
||||
sendReceiverMessage({
|
||||
type: "SET_VOLUME",
|
||||
volume
|
||||
});
|
||||
}
|
||||
|
||||
function onMenuClicked(info: browser.menus.OnClickData) {
|
||||
if (!isTarget(info)) return;
|
||||
|
||||
switch (info.menuItemId) {
|
||||
case menuIds.POPUP_MEDIA_PLAY_PAUSE:
|
||||
handleMediaPlayPause();
|
||||
break;
|
||||
case menuIds.POPUP_MEDIA_MUTE:
|
||||
if (
|
||||
!device.status?.volume.muted &&
|
||||
device.status?.volume.level === 0
|
||||
) {
|
||||
handleVolumeChange({ level: 1 });
|
||||
} else {
|
||||
handleVolumeChange({ muted: !device.status?.volume.muted });
|
||||
}
|
||||
break;
|
||||
case menuIds.POPUP_MEDIA_SKIP_PREVIOUS:
|
||||
handleMediaSkipPrevious();
|
||||
break;
|
||||
case menuIds.POPUP_MEDIA_SKIP_NEXT:
|
||||
handleMediaSkipNext();
|
||||
break;
|
||||
}
|
||||
|
||||
// Handle caption submenu items
|
||||
if (info.parentMenuItemId === menuIds.POPUP_MEDIA_CC) {
|
||||
// Filter and append active track IDs array
|
||||
if (!mediaStatus?.activeTrackIds) return;
|
||||
const activeTrackIds = mediaStatus.activeTrackIds.filter(
|
||||
activeTrackId => activeTrackId !== activeTextTrackId
|
||||
);
|
||||
|
||||
const trackId = captionSubmenus.get(info.menuItemId);
|
||||
if (trackId) {
|
||||
activeTrackIds.push(trackId);
|
||||
}
|
||||
|
||||
handleMediaTrackChange(activeTrackIds);
|
||||
}
|
||||
}
|
||||
|
||||
function onContextMenu() {
|
||||
browser.menus.overrideContext({ showDefaults: false });
|
||||
}
|
||||
|
||||
/** Updates media menu items from media status. */
|
||||
function updateMediaMenus() {
|
||||
// Clear caption submenu for re-build
|
||||
if (captionSubmenus.size) {
|
||||
for (const menuId of captionSubmenus.keys()) {
|
||||
browser.menus.remove(menuId);
|
||||
}
|
||||
captionSubmenus.clear();
|
||||
}
|
||||
|
||||
// Hide all media menu items if no media status
|
||||
if (!mediaStatus) {
|
||||
for (const menuId of menuIds.mediaMenuIds) {
|
||||
browser.menus.update(menuId, { visible: false });
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
browser.menus.update(menuIds.POPUP_MEDIA_SEPARATOR, {
|
||||
visible: true
|
||||
});
|
||||
|
||||
// Play/pause menu item
|
||||
if (mediaStatus.supportedMediaCommands & _MediaCommand.PAUSE) {
|
||||
browser.menus.update(menuIds.POPUP_MEDIA_PLAY_PAUSE, {
|
||||
visible: true,
|
||||
title:
|
||||
mediaStatus.playerState === PlayerState.PLAYING ||
|
||||
mediaStatus.playerState === PlayerState.BUFFERING
|
||||
? _("popupMediaPause")
|
||||
: _("popupMediaPlay"),
|
||||
enabled:
|
||||
mediaStatus.playerState === PlayerState.PLAYING ||
|
||||
mediaStatus.playerState === PlayerState.PAUSED
|
||||
});
|
||||
} else {
|
||||
browser.menus.update(menuIds.POPUP_MEDIA_PLAY_PAUSE, {
|
||||
visible: false
|
||||
});
|
||||
}
|
||||
|
||||
// Mute/unmute menu item
|
||||
if (device.status?.volume) {
|
||||
const volume = device.status.volume;
|
||||
|
||||
browser.menus.update(menuIds.POPUP_MEDIA_MUTE, {
|
||||
visible: true,
|
||||
title: _("popupMediaMute"),
|
||||
checked: volume.muted || volume.level === 0,
|
||||
enabled: "muted" in volume
|
||||
});
|
||||
} else {
|
||||
browser.menus.update(menuIds.POPUP_MEDIA_MUTE, {
|
||||
visible: false
|
||||
});
|
||||
}
|
||||
|
||||
browser.menus.update(menuIds.POPUP_MEDIA_SKIP_PREVIOUS, {
|
||||
visible: !!(
|
||||
mediaStatus.supportedMediaCommands & _MediaCommand.QUEUE_PREV
|
||||
)
|
||||
});
|
||||
browser.menus.update(menuIds.POPUP_MEDIA_SKIP_NEXT, {
|
||||
visible: !!(
|
||||
mediaStatus.supportedMediaCommands & _MediaCommand.QUEUE_NEXT
|
||||
)
|
||||
});
|
||||
|
||||
// Build captions submenu from text tracks
|
||||
if (
|
||||
textTracks?.length &&
|
||||
mediaStatus.supportedMediaCommands & _MediaCommand.EDIT_TRACKS
|
||||
) {
|
||||
browser.menus.update(menuIds.POPUP_MEDIA_CC, { visible: true });
|
||||
browser.menus.update(menuIds.POPUP_MEDIA_CC_OFF, {
|
||||
visible: true,
|
||||
checked: activeTextTrackId === undefined
|
||||
});
|
||||
|
||||
for (const track of textTracks) {
|
||||
const menuId = browser.menus.create({
|
||||
title: track.name ?? track.trackId.toString(),
|
||||
parentId: menuIds.POPUP_MEDIA_CC,
|
||||
type: "radio",
|
||||
checked: track.trackId === activeTextTrackId
|
||||
});
|
||||
|
||||
captionSubmenus.set(menuId, track.trackId);
|
||||
}
|
||||
} else {
|
||||
browser.menus.update(menuIds.POPUP_MEDIA_CC, {
|
||||
visible: false
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
sendMediaMessage({
|
||||
type: "GET_STATUS"
|
||||
});
|
||||
|
||||
browser.menus.onShown.addListener(onMenuShown);
|
||||
browser.menus.onClicked.addListener(onMenuClicked);
|
||||
|
||||
return () => {
|
||||
browser.menus.onShown.removeListener(onMenuShown);
|
||||
browser.menus.onClicked.removeListener(onMenuClicked);
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<li class="receiver">
|
||||
<li class="receiver" bind:this={receiverElement} on:contextmenu={onContextMenu}>
|
||||
<img
|
||||
class="receiver__icon"
|
||||
src="icons/{device.capabilities & ReceiverDeviceCapabilities.VIDEO_OUT
|
||||
@@ -151,46 +429,19 @@
|
||||
<ReceiverMedia
|
||||
status={mediaStatus}
|
||||
{device}
|
||||
on:togglePlayback={() => {
|
||||
switch (mediaStatus?.playerState) {
|
||||
case PlayerState.PLAYING:
|
||||
sendMediaMessage({ type: "PAUSE" });
|
||||
break;
|
||||
case PlayerState.PAUSED:
|
||||
sendMediaMessage({ type: "PLAY" });
|
||||
break;
|
||||
}
|
||||
}}
|
||||
on:previous={() => {
|
||||
sendMediaMessage({
|
||||
type: "QUEUE_UPDATE",
|
||||
jump: -1
|
||||
});
|
||||
}}
|
||||
on:next={() => {
|
||||
sendMediaMessage({
|
||||
type: "QUEUE_UPDATE",
|
||||
jump: 1
|
||||
});
|
||||
}}
|
||||
{textTracks}
|
||||
on:togglePlayback={() => handleMediaPlayPause()}
|
||||
on:previous={() => handleMediaSkipPrevious()}
|
||||
on:next={() => handleMediaSkipNext()}
|
||||
on:seek={ev => {
|
||||
sendMediaMessage({
|
||||
type: "SEEK",
|
||||
currentTime: ev.detail.position
|
||||
});
|
||||
}}
|
||||
on:trackChanged={ev => {
|
||||
sendMediaMessage({
|
||||
type: "EDIT_TRACKS_INFO",
|
||||
activeTrackIds: ev.detail.activeTrackIds
|
||||
});
|
||||
}}
|
||||
on:volumeChanged={ev => {
|
||||
sendReceiverMessage({
|
||||
type: "SET_VOLUME",
|
||||
volume: ev.detail
|
||||
});
|
||||
}}
|
||||
on:trackChanged={ev =>
|
||||
handleMediaTrackChange(ev.detail.activeTrackIds)}
|
||||
on:volumeChanged={ev => handleVolumeChange(ev.detail)}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -8,9 +8,9 @@
|
||||
import {
|
||||
MetadataType,
|
||||
PlayerState,
|
||||
StreamType,
|
||||
TrackType
|
||||
StreamType
|
||||
} from "../../cast/sdk/media/enums";
|
||||
import type { Track } from "../../cast/sdk/media/classes";
|
||||
import { getEstimatedTime } from "../../cast/utils";
|
||||
|
||||
const _ = browser.i18n.getMessage;
|
||||
@@ -26,6 +26,7 @@
|
||||
|
||||
export let status: MediaStatus;
|
||||
export let device: ReceiverDevice;
|
||||
export let textTracks: Track[] = [];
|
||||
|
||||
$: isPlayingOrPaused =
|
||||
status.playerState === PlayerState.PLAYING ||
|
||||
@@ -72,32 +73,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
const languageNames = new Intl.DisplayNames(
|
||||
[browser.i18n.getUILanguage()],
|
||||
{ type: "language" }
|
||||
);
|
||||
|
||||
// Subtitle/caption tracks
|
||||
$: textTracks = status?.media?.tracks
|
||||
?.filter(track => track.type === TrackType.TEXT)
|
||||
.map(track => {
|
||||
/**
|
||||
* If track has no name, but does have a language, get a
|
||||
* display name for the language.
|
||||
*/
|
||||
if (!track.name && track.language) {
|
||||
try {
|
||||
const displayName = languageNames.of(track.language);
|
||||
if (displayName) {
|
||||
track.name = displayName;
|
||||
}
|
||||
// eslint-disable-next-line no-empty
|
||||
} catch (err) {}
|
||||
}
|
||||
|
||||
return track;
|
||||
});
|
||||
|
||||
// Keep track of update times for currentTime estimations
|
||||
let lastUpdateTime = 0;
|
||||
let currentTime = getEstimatedMediaTime();
|
||||
@@ -258,7 +233,7 @@
|
||||
class="media__cc-button ghost"
|
||||
class:media__cc-button--off={activeTextTrackId ===
|
||||
undefined}
|
||||
title={_("popupMediaSubtitlesClosedCaptions")}
|
||||
title={_("popupMediaSubtitlesCaptions")}
|
||||
value={activeTextTrackId}
|
||||
on:change={ev => {
|
||||
if (!status.activeTrackIds) return;
|
||||
@@ -276,7 +251,7 @@
|
||||
}}
|
||||
>
|
||||
<option value={undefined}>
|
||||
{_("popupMediaSubtitlesClosedCaptionsOff")}
|
||||
{_("popupMediaSubtitlesCaptionsOff")}
|
||||
</option>
|
||||
{#each textTracks as track}
|
||||
<option value={track.trackId}>
|
||||
|
||||
Reference in New Issue
Block a user