mirror of
https://github.com/th-ch/youtube-music.git
synced 2026-01-11 18:41:47 +00:00
Compare commits
18 Commits
v2.1.3
...
legacy/htt
| Author | SHA1 | Date | |
|---|---|---|---|
| 9e97699e6c | |||
| a810621762 | |||
| a41db79c35 | |||
| 87786d9aef | |||
| 22f5866050 | |||
| 04894fbcf5 | |||
| c17c624ba4 | |||
| bfe7249df8 | |||
| 13c570efe9 | |||
| b299846f0f | |||
| 59e9289d27 | |||
| 8dc29caa1b | |||
| 7fedf88654 | |||
| 5da0202425 | |||
| 6288d0b171 | |||
| 4248d20e8e | |||
| 0b413492ad | |||
| dc73561c8a |
8
.github/workflows/build.yml
vendored
8
.github/workflows/build.yml
vendored
@ -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 }}
|
||||||
|
|
||||||
|
|||||||
19
changelog.md
19
changelog.md
@ -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)
|
||||||
|
|||||||
41
package.json
41
package.json
@ -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"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
746
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -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 can’t 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 can’t 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
|
||||||
|
|||||||
@ -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': {
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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);
|
||||||
};
|
};
|
||||||
|
|||||||
4
src/plugins/ambient-mode/config.ts
Normal file
4
src/plugins/ambient-mode/config.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import { PluginConfig } from '../../config/dynamic';
|
||||||
|
|
||||||
|
const config = new PluginConfig('ambient-mode');
|
||||||
|
export default config;
|
||||||
@ -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);
|
||||||
|
|||||||
87
src/plugins/ambient-mode/menu.ts
Normal file
87
src/plugins/ambient-mode/menu.ts
Normal 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);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
112
src/plugins/http-api/back.ts
Normal file
112
src/plugins/http-api/back.ts
Normal 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
28
youtube-music.rb
Normal 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
|
||||||
Reference in New Issue
Block a user