mirror of
https://github.com/th-ch/youtube-music.git
synced 2026-01-10 10:11:46 +00:00
feat: apply rollup 🚀 (#20)
Co-authored-by: Su-Yong <simssy2205@gmail.com>
This commit is contained in:
@ -1 +1,2 @@
|
||||
.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
|
||||
if: startsWith(matrix.os, 'macOS') && github.repository != 'organization/youtube-music-next'
|
||||
run: |
|
||||
npm run build:mac
|
||||
npm run dist:mac
|
||||
|
||||
- name: Build on Linux
|
||||
if: startsWith(matrix.os, 'ubuntu') && github.repository != 'organization/youtube-music-next'
|
||||
run: |
|
||||
npm run build:linux
|
||||
npm run dist:linux
|
||||
|
||||
- name: Build on Windows
|
||||
if: startsWith(matrix.os, 'windows') && github.repository != 'organization/youtube-music-next'
|
||||
run: |
|
||||
npm run build:win
|
||||
npm run dist:win
|
||||
|
||||
release:
|
||||
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 { setApplicationMenu } from './menu';
|
||||
import { fileExists, injectCSS } from './plugins/utils';
|
||||
import { fileExists, injectCSS, injectCSSAsFile } from './plugins/utils';
|
||||
import { isTesting } from './utils/testing';
|
||||
import { setUpTray } from './tray';
|
||||
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 visualizer from './plugins/visualizer/back';
|
||||
|
||||
import youtubeMusicCSS from './youtube-music.css';
|
||||
|
||||
// Catch errors and log them
|
||||
unhandled({
|
||||
logger: console.error,
|
||||
@ -139,7 +141,7 @@ if (is.windows()) {
|
||||
ipcMain.handle('get-main-plugin-names', () => Object.keys(mainPlugins));
|
||||
|
||||
function loadPlugins(win: BrowserWindow) {
|
||||
injectCSS(win.webContents, path.join(__dirname, 'youtube-music.css'));
|
||||
injectCSS(win.webContents, youtubeMusicCSS);
|
||||
// Load user CSS
|
||||
const themes: string[] = config.get('options.themes');
|
||||
if (Array.isArray(themes)) {
|
||||
@ -147,7 +149,7 @@ function loadPlugins(win: BrowserWindow) {
|
||||
fileExists(
|
||||
cssFile,
|
||||
() => {
|
||||
injectCSS(win.webContents, cssFile);
|
||||
injectCSSAsFile(win.webContents, cssFile);
|
||||
},
|
||||
() => {
|
||||
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',
|
||||
type: 'radio',
|
||||
checked: config.get('options.themes').length === 0, // Todo rename "themes"
|
||||
checked: config.get('options.themes')?.length === 0, // Todo rename "themes"
|
||||
click() {
|
||||
config.set('options.themes', []);
|
||||
},
|
||||
|
||||
2588
package-lock.json
generated
2588
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
119
package.json
119
package.json
@ -14,20 +14,38 @@
|
||||
"build": {
|
||||
"appId": "com.github.th-ch.youtube-music",
|
||||
"productName": "YouTube Music",
|
||||
"files": [
|
||||
"!*",
|
||||
"dist",
|
||||
"license",
|
||||
"!node_modules",
|
||||
"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/**/*.ts"
|
||||
],
|
||||
"mac": {
|
||||
"identity": null,
|
||||
"files": [
|
||||
"!*",
|
||||
"dist",
|
||||
"!dist/plugins/taskbar-mediacontrol${/*}",
|
||||
"license",
|
||||
"node_modules",
|
||||
"!node_modules/sharp/vendor",
|
||||
"!node_modules/**/*.map",
|
||||
"!node_modules/**/*.ts",
|
||||
"package.json",
|
||||
"tests"
|
||||
],
|
||||
"target": [
|
||||
{
|
||||
"target": "dmg",
|
||||
@ -41,18 +59,6 @@
|
||||
},
|
||||
"win": {
|
||||
"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": "nsis",
|
||||
@ -75,18 +81,6 @@
|
||||
},
|
||||
"linux": {
|
||||
"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",
|
||||
"target": [
|
||||
"AppImage",
|
||||
@ -110,31 +104,29 @@
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"test": "playwright test",
|
||||
"test:debug": "DEBUG=pw:browser* playwright test",
|
||||
"start": "npm run tsc-and-copy && electron ./dist/index.js",
|
||||
"start:debug": "ELECTRON_ENABLE_LOGGING=1 electron ./dist/index.js",
|
||||
"test": "npm run build && playwright test",
|
||||
"test:debug": "DEBUG=pw:browser* npm run build && playwright test",
|
||||
"rollup:preload": "rollup -c rollup.preload.config.ts --configPlugin @rollup/plugin-typescript --bundleConfigAsCjs",
|
||||
"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",
|
||||
"postinstall": "npm run plugins",
|
||||
"postinstall": "npm run plugins && npm run clean",
|
||||
"clean": "del-cli dist && del-cli pack",
|
||||
"ytm-resource-copy-files": "copyfiles error.html youtube-music.css assets/**/* dist/",
|
||||
"copy-files": "copyfiles -u 1 plugins/**/*.html plugins/**/*.css plugins/**/*.bin plugins/**/*.js dist/plugins/",
|
||||
"tsc-and-copy": "tsc && npm run plugin:adblocker-without-tsc && npm run ytm-resource-copy-files && npm run copy-files",
|
||||
"build": "npm run clean && npm run tsc-and-copy && electron-builder --win --mac --linux -p never",
|
||||
"build:linux": "npm run clean && npm run tsc-and-copy && electron-builder --linux -p never",
|
||||
"build:mac": "npm run clean && npm run tsc-and-copy && electron-builder --mac dmg: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",
|
||||
"dist": "npm run clean && npm run build && electron-builder --win --mac --linux -p never",
|
||||
"dist:linux": "npm run clean && npm run build && electron-builder --linux -p never",
|
||||
"dist:mac": "npm run clean && npm run build && electron-builder --mac dmg:x64 -p never",
|
||||
"dist:mac:arm64": "npm run clean && npm run build && electron-builder --mac dmg:arm64 -p never",
|
||||
"dist:win": "npm run clean && npm run build && electron-builder --win -p never",
|
||||
"dist:win:x64": "npm run clean && npm run build && electron-builder --win nsis:x64 -p never",
|
||||
"lint": "eslint .",
|
||||
"changelog": "auto-changelog",
|
||||
"plugins": "npm run plugin:adblocker && 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",
|
||||
"plugins": "npm run plugin:bypass-age-restrictions",
|
||||
"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:mac": "npm run clean && npm run tsc-and-copy && electron-builder --mac -p always",
|
||||
"release:win": "npm run clean && npm run tsc-and-copy && electron-builder --win -p always",
|
||||
"release:linux": "npm run clean && npm run build && electron-builder --linux -p always -c.snap.publish=github",
|
||||
"release:mac": "npm run clean && npm run build && electron-builder --mac -p always",
|
||||
"release:win": "npm run clean && npm run build && electron-builder --win -p always",
|
||||
"typecheck": "tsc -p tsconfig.json --noEmit"
|
||||
},
|
||||
"engines": {
|
||||
@ -168,25 +160,34 @@
|
||||
"node-id3": "0.2.6",
|
||||
"simple-youtube-age-restriction-bypass": "git+https://github.com/MiepHD/Simple-YouTube-Age-Restriction-Bypass.git#v2.5.5",
|
||||
"vudio": "2.1.1",
|
||||
"x11": "2.3.0",
|
||||
"youtubei.js": "6.4.1",
|
||||
"ytpl": "2.3.0"
|
||||
},
|
||||
"overrides": {
|
||||
"node-gyp": "9.4.0",
|
||||
"xml2js": "0.6.2",
|
||||
"sharp": "0.32.6",
|
||||
"dbus-next": "0.10.2",
|
||||
"node-fetch": "2.7.0",
|
||||
"@electron/universal": "1.4.2",
|
||||
"electron": "27.0.0-beta.9"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@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",
|
||||
"@types/electron-localshortcut": "3.1.1",
|
||||
"@types/howler": "2.2.9",
|
||||
"@types/html-to-text": "9.0.2",
|
||||
"@typescript-eslint/eslint-plugin": "6.7.4",
|
||||
"auto-changelog": "2.4.0",
|
||||
"copyfiles": "2.4.1",
|
||||
"del-cli": "5.1.0",
|
||||
"electron": "27.0.0-beta.9",
|
||||
"electron-builder": "24.6.4",
|
||||
@ -196,6 +197,10 @@
|
||||
"eslint-plugin-prettier": "5.0.0",
|
||||
"node-gyp": "9.4.0",
|
||||
"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"
|
||||
},
|
||||
"auto-changelog": {
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
// Used for caching
|
||||
import path from 'node:path';
|
||||
import { promises } from 'node:fs';
|
||||
import fs, { promises } from 'node:fs';
|
||||
|
||||
import { ElectronBlocker } from '@cliqz/adblocker-electron';
|
||||
import { app } from 'electron';
|
||||
|
||||
const SOURCES = [
|
||||
'https://raw.githubusercontent.com/kbinani/adblock-youtube-ads/master/signed.txt',
|
||||
@ -23,10 +24,19 @@ export const loadAdBlockerEngine = (
|
||||
disableDefaultLists: boolean | string[] = false,
|
||||
) => {
|
||||
// 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
|
||||
= cache && additionalBlockLists.length === 0
|
||||
? {
|
||||
path: path.resolve(__dirname, 'ad-blocker-engine.bin'),
|
||||
path: path.join(cacheDirectory, 'adblocker-engine.bin'),
|
||||
read: promises.readFile,
|
||||
write: promises.writeFile,
|
||||
}
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
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 { BrowserWindow } from 'electron';
|
||||
|
||||
import style from './style.css';
|
||||
|
||||
import { injectCSS } from '../utils';
|
||||
import registerCallback from '../../providers/song-info';
|
||||
|
||||
|
||||
export default (win: BrowserWindow) => {
|
||||
injectCSS(win.webContents, path.join(__dirname, 'style.css'));
|
||||
injectCSS(win.webContents, style);
|
||||
|
||||
registerCallback((songInfo) => {
|
||||
const songTitle = songInfo.title;
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import path from 'node:path';
|
||||
|
||||
import { BrowserWindow } from 'electron';
|
||||
|
||||
import style from './style.css';
|
||||
|
||||
import { injectCSS } from '../utils';
|
||||
|
||||
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 { ElementFromFile, templatePath } from '../utils';
|
||||
import CaptionsSettingsButtonHTML from './templates/captions-settings-template.html';
|
||||
|
||||
import { ElementFromHtml } from '../utils';
|
||||
import { YoutubePlayer } from '../../types/youtube-player';
|
||||
|
||||
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 captionsSettingsButton = ElementFromFile(
|
||||
templatePath(__dirname, 'captions-settings-template.html'),
|
||||
);
|
||||
const captionsSettingsButton = ElementFromHtml(CaptionsSettingsButtonHTML);
|
||||
|
||||
export default async () => {
|
||||
// RENDERER
|
||||
|
||||
@ -24,6 +24,8 @@ import { cropMaxWidth, getFolder, presets, sendFeedback as sendFeedback_, setBad
|
||||
|
||||
import config from './config';
|
||||
|
||||
import style from './style.css';
|
||||
|
||||
import { fetchFromGenius } from '../lyrics-genius/back';
|
||||
import { isEnabled } from '../../config/plugins';
|
||||
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';
|
||||
|
||||
|
||||
type CustomSongInfo = SongInfo & { trackId?: string };
|
||||
|
||||
const ffmpeg = createFFmpeg({
|
||||
@ -68,7 +71,7 @@ const sendError = (error: Error, source?: string) => {
|
||||
|
||||
export default async (win_: BrowserWindow) => {
|
||||
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) =>
|
||||
it.name + '=' + it.value + ';'
|
||||
@ -104,6 +107,7 @@ export async function downloadSong(
|
||||
increasePlaylistProgress,
|
||||
);
|
||||
} catch (error: unknown) {
|
||||
console.log('maybe?????', error);
|
||||
sendError(error as Error, resolvedName || url);
|
||||
}
|
||||
}
|
||||
@ -316,6 +320,7 @@ async function iterableStreamToMP3(
|
||||
ffmpeg.FS('unlink', `${safeVideoName}.mp3`);
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
console.log('maybe?', error);
|
||||
sendError(error as Error, safeVideoName);
|
||||
} finally {
|
||||
releaseFFmpegMutex();
|
||||
@ -368,6 +373,7 @@ async function writeID3(buffer: Buffer, metadata: CustomSongInfo, sendFeedback:
|
||||
|
||||
return NodeID3.write(tags, buffer);
|
||||
} catch (error: unknown) {
|
||||
console.log('fallback', error);
|
||||
sendError(error as Error, `${metadata.artist} - ${metadata.title}`);
|
||||
return null;
|
||||
}
|
||||
@ -482,6 +488,7 @@ export async function downloadPlaylist(givenUrl?: string | URL) {
|
||||
counter++;
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
console.log('also?', error);
|
||||
sendError(error as Error);
|
||||
} finally {
|
||||
win.setProgressBar(-1); // Close progress bar
|
||||
@ -507,6 +514,7 @@ async function ffmpegWriteTags(filePath: string, metadata: CustomSongInfo, prese
|
||||
filePath,
|
||||
);
|
||||
} catch (error: unknown) {
|
||||
console.log('ffmpeg?', error);
|
||||
sendError(error as Error);
|
||||
} finally {
|
||||
releaseFFmpegMutex();
|
||||
|
||||
@ -1,15 +1,15 @@
|
||||
import { ipcRenderer } from 'electron';
|
||||
|
||||
import downloadHTML from './templates/download.html';
|
||||
|
||||
import defaultConfig from '../../config/defaults';
|
||||
import { getSongMenu } from '../../providers/dom-elements';
|
||||
import { ElementFromFile, templatePath } from '../utils';
|
||||
import { ElementFromHtml } from '../utils';
|
||||
import { getSongInfo } from '../../providers/song-info-front';
|
||||
|
||||
let menu: Element | null = null;
|
||||
let progress: Element | null = null;
|
||||
const downloadButton = ElementFromFile(
|
||||
templatePath(__dirname, 'download.html'),
|
||||
);
|
||||
const downloadButton = ElementFromHtml(downloadHTML);
|
||||
|
||||
let doneFirstLoad = false;
|
||||
|
||||
|
||||
@ -4,11 +4,13 @@ import { register } from 'electron-localshortcut';
|
||||
|
||||
import { BrowserWindow, Menu, MenuItem, ipcMain } from 'electron';
|
||||
|
||||
import titlebarStyle from './titlebar.css';
|
||||
|
||||
import { injectCSS } from '../utils';
|
||||
|
||||
// Tracks menu visibility
|
||||
export default (win: BrowserWindow) => {
|
||||
injectCSS(win.webContents, path.join(__dirname, 'titlebar.css'));
|
||||
injectCSS(win.webContents, titlebarStyle);
|
||||
|
||||
win.once('ready-to-show', () => {
|
||||
register(win, '`', () => {
|
||||
|
||||
@ -1,10 +1,8 @@
|
||||
import path from 'node:path';
|
||||
|
||||
import { ipcRenderer, Menu } from 'electron';
|
||||
|
||||
import { createPanel } from './menu/panel';
|
||||
|
||||
import { ElementFromFile } from '../utils';
|
||||
import logo from '../../assets/youtube-music.svg';
|
||||
import { isEnabled } from '../../config/plugins';
|
||||
|
||||
function $<E extends Element = Element>(selector: string) {
|
||||
@ -18,7 +16,6 @@ export default () => {
|
||||
const navBar = document.querySelector<HTMLDivElement>('#nav-bar-background');
|
||||
if (isMacOS) titleBar.style.setProperty('--offset-left', '70px');
|
||||
|
||||
const logo = ElementFromFile(path.join(__dirname, '..' , '..' , 'assets', 'youtube-music.svg'));
|
||||
logo.classList.add('title-bar-icon');
|
||||
|
||||
if (!isMacOS) titleBar.appendChild(logo);
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
import { join } from 'node:path';
|
||||
|
||||
import { BrowserWindow, ipcMain, net } from 'electron';
|
||||
import is from 'electron-is';
|
||||
import { convert } from 'html-to-text';
|
||||
|
||||
import style from './style.css';
|
||||
import { GetGeniusLyric } from './types';
|
||||
|
||||
import { cleanupName, SongInfo } from '../../providers/song-info';
|
||||
@ -22,7 +21,7 @@ export default (win: BrowserWindow, options: LyricGeniusType) => {
|
||||
revRomanized = true;
|
||||
}
|
||||
|
||||
injectCSS(win.webContents, join(__dirname, 'style.css'));
|
||||
injectCSS(win.webContents, style);
|
||||
|
||||
ipcMain.handle('search-genius-lyrics', async (_, extractedSongInfo: SongInfo) => {
|
||||
const metadata = extractedSongInfo;
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
import path from 'node:path';
|
||||
|
||||
import { BrowserWindow } from 'electron';
|
||||
|
||||
import style from './style.css';
|
||||
|
||||
import { injectCSS } from '../utils';
|
||||
|
||||
export function handle(win: BrowserWindow) {
|
||||
injectCSS(win.webContents, path.join(__dirname, 'style.css'), () => {
|
||||
injectCSS(win.webContents, style, () => {
|
||||
win.webContents.send('navigation-css-ready');
|
||||
});
|
||||
}
|
||||
|
||||
@ -1,13 +1,14 @@
|
||||
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() {
|
||||
ipcRenderer.on('navigation-css-ready', () => {
|
||||
const forwardButton = ElementFromFile(
|
||||
templatePath(__dirname, 'forward.html'),
|
||||
);
|
||||
const backButton = ElementFromFile(templatePath(__dirname, 'back.html'));
|
||||
const forwardButton = ElementFromHtml(forwardHTML);
|
||||
const backButton = ElementFromHtml(backHTML);
|
||||
const menu = document.querySelector('#right-content');
|
||||
|
||||
if (menu) {
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import path from 'node:path';
|
||||
|
||||
import { BrowserWindow } from 'electron';
|
||||
|
||||
import style from './style.css';
|
||||
|
||||
import { injectCSS } from '../utils';
|
||||
|
||||
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
|
||||
? 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) => {
|
||||
if (config.get('toastStyle') === ToastStyles.legacy) {
|
||||
|
||||
@ -88,7 +88,7 @@ export const saveTempIcon = () => {
|
||||
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.copyFile(iconPath, destinationPath, () => {
|
||||
});
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import path from 'node:path';
|
||||
|
||||
import { app, BrowserWindow, ipcMain } from 'electron';
|
||||
|
||||
import { setOptions as setPluginOptions } from '../../config/plugins';
|
||||
import style from './style.css';
|
||||
|
||||
import { injectCSS } from '../utils';
|
||||
import { setOptions as setPluginOptions } from '../../config/plugins';
|
||||
|
||||
import type { ConfigType } from '../../config/dynamic';
|
||||
|
||||
@ -102,7 +102,7 @@ export default (_win: BrowserWindow, _options: PiPOptions) => {
|
||||
options ??= _options;
|
||||
win ??= _win;
|
||||
setLocalOptions({ isInPiP });
|
||||
injectCSS(win.webContents, path.join(__dirname, 'style.css'));
|
||||
injectCSS(win.webContents, style);
|
||||
ipcMain.on('picture-in-picture', () => {
|
||||
togglePiP();
|
||||
});
|
||||
|
||||
@ -2,9 +2,11 @@ import { ipcRenderer } from 'electron';
|
||||
import { toKeyEvent } from 'keyboardevent-from-electron-accelerator';
|
||||
import keyEventAreEqual from 'keyboardevents-areequal';
|
||||
|
||||
import pipHTML from './templates/picture-in-picture.html';
|
||||
|
||||
import { getSongMenu } from '../../providers/dom-elements';
|
||||
|
||||
import { ElementFromFile, templatePath } from '../utils';
|
||||
import { ElementFromHtml } from '../utils';
|
||||
|
||||
import type { ConfigType } from '../../config/dynamic';
|
||||
|
||||
@ -16,9 +18,7 @@ function $<E extends Element = Element>(selector: string) {
|
||||
|
||||
let useNativePiP = false;
|
||||
let menu: Element | null = null;
|
||||
const pipButton = ElementFromFile(
|
||||
templatePath(__dirname, 'picture-in-picture.html'),
|
||||
);
|
||||
const pipButton = ElementFromHtml(pipHTML);
|
||||
|
||||
// Will also clone
|
||||
function replaceButton(query: string, button: Element) {
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
import sliderHTML from './templates/slider.html';
|
||||
|
||||
import { getSongMenu } from '../../providers/dom-elements';
|
||||
import { ElementFromFile, templatePath } from '../utils';
|
||||
import { ElementFromHtml } from '../utils';
|
||||
import { singleton } from '../../providers/decorators';
|
||||
|
||||
|
||||
@ -7,7 +9,7 @@ function $<E extends Element = Element>(selector: string) {
|
||||
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;
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import path from 'node:path';
|
||||
|
||||
import { globalShortcut, BrowserWindow } from 'electron';
|
||||
|
||||
import volumeHudStyle from './volume-hud.css';
|
||||
|
||||
import { injectCSS } from '../utils';
|
||||
|
||||
import type { ConfigType } from '../../config/dynamic';
|
||||
@ -16,7 +16,7 @@ export const enabled = () => isEnabled;
|
||||
|
||||
export default (win: BrowserWindow, options: ConfigType<'precise-volume'>) => {
|
||||
isEnabled = true;
|
||||
injectCSS(win.webContents, path.join(__dirname, 'volume-hud.css'));
|
||||
injectCSS(win.webContents, volumeHudStyle);
|
||||
|
||||
if (options.globalShortcuts?.volumeUp) {
|
||||
globalShortcut.register((options.globalShortcuts.volumeUp), () => win.webContents.send('changeVolume', true));
|
||||
|
||||
@ -1,15 +1,15 @@
|
||||
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';
|
||||
|
||||
function $(selector: string): HTMLElement | null {
|
||||
return document.querySelector(selector);
|
||||
}
|
||||
|
||||
const qualitySettingsButton = ElementFromFile(
|
||||
templatePath(__dirname, 'qualitySettingsTemplate.html'),
|
||||
);
|
||||
const qualitySettingsButton = ElementFromHtml(qualitySettingsTemplate);
|
||||
|
||||
function setup(event: CustomEvent<YoutubePlayer>) {
|
||||
const api = event.detail;
|
||||
|
||||
@ -49,18 +49,32 @@ export const fileExists = (
|
||||
});
|
||||
};
|
||||
|
||||
const cssToInject = new Map();
|
||||
export const injectCSS = (webContents: Electron.WebContents, filepath: unknown, cb: (() => void) | undefined = undefined) => {
|
||||
if (cssToInject.size === 0) {
|
||||
const cssToInject = new Map<string, (() => void) | undefined>();
|
||||
const cssToInjectFile = new Map<string, (() => void) | undefined>();
|
||||
export const injectCSS = (webContents: Electron.WebContents, css: string, cb: (() => void) | undefined = undefined) => {
|
||||
if (cssToInject.size === 0 && cssToInjectFile.size === 0) {
|
||||
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) => {
|
||||
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'));
|
||||
callback?.();
|
||||
});
|
||||
|
||||
@ -1,15 +1,16 @@
|
||||
import path from 'node:path';
|
||||
|
||||
import { BrowserWindow } from 'electron';
|
||||
|
||||
import forceHideStyle from './force-hide.css';
|
||||
import buttonSwitcherStyle from './button-switcher.css';
|
||||
|
||||
import { injectCSS } from '../utils';
|
||||
|
||||
import type { ConfigType } from '../../config/dynamic';
|
||||
|
||||
export default (win: BrowserWindow, options: ConfigType<'video-toggle'>) => {
|
||||
if (options.forceHide) {
|
||||
injectCSS(win.webContents, path.join(__dirname, 'force-hide.css'));
|
||||
injectCSS(win.webContents, forceHideStyle);
|
||||
} 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 { moveVolumeHud as preciseVolumeMoveVolumeHud } from '../precise-volume/front';
|
||||
@ -19,9 +21,7 @@ let player: HTMLElement & { videoMode_: boolean } | null;
|
||||
let video: HTMLVideoElement | null;
|
||||
let api: YoutubePlayer;
|
||||
|
||||
const switchButtonDiv = ElementFromFile(
|
||||
templatePath(__dirname, 'button_template.html'),
|
||||
);
|
||||
const switchButtonDiv = ElementFromHtml(buttonTemplate);
|
||||
|
||||
export default (_options: ConfigType<'video-toggle'>) => {
|
||||
if (_options.forceHide) {
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import path from 'node:path';
|
||||
|
||||
import { BrowserWindow } from 'electron';
|
||||
|
||||
import emptyPlayerStyle from './empty-player.css';
|
||||
|
||||
import { injectCSS } from '../utils';
|
||||
|
||||
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 vudio from './visualizers/vudio';
|
||||
import wave from './visualizers/wave';
|
||||
import butterchurn from './visualizers/butterchurn';
|
||||
|
||||
import defaultConfig from '../../config/defaults';
|
||||
|
||||
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 { setMenuOptions } from '../../config/plugins';
|
||||
|
||||
import { MenuTemplate } from '../../menu';
|
||||
import { setMenuOptions } from '../../config/plugins';
|
||||
|
||||
import type { ConfigType } from '../../config/dynamic';
|
||||
|
||||
const visualizerTypes = readdirSync(path.join(__dirname, 'visualizers'))
|
||||
.map((filename) => path.parse(filename).name)
|
||||
.filter((filename) => filename !== 'visualizer');
|
||||
const visualizerTypes = ['butterchurn', 'vudio', 'wave']; // For bundling
|
||||
|
||||
export default (win: BrowserWindow, options: ConfigType<'visualizer'>): MenuTemplate => [
|
||||
{
|
||||
|
||||
@ -8,6 +8,8 @@ import { ConfigType } from '../../../config/dynamic';
|
||||
const presets = ButterchurnPresets.getPresets();
|
||||
|
||||
class ButterchurnVisualizer extends Visualizer<Butterchurn> {
|
||||
name = 'butterchurn';
|
||||
|
||||
visualizer: ReturnType<typeof Butterchurn.createVisualizer>;
|
||||
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';
|
||||
|
||||
export abstract class Visualizer<T> {
|
||||
/**
|
||||
* The name must be the same as the file name.
|
||||
*/
|
||||
abstract name: string;
|
||||
abstract visualizer: T;
|
||||
|
||||
protected constructor(
|
||||
|
||||
@ -5,6 +5,8 @@ import { Visualizer } from './visualizer';
|
||||
import type { ConfigType } from '../../../config/dynamic';
|
||||
|
||||
class VudioVisualizer extends Visualizer<Vudio> {
|
||||
name = 'vudio';
|
||||
|
||||
visualizer: Vudio;
|
||||
|
||||
constructor(
|
||||
|
||||
@ -5,6 +5,8 @@ import { Visualizer } from './visualizer';
|
||||
import type { ConfigType } from '../../../config/dynamic';
|
||||
|
||||
class WaveVisualizer extends Visualizer<Wave> {
|
||||
name = 'wave';
|
||||
|
||||
visualizer: Wave;
|
||||
|
||||
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');
|
||||
|
||||
const { _electron: electron } = require('playwright');
|
||||
const { test, expect } = require('@playwright/test');
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { _electron as electron } from 'playwright';
|
||||
import { expect, test } from '@playwright/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 () => {
|
||||
const app = await electron.launch({
|
||||
@ -16,6 +16,7 @@ test('YouTube Music App - With default settings, app is launched and visible', a
|
||||
'--disable-gpu',
|
||||
'--whitelisted-ips=',
|
||||
'--disable-dev-shm-usage',
|
||||
'dist/index.js',
|
||||
],
|
||||
});
|
||||
|
||||
@ -15,7 +15,6 @@
|
||||
"skipLibCheck": true
|
||||
},
|
||||
"exclude": [
|
||||
"*.config.ts",
|
||||
"./dist"
|
||||
],
|
||||
"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