Merge branch 'master' into migrate-from-remote-to-ipc

This commit is contained in:
Araxeus
2022-05-01 23:09:13 +03:00
committed by GitHub
31 changed files with 664 additions and 177 deletions

View File

@ -0,0 +1,4 @@
module.exports = () => {
// See https://github.com/zerodytrash/Simple-YouTube-Age-Restriction-Bypass#userscript
require("simple-youtube-age-restriction-bypass/Simple-YouTube-Age-Restriction-Bypass.user.js");
};

View File

@ -8,7 +8,9 @@ const registerCallback = require("../../providers/song-info");
const { injectCSS, listenAction } = require("../utils");
const { cropMaxWidth } = require("./utils");
const { ACTIONS, CHANNEL } = require("./actions.js");
const { isEnabled } = require("../../config/plugins");
const { getImage } = require("../../providers/song-info");
const { fetchFromGenius } = require("../lyrics-genius/back");
const sendError = (win, error) => {
win.setProgressBar(-1); // close progress bar
@ -71,6 +73,15 @@ function handle(win) {
description: ""
});
}
if (isEnabled("lyrics-genius")) {
const lyrics = await fetchFromGenius(songMetadata);
if (lyrics) {
writer.setFrame("USLT", {
description: lyrics,
lyrics: lyrics,
});
}
}
writer.addTag();
fileBuffer = Buffer.from(writer.arrayBuffer);
} catch (error) {

View File

@ -3,7 +3,7 @@ const config = require("../../config");
const { Titlebar, Color } = require("custom-electron-titlebar");
function $(selector) { return document.querySelector(selector); }
module.exports = () => {
module.exports = (options) => {
let visible = !config.get("options.hideMenu");
const bar = new Titlebar({
backgroundColor: Color.fromHex("#050505"),
@ -14,6 +14,10 @@ module.exports = () => {
bar.updateTitle(" ");
document.title = "Youtube Music";
const hideIcon = hide => $('.cet-window-icon').style.display = hide ? 'none' : 'flex';
if (options.hideIcon) hideIcon(true);
ipcRenderer.on("refreshMenu", (_, showMenu) => {
if (showMenu === undefined && !visible) return;
if (showMenu === false) {
@ -25,6 +29,8 @@ module.exports = () => {
}
});
ipcRenderer.on("hideIcon", (_, hide) => hideIcon(hide));
// Increases the right margin of Navbar background when the scrollbar is visible to avoid blocking it (z-index doesn't affect it)
document.addEventListener('apiLoaded', () => {
setNavbarMargin();

View File

@ -0,0 +1,14 @@
const { setOptions } = require("../../config/plugins");
module.exports = (win, options) => [
{
label: "Hide Icon",
type: "checkbox",
checked: options.hideIcon,
click: (item) => {
win.webContents.send("hideIcon", item.checked);
options.hideIcon = item.checked;
setOptions("in-app-menu", options);
},
}
];

View File

@ -57,10 +57,10 @@ yt-page-navigation-progress,
/* The scrollbar 'thumb' ...that marque oval shape in a scrollbar */
::-webkit-scrollbar-thumb:vertical {
background-clip: padding-box;
border: 2px solid rgba(0, 0, 0, 0);
background: #3a3a3a;
background-clip: padding-box;
border-radius: 100px;
-moz-border-radius: 100px;
-webkit-border-radius: 100px;
@ -71,3 +71,7 @@ yt-page-navigation-progress,
-moz-border-radius: 100px;
-webkit-border-radius: 100px;
}
.cet-menubar-menu-container .cet-action-item {
background-color: inherit
}

View File

@ -89,6 +89,7 @@ const postSongDataToAPI = async (songInfo, config, data) => {
track: songInfo.title,
duration: songInfo.songDuration,
artist: songInfo.artist,
...(songInfo.album ? { album: songInfo.album } : undefined), // will be undefined if current song is a video
api_key: config.api_key,
sk: config.session_key,
format: 'json',
@ -157,4 +158,4 @@ const lastfm = async (_win, config) => {
});
}
module.exports = lastfm;
module.exports = lastfm;

View File

@ -2,6 +2,7 @@ const { join } = require("path");
const { ipcMain } = require("electron");
const is = require("electron-is");
const { convert } = require("html-to-text");
const fetch = require("node-fetch");
const { cleanupName } = require("../../providers/song-info");
@ -12,15 +13,14 @@ module.exports = async (win) => {
ipcMain.on("search-genius-lyrics", async (event, extractedSongInfo) => {
const metadata = JSON.parse(extractedSongInfo);
const queryString = `${cleanupName(metadata.artist)} ${cleanupName(
metadata.title
)}`;
event.returnValue = await fetchFromGenius(queryString);
event.returnValue = await fetchFromGenius(metadata);
});
};
const fetchFromGenius = async (queryString) => {
const fetchFromGenius = async (metadata) => {
const queryString = `${cleanupName(metadata.artist)} ${cleanupName(
metadata.title
)}`;
let response = await fetch(
`https://genius.com/api/search/multi?per_page=5&q=${encodeURI(queryString)}`
);
@ -46,5 +46,26 @@ const fetchFromGenius = async (queryString) => {
return null;
}
return await response.text();
const html = await response.text();
const lyrics = convert(html, {
baseElements: {
selectors: ['[class^="Lyrics__Container"]', ".lyrics"],
},
selectors: [
{
selector: "a",
format: "linkFormatter",
},
],
formatters: {
// Remove links by keeping only the content
linkFormatter: (elem, walk, builder) => {
walk(elem.children, builder);
},
},
});
return lyrics;
};
module.exports.fetchFromGenius = fetchFromGenius;

View File

@ -17,11 +17,11 @@ module.exports = () => {
let hasLyrics = true;
const html = ipcRenderer.sendSync(
const lyrics = ipcRenderer.sendSync(
"search-genius-lyrics",
extractedSongInfo
);
if (!html) {
if (!lyrics) {
// Delete previous lyrics if tab is open and couldn't get new lyrics
checkLyricsContainer(() => {
hasLyrics = false;
@ -34,16 +34,6 @@ module.exports = () => {
console.log("Fetched lyrics from Genius");
}
const wrapper = document.createElement("div");
wrapper.innerHTML = html;
const lyrics = [...wrapper.querySelectorAll('[class^="Lyrics__Container"]')].map(d => d.innerHTML).join('<br>')
|| wrapper.querySelector(".lyrics")?.innerHTML;
if (!lyrics) {
return;
}
enableLyricsTab();
setTabsOnclick(enableLyricsTab);
@ -73,9 +63,12 @@ module.exports = () => {
}
function setLyrics(lyricsContainer) {
lyricsContainer.innerHTML =
`<div id="contents" class="style-scope ytmusic-section-list-renderer description ytmusic-description-shelf-renderer genius-lyrics">
${hasLyrics ? lyrics : 'Could not retrieve lyrics from genius'}
lyricsContainer.innerHTML = `<div id="contents" class="style-scope ytmusic-section-list-renderer description ytmusic-description-shelf-renderer genius-lyrics">
${
hasLyrics
? lyrics.replace(/(?:\r\n|\r|\n)/g, "<br/>")
: "Could not retrieve lyrics from genius"
}
</div>
<yt-formatted-string class="footer style-scope ytmusic-description-shelf-renderer" style="align-self: baseline"></yt-formatted-string>`;

View File

@ -3,7 +3,6 @@
font-size: 20px;
line-height: var(--ytmusic-title-1_-_line-height);
font-weight: 500;
color: #fff;
--yt-endpoint-color: #fff;
--yt-endpoint-hover-color: #fff;
--yt-endpoint-visited-color: #fff;

View 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();
}
};

View 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;

View 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%);
}

View 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>

View File

@ -85,7 +85,7 @@ function forcePlaybackRate(e) {
}
module.exports = () => {
document.addEventListener('apiLoaded', e => {
document.addEventListener('apiLoaded', () => {
observePopupContainer();
observeVideo();
setupWheelListener();

View File

@ -1,4 +1,3 @@
const { ipcRenderer } = require("electron");
const is = require("electron-is");
let ignored = {

View File

@ -3,7 +3,7 @@ const hark = require("hark/hark.bundle.js");
module.exports = () => {
let isSilent = false;
document.addEventListener("apiLoaded", (apiEvent) => {
document.addEventListener("apiLoaded", () => {
const video = document.querySelector("video");
const speechEvents = hark(video, {
threshold: -100, // dB (-100 = absolute silence, 0 = loudest)

View File

@ -42,15 +42,22 @@ module.exports.fileExists = (path, callbackIfExists) => {
});
};
const cssToInject = new Map();
module.exports.injectCSS = (webContents, filepath, cb = undefined) => {
webContents.on("did-finish-load", async () => {
await webContents.insertCSS(fs.readFileSync(filepath, "utf8"));
if (cb) {
cb();
}
});
if (!cssToInject.size) setupCssInjection(webContents);
cssToInject.set(filepath, cb);
};
const setupCssInjection = (webContents) => {
webContents.on("did-finish-load", () => {
cssToInject.forEach(async (cb, filepath) => {
await webContents.insertCSS(fs.readFileSync(filepath, "utf8"));
cb?.();
})
});
}
module.exports.getAllPlugins = () => {
const isDirectory = (source) => fs.lstatSync(source).isDirectory();
return fs

View File

@ -4,7 +4,7 @@ const path = require("path");
module.exports = (win, options) => {
if (options.forceHide) {
injectCSS(win.webContents, path.join(__dirname, "force-hide.css"));
} else {
} else if (!options.mode || options.mode === "custom") {
injectCSS(win.webContents, path.join(__dirname, "button-switcher.css"));
}
};

View File

@ -75,3 +75,8 @@
transform: translateX(0);
transition: transform 300ms;
}
/* disable the native toggler */
#av-id {
display: none;
}

View File

@ -14,9 +14,24 @@ const switchButtonDiv = ElementFromFile(
module.exports = (_options) => {
if (_options.forceHide) return;
options = _options;
document.addEventListener('apiLoaded', setup, { once: true, passive: true });
}
switch (_options.mode) {
case "native": {
$("ytmusic-player-page").setAttribute("has-av-switcher");
$("ytmusic-player").setAttribute("has-av-switcher");
return;
}
case "disabled": {
$("ytmusic-player-page").removeAttribute("has-av-switcher");
$("ytmusic-player").removeAttribute("has-av-switcher");
return;
}
default:
case "custom": {
options = _options;
document.addEventListener("apiLoaded", setup, { once: true, passive: true });
}
}
};
function setup(e) {
api = e.detail;
@ -25,8 +40,6 @@ function setup(e) {
$('ytmusic-player-page').prepend(switchButtonDiv);
$('#song-image.ytmusic-player').style.display = "block";
if (options.hideVideo) {
$('.video-switch-button-checkbox').checked = false;
changeDisplay(false);
@ -50,7 +63,10 @@ function setup(e) {
function changeDisplay(showVideo) {
player.style.margin = showVideo ? '' : 'auto 0px';
player.setAttribute('playback-mode', showVideo ? 'OMV_PREFERRED' : 'ATV_PREFERRED');
$('#song-video.ytmusic-player').style.display = showVideo ? 'unset' : 'none';
$('#song-video.ytmusic-player').style.display = showVideo ? 'block' : 'none';
$('#song-image').style.display = showVideo ? 'none' : 'block';
if (showVideo && !video.style.top) {
video.style.top = `${(player.clientHeight - video.clientHeight) / 2}px`;
}

View File

@ -1,6 +1,38 @@
const { setMenuOptions } = require("../../config/plugins");
module.exports = (win, options) => [
{
label: "Mode",
submenu: [
{
label: "Custom toggle",
type: "radio",
checked: options.mode === 'custom',
click: () => {
options.mode = 'custom';
setMenuOptions("video-toggle", options);
}
},
{
label: "Native toggle",
type: "radio",
checked: options.mode === 'native',
click: () => {
options.mode = 'native';
setMenuOptions("video-toggle", options);
}
},
{
label: "Disabled",
type: "radio",
checked: options.mode === 'disabled',
click: () => {
options.mode = 'disabled';
setMenuOptions("video-toggle", options);
}
},
]
},
{
label: "Force Remove Video Tab",
type: "checkbox",