Compare commits

..

24 Commits

Author SHA1 Message Date
9e97699e6c fix: seek 2023-11-03 10:09:40 +09:00
a810621762 feat: http api 2023-11-03 10:09:40 +09:00
a41db79c35 fix(deps): fix pnpm.overrides 2023-11-03 10:09:04 +09:00
87786d9aef chore(deps): update dependency node-gyp to v10.0.1 2023-11-03 09:57:24 +09:00
22f5866050 chore(deps): update dependency @electron/universal to v1.4.4 2023-11-03 09:57:12 +09:00
04894fbcf5 chore(deps): update dependency node-gyp to v10 2023-11-02 12:51:52 +09:00
c17c624ba4 chore(deps): bump deps 2023-11-02 12:48:44 +09:00
bfe7249df8 Add Homebrew cask install option for MacOS. (#1357) 2023-11-02 09:42:13 +09:00
13c570efe9 fix: use node-fetch v3 instead of v2
- auto-change: moved from devDependencies to npx
2023-10-29 06:17:33 +09:00
b299846f0f fix: fix node-gyp version 2023-10-29 06:09:47 +09:00
59e9289d27 remove: remove patch-package
- see https://github.com/LuanRT/YouTube.js/pull/509
2023-10-29 06:05:15 +09:00
8dc29caa1b chore(actions): update setup-node v3 to v4 2023-10-29 06:02:03 +09:00
7fedf88654 chore(deps): bump deps version
- rollup: 4.1.4 to 4.1.5
- node-gyp: 9.4.0 to 9.4.1
- @cliqz/adblocker: 1.26.8 to 1.26.9
- youtubei.js: 6.4.1 to 7.0.0
- pnpm: 8.9.2 to 8.10.0
2023-10-29 05:54:43 +09:00
5da0202425 Update changelog for v2.2.0 2023-10-26 17:16:26 +00:00
6288d0b171 Bump version to 2.2.0 2023-10-27 02:01:44 +09:00
4248d20e8e bump deps 2023-10-25 16:46:59 +09:00
0b413492ad feat(ambient-mode): add config for ambient-mode plugin (#1349) 2023-10-25 15:37:51 +09:00
dc73561c8a Update changelog for v2.1.3 2023-10-22 15:50:12 +00:00
949a2f6428 Bump version to 2.1.3 2023-10-23 00:35:10 +09:00
bceaa05197 fix(store): fix listenAlong statement 2023-10-23 00:29:59 +09:00
776cdac30d feat(discord): rename Listen Along to Play on YTM
resolve #1341
2023-10-23 00:27:43 +09:00
4333891cca chore(deps): bump deps 2023-10-23 00:19:42 +09:00
8a89bbccf7 fix: fixed bugs in downloader (#1342) 2023-10-23 00:19:01 +09:00
fa4c69d228 Update changelog for v2.1.2 2023-10-19 13:55:04 +00:00
21 changed files with 765 additions and 561 deletions

View File

@ -28,14 +28,14 @@ jobs:
- name: Setup NodeJS
if: startsWith(matrix.os, 'macOS') != true
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'pnpm'
- name: Setup NodeJS for macOS
if: startsWith(matrix.os, 'macOS')
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
@ -96,14 +96,14 @@ jobs:
- name: Setup NodeJS
if: startsWith(matrix.os, 'macOS') != true
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'pnpm'
- name: Setup NodeJS for macOS
if: startsWith(matrix.os, 'macOS')
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}

View File

@ -2,8 +2,45 @@
All notable changes to this project will be documented in this file. Dates are displayed in UTC.
#### [v2.2.0](https://github.com/th-ch/youtube-music/compare/v2.1.3...v2.2.0)
- feat(ambient-mode): add config for `ambient-mode` plugin [`#1349`](https://github.com/th-ch/youtube-music/pull/1349)
- bump deps [`4248d20`](https://github.com/th-ch/youtube-music/commit/4248d20e8ef926ce7b1d07eb83743755a341d9f6)
- Update changelog for v2.1.3 [`dc73561`](https://github.com/th-ch/youtube-music/commit/dc73561c8a8acfc8ba91aff2dc78e4267869f2fd)
- Bump version to 2.2.0 [`6288d0b`](https://github.com/th-ch/youtube-music/commit/6288d0b171a65ea015922cdf3af6c7bd9a1f269b)
#### [v2.1.3](https://github.com/th-ch/youtube-music/compare/v2.1.2...v2.1.3)
> 23 October 2023
- fix: fixed bugs in downloader [`#1342`](https://github.com/th-ch/youtube-music/pull/1342)
- feat(discord): rename `Listen Along` to `Play on YTM` [`#1341`](https://github.com/th-ch/youtube-music/issues/1341)
- chore(deps): bump deps [`4333891`](https://github.com/th-ch/youtube-music/commit/4333891ccabe42aedf756fd48618be715db13262)
- Update changelog for v2.1.2 [`fa4c69d`](https://github.com/th-ch/youtube-music/commit/fa4c69d228d4e06a7858e2b22fcdfa075a8ca766)
- fix(store): fix listenAlong statement [`bceaa05`](https://github.com/th-ch/youtube-music/commit/bceaa05197d47a4a4bbd22e767d1e4d6ec277514)
#### [v2.1.2](https://github.com/th-ch/youtube-music/compare/v2.1.1...v2.1.2)
> 19 October 2023
- feat(in-app-menu): add an option to hide the window controls [`#1335`](https://github.com/th-ch/youtube-music/pull/1335)
- fix: fixed an issue where the album name was missing [`#1334`](https://github.com/th-ch/youtube-music/pull/1334)
- chore(deps): update dependency electron to v27.0.1 [`#1331`](https://github.com/th-ch/youtube-music/pull/1331)
- fix: fixed an issue where only the first 100 songs in a playlist were downloaded [`#1329`](https://github.com/th-ch/youtube-music/pull/1329)
- Updated readme plugins list [`#1326`](https://github.com/th-ch/youtube-music/pull/1326)
- QOL: Move source code under the src directory. [`#1318`](https://github.com/th-ch/youtube-music/pull/1318)
- feat: migrate from `npm` to `pnpm` [`#1316`](https://github.com/th-ch/youtube-music/pull/1316)
- fix: fix unresponsive (fix #1325) [`#1325`](https://github.com/th-ch/youtube-music/issues/1325)
- fix(blocker): remove the `app.isPackaged` check (fix #1315) [`#1315`](https://github.com/th-ch/youtube-music/issues/1315)
- fix(discord): `Discord RPC fails if a song's title is only one character` (fix #1314) [`#1314`](https://github.com/th-ch/youtube-music/issues/1314)
- chore(deps): Bump @rollup/plugin-commonjs, pnpm version, Remove ytpl [`9705f84`](https://github.com/th-ch/youtube-music/commit/9705f8489d7bf262bfd8b15ab84c2d3485f10eae)
- chore(deps): Bump rollup, @xhayper/discord-rpc version [`00a3e8d`](https://github.com/th-ch/youtube-music/commit/00a3e8d35ec335e1913be19f30ae09dbe0b7acdd)
- chore(deps): update dependency rollup to v4.1.4 [`6774d54`](https://github.com/th-ch/youtube-music/commit/6774d54f5eca432edc2e11743d9d1b1c2fda9ac8)
#### [v2.1.1](https://github.com/th-ch/youtube-music/compare/v2.1.0...v2.1.1)
> 14 October 2023
- hotfix(downloader): can't get an album title (fix #1313) [`#1313`](https://github.com/th-ch/youtube-music/issues/1313)
- Update changelog for v2.1.0 [`92cab89`](https://github.com/th-ch/youtube-music/commit/92cab89d17175741e60e65ea61633e23ebdc1f45)
- Bump version to 2.1.1 [`3bb5bc2`](https://github.com/th-ch/youtube-music/commit/3bb5bc2ca1856f4e222ee1e01e865f1ab804fdba)

View File

@ -1,7 +1,7 @@
{
"name": "youtube-music",
"productName": "YouTube Music",
"version": "2.1.2",
"version": "2.2.0",
"description": "YouTube Music Desktop App - including custom plugins",
"main": "./dist/index.js",
"license": "MIT",
@ -94,7 +94,6 @@
"build": "yarpm-pnpm run rollup:preload && yarpm-pnpm run rollup:main",
"start": "yarpm-pnpm run build && electron ./dist/index.js",
"start:debug": "cross-env ELECTRON_ENABLE_LOGGING=1 yarpm-pnpm run start",
"postinstall": "patch-package",
"clean": "del-cli dist && del-cli pack",
"dist": "yarpm-pnpm run clean && yarpm-pnpm run build && electron-builder --win --mac --linux -p never",
"dist:linux": "yarpm-pnpm run clean && yarpm-pnpm run build && electron-builder --linux -p never",
@ -103,7 +102,7 @@
"dist:win": "yarpm-pnpm run clean && yarpm-pnpm run build && electron-builder --win -p never",
"dist:win:x64": "yarpm-pnpm run clean && yarpm-pnpm run build && electron-builder --win nsis-web:x64 -p never",
"lint": "eslint .",
"changelog": "auto-changelog",
"changelog": "npx --yes auto-changelog",
"release:linux": "yarpm-pnpm run clean && yarpm-pnpm run build && electron-builder --linux -p always -c.snap.publish=github",
"release:mac": "yarpm-pnpm run clean && yarpm-pnpm run build && electron-builder --mac -p always",
"release:win": "yarpm-pnpm run clean && yarpm-pnpm run build && electron-builder --win -p always",
@ -114,25 +113,25 @@
},
"pnpm": {
"overrides": {
"rollup": "4.1.4",
"node-gyp": "9.4.0",
"rollup": "4.2.0",
"node-gyp": "10.0.1",
"xml2js": "0.6.2",
"node-fetch": "2.7.0",
"@electron/universal": "1.4.2",
"node-fetch": "3.3.2",
"@electron/universal": "1.4.4",
"@babel/runtime": "7.23.2"
}
},
"overrides": {
"rollup": "4.1.4",
"node-gyp": "9.4.0",
"rollup": "4.2.0",
"node-gyp": "10.0.1",
"xml2js": "0.6.2",
"node-fetch": "2.7.0",
"@electron/universal": "1.4.2",
"node-fetch": "3.3.2",
"@electron/universal": "1.4.4",
"@babel/runtime": "7.23.2"
},
"dependencies": {
"@cliqz/adblocker-electron": "1.26.8",
"@cliqz/adblocker-electron-preload": "1.26.8",
"@cliqz/adblocker-electron": "1.26.10",
"@cliqz/adblocker-electron-preload": "1.26.10",
"@ffmpeg.wasm/core-mt": "0.12.0",
"@ffmpeg.wasm/main": "0.12.0",
"@foobar404/wave": "2.0.4",
@ -160,10 +159,9 @@
"simple-youtube-age-restriction-bypass": "git+https://github.com/organization/Simple-YouTube-Age-Restriction-Bypass.git#v2.5.8",
"vudio": "2.1.1",
"x11": "2.3.0",
"youtubei.js": "6.4.1"
"youtubei.js": "7.0.0"
},
"devDependencies": {
"@milahu/patch-package": "6.4.14",
"@playwright/test": "1.39.0",
"@rollup/plugin-commonjs": "25.0.7",
"@rollup/plugin-image": "3.0.3",
@ -176,20 +174,19 @@
"@types/electron-localshortcut": "3.1.2",
"@types/howler": "2.2.10",
"@types/html-to-text": "9.0.3",
"@typescript-eslint/eslint-plugin": "6.8.0",
"auto-changelog": "2.4.0",
"@typescript-eslint/eslint-plugin": "6.9.1",
"builtin-modules": "^3.3.0",
"cross-env": "7.0.3",
"del-cli": "5.1.0",
"electron": "27.0.1",
"electron": "27.0.3",
"electron-builder": "24.6.4",
"electron-devtools-installer": "3.2.0",
"eslint": "8.51.0",
"eslint-plugin-import": "2.28.1",
"eslint": "8.52.0",
"eslint-plugin-import": "2.29.0",
"eslint-plugin-prettier": "5.0.1",
"node-gyp": "9.4.0",
"node-gyp": "10.0.1",
"playwright": "1.39.0",
"rollup": "4.1.4",
"rollup": "4.2.0",
"rollup-plugin-copy": "3.5.0",
"rollup-plugin-import-css": "3.3.5",
"rollup-plugin-string": "3.0.0",
@ -202,5 +199,5 @@
"unreleased": true,
"output": "changelog.md"
},
"packageManager": "pnpm@8.9.2"
"packageManager": "pnpm@8.10.2"
}

View File

@ -1,38 +0,0 @@
diff --git a/node_modules/youtubei.js/bundle/node.cjs b/node_modules/youtubei.js/bundle/node.cjs
index 7e3072e..bf5be6a 100644
--- a/node_modules/youtubei.js/bundle/node.cjs
+++ b/node_modules/youtubei.js/bundle/node.cjs
@@ -16969,7 +16969,13 @@ var _Cache_createCache;
var meta_url = import_meta.url;
var is_cjs = !meta_url;
var __dirname__ = is_cjs ? __dirname : import_path.default.dirname((0, import_url.fileURLToPath)(meta_url));
-var package_json = JSON.parse((0, import_fs.readFileSync)(import_path.default.resolve(__dirname__, is_cjs ? "../package.json" : "../../package.json"), "utf-8"));
+var package_json = {
+ homepage: "https://github.com/LuanRT/YouTube.js#readme",
+ version: "6.4.1",
+ bugs: {
+ url: "https://github.com/LuanRT/YouTube.js/issues"
+ }
+};
var repo_url = (_a3 = package_json.homepage) === null || _a3 === void 0 ? void 0 : _a3.split("#")[0];
var Cache = class {
constructor(persistent = false, persistent_directory) {
diff --git a/node_modules/youtubei.js/dist/src/platform/node.js b/node_modules/youtubei.js/dist/src/platform/node.js
index 0e1b2ca..17b437c 100644
--- a/node_modules/youtubei.js/dist/src/platform/node.js
+++ b/node_modules/youtubei.js/dist/src/platform/node.js
@@ -16,7 +16,13 @@ import evaluate from './jsruntime/jinter.js';
const meta_url = import.meta.url;
const is_cjs = !meta_url;
const __dirname__ = is_cjs ? __dirname : path.dirname(fileURLToPath(meta_url));
-const package_json = JSON.parse(readFileSync(path.resolve(__dirname__, is_cjs ? '../package.json' : '../../package.json'), 'utf-8'));
+const package_json = {
+ homepage: "https://github.com/LuanRT/YouTube.js#readme",
+ version: "6.4.1",
+ bugs: {
+ url: "https://github.com/LuanRT/YouTube.js/issues"
+ }
+};
const repo_url = (_a = package_json.homepage) === null || _a === void 0 ? void 0 : _a.split('#')[0];
class Cache {
constructor(persistent = false, persistent_directory) {

816
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -38,7 +38,12 @@ this [wiki page](https://wiki.archlinux.org/index.php/Arch_User_Repository#Insta
### MacOS
If you get an error "is damaged and cant be opened." when launching the app, run the following in the Terminal:
You can install the app using Homebrew:
```bash
brew install --cask https://raw.githubusercontent.com/th-ch/youtube-music/master/youtube-music.rb
```
If you install the app manually and get an error "is damaged and cant be opened." when launching the app, run the following in the Terminal:
```bash
xattr -cr /Applications/YouTube\ Music.app

View File

@ -81,7 +81,16 @@ const defaultConfig = {
disableDefaultLists: false,
},
'album-color-theme': {},
'ambient-mode': {},
'ambient-mode': {
enabled: false,
quality: 50,
buffer: 30,
interpolationTime: 1500,
blur: 100,
size: 100,
opacity: 1,
fullscreen: false,
},
'audio-compressor': {},
'blur-nav-bar': {},
'bypass-age-restrictions': {},
@ -107,7 +116,7 @@ const defaultConfig = {
autoReconnect: true, // If enabled, will try to reconnect to discord every 5 seconds after disconnecting or failing to connect
activityTimoutEnabled: true, // If enabled, the discord rich presence gets cleared when music paused after the time specified below
activityTimoutTime: 10 * 60 * 1000, // 10 minutes
listenAlong: true, // Add a "listen along" button to rich presence
playOnYouTubeMusic: true, // Add a "Play on YouTube Music" button to rich presence
hideGitHubButton: false, // Disable the "View App On GitHub" button
hideDurationLeft: false, // Hides the start and end time of the song to rich presence
},
@ -119,6 +128,7 @@ const defaultConfig = {
skipExisting: false,
playlistMaxItems: undefined as number | undefined,
},
'http-api': {},
'exponential-volume': {},
'in-app-menu': {
/**

View File

@ -22,8 +22,20 @@ export function isEnabled(plugin: string) {
return pluginConfig !== undefined && pluginConfig.enabled;
}
export function setOptions<T>(plugin: string, options: T) {
/**
* Set options for a plugin
* @param plugin Plugin name
* @param options Options to set
* @param exclude Options to exclude from the options object
*/
export function setOptions<T>(plugin: string, options: T, exclude: string[] = ['enabled']) {
const plugins = store.get('plugins') as Record<string, T>;
// HACK: This is a workaround for preventing changed options from being overwritten
exclude.forEach((key) => {
if (Object.prototype.hasOwnProperty.call(options, key)) {
delete options[key as keyof T];
}
});
store.set('plugins', {
...plugins,
[plugin]: {
@ -33,8 +45,8 @@ export function setOptions<T>(plugin: string, options: T) {
});
}
export function setMenuOptions<T>(plugin: string, options: T) {
setOptions(plugin, options);
export function setMenuOptions<T>(plugin: string, options: T, exclude: string[] = ['enabled']) {
setOptions(plugin, options, exclude);
if (store.get('options.restartOnConfigChanges')) {
restart();
}
@ -45,11 +57,11 @@ export function getOptions<T>(plugin: string): T {
}
export function enable(plugin: string) {
setMenuOptions(plugin, { enabled: true });
setMenuOptions(plugin, { enabled: true }, []);
}
export function disable(plugin: string) {
setMenuOptions(plugin, { enabled: false });
setMenuOptions(plugin, { enabled: false }, []);
}
export default {

View File

@ -20,6 +20,13 @@ const setDefaultPluginOptions = (store: Conf<Record<string, unknown>>, plugin: k
};
const migrations = {
'>=2.1.3'(store: Conf<Record<string, unknown>>) {
const listenAlong = store.get('plugins.discord.listenAlong');
if (listenAlong !== undefined) {
store.set('plugins.discord.playOnYouTubeMusic', listenAlong);
store.delete('plugins.discord.listenAlong');
}
},
'>=2.1.0'(store: Conf<Record<string, unknown>>) {
const originalPreset = store.get('plugins.downloader.preset') as string | undefined;
if (originalPreset) {

View File

@ -24,6 +24,7 @@ import captionsSelector from './plugins/captions-selector/back';
import crossfade from './plugins/crossfade/back';
import discord from './plugins/discord/back';
import downloader from './plugins/downloader/back';
import httpApi from './plugins/http-api/back';
import inAppMenu from './plugins/in-app-menu/back';
import lastFm from './plugins/last-fm/back';
import lumiaStream from './plugins/lumiastream/back';
@ -109,6 +110,7 @@ const mainPlugins = {
'crossfade': crossfade,
'discord': discord,
'downloader': downloader,
'http-api': httpApi,
'in-app-menu': inAppMenu,
'last-fm': lastFm,
'lumiastream': lumiaStream,

View File

@ -8,6 +8,7 @@ import { startingPages } from './providers/extracted-data';
import promptOptions from './providers/prompt-options';
import adblockerMenu from './plugins/adblocker/menu';
import ambientModeMenu from './plugins/ambient-mode/menu';
import captionsSelectorMenu from './plugins/captions-selector/menu';
import crossfadeMenu from './plugins/crossfade/menu';
import disableAutoplayMenu from './plugins/disable-autoplay/menu';
@ -32,6 +33,7 @@ const betaPlugins = ['crossfade', 'lumiastream'];
const pluginMenus = {
'adblocker': adblockerMenu,
'ambient-mode': ambientModeMenu,
'disable-autoplay': disableAutoplayMenu,
'captions-selector': captionsSelectorMenu,
'crossfade': crossfadeMenu,

View File

@ -1,10 +1,14 @@
import { BrowserWindow } from 'electron';
import config from './config';
import style from './style.css';
import { injectCSS } from '../utils';
export default (win: BrowserWindow) => {
config.subscribeAll((newConfig) => {
win.webContents.send('ambient-mode:config-change', newConfig);
});
export default (win: BrowserWindow) => {
injectCSS(win.webContents, style);
};

View File

@ -0,0 +1,4 @@
import { PluginConfig } from '../../config/dynamic';
const config = new PluginConfig('ambient-mode');
export default config;

View File

@ -1,9 +1,15 @@
import { ipcRenderer } from 'electron';
import { ConfigType } from '../../config/dynamic';
export default (_: ConfigType<'ambient-mode'>) => {
const interpolationTime = 3000; // interpolation time (ms)
const framerate = 30; // frame
const qualityRatio = 50; // width size (pixel)
export default (config: ConfigType<'ambient-mode'>) => {
let interpolationTime = config.interpolationTime; // interpolation time (ms)
let buffer = config.buffer; // frame
let qualityRatio = config.quality; // width size (pixel)
let sizeRatio = config.size / 100; // size ratio (percent)
let blur = config.blur; // blur (pixel)
let opacity = config.opacity; // opacity (percent)
let isFullscreen = config.fullscreen; // fullscreen (boolean)
let unregister: (() => void) | null = null;
@ -37,7 +43,7 @@ export default (_: ConfigType<'ambient-mode'>) => {
context.globalAlpha = 1;
if (lastImageData) {
const frameOffset = (1 / framerate) * (1000 / interpolationTime);
const frameOffset = (1 / buffer) * (1000 / interpolationTime);
context.globalAlpha = 1 - (frameOffset * 2); // because of alpha value must be < 1
context.putImageData(lastImageData, 0, 0);
context.globalAlpha = frameOffset;
@ -61,8 +67,18 @@ export default (_: ConfigType<'ambient-mode'>) => {
blurCanvas.width = qualityRatio;
blurCanvas.height = Math.floor(newHeight / newWidth * qualityRatio);
blurCanvas.style.width = `${newWidth}px`;
blurCanvas.style.height = `${newHeight}px`;
blurCanvas.style.width = `${newWidth * sizeRatio}px`;
blurCanvas.style.height = `${newHeight * sizeRatio}px`;
if (isFullscreen) blurCanvas.classList.add('fullscreen');
else blurCanvas.classList.remove('fullscreen');
const leftOffset = newWidth * (sizeRatio - 1) / 2;
const topOffset = newHeight * (sizeRatio - 1) / 2;
blurCanvas.style.setProperty('--left', `${-1 * leftOffset}px`);
blurCanvas.style.setProperty('--top', `${-1 * topOffset}px`);
blurCanvas.style.setProperty('--blur', `${blur}px`);
blurCanvas.style.setProperty('--opacity', `${opacity}`);
};
const observer = new MutationObserver((mutations) => {
@ -75,10 +91,22 @@ export default (_: ConfigType<'ambient-mode'>) => {
const resizeObserver = new ResizeObserver(() => {
applyVideoAttributes();
});
const onConfigSync = (_: Electron.IpcRendererEvent, newConfig: ConfigType<'ambient-mode'>) => {
if (typeof newConfig.interpolationTime === 'number') interpolationTime = newConfig.interpolationTime;
if (typeof newConfig.buffer === 'number') buffer = newConfig.buffer;
if (typeof newConfig.quality === 'number') qualityRatio = newConfig.quality;
if (typeof newConfig.size === 'number') sizeRatio = newConfig.size / 100;
if (typeof newConfig.blur === 'number') blur = newConfig.blur;
if (typeof newConfig.opacity === 'number') opacity = newConfig.opacity;
if (typeof newConfig.fullscreen === 'boolean') isFullscreen = newConfig.fullscreen;
applyVideoAttributes();
};
ipcRenderer.on('ambient-mode:config-change', onConfigSync);
/* hooking */
let canvasInterval: NodeJS.Timeout | null = null;
canvasInterval = setInterval(onSync, Math.max(1, Math.ceil(1000 / framerate)));
canvasInterval = setInterval(onSync, Math.max(1, Math.ceil(1000 / buffer)));
applyVideoAttributes();
observer.observe(songVideo, { attributes: true });
resizeObserver.observe(songVideo);
@ -90,7 +118,7 @@ export default (_: ConfigType<'ambient-mode'>) => {
};
const onPlay = () => {
if (canvasInterval) clearInterval(canvasInterval);
canvasInterval = setInterval(onSync, Math.max(1, Math.ceil(1000 / framerate)));
canvasInterval = setInterval(onSync, Math.max(1, Math.ceil(1000 / buffer)));
};
songVideo.addEventListener('pause', onPause);
songVideo.addEventListener('play', onPlay);
@ -107,6 +135,7 @@ export default (_: ConfigType<'ambient-mode'>) => {
observer.disconnect();
resizeObserver.disconnect();
ipcRenderer.off('ambient-mode:config-change', onConfigSync);
window.removeEventListener('resize', applyVideoAttributes);
wrapper.removeChild(blurCanvas);

View File

@ -0,0 +1,87 @@
import config from './config';
import { MenuTemplate } from '../../menu';
const interpolationTimeList = [0, 500, 1000, 1500, 2000, 3000, 4000, 5000];
const qualityList = [10, 25, 50, 100, 200, 500, 1000];
const sizeList = [100, 110, 125, 150, 175, 200, 300];
const bufferList = [1, 5, 10, 20, 30];
const blurAmountList = [0, 5, 10, 25, 50, 100, 150, 200, 500];
const opacityList = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1];
export default (): MenuTemplate => [
{
label: 'Smoothness transition',
submenu: interpolationTimeList.map((interpolationTime) => ({
label: `During ${interpolationTime / 1000}s`,
type: 'radio',
checked: config.get('interpolationTime') === interpolationTime,
click() {
config.set('interpolationTime', interpolationTime);
},
})),
},
{
label: 'Quality',
submenu: qualityList.map((quality) => ({
label: `${quality} pixels`,
type: 'radio',
checked: config.get('quality') === quality,
click() {
config.set('quality', quality);
},
})),
},
{
label: 'Size',
submenu: sizeList.map((size) => ({
label: `${size}%`,
type: 'radio',
checked: config.get('size') === size,
click() {
config.set('size', size);
},
})),
},
{
label: 'Buffer',
submenu: bufferList.map((buffer) => ({
label: `${buffer}`,
type: 'radio',
checked: config.get('buffer') === buffer,
click() {
config.set('buffer', buffer);
},
})),
},
{
label: 'Opacity',
submenu: opacityList.map((opacity) => ({
label: `${opacity * 100}%`,
type: 'radio',
checked: config.get('opacity') === opacity,
click() {
config.set('opacity', opacity);
},
})),
},
{
label: 'Blur amount',
submenu: blurAmountList.map((blur) => ({
label: `${blur} pixels`,
type: 'radio',
checked: config.get('blur') === blur,
click() {
config.set('blur', blur);
},
})),
},
{
label: 'Using fullscreen',
type: 'checkbox',
checked: config.get('fullscreen'),
click(item) {
config.set('fullscreen', item.checked);
},
},
];

View File

@ -1,7 +1,26 @@
#song-video canvas.html5-blur-canvas{
position: absolute;
left: 0;
top: 0;
#song-video canvas.html5-blur-canvas {
filter: blur(var(--blur, 100px));
opacity: var(--opacity, 1);
filter: blur(100px);
pointer-events: none;
}
#song-video canvas.html5-blur-canvas:not(.fullscreen) {
position: absolute;
left: var(--left, 0px);
top: var(--top, 0px);
}
#song-video canvas.html5-blur-canvas.fullscreen {
position: fixed;
width: 100% !important;
height: 100% !important;
left: 0 !important;
top: 0 !important;
}
#song-video .html5-video-container > video {
top: 0 !important;
}

View File

@ -170,7 +170,7 @@ export default (
largeImageKey: songInfo.imageSrc ?? '',
largeImageText: songInfo.album ?? '',
buttons: [
...(options.listenAlong ? [{ label: 'Listen Along', url: songInfo.url ?? '' }] : []),
...(options.playOnYouTubeMusic ? [{ label: 'Play on YouTube Music', url: songInfo.url ?? '' }] : []),
...(options.hideGitHubButton ? [] : [{ label: 'View App On GitHub', url: 'https://github.com/th-ch/youtube-music' }]),
],
};

View File

@ -47,11 +47,11 @@ export default (win: Electron.BrowserWindow, options: DiscordOptions, refreshMen
},
},
{
label: 'Listen Along',
label: 'Play on YouTube Music',
type: 'checkbox',
checked: options.listenAlong,
checked: options.playOnYouTubeMusic,
click(item: Electron.MenuItem) {
options.listenAlong = item.checked;
options.playOnYouTubeMusic = item.checked;
setMenuOptions('discord', options);
},
},

View File

@ -84,8 +84,8 @@ export const getCookieFromWindow = async (win: BrowserWindow) => {
url: 'https://music.youtube.com',
})
)
.map((it) => it.name + '=' + it.value + ';')
.join('');
.map((it) => it.name + '=' + it.value)
.join(';');
};
export default async (win_: BrowserWindow) => {
@ -450,12 +450,11 @@ export async function downloadPlaylist(givenUrl?: string | URL) {
try {
givenUrl = new URL(givenUrl ?? '');
} catch {
return;
givenUrl = new URL(win.webContents.getURL());
}
const playlistId =
getPlaylistID(givenUrl) ||
getPlaylistID(new URL(win.webContents.getURL())) ||
getPlaylistID(new URL(playingUrl));
if (!playlistId) {
@ -604,7 +603,7 @@ function getFFmpegMetadataArgs(metadata: CustomSongInfo) {
// Playlist radio modifier needs to be cut from playlist ID
const INVALID_PLAYLIST_MODIFIER = 'RDAMPL';
const getPlaylistID = (aURL: URL) => {
const getPlaylistID = (aURL?: URL): string | null | undefined => {
const result =
aURL?.searchParams.get('list') || aURL?.searchParams.get('playlist');
if (result?.startsWith(INVALID_PLAYLIST_MODIFIER)) {

View File

@ -0,0 +1,112 @@
import http from 'node:http';
import { BrowserWindow, ipcMain } from 'electron';
import is from 'electron-is';
import registerCallback, { SongInfo } from '../../providers/song-info';
import getSongControls from '../../providers/song-controls';
import { isEnabled } from '../../config/plugins';
const port = 9669; // Choose a port number
export default (win: BrowserWindow) => {
let songInfo: (SongInfo & { loopStatus?: string, volume?: number }) | undefined;
if (!is.linux() || (is.linux() && !isEnabled('shortcuts'))) {
ipcMain.on('apiLoaded', () => {
win.webContents.send('setupSeekedListener', 'webinterface');
win.webContents.send('setupTimeChangedListener', 'webinterface');
win.webContents.send('setupRepeatChangedListener', 'webinterface');
win.webContents.send('setupVolumeChangedListener', 'webinterface');
});
}
// updations
ipcMain.on('seeked', (_, t: number) => {
if (songInfo) {
songInfo.elapsedSeconds = t;
}
});
ipcMain.on('timeChanged', (_, t: number) => {
if (songInfo) {
songInfo.elapsedSeconds = t;
}
});
ipcMain.on('repeatChanged', (_, mode: string) => {
if (songInfo) {
songInfo.loopStatus = mode;
}
});
ipcMain.on('volumeChanged', (_, newVolume: number) => {
if(songInfo) {
songInfo.volume = newVolume;
}
});
registerCallback((info) => songInfo = info as SongInfo & { loopStatus?: string, volume?: number });
const doAction = (action: URLSearchParams) => {
//actions
const songControls = getSongControls(win);
const { playPause, next, previous, volumeMinus10, volumePlus10, shuffle } = songControls;
switch (action.get('action')) {
case 'play':
case 'pause':
case 'playpause':
if (songInfo) {
songInfo.isPaused = !songInfo.isPaused;
}
playPause();
break;
case 'next':
next();
break;
case 'previous':
previous();
break;
case 'shuffle':
shuffle();
break;
case 'looptoggle':
songControls.switchRepeat(1);
break;
case 'volumeplus10':
volumePlus10();
break;
case 'volumeminus10':
volumeMinus10();
break;
case 'seek':
console.log('seek', action.get('value'));
win.webContents.send('seekTo', action.get('value'));
break;
default:
throw Error(`web-interface: Requested action "${action.get('action')}" not found`);
}
};
const server = http.createServer((req,res) => {
if (req?.url?.slice(0,4) === '/api') {
const url = new URL(req.url, `http://${req.headers.host}`);
if (url.searchParams.has('action')) {
console.log('webinterface: received and trying action ' + url.searchParams.get('action'));
try {
doAction(url.searchParams);
} catch (e) {
res.statusCode = 404;
res.write(e);
return;
}
}
res.setHeader('Content-Type','application/json');
res.write(JSON.stringify(songInfo));
}
res.end();
});
server.listen(port, () => {
console.log(`web-interface API Server is running on port ${port}`);
});
};

28
youtube-music.rb Normal file
View File

@ -0,0 +1,28 @@
require 'json'
require 'open-uri'
cask "youtube-music" do
desc "YouTube Music Desktop App"
homepage "https://github.com/th-ch/youtube-music"
# Fetch the latest release version from GitHub API
latest_release = JSON.parse(open("https://api.github.com/repos/th-ch/youtube-music/releases/latest").read)['tag_name']
version latest_release
base_url = "https://github.com/th-ch/youtube-music/releases/download/#{latest_release}/YouTube-Music-#{latest_release.delete_prefix('v')}"
file_extension = Hardware::CPU.arm? ? "-arm64.dmg" : ".dmg"
url "#{base_url}#{file_extension}"
# TODO checksum
sha256 :no_check
app "YouTube Music.app"
postflight do
print("Removing quarantine attribute from YouTube Music.app.\n")
system "xattr -cr '/Applications/YouTube Music.app'"
end
auto_updates true
end