mirror of
https://github.com/th-ch/youtube-music.git
synced 2026-01-13 19:31:46 +00:00
Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c5bda4f3be | |||
| 3dc92b4939 | |||
| 150146385f | |||
| f50bd32fa3 | |||
| 0dcf820944 | |||
| 6fd16684f7 | |||
| eaa957168f | |||
| 796a7aaaf1 | |||
| 9aaae7b2d9 | |||
| c00609223b | |||
| 5641c3fc87 | |||
| dd1bdae947 | |||
| 70973b2281 | |||
| 5842a6d42f | |||
| 538ab52abd |
@ -14,6 +14,7 @@ const defaultConfig = {
|
|||||||
restartOnConfigChanges: false,
|
restartOnConfigChanges: false,
|
||||||
trayClickPlayPause: false,
|
trayClickPlayPause: false,
|
||||||
autoResetAppCache: false,
|
autoResetAppCache: false,
|
||||||
|
resumeOnStart: true,
|
||||||
},
|
},
|
||||||
plugins: {
|
plugins: {
|
||||||
// Enabled plugins
|
// Enabled plugins
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
const defaultConfig = require("./defaults");
|
||||||
const plugins = require("./plugins");
|
const plugins = require("./plugins");
|
||||||
const store = require("./store");
|
const store = require("./store");
|
||||||
|
|
||||||
@ -10,6 +11,7 @@ const get = (key) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
defaultConfig,
|
||||||
get,
|
get,
|
||||||
set,
|
set,
|
||||||
edit: () => store.openInEditor(),
|
edit: () => store.openInEditor(),
|
||||||
|
|||||||
@ -3,6 +3,11 @@ const Store = require("electron-store");
|
|||||||
const defaults = require("./defaults");
|
const defaults = require("./defaults");
|
||||||
|
|
||||||
const migrations = {
|
const migrations = {
|
||||||
|
">=1.11.0": (store) => {
|
||||||
|
if (store.get("options.resumeOnStart") === undefined) {
|
||||||
|
store.set("options.resumeOnStart", true);
|
||||||
|
}
|
||||||
|
},
|
||||||
">=1.7.0": (store) => {
|
">=1.7.0": (store) => {
|
||||||
const enabledPlugins = store.get("plugins");
|
const enabledPlugins = store.get("plugins");
|
||||||
if (!Array.isArray(enabledPlugins)) {
|
if (!Array.isArray(enabledPlugins)) {
|
||||||
|
|||||||
22
index.js
22
index.js
@ -3,6 +3,7 @@ const path = require("path");
|
|||||||
|
|
||||||
const electron = require("electron");
|
const electron = require("electron");
|
||||||
const is = require("electron-is");
|
const is = require("electron-is");
|
||||||
|
const unhandled = require("electron-unhandled");
|
||||||
const { autoUpdater } = require("electron-updater");
|
const { autoUpdater } = require("electron-updater");
|
||||||
|
|
||||||
const config = require("./config");
|
const config = require("./config");
|
||||||
@ -11,6 +12,12 @@ const { fileExists, injectCSS } = require("./plugins/utils");
|
|||||||
const { isTesting } = require("./utils/testing");
|
const { isTesting } = require("./utils/testing");
|
||||||
const { setUpTray } = require("./tray");
|
const { setUpTray } = require("./tray");
|
||||||
|
|
||||||
|
// Catch errors and log them
|
||||||
|
unhandled({
|
||||||
|
logger: console.error,
|
||||||
|
showDialog: false,
|
||||||
|
});
|
||||||
|
|
||||||
const app = electron.app;
|
const app = electron.app;
|
||||||
app.commandLine.appendSwitch(
|
app.commandLine.appendSwitch(
|
||||||
"js-flags",
|
"js-flags",
|
||||||
@ -104,7 +111,10 @@ function createMainWindow() {
|
|||||||
win.maximize();
|
win.maximize();
|
||||||
}
|
}
|
||||||
|
|
||||||
win.webContents.loadURL(config.get("url"));
|
const urlToLoad = config.get("options.resumeOnStart")
|
||||||
|
? config.get("url")
|
||||||
|
: config.defaultConfig.url;
|
||||||
|
win.webContents.loadURL(urlToLoad);
|
||||||
win.on("closed", onClosed);
|
win.on("closed", onClosed);
|
||||||
|
|
||||||
win.on("move", () => {
|
win.on("move", () => {
|
||||||
@ -202,8 +212,14 @@ app.on("activate", () => {
|
|||||||
|
|
||||||
app.on("ready", () => {
|
app.on("ready", () => {
|
||||||
if (config.get("options.autoResetAppCache")) {
|
if (config.get("options.autoResetAppCache")) {
|
||||||
// Clear cache
|
// Clear cache after 20s
|
||||||
electron.session.defaultSession.clearCache();
|
const clearCacheTimeout = setTimeout(() => {
|
||||||
|
if (is.dev()) {
|
||||||
|
console.log("Clearing app cache.");
|
||||||
|
}
|
||||||
|
electron.session.defaultSession.clearCache();
|
||||||
|
clearTimeout(clearCacheTimeout);
|
||||||
|
}, 20000);
|
||||||
}
|
}
|
||||||
|
|
||||||
mainWindow = createMainWindow();
|
mainWindow = createMainWindow();
|
||||||
|
|||||||
8
menu.js
8
menu.js
@ -66,6 +66,14 @@ const mainMenuTemplate = (win) => [
|
|||||||
config.set("options.autoResetAppCache", item.checked);
|
config.set("options.autoResetAppCache", item.checked);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: "Resume last song when app starts",
|
||||||
|
type: "checkbox",
|
||||||
|
checked: config.get("options.resumeOnStart"),
|
||||||
|
click: (item) => {
|
||||||
|
config.set("options.resumeOnStart", item.checked);
|
||||||
|
},
|
||||||
|
},
|
||||||
...(is.windows() || is.linux()
|
...(is.windows() || is.linux()
|
||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
|
|||||||
25
package.json
25
package.json
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "youtube-music",
|
"name": "youtube-music",
|
||||||
"productName": "YouTube Music",
|
"productName": "YouTube Music",
|
||||||
"version": "1.10.0",
|
"version": "1.11.0",
|
||||||
"description": "YouTube Music Desktop App - including custom plugins",
|
"description": "YouTube Music Desktop App - including custom plugins",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"repository": "th-ch/youtube-music",
|
"repository": "th-ch/youtube-music",
|
||||||
@ -19,7 +19,10 @@
|
|||||||
},
|
},
|
||||||
"win": {
|
"win": {
|
||||||
"icon": "assets/generated/icons/win/icon.ico",
|
"icon": "assets/generated/icons/win/icon.ico",
|
||||||
"target": ["nsis", "portable"]
|
"target": [
|
||||||
|
"nsis",
|
||||||
|
"portable"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"nsis": {
|
"nsis": {
|
||||||
"runAfterFinish": false
|
"runAfterFinish": false
|
||||||
@ -59,31 +62,33 @@
|
|||||||
"npm": "Please use yarn and not npm"
|
"npm": "Please use yarn and not npm"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@cliqz/adblocker-electron": "^1.19.0",
|
"@cliqz/adblocker-electron": "^1.20.0",
|
||||||
"@ffmpeg/core": "^0.8.5",
|
"@ffmpeg/core": "^0.8.5",
|
||||||
"@ffmpeg/ffmpeg": "^0.9.7",
|
"@ffmpeg/ffmpeg": "^0.9.7",
|
||||||
"YoutubeNonStop": "git://github.com/lawfx/YoutubeNonStop.git#v0.8.1",
|
"YoutubeNonStop": "git://github.com/lawfx/YoutubeNonStop.git#v0.8.1",
|
||||||
"discord-rpc": "^3.1.4",
|
"browser-id3-writer": "^4.4.0",
|
||||||
|
"discord-rpc": "^3.2.0",
|
||||||
"downloads-folder": "^3.0.1",
|
"downloads-folder": "^3.0.1",
|
||||||
"electron-debug": "^3.2.0",
|
"electron-debug": "^3.2.0",
|
||||||
"electron-is": "^3.0.0",
|
"electron-is": "^3.0.0",
|
||||||
"electron-localshortcut": "^3.2.1",
|
"electron-localshortcut": "^3.2.1",
|
||||||
"electron-store": "^6.0.1",
|
"electron-store": "^7.0.2",
|
||||||
|
"electron-unhandled": "^3.0.2",
|
||||||
"electron-updater": "^4.3.6",
|
"electron-updater": "^4.3.6",
|
||||||
"filenamify": "^4.2.0",
|
"filenamify": "^4.2.0",
|
||||||
"node-fetch": "^2.6.1",
|
"node-fetch": "^2.6.1",
|
||||||
"ytdl-core": "^4.3.0"
|
"ytdl-core": "^4.4.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"electron": "^11.1.1",
|
"electron": "^11.2.3",
|
||||||
"electron-builder": "^22.8.1",
|
"electron-builder": "^22.9.1",
|
||||||
"electron-devtools-installer": "^3.1.1",
|
"electron-devtools-installer": "^3.1.1",
|
||||||
"electron-icon-maker": "0.0.5",
|
"electron-icon-maker": "0.0.5",
|
||||||
"get-port": "^5.1.1",
|
"get-port": "^5.1.1",
|
||||||
"jest": "^26.4.2",
|
"jest": "^26.6.3",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
"spectron": "^13.0.0",
|
"spectron": "^13.0.0",
|
||||||
"xo": "^0.33.1"
|
"xo": "^0.37.1"
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
"yargs-parser": "18.1.3"
|
"yargs-parser": "18.1.3"
|
||||||
|
|||||||
25
plugins/disable-autoplay/front.js
Normal file
25
plugins/disable-autoplay/front.js
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
let videoElement = null;
|
||||||
|
|
||||||
|
const observer = new MutationObserver((mutations, observer) => {
|
||||||
|
if (!videoElement) {
|
||||||
|
videoElement = document.querySelector("video");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (videoElement) {
|
||||||
|
videoElement.ontimeupdate = () => {
|
||||||
|
if (videoElement.currentTime === 0 && videoElement.duration !== NaN) {
|
||||||
|
// auto-confirm-when-paused plugin can interfere here if not disabled!
|
||||||
|
videoElement.pause();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function observeVideoElement() {
|
||||||
|
observer.observe(document, {
|
||||||
|
childList: true,
|
||||||
|
subtree: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = observeVideoElement;
|
||||||
@ -1,6 +1,8 @@
|
|||||||
|
const { writeFileSync } = require("fs");
|
||||||
const { join } = require("path");
|
const { join } = require("path");
|
||||||
|
|
||||||
const { dialog } = require("electron");
|
const ID3Writer = require("browser-id3-writer");
|
||||||
|
const { dialog, ipcMain } = require("electron");
|
||||||
|
|
||||||
const getSongInfo = require("../../providers/song-info");
|
const getSongInfo = require("../../providers/song-info");
|
||||||
const { injectCSS, listenAction } = require("../utils");
|
const { injectCSS, listenAction } = require("../utils");
|
||||||
@ -23,10 +25,7 @@ function handle(win) {
|
|||||||
injectCSS(win.webContents, join(__dirname, "style.css"));
|
injectCSS(win.webContents, join(__dirname, "style.css"));
|
||||||
const registerCallback = getSongInfo(win);
|
const registerCallback = getSongInfo(win);
|
||||||
registerCallback((info) => {
|
registerCallback((info) => {
|
||||||
metadata = {
|
metadata = info;
|
||||||
...info,
|
|
||||||
image: info.image ? info.image.toDataURL() : undefined,
|
|
||||||
};
|
|
||||||
});
|
});
|
||||||
|
|
||||||
listenAction(CHANNEL, (event, action, error) => {
|
listenAction(CHANNEL, (event, action, error) => {
|
||||||
@ -41,6 +40,33 @@ function handle(win) {
|
|||||||
console.log("Unknown action: " + action);
|
console.log("Unknown action: " + action);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ipcMain.on("add-metadata", (event, filePath, songBuffer) => {
|
||||||
|
let fileBuffer = songBuffer;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const coverBuffer = metadata.image.toPNG();
|
||||||
|
const writer = new ID3Writer(songBuffer);
|
||||||
|
|
||||||
|
// Create the metadata tags
|
||||||
|
writer
|
||||||
|
.setFrame("TIT2", metadata.title)
|
||||||
|
.setFrame("TPE1", [metadata.artist])
|
||||||
|
.setFrame("APIC", {
|
||||||
|
type: 3,
|
||||||
|
data: coverBuffer,
|
||||||
|
description: "",
|
||||||
|
});
|
||||||
|
writer.addTag();
|
||||||
|
fileBuffer = Buffer.from(writer.arrayBuffer);
|
||||||
|
} catch (error) {
|
||||||
|
sendError(win, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
writeFileSync(filePath, fileBuffer);
|
||||||
|
// Notify the youtube-dl file
|
||||||
|
event.reply("add-metadata-done");
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = handle;
|
module.exports = handle;
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
const { contextBridge } = require("electron");
|
const { contextBridge } = require("electron");
|
||||||
|
|
||||||
|
const { getSongMenu } = require("../../providers/dom-elements");
|
||||||
const { ElementFromFile, templatePath, triggerAction } = require("../utils");
|
const { ElementFromFile, templatePath, triggerAction } = require("../utils");
|
||||||
const { ACTIONS, CHANNEL } = require("./actions.js");
|
const { ACTIONS, CHANNEL } = require("./actions.js");
|
||||||
const { downloadVideoToMP3 } = require("./youtube-dl");
|
const { downloadVideoToMP3 } = require("./youtube-dl");
|
||||||
@ -13,7 +14,7 @@ let pluginOptions = {};
|
|||||||
|
|
||||||
const observer = new MutationObserver((mutations, observer) => {
|
const observer = new MutationObserver((mutations, observer) => {
|
||||||
if (!menu) {
|
if (!menu) {
|
||||||
menu = document.querySelector("ytmusic-menu-popup-renderer paper-listbox");
|
menu = getSongMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (menu && !menu.contains(downloadButton)) {
|
if (menu && !menu.contains(downloadButton)) {
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
const { randomBytes } = require("crypto");
|
const { randomBytes } = require("crypto");
|
||||||
const { writeFileSync } = require("fs");
|
|
||||||
const { join } = require("path");
|
const { join } = require("path");
|
||||||
|
|
||||||
const downloadsFolder = require("downloads-folder");
|
const downloadsFolder = require("downloads-folder");
|
||||||
|
const { ipcRenderer } = require("electron");
|
||||||
const is = require("electron-is");
|
const is = require("electron-is");
|
||||||
const filenamify = require("filenamify");
|
const filenamify = require("filenamify");
|
||||||
|
|
||||||
@ -109,12 +109,15 @@ const toMP3 = async (
|
|||||||
const filename = filenamify(name + "." + extension, {
|
const filename = filenamify(name + "." + extension, {
|
||||||
replacement: "_",
|
replacement: "_",
|
||||||
});
|
});
|
||||||
writeFileSync(
|
|
||||||
|
// Add the metadata
|
||||||
|
sendFeedback("Adding metadata…");
|
||||||
|
ipcRenderer.send(
|
||||||
|
"add-metadata",
|
||||||
join(folder, filename),
|
join(folder, filename),
|
||||||
ffmpeg.FS("readFile", safeVideoName + "." + extension)
|
ffmpeg.FS("readFile", safeVideoName + "." + extension)
|
||||||
);
|
);
|
||||||
|
ipcRenderer.once("add-metadata-done", reinit);
|
||||||
reinit();
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
sendError(e);
|
sendError(e);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,7 @@
|
|||||||
const { watchDOMElement } = require("../../providers/dom-elements");
|
const {
|
||||||
|
getSongMenu,
|
||||||
|
watchDOMElement,
|
||||||
|
} = require("../../providers/dom-elements");
|
||||||
const { ElementFromFile, templatePath } = require("../utils");
|
const { ElementFromFile, templatePath } = require("../utils");
|
||||||
|
|
||||||
const slider = ElementFromFile(templatePath(__dirname, "slider.html"));
|
const slider = ElementFromFile(templatePath(__dirname, "slider.html"));
|
||||||
@ -49,8 +52,7 @@ module.exports = () => {
|
|||||||
|
|
||||||
watchDOMElement(
|
watchDOMElement(
|
||||||
"menu",
|
"menu",
|
||||||
(document) =>
|
(document) => getSongMenu(document),
|
||||||
document.querySelector("ytmusic-menu-popup-renderer paper-listbox"),
|
|
||||||
(menuElement) => {
|
(menuElement) => {
|
||||||
if (!menuElement.contains(slider)) {
|
if (!menuElement.contains(slider)) {
|
||||||
menuElement.prepend(slider);
|
menuElement.prepend(slider);
|
||||||
|
|||||||
@ -5,7 +5,7 @@
|
|||||||
aria-disabled="false"
|
aria-disabled="false"
|
||||||
aria-selected="false"
|
aria-selected="false"
|
||||||
>
|
>
|
||||||
<paper-slider
|
<tp-yt-paper-slider
|
||||||
id="playback-speed-slider"
|
id="playback-speed-slider"
|
||||||
class="volume-slider style-scope ytmusic-player-bar on-hover"
|
class="volume-slider style-scope ytmusic-player-bar on-hover"
|
||||||
max="100"
|
max="100"
|
||||||
@ -22,68 +22,53 @@
|
|||||||
aria-disabled="false"
|
aria-disabled="false"
|
||||||
value="50"
|
value="50"
|
||||||
><!--css-build:shady-->
|
><!--css-build:shady-->
|
||||||
<div id="sliderContainer" class="style-scope paper-slider">
|
<div id="sliderContainer" class="style-scope tp-yt-paper-slider">
|
||||||
<div class="bar-container style-scope paper-slider">
|
<div class="bar-container style-scope tp-yt-paper-slider">
|
||||||
<paper-progress
|
<tp-yt-paper-progress
|
||||||
id="sliderBar"
|
id="sliderBar"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
class="style-scope paper-slider"
|
class="style-scope tp-yt-paper-slider"
|
||||||
role="progressbar"
|
role="progressbar"
|
||||||
value="50"
|
value="50"
|
||||||
aria-valuenow="50"
|
aria-valuenow="50"
|
||||||
aria-valuemin="0"
|
aria-valuemin="0"
|
||||||
aria-valuemax="100"
|
aria-valuemax="100"
|
||||||
aria-disabled="false"
|
aria-disabled="false"
|
||||||
style="touch-action: none;"
|
style="touch-action: none"
|
||||||
><!--css-build:shady-->
|
><!--css-build:shady-->
|
||||||
|
|
||||||
<div id="progressContainer" class="style-scope paper-progress">
|
<div id="progressContainer" class="style-scope tp-yt-paper-progress">
|
||||||
<div
|
<div
|
||||||
id="secondaryProgress"
|
id="secondaryProgress"
|
||||||
class="style-scope paper-progress"
|
class="style-scope tp-yt-paper-progress"
|
||||||
hidden="true"
|
hidden="true"
|
||||||
style="transform: scaleX(0);"
|
style="transform: scaleX(0)"
|
||||||
></div>
|
></div>
|
||||||
<div
|
<div
|
||||||
id="primaryProgress"
|
id="primaryProgress"
|
||||||
class="style-scope paper-progress"
|
class="style-scope tp-yt-paper-progress"
|
||||||
style="transform: scaleX(0.5);"
|
style="transform: scaleX(0.5)"
|
||||||
></div>
|
></div>
|
||||||
</div>
|
</div>
|
||||||
</paper-progress>
|
</tp-yt-paper-progress>
|
||||||
</div>
|
</div>
|
||||||
<dom-if class="style-scope paper-slider"
|
<dom-if class="style-scope tp-yt-paper-slider"
|
||||||
><template is="dom-if"></template
|
><template is="dom-if"></template
|
||||||
></dom-if>
|
></dom-if>
|
||||||
<div
|
<div
|
||||||
id="sliderKnob"
|
id="sliderKnob"
|
||||||
class="slider-knob style-scope paper-slider"
|
class="slider-knob style-scope tp-yt-paper-slider"
|
||||||
style="left: 50%; touch-action: none;"
|
style="left: 50%; touch-action: none"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="slider-knob-inner style-scope paper-slider"
|
class="slider-knob-inner style-scope tp-yt-paper-slider"
|
||||||
value="50"
|
value="50"
|
||||||
></div>
|
></div>
|
||||||
<paper-ripple
|
|
||||||
id="ink"
|
|
||||||
center=""
|
|
||||||
class="circle style-scope paper-slider"
|
|
||||||
style="display: none;"
|
|
||||||
><!--css-build:shady-->
|
|
||||||
|
|
||||||
<div
|
|
||||||
id="background"
|
|
||||||
class="style-scope paper-ripple"
|
|
||||||
style="opacity: 0.006008;"
|
|
||||||
></div>
|
|
||||||
<div id="waves" class="style-scope paper-ripple"></div>
|
|
||||||
</paper-ripple>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<dom-if class="style-scope paper-slider"
|
<dom-if class="style-scope tp-yt-paper-slider"
|
||||||
><template is="dom-if"></template
|
><template is="dom-if"></template></dom-if
|
||||||
></dom-if>
|
></tp-yt-paper-slider>
|
||||||
</paper-slider>
|
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="text style-scope ytmusic-toggle-menu-service-item-renderer"
|
class="text style-scope ytmusic-toggle-menu-service-item-renderer"
|
||||||
|
|||||||
@ -17,4 +17,7 @@ const watchDOMElement = (name, selectorFn, cb) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = { watchDOMElement };
|
const getSongMenu = () =>
|
||||||
|
document.querySelector("ytmusic-menu-popup-renderer tp-yt-paper-listbox");
|
||||||
|
|
||||||
|
module.exports = { getSongMenu, watchDOMElement };
|
||||||
|
|||||||
Reference in New Issue
Block a user