Compare commits

..

18 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
16 changed files with 675 additions and 513 deletions

View File

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

View File

@ -2,8 +2,27 @@
All notable changes to this project will be documented in this file. Dates are displayed in UTC. 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) #### [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) - 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) - 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) - chore(deps): update dependency electron to v27.0.1 [`#1331`](https://github.com/th-ch/youtube-music/pull/1331)

View File

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

746
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 ### 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 ```bash
xattr -cr /Applications/YouTube\ Music.app xattr -cr /Applications/YouTube\ Music.app

View File

@ -81,7 +81,16 @@ const defaultConfig = {
disableDefaultLists: false, disableDefaultLists: false,
}, },
'album-color-theme': {}, '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': {}, 'audio-compressor': {},
'blur-nav-bar': {}, 'blur-nav-bar': {},
'bypass-age-restrictions': {}, 'bypass-age-restrictions': {},
@ -119,6 +128,7 @@ const defaultConfig = {
skipExisting: false, skipExisting: false,
playlistMaxItems: undefined as number | undefined, playlistMaxItems: undefined as number | undefined,
}, },
'http-api': {},
'exponential-volume': {}, 'exponential-volume': {},
'in-app-menu': { 'in-app-menu': {
/** /**

View File

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

View File

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

View File

@ -1,10 +1,14 @@
import { BrowserWindow } from 'electron'; import { BrowserWindow } from 'electron';
import config from './config';
import style from './style.css'; import style from './style.css';
import { injectCSS } from '../utils'; 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); 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'; import { ConfigType } from '../../config/dynamic';
export default (_: ConfigType<'ambient-mode'>) => { export default (config: ConfigType<'ambient-mode'>) => {
const interpolationTime = 3000; // interpolation time (ms) let interpolationTime = config.interpolationTime; // interpolation time (ms)
const framerate = 30; // frame let buffer = config.buffer; // frame
const qualityRatio = 50; // width size (pixel) 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; let unregister: (() => void) | null = null;
@ -37,7 +43,7 @@ export default (_: ConfigType<'ambient-mode'>) => {
context.globalAlpha = 1; context.globalAlpha = 1;
if (lastImageData) { 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.globalAlpha = 1 - (frameOffset * 2); // because of alpha value must be < 1
context.putImageData(lastImageData, 0, 0); context.putImageData(lastImageData, 0, 0);
context.globalAlpha = frameOffset; context.globalAlpha = frameOffset;
@ -61,8 +67,18 @@ export default (_: ConfigType<'ambient-mode'>) => {
blurCanvas.width = qualityRatio; blurCanvas.width = qualityRatio;
blurCanvas.height = Math.floor(newHeight / newWidth * qualityRatio); blurCanvas.height = Math.floor(newHeight / newWidth * qualityRatio);
blurCanvas.style.width = `${newWidth}px`; blurCanvas.style.width = `${newWidth * sizeRatio}px`;
blurCanvas.style.height = `${newHeight}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) => { const observer = new MutationObserver((mutations) => {
@ -75,10 +91,22 @@ export default (_: ConfigType<'ambient-mode'>) => {
const resizeObserver = new ResizeObserver(() => { const resizeObserver = new ResizeObserver(() => {
applyVideoAttributes(); 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 */ /* hooking */
let canvasInterval: NodeJS.Timeout | null = null; 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(); applyVideoAttributes();
observer.observe(songVideo, { attributes: true }); observer.observe(songVideo, { attributes: true });
resizeObserver.observe(songVideo); resizeObserver.observe(songVideo);
@ -90,7 +118,7 @@ export default (_: ConfigType<'ambient-mode'>) => {
}; };
const onPlay = () => { const onPlay = () => {
if (canvasInterval) clearInterval(canvasInterval); 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('pause', onPause);
songVideo.addEventListener('play', onPlay); songVideo.addEventListener('play', onPlay);
@ -107,6 +135,7 @@ export default (_: ConfigType<'ambient-mode'>) => {
observer.disconnect(); observer.disconnect();
resizeObserver.disconnect(); resizeObserver.disconnect();
ipcRenderer.off('ambient-mode:config-change', onConfigSync);
window.removeEventListener('resize', applyVideoAttributes); window.removeEventListener('resize', applyVideoAttributes);
wrapper.removeChild(blurCanvas); 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{ #song-video canvas.html5-blur-canvas {
position: absolute; filter: blur(var(--blur, 100px));
left: 0; opacity: var(--opacity, 1);
top: 0;
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

@ -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