mirror of
https://github.com/th-ch/youtube-music.git
synced 2026-01-11 10:31:47 +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,
|
||||
trayClickPlayPause: false,
|
||||
autoResetAppCache: false,
|
||||
resumeOnStart: true,
|
||||
},
|
||||
plugins: {
|
||||
// Enabled plugins
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
const defaultConfig = require("./defaults");
|
||||
const plugins = require("./plugins");
|
||||
const store = require("./store");
|
||||
|
||||
@ -10,6 +11,7 @@ const get = (key) => {
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
defaultConfig,
|
||||
get,
|
||||
set,
|
||||
edit: () => store.openInEditor(),
|
||||
|
||||
@ -3,6 +3,11 @@ const Store = require("electron-store");
|
||||
const defaults = require("./defaults");
|
||||
|
||||
const migrations = {
|
||||
">=1.11.0": (store) => {
|
||||
if (store.get("options.resumeOnStart") === undefined) {
|
||||
store.set("options.resumeOnStart", true);
|
||||
}
|
||||
},
|
||||
">=1.7.0": (store) => {
|
||||
const enabledPlugins = store.get("plugins");
|
||||
if (!Array.isArray(enabledPlugins)) {
|
||||
|
||||
22
index.js
22
index.js
@ -3,6 +3,7 @@ const path = require("path");
|
||||
|
||||
const electron = require("electron");
|
||||
const is = require("electron-is");
|
||||
const unhandled = require("electron-unhandled");
|
||||
const { autoUpdater } = require("electron-updater");
|
||||
|
||||
const config = require("./config");
|
||||
@ -11,6 +12,12 @@ const { fileExists, injectCSS } = require("./plugins/utils");
|
||||
const { isTesting } = require("./utils/testing");
|
||||
const { setUpTray } = require("./tray");
|
||||
|
||||
// Catch errors and log them
|
||||
unhandled({
|
||||
logger: console.error,
|
||||
showDialog: false,
|
||||
});
|
||||
|
||||
const app = electron.app;
|
||||
app.commandLine.appendSwitch(
|
||||
"js-flags",
|
||||
@ -104,7 +111,10 @@ function createMainWindow() {
|
||||
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("move", () => {
|
||||
@ -202,8 +212,14 @@ app.on("activate", () => {
|
||||
|
||||
app.on("ready", () => {
|
||||
if (config.get("options.autoResetAppCache")) {
|
||||
// Clear cache
|
||||
electron.session.defaultSession.clearCache();
|
||||
// Clear cache after 20s
|
||||
const clearCacheTimeout = setTimeout(() => {
|
||||
if (is.dev()) {
|
||||
console.log("Clearing app cache.");
|
||||
}
|
||||
electron.session.defaultSession.clearCache();
|
||||
clearTimeout(clearCacheTimeout);
|
||||
}, 20000);
|
||||
}
|
||||
|
||||
mainWindow = createMainWindow();
|
||||
|
||||
8
menu.js
8
menu.js
@ -66,6 +66,14 @@ const mainMenuTemplate = (win) => [
|
||||
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()
|
||||
? [
|
||||
{
|
||||
|
||||
25
package.json
25
package.json
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "youtube-music",
|
||||
"productName": "YouTube Music",
|
||||
"version": "1.10.0",
|
||||
"version": "1.11.0",
|
||||
"description": "YouTube Music Desktop App - including custom plugins",
|
||||
"license": "MIT",
|
||||
"repository": "th-ch/youtube-music",
|
||||
@ -19,7 +19,10 @@
|
||||
},
|
||||
"win": {
|
||||
"icon": "assets/generated/icons/win/icon.ico",
|
||||
"target": ["nsis", "portable"]
|
||||
"target": [
|
||||
"nsis",
|
||||
"portable"
|
||||
]
|
||||
},
|
||||
"nsis": {
|
||||
"runAfterFinish": false
|
||||
@ -59,31 +62,33 @@
|
||||
"npm": "Please use yarn and not npm"
|
||||
},
|
||||
"dependencies": {
|
||||
"@cliqz/adblocker-electron": "^1.19.0",
|
||||
"@cliqz/adblocker-electron": "^1.20.0",
|
||||
"@ffmpeg/core": "^0.8.5",
|
||||
"@ffmpeg/ffmpeg": "^0.9.7",
|
||||
"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",
|
||||
"electron-debug": "^3.2.0",
|
||||
"electron-is": "^3.0.0",
|
||||
"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",
|
||||
"filenamify": "^4.2.0",
|
||||
"node-fetch": "^2.6.1",
|
||||
"ytdl-core": "^4.3.0"
|
||||
"ytdl-core": "^4.4.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"electron": "^11.1.1",
|
||||
"electron-builder": "^22.8.1",
|
||||
"electron": "^11.2.3",
|
||||
"electron-builder": "^22.9.1",
|
||||
"electron-devtools-installer": "^3.1.1",
|
||||
"electron-icon-maker": "0.0.5",
|
||||
"get-port": "^5.1.1",
|
||||
"jest": "^26.4.2",
|
||||
"jest": "^26.6.3",
|
||||
"rimraf": "^3.0.2",
|
||||
"spectron": "^13.0.0",
|
||||
"xo": "^0.33.1"
|
||||
"xo": "^0.37.1"
|
||||
},
|
||||
"resolutions": {
|
||||
"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 { dialog } = require("electron");
|
||||
const ID3Writer = require("browser-id3-writer");
|
||||
const { dialog, ipcMain } = require("electron");
|
||||
|
||||
const getSongInfo = require("../../providers/song-info");
|
||||
const { injectCSS, listenAction } = require("../utils");
|
||||
@ -23,10 +25,7 @@ function handle(win) {
|
||||
injectCSS(win.webContents, join(__dirname, "style.css"));
|
||||
const registerCallback = getSongInfo(win);
|
||||
registerCallback((info) => {
|
||||
metadata = {
|
||||
...info,
|
||||
image: info.image ? info.image.toDataURL() : undefined,
|
||||
};
|
||||
metadata = info;
|
||||
});
|
||||
|
||||
listenAction(CHANNEL, (event, action, error) => {
|
||||
@ -41,6 +40,33 @@ function handle(win) {
|
||||
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;
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
const { contextBridge } = require("electron");
|
||||
|
||||
const { getSongMenu } = require("../../providers/dom-elements");
|
||||
const { ElementFromFile, templatePath, triggerAction } = require("../utils");
|
||||
const { ACTIONS, CHANNEL } = require("./actions.js");
|
||||
const { downloadVideoToMP3 } = require("./youtube-dl");
|
||||
@ -13,7 +14,7 @@ let pluginOptions = {};
|
||||
|
||||
const observer = new MutationObserver((mutations, observer) => {
|
||||
if (!menu) {
|
||||
menu = document.querySelector("ytmusic-menu-popup-renderer paper-listbox");
|
||||
menu = getSongMenu();
|
||||
}
|
||||
|
||||
if (menu && !menu.contains(downloadButton)) {
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
const { randomBytes } = require("crypto");
|
||||
const { writeFileSync } = require("fs");
|
||||
const { join } = require("path");
|
||||
|
||||
const downloadsFolder = require("downloads-folder");
|
||||
const { ipcRenderer } = require("electron");
|
||||
const is = require("electron-is");
|
||||
const filenamify = require("filenamify");
|
||||
|
||||
@ -109,12 +109,15 @@ const toMP3 = async (
|
||||
const filename = filenamify(name + "." + extension, {
|
||||
replacement: "_",
|
||||
});
|
||||
writeFileSync(
|
||||
|
||||
// Add the metadata
|
||||
sendFeedback("Adding metadata…");
|
||||
ipcRenderer.send(
|
||||
"add-metadata",
|
||||
join(folder, filename),
|
||||
ffmpeg.FS("readFile", safeVideoName + "." + extension)
|
||||
);
|
||||
|
||||
reinit();
|
||||
ipcRenderer.once("add-metadata-done", reinit);
|
||||
} catch (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 slider = ElementFromFile(templatePath(__dirname, "slider.html"));
|
||||
@ -49,8 +52,7 @@ module.exports = () => {
|
||||
|
||||
watchDOMElement(
|
||||
"menu",
|
||||
(document) =>
|
||||
document.querySelector("ytmusic-menu-popup-renderer paper-listbox"),
|
||||
(document) => getSongMenu(document),
|
||||
(menuElement) => {
|
||||
if (!menuElement.contains(slider)) {
|
||||
menuElement.prepend(slider);
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
aria-disabled="false"
|
||||
aria-selected="false"
|
||||
>
|
||||
<paper-slider
|
||||
<tp-yt-paper-slider
|
||||
id="playback-speed-slider"
|
||||
class="volume-slider style-scope ytmusic-player-bar on-hover"
|
||||
max="100"
|
||||
@ -22,68 +22,53 @@
|
||||
aria-disabled="false"
|
||||
value="50"
|
||||
><!--css-build:shady-->
|
||||
<div id="sliderContainer" class="style-scope paper-slider">
|
||||
<div class="bar-container style-scope paper-slider">
|
||||
<paper-progress
|
||||
<div id="sliderContainer" class="style-scope tp-yt-paper-slider">
|
||||
<div class="bar-container style-scope tp-yt-paper-slider">
|
||||
<tp-yt-paper-progress
|
||||
id="sliderBar"
|
||||
aria-hidden="true"
|
||||
class="style-scope paper-slider"
|
||||
class="style-scope tp-yt-paper-slider"
|
||||
role="progressbar"
|
||||
value="50"
|
||||
aria-valuenow="50"
|
||||
aria-valuemin="0"
|
||||
aria-valuemax="100"
|
||||
aria-disabled="false"
|
||||
style="touch-action: none;"
|
||||
style="touch-action: none"
|
||||
><!--css-build:shady-->
|
||||
|
||||
<div id="progressContainer" class="style-scope paper-progress">
|
||||
<div id="progressContainer" class="style-scope tp-yt-paper-progress">
|
||||
<div
|
||||
id="secondaryProgress"
|
||||
class="style-scope paper-progress"
|
||||
class="style-scope tp-yt-paper-progress"
|
||||
hidden="true"
|
||||
style="transform: scaleX(0);"
|
||||
style="transform: scaleX(0)"
|
||||
></div>
|
||||
<div
|
||||
id="primaryProgress"
|
||||
class="style-scope paper-progress"
|
||||
style="transform: scaleX(0.5);"
|
||||
class="style-scope tp-yt-paper-progress"
|
||||
style="transform: scaleX(0.5)"
|
||||
></div>
|
||||
</div>
|
||||
</paper-progress>
|
||||
</tp-yt-paper-progress>
|
||||
</div>
|
||||
<dom-if class="style-scope paper-slider"
|
||||
<dom-if class="style-scope tp-yt-paper-slider"
|
||||
><template is="dom-if"></template
|
||||
></dom-if>
|
||||
<div
|
||||
id="sliderKnob"
|
||||
class="slider-knob style-scope paper-slider"
|
||||
style="left: 50%; touch-action: none;"
|
||||
class="slider-knob style-scope tp-yt-paper-slider"
|
||||
style="left: 50%; touch-action: none"
|
||||
>
|
||||
<div
|
||||
class="slider-knob-inner style-scope paper-slider"
|
||||
class="slider-knob-inner style-scope tp-yt-paper-slider"
|
||||
value="50"
|
||||
></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>
|
||||
<dom-if class="style-scope paper-slider"
|
||||
><template is="dom-if"></template
|
||||
></dom-if>
|
||||
</paper-slider>
|
||||
<dom-if class="style-scope tp-yt-paper-slider"
|
||||
><template is="dom-if"></template></dom-if
|
||||
></tp-yt-paper-slider>
|
||||
|
||||
<div
|
||||
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