mirror of
https://github.com/th-ch/youtube-music.git
synced 2026-01-11 10:31:47 +00:00
Merge pull request #674 from th-ch/picture-in-picture-plugin
Add "Picture in picture" plugin
This commit is contained in:
10
index.js
10
index.js
@ -178,7 +178,12 @@ function createMainWindow() {
|
|||||||
win.on("move", () => {
|
win.on("move", () => {
|
||||||
if (win.isMaximized()) return;
|
if (win.isMaximized()) return;
|
||||||
let position = win.getPosition();
|
let position = win.getPosition();
|
||||||
|
const isPiPEnabled =
|
||||||
|
config.plugins.isEnabled("picture-in-picture") &&
|
||||||
|
config.plugins.getOptions("picture-in-picture")["isInPiP"];
|
||||||
|
if (!isPiPEnabled) {
|
||||||
lateSave("window-position", { x: position[0], y: position[1] });
|
lateSave("window-position", { x: position[0], y: position[1] });
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let winWasMaximized;
|
let winWasMaximized;
|
||||||
@ -191,7 +196,10 @@ function createMainWindow() {
|
|||||||
winWasMaximized = isMaximized;
|
winWasMaximized = isMaximized;
|
||||||
config.set("window-maximized", isMaximized);
|
config.set("window-maximized", isMaximized);
|
||||||
}
|
}
|
||||||
if (!isMaximized) {
|
const isPiPEnabled =
|
||||||
|
config.plugins.isEnabled("picture-in-picture") &&
|
||||||
|
config.plugins.getOptions("picture-in-picture")["isInPiP"];
|
||||||
|
if (!isMaximized && !isPiPEnabled) {
|
||||||
lateSave("window-size", {
|
lateSave("window-size", {
|
||||||
width: windowSize[0],
|
width: windowSize[0],
|
||||||
height: windowSize[1],
|
height: windowSize[1],
|
||||||
|
|||||||
79
plugins/picture-in-picture/back.js
Normal file
79
plugins/picture-in-picture/back.js
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
const path = require("path");
|
||||||
|
|
||||||
|
const { app, ipcMain } = require("electron");
|
||||||
|
|
||||||
|
const { setOptions } = require("../../config/plugins");
|
||||||
|
const { injectCSS } = require("../utils");
|
||||||
|
|
||||||
|
let isInPiPMode = false;
|
||||||
|
let originalPosition;
|
||||||
|
let originalSize;
|
||||||
|
|
||||||
|
const pipPosition = [10, 10];
|
||||||
|
const pipSize = [450, 275];
|
||||||
|
|
||||||
|
const togglePiP = async (win) => {
|
||||||
|
isInPiPMode = !isInPiPMode;
|
||||||
|
setOptions("picture-in-picture", { isInPiP: isInPiPMode });
|
||||||
|
|
||||||
|
if (isInPiPMode) {
|
||||||
|
originalPosition = win.getPosition();
|
||||||
|
originalSize = win.getSize();
|
||||||
|
|
||||||
|
win.webContents.on("before-input-event", blockShortcutsInPiP);
|
||||||
|
|
||||||
|
win.setFullScreenable(false);
|
||||||
|
await win.webContents.executeJavaScript(
|
||||||
|
// Go fullscreen
|
||||||
|
`
|
||||||
|
if (!document.querySelector("ytmusic-player-page").playerPageOpen_) {
|
||||||
|
document.querySelector(".toggle-player-page-button").click();
|
||||||
|
}
|
||||||
|
document.querySelector(".fullscreen-button").click();
|
||||||
|
document.querySelector("ytmusic-player-bar").classList.add("pip");
|
||||||
|
`
|
||||||
|
);
|
||||||
|
win.setFullScreenable(true);
|
||||||
|
|
||||||
|
app.dock?.hide();
|
||||||
|
win.setVisibleOnAllWorkspaces(true, {
|
||||||
|
visibleOnFullScreen: true,
|
||||||
|
});
|
||||||
|
app.dock?.show();
|
||||||
|
win.setAlwaysOnTop(true, "screen-saver", 1);
|
||||||
|
} else {
|
||||||
|
win.webContents.removeListener("before-input-event", blockShortcutsInPiP);
|
||||||
|
|
||||||
|
await win.webContents.executeJavaScript(
|
||||||
|
// Exit fullscreen
|
||||||
|
`
|
||||||
|
document.querySelector(".exit-fullscreen-button").click();
|
||||||
|
document.querySelector("ytmusic-player-bar").classList.remove("pip");
|
||||||
|
`
|
||||||
|
);
|
||||||
|
|
||||||
|
win.setVisibleOnAllWorkspaces(false);
|
||||||
|
win.setAlwaysOnTop(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
const [x, y] = isInPiPMode ? pipPosition : originalPosition;
|
||||||
|
const [w, h] = isInPiPMode ? pipSize : originalSize;
|
||||||
|
win.setPosition(x, y);
|
||||||
|
win.setSize(w, h);
|
||||||
|
|
||||||
|
win.setWindowButtonVisibility?.(!isInPiPMode);
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = (win) => {
|
||||||
|
injectCSS(win.webContents, path.join(__dirname, "style.css"));
|
||||||
|
ipcMain.on("picture-in-picture", async () => {
|
||||||
|
await togglePiP(win);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const blockShortcutsInPiP = (event, input) => {
|
||||||
|
const blockedShortcuts = ["f", "escape"];
|
||||||
|
if (blockedShortcuts.includes(input.key.toLowerCase())) {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
};
|
||||||
42
plugins/picture-in-picture/front.js
Normal file
42
plugins/picture-in-picture/front.js
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
const { ipcRenderer } = require("electron");
|
||||||
|
|
||||||
|
const { getSongMenu } = require("../../providers/dom-elements");
|
||||||
|
const { ElementFromFile, templatePath } = require("../utils");
|
||||||
|
|
||||||
|
let menu = null;
|
||||||
|
const pipButton = ElementFromFile(
|
||||||
|
templatePath(__dirname, "picture-in-picture.html")
|
||||||
|
);
|
||||||
|
|
||||||
|
const observer = new MutationObserver(() => {
|
||||||
|
if (!menu) {
|
||||||
|
menu = getSongMenu();
|
||||||
|
if (!menu) return;
|
||||||
|
}
|
||||||
|
if (menu.contains(pipButton)) return;
|
||||||
|
const menuUrl = document.querySelector(
|
||||||
|
'tp-yt-paper-listbox [tabindex="0"] #navigation-endpoint'
|
||||||
|
)?.href;
|
||||||
|
if (menuUrl && !menuUrl.includes("watch?")) return;
|
||||||
|
|
||||||
|
menu.prepend(pipButton);
|
||||||
|
});
|
||||||
|
|
||||||
|
global.togglePictureInPicture = () => {
|
||||||
|
ipcRenderer.send("picture-in-picture");
|
||||||
|
};
|
||||||
|
|
||||||
|
function observeMenu(options) {
|
||||||
|
document.addEventListener(
|
||||||
|
"apiLoaded",
|
||||||
|
() => {
|
||||||
|
observer.observe(document.querySelector("ytmusic-popup-container"), {
|
||||||
|
childList: true,
|
||||||
|
subtree: true,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
{ once: true, passive: true }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = observeMenu;
|
||||||
11
plugins/picture-in-picture/style.css
Normal file
11
plugins/picture-in-picture/style.css
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
ytmusic-player-bar.pip svg,
|
||||||
|
ytmusic-player-bar.pip yt-formatted-string {
|
||||||
|
filter: drop-shadow(2px 4px 6px black);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
ytmusic-player-bar.pip ytmusic-player-expanding-menu {
|
||||||
|
border-radius: 30px;
|
||||||
|
background-color: rgba(0, 0, 0, 0.3);
|
||||||
|
backdrop-filter: blur(5px) brightness(20%);
|
||||||
|
}
|
||||||
51
plugins/picture-in-picture/templates/picture-in-picture.html
Normal file
51
plugins/picture-in-picture/templates/picture-in-picture.html
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
<div
|
||||||
|
class="style-scope menu-item ytmusic-menu-popup-renderer"
|
||||||
|
role="option"
|
||||||
|
tabindex="-1"
|
||||||
|
aria-disabled="false"
|
||||||
|
aria-selected="false"
|
||||||
|
onclick="togglePictureInPicture()"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
id="navigation-endpoint"
|
||||||
|
class="yt-simple-endpoint style-scope ytmusic-menu-navigation-item-renderer"
|
||||||
|
tabindex="-1"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="icon menu-icon style-scope ytmusic-menu-navigation-item-renderer"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
version="1.1"
|
||||||
|
id="Layer_1"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
x="0px"
|
||||||
|
y="0px"
|
||||||
|
viewBox="0 0 512 512"
|
||||||
|
style="enable-background: new 0 0 512 512"
|
||||||
|
xml:space="preserve"
|
||||||
|
>
|
||||||
|
<style type="text/css">
|
||||||
|
.st0 {
|
||||||
|
fill: #aaaaaa;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<g id="XMLID_6_">
|
||||||
|
<path
|
||||||
|
id="XMLID_11_"
|
||||||
|
class="st0"
|
||||||
|
d="M418.5,139.4H232.4v139.8h186.1V139.4z M464.8,46.7H46.3C20.5,46.7,0,68.1,0,93.1v325.9
|
||||||
|
c0,25.8,21.4,46.3,46.3,46.3h419.4c25.8,0,46.3-20.5,46.3-46.3V93.1C512,67.2,490.6,46.7,464.8,46.7z M464.8,418.9H46.3V92.2h419.4
|
||||||
|
v326.8H464.8z"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="text style-scope ytmusic-menu-navigation-item-renderer"
|
||||||
|
id="ytmcustom-pip"
|
||||||
|
>
|
||||||
|
Picture in picture
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@ -63,6 +63,8 @@ Install the `youtube-music-bin` package from the AUR. For AUR installation instr
|
|||||||
|
|
||||||
- **Notifications**: Display a notification when a song starts playing ([interactive notifications](https://user-images.githubusercontent.com/78568641/114102651-63ce0e00-98d0-11eb-9dfe-c5a02bb54f9c.png) are available on windows)
|
- **Notifications**: Display a notification when a song starts playing ([interactive notifications](https://user-images.githubusercontent.com/78568641/114102651-63ce0e00-98d0-11eb-9dfe-c5a02bb54f9c.png) are available on windows)
|
||||||
|
|
||||||
|
- **Picture in picture**: allows to switch the app to picture-in-picture mode
|
||||||
|
|
||||||
- **Playback Speed**: Listen fast, listen slow! [Adds a slider that controls song speed](https://user-images.githubusercontent.com/61631665/129976003-e55db5ba-bf42-448c-a059-26a009775e68.png)
|
- **Playback Speed**: Listen fast, listen slow! [Adds a slider that controls song speed](https://user-images.githubusercontent.com/61631665/129976003-e55db5ba-bf42-448c-a059-26a009775e68.png)
|
||||||
|
|
||||||
- **Precise Volume**: Control the volume precisely using mousewheel/hotkeys, with a custom hud and customizable volume steps
|
- **Precise Volume**: Control the volume precisely using mousewheel/hotkeys, with a custom hud and customizable volume steps
|
||||||
|
|||||||
Reference in New Issue
Block a user