Compare commits

...

74 Commits

Author SHA1 Message Date
0d462ac3a2 HOTFIX: Bump version to 3.7.4 2025-02-18 00:37:57 +09:00
bcb94f6de8 chore(deps): update dependency rollup to v4.34.8 (#2982)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-18 00:36:54 +09:00
fe925ec8ee fix(plugin-loader): update context filtering logic for backend mode (#2990) 2025-02-18 00:35:55 +09:00
86c77d141f Update changelog for v3.7.3 2025-02-17 04:04:16 +00:00
754ca3caaa Bump version to 3.7.3 2025-02-17 12:47:57 +09:00
61ea104d7b fix(downloader): use the upgrade button to check for premium status (#2987) 2025-02-17 12:42:07 +09:00
573bdfae03 chore(deps): update dependency electron-vite to v3 (#2986)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-17 02:45:21 +09:00
cca493b7d5 fix: build performance 2025-02-17 02:44:47 +09:00
f47262d27b fix(album-color-theme): fix Color deps 2025-02-17 01:44:24 +09:00
65bf9129ea fix(action-release): bump pnpm version to v10 2025-02-16 15:22:07 +09:00
87e9b9f7a8 fix: revert some deps 2025-02-16 14:43:13 +09:00
07bc4f05fd chore(deps): update dependency @babel/runtime to v7.26.9 (#2980)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-16 14:37:52 +09:00
e3a6808087 fix: pnpm overrides 2025-02-16 14:37:22 +09:00
e9184e5d60 fix: bump deps 2025-02-16 14:30:02 +09:00
a5b32d96f8 fix(action): bump pnpm version to v10 2025-02-16 14:14:08 +09:00
040db7539c fix(deps): fix pnpm 2025-02-16 14:12:28 +09:00
da646c1d53 fix(vite): set server.cors.origin (#2981) 2025-02-16 13:23:35 +09:00
5f5917f972 chore(deps-dev): bump esbuild from 0.24.2 to 0.25.0 (#2973)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-16 00:08:47 +09:00
eccb0d2f08 fix(deps): update dependency solid-transition-group to v0.3.0 (#2949)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-15 03:50:09 +09:00
4cd9dd17df fix: remove disable-gpu-memory-buffer-video-frames flag (#2963) 2025-02-15 03:49:52 +09:00
5de07b9a96 fix(downloader): fix pmd undefined 2025-02-14 21:23:19 +09:00
151f067beb chore(i18n): Translated using Weblate (Hindi)
Currently translated at 33.2% (135 of 406 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/hi/
2025-02-10 07:01:54 +01:00
c68a7bd19f chore(i18n): Translated using Weblate (Filipino)
Currently translated at 86.6% (352 of 406 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/fil/
2025-02-10 07:01:53 +01:00
b87e5e31df chore(i18n): Translated using Weblate (Arabic)
Currently translated at 35.9% (146 of 406 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/ar/
2025-02-08 21:49:42 +01:00
03229d61c8 chore(i18n): Translated using Weblate (Arabic)
Currently translated at 35.9% (146 of 406 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/ar/
2025-02-08 01:01:53 +00:00
b6330eed18 fix(deps): update dependency semver to v7.7.0 (#2948)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-03 18:47:32 +09:00
b254812ac2 chore(deps): update playwright monorepo to v1.50.1 (#2943)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-03 18:45:11 +09:00
7e243e2fbf fix(deps): update dependency @hono/node-server to v1.13.8 (#2944)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-03 18:44:10 +09:00
307e52cc89 fix(deps): update dependency electron-store to v10.0.1 (#2945)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-03 18:43:45 +09:00
f7b7ea916f chore(deps): update dependency rollup to v4.34.1 (#2946)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-03 18:43:34 +09:00
3eccf8daca chore(deps): update dependency typescript-eslint to v8.22.0 (#2947)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-03 18:43:23 +09:00
aa48944212 fix(synced-lyrics): Fix reverse direction of synced lyrics for persian or other rtl languages (#2940)
Add direction auto to synced lyrics to fix reverse direction in rtl languages
2025-02-02 23:55:07 +09:00
4d51f1a412 chore(deps): update dependency electron to v34.0.2 (#2942)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-02 23:54:45 +09:00
d3c9f76582 chore(deps): update dependency discord-api-types to v0.37.119 (#2941)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-02 23:46:22 +09:00
d638a6cf28 fix(deps): update dependency hono to v4.6.20 (#2932)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-02 23:46:12 +09:00
f93651b219 chore(deps): update eslint monorepo to v9.19.0 (#2935)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-02 23:45:20 +09:00
cb8c6c69fe fix(deps): update dependency bgutils-js to v3.1.3 (#2934)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-02 23:45:10 +09:00
4e7266fb1b fix(deps): update dependency i18next to v24.2.2 (#2933)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-02 23:44:59 +09:00
8de75ff3a5 fix(deps): update dependency happy-dom to v16.8.1 (#2936)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-02 23:44:38 +09:00
3236c88eb2 chore(i18n): Translated using Weblate (Hebrew)
Currently translated at 11.8% (48 of 406 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/he/
2025-01-27 18:02:51 +00:00
c9f0ad14c2 chore(deps): update dependency @babel/runtime to v7.26.7 (#2924)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-25 00:36:15 +09:00
0a9199c92b chore(config): migrate renovate config (#2925)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-25 00:36:04 +09:00
3ffcff7d9c fix(deps): update dependency @ghostery/adblocker-electron-preload to v2.5.0 (#2923)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-25 00:27:46 +09:00
ddf614d362 fix(deps): update dependency @ghostery/adblocker-electron to v2.5.0 (#2922)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-25 00:21:45 +09:00
6f1a77bbb9 chore(deps): update playwright monorepo to v1.50.0 (#2921)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-25 00:21:33 +09:00
8595f9761e fix: pnpm patch key 2025-01-25 00:21:18 +09:00
cc442182fd chore(deps): update dependency vite-plugin-inspect to v10.1.0 (#2920)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-25 00:20:11 +09:00
b827a05eea chore(deps): update dependency rollup to v4.32.0 (#2919)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-25 00:14:57 +09:00
250abab8bc fix(deps): update dependency hono to v4.6.18 (#2918)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-25 00:14:46 +09:00
8e45518ccf fix(deps): update dependency deepmerge-ts to v7.1.4 (#2917)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-25 00:14:36 +09:00
7485e065ed chore(deps): update dependency vite to v6.0.11 (#2894)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-25 00:14:21 +09:00
c1d88f91d4 chore(deps): update dependency electron to v34.0.1 (#2916)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-25 00:14:09 +09:00
124a2bd8d0 fix: pnpm patch 2025-01-25 00:13:55 +09:00
f8f94f9665 Rename app-builder-lib@26.0.0-alpha.9.patch to app-builder-lib@26.0.0-alpha.10.patch 2025-01-25 00:13:15 +09:00
5e98a82b23 chore(deps): update dependency electron-builder-squirrel-windows to v26.0.0-alpha.10 (#2899)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-25 00:12:48 +09:00
a5c20a66b3 chore(deps): update dependency electron-builder to v26.0.0-alpha.10 (#2898)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-25 00:07:25 +09:00
cc84116ad1 chore(deps): update dependency typescript-eslint to v8.21.0 (#2901)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-25 00:06:16 +09:00
a2e2031708 chore(deps): update dependency discord-api-types to v0.37.117 (#2895)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-25 00:05:46 +09:00
5001eabf23 fix(deps): update dependency youtubei.js to v13 (#2904)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-25 00:05:14 +09:00
aac2974430 chore(deps): update dependency vite to v6.0.9 [security] (#2907)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-25 00:04:50 +09:00
f7f005bb3d fix(deps): update dependency happy-dom to v16.7.2 (#2902)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-25 00:02:58 +09:00
e1f6d5b7f2 fix(discord-plugin): handle album name padding if length < 2 (#2903) 2025-01-25 00:02:38 +09:00
b6b607897e feat(navigation): added nav icon padding (#2905) 2025-01-25 00:02:24 +09:00
651ebb2b1a chore(i18n): Translated using Weblate (Vietnamese)
Currently translated at 97.7% (397 of 406 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/vi/
2025-01-23 22:01:53 +01:00
9fa24deed2 chore(i18n): Translated using Weblate (Indonesian)
Currently translated at 100.0% (406 of 406 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/id/
2025-01-23 22:01:52 +01:00
c81022d373 chore(i18n): Translated using Weblate (Spanish)
Currently translated at 99.5% (404 of 406 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/es/
2025-01-23 22:01:51 +01:00
b726dc7580 chore(i18n): Translated using Weblate (Hindi)
Currently translated at 30.7% (125 of 406 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/hi/
2025-01-22 06:47:10 +01:00
471aa7d0a6 chore(i18n): Translated using Weblate (Persian)
Currently translated at 100.0% (406 of 406 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/fa/
2025-01-22 06:47:09 +01:00
f34d645ac3 chore(deps): update dependency rollup to v4.31.0 (#2891)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-20 11:55:56 +09:00
d2a11a560e chore(deps): update dependency eslint-plugin-prettier to v5.2.3 (#2889)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-19 22:08:46 +09:00
9d185872db chore(i18n): Translated using Weblate (Persian)
Currently translated at 100.0% (406 of 406 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/fa/
2025-01-18 15:56:20 +01:00
d0ff71aa66 chore(deps): update dependency vite-plugin-inspect to v10.0.7 (#2882)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-18 22:44:03 +09:00
bc8999585f fix(deps): update dependency hono to v4.6.17 (#2883)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-18 18:15:36 +09:00
1e1582e31f Update changelog for v3.7.2 2025-01-18 05:44:19 +00:00
24 changed files with 2508 additions and 1932 deletions

View File

@ -23,7 +23,7 @@ jobs:
- name: Install pnpm - name: Install pnpm
uses: pnpm/action-setup@v4 uses: pnpm/action-setup@v4
with: with:
version: 9 version: 10
run_install: false run_install: false
- name: Setup NodeJS - name: Setup NodeJS
@ -98,7 +98,7 @@ jobs:
- name: Install pnpm - name: Install pnpm
uses: pnpm/action-setup@v4 uses: pnpm/action-setup@v4
with: with:
version: 9 version: 10
run_install: false run_install: false
- name: Setup NodeJS - name: Setup NodeJS

View File

@ -2,8 +2,110 @@
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.
#### [v3.7.3](https://github.com/th-ch/youtube-music/compare/v3.7.2...v3.7.3)
- fix(downloader): use the upgrade button to check for premium status [`#2987`](https://github.com/th-ch/youtube-music/pull/2987)
- chore(deps): update dependency electron-vite to v3 [`#2986`](https://github.com/th-ch/youtube-music/pull/2986)
- chore(deps): update dependency @babel/runtime to v7.26.9 [`#2980`](https://github.com/th-ch/youtube-music/pull/2980)
- fix(vite): set server.cors.origin [`#2981`](https://github.com/th-ch/youtube-music/pull/2981)
- chore(deps-dev): bump esbuild from 0.24.2 to 0.25.0 [`#2973`](https://github.com/th-ch/youtube-music/pull/2973)
- fix(deps): update dependency solid-transition-group to v0.3.0 [`#2949`](https://github.com/th-ch/youtube-music/pull/2949)
- fix: remove disable-gpu-memory-buffer-video-frames flag [`#2963`](https://github.com/th-ch/youtube-music/pull/2963)
- fix(deps): update dependency semver to v7.7.0 [`#2948`](https://github.com/th-ch/youtube-music/pull/2948)
- chore(deps): update playwright monorepo to v1.50.1 [`#2943`](https://github.com/th-ch/youtube-music/pull/2943)
- fix(deps): update dependency @hono/node-server to v1.13.8 [`#2944`](https://github.com/th-ch/youtube-music/pull/2944)
- fix(deps): update dependency electron-store to v10.0.1 [`#2945`](https://github.com/th-ch/youtube-music/pull/2945)
- chore(deps): update dependency rollup to v4.34.1 [`#2946`](https://github.com/th-ch/youtube-music/pull/2946)
- chore(deps): update dependency typescript-eslint to v8.22.0 [`#2947`](https://github.com/th-ch/youtube-music/pull/2947)
- fix(synced-lyrics): Fix reverse direction of synced lyrics for persian or other rtl languages [`#2940`](https://github.com/th-ch/youtube-music/pull/2940)
- chore(deps): update dependency electron to v34.0.2 [`#2942`](https://github.com/th-ch/youtube-music/pull/2942)
- chore(deps): update dependency discord-api-types to v0.37.119 [`#2941`](https://github.com/th-ch/youtube-music/pull/2941)
- fix(deps): update dependency hono to v4.6.20 [`#2932`](https://github.com/th-ch/youtube-music/pull/2932)
- chore(deps): update eslint monorepo to v9.19.0 [`#2935`](https://github.com/th-ch/youtube-music/pull/2935)
- fix(deps): update dependency bgutils-js to v3.1.3 [`#2934`](https://github.com/th-ch/youtube-music/pull/2934)
- fix(deps): update dependency i18next to v24.2.2 [`#2933`](https://github.com/th-ch/youtube-music/pull/2933)
- fix(deps): update dependency happy-dom to v16.8.1 [`#2936`](https://github.com/th-ch/youtube-music/pull/2936)
- chore(deps): update dependency @babel/runtime to v7.26.7 [`#2924`](https://github.com/th-ch/youtube-music/pull/2924)
- chore(config): migrate renovate config [`#2925`](https://github.com/th-ch/youtube-music/pull/2925)
- fix(deps): update dependency @ghostery/adblocker-electron-preload to v2.5.0 [`#2923`](https://github.com/th-ch/youtube-music/pull/2923)
- fix(deps): update dependency @ghostery/adblocker-electron to v2.5.0 [`#2922`](https://github.com/th-ch/youtube-music/pull/2922)
- chore(deps): update playwright monorepo to v1.50.0 [`#2921`](https://github.com/th-ch/youtube-music/pull/2921)
- chore(deps): update dependency vite-plugin-inspect to v10.1.0 [`#2920`](https://github.com/th-ch/youtube-music/pull/2920)
- chore(deps): update dependency rollup to v4.32.0 [`#2919`](https://github.com/th-ch/youtube-music/pull/2919)
- fix(deps): update dependency hono to v4.6.18 [`#2918`](https://github.com/th-ch/youtube-music/pull/2918)
- fix(deps): update dependency deepmerge-ts to v7.1.4 [`#2917`](https://github.com/th-ch/youtube-music/pull/2917)
- chore(deps): update dependency vite to v6.0.11 [`#2894`](https://github.com/th-ch/youtube-music/pull/2894)
- chore(deps): update dependency electron to v34.0.1 [`#2916`](https://github.com/th-ch/youtube-music/pull/2916)
- chore(deps): update dependency electron-builder-squirrel-windows to v26.0.0-alpha.10 [`#2899`](https://github.com/th-ch/youtube-music/pull/2899)
- chore(deps): update dependency electron-builder to v26.0.0-alpha.10 [`#2898`](https://github.com/th-ch/youtube-music/pull/2898)
- chore(deps): update dependency typescript-eslint to v8.21.0 [`#2901`](https://github.com/th-ch/youtube-music/pull/2901)
- chore(deps): update dependency discord-api-types to v0.37.117 [`#2895`](https://github.com/th-ch/youtube-music/pull/2895)
- fix(deps): update dependency youtubei.js to v13 [`#2904`](https://github.com/th-ch/youtube-music/pull/2904)
- chore(deps): update dependency vite to v6.0.9 [security] [`#2907`](https://github.com/th-ch/youtube-music/pull/2907)
- fix(deps): update dependency happy-dom to v16.7.2 [`#2902`](https://github.com/th-ch/youtube-music/pull/2902)
- fix(discord-plugin): handle album name padding if length &lt; 2 [`#2903`](https://github.com/th-ch/youtube-music/pull/2903)
- feat(navigation): added nav icon padding [`#2905`](https://github.com/th-ch/youtube-music/pull/2905)
- chore(deps): update dependency rollup to v4.31.0 [`#2891`](https://github.com/th-ch/youtube-music/pull/2891)
- chore(deps): update dependency eslint-plugin-prettier to v5.2.3 [`#2889`](https://github.com/th-ch/youtube-music/pull/2889)
- chore(deps): update dependency vite-plugin-inspect to v10.0.7 [`#2882`](https://github.com/th-ch/youtube-music/pull/2882)
- fix(deps): update dependency hono to v4.6.17 [`#2883`](https://github.com/th-ch/youtube-music/pull/2883)
- fix: bump deps [`e9184e5`](https://github.com/th-ch/youtube-music/commit/e9184e5d60c2495473a7c3226ce9748ba89fceb3)
- fix(deps): fix pnpm [`040db75`](https://github.com/th-ch/youtube-music/commit/040db7539ccd1ae40f2632fdf38168cdaa26f112)
- chore(i18n): Translated using Weblate (Persian) [`9d18587`](https://github.com/th-ch/youtube-music/commit/9d185872dba5b56dabc691e56eafb13dc192b9cd)
#### [v3.7.2](https://github.com/th-ch/youtube-music/compare/v3.7.1...v3.7.2)
> 18 January 2025
- feat(api-server): add endpoint to get shuffle state [`#2792`](https://github.com/th-ch/youtube-music/pull/2792)
- chore(deps): update dependency discord-api-types to v0.37.116 [`#2877`](https://github.com/th-ch/youtube-music/pull/2877)
- chore(deps): update dependency eslint-plugin-prettier to v5.2.2 [`#2875`](https://github.com/th-ch/youtube-music/pull/2875)
- chore(deps): update eslint monorepo to v9.18.0 [`#2858`](https://github.com/th-ch/youtube-music/pull/2858)
- chore(deps): update dependency glob to v11.0.1 [`#2857`](https://github.com/th-ch/youtube-music/pull/2857)
- chore(deps): update dependency electron-builder-squirrel-windows to v26.0.0-alpha.9 [`#2874`](https://github.com/th-ch/youtube-music/pull/2874)
- chore(deps): update dependency electron to v34 [`#2867`](https://github.com/th-ch/youtube-music/pull/2867)
- chore(deps): update dependency eslint-config-prettier to v10 [`#2866`](https://github.com/th-ch/youtube-music/pull/2866)
- chore(deps): update dependency @stylistic/eslint-plugin-js to v2.13.0 [`#2864`](https://github.com/th-ch/youtube-music/pull/2864)
- chore(deps): update dependency typescript-eslint to v8.20.0 [`#2865`](https://github.com/th-ch/youtube-music/pull/2865)
- chore(deps): update dependency electron-builder to v26.0.0-alpha.9 [`#2869`](https://github.com/th-ch/youtube-music/pull/2869)
- fix: fix build.linux.desktop.entry [`#2859`](https://github.com/th-ch/youtube-music/pull/2859)
- feat(api-server): add endpoint to get volume state [`#2813`](https://github.com/th-ch/youtube-music/pull/2813)
- chore(deps): update dependency vite-plugin-inspect to v10 [`#2856`](https://github.com/th-ch/youtube-music/pull/2856)
- chore(deps): update dependency typescript to v5.7.3 [`#2855`](https://github.com/th-ch/youtube-music/pull/2855)
- fix(deps): update dependency @floating-ui/dom to v1.6.13 [`#2846`](https://github.com/th-ch/youtube-music/pull/2846)
- chore(deps): bump nanoid from 3.3.7 to 3.3.8 [`#2854`](https://github.com/th-ch/youtube-music/pull/2854)
- chore(deps): update dependency electron to v33.3.1 [`#2841`](https://github.com/th-ch/youtube-music/pull/2841)
- fix(deps): update dependency i18next to v24.2.1 [`#2840`](https://github.com/th-ch/youtube-music/pull/2840)
- chore(deps): update dependency typescript-eslint to v8.19.1 [`#2836`](https://github.com/th-ch/youtube-music/pull/2836)
- chore(deps): update dependency rollup to v4.30.1 [`#2833`](https://github.com/th-ch/youtube-music/pull/2833)
- fix(deps): update dependency solid-js to v1.9.4 [`#2849`](https://github.com/th-ch/youtube-music/pull/2849)
- fix(deps): update dependency fast-equals to v5.2.2 [`#2842`](https://github.com/th-ch/youtube-music/pull/2842)
- chore: Update README.md [`#2845`](https://github.com/th-ch/youtube-music/pull/2845)
- chore: Fixing the Content section in the README-ru.md file. [`#2847`](https://github.com/th-ch/youtube-music/pull/2847)
- chore: Create youtube-music-hu.svg [`#2844`](https://github.com/th-ch/youtube-music/pull/2844)
- chore: Create Transalated README-hu.md [`#2843`](https://github.com/th-ch/youtube-music/pull/2843)
- chore(deps): update dependency vite to v6.0.7 [`#2819`](https://github.com/th-ch/youtube-music/pull/2819)
- chore(deps): update dependency discord-api-types to v0.37.115 [`#2818`](https://github.com/th-ch/youtube-music/pull/2818)
- fix(deps): update dependency hono to v4.6.16 [`#2829`](https://github.com/th-ch/youtube-music/pull/2829)
- chore(deps): update dependency rollup to v4.29.2 [`#2832`](https://github.com/th-ch/youtube-music/pull/2832)
- fix(deps): update dependency fast-equals to v5.2.0 [`#2822`](https://github.com/th-ch/youtube-music/pull/2822)
- feat(api-server): add `insertPosition` for `addSongToQueue` [`#2808`](https://github.com/th-ch/youtube-music/pull/2808)
- chore(deps): update dependency typescript-eslint to v8.19.0 [`#2812`](https://github.com/th-ch/youtube-music/pull/2812)
- fix(deps): update dependency ts-morph to v25 [`#2810`](https://github.com/th-ch/youtube-music/pull/2810)
- fix(renderer): update event handler from onVolumeTap to onVolumeClick [`#2791`](https://github.com/th-ch/youtube-music/pull/2791)
- fix(deps): update dependency hono to v4.6.15 [`#2796`](https://github.com/th-ch/youtube-music/pull/2796)
- chore(deps): update dependency bufferutil to v4.0.9 [`#2787`](https://github.com/th-ch/youtube-music/pull/2787)
- feat: Refactor Menu Navigation and Update Media Control Icons [`#2783`](https://github.com/th-ch/youtube-music/pull/2783)
- fix(synced-lyrics): Revert font-size behavior for non-fancy modes [`#2788`](https://github.com/th-ch/youtube-music/pull/2788)
- fix(downloader): apply poToken [`#2863`](https://github.com/th-ch/youtube-music/issues/2863) [`#2780`](https://github.com/th-ch/youtube-music/issues/2780)
- chore(deps): update dependency electron-builder to v26 [`67fc0a4`](https://github.com/th-ch/youtube-music/commit/67fc0a415cae231a11f2846aadf01edb04f5c677)
- fix: fix lock file [`3339f99`](https://github.com/th-ch/youtube-music/commit/3339f997e3c2d4d2c32b3aee95c65d561f123fcb)
- chore(i18n): Translated using Weblate (Romanian) [`845dac3`](https://github.com/th-ch/youtube-music/commit/845dac3c0393dadea8efdd03ba1f41b1b36e6191)
#### [v3.7.1](https://github.com/th-ch/youtube-music/compare/v3.7.0...v3.7.1) #### [v3.7.1](https://github.com/th-ch/youtube-music/compare/v3.7.0...v3.7.1)
> 27 December 2024
- fix(deps): update dependency node-html-parser to v7 [`#2776`](https://github.com/th-ch/youtube-music/pull/2776) - fix(deps): update dependency node-html-parser to v7 [`#2776`](https://github.com/th-ch/youtube-music/pull/2776)
- chore(deps): update dependency vite to v6.0.6 [`#2774`](https://github.com/th-ch/youtube-music/pull/2774) - chore(deps): update dependency vite to v6.0.6 [`#2774`](https://github.com/th-ch/youtube-music/pull/2774)
- feat(api-server): Add queue api [`#2767`](https://github.com/th-ch/youtube-music/pull/2767) - feat(api-server): Add queue api [`#2767`](https://github.com/th-ch/youtube-music/pull/2767)

View File

@ -147,6 +147,11 @@ export default defineConfig({
resolve: { resolve: {
alias: resolveAlias, alias: resolveAlias,
}, },
server: {
cors: {
origin: 'https://music.youtube.com',
},
},
}; };
if (mode === 'development') { if (mode === 'development') {

View File

@ -2,7 +2,7 @@
"name": "youtube-music", "name": "youtube-music",
"desktopName": "com.github.th_ch.youtube_music", "desktopName": "com.github.th_ch.youtube_music",
"productName": "YouTube Music", "productName": "YouTube Music",
"version": "3.7.2", "version": "3.7.4",
"description": "YouTube Music Desktop App - including custom plugins", "description": "YouTube Music Desktop App - including custom plugins",
"main": "./dist/main/index.js", "main": "./dist/main/index.js",
"license": "MIT", "license": "MIT",
@ -222,18 +222,19 @@
}, },
"pnpm": { "pnpm": {
"overrides": { "overrides": {
"vite": "6.0.7", "vite": "6.1.0",
"node-gyp": "11.0.0", "node-gyp": "11.1.0",
"xml2js": "0.6.2", "xml2js": "0.6.2",
"node-fetch": "3.3.2", "node-fetch": "3.3.2",
"@electron/universal": "2.0.1", "@electron/universal": "2.0.1",
"@babel/runtime": "7.26.0" "@babel/runtime": "7.26.9"
}, },
"patchedDependencies": { "patchedDependencies": {
"vudio@2.1.1": "patches/vudio@2.1.1.patch", "vudio@2.1.1": "patches/vudio@2.1.1.patch",
"app-builder-lib@26.0.0-alpha.9": "patches/app-builder-lib@26.0.0-alpha.9.patch", "app-builder-lib@26.0.6": "patches/app-builder-lib@26.0.6.patch",
"@malept/flatpak-bundler": "patches/@malept__flatpak-bundler.patch" "@malept/flatpak-bundler": "patches/@malept__flatpak-bundler.patch"
} },
"neverBuiltDependencies": []
}, },
"dependencies": { "dependencies": {
"@electron-toolkit/tsconfig": "1.0.1", "@electron-toolkit/tsconfig": "1.0.1",
@ -242,12 +243,12 @@
"@ffmpeg.wasm/main": "0.12.0", "@ffmpeg.wasm/main": "0.12.0",
"@floating-ui/dom": "1.6.13", "@floating-ui/dom": "1.6.13",
"@foobar404/wave": "2.0.5", "@foobar404/wave": "2.0.5",
"@ghostery/adblocker-electron": "2.3.1", "@ghostery/adblocker-electron": "2.5.0",
"@ghostery/adblocker-electron-preload": "2.3.1", "@ghostery/adblocker-electron-preload": "2.5.0",
"@hono/node-server": "1.13.7", "@hono/node-server": "1.13.8",
"@hono/swagger-ui": "0.5.0", "@hono/swagger-ui": "0.5.0",
"@hono/zod-openapi": "0.18.3", "@hono/zod-openapi": "0.18.4",
"@hono/zod-validator": "0.4.2", "@hono/zod-validator": "0.4.3",
"@jellybrick/dbus-next": "0.10.3", "@jellybrick/dbus-next": "0.10.3",
"@jellybrick/electron-better-web-request": "1.0.4", "@jellybrick/electron-better-web-request": "1.0.4",
"@jellybrick/mpris-service": "2.1.5", "@jellybrick/mpris-service": "2.1.5",
@ -255,52 +256,51 @@
"@skyra/jaro-winkler": "1.1.1", "@skyra/jaro-winkler": "1.1.1",
"@xhayper/discord-rpc": "1.2.0", "@xhayper/discord-rpc": "1.2.0",
"async-mutex": "0.5.0", "async-mutex": "0.5.0",
"bgutils-js": "3.1.2", "bgutils-js": "3.1.3",
"butterchurn": "3.0.0-beta.4", "butterchurn": "3.0.0-beta.4",
"butterchurn-presets": "3.0.0-beta.4", "butterchurn-presets": "3.0.0-beta.4",
"color": "4.2.3", "color": "5.0.0",
"conf": "13.1.0", "conf": "13.1.0",
"custom-electron-prompt": "1.5.8", "custom-electron-prompt": "1.5.8",
"deepmerge-ts": "7.1.3", "deepmerge-ts": "7.1.4",
"electron-debug": "4.1.0", "electron-debug": "4.1.0",
"electron-is": "3.0.0", "electron-is": "3.0.0",
"electron-localshortcut": "3.2.1", "electron-localshortcut": "3.2.1",
"electron-store": "10.0.0", "electron-store": "10.0.1",
"electron-unhandled": "4.0.1", "electron-unhandled": "4.0.1",
"electron-updater": "6.3.9", "electron-updater": "6.3.9",
"fast-average-color": "9.4.0", "fast-average-color": "9.4.0",
"fast-equals": "5.2.2", "fast-equals": "5.2.2",
"filenamify": "6.0.0", "filenamify": "6.0.0",
"happy-dom": "16.6.0", "happy-dom": "17.1.0",
"hono": "4.6.16", "hono": "4.7.1",
"howler": "2.2.4", "howler": "2.2.4",
"html-to-text": "9.0.5", "html-to-text": "9.0.5",
"i18next": "24.2.1", "i18next": "24.2.2",
"jimp": "1.6.0", "jimp": "1.6.0",
"keyboardevent-from-electron-accelerator": "2.0.0", "keyboardevent-from-electron-accelerator": "2.0.0",
"keyboardevents-areequal": "0.2.2", "keyboardevents-areequal": "0.2.2",
"node-html-parser": "7.0.1", "node-html-parser": "7.0.1",
"node-id3": "0.2.6", "node-id3": "0.2.7",
"peerjs": "1.5.4", "peerjs": "1.5.4",
"semver": "7.6.3", "semver": "7.7.1",
"serve": "14.2.4", "serve": "14.2.4",
"simple-youtube-age-restriction-bypass": "github:organization/Simple-YouTube-Age-Restriction-Bypass#v2.5.9", "simple-youtube-age-restriction-bypass": "github:organization/Simple-YouTube-Age-Restriction-Bypass#v2.5.9",
"solid-floating-ui": "0.3.1", "solid-floating-ui": "0.3.1",
"solid-js": "1.9.4", "solid-js": "1.9.4",
"solid-styled-components": "0.28.5", "solid-styled-components": "0.28.5",
"solid-transition-group": "0.2.3", "solid-transition-group": "0.3.0",
"ts-morph": "25.0.0", "ts-morph": "25.0.1",
"vudio": "2.1.1", "vudio": "2.1.1",
"x11": "2.3.0", "x11": "2.3.0",
"youtubei.js": "12.2.0", "youtubei.js": "13.0.0",
"zod": "3.24.1" "zod": "3.24.2"
}, },
"devDependencies": { "devDependencies": {
"@eslint/js": "9.18.0", "@eslint/js": "9.20.0",
"@playwright/test": "1.49.1", "@playwright/test": "1.50.1",
"@stylistic/eslint-plugin-js": "2.13.0", "@stylistic/eslint-plugin-js": "3.1.0",
"@total-typescript/ts-reset": "0.6.1", "@total-typescript/ts-reset": "0.6.1",
"@types/color": "4.2.0",
"@types/electron-localshortcut": "3.1.3", "@types/electron-localshortcut": "3.1.3",
"@types/eslint__js": "8.42.3", "@types/eslint__js": "8.42.3",
"@types/howler": "2.2.12", "@types/howler": "2.2.12",
@ -311,30 +311,30 @@
"builtin-modules": "4.0.0", "builtin-modules": "4.0.0",
"cross-env": "7.0.3", "cross-env": "7.0.3",
"del-cli": "6.0.0", "del-cli": "6.0.0",
"discord-api-types": "0.37.116", "discord-api-types": "0.37.119",
"electron": "34.0.0", "electron": "34.2.0",
"electron-builder": "26.0.0-alpha.9", "electron-builder": "26.0.6",
"electron-builder-squirrel-windows": "26.0.0-alpha.9", "electron-builder-squirrel-windows": "26.0.6",
"electron-devtools-installer": "4.0.0", "electron-devtools-installer": "4.0.0",
"electron-vite": "2.3.0", "electron-vite": "3.0.0",
"esbuild": "0.24.2", "esbuild": "0.25.0",
"eslint": "9.18.0", "eslint": "9.20.1",
"eslint-config-prettier": "10.0.1", "eslint-config-prettier": "10.0.1",
"eslint-import-resolver-exports": "1.0.0-beta.5", "eslint-import-resolver-exports": "1.0.0-beta.5",
"eslint-import-resolver-typescript": "3.7.0", "eslint-import-resolver-typescript": "3.8.0",
"eslint-plugin-import": "2.31.0", "eslint-plugin-import": "2.31.0",
"eslint-plugin-prettier": "5.2.2", "eslint-plugin-prettier": "5.2.3",
"glob": "11.0.1", "glob": "11.0.1",
"node-gyp": "11.0.0", "node-gyp": "11.1.0",
"playwright": "1.49.1", "playwright": "1.50.1",
"rollup": "4.30.1", "rollup": "4.34.8",
"typescript": "5.7.3", "typescript": "5.7.3",
"typescript-eslint": "8.20.0", "typescript-eslint": "8.24.0",
"utf-8-validate": "6.0.5", "utf-8-validate": "6.0.5",
"vite": "6.0.7", "vite": "6.1.0",
"vite-plugin-inspect": "10.0.6", "vite-plugin-inspect": "10.2.1",
"vite-plugin-resolve": "2.5.2", "vite-plugin-resolve": "2.5.2",
"vite-plugin-solid": "2.11.0", "vite-plugin-solid": "2.11.1",
"ws": "8.18.0" "ws": "8.18.0"
}, },
"auto-changelog": { "auto-changelog": {

3399
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,6 @@
{ {
"$schema": "https://docs.renovatebot.com/renovate-schema.json", "$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [ "extends": ["config:recommended"],
"config:base"
],
"labels": ["dependencies"], "labels": ["dependencies"],
"postUpdateOptions": ["pnpmDedupe"] "postUpdateOptions": ["pnpmDedupe"]
} }

View File

@ -745,7 +745,8 @@
"label": "Efecto de la línea", "label": "Efecto de la línea",
"submenu": { "submenu": {
"fancy": { "fancy": {
"label": "Elegante" "label": "Elegante",
"tooltip": "Usar efectos grandes, similares a los de una aplicación, en la línea actual"
}, },
"focus": { "focus": {
"label": "Enfoque", "label": "Enfoque",

View File

@ -81,12 +81,12 @@
"menu": { "menu": {
"about": "درباره", "about": "درباره",
"navigation": { "navigation": {
"label": "ناوبری", "label": "کنترل‌های رابط",
"submenu": { "submenu": {
"copy-current-url": "کپی کردن URL فعلی", "copy-current-url": "کپی کردن لینک صفحه فعلی",
"go-back": "بازگشت", "go-back": "صفحه قبل",
"go-forward": "حرکت به جلو", "go-forward": "صفحه بعدی",
"quit": "خروجی", "quit": "خروج از برنامه",
"restart": "راه‌اندازی مجدد برنامه" "restart": "راه‌اندازی مجدد برنامه"
} }
}, },
@ -98,8 +98,8 @@
"submenu": { "submenu": {
"auto-reset-app-cache": "ریست کردن حافظه کش برنامه هنگام شروع", "auto-reset-app-cache": "ریست کردن حافظه کش برنامه هنگام شروع",
"disable-hardware-acceleration": "غیرفعال کردن شتاب سخت‌افزاری", "disable-hardware-acceleration": "غیرفعال کردن شتاب سخت‌افزاری",
"edit-config-json": "ویرایش config.json", "edit-config-json": "config.json ویرایش",
"override-user-agent": "تغییر User-Agent", "override-user-agent": "User-Agent تغییر",
"restart-on-config-changes": "راه‌اندازی مجدد در صورت تغییرات در پیکربندی", "restart-on-config-changes": "راه‌اندازی مجدد در صورت تغییرات در پیکربندی",
"set-proxy": { "set-proxy": {
"label": "تنظیم پراکسی", "label": "تنظیم پراکسی",
@ -109,7 +109,7 @@
"title": "تنظیم پراکسی" "title": "تنظیم پراکسی"
} }
}, },
"toggle-dev-tools": "باز کردن DevTools" "toggle-dev-tools": "DevTools باز کردن"
} }
}, },
"always-on-top": "همیشه در بالا", "always-on-top": "همیشه در بالا",
@ -168,7 +168,7 @@
}, },
"label": "تم", "label": "تم",
"submenu": { "submenu": {
"import-css-file": "وارد کردن فایل CSS سفارشی", "import-css-file": "سفارشی CSS وارد کردن فایل",
"no-theme": "بدون تم" "no-theme": "بدون تم"
} }
} }
@ -177,7 +177,7 @@
} }
}, },
"plugins": { "plugins": {
"enabled": "فعال", "enabled": "فعال/غیرفعال کردن",
"label": "افزونه‌ها", "label": "افزونه‌ها",
"new": "جدید" "new": "جدید"
}, },
@ -187,7 +187,7 @@
"force-reload": "اجبار به بارگذاری مجدد", "force-reload": "اجبار به بارگذاری مجدد",
"reload": "بارگذاری مجدد", "reload": "بارگذاری مجدد",
"reset-zoom": "اندازه واقعی", "reset-zoom": "اندازه واقعی",
"toggle-fullscreen": "تغییر به تمام‌صفحه", "toggle-fullscreen": "تغییر به تمام‌ صفحه",
"zoom-in": "بزرگنمایی", "zoom-in": "بزرگنمایی",
"zoom-out": "کوچکنمایی" "zoom-out": "کوچکنمایی"
} }
@ -219,7 +219,7 @@
"name": "مسدودکننده تبلیغات" "name": "مسدودکننده تبلیغات"
}, },
"album-actions": { "album-actions": {
"description": "افزودن دکمه‌های \"برگرفتن ناپسند\"، \"ناپسند\"، \"پسند\"، و \"حذف پسند\" برای اعمال آنها روی همه آهنگ‌ها در یک فهرست پخش یا آلبوم", "description": "اضافه کردن دکمه‌های عدم پسندیدن، پسندیدن و لغو پسندیدن برای اعمال این تغییرات به تمامی آهنگ‌های یک فهرست پخش یا آلبوم",
"name": "عملیات آلبوم" "name": "عملیات آلبوم"
}, },
"album-color-theme": { "album-color-theme": {
@ -250,7 +250,10 @@
} }
}, },
"opacity": { "opacity": {
"label": "شفافیت" "label": "شفافیت",
"submenu": {
"percent": "{{opacity}}%"
}
}, },
"quality": { "quality": {
"label": "کیفیت", "label": "کیفیت",
@ -259,7 +262,10 @@
} }
}, },
"size": { "size": {
"label": "اندازه" "label": "اندازه",
"submenu": {
"percent": "{{size}}%"
}
}, },
"smoothness-transition": { "smoothness-transition": {
"label": "انتقال نرمی", "label": "انتقال نرمی",
@ -273,8 +279,15 @@
}, },
"name": "حالت محیطی" "name": "حالت محیطی"
}, },
"amuse": {
"description": "حالا ویجت Amuse از YouTube Music هم پشتیبانی می‌کنه! (توسط 6K Labs)",
"name": "Amuse",
"response": {
"query": "سرور Amuse فعال است. برای دریافت اطلاعات آهنگ، از آدرس /query استفاده کنید."
}
},
"api-server": { "api-server": {
"description": "افزودن یک سرور API برای کنترل پخش‌کننده", "description": "برای کنترل پخش‌کننده API افزودن یک سرور",
"dialog": { "dialog": {
"request": { "request": {
"buttons": { "buttons": {
@ -304,14 +317,14 @@
"label": "پورت" "label": "پورت"
} }
}, },
"name": "سرور API [بتا]", "name": "[بتا]API سرور",
"prompt": { "prompt": {
"hostname": { "hostname": {
"label": "نام میزبان را برای سرور API وارد کنید (مثل 0.0.0.0):", "label": "وارد کنید (مثل 0.0.0.0): API نام میزبان را برای سرور",
"title": "نام میزبان" "title": "نام میزبان"
}, },
"port": { "port": {
"label": "پورت را برای سرور API وارد کنید:", "label": "وارد کنید: API پورت را برای سرور",
"title": "پورت" "title": "پورت"
} }
} }
@ -321,8 +334,8 @@
"name": "فشرده‌ساز صدا" "name": "فشرده‌ساز صدا"
}, },
"blur-nav-bar": { "blur-nav-bar": {
"description": "شفاف و محو کردن نوار ناوبری", "description": "شفاف و محو کردن نوار کنترل",
"name": "محو کردن نوار ناوبری" "name": "محو کردن نوار کنترل"
}, },
"bypass-age-restrictions": { "bypass-age-restrictions": {
"description": "دور زدن تأیید سن یوتیوب", "description": "دور زدن تأیید سن یوتیوب",
@ -381,27 +394,27 @@
}, },
"discord": { "discord": {
"backend": { "backend": {
"already-connected": "تلاش برای اتصال با اتصال فعال", "already-connected": "تلاش برای برقراری ارتباط با اتصال فعال",
"connected": "متصل به Discord", "connected": "متصل به دیسکورد",
"disconnected": "قطع اتصال از Discord" "disconnected": "ارتباط با دیسکورد قطع شد"
}, },
"description": "نمایش آنچه گوش می‌دهید به دوستان با Rich Presence", "description": "Rich Presence نمایش آنچه گوش می‌دهید به دوستان با",
"menu": { "menu": {
"auto-reconnect": "اتصال خودکار مجدد", "auto-reconnect": "اتصال خودکار",
"clear-activity": "پاک کردن فعالیت", "clear-activity": "پاک کردن فعالیت",
"clear-activity-after-timeout": "پاک کردن فعالیت پس از تایم‌اوت", "clear-activity-after-timeout": "حذف فعالیت پس از اتمام زمان تعیین‌شده",
"connected": "متصل", "connected": "اتصال برقرار شد",
"disconnected": "قطع شده", "disconnected": "اتصال قطع شد",
"hide-duration-left": "مخفی کردن مدت زمان باقی‌مانده", "hide-duration-left": "مخفی کردن مدت زمان باقی‌مانده",
"hide-github-button": "مخفی کردن دکمه لینک GitHub", "hide-github-button": "مخفی کردن دکمه لینک گیت هاب",
"play-on-youtube-music": "پخش در یوتیوب موسیقی", "play-on-youtube-music": "پخش در یوتیوب موزیک",
"set-inactivity-timeout": "تنظیم تایم‌اوت عدم فعالیت" "set-inactivity-timeout": "تنظیم زمان عدم فعالیت"
}, },
"name": "Rich Presence در Discord", "name": "Discord Rich Presence",
"prompt": { "prompt": {
"set-inactivity-timeout": { "set-inactivity-timeout": {
"label": "ورود تایم‌اوت عدم فعالیت به ثانیه:", "label": "محدودیت زمان عدم فعالیت را به ثانیه وارد کنید:",
"title": "تنظیم تایم‌اوت عدم فعالیت" "title": "تنظیم زمان عدم فعالیت"
} }
} }
}, },
@ -478,6 +491,18 @@
"button": "دانلود" "button": "دانلود"
} }
}, },
"equalizer": {
"description": "اضافه کردن یک اکولایزر به پخش‌کننده",
"menu": {
"presets": {
"label": "تنظیمات از پیش تعیین شده",
"list": {
"bass-booster": "تقویت‌کننده باس صدا"
}
}
},
"name": "اکولایزر"
},
"exponential-volume": { "exponential-volume": {
"description": "نوار لغزنده حجم را به صورت نمایی می‌سازد تا انتخاب حجم‌های پایین‌تر آسان‌تر شود.", "description": "نوار لغزنده حجم را به صورت نمایی می‌سازد تا انتخاب حجم‌های پایین‌تر آسان‌تر شود.",
"name": "حجم نمایی" "name": "حجم نمایی"
@ -490,17 +515,17 @@
"name": "منوی داخل برنامه" "name": "منوی داخل برنامه"
}, },
"lumiastream": { "lumiastream": {
"description": "افزودن پشتیبانی از Lumia Stream", "description": "Lumia Stream افزودن پشتیبانی از",
"name": "Lumia Stream [بتا]" "name": "Lumia Stream [بتا]"
}, },
"lyrics-genius": { "lyrics-genius": {
"description": "افزودن پشتیبانی از متن آهنگ برای بیشتر آهنگ‌ها", "description": "افزودن متن ترانه پشتیبان برای اکثر ترانه ها",
"menu": { "menu": {
"romanized-lyrics": "متن رومی‌شده" "romanized-lyrics": "الفبای لاتین برای آهنگ‌هایی با الفبای شرقی (فینگلیش)"
}, },
"name": "متن آهنگ Genius", "name": "Genius متن آهنگ",
"renderer": { "renderer": {
"fetched-lyrics": "متن آهنگ از Genius بازیابی شد" "fetched-lyrics": "بازیابی شد Genius متن ترانه توسط"
} }
}, },
"music-together": { "music-together": {
@ -536,13 +561,13 @@
"name": "Music Together [بتا]", "name": "Music Together [بتا]",
"toast": { "toast": {
"add-song-failed": "افزودن آهنگ با شکست مواجه شد", "add-song-failed": "افزودن آهنگ با شکست مواجه شد",
"closed": "Music Together بسته شد", "closed": "بسته شد Music Together",
"disconnected": "قطع اتصال Music Together", "disconnected": "Music Together قطع اتصال",
"host-failed": "میزبانی Music Together با شکست مواجه شد", "host-failed": "با شکست مواجه شد Music Together میزبانی",
"id-copied": "شناسه میزبان به کلیپ‌بورد کپی شد", "id-copied": "شناسه میزبان به کلیپ‌بورد کپی شد",
"id-copy-failed": "کپی شناسه میزبان به کلیپ‌بورد با شکست مواجه شد", "id-copy-failed": "کپی شناسه میزبان به کلیپ‌بورد با شکست مواجه شد",
"join-failed": "پیوستن به Music Together با شکست مواجه شد", "join-failed": "با شکست مواجه شد Music Together پیوستن به",
"joined": "به Music Together پیوست", "joined": "پیوست Music Together به",
"permission-changed": "مجوز Music Together به \"{{permission}}\" تغییر یافت", "permission-changed": "مجوز Music Together به \"{{permission}}\" تغییر یافت",
"remove-song-failed": "حذف آهنگ با شکست مواجه شد", "remove-song-failed": "حذف آهنگ با شکست مواجه شد",
"user-connected": "{{name}} به Music Together پیوست", "user-connected": "{{name}} به Music Together پیوست",
@ -551,11 +576,11 @@
}, },
"navigation": { "navigation": {
"description": "بعدی/قبلی به طور مستقیم در رابط یکپارچه شده‌اند، مانند مرورگر مورد علاقه شما", "description": "بعدی/قبلی به طور مستقیم در رابط یکپارچه شده‌اند، مانند مرورگر مورد علاقه شما",
"name": "ناوبری" "name": "کنترل های رابط"
}, },
"no-google-login": { "no-google-login": {
"description": "حذف دکمه‌های ورود به سیستم Google و لینک‌ها از رابط", "description": "حذف دکمه‌ها و لینک‌های ورود به گوگل از رابط کاربری",
"name": "بدون ورود به Google" "name": "بدون ورود به گوگل"
}, },
"notifications": { "notifications": {
"description": "نمایش اعلان هنگامی که آهنگی شروع به پخش می‌کند (اعلان‌های تعاملی در ویندوز در دسترس هستند)", "description": "نمایش اعلان هنگامی که آهنگی شروع به پخش می‌کند (اعلان‌های تعاملی در ویندوز در دسترس هستند)",
@ -566,11 +591,11 @@
"submenu": { "submenu": {
"hide-button-text": "مخفی کردن متن دکمه", "hide-button-text": "مخفی کردن متن دکمه",
"refresh-on-play-pause": "تازه‌سازی در پخش/توقف", "refresh-on-play-pause": "تازه‌سازی در پخش/توقف",
"tray-controls": "باز/بسته شدن با کلیک روی سینی" "tray-controls": "باز/بسته شدن با کلیک روی آیکون در نوار وظیفه"
} }
}, },
"priority": "اولویت اعلان", "priority": "اولویت اعلان",
"toast-style": "سبک Toast", "toast-style": "Toast سبک",
"unpause-notification": "نمایش اعلان هنگام از سرگیری پخش" "unpause-notification": "نمایش اعلان هنگام از سرگیری پخش"
}, },
"name": "اعلان‌ها" "name": "اعلان‌ها"
@ -578,8 +603,234 @@
"picture-in-picture": { "picture-in-picture": {
"description": "اجازه می‌دهد تا برنامه به حالت تصویر در تصویر تغییر کند", "description": "اجازه می‌دهد تا برنامه به حالت تصویر در تصویر تغییر کند",
"menu": { "menu": {
"always-on-top": "همیشه در بالا" "always-on-top": "همیشه در بالا",
"hotkey": {
"label": "کلید میانبر",
"prompt": {
"keybind-options": {
"hotkey": "کلید میانبر"
},
"label": "یک کلید میانبر انتخاب کنید برای فعال/غیرفعال کردن حالت تصویر در تصویر",
"title": "کلید میانبر برای حالت تصویر در تصویر"
}
},
"save-window-position": "ذخیره موقعیت پنجره",
"save-window-size": "ذخیره اندازه پنجره",
"use-native-pip": "استفاده از حالت تصویر در تصویر اصلی مرورگر"
},
"name": "تصویر در تصویر",
"templates": {
"button": "تصویر در تصویر"
} }
},
"playback-speed": {
"description": "به سرعت گوش بده، به آرامی گوش بده! یک دکمه کشویی برای تنظیم سرعت آهنگ اضافه شد",
"name": "سرعت پخش",
"templates": {
"button": "سرعت"
}
},
"precise-volume": {
"description": "کنترل دقیق صدا با استفاده از چرخ موس/میانبرها، همراه با HUD سفارشی و مراحل تنظیم حجم قابل تنظیم",
"menu": {
"arrows-shortcuts": "میانبرهای کلیدهای فلشی",
"custom-volume-steps": "مراحل تنظیم صدای دلخواه",
"global-shortcuts": "کلید های میانبر جهانی"
},
"name": "صدای دقیق",
"prompt": {
"global-shortcuts": {
"keybind-options": {
"decrease": "کاهش صدا",
"increase": "افزایش صدا"
},
"label": "انتخاب کلیدهای میانبر سراسری صدا:",
"title": "میانبرهای کلید سراسری صدا"
},
"volume-steps": {
"label": "مراحل انتخاب افزایش/کاهش صدا",
"title": "سطح صدا"
}
}
},
"quality-changer": {
"backend": {
"dialog": {
"quality-changer": {
"detail": "کیفیت کنونی: {{quality}}",
"message": "انتخاب کیفیت ویدیو:",
"title": "انتخاب کیفیت ویدیو"
}
}
},
"description": "امکان تغییر کیفیت ویدیو با استفاده از دکمه در رابط پخش ویدیو",
"name": "تغییر دهنده کیفیت ویدیو"
},
"scrobbler": {
"description": "اضافه کردن پشتیبانی از اسکرابلینگ (etc. last.fm, Listenbrainz)",
"dialog": {
"lastfm": {
"auth-failed": {
"message": "احراز هویت با Last.fm ناموفق بود\nپنجره شناور را تا راه‌اندازی مجدد بعدی مخفی کن.",
"title": "احراز هویت ناموفق بود"
}
}
},
"menu": {
"lastfm": {
"api-settings": "تنظیمات \"Last.fm \"API"
},
"listenbrainz": {
"token": "توکن کاربری ListenBrainz را وارد کنید"
},
"scrobble-other-media": "ردیابی رسانه‌های دیگر"
},
"name": "ابزار ثبت‌کننده‌ی آهنگ",
"prompt": {
"lastfm": {
"api-key": "کلید Last.fm API",
"api-secret": "API مخفی Last.fm"
},
"listenbrainz": {
"token": {
"label": "توکن کاربری ListenBrainz خود را وارد کنید:",
"title": "توکن ListenBrainz"
}
}
}
},
"shortcuts": {
"description": "امکان تنظیم میانبرهای سراسری برای کنترل (پخش/توقف/بعدی/قبلی) و خاموش کردن OSD رسانه با بازنویسی کلیدهای رسانه‌ای، فعال‌سازی Ctrl/CMD + F برای جستجو، فعال‌سازی پشتیبانی MPRIS در لینوکس برای کلیدهای رسانه‌ای، و میانبرهای سفارشی برای کاربران پیشرفته",
"menu": {
"override-media-keys": "تغییر عملکرد کلیدهای رسانه‌",
"set-keybinds": "تنظیم کنترل‌های سراسری آهنگ"
},
"name": "میانبرها (& MPRIS)",
"prompt": {
"keybind": {
"keybind-options": {
"next": "بعدی",
"play-pause": "پخش / توقف",
"previous": "قبلی"
},
"label": "انتخاب میانبرهای سراسری برای کنترل آهنگ‌ها:",
"title": "میانبرهای کلیدی سراسری"
}
}
},
"skip-disliked-songs": {
"description": "خودکار آهنگ های غیر موردعلاقه رد میشن",
"name": "رد آهنگ‌های غیر مورد علاقه"
},
"skip-silences": {
"description": "رد خودکار بخش‌های بی صدا آهنگ ها",
"name": "رد بخش‌های بی‌صدا"
},
"sponsorblock": {
"description": "به‌طور خودکار بخش‌های غیرموسیقی مانند مقدمه/پایان یا قسمت‌هایی از ویدیوهای موسیقی که آهنگ در آن پخش نمی‌شود را رد می‌کند",
"name": "مسدودکننده اسپانسر"
},
"synced-lyrics": {
"description": "ارائه متن ترانه‌ها به صورت هماهنگ با آهنگ‌ها، با استفاده از ارائه‌دهندگانی مانند LRClib.",
"errors": {
"fetch": "⚠️هنگام بارگیری متن ترانه خطایی رخ داده است.\n\tلطفاً بعداً دوباره تلاش کنید.",
"not-found": "⚠️ متنی برای این ترانه پیدا نشد."
},
"menu": {
"default-text-string": {
"label": "حرف/کاراکتر پیش‌فرض بین متن‌های ترانه",
"tooltip": "حرف/کاراکتر پیش‌فرض را برای فاصله بین متن‌های ترانه انتخاب کنید"
},
"line-effect": {
"label": "افکت خط متن",
"submenu": {
"fancy": {
"label": "شیک",
"tooltip": "استفاده از افکت‌های بزرگ و شبیه به اپلیکیشن‌ها برای خط فعلی"
},
"focus": {
"label": "تمرکز",
"tooltip": "فقط خط فعلی رو سفید کن"
},
"offset": {
"label": "جابجایی",
"tooltip": "جابجایی خط فعلی به سمت راست"
},
"scale": {
"label": "مقیاس",
"tooltip": "تغییر اندازه خط فعلی"
}
},
"tooltip": "افکت مورد نظر را برای خط فعلی انتخاب کنید"
},
"precise-timing": {
"label": "هماهنگ‌سازی کامل متن ترانه‌",
"tooltip": "محاسبه دقیق نمایش خط بعدی تا میلی‌ثانیه (ممکن است تاثیر کمی بر عملکرد داشته باشد)"
},
"show-lyrics-even-if-inexact": {
"label": "نمایش متن ترانه ها حتی اگر دقیق نباشد",
"tooltip": "اگر آهنگ پیدا نشد، افزونه دوباره با یک جستجوی متفاوت امتحان می‌کند.\nنتیجهی این تلاش ممکن است دقیق نباشد."
},
"show-time-codes": {
"label": "نمایش زمان‌بندی‌ها",
"tooltip": "نمایش زمان‌بندی‌ها کنار متن ترانه‌"
}
},
"name": "متن ترانه هماهنگ شد",
"refetch-btn": {
"fetching": "در حال بارگذاری...",
"normal": "دریافت مجدد متن ترانه"
},
"warnings": {
"duration-mismatch": "⚠️ - ممکن است متن ترانه به دلیل عدم تطابق زمان با مشکل هماهنگی مواجه شود.",
"inexact": "⚠️ - ممکن است متن ترانه برای این آهنگ دقیق نباشد",
"instrumental": "⚠️ - این آهنگ بی کلام است"
}
},
"taskbar-mediacontrol": {
"description": "کنترل پخش از نوار وظیفه ویندوز(taskbar)",
"name": "کنترل رسانه از نوار وظیفه (taskbar)"
},
"touchbar": {
"description": "افزودن ویجت TouchBar برای کاربران macOS",
"name": "نوار لمسی"
},
"tuna-obs": {
"description": "ادغام با پلاگین Tuna در OBS",
"name": "Tuna OBS"
},
"video-toggle": {
"description": "دکمه‌ای اضافه می‌کند برای جابجایی بین حالت ویدیو/آهنگ. همچنین به صورت اختیاری می‌تواند تب ویدیو را حذف کند",
"menu": {
"align": {
"label": "چینش",
"submenu": {
"left": "چپ",
"middle": "میانه",
"right": "راست"
}
},
"force-hide": "حذف اجباری تب ویدیو",
"mode": {
"label": "حالت",
"submenu": {
"custom": "حالت شخصی‌سازی شده",
"disabled": "غیرفعال",
"native": "حالت پیشفرض"
}
}
},
"name": "ویدیو به آهنگ",
"templates": {
"button": "ترانه"
}
},
"visualizer": {
"description": "اضافه کردن نمایش‌دهنده تصویری به پخش‌کننده",
"menu": {
"visualizer-type": "نوع نمایش‌دهنده تصویری"
},
"name": "نمایش‌دهنده تصویری"
} }
} }
} }

View File

@ -279,6 +279,12 @@
}, },
"name": "Ambient Mode" "name": "Ambient Mode"
}, },
"amuse": {
"description": "Nagdaragdag ng suporta sa YouTube Music para sa Amuse now playing widget ng 6K Labs",
"response": {
"query": "Tumatakbo ang Amuse API server. Gamitin ang GET /query para makuha ang impo ng kanta."
}
},
"api-server": { "api-server": {
"description": "Nagdadagdag ng API Server upang kontrolin ang player", "description": "Nagdadagdag ng API Server upang kontrolin ang player",
"dialog": { "dialog": {
@ -468,6 +474,14 @@
"button": "Mag-download" "button": "Mag-download"
} }
}, },
"equalizer": {
"description": "Nagdaragdag ng equalizer sa player",
"menu": {
"presets": {
"label": "Mga Preset"
}
}
},
"exponential-volume": { "exponential-volume": {
"description": "Ginagawang exponential ang volume slider para mas madaling pumili ng mas mababang volume." "description": "Ginagawang exponential ang volume slider para mas madaling pumili ng mas mababang volume."
}, },
@ -674,8 +688,8 @@
"synced-lyrics": { "synced-lyrics": {
"description": "Nagbibigay ng naka-sync na lyrics sa mga kanta, gamit ang mga provider tulad ng LRClib.", "description": "Nagbibigay ng naka-sync na lyrics sa mga kanta, gamit ang mga provider tulad ng LRClib.",
"errors": { "errors": {
"fetch": "⚠️ - Nagkaroon ng error habang kinukuha ang lyrics. Subukang muli mamaya.", "fetch": "⚠️\t Nagkaroon ng error habang kinukuha ang lyrics.\n\t Subukang muli mamaya.",
"not-found": "⚠️ - Walang nakitang lyrics para sa kantang ito." "not-found": "⚠️ Walang nakitang lyrics para sa kantang ito."
}, },
"menu": { "menu": {
"default-text-string": { "default-text-string": {
@ -685,6 +699,10 @@
"line-effect": { "line-effect": {
"label": "Effect ng Linya", "label": "Effect ng Linya",
"submenu": { "submenu": {
"fancy": {
"label": "Magarbo",
"tooltip": "Gumamit ng malaki, mala-app na effect sa kasalukuyang linya"
},
"focus": { "focus": {
"tooltip": "Gawing puti lamang ang kasalukuyang linya" "tooltip": "Gawing puti lamang ang kasalukuyang linya"
}, },

View File

@ -53,7 +53,8 @@
"later": "אחר כך", "later": "אחר כך",
"restart-now": "מתחיל את התוכנה מחדש עכשיו" "restart-now": "מתחיל את התוכנה מחדש עכשיו"
}, },
"message": "נדרש אתחול", "detail": "\"{{pluginName}}\" מצריך אתחול",
"message": "\"{{pluginName}}\" דורש אתחול",
"title": "נדרשת הפעלה מחדש" "title": "נדרשת הפעלה מחדש"
}, },
"unresponsive": { "unresponsive": {
@ -70,9 +71,10 @@
"buttons": { "buttons": {
"disable": "בטל עדכונים", "disable": "בטל עדכונים",
"download": "הורדה", "download": "הורדה",
"ok": "אוקי" "ok": "אוקיי"
}, },
"message": ירסא חדשה זמינה כעת", "detail": "גרסה חדשה זמינה, ניתן להוריד אותה ב-{{downloadLink}}",
"message": "גירסה חדשה זמינה כעת",
"title": "קיים עדכון חדש" "title": "קיים עדכון חדש"
} }
}, },

View File

@ -226,10 +226,50 @@
"description": "एल्बम रंग पैलेट के आधार पर एक गतिशील थीम और दृश्य प्रभाव लागू करता है", "description": "एल्बम रंग पैलेट के आधार पर एक गतिशील थीम और दृश्य प्रभाव लागू करता है",
"menu": { "menu": {
"color-mix-ratio": { "color-mix-ratio": {
"label": "रंग मिश्रण अनुपात",
"submenu": { "submenu": {
"percent": "{{ratio}}%" "percent": "{{ratio}}%"
} }
} }
},
"name": "एल्बम रंग थीम"
},
"ambient-mode": {
"description": "वीडियो से हल्के रंगों को आपकी स्क्रीन की पृष्ठभूमि में डालकर एक प्रकाश प्रभाव लागू करता है",
"menu": {
"blur-amount": {
"label": "धुंधलापन मात्रा",
"submenu": {
"pixels": "{{blurAmount}} पिक्सल"
}
},
"buffer": {
"label": "बफर",
"submenu": {
"buffer": "{{buffer}}"
}
},
"opacity": {
"label": "अस्पष्टता",
"submenu": {
"percent": "{{opacity}}%"
}
},
"quality": {
"label": "गुणवत्ता",
"submenu": {
"pixels": "{{quality}} पिक्सल"
}
},
"size": {
"label": "माप",
"submenu": {
"percent": "{{size}}%"
}
},
"smoothness-transition": {
"label": "चिकनाई संक्रमण"
}
} }
}, },
"video-toggle": { "video-toggle": {

View File

@ -279,6 +279,13 @@
}, },
"name": "Mode ambient" "name": "Mode ambient"
}, },
"amuse": {
"description": "Menambahkan dukungan YouTube Music untuk widget Amuse yang sedang diputar oleh 6K Labs",
"name": "Amuse",
"response": {
"query": "Server API Amuse sedang berjalan. GET /query untuk mendapatkan info lagu."
}
},
"api-server": { "api-server": {
"description": "Menambahkan server API untuk mengontrol pemutar", "description": "Menambahkan server API untuk mengontrol pemutar",
"dialog": { "dialog": {
@ -726,8 +733,8 @@
"synced-lyrics": { "synced-lyrics": {
"description": "Menyediakan lirik lagu yang disinkronkan, menggunakan penyedia seperti LRClib.", "description": "Menyediakan lirik lagu yang disinkronkan, menggunakan penyedia seperti LRClib.",
"errors": { "errors": {
"fetch": "⚠️ - Terjadi kesalahan saat mengambil lirik. Coba lagi nanti.", "fetch": "⚠️\tTerjadi kesalahan saat mengambil lirik.\n\tSilakan coba lagi nanti.",
"not-found": "⚠️ - Tidak ada lirik yang ditemukan untuk lagu ini." "not-found": "⚠️ Tidak ada lirik yang ditemukan untuk lagu ini."
}, },
"menu": { "menu": {
"default-text-string": { "default-text-string": {
@ -737,6 +744,10 @@
"line-effect": { "line-effect": {
"label": "Efek garis", "label": "Efek garis",
"submenu": { "submenu": {
"fancy": {
"label": "Mewah",
"tooltip": "Gunakan efek besar seperti aplikasi pada baris saat ini"
},
"focus": { "focus": {
"label": "Fokus", "label": "Fokus",
"tooltip": "Jadikan hanya baris saat ini berwarna putih" "tooltip": "Jadikan hanya baris saat ini berwarna putih"

View File

@ -279,6 +279,12 @@
}, },
"name": "Chế độ Môi trường xung quanh" "name": "Chế độ Môi trường xung quanh"
}, },
"amuse": {
"name": "Amuse",
"response": {
"query": "Máy chủ API của Amuse đang chạy. GET /query để lấy thông tin về bài hát."
}
},
"api-server": { "api-server": {
"description": "Thêm máy chủ API để điều khiển trình phát", "description": "Thêm máy chủ API để điều khiển trình phát",
"dialog": { "dialog": {
@ -299,7 +305,7 @@
"label": "Xác thực ngay yêu cầu đầu tiên" "label": "Xác thực ngay yêu cầu đầu tiên"
}, },
"none": { "none": {
"label": "Không/Chưa xác thực (Need context)" "label": "Không xác thực"
} }
} }
}, },

View File

@ -134,14 +134,6 @@ if (is.linux()) {
// Overrides WM_CLASS for X11 to correspond to icon filename // Overrides WM_CLASS for X11 to correspond to icon filename
app.setName('com.github.th_ch.youtube_music'); app.setName('com.github.th_ch.youtube_music');
// Workaround for issue #2248
if (
process.env.XDG_SESSION_TYPE === 'wayland' ||
process.env.WAYLAND_DISPLAY
) {
app.commandLine.appendSwitch('disable-gpu-memory-buffer-video-frames');
}
// Stops chromium from launching its own MPRIS service // Stops chromium from launching its own MPRIS service
if (config.plugins.isEnabled('shortcuts')) { if (config.plugins.isEnabled('shortcuts')) {
app.commandLine.appendSwitch('disable-features', 'MediaSessionService'); app.commandLine.appendSwitch('disable-features', 'MediaSessionService');

View File

@ -1,5 +1,5 @@
import { FastAverageColor } from 'fast-average-color'; import { FastAverageColor } from 'fast-average-color';
import Color from 'color'; import Color, { ColorInstance } from 'color';
import style from './style.css?inline'; import style from './style.css?inline';
@ -14,8 +14,8 @@ export default createPlugin<
unknown, unknown,
unknown, unknown,
{ {
color?: Color; color?: ColorInstance;
darkColor?: Color; darkColor?: ColorInstance;
playerPage: HTMLElement | null; playerPage: HTMLElement | null;
navBarBackground: HTMLElement | null; navBarBackground: HTMLElement | null;

View File

@ -154,15 +154,14 @@ export const backend = createBackend<
// @see https://discord.com/developers/docs/topics/gateway#activity-object // @see https://discord.com/developers/docs/topics/gateway#activity-object
// not all options are transfered through https://github.com/discordjs/RPC/blob/6f83d8d812c87cb7ae22064acd132600407d7d05/src/client.js#L518-530 // not all options are transfered through https://github.com/discordjs/RPC/blob/6f83d8d812c87cb7ae22064acd132600407d7d05/src/client.js#L518-530
const hangulFillerUnicodeCharacter = '\u3164'; // This is an empty character const hangulFillerUnicodeCharacter = '\u3164'; // This is an empty character
if (songInfo.title.length < 2) { const paddedInfoKeys: (keyof SongInfo)[] = ['title', 'artist', 'album'];
songInfo.title += hangulFillerUnicodeCharacter.repeat( for (const key of paddedInfoKeys) {
2 - songInfo.title.length, const keyLength = (songInfo[key] as string)?.length;
); if (keyLength < 2) {
} (songInfo[key] as string) += hangulFillerUnicodeCharacter.repeat(
if (songInfo.artist.length < 2) { 2 - keyLength,
songInfo.artist += hangulFillerUnicodeCharacter.repeat( );
2 - songInfo.title.length, }
);
} }
// see https://github.com/th-ch/youtube-music/issues/1664 // see https://github.com/th-ch/youtube-music/issues/1664

View File

@ -55,10 +55,20 @@ let yt: Innertube;
let win: BrowserWindow; let win: BrowserWindow;
let playingUrl: string; let playingUrl: string;
const isYouTubePremium = () => const isYouTubeMusicPremium = async () => {
win.webContents.executeJavaScript( const upgradeBtnIconPathData = (await win.webContents.executeJavaScript(
'!document.querySelector(\'#endpoint[href="/music_premium"]\')', 'document.querySelector(\'iron-iconset-svg[name="yt-sys-icons"] #youtube_music_monochrome\')?.firstChild?.getAttribute("d")?.substring(0, 15)',
) as Promise<boolean>; )) as string | null;
// Fallback to non-premium if the icon is not found
if (!upgradeBtnIconPathData) return false;
const selector = `ytmusic-guide-entry-renderer:has(> tp-yt-paper-item > yt-icon path[d^="${upgradeBtnIconPathData}"])`;
return (await win.webContents.executeJavaScript(
`!document.querySelector('${selector}')`,
)) as boolean;
};
const sendError = (error: Error, source?: string) => { const sendError = (error: Error, source?: string) => {
win.setProgressBar(-1); // Close progress bar win.setProgressBar(-1); // Close progress bar
@ -116,6 +126,11 @@ export const onMainLoad = async ({
const visitorData = yt.session.context.client.visitorData; const visitorData = yt.session.context.client.visitorData;
if (visitorData) { if (visitorData) {
const cleanUp = (context: Partial<typeof globalThis>) => {
delete context.window;
delete context.document;
};
try { try {
const [width, height] = win.getSize(); const [width, height] = win.getSize();
// emulate jsdom using linkedom // emulate jsdom using linkedom
@ -153,16 +168,16 @@ export const onMainLoad = async ({
program: bgChallenge.program, program: bgChallenge.program,
globalName: bgChallenge.globalName, globalName: bgChallenge.globalName,
bgConfig, bgConfig,
}).finally(() => {
cleanUp(globalThis);
}); });
yt.session.po_token = poTokenResult.poToken; yt.session.po_token = poTokenResult.poToken;
} else {
cleanUp(globalThis);
} }
} finally { } catch {
// Bypass TypeScript checks cleanUp(globalThis);
((x: Partial<typeof globalThis>) => {
delete x.window;
delete x.document;
})(globalThis);
} }
} }
@ -365,7 +380,7 @@ async function downloadSongUnsafe(
} }
const downloadOptions: FormatOptions = { const downloadOptions: FormatOptions = {
type: (await isYouTubePremium()) ? 'audio' : 'video+audio', // Audio, video or video+audio type: (await isYouTubeMusicPremium()) ? 'audio' : 'video+audio', // Audio, video or video+audio
quality: 'best', // Best, bestefficiency, 144p, 240p, 480p, 720p and so on. quality: 'best', // Best, bestefficiency, 144p, 240p, 480p, 720p and so on.
format: 'any', // Media container format format: 'any', // Media container format
}; };

View File

@ -14,7 +14,7 @@
align-items: center; align-items: center;
color: rgba(255, 255, 255, 0.5); color: rgba(255, 255, 255, 0.5);
cursor: pointer; cursor: pointer;
margin: 0 var(--ytd-rich-grid-item-margin); margin: 0 var(--ytd-margin-2x, 8px);
} }
.navigation-item:hover { .navigation-item:hover {
@ -32,4 +32,5 @@
width: var(--iron-icon-width, 24px); width: var(--iron-icon-width, 24px);
height: var(--iron-icon-height, 24px); height: var(--iron-icon-height, 24px);
animation: var(--iron-icon_-_animation); animation: var(--iron-icon_-_animation);
padding: var(--ytd-margin-base, 4px) var(--ytd-margin-2x, 8px);
} }

View File

@ -50,7 +50,7 @@ export const SyncedLine = ({ line }: SyncedLineProps) => {
_ytAPI?.seekTo(line.timeInMs / 1000); _ytAPI?.seekTo(line.timeInMs / 1000);
}} }}
> >
<div class="text-lyrics description ytmusic-description-shelf-renderer"> <div dir="auto" class="text-lyrics description ytmusic-description-shelf-renderer">
<yt-formatted-string <yt-formatted-string
text={{ text={{
runs: [{ text: config()?.showTimeCodes ? `[${line.time}] ` : '' }], runs: [{ text: config()?.showTimeCodes ? `[${line.time}] ` : '' }],

View File

@ -71,7 +71,7 @@ export function throttle<T extends (...params: unknown[]) => unknown>(
}) as T; }) as T;
} }
function memoize<T extends (...params: unknown[]) => unknown>(fn: T): T { export function memoize<T extends (...params: unknown[]) => unknown>(fn: T): T {
const cache = new Map(); const cache = new Map();
return ((...args) => { return ((...args) => {
@ -84,7 +84,7 @@ function memoize<T extends (...params: unknown[]) => unknown>(fn: T): T {
}) as T; }) as T;
} }
function retry<T extends (...params: unknown[]) => Promise<unknown>>( export function retry<T extends (...params: unknown[]) => Promise<unknown>>(
fn: T, fn: T,
{ retries = 3, delay = 1000 } = {}, { retries = 3, delay = 1000 } = {},
) { ) {
@ -102,12 +102,3 @@ function retry<T extends (...params: unknown[]) => Promise<unknown>>(
throw latestError; throw latestError;
}; };
} }
export default {
singleton,
debounce,
cache,
throttle,
memoize,
retry,
};

View File

@ -7,15 +7,15 @@ import { Project } from 'ts-morph';
const snakeToCamel = (text: string) => const snakeToCamel = (text: string) =>
text.replace(/-(\w)/g, (_, letter: string) => letter.toUpperCase()); text.replace(/-(\w)/g, (_, letter: string) => letter.toUpperCase());
export const i18nImporter = () => { const __dirname = dirname(fileURLToPath(import.meta.url));
const __dirname = dirname(fileURLToPath(import.meta.url)); const globalProject = new Project({
const project = new Project({ tsConfigFilePath: resolve(__dirname, '..', 'tsconfig.json'),
tsConfigFilePath: resolve(__dirname, '..', 'tsconfig.json'), skipAddingFilesFromTsConfig: true,
skipAddingFilesFromTsConfig: true, skipLoadingLibFiles: true,
skipLoadingLibFiles: true, skipFileDependencyResolution: true,
skipFileDependencyResolution: true, });
});
export const i18nImporter = () => {
const srcPath = resolve(__dirname, '..', 'src'); const srcPath = resolve(__dirname, '..', 'src');
const plugins = globSync(['src/i18n/resources/*.json']).map((path) => { const plugins = globSync(['src/i18n/resources/*.json']).map((path) => {
const nameWithExt = basename(path); const nameWithExt = basename(path);
@ -24,24 +24,28 @@ export const i18nImporter = () => {
return { name, path }; return { name, path };
}); });
const src = project.createSourceFile('vm:i18n', (writer) => { const src = globalProject.createSourceFile(
// prettier-ignore 'vm:i18n',
for (const { name, path } of plugins) { (writer) => {
// prettier-ignore
for (const { name, path } of plugins) {
const relativePath = relative(resolve(srcPath, '..'), path).replace(/\\/g, '/'); const relativePath = relative(resolve(srcPath, '..'), path).replace(/\\/g, '/');
writer.writeLine(`import ${snakeToCamel(name)}Json from "./${relativePath}";`); writer.writeLine(`import ${snakeToCamel(name)}Json from "./${relativePath}";`);
} }
writer.blankLine(); writer.blankLine();
writer.writeLine('export const languageResources = {'); writer.writeLine('export const languageResources = {');
for (const { name } of plugins) { for (const { name } of plugins) {
writer.writeLine(` "${name}": {`); writer.writeLine(` "${name}": {`);
writer.writeLine(` translation: ${snakeToCamel(name)}Json,`); writer.writeLine(` translation: ${snakeToCamel(name)}Json,`);
writer.writeLine(' },'); writer.writeLine(' },');
} }
writer.writeLine('};'); writer.writeLine('};');
writer.blankLine(); writer.blankLine();
}); },
{ overwrite: true },
);
return src.getText(); return src.getText();
}; };

View File

@ -7,17 +7,17 @@ import { Project } from 'ts-morph';
const snakeToCamel = (text: string) => const snakeToCamel = (text: string) =>
text.replace(/-(\w)/g, (_, letter: string) => letter.toUpperCase()); text.replace(/-(\w)/g, (_, letter: string) => letter.toUpperCase());
const __dirname = dirname(fileURLToPath(import.meta.url));
const globalProject = new Project({
tsConfigFilePath: resolve(__dirname, '..', 'tsconfig.json'),
skipAddingFilesFromTsConfig: true,
skipLoadingLibFiles: true,
skipFileDependencyResolution: true,
});
export const pluginVirtualModuleGenerator = ( export const pluginVirtualModuleGenerator = (
mode: 'main' | 'preload' | 'renderer', mode: 'main' | 'preload' | 'renderer',
) => { ) => {
const __dirname = dirname(fileURLToPath(import.meta.url));
const project = new Project({
tsConfigFilePath: resolve(__dirname, '..', 'tsconfig.json'),
skipAddingFilesFromTsConfig: true,
skipLoadingLibFiles: true,
skipFileDependencyResolution: true,
});
const srcPath = resolve(__dirname, '..', 'src'); const srcPath = resolve(__dirname, '..', 'src');
const plugins = globSync([ const plugins = globSync([
'src/plugins/*/index.{js,ts}', 'src/plugins/*/index.{js,ts}',
@ -35,35 +35,39 @@ export const pluginVirtualModuleGenerator = (
return { name, path }; return { name, path };
}); });
const src = project.createSourceFile('vm:pluginIndexes', (writer) => { const src = globalProject.createSourceFile(
// prettier-ignore 'vm:pluginIndexes',
for (const { name, path } of plugins) { (writer) => {
// prettier-ignore
for (const { name, path } of plugins) {
const relativePath = relative(resolve(srcPath, '..'), path).replace(/\\/g, '/'); const relativePath = relative(resolve(srcPath, '..'), path).replace(/\\/g, '/');
writer.writeLine(`import ${snakeToCamel(name)}Plugin, { pluginStub as ${snakeToCamel(name)}PluginStub } from "./${relativePath}";`); writer.writeLine(`import ${snakeToCamel(name)}Plugin, { pluginStub as ${snakeToCamel(name)}PluginStub } from "./${relativePath}";`);
} }
writer.blankLine(); writer.blankLine();
// Context-specific exports // Context-specific exports
writer.writeLine(`export const ${mode}Plugins = {`); writer.writeLine(`export const ${mode}Plugins = {`);
for (const { name } of plugins) { for (const { name } of plugins) {
const checkMode = mode === 'main' ? 'backend' : mode; const checkMode = mode === 'main' ? 'backend' : mode;
// HACK: To avoid situation like importing renderer plugins in main // HACK: To avoid situation like importing renderer plugins in main
writer.writeLine( writer.writeLine(
` ...(${snakeToCamel(name)}Plugin['${checkMode}'] ? { "${name}": ${snakeToCamel(name)}Plugin } : {}),`, ` ...(${snakeToCamel(name)}Plugin['${checkMode}'] ? { "${name}": ${snakeToCamel(name)}Plugin } : {}),`,
); );
} }
writer.writeLine('};'); writer.writeLine('};');
writer.blankLine(); writer.blankLine();
// All plugins export (stub only) // Omit<Plugin, 'backend' | 'preload' | 'renderer'> // All plugins export (stub only) // Omit<Plugin, 'backend' | 'preload' | 'renderer'>
writer.writeLine('export const allPlugins = {'); writer.writeLine('export const allPlugins = {');
for (const { name } of plugins) { for (const { name } of plugins) {
writer.writeLine(` "${name}": ${snakeToCamel(name)}PluginStub,`); writer.writeLine(` "${name}": ${snakeToCamel(name)}PluginStub,`);
} }
writer.writeLine('};'); writer.writeLine('};');
writer.blankLine(); writer.blankLine();
}); },
{ overwrite: true },
);
return src.getText(); return src.getText();
}; };

View File

@ -1,4 +1,4 @@
import { readFile } from 'node:fs/promises'; import { readFileSync } from 'node:fs';
import { resolve, basename, dirname } from 'node:path'; import { resolve, basename, dirname } from 'node:path';
import { fileURLToPath } from 'node:url'; import { fileURLToPath } from 'node:url';
@ -8,10 +8,34 @@ import {
ts, ts,
ObjectLiteralExpression, ObjectLiteralExpression,
VariableDeclarationKind, VariableDeclarationKind,
Node,
type ObjectLiteralElementLike,
} from 'ts-morph'; } from 'ts-morph';
import type { PluginOption } from 'vite'; import type { PluginOption } from 'vite';
// Initialize a global project instance to reuse across load calls
const __dirname = dirname(fileURLToPath(import.meta.url));
const globalProject = new Project({
tsConfigFilePath: resolve(__dirname, '..', 'tsconfig.json'),
skipAddingFilesFromTsConfig: true,
skipLoadingLibFiles: true,
skipFileDependencyResolution: true,
});
// Helper to extract a propertys name from its node
const getPropertyName = (prop: Node): string | null => {
const kind = prop.getKind();
if (
kind === ts.SyntaxKind.PropertyAssignment ||
kind === ts.SyntaxKind.ShorthandPropertyAssignment ||
kind === ts.SyntaxKind.MethodDeclaration
) {
return prop.getFirstChildByKindOrThrow(ts.SyntaxKind.Identifier).getText();
}
return null;
};
export default function ( export default function (
mode: 'backend' | 'preload' | 'renderer' | 'none', mode: 'backend' | 'preload' | 'renderer' | 'none',
): PluginOption { ): PluginOption {
@ -22,131 +46,96 @@ export default function (
return { return {
name: 'ytm-plugin-loader', name: 'ytm-plugin-loader',
async load(id) { load(id) {
if (!pluginFilter(id)) return null; if (!pluginFilter(id)) return null;
const __dirname = dirname(fileURLToPath(import.meta.url)); // Read file asynchronously
const fileContent = readFileSync(id, 'utf8');
const project = new Project({ // Create or update source file in the global project instance
tsConfigFilePath: resolve(__dirname, '..', 'tsconfig.json'), const src = globalProject.createSourceFile(
skipAddingFilesFromTsConfig: true,
skipLoadingLibFiles: true,
skipFileDependencyResolution: true,
});
const src = project.createSourceFile(
'_pf' + basename(id), '_pf' + basename(id),
await readFile(id, 'utf8'), fileContent,
{ overwrite: true },
); );
const exports = src.getExportedDeclarations(); const exports = src.getExportedDeclarations();
let objExpr: ObjectLiteralExpression | undefined = undefined; let objExpr: ObjectLiteralExpression | undefined;
for (const [name, [expr]] of exports) { // Identify the default export as an object literal, or via a 'createPlugin' call
if (name !== 'default') continue; for (const [exportName, declarations] of exports) {
if (exportName !== 'default') continue;
switch (expr.getKind()) { const expr = declarations[0];
case ts.SyntaxKind.ObjectLiteralExpression: {
objExpr = expr.asKindOrThrow(ts.SyntaxKind.ObjectLiteralExpression);
break;
}
case ts.SyntaxKind.CallExpression: {
const callExpr = expr.asKindOrThrow(ts.SyntaxKind.CallExpression);
if (callExpr.getArguments().length !== 1) continue;
const name = callExpr.getExpression().getText();
if (name !== 'createPlugin') continue;
const exprKind = expr.getKind();
if (exprKind === ts.SyntaxKind.ObjectLiteralExpression) {
objExpr = expr.asKindOrThrow(ts.SyntaxKind.ObjectLiteralExpression);
break;
} else if (exprKind === ts.SyntaxKind.CallExpression) {
const callExpr = expr.asKindOrThrow(ts.SyntaxKind.CallExpression);
if (
callExpr.getArguments().length === 1 &&
callExpr.getExpression().getText() === 'createPlugin'
) {
const arg = callExpr.getArguments()[0]; const arg = callExpr.getArguments()[0];
if (arg.getKind() !== ts.SyntaxKind.ObjectLiteralExpression) if (arg.getKind() === ts.SyntaxKind.ObjectLiteralExpression) {
continue; objExpr = arg.asKindOrThrow(
ts.SyntaxKind.ObjectLiteralExpression,
objExpr = arg.asKindOrThrow(ts.SyntaxKind.ObjectLiteralExpression); );
break; break;
}
} }
} }
} }
if (!objExpr) return null; if (!objExpr) return null;
const properties = objExpr.getProperties(); // Build a map of property names to their AST nodes for fast lookup
const propertyNames = properties.map((prop) => { const propMap = new Map<string, ObjectLiteralElementLike>();
switch (prop.getKind()) { for (const prop of objExpr.getProperties()) {
case ts.SyntaxKind.PropertyAssignment: const name = getPropertyName(prop);
return prop if (name) propMap.set(name, prop);
.asKindOrThrow(ts.SyntaxKind.PropertyAssignment) }
.getName();
case ts.SyntaxKind.ShorthandPropertyAssignment:
return prop
.asKindOrThrow(ts.SyntaxKind.ShorthandPropertyAssignment)
.getName();
case ts.SyntaxKind.MethodDeclaration:
return prop
.asKindOrThrow(ts.SyntaxKind.MethodDeclaration)
.getName();
default:
throw new Error('Not implemented');
}
});
const contexts = ['backend', 'preload', 'renderer', 'menu']; const contexts = ['backend', 'preload', 'renderer', 'menu'];
for (const ctx of contexts) { for (const ctx of contexts) {
if (mode === 'none') { if (mode === 'none' && propMap.has(ctx)) {
const index = propertyNames.indexOf(ctx); propMap.get(ctx)?.remove();
if (index === -1) continue;
objExpr.getProperty(propertyNames[index])?.remove();
continue; continue;
} }
if (ctx === mode || (ctx === 'menu' && mode === 'backend')) continue;
if (ctx === mode) continue; if (propMap.has(ctx)) propMap.get(ctx)?.remove();
if (ctx === 'menu' && mode === 'backend') continue;
const index = propertyNames.indexOf(ctx);
if (index === -1) continue;
objExpr.getProperty(propertyNames[index])?.remove();
} }
const stubObjExpr = src // Add an exported variable 'pluginStub' with the modified object literal's text
.addVariableStatement({ const varStmt = src.addVariableStatement({
isExported: true, isExported: true,
declarationKind: VariableDeclarationKind.Const, declarationKind: VariableDeclarationKind.Const,
declarations: [ declarations: [
{ {
name: 'pluginStub', name: 'pluginStub',
initializer: (writer) => writer.write(objExpr.getText()), initializer: (writer) => writer.write(objExpr.getText()),
}, },
], ],
})
.getDeclarations()[0]
.getInitializer() as ObjectLiteralExpression;
const stubProperties = stubObjExpr.getProperties();
const stubPropertyNames = stubProperties.map((prop) => {
switch (prop.getKind()) {
case ts.SyntaxKind.PropertyAssignment:
return prop
.asKindOrThrow(ts.SyntaxKind.PropertyAssignment)
.getName();
case ts.SyntaxKind.ShorthandPropertyAssignment:
return prop
.asKindOrThrow(ts.SyntaxKind.ShorthandPropertyAssignment)
.getName();
case ts.SyntaxKind.MethodDeclaration:
return prop
.asKindOrThrow(ts.SyntaxKind.MethodDeclaration)
.getName();
default:
throw new Error('Not implemented');
}
}); });
const stubObjExpr = varStmt
.getDeclarations()[0]
.getInitializerIfKindOrThrow(ts.SyntaxKind.ObjectLiteralExpression);
if (mode === 'backend') contexts.pop(); // Similarly build a map for the stub properties
for (const ctx of contexts) { const stubMap = new Map<string, ObjectLiteralElementLike>();
const index = stubPropertyNames.indexOf(ctx); for (const prop of stubObjExpr.getProperties()) {
if (index === -1) continue; const name = getPropertyName(prop);
if (name) stubMap.set(name, prop);
}
stubObjExpr.getProperty(stubPropertyNames[index])?.remove(); const stubContexts =
mode === 'backend'
? contexts.filter((ctx) => ctx !== 'menu')
: contexts;
for (const ctx of stubContexts) {
if (stubMap.has(ctx)) {
stubMap.get(ctx)?.remove();
}
} }
return { return {