mirror of
https://gitlab.com/futo-org/fcast.git
synced 2025-09-03 04:53:06 +00:00
webOS: Add support for remote control bar
This commit is contained in:
parent
144c3e17f5
commit
8dde1ec5b3
20 changed files with 855 additions and 232 deletions
|
@ -22,6 +22,8 @@ export class Timer {
|
|||
private delay: number;
|
||||
private startTime: number;
|
||||
private remainingTime: number;
|
||||
private enabled: boolean;
|
||||
|
||||
public started: boolean;
|
||||
|
||||
constructor(callback: () => void, delay: number, autoStart: boolean = true) {
|
||||
|
@ -29,6 +31,7 @@ export class Timer {
|
|||
this.callback = callback;
|
||||
this.delay = delay;
|
||||
this.started = false;
|
||||
this.enabled = true;
|
||||
|
||||
if (autoStart) {
|
||||
this.start();
|
||||
|
@ -36,6 +39,7 @@ export class Timer {
|
|||
}
|
||||
|
||||
public start(delay?: number) {
|
||||
if (this.enabled) {
|
||||
this.delay = delay ? delay : this.delay;
|
||||
|
||||
if (this.handle) {
|
||||
|
@ -47,9 +51,10 @@ export class Timer {
|
|||
this.remainingTime = null;
|
||||
this.handle = window.setTimeout(this.callback, this.delay);
|
||||
}
|
||||
}
|
||||
|
||||
public pause() {
|
||||
if (this.handle) {
|
||||
if (this.enabled && this.handle) {
|
||||
window.clearTimeout(this.handle);
|
||||
this.handle = null;
|
||||
this.remainingTime = this.delay - (Date.now() - this.startTime);
|
||||
|
@ -57,7 +62,7 @@ export class Timer {
|
|||
}
|
||||
|
||||
public resume() {
|
||||
if (this.remainingTime) {
|
||||
if (this.enabled && this.remainingTime) {
|
||||
this.start(this.remainingTime);
|
||||
}
|
||||
}
|
||||
|
@ -70,4 +75,32 @@ export class Timer {
|
|||
this.started = false;
|
||||
}
|
||||
}
|
||||
|
||||
public end() {
|
||||
this.stop();
|
||||
this.callback();
|
||||
}
|
||||
|
||||
public enable() {
|
||||
this.enabled = true;
|
||||
}
|
||||
|
||||
public disable() {
|
||||
this.enabled = false;
|
||||
this.stop();
|
||||
}
|
||||
|
||||
public setDelay(delay: number) {
|
||||
this.stop();
|
||||
this.delay = delay;
|
||||
}
|
||||
|
||||
public setCallback(callback: () => void) {
|
||||
this.stop();
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
public isPaused(): boolean {
|
||||
return this.remainingTime !== null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -86,13 +86,14 @@ if (TARGET === 'electron') {
|
|||
} else if (TARGET === 'webOS' || TARGET === 'tizenOS') {
|
||||
preloadData.onDeviceInfoCb = () => { logger.warn('Main: Callback not set while fetching device info'); };
|
||||
preloadData.getSessionsCb = () => { logger.error('Main: Callback not set while calling getSessions'); };
|
||||
preloadData.initializeSubscribedKeysCb = () => { logger.error('Main: Callback not set while calling initializeSubscribedKeys'); };
|
||||
preloadData.onConnectCb = (_, value: any) => { logger.error('Main: Callback not set while calling onConnect'); };
|
||||
preloadData.onDisconnectCb = (_, value: any) => { logger.error('Main: Callback not set while calling onDisconnect'); };
|
||||
preloadData.sendEventCb = (message: EventMessage) => { logger.error('Main: Callback not set while calling onSendEventCb'); };
|
||||
|
||||
preloadData.onEventSubscribedKeysUpdate = (value: { keyDown: Set<string>, keyUp: Set<string> }) => {
|
||||
preloadData.subscribedKeys.keyDown = value.keyDown;
|
||||
preloadData.subscribedKeys.keyUp = value.keyUp;
|
||||
preloadData.onEventSubscribedKeysUpdate = (value: { keyDown: string[], keyUp: string[] }) => {
|
||||
preloadData.subscribedKeys.keyDown = new Set(value.keyDown);
|
||||
preloadData.subscribedKeys.keyUp = new Set(value.keyUp);
|
||||
};
|
||||
|
||||
preloadData.onToast = (message: string, icon: ToastIcon = ToastIcon.INFO, duration: number = 5000) => {
|
||||
|
@ -110,6 +111,17 @@ if (TARGET === 'electron') {
|
|||
return preloadData.getSessionsCb();
|
||||
}
|
||||
},
|
||||
initializeSubscribedKeys: (callback?: () => Promise<{ keyDown: string[], keyUp: string[] }>) => {
|
||||
if (callback) {
|
||||
preloadData.initializeSubscribedKeysCb = callback;
|
||||
}
|
||||
else {
|
||||
preloadData.initializeSubscribedKeysCb().then((value: { keyDown: Set<string>, keyUp: Set<string> }) => {
|
||||
preloadData.subscribedKeys.keyDown = new Set(value.keyDown);
|
||||
preloadData.subscribedKeys.keyUp = new Set(value.keyUp);
|
||||
});
|
||||
}
|
||||
},
|
||||
getSubscribedKeys: () => preloadData.subscribedKeys,
|
||||
onConnect: (callback: (_, value: any) => void) => preloadData.onConnectCb = callback,
|
||||
onDisconnect: (callback: (_, value: any) => void) => preloadData.onDisconnectCb = callback,
|
||||
|
|
|
@ -212,7 +212,7 @@ export function keyDownEventHandler(event: KeyboardEvent) {
|
|||
let key = (TARGET === 'webOS' && result.key !== '') ? result.key : event.key;
|
||||
|
||||
if (!handledCase) {
|
||||
switch (event.key) {
|
||||
switch (event.key.toLowerCase()) {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -232,7 +232,7 @@ export function keyUpEventHandler(event: KeyboardEvent) {
|
|||
let key = (TARGET === 'webOS' && result.key !== '') ? result.key : event.key;
|
||||
|
||||
if (!handledCase) {
|
||||
switch (event.key) {
|
||||
switch (event.key.toLowerCase()) {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -14,6 +14,8 @@ export class Player {
|
|||
private player: HTMLVideoElement;
|
||||
private playMessage: PlayMessage;
|
||||
private source: string;
|
||||
private playCb: any;
|
||||
private pauseCb: any;
|
||||
|
||||
// Todo: use a common event handler interface instead of exposing internal players
|
||||
public playerType: PlayerType;
|
||||
|
@ -23,6 +25,8 @@ export class Player {
|
|||
constructor(player: HTMLVideoElement, message: PlayMessage) {
|
||||
this.player = player;
|
||||
this.playMessage = message;
|
||||
this.playCb = null;
|
||||
this.pauseCb = null;
|
||||
|
||||
if (message.container === 'application/dash+xml') {
|
||||
this.playerType = PlayerType.Dash;
|
||||
|
@ -110,6 +114,8 @@ export class Player {
|
|||
this.hlsPlayer = null;
|
||||
this.playMessage = null;
|
||||
this.source = null;
|
||||
this.playCb = null;
|
||||
this.pauseCb = null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -143,6 +149,10 @@ export class Player {
|
|||
} else { // HLS, HTML
|
||||
this.player.play();
|
||||
}
|
||||
|
||||
if (this.playCb) {
|
||||
this.playCb();
|
||||
}
|
||||
}
|
||||
|
||||
public isPaused(): boolean {
|
||||
|
@ -161,6 +171,15 @@ export class Player {
|
|||
} else { // HLS, HTML
|
||||
this.player.pause();
|
||||
}
|
||||
|
||||
if (this.pauseCb) {
|
||||
this.pauseCb();
|
||||
}
|
||||
}
|
||||
|
||||
public setPlayPauseCallback(playCallback: (() => void), pauseCallback: (() => void)) {
|
||||
this.playCb = playCallback;
|
||||
this.pauseCb = pauseCallback;
|
||||
}
|
||||
|
||||
public stop() {
|
||||
|
|
|
@ -19,7 +19,6 @@ declare global {
|
|||
interface Window {
|
||||
electronAPI: any;
|
||||
tizenOSAPI: any;
|
||||
webOSAPI: any;
|
||||
webOS: any;
|
||||
targetAPI: any;
|
||||
}
|
||||
|
@ -90,13 +89,14 @@ if (TARGET === 'electron') {
|
|||
|
||||
preloadData.sendPlayRequestCb = () => { logger.error('Player: Callback "sendPlayRequest" not set'); };
|
||||
preloadData.getSessionsCb = () => { logger.error('Player: Callback "getSessions" not set'); };
|
||||
preloadData.onConnectCb = () => { logger.error('Player: Callback "onConnect" not set'); };
|
||||
preloadData.onDisconnectCb = () => { logger.error('Player: Callback "onDisconnect" not set'); };
|
||||
preloadData.initializeSubscribedKeysCb = () => { logger.error('Player: Callback "initializeSubscribedKeys" not set'); };
|
||||
preloadData.onConnectCb = () => { logger.warn('Player: Callback "onConnect" not set'); };
|
||||
preloadData.onDisconnectCb = () => { logger.warn('Player: Callback "onDisconnect" not set'); };
|
||||
preloadData.onPlayPlaylistCb = () => { logger.error('Player: Callback "onPlayPlaylist" not set'); };
|
||||
|
||||
preloadData.onEventSubscribedKeysUpdate = (value: { keyDown: Set<string>, keyUp: Set<string> }) => {
|
||||
preloadData.subscribedKeys.keyDown = value.keyDown;
|
||||
preloadData.subscribedKeys.keyUp = value.keyUp;
|
||||
preloadData.onEventSubscribedKeysUpdate = (value: { keyDown: string[], keyUp: string[] }) => {
|
||||
preloadData.subscribedKeys.keyDown = new Set(value.keyDown);
|
||||
preloadData.subscribedKeys.keyUp = new Set(value.keyUp);
|
||||
};
|
||||
|
||||
preloadData.onToast = (message: string, icon: ToastIcon = ToastIcon.INFO, duration: number = 5000) => {
|
||||
|
@ -125,6 +125,17 @@ if (TARGET === 'electron') {
|
|||
return preloadData.getSessionsCb();
|
||||
}
|
||||
},
|
||||
initializeSubscribedKeys: (callback?: () => Promise<{ keyDown: string[], keyUp: string[] }>) => {
|
||||
if (callback) {
|
||||
preloadData.initializeSubscribedKeysCb = callback;
|
||||
}
|
||||
else {
|
||||
preloadData.initializeSubscribedKeysCb().then((value: { keyDown: Set<string>, keyUp: Set<string> }) => {
|
||||
preloadData.subscribedKeys.keyDown = new Set(value.keyDown);
|
||||
preloadData.subscribedKeys.keyUp = new Set(value.keyUp);
|
||||
});
|
||||
}
|
||||
},
|
||||
getSubscribedKeys: () => preloadData.subscribedKeys,
|
||||
onConnect: (callback: any) => { preloadData.onConnectCb = callback; },
|
||||
onDisconnect: (callback: any) => { preloadData.onDisconnectCb = callback; },
|
||||
|
|
|
@ -40,7 +40,7 @@ const playerCtrlProgressBarBuffer = document.getElementById("progressBarBuffer")
|
|||
const playerCtrlProgressBarProgress = document.getElementById("progressBarProgress");
|
||||
const playerCtrlProgressBarPosition = document.getElementById("progressBarPosition");
|
||||
const playerCtrlProgressBarHandle = document.getElementById("progressBarHandle");
|
||||
const PlayerCtrlProgressBarInteractiveArea = document.getElementById("progressBarInteractiveArea");
|
||||
const playerCtrlProgressBarInteractiveArea = document.getElementById("progressBarInteractiveArea");
|
||||
|
||||
const playerCtrlVolumeBar = document.getElementById("volumeBar");
|
||||
const playerCtrlVolumeBarProgress = document.getElementById("volumeBarProgress");
|
||||
|
@ -80,10 +80,7 @@ let playlistIndex = 0;
|
|||
let isMediaItem = false;
|
||||
let playItemCached = false;
|
||||
|
||||
let uiHideTimer = new Timer(() => {
|
||||
uiVisible = false;
|
||||
playerCtrlStateUpdate(PlayerControlEvent.UiFadeOut);
|
||||
}, 3000);
|
||||
let uiHideTimer = new Timer(() => { playerCtrlStateUpdate(PlayerControlEvent.UiFadeOut); }, 3000);
|
||||
let loadingTimer = new Timer(() => { loadingSpinner.style.display = 'block'; }, 100, false);
|
||||
let showDurationTimer = new Timer(mediaEndHandler, 0, false);
|
||||
let mediaTitleShowTimer = new Timer(() => { mediaTitle.style.display = 'none'; }, 5000);
|
||||
|
@ -567,6 +564,7 @@ function playerCtrlStateUpdate(event: PlayerControlEvent) {
|
|||
}
|
||||
|
||||
case PlayerControlEvent.UiFadeOut: {
|
||||
uiVisible = false;
|
||||
document.body.style.cursor = "none";
|
||||
playerControls.style.opacity = '0';
|
||||
captionsBaseHeight = captionsBaseHeightCollapsed;
|
||||
|
@ -582,6 +580,7 @@ function playerCtrlStateUpdate(event: PlayerControlEvent) {
|
|||
}
|
||||
|
||||
case PlayerControlEvent.UiFadeIn: {
|
||||
uiVisible = true;
|
||||
document.body.style.cursor = "default";
|
||||
playerControls.style.opacity = '1';
|
||||
captionsBaseHeight = captionsBaseHeightExpanded;
|
||||
|
@ -644,7 +643,7 @@ function playerCtrlStateUpdate(event: PlayerControlEvent) {
|
|||
|
||||
function scrubbingMouseUIHandler(e: MouseEvent) {
|
||||
const progressBarOffset = e.offsetX - playerCtrlProgressBar.offsetLeft;
|
||||
const progressBarWidth = PlayerCtrlProgressBarInteractiveArea.offsetWidth - (playerCtrlProgressBar.offsetLeft * 2);
|
||||
const progressBarWidth = playerCtrlProgressBarInteractiveArea.offsetWidth - (playerCtrlProgressBar.offsetLeft * 2);
|
||||
let time = isLive ? Math.round((1 - (progressBarOffset / progressBarWidth)) * player?.getDuration()) : Math.round((progressBarOffset / progressBarWidth) * player?.getDuration());
|
||||
time = Math.min(player?.getDuration(), Math.max(0.0, time));
|
||||
|
||||
|
@ -657,7 +656,7 @@ function scrubbingMouseUIHandler(e: MouseEvent) {
|
|||
playerCtrlProgressBarPosition.textContent = isLive ? `${livePrefix}${formatDuration(time)}` : formatDuration(time);
|
||||
|
||||
let offset = e.offsetX - (playerCtrlProgressBarPosition.offsetWidth / 2);
|
||||
offset = Math.min(PlayerCtrlProgressBarInteractiveArea.offsetWidth - (playerCtrlProgressBarPosition.offsetWidth / 1), Math.max(8, offset));
|
||||
offset = Math.min(playerCtrlProgressBarInteractiveArea.offsetWidth - (playerCtrlProgressBarPosition.offsetWidth / 1), Math.max(8, offset));
|
||||
playerCtrlProgressBarPosition.setAttribute("style", `display: block; left: ${offset}px`);
|
||||
}
|
||||
|
||||
|
@ -674,21 +673,21 @@ playerCtrlPlayPrevious.onclick = () => { setPlaylistItem(playlistIndex - 1); }
|
|||
playerCtrlPlayNext.onclick = () => { setPlaylistItem(playlistIndex + 1); }
|
||||
playerCtrlVolume.onclick = () => { player?.setMute(!player?.isMuted()); };
|
||||
|
||||
PlayerCtrlProgressBarInteractiveArea.onmousedown = (e: MouseEvent) => { scrubbing = true; scrubbingMouseHandler(e) };
|
||||
PlayerCtrlProgressBarInteractiveArea.onmouseup = () => { scrubbing = false; };
|
||||
PlayerCtrlProgressBarInteractiveArea.onmouseenter = (e: MouseEvent) => {
|
||||
playerCtrlProgressBarInteractiveArea.onmousedown = (e: MouseEvent) => { scrubbing = true; scrubbingMouseHandler(e) };
|
||||
playerCtrlProgressBarInteractiveArea.onmouseup = () => { scrubbing = false; };
|
||||
playerCtrlProgressBarInteractiveArea.onmouseenter = (e: MouseEvent) => {
|
||||
if (e.buttons === 0) {
|
||||
volumeChanging = false;
|
||||
}
|
||||
|
||||
scrubbingMouseUIHandler(e);
|
||||
};
|
||||
PlayerCtrlProgressBarInteractiveArea.onmouseleave = () => { playerCtrlProgressBarPosition.setAttribute("style", "display: none"); };
|
||||
PlayerCtrlProgressBarInteractiveArea.onmousemove = (e: MouseEvent) => { scrubbingMouseHandler(e) };
|
||||
playerCtrlProgressBarInteractiveArea.onmouseleave = () => { playerCtrlProgressBarPosition.setAttribute("style", "display: none"); };
|
||||
playerCtrlProgressBarInteractiveArea.onmousemove = (e: MouseEvent) => { scrubbingMouseHandler(e) };
|
||||
|
||||
function scrubbingMouseHandler(e: MouseEvent) {
|
||||
const progressBarOffset = e.offsetX - playerCtrlProgressBar.offsetLeft;
|
||||
const progressBarWidth = PlayerCtrlProgressBarInteractiveArea.offsetWidth - (playerCtrlProgressBar.offsetLeft * 2);
|
||||
const progressBarWidth = playerCtrlProgressBarInteractiveArea.offsetWidth - (playerCtrlProgressBar.offsetLeft * 2);
|
||||
let time = Math.round((progressBarOffset / progressBarWidth) * player?.getDuration());
|
||||
time = Math.min(player?.getDuration(), Math.max(0.0, time));
|
||||
|
||||
|
@ -880,17 +879,11 @@ function stopUiHideTimer() {
|
|||
uiHideTimer.stop();
|
||||
|
||||
if (!uiVisible) {
|
||||
uiVisible = true;
|
||||
playerCtrlStateUpdate(PlayerControlEvent.UiFadeIn);
|
||||
}
|
||||
}
|
||||
|
||||
document.onmouseout = () => {
|
||||
uiHideTimer.stop();
|
||||
uiVisible = false;
|
||||
playerCtrlStateUpdate(PlayerControlEvent.UiFadeOut);
|
||||
}
|
||||
|
||||
document.onmouseout = () => { uiHideTimer.end(); }
|
||||
document.onmousemove = () => {
|
||||
stopUiHideTimer();
|
||||
|
||||
|
@ -910,16 +903,62 @@ document.addEventListener('click', (event: MouseEvent) => {
|
|||
});
|
||||
|
||||
// Add the keydown event listener to the document
|
||||
const skipInterval = 10;
|
||||
const minSkipInterval = 10;
|
||||
const volumeIncrement = 0.1;
|
||||
|
||||
function skipBack() {
|
||||
player?.setCurrentTime(Math.max(player?.getCurrentTime() - skipInterval, 0));
|
||||
let skipBackRepeat = false;
|
||||
let skipBackInterval = minSkipInterval;
|
||||
let skipBackIntervalIncrease = false;
|
||||
let skipBackTimer = new Timer(() => { skipBackIntervalIncrease = true; }, 2000, false);
|
||||
|
||||
let skipForwardRepeat = false;
|
||||
let skipForwardInterval = minSkipInterval;
|
||||
let skipForwardIntervalIncrease = false;
|
||||
let skipForwardTimer = new Timer(() => { skipForwardIntervalIncrease = true; }, 2000, false);
|
||||
|
||||
function skipBack(repeat: boolean = false) {
|
||||
if (!skipBackRepeat && repeat) {
|
||||
skipBackRepeat = true;
|
||||
skipBackTimer.start();
|
||||
}
|
||||
else if (skipBackRepeat && skipBackIntervalIncrease && repeat) {
|
||||
skipBackInterval = skipBackInterval === 10 ? 30 : Math.min(skipBackInterval + 30, 300);
|
||||
skipBackIntervalIncrease = false;
|
||||
skipBackTimer.start();
|
||||
}
|
||||
else if (!repeat) {
|
||||
skipBackTimer.stop();
|
||||
skipBackRepeat = false;
|
||||
skipBackIntervalIncrease = false;
|
||||
skipBackInterval = minSkipInterval;
|
||||
}
|
||||
|
||||
player?.setCurrentTime(Math.max(player?.getCurrentTime() - skipBackInterval, 0));
|
||||
// Force time update since player triggered update only occurs in real-time if skipping within loaded buffer
|
||||
playerCtrlStateUpdate(PlayerControlEvent.TimeUpdate);
|
||||
}
|
||||
|
||||
function skipForward(repeat: boolean = false) {
|
||||
if (!skipForwardRepeat && repeat) {
|
||||
skipForwardRepeat = true;
|
||||
skipForwardTimer.start();
|
||||
}
|
||||
else if (skipForwardRepeat && skipForwardIntervalIncrease && repeat) {
|
||||
skipForwardInterval = skipForwardInterval === 10 ? 30 : Math.min(skipForwardInterval + 30, 300);
|
||||
skipForwardIntervalIncrease = false;
|
||||
skipForwardTimer.start();
|
||||
}
|
||||
else if (!repeat) {
|
||||
skipForwardTimer.stop();
|
||||
skipForwardRepeat = false;
|
||||
skipForwardIntervalIncrease = false;
|
||||
skipForwardInterval = minSkipInterval;
|
||||
}
|
||||
|
||||
function skipForward() {
|
||||
if (!isLivePosition) {
|
||||
player?.setCurrentTime(Math.min(player?.getCurrentTime() + skipInterval, player?.getDuration()));
|
||||
player?.setCurrentTime(Math.min(player?.getCurrentTime() + skipForwardInterval, player?.getDuration()));
|
||||
// Force time update since player triggered update only occurs in real-time if skipping within loaded buffer
|
||||
playerCtrlStateUpdate(PlayerControlEvent.TimeUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -934,12 +973,12 @@ function keyDownEventHandler(event: KeyboardEvent) {
|
|||
if (!handledCase) {
|
||||
switch (event.key.toLowerCase()) {
|
||||
case 'arrowleft':
|
||||
skipBack();
|
||||
skipBack(event.repeat);
|
||||
event.preventDefault();
|
||||
handledCase = true;
|
||||
break;
|
||||
case 'arrowright':
|
||||
skipForward();
|
||||
skipForward(event.repeat);
|
||||
event.preventDefault();
|
||||
handledCase = true;
|
||||
break;
|
||||
|
@ -1004,7 +1043,7 @@ function keyUpEventHandler(event: KeyboardEvent) {
|
|||
let key = (TARGET === 'webOS' && result.key !== '') ? result.key : event.key;
|
||||
|
||||
if (!handledCase) {
|
||||
switch (event.key) {
|
||||
switch (event.key.toLowerCase()) {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -1025,25 +1064,18 @@ export {
|
|||
idleIcon,
|
||||
videoElement,
|
||||
videoCaptions,
|
||||
playerCtrlProgressBar,
|
||||
playerCtrlProgressBarBuffer,
|
||||
playerCtrlProgressBarProgress,
|
||||
playerCtrlProgressBarHandle,
|
||||
playerCtrlVolumeBar,
|
||||
playerCtrlVolumeBarProgress,
|
||||
playerCtrlVolumeBarHandle,
|
||||
playerCtrlLiveBadge,
|
||||
playerCtrlPosition,
|
||||
playerCtrlDuration,
|
||||
playerCtrlCaptions,
|
||||
player,
|
||||
uiHideTimer,
|
||||
isLive,
|
||||
playlistIndex,
|
||||
captionsBaseHeight,
|
||||
captionsLineHeight,
|
||||
onPlay,
|
||||
onPlayPlaylist,
|
||||
setPlaylistItem,
|
||||
playerCtrlStateUpdate,
|
||||
formatDuration,
|
||||
skipBack,
|
||||
skipForward,
|
||||
keyDownEventHandler,
|
||||
|
|
|
@ -5,6 +5,7 @@ import * as connectionMonitor from 'common/ConnectionMonitor';
|
|||
import { toast, ToastIcon } from 'common/components/Toast';
|
||||
import {
|
||||
targetPlayerCtrlStateUpdate,
|
||||
targetPlayerCtrlPostStateUpdate,
|
||||
targetKeyDownEventListener,
|
||||
targetKeyUpEventListener,
|
||||
} from 'src/viewer/Renderer';
|
||||
|
@ -34,10 +35,7 @@ let isMediaItem = false;
|
|||
let playItemCached = false;
|
||||
let imageViewerPlaybackState: PlaybackState = PlaybackState.Idle;
|
||||
|
||||
let uiHideTimer = new Timer(() => {
|
||||
uiVisible = false;
|
||||
playerCtrlStateUpdate(PlayerControlEvent.UiFadeOut);
|
||||
}, 3000);
|
||||
let uiHideTimer = new Timer(() => { playerCtrlStateUpdate(PlayerControlEvent.UiFadeOut); }, 3000);
|
||||
let loadingTimer = new Timer(() => { loadingSpinner.style.display = 'block'; }, 100, false);
|
||||
|
||||
let showDurationTimer = new Timer(() => {
|
||||
|
@ -278,12 +276,14 @@ function playerCtrlStateUpdate(event: PlayerControlEvent) {
|
|||
break;
|
||||
|
||||
case PlayerControlEvent.UiFadeOut: {
|
||||
uiVisible = false;
|
||||
document.body.style.cursor = "none";
|
||||
playerControls.style.opacity = '0';
|
||||
break;
|
||||
}
|
||||
|
||||
case PlayerControlEvent.UiFadeIn: {
|
||||
uiVisible = true;
|
||||
document.body.style.cursor = "default";
|
||||
playerControls.style.opacity = '1';
|
||||
break;
|
||||
|
@ -292,6 +292,8 @@ function playerCtrlStateUpdate(event: PlayerControlEvent) {
|
|||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
targetPlayerCtrlPostStateUpdate(event);
|
||||
}
|
||||
|
||||
// Receiver generated event handlers
|
||||
|
@ -313,17 +315,11 @@ function stopUiHideTimer() {
|
|||
uiHideTimer.stop();
|
||||
|
||||
if (!uiVisible) {
|
||||
uiVisible = true;
|
||||
playerCtrlStateUpdate(PlayerControlEvent.UiFadeIn);
|
||||
}
|
||||
}
|
||||
|
||||
document.onmouseout = () => {
|
||||
uiHideTimer.stop();
|
||||
uiVisible = false;
|
||||
playerCtrlStateUpdate(PlayerControlEvent.UiFadeOut);
|
||||
}
|
||||
|
||||
document.onmouseout = () => { uiHideTimer.end(); }
|
||||
document.onmousemove = () => {
|
||||
stopUiHideTimer();
|
||||
uiHideTimer.start();
|
||||
|
@ -337,37 +333,40 @@ function keyDownEventHandler(event: KeyboardEvent) {
|
|||
// @ts-ignore
|
||||
let key = (TARGET === 'webOS' && result.key !== '') ? result.key : event.key;
|
||||
|
||||
if (!handledCase) {
|
||||
switch (event.code) {
|
||||
case 'ArrowLeft':
|
||||
if (!handledCase && isMediaItem) {
|
||||
switch (event.key.toLowerCase()) {
|
||||
case 'arrowleft':
|
||||
setPlaylistItem(playlistIndex - 1);
|
||||
event.preventDefault();
|
||||
handledCase = true;
|
||||
break;
|
||||
case 'ArrowRight':
|
||||
case 'arrowright':
|
||||
setPlaylistItem(playlistIndex + 1);
|
||||
event.preventDefault();
|
||||
handledCase = true;
|
||||
break;
|
||||
case "Home":
|
||||
case "home":
|
||||
setPlaylistItem(0);
|
||||
event.preventDefault();
|
||||
handledCase = true;
|
||||
break;
|
||||
case "End":
|
||||
case "end":
|
||||
setPlaylistItem(cachedPlaylist.items.length - 1);
|
||||
event.preventDefault();
|
||||
handledCase = true;
|
||||
break;
|
||||
case 'KeyK':
|
||||
case 'Space':
|
||||
case 'Enter':
|
||||
case 'k':
|
||||
case ' ':
|
||||
case 'enter':
|
||||
// Play/pause toggle
|
||||
if (cachedPlayMediaItem.showDuration && cachedPlayMediaItem.showDuration > 0) {
|
||||
if (imageViewerPlaybackState === PlaybackState.Paused || imageViewerPlaybackState === PlaybackState.Idle) {
|
||||
playerCtrlStateUpdate(PlayerControlEvent.Play);
|
||||
} else {
|
||||
playerCtrlStateUpdate(PlayerControlEvent.Pause);
|
||||
}
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
handledCase = true;
|
||||
break;
|
||||
|
@ -390,7 +389,7 @@ function keyUpEventHandler(event: KeyboardEvent) {
|
|||
let key = (TARGET === 'webOS' && result.key !== '') ? result.key : event.key;
|
||||
|
||||
if (!handledCase) {
|
||||
switch (event.key) {
|
||||
switch (event.key.toLowerCase()) {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -410,7 +409,12 @@ export {
|
|||
idleIcon,
|
||||
imageViewer,
|
||||
genericViewer,
|
||||
uiHideTimer,
|
||||
showDurationTimer,
|
||||
isMediaItem,
|
||||
playlistIndex,
|
||||
cachedPlayMediaItem,
|
||||
imageViewerPlaybackState,
|
||||
onPlay,
|
||||
onPlayPlaylist,
|
||||
playerCtrlStateUpdate,
|
||||
|
|
|
@ -44,6 +44,14 @@ export function targetPlayerCtrlStateUpdate(event: PlayerControlEvent): boolean
|
|||
return handledCase;
|
||||
}
|
||||
|
||||
export function targetPlayerCtrlPostStateUpdate(event: PlayerControlEvent) {
|
||||
// Currently unused in electron player
|
||||
switch (event) {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
export function targetKeyDownEventListener(event: KeyboardEvent): { handledCase: boolean, key: string } {
|
||||
let handledCase = false;
|
||||
|
||||
|
|
|
@ -40,6 +40,14 @@ export function targetPlayerCtrlStateUpdate(event: PlayerControlEvent): boolean
|
|||
return handledCase;
|
||||
}
|
||||
|
||||
export function targetPlayerCtrlPostStateUpdate(event: PlayerControlEvent) {
|
||||
// Currently unused in electron player
|
||||
switch (event) {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
export function targetKeyDownEventListener(event: KeyboardEvent): { handledCase: boolean, key: string } {
|
||||
let handledCase = false;
|
||||
|
||||
|
|
|
@ -175,6 +175,22 @@ export class Main {
|
|||
return;
|
||||
}
|
||||
|
||||
case 'get_subscribed_keys': {
|
||||
const tcpListenerSubscribedKeys = Main.tcpListenerService.getAllSubscribedKeys();
|
||||
const webSocketListenerSubscribedKeys = Main.webSocketListenerService.getAllSubscribedKeys();
|
||||
// webOS compatibility: Need to convert set objects to array objects since data needs to be JSON compatible
|
||||
const subscribeData = {
|
||||
keyDown: Array.from(new Set([...tcpListenerSubscribedKeys.keyDown, ...webSocketListenerSubscribedKeys.keyDown])),
|
||||
keyUp: Array.from(new Set([...tcpListenerSubscribedKeys.keyUp, ...webSocketListenerSubscribedKeys.keyUp])),
|
||||
};
|
||||
|
||||
message.respond({
|
||||
returnValue: true,
|
||||
value: subscribeData
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
case 'network_changed': {
|
||||
logger.info('Network interfaces have changed', message);
|
||||
Main.discoveryService.stop();
|
||||
|
@ -240,17 +256,34 @@ export class Main {
|
|||
});
|
||||
l.emitter.on('setplaylistitem', (message: SetPlaylistItemMessage) => Main.emitter.emit('setplaylistitem', message));
|
||||
l.emitter.on('subscribeevent', (message) => {
|
||||
const subscribeData = l.subscribeEvent(message.sessionId, message.body.event);
|
||||
l.subscribeEvent(message.sessionId, message.body.event);
|
||||
|
||||
if (message.body.event.type === EventType.KeyDown.valueOf() || message.body.event.type === EventType.KeyUp.valueOf()) {
|
||||
const tcpListenerSubscribedKeys = Main.tcpListenerService.getAllSubscribedKeys();
|
||||
const webSocketListenerSubscribedKeys = Main.webSocketListenerService.getAllSubscribedKeys();
|
||||
// webOS compatibility: Need to convert set objects to array objects since data needs to be JSON compatible
|
||||
const subscribeData = {
|
||||
keyDown: Array.from(new Set([...tcpListenerSubscribedKeys.keyDown, ...webSocketListenerSubscribedKeys.keyDown])),
|
||||
keyUp: Array.from(new Set([...tcpListenerSubscribedKeys.keyUp, ...webSocketListenerSubscribedKeys.keyUp])),
|
||||
};
|
||||
|
||||
console.log('emitting set info ON SUBSCRIBE ONLY', subscribeData)
|
||||
Main.emitter.emit('event_subscribed_keys_update', subscribeData);
|
||||
}
|
||||
});
|
||||
l.emitter.on('unsubscribeevent', (message) => {
|
||||
const unsubscribeData = l.unsubscribeEvent(message.sessionId, message.body.event);
|
||||
l.unsubscribeEvent(message.sessionId, message.body.event);
|
||||
|
||||
if (message.body.event.type === EventType.KeyDown.valueOf() || message.body.event.type === EventType.KeyUp.valueOf()) {
|
||||
Main.emitter.emit('event_subscribed_keys_update', unsubscribeData);
|
||||
const tcpListenerSubscribedKeys = Main.tcpListenerService.getAllSubscribedKeys();
|
||||
const webSocketListenerSubscribedKeys = Main.webSocketListenerService.getAllSubscribedKeys();
|
||||
// webOS compatibility: Need to convert set objects to array objects since data needs to be JSON compatible
|
||||
const subscribeData = {
|
||||
keyDown: Array.from(new Set([...tcpListenerSubscribedKeys.keyDown, ...webSocketListenerSubscribedKeys.keyDown])),
|
||||
keyUp: Array.from(new Set([...tcpListenerSubscribedKeys.keyUp, ...webSocketListenerSubscribedKeys.keyUp])),
|
||||
};
|
||||
|
||||
Main.emitter.emit('event_subscribed_keys_update', subscribeData);
|
||||
}
|
||||
});
|
||||
l.start();
|
||||
|
|
|
@ -15,6 +15,21 @@ export enum RemoteKeyCode {
|
|||
Back = 461,
|
||||
}
|
||||
|
||||
export enum KeyCode {
|
||||
ArrowUp = 38,
|
||||
ArrowDown = 40,
|
||||
ArrowLeft = 37,
|
||||
ArrowRight = 39,
|
||||
KeyK = 75,
|
||||
Space = 32,
|
||||
Enter = 13,
|
||||
}
|
||||
|
||||
export enum ControlBarMode {
|
||||
KeyboardMouse,
|
||||
Remote
|
||||
}
|
||||
|
||||
export class ServiceManager {
|
||||
private static serviceChannelSuccessCbHandler?: (message: any) => void;
|
||||
private static serviceChannelFailureCbHandler?: (message: any) => void;
|
||||
|
|
|
@ -48,7 +48,8 @@ window.webOSApp = {
|
|||
keyUpEventHandler = callback;
|
||||
document.addEventListener('keyup', keyUpEventHandler);
|
||||
},
|
||||
loadPage: loadPage
|
||||
loadPage: loadPage,
|
||||
pendingPlay: null,
|
||||
};
|
||||
|
||||
document.addEventListener('webOSLaunch', launchHandlerCallback);
|
||||
|
|
|
@ -104,6 +104,12 @@ try {
|
|||
});
|
||||
});
|
||||
|
||||
window.targetAPI.initializeSubscribedKeys(() => {
|
||||
return new Promise((resolve, reject) => {
|
||||
serviceManager.call('get_subscribed_keys', {}, (message: any) => resolve(message.value), (message: any) => reject(message));
|
||||
});
|
||||
});
|
||||
|
||||
preloadData.sendEventCb = (event: EventMessage) => {
|
||||
serviceManager.call('send_event', event, null, (message: any) => { logger.error(`Player: send_event ${JSON.stringify(message)}`); });
|
||||
};
|
||||
|
|
|
@ -10,7 +10,6 @@ require('lib/webOSTVjs-1.2.10/webOSTV-dev.js');
|
|||
declare global {
|
||||
interface Window {
|
||||
targetAPI: any;
|
||||
webOSAPI: any;
|
||||
webOSApp: any;
|
||||
}
|
||||
}
|
||||
|
@ -20,10 +19,8 @@ const logger = window.targetAPI.logger;
|
|||
try {
|
||||
initializeWindowSizeStylesheet();
|
||||
|
||||
window.webOSAPI = {
|
||||
pendingPlay: JSON.parse(sessionStorage.getItem('playInfo'))
|
||||
};
|
||||
const contentViewer = window.webOSAPI.pendingPlay?.contentViewer;
|
||||
window.parent.webOSApp.pendingPlay = JSON.parse(sessionStorage.getItem('playInfo'));
|
||||
const contentViewer = window.parent.webOSApp.pendingPlay?.contentViewer;
|
||||
|
||||
const serviceManager: ServiceManager = window.parent.webOSApp.serviceManager;
|
||||
serviceManager.subscribeToServiceChannel((message: any) => {
|
||||
|
@ -34,12 +31,13 @@ try {
|
|||
|
||||
case 'play': {
|
||||
if (contentViewer !== message.value.contentViewer) {
|
||||
sessionStorage.setItem('playInfo', JSON.stringify(message.value));
|
||||
window.parent.webOSApp.loadPage(`${message.value.contentViewer}/index.html`);
|
||||
}
|
||||
else {
|
||||
if (message.value.rendererEvent === 'play-playlist') {
|
||||
if (preloadData.onPlayCb === undefined) {
|
||||
window.webOSAPI.pendingPlay = message.value;
|
||||
window.parent.webOSApp.pendingPlay = message.value;
|
||||
}
|
||||
else {
|
||||
preloadData.onPlayPlaylistCb(null, message.value.rendererMessage);
|
||||
|
@ -47,7 +45,7 @@ try {
|
|||
}
|
||||
else {
|
||||
if (preloadData.onPlayCb === undefined) {
|
||||
window.webOSAPI.pendingPlay = message.value;
|
||||
window.parent.webOSApp.pendingPlay = message.value;
|
||||
}
|
||||
else {
|
||||
preloadData.onPlayCb(null, message.value.rendererMessage);
|
||||
|
@ -125,6 +123,11 @@ try {
|
|||
serviceManager.call('get_sessions', {}, (message: any) => resolve(message.value), (message: any) => reject(message));
|
||||
});
|
||||
});
|
||||
window.targetAPI.initializeSubscribedKeys(() => {
|
||||
return new Promise((resolve, reject) => {
|
||||
serviceManager.call('get_subscribed_keys', {}, (message: any) => resolve(message.value), (message: any) => reject(message));
|
||||
});
|
||||
});
|
||||
|
||||
const launchHandler = () => {
|
||||
// args don't seem to be passed in via event despite what documentation says...
|
||||
|
|
|
@ -1,81 +1,125 @@
|
|||
import {
|
||||
isLive,
|
||||
onPlay,
|
||||
onPlayPlaylist,
|
||||
setPlaylistItem,
|
||||
playerCtrlStateUpdate,
|
||||
playlistIndex,
|
||||
player,
|
||||
uiHideTimer,
|
||||
PlayerControlEvent,
|
||||
playerCtrlCaptions,
|
||||
playerCtrlDuration,
|
||||
playerCtrlLiveBadge,
|
||||
playerCtrlPosition,
|
||||
playerCtrlProgressBar,
|
||||
playerCtrlProgressBarBuffer,
|
||||
playerCtrlProgressBarHandle,
|
||||
playerCtrlProgressBarProgress,
|
||||
playerCtrlStateUpdate,
|
||||
playerCtrlVolumeBar,
|
||||
playerCtrlVolumeBarHandle,
|
||||
playerCtrlVolumeBarProgress,
|
||||
videoCaptions,
|
||||
formatDuration,
|
||||
skipBack,
|
||||
skipForward,
|
||||
keyDownEventHandler,
|
||||
keyUpEventHandler,
|
||||
playerCtrlProgressBarHandle,
|
||||
} from 'common/player/Renderer';
|
||||
import { RemoteKeyCode } from 'lib/common';
|
||||
import { KeyCode, RemoteKeyCode, ControlBarMode } from 'lib/common';
|
||||
import * as common from 'lib/common';
|
||||
|
||||
const logger = window.targetAPI.logger;
|
||||
const captionsBaseHeightCollapsed = 150;
|
||||
const captionsBaseHeightExpanded = 320;
|
||||
const captionsLineHeight = 68;
|
||||
|
||||
const playPreviousContainer = document.getElementById('playPreviousContainer');
|
||||
const actionContainer = document.getElementById('actionContainer');
|
||||
const playNextContainer = document.getElementById('playNextContainer');
|
||||
|
||||
const playPrevious = document.getElementById('playPrevious');
|
||||
const playNext = document.getElementById('playNext');
|
||||
|
||||
enum ControlFocus {
|
||||
ProgressBar,
|
||||
Action,
|
||||
PlayPrevious,
|
||||
PlayNext,
|
||||
}
|
||||
|
||||
let controlMode = ControlBarMode.KeyboardMouse;
|
||||
let controlFocus = ControlFocus.ProgressBar;
|
||||
|
||||
// Hide
|
||||
// [<<][>][>>]
|
||||
// [|<][>][>|]
|
||||
// Hide
|
||||
let locationMap = {
|
||||
ProgressBar: playerCtrlProgressBarHandle,
|
||||
Action: actionContainer,
|
||||
PlayPrevious: playPreviousContainer,
|
||||
PlayNext: playNextContainer,
|
||||
};
|
||||
|
||||
window.parent.webOSApp.setKeyDownHandler(keyDownEventHandler);
|
||||
window.parent.webOSApp.setKeyUpHandler(keyUpEventHandler);
|
||||
|
||||
uiHideTimer.setDelay(5000);
|
||||
uiHideTimer.setCallback(() => {
|
||||
if (!player?.isPaused()) {
|
||||
controlMode = ControlBarMode.KeyboardMouse;
|
||||
removeFocus(controlFocus);
|
||||
playerCtrlStateUpdate(PlayerControlEvent.UiFadeOut);
|
||||
}
|
||||
});
|
||||
|
||||
// Leave control bar on screen if magic remote cursor leaves window
|
||||
document.onmouseout = () => {
|
||||
if (controlMode === ControlBarMode.KeyboardMouse) {
|
||||
uiHideTimer.end();
|
||||
}
|
||||
}
|
||||
|
||||
function addFocus(location: ControlFocus) {
|
||||
if (location === ControlFocus.ProgressBar) {
|
||||
locationMap[ControlFocus[location]].classList.remove('progressBarHandleHide');
|
||||
}
|
||||
else {
|
||||
locationMap[ControlFocus[location]].classList.add('buttonFocus');
|
||||
}
|
||||
}
|
||||
|
||||
function removeFocus(location: ControlFocus) {
|
||||
if (location === ControlFocus.ProgressBar) {
|
||||
locationMap[ControlFocus[location]].classList.add('progressBarHandleHide');
|
||||
}
|
||||
else {
|
||||
locationMap[ControlFocus[location]].classList.remove('buttonFocus');
|
||||
}
|
||||
}
|
||||
|
||||
function remoteNavigateTo(location: ControlFocus) {
|
||||
// Issues with using standard focus, so manually managing styles
|
||||
removeFocus(controlFocus);
|
||||
controlFocus = location;
|
||||
addFocus(controlFocus);
|
||||
}
|
||||
|
||||
function setControlMode(mode: ControlBarMode, immediateHide: boolean = true) {
|
||||
if (mode === ControlBarMode.KeyboardMouse) {
|
||||
uiHideTimer.enable();
|
||||
|
||||
if (immediateHide) {
|
||||
removeFocus(controlFocus);
|
||||
playerCtrlStateUpdate(PlayerControlEvent.UiFadeOut);
|
||||
}
|
||||
else {
|
||||
uiHideTimer.start();
|
||||
}
|
||||
}
|
||||
else {
|
||||
remoteNavigateTo(ControlFocus.ProgressBar);
|
||||
playerCtrlStateUpdate(PlayerControlEvent.UiFadeIn);
|
||||
uiHideTimer.start();
|
||||
}
|
||||
|
||||
controlMode = mode;
|
||||
}
|
||||
|
||||
export function targetPlayerCtrlStateUpdate(event: PlayerControlEvent): boolean {
|
||||
let handledCase = false;
|
||||
|
||||
switch (event) {
|
||||
case PlayerControlEvent.Load: {
|
||||
playerCtrlProgressBarBuffer.setAttribute("style", "width: 0px");
|
||||
playerCtrlProgressBarProgress.setAttribute("style", "width: 0px");
|
||||
playerCtrlProgressBarHandle.setAttribute("style", `left: ${playerCtrlProgressBar.offsetLeft}px`);
|
||||
|
||||
const volume = Math.round(player.getVolume() * playerCtrlVolumeBar.offsetWidth);
|
||||
playerCtrlVolumeBarProgress.setAttribute("style", `width: ${volume}px`);
|
||||
playerCtrlVolumeBarHandle.setAttribute("style", `left: ${volume + 8}px`);
|
||||
|
||||
if (isLive) {
|
||||
playerCtrlLiveBadge.setAttribute("style", "display: block");
|
||||
playerCtrlPosition.setAttribute("style", "display: none");
|
||||
playerCtrlDuration.setAttribute("style", "display: none");
|
||||
}
|
||||
else {
|
||||
playerCtrlLiveBadge.setAttribute("style", "display: none");
|
||||
playerCtrlPosition.setAttribute("style", "display: block");
|
||||
playerCtrlDuration.setAttribute("style", "display: block");
|
||||
playerCtrlPosition.textContent = formatDuration(player.getCurrentTime());
|
||||
playerCtrlDuration.innerHTML = formatDuration(player.getDuration());
|
||||
}
|
||||
|
||||
if (player.isCaptionsSupported()) {
|
||||
// Disabling receiver captions control on TV players
|
||||
playerCtrlCaptions.setAttribute("style", "display: none");
|
||||
// playerCtrlCaptions.setAttribute("style", "display: block");
|
||||
videoCaptions.setAttribute("style", "display: block");
|
||||
}
|
||||
else {
|
||||
playerCtrlCaptions.setAttribute("style", "display: none");
|
||||
videoCaptions.setAttribute("style", "display: none");
|
||||
player.enableCaptions(false);
|
||||
}
|
||||
playerCtrlStateUpdate(PlayerControlEvent.SetCaptions);
|
||||
|
||||
handledCase = true;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -83,20 +127,164 @@ export function targetPlayerCtrlStateUpdate(event: PlayerControlEvent): boolean
|
|||
return handledCase;
|
||||
}
|
||||
|
||||
export function targetPlayerCtrlPostStateUpdate(event: PlayerControlEvent) {
|
||||
switch (event) {
|
||||
case PlayerControlEvent.Load: {
|
||||
player.setPlayPauseCallback(() => {
|
||||
uiHideTimer.enable();
|
||||
uiHideTimer.start();
|
||||
}, () => {
|
||||
uiHideTimer.disable();
|
||||
});
|
||||
|
||||
if (player.isCaptionsSupported()) {
|
||||
// Disabling receiver captions control on TV players
|
||||
// playerCtrlCaptions.style.display = 'block';
|
||||
playerCtrlCaptions.style.display = 'none';
|
||||
videoCaptions.style.display = 'block';
|
||||
}
|
||||
else {
|
||||
playerCtrlCaptions.style.display = 'none';
|
||||
videoCaptions.style.display = 'none';
|
||||
player.enableCaptions(false);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
export function targetKeyDownEventListener(event: KeyboardEvent): { handledCase: boolean, key: string } {
|
||||
// logger.info("KeyDown", event.keyCode);
|
||||
let handledCase = false;
|
||||
let key = '';
|
||||
|
||||
switch (event.keyCode) {
|
||||
case KeyCode.KeyK:
|
||||
case KeyCode.Space:
|
||||
// Play/pause toggle
|
||||
if (player?.isPaused()) {
|
||||
player?.play();
|
||||
} else {
|
||||
player?.pause();
|
||||
}
|
||||
event.preventDefault();
|
||||
handledCase = true;
|
||||
break;
|
||||
|
||||
case KeyCode.Enter:
|
||||
if (controlMode === ControlBarMode.KeyboardMouse) {
|
||||
setControlMode(ControlBarMode.Remote);
|
||||
}
|
||||
else {
|
||||
if (controlFocus === ControlFocus.ProgressBar || controlFocus === ControlFocus.Action) {
|
||||
// Play/pause toggle
|
||||
if (player?.isPaused()) {
|
||||
player?.play();
|
||||
} else {
|
||||
player?.pause();
|
||||
}
|
||||
}
|
||||
else if (controlFocus === ControlFocus.PlayPrevious) {
|
||||
setPlaylistItem(playlistIndex - 1);
|
||||
}
|
||||
else if (controlFocus === ControlFocus.PlayNext) {
|
||||
setPlaylistItem(playlistIndex + 1);
|
||||
}
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
handledCase = true;
|
||||
break;
|
||||
case KeyCode.ArrowUp:
|
||||
if (controlMode === ControlBarMode.KeyboardMouse) {
|
||||
setControlMode(ControlBarMode.Remote);
|
||||
}
|
||||
else {
|
||||
if (controlFocus === ControlFocus.ProgressBar) {
|
||||
setControlMode(ControlBarMode.KeyboardMouse);
|
||||
}
|
||||
else {
|
||||
remoteNavigateTo(ControlFocus.ProgressBar);
|
||||
}
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
handledCase = true;
|
||||
break;
|
||||
case KeyCode.ArrowDown:
|
||||
if (controlMode === ControlBarMode.KeyboardMouse) {
|
||||
setControlMode(ControlBarMode.Remote);
|
||||
}
|
||||
else {
|
||||
if (controlFocus === ControlFocus.ProgressBar) {
|
||||
remoteNavigateTo(ControlFocus.Action);
|
||||
}
|
||||
else {
|
||||
setControlMode(ControlBarMode.KeyboardMouse);
|
||||
}
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
handledCase = true;
|
||||
break;
|
||||
case KeyCode.ArrowLeft:
|
||||
if (controlMode === ControlBarMode.KeyboardMouse) {
|
||||
setControlMode(ControlBarMode.Remote);
|
||||
}
|
||||
else {
|
||||
if (controlFocus === ControlFocus.ProgressBar || playPrevious?.style.display === 'none') {
|
||||
// Note that skip repeat does not trigger in simulator
|
||||
skipBack(event.repeat);
|
||||
}
|
||||
else {
|
||||
if (controlFocus === ControlFocus.Action) {
|
||||
remoteNavigateTo(ControlFocus.PlayPrevious);
|
||||
}
|
||||
else if (controlFocus === ControlFocus.PlayNext) {
|
||||
remoteNavigateTo(ControlFocus.Action);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
handledCase = true;
|
||||
break;
|
||||
case KeyCode.ArrowRight:
|
||||
if (controlMode === ControlBarMode.KeyboardMouse) {
|
||||
setControlMode(ControlBarMode.Remote);
|
||||
}
|
||||
else {
|
||||
if (controlFocus === ControlFocus.ProgressBar || playNext?.style.display === 'none') {
|
||||
// Note that skip repeat does not trigger in simulator
|
||||
skipForward(event.repeat);
|
||||
}
|
||||
else {
|
||||
if (controlFocus === ControlFocus.Action) {
|
||||
remoteNavigateTo(ControlFocus.PlayNext);
|
||||
}
|
||||
else if (controlFocus === ControlFocus.PlayPrevious) {
|
||||
remoteNavigateTo(ControlFocus.Action);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
handledCase = true;
|
||||
break;
|
||||
|
||||
case RemoteKeyCode.Stop:
|
||||
// history.back();
|
||||
window.open('../main_window/index.html', '_self');
|
||||
window.parent.webOSApp.loadPage('main_window/index.html');
|
||||
handledCase = true;
|
||||
key = 'Stop';
|
||||
break;
|
||||
|
||||
// Note that in simulator rewind and fast forward key codes are sent twice...
|
||||
case RemoteKeyCode.Rewind:
|
||||
skipBack();
|
||||
skipBack(event.repeat);
|
||||
event.preventDefault();
|
||||
handledCase = true;
|
||||
key = 'Rewind';
|
||||
|
@ -119,18 +307,16 @@ export function targetKeyDownEventListener(event: KeyboardEvent): { handledCase:
|
|||
key = 'Pause';
|
||||
break;
|
||||
|
||||
// Note that in simulator rewind and fast forward key codes are sent twice...
|
||||
case RemoteKeyCode.FastForward:
|
||||
skipForward();
|
||||
skipForward(event.repeat);
|
||||
event.preventDefault();
|
||||
handledCase = true;
|
||||
key = 'FastForward';
|
||||
break;
|
||||
|
||||
// WebOS 22 and earlier does not work well using the history API,
|
||||
// so manually handling page navigation...
|
||||
case RemoteKeyCode.Back:
|
||||
// history.back();
|
||||
window.open('../main_window/index.html', '_self');
|
||||
window.parent.webOSApp.loadPage('main_window/index.html');
|
||||
event.preventDefault();
|
||||
handledCase = true;
|
||||
key = 'Back';
|
||||
|
@ -147,12 +333,12 @@ export function targetKeyUpEventListener(event: KeyboardEvent): { handledCase: b
|
|||
return common.targetKeyUpEventListener(event);
|
||||
};
|
||||
|
||||
if (window.webOSAPI.pendingPlay !== null) {
|
||||
if (window.webOSAPI.pendingPlay.rendererEvent === 'play-playlist') {
|
||||
onPlayPlaylist(null, window.webOSAPI.pendingPlay.rendererMessage);
|
||||
if (window.parent.webOSApp.pendingPlay !== null) {
|
||||
if (window.parent.webOSApp.pendingPlay.rendererEvent === 'play-playlist') {
|
||||
onPlayPlaylist(null, window.parent.webOSApp.pendingPlay.rendererMessage);
|
||||
}
|
||||
else {
|
||||
onPlay(null, window.webOSAPI.pendingPlay.rendererMessage);
|
||||
onPlay(null, window.parent.webOSApp.pendingPlay.rendererMessage);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
<div id="progressBarProgress" class="progressBarProgress" ></div>
|
||||
<div id="progressBarPosition" class="progressBarPosition" ></div>
|
||||
<!-- <div class="progressBarChapterContainer"></div> -->
|
||||
<div id="progressBarHandle" class="progressBarHandle" ></div>
|
||||
<div id="progressBarHandle" class="progressBarHandle progressBarHandleHide" ></div>
|
||||
<div id="progressBarInteractiveArea" class="progressBarInteractiveArea" ></div>
|
||||
</div>
|
||||
|
||||
|
@ -37,9 +37,9 @@
|
|||
</div>
|
||||
|
||||
<div class="leftButtonContainer">
|
||||
<div id="playPrevious" class="playPrevious iconSize"></div>
|
||||
<div id="action" class="play iconSize"></div>
|
||||
<div id="playNext" class="playNext iconSize"></div>
|
||||
<div id="playPreviousContainer" class="buttonFocusContainer"><div id="playPrevious" class="playPrevious iconSize"></div></div>
|
||||
<div id="actionContainer" class="buttonFocusContainer"><div id="action" class="play iconSize"></div></div>
|
||||
<div id="playNextContainer" class="buttonFocusContainer"><div id="playNext" class="playNext iconSize"></div></div>
|
||||
|
||||
<div id="volume" class="volume_high iconSize"></div>
|
||||
<div class="volumeContainer">
|
||||
|
@ -59,7 +59,7 @@
|
|||
<div class="buttonContainer">
|
||||
<!-- <div id="fullscreen" class="fullscreen_on iconSize"></div> -->
|
||||
<div id="speed" class="speed iconSize"></div>
|
||||
<div id="captions" class="captions_off iconSize"></div>
|
||||
<div id="captions" class="captions_off iconSize" style="display: none"></div>
|
||||
<div id="duration" class="duration">00:00</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ html {
|
|||
|
||||
.container {
|
||||
height: 240px;
|
||||
background: linear-gradient(to top, rgba(0, 0, 0, 1.0) 0%, rgba(0, 0, 0, 0.0) 80%);
|
||||
}
|
||||
|
||||
.iconSize {
|
||||
|
@ -19,39 +20,9 @@ html {
|
|||
}
|
||||
|
||||
.volumeContainer {
|
||||
height: 48px;
|
||||
width: 184px;
|
||||
|
||||
display: none;
|
||||
}
|
||||
|
||||
.volumeBar {
|
||||
left: 16px;
|
||||
top: 20px;
|
||||
height: 8px;
|
||||
width: 152px;
|
||||
}
|
||||
|
||||
.volumeBarInteractiveArea {
|
||||
height: 48px;
|
||||
width: 184px;
|
||||
}
|
||||
|
||||
.volumeBarHandle {
|
||||
left: 168px;
|
||||
top: 8px;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
box-shadow: 0px 64px 128px 0px rgba(0, 0, 0, 0.56), 0px 4px 42px 0px rgba(0, 0, 0, 0.55);
|
||||
}
|
||||
|
||||
.volumeBarProgress {
|
||||
left: 16px;
|
||||
top: 20px;
|
||||
height: 8px;
|
||||
width: 152px;
|
||||
}
|
||||
|
||||
.progressBarContainer {
|
||||
bottom: 120px;
|
||||
left: 32px;
|
||||
|
@ -138,21 +109,42 @@ html {
|
|||
}
|
||||
|
||||
.leftButtonContainer {
|
||||
bottom: 48px;
|
||||
bottom: 24px;
|
||||
left: 48px;
|
||||
height: 48px;
|
||||
/* right: 320px; */
|
||||
right: 32px;
|
||||
gap: 48px;
|
||||
height: 96px;
|
||||
right: 48px;
|
||||
/* gap: 48px; */
|
||||
gap: unset;
|
||||
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.buttonFocusContainer {
|
||||
margin-right: 28px;
|
||||
padding: 20px;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
.buttonFocus {
|
||||
/* background-image: linear-gradient(to bottom, #008BD7 35%, #0069AA); */
|
||||
background-image: linear-gradient(to bottom, #808080 35%, #202020);
|
||||
border: 1px solid #4E4E4E;
|
||||
}
|
||||
|
||||
.progressBarHandle {
|
||||
border: 1px solid #4E4E4E;
|
||||
}
|
||||
|
||||
.progressBarHandleHide {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.buttonContainer {
|
||||
bottom: 48px;
|
||||
right: 48px;
|
||||
height: 48px;
|
||||
gap: 48px;
|
||||
/* gap: 48px; */
|
||||
gap: unset;
|
||||
}
|
||||
|
||||
.captionsContainer {
|
||||
|
|
|
@ -1,60 +1,274 @@
|
|||
import { PlayerControlEvent, playerCtrlStateUpdate, onPlay, onPlayPlaylist, setPlaylistItem, playlistIndex, keyDownEventHandler, keyUpEventHandler } from 'common/viewer/Renderer';
|
||||
import { RemoteKeyCode } from 'lib/common';
|
||||
import {
|
||||
PlayerControlEvent,
|
||||
playerCtrlStateUpdate,
|
||||
onPlay,
|
||||
onPlayPlaylist,
|
||||
setPlaylistItem,
|
||||
playlistIndex,
|
||||
uiHideTimer,
|
||||
showDurationTimer,
|
||||
isMediaItem,
|
||||
cachedPlayMediaItem,
|
||||
imageViewerPlaybackState,
|
||||
keyDownEventHandler,
|
||||
keyUpEventHandler
|
||||
} from 'common/viewer/Renderer';
|
||||
import { KeyCode, RemoteKeyCode, ControlBarMode } from 'lib/common';
|
||||
import * as common from 'lib/common';
|
||||
import { PlaybackState } from 'common/Packets';
|
||||
|
||||
const logger = window.targetAPI.logger;
|
||||
|
||||
const playPreviousContainer = document.getElementById('playPreviousContainer');
|
||||
const actionContainer = document.getElementById('actionContainer');
|
||||
const playNextContainer = document.getElementById('playNextContainer');
|
||||
const action = document.getElementById('action');
|
||||
|
||||
enum ControlFocus {
|
||||
Action,
|
||||
PlayPrevious,
|
||||
PlayNext,
|
||||
}
|
||||
|
||||
let controlMode = ControlBarMode.KeyboardMouse;
|
||||
let controlFocus = ControlFocus.Action;
|
||||
|
||||
// Hide
|
||||
// [|<][>][>|]
|
||||
// Hide
|
||||
let locationMap = {
|
||||
Action: actionContainer,
|
||||
PlayPrevious: playPreviousContainer,
|
||||
PlayNext: playNextContainer,
|
||||
};
|
||||
|
||||
|
||||
window.parent.webOSApp.setKeyDownHandler(keyDownEventHandler);
|
||||
window.parent.webOSApp.setKeyUpHandler(keyUpEventHandler);
|
||||
|
||||
uiHideTimer.setDelay(5000);
|
||||
uiHideTimer.setCallback(() => {
|
||||
if (controlMode === ControlBarMode.KeyboardMouse || !showDurationTimer.isPaused()) {
|
||||
controlMode = ControlBarMode.KeyboardMouse;
|
||||
locationMap[ControlFocus[controlFocus]].classList.remove('buttonFocus');
|
||||
playerCtrlStateUpdate(PlayerControlEvent.UiFadeOut);
|
||||
}
|
||||
});
|
||||
|
||||
// Leave control bar on screen if magic remote cursor leaves window
|
||||
document.onmouseout = () => {
|
||||
if (controlMode === ControlBarMode.KeyboardMouse) {
|
||||
uiHideTimer.end();
|
||||
}
|
||||
}
|
||||
|
||||
function remoteNavigateTo(location: ControlFocus) {
|
||||
// Issues with using standard focus, so manually managing styles
|
||||
locationMap[ControlFocus[controlFocus]].classList.remove('buttonFocus');
|
||||
controlFocus = location;
|
||||
locationMap[ControlFocus[controlFocus]].classList.add('buttonFocus');
|
||||
}
|
||||
|
||||
function setControlMode(mode: ControlBarMode, immediateHide: boolean = true) {
|
||||
if (mode === ControlBarMode.KeyboardMouse) {
|
||||
uiHideTimer.enable();
|
||||
|
||||
if (immediateHide) {
|
||||
locationMap[ControlFocus[controlFocus]].classList.remove('buttonFocus');
|
||||
playerCtrlStateUpdate(PlayerControlEvent.UiFadeOut);
|
||||
}
|
||||
else {
|
||||
uiHideTimer.start();
|
||||
}
|
||||
}
|
||||
else {
|
||||
const focus = action?.style.display === 'none' ? ControlFocus.PlayNext : ControlFocus.Action;
|
||||
remoteNavigateTo(focus);
|
||||
playerCtrlStateUpdate(PlayerControlEvent.UiFadeIn);
|
||||
uiHideTimer.start();
|
||||
}
|
||||
|
||||
controlMode = mode;
|
||||
}
|
||||
|
||||
export function targetPlayerCtrlStateUpdate(event: PlayerControlEvent): boolean {
|
||||
let handledCase = false;
|
||||
|
||||
switch (event) {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return handledCase;
|
||||
}
|
||||
|
||||
export function targetPlayerCtrlPostStateUpdate(event: PlayerControlEvent) {
|
||||
switch (event) {
|
||||
case PlayerControlEvent.Load: {
|
||||
if (!isMediaItem && controlMode === ControlBarMode.Remote) {
|
||||
setControlMode(ControlBarMode.KeyboardMouse);
|
||||
}
|
||||
if (action?.style.display === 'none') {
|
||||
actionContainer.style.display = 'none';
|
||||
}
|
||||
else {
|
||||
actionContainer.style.display = 'block';
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
export function targetKeyDownEventListener(event: KeyboardEvent): { handledCase: boolean, key: string } {
|
||||
let handledCase = false;
|
||||
let key = '';
|
||||
|
||||
switch (event.keyCode) {
|
||||
case KeyCode.KeyK:
|
||||
case KeyCode.Space:
|
||||
if (isMediaItem) {
|
||||
// Play/pause toggle
|
||||
if (cachedPlayMediaItem.showDuration && cachedPlayMediaItem.showDuration > 0) {
|
||||
if (imageViewerPlaybackState === PlaybackState.Paused || imageViewerPlaybackState === PlaybackState.Idle) {
|
||||
playerCtrlStateUpdate(PlayerControlEvent.Play);
|
||||
} else {
|
||||
playerCtrlStateUpdate(PlayerControlEvent.Pause);
|
||||
}
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
handledCase = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case KeyCode.Enter:
|
||||
if (isMediaItem) {
|
||||
if (controlMode === ControlBarMode.KeyboardMouse) {
|
||||
setControlMode(ControlBarMode.Remote);
|
||||
}
|
||||
else {
|
||||
if (controlFocus === ControlFocus.Action) {
|
||||
// Play/pause toggle
|
||||
if (cachedPlayMediaItem.showDuration && cachedPlayMediaItem.showDuration > 0) {
|
||||
if (imageViewerPlaybackState === PlaybackState.Paused || imageViewerPlaybackState === PlaybackState.Idle) {
|
||||
playerCtrlStateUpdate(PlayerControlEvent.Play);
|
||||
} else {
|
||||
playerCtrlStateUpdate(PlayerControlEvent.Pause);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (controlFocus === ControlFocus.PlayPrevious) {
|
||||
setPlaylistItem(playlistIndex - 1);
|
||||
}
|
||||
else if (controlFocus === ControlFocus.PlayNext) {
|
||||
setPlaylistItem(playlistIndex + 1);
|
||||
}
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
handledCase = true;
|
||||
}
|
||||
break;
|
||||
case KeyCode.ArrowUp:
|
||||
case KeyCode.ArrowDown:
|
||||
if (isMediaItem) {
|
||||
if (controlMode === ControlBarMode.KeyboardMouse) {
|
||||
setControlMode(ControlBarMode.Remote);
|
||||
}
|
||||
else {
|
||||
setControlMode(ControlBarMode.KeyboardMouse);
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
handledCase = true;
|
||||
}
|
||||
break;
|
||||
case KeyCode.ArrowLeft:
|
||||
if (isMediaItem) {
|
||||
if (controlMode === ControlBarMode.KeyboardMouse) {
|
||||
setPlaylistItem(playlistIndex - 1);
|
||||
}
|
||||
else {
|
||||
if (controlFocus === ControlFocus.Action || action?.style.display === 'none') {
|
||||
remoteNavigateTo(ControlFocus.PlayPrevious);
|
||||
}
|
||||
else if (controlFocus === ControlFocus.PlayNext) {
|
||||
remoteNavigateTo(ControlFocus.Action);
|
||||
}
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
handledCase = true;
|
||||
}
|
||||
break;
|
||||
case KeyCode.ArrowRight:
|
||||
if (isMediaItem) {
|
||||
if (controlMode === ControlBarMode.KeyboardMouse) {
|
||||
setPlaylistItem(playlistIndex + 1);
|
||||
}
|
||||
else {
|
||||
if (controlFocus === ControlFocus.Action || action?.style.display === 'none') {
|
||||
remoteNavigateTo(ControlFocus.PlayNext);
|
||||
}
|
||||
else if (controlFocus === ControlFocus.PlayPrevious) {
|
||||
remoteNavigateTo(ControlFocus.Action);
|
||||
}
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
handledCase = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case RemoteKeyCode.Stop:
|
||||
// history.back();
|
||||
window.open('../main_window/index.html', '_self');
|
||||
window.parent.webOSApp.loadPage('main_window/index.html');
|
||||
event.preventDefault();
|
||||
handledCase = true;
|
||||
key = 'Stop';
|
||||
break;
|
||||
|
||||
// Note that in simulator rewind and fast forward key codes are sent twice...
|
||||
case RemoteKeyCode.Rewind:
|
||||
if (isMediaItem) {
|
||||
setPlaylistItem(playlistIndex - 1);
|
||||
event.preventDefault();
|
||||
handledCase = true;
|
||||
key = 'Rewind';
|
||||
}
|
||||
break;
|
||||
|
||||
case RemoteKeyCode.Play:
|
||||
if (isMediaItem) {
|
||||
playerCtrlStateUpdate(PlayerControlEvent.Play);
|
||||
event.preventDefault();
|
||||
handledCase = true;
|
||||
key = 'Play';
|
||||
}
|
||||
break;
|
||||
case RemoteKeyCode.Pause:
|
||||
if (isMediaItem) {
|
||||
playerCtrlStateUpdate(PlayerControlEvent.Pause);
|
||||
event.preventDefault();
|
||||
handledCase = true;
|
||||
key = 'Pause';
|
||||
}
|
||||
break;
|
||||
|
||||
// Note that in simulator rewind and fast forward key codes are sent twice...
|
||||
case RemoteKeyCode.FastForward:
|
||||
if (isMediaItem) {
|
||||
setPlaylistItem(playlistIndex + 1);
|
||||
event.preventDefault();
|
||||
handledCase = true;
|
||||
key = 'FastForward';
|
||||
}
|
||||
break;
|
||||
|
||||
// WebOS 22 and earlier does not work well using the history API,
|
||||
// so manually handling page navigation...
|
||||
case RemoteKeyCode.Back:
|
||||
// history.back();
|
||||
window.open('../main_window/index.html', '_self');
|
||||
window.parent.webOSApp.loadPage('main_window/index.html');
|
||||
event.preventDefault();
|
||||
handledCase = true;
|
||||
key = 'Back';
|
||||
|
@ -71,11 +285,11 @@ export function targetKeyUpEventListener(event: KeyboardEvent): { handledCase: b
|
|||
return common.targetKeyUpEventListener(event);
|
||||
};
|
||||
|
||||
if (window.webOSAPI.pendingPlay !== null) {
|
||||
if (window.webOSAPI.pendingPlay.rendererEvent === 'play-playlist') {
|
||||
onPlayPlaylist(null, window.webOSAPI.pendingPlay.rendererMessage);
|
||||
if (window.parent.webOSApp.pendingPlay !== null) {
|
||||
if (window.parent.webOSApp.pendingPlay.rendererEvent === 'play-playlist') {
|
||||
onPlayPlaylist(null, window.parent.webOSApp.pendingPlay.rendererMessage);
|
||||
}
|
||||
else {
|
||||
onPlay(null, window.webOSAPI.pendingPlay.rendererMessage);
|
||||
onPlay(null, window.parent.webOSApp.pendingPlay.rendererMessage);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,10 +24,10 @@
|
|||
<div id="mediaTitle"></div>
|
||||
</div>
|
||||
<div id="centerButtonContainer" class="buttonContainer">
|
||||
<div id="playPrevious" class="playPrevious iconSize" style="display: none"></div>
|
||||
<div id="action" class="play iconSize" style="display: none"></div>
|
||||
<div id="playPreviousContainer" class="buttonFocusContainer"><div id="playPrevious" class="playPrevious iconSize" style="display: none"></div></div>
|
||||
<div id="actionContainer" class="buttonFocusContainer"><div id="action" class="play iconSize" style="display: none"></div></div>
|
||||
<div id="playlistLength" style="display: none"></div>
|
||||
<div id="playNext" class="playNext iconSize" style="display: none"></div>
|
||||
<div id="playNextContainer" class="buttonFocusContainer"><div id="playNext" class="playNext iconSize" style="display: none"></div></div>
|
||||
</div>
|
||||
|
||||
<!-- <div id="rightButtonContainer" class="buttonContainer">
|
||||
|
|
|
@ -4,6 +4,52 @@ html {
|
|||
overflow: hidden;
|
||||
}
|
||||
|
||||
.container {
|
||||
height: 140px;
|
||||
background: linear-gradient(to top, rgba(0, 0, 0, 1.0) 0%, rgba(0, 0, 0, 0.0) 100%);
|
||||
}
|
||||
|
||||
.iconSize {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
#leftButtonContainer {
|
||||
left: 48px;
|
||||
|
||||
font-family: InterRegular;
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
#centerButtonContainer {
|
||||
font-family: InterRegular;
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
#playlistLength {
|
||||
margin-right: 28px;
|
||||
}
|
||||
|
||||
.buttonContainer {
|
||||
bottom: 24px;
|
||||
height: 96px;
|
||||
/* gap: 48px; */
|
||||
gap: unset;
|
||||
}
|
||||
|
||||
.buttonFocusContainer {
|
||||
margin-right: 28px;
|
||||
padding: 20px;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
.buttonFocus {
|
||||
/* background-image: linear-gradient(to bottom, #008BD7 35%, #0069AA); */
|
||||
background-image: linear-gradient(to bottom, #808080 35%, #202020);
|
||||
border: 1px solid #4E4E4E;
|
||||
}
|
||||
|
||||
#toast-notification {
|
||||
gap: unset;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue