Files
youtube-music/plugins/picture-in-picture/front.js
2023-08-29 17:22:38 +09:00

157 lines
4.2 KiB
JavaScript

const { ipcRenderer } = require('electron');
const { toKeyEvent } = require('keyboardevent-from-electron-accelerator');
const keyEventAreEqual = require('keyboardevents-areequal');
const { getSongMenu } = require('../../providers/dom-elements');
const { ElementFromFile, templatePath } = require('../utils');
function $(selector) {
return document.querySelector(selector);
}
let useNativePiP = false;
let menu = null;
const pipButton = ElementFromFile(
templatePath(__dirname, 'picture-in-picture.html'),
);
// Will also clone
function replaceButton(query, button) {
const svg = button.querySelector('#icon svg').cloneNode(true);
button.replaceWith(button.cloneNode(true));
button.remove();
const newButton = $(query);
newButton.querySelector('#icon').append(svg);
return newButton;
}
function cloneButton(query) {
replaceButton(query, $(query));
return $(query);
}
const observer = new MutationObserver(() => {
if (!menu) {
menu = getSongMenu();
if (!menu) {
return;
}
}
if (menu.contains(pipButton) || !menu.parentElement.eventSink_?.matches('ytmusic-menu-renderer.ytmusic-player-bar')) {
return;
}
const menuUrl = $(
'tp-yt-paper-listbox [tabindex="0"] #navigation-endpoint',
)?.href;
if (menuUrl && !menuUrl.includes('watch?')) {
return;
}
menu.prepend(pipButton);
});
global.togglePictureInPicture = async () => {
if (useNativePiP) {
const isInPiP = document.pictureInPictureElement !== null;
const video = $('video');
const togglePiP = () =>
isInPiP
? document.exitPictureInPicture.call(document)
: video.requestPictureInPicture.call(video);
try {
await togglePiP();
$('#icon').click(); // Close the menu
return true;
} catch {
}
}
ipcRenderer.send('picture-in-picture');
return false;
};
const listenForToggle = () => {
const originalExitButton = $('.exit-fullscreen-button');
const appLayout = $('ytmusic-app-layout');
const expandMenu = $('#expanding-menu');
const middleControls = $('.middle-controls');
const playerPage = $('ytmusic-player-page');
const togglePlayerPageButton = $('.toggle-player-page-button');
const fullScreenButton = $('.fullscreen-button');
const player = $('#player');
const onPlayerDblClick = player.onDoubleClick_;
const titlebar = $('.cet-titlebar');
ipcRenderer.on('pip-toggle', (_, isPip) => {
if (isPip) {
replaceButton('.exit-fullscreen-button', originalExitButton).addEventListener('click', () => togglePictureInPicture());
player.onDoubleClick_ = () => {
};
expandMenu.addEventListener('mouseleave', () => middleControls.click());
if (!playerPage.playerPageOpen_) {
togglePlayerPageButton.click();
}
fullScreenButton.click();
appLayout.classList.add('pip');
if (titlebar) {
titlebar.style.display = 'none';
}
} else {
$('.exit-fullscreen-button').replaceWith(originalExitButton);
player.onDoubleClick_ = onPlayerDblClick;
expandMenu.onmouseleave = undefined;
originalExitButton.click();
appLayout.classList.remove('pip');
if (titlebar) {
titlebar.style.display = 'flex';
}
}
});
};
function observeMenu(options) {
useNativePiP = options.useNativePiP;
document.addEventListener(
'apiLoaded',
() => {
listenForToggle();
cloneButton('.player-minimize-button').addEventListener('click', async () => {
await global.togglePictureInPicture();
setTimeout(() => $('#player').click());
});
// Allows easily closing the menu by programmatically clicking outside of it
$('#expanding-menu').removeAttribute('no-cancel-on-outside-click');
// TODO: think about wether an additional button in songMenu is needed
observer.observe($('ytmusic-popup-container'), {
childList: true,
subtree: true,
});
},
{ once: true, passive: true },
);
}
module.exports = (options) => {
observeMenu(options);
if (options.hotkey) {
const hotkeyEvent = toKeyEvent(options.hotkey);
window.addEventListener('keydown', (event) => {
if (
keyEventAreEqual(event, hotkeyEvent)
&& !$('ytmusic-search-box').opened
) {
togglePictureInPicture();
}
});
}
};