mirror of
https://github.com/th-ch/youtube-music.git
synced 2026-01-11 10:31:47 +00:00
feat: apply rollup 🚀 (#20)
Co-authored-by: Su-Yong <simssy2205@gmail.com>
This commit is contained in:
@ -1 +1,2 @@
|
|||||||
.eslintrc.js
|
.eslintrc.js
|
||||||
|
rollup.config.ts
|
||||||
|
|||||||
6
.github/workflows/build.yml
vendored
6
.github/workflows/build.yml
vendored
@ -62,17 +62,17 @@ jobs:
|
|||||||
- name: Build on Mac
|
- name: Build on Mac
|
||||||
if: startsWith(matrix.os, 'macOS') && github.repository != 'organization/youtube-music-next'
|
if: startsWith(matrix.os, 'macOS') && github.repository != 'organization/youtube-music-next'
|
||||||
run: |
|
run: |
|
||||||
npm run build:mac
|
npm run dist:mac
|
||||||
|
|
||||||
- name: Build on Linux
|
- name: Build on Linux
|
||||||
if: startsWith(matrix.os, 'ubuntu') && github.repository != 'organization/youtube-music-next'
|
if: startsWith(matrix.os, 'ubuntu') && github.repository != 'organization/youtube-music-next'
|
||||||
run: |
|
run: |
|
||||||
npm run build:linux
|
npm run dist:linux
|
||||||
|
|
||||||
- name: Build on Windows
|
- name: Build on Windows
|
||||||
if: startsWith(matrix.os, 'windows') && github.repository != 'organization/youtube-music-next'
|
if: startsWith(matrix.os, 'windows') && github.repository != 'organization/youtube-music-next'
|
||||||
run: |
|
run: |
|
||||||
npm run build:win
|
npm run dist:win
|
||||||
|
|
||||||
release:
|
release:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|||||||
8
index.ts
8
index.ts
@ -11,7 +11,7 @@ import { BetterWebRequest } from 'electron-better-web-request/lib/electron-bette
|
|||||||
|
|
||||||
import config from './config';
|
import config from './config';
|
||||||
import { setApplicationMenu } from './menu';
|
import { setApplicationMenu } from './menu';
|
||||||
import { fileExists, injectCSS } from './plugins/utils';
|
import { fileExists, injectCSS, injectCSSAsFile } from './plugins/utils';
|
||||||
import { isTesting } from './utils/testing';
|
import { isTesting } from './utils/testing';
|
||||||
import { setUpTray } from './tray';
|
import { setUpTray } from './tray';
|
||||||
import { setupSongInfo } from './providers/song-info';
|
import { setupSongInfo } from './providers/song-info';
|
||||||
@ -42,6 +42,8 @@ import tunaObs from './plugins/tuna-obs/back';
|
|||||||
import videoToggle from './plugins/video-toggle/back';
|
import videoToggle from './plugins/video-toggle/back';
|
||||||
import visualizer from './plugins/visualizer/back';
|
import visualizer from './plugins/visualizer/back';
|
||||||
|
|
||||||
|
import youtubeMusicCSS from './youtube-music.css';
|
||||||
|
|
||||||
// Catch errors and log them
|
// Catch errors and log them
|
||||||
unhandled({
|
unhandled({
|
||||||
logger: console.error,
|
logger: console.error,
|
||||||
@ -139,7 +141,7 @@ if (is.windows()) {
|
|||||||
ipcMain.handle('get-main-plugin-names', () => Object.keys(mainPlugins));
|
ipcMain.handle('get-main-plugin-names', () => Object.keys(mainPlugins));
|
||||||
|
|
||||||
function loadPlugins(win: BrowserWindow) {
|
function loadPlugins(win: BrowserWindow) {
|
||||||
injectCSS(win.webContents, path.join(__dirname, 'youtube-music.css'));
|
injectCSS(win.webContents, youtubeMusicCSS);
|
||||||
// Load user CSS
|
// Load user CSS
|
||||||
const themes: string[] = config.get('options.themes');
|
const themes: string[] = config.get('options.themes');
|
||||||
if (Array.isArray(themes)) {
|
if (Array.isArray(themes)) {
|
||||||
@ -147,7 +149,7 @@ function loadPlugins(win: BrowserWindow) {
|
|||||||
fileExists(
|
fileExists(
|
||||||
cssFile,
|
cssFile,
|
||||||
() => {
|
() => {
|
||||||
injectCSS(win.webContents, cssFile);
|
injectCSSAsFile(win.webContents, cssFile);
|
||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
console.warn(`CSS file "${cssFile}" does not exist, ignoring`);
|
console.warn(`CSS file "${cssFile}" does not exist, ignoring`);
|
||||||
|
|||||||
2
menu.ts
2
menu.ts
@ -181,7 +181,7 @@ export const mainMenuTemplate = (win: BrowserWindow): MenuTemplate => {
|
|||||||
{
|
{
|
||||||
label: 'No theme',
|
label: 'No theme',
|
||||||
type: 'radio',
|
type: 'radio',
|
||||||
checked: config.get('options.themes').length === 0, // Todo rename "themes"
|
checked: config.get('options.themes')?.length === 0, // Todo rename "themes"
|
||||||
click() {
|
click() {
|
||||||
config.set('options.themes', []);
|
config.set('options.themes', []);
|
||||||
},
|
},
|
||||||
|
|||||||
2588
package-lock.json
generated
2588
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
111
package.json
111
package.json
@ -14,20 +14,38 @@
|
|||||||
"build": {
|
"build": {
|
||||||
"appId": "com.github.th-ch.youtube-music",
|
"appId": "com.github.th-ch.youtube-music",
|
||||||
"productName": "YouTube Music",
|
"productName": "YouTube Music",
|
||||||
"mac": {
|
|
||||||
"identity": null,
|
|
||||||
"files": [
|
"files": [
|
||||||
"!*",
|
"!*",
|
||||||
"dist",
|
"dist",
|
||||||
"!dist/plugins/taskbar-mediacontrol${/*}",
|
|
||||||
"license",
|
"license",
|
||||||
"node_modules",
|
"!node_modules",
|
||||||
"!node_modules/sharp/vendor",
|
"node_modules/custom-electron-prompt/**",
|
||||||
|
"node_modules/youtubei.js/**",
|
||||||
|
"node_modules/undici/**",
|
||||||
|
"node_modules/@fastify/busboy/**",
|
||||||
|
"node_modules/jintr/**",
|
||||||
|
"node_modules/acorn/**",
|
||||||
|
"node_modules/tslib/**",
|
||||||
|
"node_modules/sharp/**",
|
||||||
|
"!node_modules/sharp/vendor/*/win32-x64",
|
||||||
|
"node_modules/semver/**",
|
||||||
|
"node_modules/lru-cache/**",
|
||||||
|
"node_modules/detect-libc/**",
|
||||||
|
"node_modules/color/**",
|
||||||
|
"node_modules/color-convert/**",
|
||||||
|
"node_modules/color-string/**",
|
||||||
|
"node_modules/color-name/**",
|
||||||
|
"node_modules/simple-swizzle/**",
|
||||||
|
"node_modules/is-arrayish/**",
|
||||||
|
"node_modules/@cliqz/adblocker-electron-preload/**",
|
||||||
|
"node_modules/@cliqz/adblocker-content/**",
|
||||||
|
"node_modules/@cliqz/adblocker-extended-selectors/**",
|
||||||
|
"node_modules/@ffmpeg.wasm/core-mt/**",
|
||||||
"!node_modules/**/*.map",
|
"!node_modules/**/*.map",
|
||||||
"!node_modules/**/*.ts",
|
"!node_modules/**/*.ts"
|
||||||
"package.json",
|
|
||||||
"tests"
|
|
||||||
],
|
],
|
||||||
|
"mac": {
|
||||||
|
"identity": null,
|
||||||
"target": [
|
"target": [
|
||||||
{
|
{
|
||||||
"target": "dmg",
|
"target": "dmg",
|
||||||
@ -41,18 +59,6 @@
|
|||||||
},
|
},
|
||||||
"win": {
|
"win": {
|
||||||
"icon": "assets/generated/icons/win/icon.ico",
|
"icon": "assets/generated/icons/win/icon.ico",
|
||||||
"files": [
|
|
||||||
"!*",
|
|
||||||
"dist",
|
|
||||||
"!dist/plugins/touchbar${/*}",
|
|
||||||
"license",
|
|
||||||
"node_modules",
|
|
||||||
"!node_modules/sharp/vendor",
|
|
||||||
"!node_modules/**/*.map",
|
|
||||||
"!node_modules/**/*.ts",
|
|
||||||
"package.json",
|
|
||||||
"tests"
|
|
||||||
],
|
|
||||||
"target": [
|
"target": [
|
||||||
{
|
{
|
||||||
"target": "nsis",
|
"target": "nsis",
|
||||||
@ -75,18 +81,6 @@
|
|||||||
},
|
},
|
||||||
"linux": {
|
"linux": {
|
||||||
"icon": "assets/generated/icons/png",
|
"icon": "assets/generated/icons/png",
|
||||||
"files": [
|
|
||||||
"!*",
|
|
||||||
"dist",
|
|
||||||
"!dist/plugins/{touchbar,taskbar-mediacontrol}${/*}",
|
|
||||||
"license",
|
|
||||||
"node_modules",
|
|
||||||
"!node_modules/sharp/vendor",
|
|
||||||
"!node_modules/**/*.map",
|
|
||||||
"!node_modules/**/*.ts",
|
|
||||||
"package.json",
|
|
||||||
"tests"
|
|
||||||
],
|
|
||||||
"category": "AudioVideo",
|
"category": "AudioVideo",
|
||||||
"target": [
|
"target": [
|
||||||
"AppImage",
|
"AppImage",
|
||||||
@ -110,31 +104,29 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "playwright test",
|
"test": "npm run build && playwright test",
|
||||||
"test:debug": "DEBUG=pw:browser* playwright test",
|
"test:debug": "DEBUG=pw:browser* npm run build && playwright test",
|
||||||
"start": "npm run tsc-and-copy && electron ./dist/index.js",
|
"rollup:preload": "rollup -c rollup.preload.config.ts --configPlugin @rollup/plugin-typescript --bundleConfigAsCjs",
|
||||||
"start:debug": "ELECTRON_ENABLE_LOGGING=1 electron ./dist/index.js",
|
"rollup:main": "rollup -c rollup.main.config.ts --configPlugin @rollup/plugin-typescript --bundleConfigAsCjs",
|
||||||
|
"build": "npm run rollup:preload && npm run rollup:main",
|
||||||
|
"start": "npm run build && electron ./dist/index.js",
|
||||||
|
"start:debug": "ELECTRON_ENABLE_LOGGING=1 npm run start",
|
||||||
"generate:package": "node utils/generate-package-json.js",
|
"generate:package": "node utils/generate-package-json.js",
|
||||||
"postinstall": "npm run plugins",
|
"postinstall": "npm run plugins && npm run clean",
|
||||||
"clean": "del-cli dist && del-cli pack",
|
"clean": "del-cli dist && del-cli pack",
|
||||||
"ytm-resource-copy-files": "copyfiles error.html youtube-music.css assets/**/* dist/",
|
"dist": "npm run clean && npm run build && electron-builder --win --mac --linux -p never",
|
||||||
"copy-files": "copyfiles -u 1 plugins/**/*.html plugins/**/*.css plugins/**/*.bin plugins/**/*.js dist/plugins/",
|
"dist:linux": "npm run clean && npm run build && electron-builder --linux -p never",
|
||||||
"tsc-and-copy": "tsc && npm run plugin:adblocker-without-tsc && npm run ytm-resource-copy-files && npm run copy-files",
|
"dist:mac": "npm run clean && npm run build && electron-builder --mac dmg:x64 -p never",
|
||||||
"build": "npm run clean && npm run tsc-and-copy && electron-builder --win --mac --linux -p never",
|
"dist:mac:arm64": "npm run clean && npm run build && electron-builder --mac dmg:arm64 -p never",
|
||||||
"build:linux": "npm run clean && npm run tsc-and-copy && electron-builder --linux -p never",
|
"dist:win": "npm run clean && npm run build && electron-builder --win -p never",
|
||||||
"build:mac": "npm run clean && npm run tsc-and-copy && electron-builder --mac dmg:x64 -p never",
|
"dist:win:x64": "npm run clean && npm run build && electron-builder --win nsis:x64 -p never",
|
||||||
"build:mac:arm64": "npm run clean && npm run tsc-and-copy && electron-builder --mac dmg:arm64 -p never",
|
|
||||||
"build:win": "npm run clean && npm run tsc-and-copy && electron-builder --win -p never",
|
|
||||||
"build:win:x64": "npm run clean && npm run tsc-and-copy && electron-builder --win nsis:x64 -p never",
|
|
||||||
"lint": "eslint .",
|
"lint": "eslint .",
|
||||||
"changelog": "auto-changelog",
|
"changelog": "auto-changelog",
|
||||||
"plugins": "npm run plugin:adblocker && npm run plugin:bypass-age-restrictions",
|
"plugins": "npm run plugin:bypass-age-restrictions",
|
||||||
"plugin:adblocker-without-tsc": "del-cli plugins/adblocker/ad-blocker-engine.bin && node dist/plugins/adblocker/blocker.js",
|
|
||||||
"plugin:adblocker": "del-cli plugins/adblocker/ad-blocker-engine.bin && tsc && node dist/plugins/adblocker/blocker.js",
|
|
||||||
"plugin:bypass-age-restrictions": "del-cli node_modules/simple-youtube-age-restriction-bypass/package.json && npm run generate:package simple-youtube-age-restriction-bypass",
|
"plugin:bypass-age-restrictions": "del-cli node_modules/simple-youtube-age-restriction-bypass/package.json && npm run generate:package simple-youtube-age-restriction-bypass",
|
||||||
"release:linux": "npm run clean && npm run tsc-and-copy && electron-builder --linux -p always -c.snap.publish=github",
|
"release:linux": "npm run clean && npm run build && electron-builder --linux -p always -c.snap.publish=github",
|
||||||
"release:mac": "npm run clean && npm run tsc-and-copy && electron-builder --mac -p always",
|
"release:mac": "npm run clean && npm run build && electron-builder --mac -p always",
|
||||||
"release:win": "npm run clean && npm run tsc-and-copy && electron-builder --win -p always",
|
"release:win": "npm run clean && npm run build && electron-builder --win -p always",
|
||||||
"typecheck": "tsc -p tsconfig.json --noEmit"
|
"typecheck": "tsc -p tsconfig.json --noEmit"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@ -168,25 +160,34 @@
|
|||||||
"node-id3": "0.2.6",
|
"node-id3": "0.2.6",
|
||||||
"simple-youtube-age-restriction-bypass": "git+https://github.com/MiepHD/Simple-YouTube-Age-Restriction-Bypass.git#v2.5.5",
|
"simple-youtube-age-restriction-bypass": "git+https://github.com/MiepHD/Simple-YouTube-Age-Restriction-Bypass.git#v2.5.5",
|
||||||
"vudio": "2.1.1",
|
"vudio": "2.1.1",
|
||||||
|
"x11": "2.3.0",
|
||||||
"youtubei.js": "6.4.1",
|
"youtubei.js": "6.4.1",
|
||||||
"ytpl": "2.3.0"
|
"ytpl": "2.3.0"
|
||||||
},
|
},
|
||||||
"overrides": {
|
"overrides": {
|
||||||
|
"node-gyp": "9.4.0",
|
||||||
"xml2js": "0.6.2",
|
"xml2js": "0.6.2",
|
||||||
"sharp": "0.32.6",
|
"sharp": "0.32.6",
|
||||||
|
"dbus-next": "0.10.2",
|
||||||
"node-fetch": "2.7.0",
|
"node-fetch": "2.7.0",
|
||||||
"@electron/universal": "1.4.2",
|
"@electron/universal": "1.4.2",
|
||||||
"electron": "27.0.0-beta.9"
|
"electron": "27.0.0-beta.9"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@playwright/test": "1.38.1",
|
"@playwright/test": "1.38.1",
|
||||||
|
"@rollup/plugin-commonjs": "25.0.4",
|
||||||
|
"@rollup/plugin-image": "3.0.2",
|
||||||
|
"@rollup/plugin-json": "6.0.0",
|
||||||
|
"@rollup/plugin-node-resolve": "15.2.1",
|
||||||
|
"@rollup/plugin-terser": "0.4.3",
|
||||||
|
"@rollup/plugin-typescript": "11.1.4",
|
||||||
|
"@rollup/plugin-wasm": "6.2.1",
|
||||||
"@total-typescript/ts-reset": "0.5.1",
|
"@total-typescript/ts-reset": "0.5.1",
|
||||||
"@types/electron-localshortcut": "3.1.1",
|
"@types/electron-localshortcut": "3.1.1",
|
||||||
"@types/howler": "2.2.9",
|
"@types/howler": "2.2.9",
|
||||||
"@types/html-to-text": "9.0.2",
|
"@types/html-to-text": "9.0.2",
|
||||||
"@typescript-eslint/eslint-plugin": "6.7.4",
|
"@typescript-eslint/eslint-plugin": "6.7.4",
|
||||||
"auto-changelog": "2.4.0",
|
"auto-changelog": "2.4.0",
|
||||||
"copyfiles": "2.4.1",
|
|
||||||
"del-cli": "5.1.0",
|
"del-cli": "5.1.0",
|
||||||
"electron": "27.0.0-beta.9",
|
"electron": "27.0.0-beta.9",
|
||||||
"electron-builder": "24.6.4",
|
"electron-builder": "24.6.4",
|
||||||
@ -196,6 +197,10 @@
|
|||||||
"eslint-plugin-prettier": "5.0.0",
|
"eslint-plugin-prettier": "5.0.0",
|
||||||
"node-gyp": "9.4.0",
|
"node-gyp": "9.4.0",
|
||||||
"playwright": "1.38.1",
|
"playwright": "1.38.1",
|
||||||
|
"rollup": "3.29.4",
|
||||||
|
"rollup-plugin-copy": "3.5.0",
|
||||||
|
"rollup-plugin-import-css": "3.3.4",
|
||||||
|
"rollup-plugin-string": "3.0.0",
|
||||||
"typescript": "5.2.2"
|
"typescript": "5.2.2"
|
||||||
},
|
},
|
||||||
"auto-changelog": {
|
"auto-changelog": {
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
// Used for caching
|
// Used for caching
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import { promises } from 'node:fs';
|
import fs, { promises } from 'node:fs';
|
||||||
|
|
||||||
import { ElectronBlocker } from '@cliqz/adblocker-electron';
|
import { ElectronBlocker } from '@cliqz/adblocker-electron';
|
||||||
|
import { app } from 'electron';
|
||||||
|
|
||||||
const SOURCES = [
|
const SOURCES = [
|
||||||
'https://raw.githubusercontent.com/kbinani/adblock-youtube-ads/master/signed.txt',
|
'https://raw.githubusercontent.com/kbinani/adblock-youtube-ads/master/signed.txt',
|
||||||
@ -23,10 +24,19 @@ export const loadAdBlockerEngine = (
|
|||||||
disableDefaultLists: boolean | string[] = false,
|
disableDefaultLists: boolean | string[] = false,
|
||||||
) => {
|
) => {
|
||||||
// Only use cache if no additional blocklists are passed
|
// Only use cache if no additional blocklists are passed
|
||||||
|
let cacheDirectory: string;
|
||||||
|
if (app.isPackaged) {
|
||||||
|
cacheDirectory = path.join(app.getPath('userData'), 'cache');
|
||||||
|
} else {
|
||||||
|
cacheDirectory = path.resolve(__dirname, 'cache');
|
||||||
|
}
|
||||||
|
if (!fs.existsSync(cacheDirectory)) {
|
||||||
|
fs.mkdirSync(cacheDirectory);
|
||||||
|
}
|
||||||
const cachingOptions
|
const cachingOptions
|
||||||
= cache && additionalBlockLists.length === 0
|
= cache && additionalBlockLists.length === 0
|
||||||
? {
|
? {
|
||||||
path: path.resolve(__dirname, 'ad-blocker-engine.bin'),
|
path: path.join(cacheDirectory, 'adblocker-engine.bin'),
|
||||||
read: promises.readFile,
|
read: promises.readFile,
|
||||||
write: promises.writeFile,
|
write: promises.writeFile,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
export default () => {
|
export default () => {
|
||||||
require('@cliqz/adblocker-electron-preload');
|
const path = require.resolve('@cliqz/adblocker-electron-preload'); // prevent require hoisting
|
||||||
|
require(path);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,14 +1,13 @@
|
|||||||
import path from 'node:path';
|
|
||||||
|
|
||||||
import { getAverageColor } from 'fast-average-color-node';
|
import { getAverageColor } from 'fast-average-color-node';
|
||||||
import { BrowserWindow } from 'electron';
|
import { BrowserWindow } from 'electron';
|
||||||
|
|
||||||
|
import style from './style.css';
|
||||||
|
|
||||||
import { injectCSS } from '../utils';
|
import { injectCSS } from '../utils';
|
||||||
import registerCallback from '../../providers/song-info';
|
import registerCallback from '../../providers/song-info';
|
||||||
|
|
||||||
|
|
||||||
export default (win: BrowserWindow) => {
|
export default (win: BrowserWindow) => {
|
||||||
injectCSS(win.webContents, path.join(__dirname, 'style.css'));
|
injectCSS(win.webContents, style);
|
||||||
|
|
||||||
registerCallback((songInfo) => {
|
registerCallback((songInfo) => {
|
||||||
const songTitle = songInfo.title;
|
const songTitle = songInfo.title;
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
import path from 'node:path';
|
|
||||||
|
|
||||||
import { BrowserWindow } from 'electron';
|
import { BrowserWindow } from 'electron';
|
||||||
|
|
||||||
|
import style from './style.css';
|
||||||
|
|
||||||
import { injectCSS } from '../utils';
|
import { injectCSS } from '../utils';
|
||||||
|
|
||||||
export default (win: BrowserWindow) => {
|
export default (win: BrowserWindow) => {
|
||||||
injectCSS(win.webContents, path.join(__dirname, 'style.css'));
|
injectCSS(win.webContents, style);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -5,7 +5,9 @@ import { ipcRenderer } from 'electron';
|
|||||||
|
|
||||||
import configProvider from './config';
|
import configProvider from './config';
|
||||||
|
|
||||||
import { ElementFromFile, templatePath } from '../utils';
|
import CaptionsSettingsButtonHTML from './templates/captions-settings-template.html';
|
||||||
|
|
||||||
|
import { ElementFromHtml } from '../utils';
|
||||||
import { YoutubePlayer } from '../../types/youtube-player';
|
import { YoutubePlayer } from '../../types/youtube-player';
|
||||||
|
|
||||||
import type { ConfigType } from '../../config/dynamic';
|
import type { ConfigType } from '../../config/dynamic';
|
||||||
@ -27,9 +29,7 @@ let config: ConfigType<'captions-selector'>;
|
|||||||
|
|
||||||
const $ = <Element extends HTMLElement>(selector: string): Element => document.querySelector(selector)!;
|
const $ = <Element extends HTMLElement>(selector: string): Element => document.querySelector(selector)!;
|
||||||
|
|
||||||
const captionsSettingsButton = ElementFromFile(
|
const captionsSettingsButton = ElementFromHtml(CaptionsSettingsButtonHTML);
|
||||||
templatePath(__dirname, 'captions-settings-template.html'),
|
|
||||||
);
|
|
||||||
|
|
||||||
export default async () => {
|
export default async () => {
|
||||||
// RENDERER
|
// RENDERER
|
||||||
|
|||||||
@ -24,6 +24,8 @@ import { cropMaxWidth, getFolder, presets, sendFeedback as sendFeedback_, setBad
|
|||||||
|
|
||||||
import config from './config';
|
import config from './config';
|
||||||
|
|
||||||
|
import style from './style.css';
|
||||||
|
|
||||||
import { fetchFromGenius } from '../lyrics-genius/back';
|
import { fetchFromGenius } from '../lyrics-genius/back';
|
||||||
import { isEnabled } from '../../config/plugins';
|
import { isEnabled } from '../../config/plugins';
|
||||||
import { cleanupName, getImage, SongInfo } from '../../providers/song-info';
|
import { cleanupName, getImage, SongInfo } from '../../providers/song-info';
|
||||||
@ -32,6 +34,7 @@ import { cache } from '../../providers/decorators';
|
|||||||
|
|
||||||
import type { GetPlayerResponse } from '../../types/get-player-response';
|
import type { GetPlayerResponse } from '../../types/get-player-response';
|
||||||
|
|
||||||
|
|
||||||
type CustomSongInfo = SongInfo & { trackId?: string };
|
type CustomSongInfo = SongInfo & { trackId?: string };
|
||||||
|
|
||||||
const ffmpeg = createFFmpeg({
|
const ffmpeg = createFFmpeg({
|
||||||
@ -68,7 +71,7 @@ const sendError = (error: Error, source?: string) => {
|
|||||||
|
|
||||||
export default async (win_: BrowserWindow) => {
|
export default async (win_: BrowserWindow) => {
|
||||||
win = win_;
|
win = win_;
|
||||||
injectCSS(win.webContents, join(__dirname, 'style.css'));
|
injectCSS(win.webContents, style);
|
||||||
|
|
||||||
const cookie = (await win.webContents.session.cookies.get({ url: 'https://music.youtube.com' })).map((it) =>
|
const cookie = (await win.webContents.session.cookies.get({ url: 'https://music.youtube.com' })).map((it) =>
|
||||||
it.name + '=' + it.value + ';'
|
it.name + '=' + it.value + ';'
|
||||||
@ -104,6 +107,7 @@ export async function downloadSong(
|
|||||||
increasePlaylistProgress,
|
increasePlaylistProgress,
|
||||||
);
|
);
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
|
console.log('maybe?????', error);
|
||||||
sendError(error as Error, resolvedName || url);
|
sendError(error as Error, resolvedName || url);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -316,6 +320,7 @@ async function iterableStreamToMP3(
|
|||||||
ffmpeg.FS('unlink', `${safeVideoName}.mp3`);
|
ffmpeg.FS('unlink', `${safeVideoName}.mp3`);
|
||||||
}
|
}
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
|
console.log('maybe?', error);
|
||||||
sendError(error as Error, safeVideoName);
|
sendError(error as Error, safeVideoName);
|
||||||
} finally {
|
} finally {
|
||||||
releaseFFmpegMutex();
|
releaseFFmpegMutex();
|
||||||
@ -368,6 +373,7 @@ async function writeID3(buffer: Buffer, metadata: CustomSongInfo, sendFeedback:
|
|||||||
|
|
||||||
return NodeID3.write(tags, buffer);
|
return NodeID3.write(tags, buffer);
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
|
console.log('fallback', error);
|
||||||
sendError(error as Error, `${metadata.artist} - ${metadata.title}`);
|
sendError(error as Error, `${metadata.artist} - ${metadata.title}`);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -482,6 +488,7 @@ export async function downloadPlaylist(givenUrl?: string | URL) {
|
|||||||
counter++;
|
counter++;
|
||||||
}
|
}
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
|
console.log('also?', error);
|
||||||
sendError(error as Error);
|
sendError(error as Error);
|
||||||
} finally {
|
} finally {
|
||||||
win.setProgressBar(-1); // Close progress bar
|
win.setProgressBar(-1); // Close progress bar
|
||||||
@ -507,6 +514,7 @@ async function ffmpegWriteTags(filePath: string, metadata: CustomSongInfo, prese
|
|||||||
filePath,
|
filePath,
|
||||||
);
|
);
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
|
console.log('ffmpeg?', error);
|
||||||
sendError(error as Error);
|
sendError(error as Error);
|
||||||
} finally {
|
} finally {
|
||||||
releaseFFmpegMutex();
|
releaseFFmpegMutex();
|
||||||
|
|||||||
@ -1,15 +1,15 @@
|
|||||||
import { ipcRenderer } from 'electron';
|
import { ipcRenderer } from 'electron';
|
||||||
|
|
||||||
|
import downloadHTML from './templates/download.html';
|
||||||
|
|
||||||
import defaultConfig from '../../config/defaults';
|
import defaultConfig from '../../config/defaults';
|
||||||
import { getSongMenu } from '../../providers/dom-elements';
|
import { getSongMenu } from '../../providers/dom-elements';
|
||||||
import { ElementFromFile, templatePath } from '../utils';
|
import { ElementFromHtml } from '../utils';
|
||||||
import { getSongInfo } from '../../providers/song-info-front';
|
import { getSongInfo } from '../../providers/song-info-front';
|
||||||
|
|
||||||
let menu: Element | null = null;
|
let menu: Element | null = null;
|
||||||
let progress: Element | null = null;
|
let progress: Element | null = null;
|
||||||
const downloadButton = ElementFromFile(
|
const downloadButton = ElementFromHtml(downloadHTML);
|
||||||
templatePath(__dirname, 'download.html'),
|
|
||||||
);
|
|
||||||
|
|
||||||
let doneFirstLoad = false;
|
let doneFirstLoad = false;
|
||||||
|
|
||||||
|
|||||||
@ -4,11 +4,13 @@ import { register } from 'electron-localshortcut';
|
|||||||
|
|
||||||
import { BrowserWindow, Menu, MenuItem, ipcMain } from 'electron';
|
import { BrowserWindow, Menu, MenuItem, ipcMain } from 'electron';
|
||||||
|
|
||||||
|
import titlebarStyle from './titlebar.css';
|
||||||
|
|
||||||
import { injectCSS } from '../utils';
|
import { injectCSS } from '../utils';
|
||||||
|
|
||||||
// Tracks menu visibility
|
// Tracks menu visibility
|
||||||
export default (win: BrowserWindow) => {
|
export default (win: BrowserWindow) => {
|
||||||
injectCSS(win.webContents, path.join(__dirname, 'titlebar.css'));
|
injectCSS(win.webContents, titlebarStyle);
|
||||||
|
|
||||||
win.once('ready-to-show', () => {
|
win.once('ready-to-show', () => {
|
||||||
register(win, '`', () => {
|
register(win, '`', () => {
|
||||||
|
|||||||
@ -1,10 +1,8 @@
|
|||||||
import path from 'node:path';
|
|
||||||
|
|
||||||
import { ipcRenderer, Menu } from 'electron';
|
import { ipcRenderer, Menu } from 'electron';
|
||||||
|
|
||||||
import { createPanel } from './menu/panel';
|
import { createPanel } from './menu/panel';
|
||||||
|
|
||||||
import { ElementFromFile } from '../utils';
|
import logo from '../../assets/youtube-music.svg';
|
||||||
import { isEnabled } from '../../config/plugins';
|
import { isEnabled } from '../../config/plugins';
|
||||||
|
|
||||||
function $<E extends Element = Element>(selector: string) {
|
function $<E extends Element = Element>(selector: string) {
|
||||||
@ -18,7 +16,6 @@ export default () => {
|
|||||||
const navBar = document.querySelector<HTMLDivElement>('#nav-bar-background');
|
const navBar = document.querySelector<HTMLDivElement>('#nav-bar-background');
|
||||||
if (isMacOS) titleBar.style.setProperty('--offset-left', '70px');
|
if (isMacOS) titleBar.style.setProperty('--offset-left', '70px');
|
||||||
|
|
||||||
const logo = ElementFromFile(path.join(__dirname, '..' , '..' , 'assets', 'youtube-music.svg'));
|
|
||||||
logo.classList.add('title-bar-icon');
|
logo.classList.add('title-bar-icon');
|
||||||
|
|
||||||
if (!isMacOS) titleBar.appendChild(logo);
|
if (!isMacOS) titleBar.appendChild(logo);
|
||||||
|
|||||||
@ -1,9 +1,8 @@
|
|||||||
import { join } from 'node:path';
|
|
||||||
|
|
||||||
import { BrowserWindow, ipcMain, net } from 'electron';
|
import { BrowserWindow, ipcMain, net } from 'electron';
|
||||||
import is from 'electron-is';
|
import is from 'electron-is';
|
||||||
import { convert } from 'html-to-text';
|
import { convert } from 'html-to-text';
|
||||||
|
|
||||||
|
import style from './style.css';
|
||||||
import { GetGeniusLyric } from './types';
|
import { GetGeniusLyric } from './types';
|
||||||
|
|
||||||
import { cleanupName, SongInfo } from '../../providers/song-info';
|
import { cleanupName, SongInfo } from '../../providers/song-info';
|
||||||
@ -22,7 +21,7 @@ export default (win: BrowserWindow, options: LyricGeniusType) => {
|
|||||||
revRomanized = true;
|
revRomanized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
injectCSS(win.webContents, join(__dirname, 'style.css'));
|
injectCSS(win.webContents, style);
|
||||||
|
|
||||||
ipcMain.handle('search-genius-lyrics', async (_, extractedSongInfo: SongInfo) => {
|
ipcMain.handle('search-genius-lyrics', async (_, extractedSongInfo: SongInfo) => {
|
||||||
const metadata = extractedSongInfo;
|
const metadata = extractedSongInfo;
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
import path from 'node:path';
|
|
||||||
|
|
||||||
import { BrowserWindow } from 'electron';
|
import { BrowserWindow } from 'electron';
|
||||||
|
|
||||||
|
import style from './style.css';
|
||||||
|
|
||||||
import { injectCSS } from '../utils';
|
import { injectCSS } from '../utils';
|
||||||
|
|
||||||
export function handle(win: BrowserWindow) {
|
export function handle(win: BrowserWindow) {
|
||||||
injectCSS(win.webContents, path.join(__dirname, 'style.css'), () => {
|
injectCSS(win.webContents, style, () => {
|
||||||
win.webContents.send('navigation-css-ready');
|
win.webContents.send('navigation-css-ready');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,13 +1,14 @@
|
|||||||
import { ipcRenderer } from 'electron';
|
import { ipcRenderer } from 'electron';
|
||||||
|
|
||||||
import { ElementFromFile, templatePath } from '../utils';
|
import forwardHTML from './templates/forward.html';
|
||||||
|
import backHTML from './templates/back.html';
|
||||||
|
|
||||||
|
import { ElementFromHtml } from '../utils';
|
||||||
|
|
||||||
export function run() {
|
export function run() {
|
||||||
ipcRenderer.on('navigation-css-ready', () => {
|
ipcRenderer.on('navigation-css-ready', () => {
|
||||||
const forwardButton = ElementFromFile(
|
const forwardButton = ElementFromHtml(forwardHTML);
|
||||||
templatePath(__dirname, 'forward.html'),
|
const backButton = ElementFromHtml(backHTML);
|
||||||
);
|
|
||||||
const backButton = ElementFromFile(templatePath(__dirname, 'back.html'));
|
|
||||||
const menu = document.querySelector('#right-content');
|
const menu = document.querySelector('#right-content');
|
||||||
|
|
||||||
if (menu) {
|
if (menu) {
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
import path from 'node:path';
|
|
||||||
|
|
||||||
import { BrowserWindow } from 'electron';
|
import { BrowserWindow } from 'electron';
|
||||||
|
|
||||||
|
import style from './style.css';
|
||||||
|
|
||||||
import { injectCSS } from '../utils';
|
import { injectCSS } from '../utils';
|
||||||
|
|
||||||
export default (win: BrowserWindow) => {
|
export default (win: BrowserWindow) => {
|
||||||
injectCSS(win.webContents, path.join(__dirname, 'style.css'));
|
injectCSS(win.webContents, style);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -154,7 +154,7 @@ const getXml = (songInfo: SongInfo, iconSrc: string) => {
|
|||||||
|
|
||||||
const iconLocation = app.isPackaged
|
const iconLocation = app.isPackaged
|
||||||
? path.resolve(app.getPath('userData'), 'icons')
|
? path.resolve(app.getPath('userData'), 'icons')
|
||||||
: path.resolve(__dirname, '..', '..', 'assets/media-icons-black');
|
: path.resolve(__dirname, 'assets', 'media-icons-black');
|
||||||
|
|
||||||
const display = (kind: keyof typeof icons) => {
|
const display = (kind: keyof typeof icons) => {
|
||||||
if (config.get('toastStyle') === ToastStyles.legacy) {
|
if (config.get('toastStyle') === ToastStyles.legacy) {
|
||||||
|
|||||||
@ -88,7 +88,7 @@ export const saveTempIcon = () => {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const iconPath = path.resolve(__dirname, '../../assets/media-icons-black', `${kind}.png`);
|
const iconPath = path.resolve(__dirname, 'assets', 'media-icons-black', `${kind}.png`);
|
||||||
fs.mkdirSync(path.dirname(destinationPath), { recursive: true });
|
fs.mkdirSync(path.dirname(destinationPath), { recursive: true });
|
||||||
fs.copyFile(iconPath, destinationPath, () => {
|
fs.copyFile(iconPath, destinationPath, () => {
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
import path from 'node:path';
|
|
||||||
|
|
||||||
import { app, BrowserWindow, ipcMain } from 'electron';
|
import { app, BrowserWindow, ipcMain } from 'electron';
|
||||||
|
|
||||||
import { setOptions as setPluginOptions } from '../../config/plugins';
|
import style from './style.css';
|
||||||
|
|
||||||
import { injectCSS } from '../utils';
|
import { injectCSS } from '../utils';
|
||||||
|
import { setOptions as setPluginOptions } from '../../config/plugins';
|
||||||
|
|
||||||
import type { ConfigType } from '../../config/dynamic';
|
import type { ConfigType } from '../../config/dynamic';
|
||||||
|
|
||||||
@ -102,7 +102,7 @@ export default (_win: BrowserWindow, _options: PiPOptions) => {
|
|||||||
options ??= _options;
|
options ??= _options;
|
||||||
win ??= _win;
|
win ??= _win;
|
||||||
setLocalOptions({ isInPiP });
|
setLocalOptions({ isInPiP });
|
||||||
injectCSS(win.webContents, path.join(__dirname, 'style.css'));
|
injectCSS(win.webContents, style);
|
||||||
ipcMain.on('picture-in-picture', () => {
|
ipcMain.on('picture-in-picture', () => {
|
||||||
togglePiP();
|
togglePiP();
|
||||||
});
|
});
|
||||||
|
|||||||
@ -2,9 +2,11 @@ import { ipcRenderer } from 'electron';
|
|||||||
import { toKeyEvent } from 'keyboardevent-from-electron-accelerator';
|
import { toKeyEvent } from 'keyboardevent-from-electron-accelerator';
|
||||||
import keyEventAreEqual from 'keyboardevents-areequal';
|
import keyEventAreEqual from 'keyboardevents-areequal';
|
||||||
|
|
||||||
|
import pipHTML from './templates/picture-in-picture.html';
|
||||||
|
|
||||||
import { getSongMenu } from '../../providers/dom-elements';
|
import { getSongMenu } from '../../providers/dom-elements';
|
||||||
|
|
||||||
import { ElementFromFile, templatePath } from '../utils';
|
import { ElementFromHtml } from '../utils';
|
||||||
|
|
||||||
import type { ConfigType } from '../../config/dynamic';
|
import type { ConfigType } from '../../config/dynamic';
|
||||||
|
|
||||||
@ -16,9 +18,7 @@ function $<E extends Element = Element>(selector: string) {
|
|||||||
|
|
||||||
let useNativePiP = false;
|
let useNativePiP = false;
|
||||||
let menu: Element | null = null;
|
let menu: Element | null = null;
|
||||||
const pipButton = ElementFromFile(
|
const pipButton = ElementFromHtml(pipHTML);
|
||||||
templatePath(__dirname, 'picture-in-picture.html'),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Will also clone
|
// Will also clone
|
||||||
function replaceButton(query: string, button: Element) {
|
function replaceButton(query: string, button: Element) {
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
|
import sliderHTML from './templates/slider.html';
|
||||||
|
|
||||||
import { getSongMenu } from '../../providers/dom-elements';
|
import { getSongMenu } from '../../providers/dom-elements';
|
||||||
import { ElementFromFile, templatePath } from '../utils';
|
import { ElementFromHtml } from '../utils';
|
||||||
import { singleton } from '../../providers/decorators';
|
import { singleton } from '../../providers/decorators';
|
||||||
|
|
||||||
|
|
||||||
@ -7,7 +9,7 @@ function $<E extends Element = Element>(selector: string) {
|
|||||||
return document.querySelector<E>(selector);
|
return document.querySelector<E>(selector);
|
||||||
}
|
}
|
||||||
|
|
||||||
const slider = ElementFromFile(templatePath(__dirname, 'slider.html'));
|
const slider = ElementFromHtml(sliderHTML);
|
||||||
|
|
||||||
const roundToTwo = (n: number) => Math.round(n * 1e2) / 1e2;
|
const roundToTwo = (n: number) => Math.round(n * 1e2) / 1e2;
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import path from 'node:path';
|
|
||||||
|
|
||||||
import { globalShortcut, BrowserWindow } from 'electron';
|
import { globalShortcut, BrowserWindow } from 'electron';
|
||||||
|
|
||||||
|
import volumeHudStyle from './volume-hud.css';
|
||||||
|
|
||||||
import { injectCSS } from '../utils';
|
import { injectCSS } from '../utils';
|
||||||
|
|
||||||
import type { ConfigType } from '../../config/dynamic';
|
import type { ConfigType } from '../../config/dynamic';
|
||||||
@ -16,7 +16,7 @@ export const enabled = () => isEnabled;
|
|||||||
|
|
||||||
export default (win: BrowserWindow, options: ConfigType<'precise-volume'>) => {
|
export default (win: BrowserWindow, options: ConfigType<'precise-volume'>) => {
|
||||||
isEnabled = true;
|
isEnabled = true;
|
||||||
injectCSS(win.webContents, path.join(__dirname, 'volume-hud.css'));
|
injectCSS(win.webContents, volumeHudStyle);
|
||||||
|
|
||||||
if (options.globalShortcuts?.volumeUp) {
|
if (options.globalShortcuts?.volumeUp) {
|
||||||
globalShortcut.register((options.globalShortcuts.volumeUp), () => win.webContents.send('changeVolume', true));
|
globalShortcut.register((options.globalShortcuts.volumeUp), () => win.webContents.send('changeVolume', true));
|
||||||
|
|||||||
@ -1,15 +1,15 @@
|
|||||||
import { ipcRenderer } from 'electron';
|
import { ipcRenderer } from 'electron';
|
||||||
|
|
||||||
import { ElementFromFile, templatePath } from '../utils';
|
import qualitySettingsTemplate from './templates/qualitySettingsTemplate.html';
|
||||||
|
|
||||||
|
import { ElementFromHtml } from '../utils';
|
||||||
import { YoutubePlayer } from '../../types/youtube-player';
|
import { YoutubePlayer } from '../../types/youtube-player';
|
||||||
|
|
||||||
function $(selector: string): HTMLElement | null {
|
function $(selector: string): HTMLElement | null {
|
||||||
return document.querySelector(selector);
|
return document.querySelector(selector);
|
||||||
}
|
}
|
||||||
|
|
||||||
const qualitySettingsButton = ElementFromFile(
|
const qualitySettingsButton = ElementFromHtml(qualitySettingsTemplate);
|
||||||
templatePath(__dirname, 'qualitySettingsTemplate.html'),
|
|
||||||
);
|
|
||||||
|
|
||||||
function setup(event: CustomEvent<YoutubePlayer>) {
|
function setup(event: CustomEvent<YoutubePlayer>) {
|
||||||
const api = event.detail;
|
const api = event.detail;
|
||||||
|
|||||||
@ -49,18 +49,32 @@ export const fileExists = (
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const cssToInject = new Map();
|
const cssToInject = new Map<string, (() => void) | undefined>();
|
||||||
export const injectCSS = (webContents: Electron.WebContents, filepath: unknown, cb: (() => void) | undefined = undefined) => {
|
const cssToInjectFile = new Map<string, (() => void) | undefined>();
|
||||||
if (cssToInject.size === 0) {
|
export const injectCSS = (webContents: Electron.WebContents, css: string, cb: (() => void) | undefined = undefined) => {
|
||||||
|
if (cssToInject.size === 0 && cssToInjectFile.size === 0) {
|
||||||
setupCssInjection(webContents);
|
setupCssInjection(webContents);
|
||||||
}
|
}
|
||||||
|
|
||||||
cssToInject.set(filepath, cb);
|
cssToInject.set(css, cb);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const injectCSSAsFile = (webContents: Electron.WebContents, filepath: string, cb: (() => void) | undefined = undefined) => {
|
||||||
|
if (cssToInject.size === 0 && cssToInjectFile.size === 0) {
|
||||||
|
setupCssInjection(webContents);
|
||||||
|
}
|
||||||
|
|
||||||
|
cssToInjectFile.set(filepath, cb);
|
||||||
};
|
};
|
||||||
|
|
||||||
const setupCssInjection = (webContents: Electron.WebContents) => {
|
const setupCssInjection = (webContents: Electron.WebContents) => {
|
||||||
webContents.on('did-finish-load', () => {
|
webContents.on('did-finish-load', () => {
|
||||||
cssToInject.forEach(async (callback: () => void | undefined, filepath: fs.PathOrFileDescriptor) => {
|
cssToInject.forEach(async (callback, css) => {
|
||||||
|
await webContents.insertCSS(css);
|
||||||
|
callback?.();
|
||||||
|
});
|
||||||
|
|
||||||
|
cssToInjectFile.forEach(async (callback, filepath) => {
|
||||||
await webContents.insertCSS(fs.readFileSync(filepath, 'utf8'));
|
await webContents.insertCSS(fs.readFileSync(filepath, 'utf8'));
|
||||||
callback?.();
|
callback?.();
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,15 +1,16 @@
|
|||||||
import path from 'node:path';
|
|
||||||
|
|
||||||
import { BrowserWindow } from 'electron';
|
import { BrowserWindow } from 'electron';
|
||||||
|
|
||||||
|
import forceHideStyle from './force-hide.css';
|
||||||
|
import buttonSwitcherStyle from './button-switcher.css';
|
||||||
|
|
||||||
import { injectCSS } from '../utils';
|
import { injectCSS } from '../utils';
|
||||||
|
|
||||||
import type { ConfigType } from '../../config/dynamic';
|
import type { ConfigType } from '../../config/dynamic';
|
||||||
|
|
||||||
export default (win: BrowserWindow, options: ConfigType<'video-toggle'>) => {
|
export default (win: BrowserWindow, options: ConfigType<'video-toggle'>) => {
|
||||||
if (options.forceHide) {
|
if (options.forceHide) {
|
||||||
injectCSS(win.webContents, path.join(__dirname, 'force-hide.css'));
|
injectCSS(win.webContents, forceHideStyle);
|
||||||
} else if (!options.mode || options.mode === 'custom') {
|
} else if (!options.mode || options.mode === 'custom') {
|
||||||
injectCSS(win.webContents, path.join(__dirname, 'button-switcher.css'));
|
injectCSS(win.webContents, buttonSwitcherStyle);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,4 +1,6 @@
|
|||||||
import { ElementFromFile, templatePath } from '../utils';
|
import buttonTemplate from './templates/button_template.html';
|
||||||
|
|
||||||
|
import { ElementFromHtml } from '../utils';
|
||||||
import { setOptions, isEnabled } from '../../config/plugins';
|
import { setOptions, isEnabled } from '../../config/plugins';
|
||||||
|
|
||||||
import { moveVolumeHud as preciseVolumeMoveVolumeHud } from '../precise-volume/front';
|
import { moveVolumeHud as preciseVolumeMoveVolumeHud } from '../precise-volume/front';
|
||||||
@ -19,9 +21,7 @@ let player: HTMLElement & { videoMode_: boolean } | null;
|
|||||||
let video: HTMLVideoElement | null;
|
let video: HTMLVideoElement | null;
|
||||||
let api: YoutubePlayer;
|
let api: YoutubePlayer;
|
||||||
|
|
||||||
const switchButtonDiv = ElementFromFile(
|
const switchButtonDiv = ElementFromHtml(buttonTemplate);
|
||||||
templatePath(__dirname, 'button_template.html'),
|
|
||||||
);
|
|
||||||
|
|
||||||
export default (_options: ConfigType<'video-toggle'>) => {
|
export default (_options: ConfigType<'video-toggle'>) => {
|
||||||
if (_options.forceHide) {
|
if (_options.forceHide) {
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
import path from 'node:path';
|
|
||||||
|
|
||||||
import { BrowserWindow } from 'electron';
|
import { BrowserWindow } from 'electron';
|
||||||
|
|
||||||
|
import emptyPlayerStyle from './empty-player.css';
|
||||||
|
|
||||||
import { injectCSS } from '../utils';
|
import { injectCSS } from '../utils';
|
||||||
|
|
||||||
export default (win: BrowserWindow) => {
|
export default (win: BrowserWindow) => {
|
||||||
injectCSS(win.webContents, path.join(__dirname, 'empty-player.css'));
|
injectCSS(win.webContents, emptyPlayerStyle);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,9 +1,6 @@
|
|||||||
|
import { ButterchurnVisualizer as butterchurn, WaveVisualizer as wave, VudioVisualizer as vudio } from './visualizers';
|
||||||
import { Visualizer } from './visualizers/visualizer';
|
import { Visualizer } from './visualizers/visualizer';
|
||||||
|
|
||||||
import vudio from './visualizers/vudio';
|
|
||||||
import wave from './visualizers/wave';
|
|
||||||
import butterchurn from './visualizers/butterchurn';
|
|
||||||
|
|
||||||
import defaultConfig from '../../config/defaults';
|
import defaultConfig from '../../config/defaults';
|
||||||
|
|
||||||
import type { ConfigType } from '../../config/dynamic';
|
import type { ConfigType } from '../../config/dynamic';
|
||||||
|
|||||||
@ -1,17 +1,11 @@
|
|||||||
import { readdirSync } from 'node:fs';
|
|
||||||
import path from 'node:path';
|
|
||||||
|
|
||||||
import { BrowserWindow } from 'electron';
|
import { BrowserWindow } from 'electron';
|
||||||
|
|
||||||
import { setMenuOptions } from '../../config/plugins';
|
|
||||||
|
|
||||||
import { MenuTemplate } from '../../menu';
|
import { MenuTemplate } from '../../menu';
|
||||||
|
import { setMenuOptions } from '../../config/plugins';
|
||||||
|
|
||||||
import type { ConfigType } from '../../config/dynamic';
|
import type { ConfigType } from '../../config/dynamic';
|
||||||
|
|
||||||
const visualizerTypes = readdirSync(path.join(__dirname, 'visualizers'))
|
const visualizerTypes = ['butterchurn', 'vudio', 'wave']; // For bundling
|
||||||
.map((filename) => path.parse(filename).name)
|
|
||||||
.filter((filename) => filename !== 'visualizer');
|
|
||||||
|
|
||||||
export default (win: BrowserWindow, options: ConfigType<'visualizer'>): MenuTemplate => [
|
export default (win: BrowserWindow, options: ConfigType<'visualizer'>): MenuTemplate => [
|
||||||
{
|
{
|
||||||
|
|||||||
@ -8,6 +8,8 @@ import { ConfigType } from '../../../config/dynamic';
|
|||||||
const presets = ButterchurnPresets.getPresets();
|
const presets = ButterchurnPresets.getPresets();
|
||||||
|
|
||||||
class ButterchurnVisualizer extends Visualizer<Butterchurn> {
|
class ButterchurnVisualizer extends Visualizer<Butterchurn> {
|
||||||
|
name = 'butterchurn';
|
||||||
|
|
||||||
visualizer: ReturnType<typeof Butterchurn.createVisualizer>;
|
visualizer: ReturnType<typeof Butterchurn.createVisualizer>;
|
||||||
private readonly renderingFrequencyInMs: number;
|
private readonly renderingFrequencyInMs: number;
|
||||||
|
|
||||||
|
|||||||
5
plugins/visualizer/visualizers/index.ts
Normal file
5
plugins/visualizer/visualizers/index.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import ButterchurnVisualizer from './butterchurn';
|
||||||
|
import VudioVisualizer from './vudio';
|
||||||
|
import WaveVisualizer from './wave';
|
||||||
|
|
||||||
|
export { ButterchurnVisualizer, VudioVisualizer, WaveVisualizer };
|
||||||
@ -1,6 +1,10 @@
|
|||||||
import type { ConfigType } from '../../../config/dynamic';
|
import type { ConfigType } from '../../../config/dynamic';
|
||||||
|
|
||||||
export abstract class Visualizer<T> {
|
export abstract class Visualizer<T> {
|
||||||
|
/**
|
||||||
|
* The name must be the same as the file name.
|
||||||
|
*/
|
||||||
|
abstract name: string;
|
||||||
abstract visualizer: T;
|
abstract visualizer: T;
|
||||||
|
|
||||||
protected constructor(
|
protected constructor(
|
||||||
|
|||||||
@ -5,6 +5,8 @@ import { Visualizer } from './visualizer';
|
|||||||
import type { ConfigType } from '../../../config/dynamic';
|
import type { ConfigType } from '../../../config/dynamic';
|
||||||
|
|
||||||
class VudioVisualizer extends Visualizer<Vudio> {
|
class VudioVisualizer extends Visualizer<Vudio> {
|
||||||
|
name = 'vudio';
|
||||||
|
|
||||||
visualizer: Vudio;
|
visualizer: Vudio;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
|||||||
@ -5,6 +5,8 @@ import { Visualizer } from './visualizer';
|
|||||||
import type { ConfigType } from '../../../config/dynamic';
|
import type { ConfigType } from '../../../config/dynamic';
|
||||||
|
|
||||||
class WaveVisualizer extends Visualizer<Wave> {
|
class WaveVisualizer extends Visualizer<Wave> {
|
||||||
|
name = 'wave';
|
||||||
|
|
||||||
visualizer: Wave;
|
visualizer: Wave;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
|||||||
57
rollup.main.config.ts
Normal file
57
rollup.main.config.ts
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import { defineConfig } from 'rollup';
|
||||||
|
import builtinModules from 'builtin-modules';
|
||||||
|
import typescript from '@rollup/plugin-typescript';
|
||||||
|
import commonjs from '@rollup/plugin-commonjs';
|
||||||
|
import nodeResolvePlugin from '@rollup/plugin-node-resolve';
|
||||||
|
import json from '@rollup/plugin-json';
|
||||||
|
import terser from '@rollup/plugin-terser';
|
||||||
|
import { string } from 'rollup-plugin-string';
|
||||||
|
import css from 'rollup-plugin-import-css';
|
||||||
|
import wasmPlugin from '@rollup/plugin-wasm';
|
||||||
|
import copy from 'rollup-plugin-copy';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [
|
||||||
|
typescript({
|
||||||
|
module: 'ESNext',
|
||||||
|
}),
|
||||||
|
nodeResolvePlugin({
|
||||||
|
browser: false,
|
||||||
|
preferBuiltins: true,
|
||||||
|
exportConditions: ['node', 'default', 'module', 'import'] ,
|
||||||
|
}),
|
||||||
|
commonjs({
|
||||||
|
ignoreDynamicRequires: true,
|
||||||
|
}),
|
||||||
|
wasmPlugin({
|
||||||
|
maxFileSize: 0,
|
||||||
|
targetEnv: 'browser',
|
||||||
|
}),
|
||||||
|
json(),
|
||||||
|
string({
|
||||||
|
include: '**/*.html',
|
||||||
|
}),
|
||||||
|
css(),
|
||||||
|
copy({
|
||||||
|
targets: [
|
||||||
|
{ src: 'assets', dest: 'dist/' },
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
terser({
|
||||||
|
ecma: 2020,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
input: './index.ts',
|
||||||
|
output: {
|
||||||
|
format: 'cjs',
|
||||||
|
name: '[name].js',
|
||||||
|
dir: './dist',
|
||||||
|
},
|
||||||
|
external: [
|
||||||
|
'electron',
|
||||||
|
'sharp',
|
||||||
|
'custom-electron-prompt',
|
||||||
|
'youtubei.js', // https://github.com/LuanRT/YouTube.js/pull/509
|
||||||
|
...builtinModules,
|
||||||
|
],
|
||||||
|
});
|
||||||
51
rollup.preload.config.ts
Normal file
51
rollup.preload.config.ts
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import { defineConfig } from 'rollup';
|
||||||
|
import builtinModules from 'builtin-modules';
|
||||||
|
import typescript from '@rollup/plugin-typescript';
|
||||||
|
import commonjs from '@rollup/plugin-commonjs';
|
||||||
|
import nodeResolvePlugin from '@rollup/plugin-node-resolve';
|
||||||
|
import json from '@rollup/plugin-json';
|
||||||
|
import terser from '@rollup/plugin-terser';
|
||||||
|
import { string } from 'rollup-plugin-string';
|
||||||
|
import css from 'rollup-plugin-import-css';
|
||||||
|
import wasmPlugin from '@rollup/plugin-wasm';
|
||||||
|
import image from '@rollup/plugin-image';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [
|
||||||
|
typescript({
|
||||||
|
module: 'ESNext',
|
||||||
|
}),
|
||||||
|
nodeResolvePlugin({
|
||||||
|
browser: false,
|
||||||
|
preferBuiltins: true,
|
||||||
|
}),
|
||||||
|
commonjs({
|
||||||
|
ignoreDynamicRequires: true,
|
||||||
|
}),
|
||||||
|
json(),
|
||||||
|
string({
|
||||||
|
include: '**/*.html',
|
||||||
|
}),
|
||||||
|
css(),
|
||||||
|
wasmPlugin({
|
||||||
|
maxFileSize: 0,
|
||||||
|
targetEnv: 'browser',
|
||||||
|
}),
|
||||||
|
image({ dom: true }),
|
||||||
|
terser({
|
||||||
|
ecma: 2020,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
input: './preload.ts',
|
||||||
|
output: {
|
||||||
|
format: 'cjs',
|
||||||
|
name: '[name].js',
|
||||||
|
dir: './dist',
|
||||||
|
},
|
||||||
|
external: [
|
||||||
|
'electron',
|
||||||
|
'sharp',
|
||||||
|
'custom-electron-prompt',
|
||||||
|
...builtinModules,
|
||||||
|
],
|
||||||
|
});
|
||||||
@ -1,11 +1,11 @@
|
|||||||
const path = require('node:path');
|
import path from 'node:path';
|
||||||
|
import { fileURLToPath } from 'node:url';
|
||||||
const { _electron: electron } = require('playwright');
|
import { _electron as electron } from 'playwright';
|
||||||
const { test, expect } = require('@playwright/test');
|
import { expect, test } from '@playwright/test';
|
||||||
|
|
||||||
process.env.NODE_ENV = 'test';
|
process.env.NODE_ENV = 'test';
|
||||||
|
|
||||||
const appPath = path.resolve(__dirname, '..');
|
const appPath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
||||||
|
|
||||||
test('YouTube Music App - With default settings, app is launched and visible', async () => {
|
test('YouTube Music App - With default settings, app is launched and visible', async () => {
|
||||||
const app = await electron.launch({
|
const app = await electron.launch({
|
||||||
@ -16,6 +16,7 @@ test('YouTube Music App - With default settings, app is launched and visible', a
|
|||||||
'--disable-gpu',
|
'--disable-gpu',
|
||||||
'--whitelisted-ips=',
|
'--whitelisted-ips=',
|
||||||
'--disable-dev-shm-usage',
|
'--disable-dev-shm-usage',
|
||||||
|
'dist/index.js',
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -15,7 +15,6 @@
|
|||||||
"skipLibCheck": true
|
"skipLibCheck": true
|
||||||
},
|
},
|
||||||
"exclude": [
|
"exclude": [
|
||||||
"*.config.ts",
|
|
||||||
"./dist"
|
"./dist"
|
||||||
],
|
],
|
||||||
"paths": {
|
"paths": {
|
||||||
|
|||||||
37
youtube-music.d.ts
vendored
Normal file
37
youtube-music.d.ts
vendored
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
declare module '*.html' {
|
||||||
|
const html: string;
|
||||||
|
|
||||||
|
export default html;
|
||||||
|
}
|
||||||
|
declare module '*.svg' {
|
||||||
|
const element: SVGAElement;
|
||||||
|
|
||||||
|
export default element;
|
||||||
|
}
|
||||||
|
declare module '*.png' {
|
||||||
|
const element: HTMLImageElement;
|
||||||
|
|
||||||
|
export default element;
|
||||||
|
}
|
||||||
|
declare module '*.jpg' {
|
||||||
|
const element: HTMLImageElement;
|
||||||
|
|
||||||
|
export default element;
|
||||||
|
}
|
||||||
|
declare module '*.css' {
|
||||||
|
const css: string;
|
||||||
|
|
||||||
|
export default css;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module 'rollup-plugin-string' {
|
||||||
|
import type { Plugin } from 'rollup';
|
||||||
|
|
||||||
|
interface PluginOptions {
|
||||||
|
include?: string[] | string;
|
||||||
|
exclude?: string[] | string;
|
||||||
|
minifier?: unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function string(options?: PluginOptions): Plugin;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user