Compare commits

...

163 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
2c48a0f6f4 Bump version to 3.7.2 2025-01-18 14:27:58 +09:00
368b251e3f feat(api-server): add endpoint to get shuffle state (#2792) 2025-01-18 14:23:17 +09:00
3339f997e3 fix: fix lock file 2025-01-18 14:14:18 +09:00
76e8e7aa7a fix(downloader): apply poToken
- fix #2863
- fix #2780
2025-01-18 14:12:31 +09:00
8c325b17f8 chore(deps): update dependency discord-api-types to v0.37.116 (#2877)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-18 11:50:43 +09:00
494bc0ccc7 chore(deps): update dependency eslint-plugin-prettier to v5.2.2 (#2875)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-16 11:41:33 +09:00
f14333d07a fix: fix pnpm 2025-01-16 11:39:14 +09:00
268ffe2d4c chore(deps): update eslint monorepo to v9.18.0 (#2858)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-16 11:36:27 +09:00
831eb63ace chore(deps): update dependency glob to v11.0.1 (#2857)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-16 11:34:08 +09:00
345f235117 chore(deps): update dependency electron-builder-squirrel-windows to v26.0.0-alpha.9 (#2874)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-16 11:33:49 +09:00
c6422d5d9c chore(deps): update dependency electron to v34 (#2867)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-16 11:33:20 +09:00
f90384386c chore(deps): update dependency eslint-config-prettier to v10 (#2866)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-16 11:33:01 +09:00
9213fdd4f7 chore(deps): update dependency @stylistic/eslint-plugin-js to v2.13.0 (#2864)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-16 11:30:54 +09:00
787c8cdceb chore(deps): update dependency typescript-eslint to v8.20.0 (#2865)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-16 11:30:34 +09:00
ace48bc79b chore(deps): update dependency electron-builder to v26.0.0-alpha.9 (#2869)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-16 11:29:54 +09:00
e5a8b7431f chore(i18n): Translated using Weblate (Hungarian)
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/hu/
2025-01-14 02:00:41 +00:00
e37f22503b fix: fix build.linux.desktop.entry (#2859)
Co-authored-by: h-banii <h-banii@users.noreply.github.com>
2025-01-11 17:40:27 +09:00
e61757a7fc feat(api-server): add endpoint to get volume state (#2813)
* add volume getter

* format

* add volume route
2025-01-10 12:25:32 +09:00
fdc798ad87 chore(deps): update dependency vite-plugin-inspect to v10 (#2856)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-10 12:19:54 +09:00
6393e6348c chore(deps): update dependency typescript to v5.7.3 (#2855)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-10 12:17:15 +09:00
ce6e115783 fix(deps): update dependency @floating-ui/dom to v1.6.13 (#2846)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-10 12:14:27 +09:00
68cbaabc0c chore(deps): bump nanoid from 3.3.7 to 3.3.8 (#2854)
Bumps [nanoid](https://github.com/ai/nanoid) from 3.3.7 to 3.3.8.
- [Release notes](https://github.com/ai/nanoid/releases)
- [Changelog](https://github.com/ai/nanoid/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ai/nanoid/compare/3.3.7...3.3.8)

---
updated-dependencies:
- dependency-name: nanoid
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-10 12:14:05 +09:00
9911bbf509 chore(deps): update dependency electron to v33.3.1 (#2841)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-10 12:13:32 +09:00
553c2c1096 fix(deps): update dependency i18next to v24.2.1 (#2840)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-10 12:13:21 +09:00
dde57e3eb0 chore(deps): update dependency typescript-eslint to v8.19.1 (#2836)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-10 12:13:11 +09:00
935461f610 chore(deps): update dependency rollup to v4.30.1 (#2833)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-10 12:12:49 +09:00
b95da863c6 fix(deps): update dependency solid-js to v1.9.4 (#2849)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-10 12:12:32 +09:00
92316a999c fix(deps): update dependency fast-equals to v5.2.2 (#2842)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-10 12:12:21 +09:00
0ec038342c chore(i18n): Translated using Weblate (Hebrew)
Currently translated at 11.3% (46 of 406 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/he/
2025-01-08 21:00:54 +01:00
4649c3bfaf chore(i18n): Translated using Weblate (Spanish)
Currently translated at 99.2% (403 of 406 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/es/
2025-01-08 21:00:54 +01:00
8441af2483 chore: Update README.md (#2845)
Hungarian has been added as a selectable option under [Read this in other languages].
2025-01-08 20:08:38 +09:00
63c4a9cfa7 chore: Fixing the Content section in the README-ru.md file. (#2847)
* Fixing the Content section in the README-ru.md file.

EN: I have corrected the Содержание (Content) hyperlinks, so now it is possible to jump between the main headings with a single click. The issue was that the content hyperlinks corresponded to the original language headings in the README file, so clicking on them did not lead anywhere within the README file.

RU: Я исправил гиперссылки Содержание (Content), так что теперь можно переходить между основными заголовками одним кликом. Проблема заключалась в том, что гиперссылки содержания соответствовали заголовкам README на оригинальном языке, поэтому при нажатии на них переход внутри файла README не осуществлялся.

* Fixing the Content section in the README-ru.md file.

EN: In this version, I fixed the SVG file path so that it displays properly; previously, it did not appear.

RU: В этой версии я исправил путь к файлу SVG, чтобы он отображался правильно; ранее он не отображался.

EN: I have corrected the Содержание (Content) hyperlinks, so now it is possible to jump between the main headings with a single click. The issue was that the content hyperlinks corresponded to the original language headings in the README file, so clicking on them did not lead anywhere within the README file.

RU: Я исправил гиперссылки Содержание (Content), так что теперь можно переходить между основными заголовками одним кликом. Проблема заключалась в том, что гиперссылки содержания соответствовали заголовкам README на оригинальном языке, поэтому при нажатии на них переход внутри файла README не осуществлялся.
2025-01-08 20:07:46 +09:00
4e8173360e chore: Create youtube-music-hu.svg (#2844)
HU: Az eredeti youtube-music.svg magyar változata.

EN: The Hungarian version of the original youtube-music.svg
2025-01-08 20:06:48 +09:00
4f37377d64 chore: Create Transalated README-hu.md (#2843)
* Create README-hu.md

HU: Elkészítettem a README angol változatának magyar fordítását, hogy a projekt könnyebben elérhető legyen a magyar felhasználók számára.

EN: I have created the Hungarian translation of the README's English version to make the project more accessible to Hungarian users.

* Create Transalated README-hu.md

EN: I have created the Hungarian translation of the README's English version to make the project more accessible to Hungarian users. In this version, I fixed the SVG file path so that it displays properly. (previously, it did not appear.)


HU: Elkészítettem a README angol változatának magyar fordítását, hogy a projekt könnyebben elérhető legyen a magyar felhasználók számára. Ebben a verzióban javítottam az SVG fájl elérési útját, hogy megjelenjen normálisan. (eddig nem jelent meg.)
2025-01-08 20:06:26 +09:00
0e886c0890 chore(i18n): Translated using Weblate (Hungarian)
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/hu/
2025-01-07 18:03:50 +00:00
cb0719ecf4 chore(deps): update dependency vite to v6.0.7 (#2819)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-05 23:45:48 +09:00
d24b31cf74 chore(deps): update dependency discord-api-types to v0.37.115 (#2818)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-05 23:42:34 +09:00
610073b982 fix(deps): update dependency hono to v4.6.16 (#2829)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-05 23:42:16 +09:00
493b65bb11 chore(deps): update dependency rollup to v4.29.2 (#2832)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-05 23:42:07 +09:00
e4e7ef3b52 fix(deps): update dependency fast-equals to v5.2.0 (#2822)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-05 23:41:50 +09:00
e1dc19e9eb chore(i18n): Translated using Weblate (Hungarian)
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/hu/
2025-01-04 11:07:48 +01:00
4b9f92ed2d chore(i18n): Translated using Weblate (Ukrainian)
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/uk/
2025-01-04 11:07:48 +01:00
622beccd95 chore(i18n): Translated using Weblate (Dutch)
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/nl/
2025-01-02 23:00:48 +00:00
8678fca9c0 chore(i18n): Translated using Weblate (Georgian)
Currently translated at 18.7% (76 of 406 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/ka/
2025-01-01 23:22:07 +01:00
4dcb9b5995 chore(i18n): Translated using Weblate (Dutch)
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/nl/
2025-01-01 23:22:07 +01:00
718025445c chore(i18n): Translated using Weblate (Dutch)
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/nl/
2025-01-01 23:22:07 +01:00
845dac3c03 chore(i18n): Translated using Weblate (Romanian)
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/ro/
2025-01-01 23:22:06 +01:00
a1ac3d1359 feat(api-server): add insertPosition for addSongToQueue (#2808) 2025-01-02 00:19:12 +09:00
c21dd08a40 fix: fix deps 2025-01-02 00:09:34 +09:00
8de27358d3 fix: fix invalid property name 2025-01-01 15:40:19 +09:00
c4910af494 fix: remove farm config 2025-01-01 15:34:41 +09:00
67fc0a415c chore(deps): update dependency electron-builder to v26 2025-01-01 15:32:56 +09:00
7d145cbca1 chore(i18n): Added translation using Weblate (Georgian) 2025-01-01 02:54:17 +01:00
a565d9fc0f chore(i18n): Translated using Weblate (Hungarian)
Currently translated at 93.8% (381 of 406 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/hu/
2024-12-31 19:00:28 +01:00
71a9ed5d65 chore(i18n): Translated using Weblate (Turkish)
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/tr/
2024-12-31 19:00:28 +01:00
fc16a325f7 chore(i18n): Translated using Weblate (German)
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/de/
2024-12-31 19:00:27 +01:00
c52d96dcbd chore(deps): update dependency typescript-eslint to v8.19.0 (#2812)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-31 12:36:23 +09:00
d2c681a047 fix(deps): update dependency ts-morph to v25 (#2810)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-31 12:36:13 +09:00
Adi
8e75df42bf chore(i18n): Translated using Weblate (French)
Currently translated at 98.5% (400 of 406 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/fr/
2024-12-30 00:25:43 +01:00
85d1dc46d1 chore(i18n): Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 99.0% (402 of 406 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/zh_Hans/
2024-12-29 18:19:09 +01:00
84f5e265f2 chore(i18n): Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 98.5% (400 of 406 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/zh_Hans/
2024-12-29 17:35:25 +01:00
0d261a8e44 fix(renderer): update event handler from onVolumeTap to onVolumeClick (#2791) 2024-12-28 23:29:22 +09:00
3c228ede48 fix(deps): update dependency hono to v4.6.15 (#2796)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-28 23:28:37 +09:00
8b971d2263 chore(deps): update dependency bufferutil to v4.0.9 (#2787)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-28 23:27:32 +09:00
6b8fed3fc2 feat: Refactor Menu Navigation and Update Media Control Icons (#2783)
* chore: update media control icons

Updated "next," "pause," "play," and "previous" icons in the media-icons-black folder. This improves visual consistency and ensures the latest icon designs are applied.

* refactor(menu): migrate to `navigationHistory` API

- Replaced deprecated `webContents` navigation methods (`canGoBack`, `goBack`, `canGoForward`, `goForward`) with `navigationHistory` API.
- Updated `mainMenuTemplate` to use the new `navigationHistory` destructured from `win.webContents`.
2024-12-28 23:27:07 +09:00
f15c51f3d7 fix(synced-lyrics): Revert font-size behavior for non-fancy modes (#2788) 2024-12-28 23:26:34 +09:00
fa3e146ef7 chore(i18n): Translated using Weblate (Ukrainian)
Currently translated at 99.7% (405 of 406 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/uk/
2024-12-27 22:00:38 +01:00
4baec1560c chore(i18n): Translated using Weblate (Polish)
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/pl/
2024-12-27 22:00:37 +01:00
0e0230b995 Update changelog for v3.7.1 2024-12-26 17:01:22 +00:00
a33a03f512 Bump version to 3.7.1 2024-12-27 01:44:11 +09:00
f8a53f0d61 fix(api-server): fix swagger 2024-12-27 01:43:03 +09:00
748d77d1c0 fix(downloader): fix #2234 2024-12-27 01:30:09 +09:00
725ad0d630 fix(deps): update dependency node-html-parser to v7 (#2776)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-27 01:15:58 +09:00
bdbab17772 chore(deps): update dependency vite to v6.0.6 (#2774)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-27 01:15:18 +09:00
57d2fa372d fix(synced-lyrics): use blur instead of static background color 2024-12-27 01:13:53 +09:00
80471b0ca4 fix: use networkManager.fetch instead of fetch 2024-12-27 01:05:55 +09:00
22fdfe3342 fix(downloader): fix #2769 2024-12-27 00:50:18 +09:00
5ecfa2a1f7 fix: fix #2645, fix #2741 2024-12-26 23:49:19 +09:00
b9beea810e fix(music-together): fix crash 2024-12-26 23:14:42 +09:00
f0e77812e7 fix: fix login CORS issue 2024-12-26 23:13:28 +09:00
6d1237c2a2 chore(i18n): Translated using Weblate (Hindi)
Currently translated at 29.8% (121 of 406 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/hi/
2024-12-26 06:04:43 +00:00
b43c92386e chore(i18n): Translated using Weblate (Portuguese (Brazil))
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/pt_BR/
2024-12-26 06:04:42 +00:00
017476a81b chore(i18n): Translated using Weblate (Chinese (Traditional Han script))
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/zh_Hant/
2024-12-26 06:04:41 +00:00
9b047d9c54 chore(i18n): Translated using Weblate (Russian)
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/ru/
2024-12-26 06:04:40 +00:00
31f009d3c4 chore(i18n): Translated using Weblate (Polish)
Currently translated at 98.7% (401 of 406 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/pl/
2024-12-26 06:04:40 +00:00
8504f2c086 chore(i18n): Translated using Weblate (Italian)
Currently translated at 99.2% (403 of 406 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/it/
2024-12-26 06:04:39 +00:00
1d6251baea feat(api-server): Add queue api (#2767) 2024-12-25 18:55:24 +09:00
3ea13a2a22 fix(api-server): repeat event listener 2024-12-25 13:08:02 +09:00
1cc153084d Update changelog for v3.7.0 2024-12-25 01:45:32 +00:00
67 changed files with 5848 additions and 2809 deletions

View File

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

View File

@ -21,7 +21,7 @@
</a>
</div>
Read this in other languages: [🇰🇷](./docs/readme/README-ko.md), [🇮🇸](./docs/readme/README-is.md), [🇨🇱 🇪🇸](./docs/readme/README-es.md), [🇷🇺](./docs/readme/README-ru.md)
Read this in other languages: [🇰🇷](./docs/readme/README-ko.md), [🇮🇸](./docs/readme/README-is.md), [🇨🇱 🇪🇸](./docs/readme/README-es.md), [🇷🇺](./docs/readme/README-ru.md), [🇭🇺](./docs/readme/README-hu.md)
**Electron wrapper around YouTube Music featuring:**

Binary file not shown.

Before

Width:  |  Height:  |  Size: 250 B

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 192 B

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 265 B

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 269 B

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -2,8 +2,282 @@
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)
> 27 December 2024
- 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)
- feat(api-server): Add queue api [`#2767`](https://github.com/th-ch/youtube-music/pull/2767)
- fix(downloader): fix #2234 [`#2234`](https://github.com/th-ch/youtube-music/issues/2234)
- fix(downloader): fix #2769 [`#2769`](https://github.com/th-ch/youtube-music/issues/2769)
- fix: fix #2645, fix #2741 [`#2645`](https://github.com/th-ch/youtube-music/issues/2645) [`#2741`](https://github.com/th-ch/youtube-music/issues/2741)
- Update changelog for v3.7.0 [`1cc1530`](https://github.com/th-ch/youtube-music/commit/1cc153084d5f354ea928fcde50207f8f6b14ea4c)
- fix: use networkManager.fetch instead of fetch [`80471b0`](https://github.com/th-ch/youtube-music/commit/80471b0ca4b3d3efc9e3db87d434290c9ebc79f6)
- chore(i18n): Translated using Weblate (Hindi) [`6d1237c`](https://github.com/th-ch/youtube-music/commit/6d1237c2a2ad2408a738e00992ae5ed8a1e900da)
#### [v3.7.0](https://github.com/th-ch/youtube-music/compare/v3.6.2...v3.7.0)
> 25 December 2024
- feat(amuse): song query api (add amuse plugin) [`#2723`](https://github.com/th-ch/youtube-music/pull/2723)
- feat(api-server): add absolute seek endpoint [`#2748`](https://github.com/th-ch/youtube-music/pull/2748)
- feat(api-server): Add repeat mode and seek time API [`#2630`](https://github.com/th-ch/youtube-music/pull/2630)
- feat(synced-lyrics): Better-Lyrics Styling for Synced-Lyrics [`#2554`](https://github.com/th-ch/youtube-music/pull/2554)
- feat(synced-lyrics): multiple lyric sources [`#2383`](https://github.com/th-ch/youtube-music/pull/2383)
- chore(deps): update dependency typescript-eslint to v8.18.2 [`#2763`](https://github.com/th-ch/youtube-music/pull/2763)
- chore(deps): update dependency discord-api-types to v0.37.114 [`#2761`](https://github.com/th-ch/youtube-music/pull/2761)
- chore(deps): update dependency discord-api-types to v0.37.113 [`#2759`](https://github.com/th-ch/youtube-music/pull/2759)
- fix: Set correct window class for X11 and Wayland [`#2758`](https://github.com/th-ch/youtube-music/pull/2758)
- feat: Specify flatpak runtime [`#2755`](https://github.com/th-ch/youtube-music/pull/2755)
- chore(deps): update dependency rollup to v4.29.1 [`#2749`](https://github.com/th-ch/youtube-music/pull/2749)
- chore(deps): update dependency esbuild to v0.24.2 [`#2742`](https://github.com/th-ch/youtube-music/pull/2742)
- fix: Add Flatpak permissions needed for MPRIS and tray icon [`#2754`](https://github.com/th-ch/youtube-music/pull/2754)
- chore(deps): update dependency vite-plugin-inspect to v0.10.6 [`#2756`](https://github.com/th-ch/youtube-music/pull/2756)
- chore(deps): update dependency vite to v6.0.5 [`#2745`](https://github.com/th-ch/youtube-music/pull/2745)
- fix(deps): update dependency i18next to v24.2.0 [`#2744`](https://github.com/th-ch/youtube-music/pull/2744)
- chore(deps): update dependency vite-plugin-inspect to v0.10.4 [`#2743`](https://github.com/th-ch/youtube-music/pull/2743)
- chore(deps): update dependency discord-api-types to v0.37.112 [`#2740`](https://github.com/th-ch/youtube-music/pull/2740)
- fix(discord): Fix Album Art failing on Discord RPC [`#2666`](https://github.com/th-ch/youtube-music/pull/2666)
- feat: Add equalizer plugin with presets (e.g. bass booster) [`#2575`](https://github.com/th-ch/youtube-music/pull/2575)
- chore(deps): update dependency vite to v6.0.4 [`#2738`](https://github.com/th-ch/youtube-music/pull/2738)
- fix: Fixed #1796 [`#2736`](https://github.com/th-ch/youtube-music/pull/2736)
- chore(deps): update dependency electron-devtools-installer to v4 [`#2734`](https://github.com/th-ch/youtube-music/pull/2734)
- Revert "chore(deps): update dependency electron-builder to v25" [`#2732`](https://github.com/th-ch/youtube-music/pull/2732)
- chore(deps): update dependency electron-builder to v25 [`#2490`](https://github.com/th-ch/youtube-music/pull/2490)
- fix(deps): update dependency i18next to v24.1.2 [`#2727`](https://github.com/th-ch/youtube-music/pull/2727)
- chore(deps): update dependency electron-devtools-installer to v3.2.1 [`#2731`](https://github.com/th-ch/youtube-music/pull/2731)
- chore(deps): update dependency typescript-eslint to v8.18.1 [`#2724`](https://github.com/th-ch/youtube-music/pull/2724)
- fix: tab misalignment [`#2713`](https://github.com/th-ch/youtube-music/pull/2713)
- fix(deps): update dependency @hono/zod-validator to v0.4.2 [`#2709`](https://github.com/th-ch/youtube-music/pull/2709)
- chore(deps): update eslint monorepo to v9.17.0 [`#2712`](https://github.com/th-ch/youtube-music/pull/2712)
- fix(deps): update dependency hono to v4.6.14 [`#2716`](https://github.com/th-ch/youtube-music/pull/2716)
- fix: discord rich presence connection status [`#2714`](https://github.com/th-ch/youtube-music/pull/2714)
- fix: Laggy scrolling behaviour in large playlists [`#2708`](https://github.com/th-ch/youtube-music/pull/2708)
- fix(deps): update dependency youtubei.js to v12.2.0 [`#2705`](https://github.com/th-ch/youtube-music/pull/2705)
- fix(deps): update dependency i18next to v24.1.0 [`#2698`](https://github.com/th-ch/youtube-music/pull/2698)
- chore(deps): update dependency @stylistic/eslint-plugin-js to v2.12.1 [`#2697`](https://github.com/th-ch/youtube-music/pull/2697)
- fix(deps): update dependency zod to v3.24.1 [`#2694`](https://github.com/th-ch/youtube-music/pull/2694)
- fix(deps): update dependency youtubei.js to v12.1.0 [`#2695`](https://github.com/th-ch/youtube-music/pull/2695)
- chore(deps): update dependency discord-api-types to v0.37.111 [`#2690`](https://github.com/th-ch/youtube-music/pull/2690)
- chore(deps): update dependency typescript-eslint to v8.18.0 [`#2692`](https://github.com/th-ch/youtube-music/pull/2692)
- chore(deps): update playwright monorepo to v1.49.1 [`#2693`](https://github.com/th-ch/youtube-music/pull/2693)
- fix(deps): update dependency hono to v4.6.13 [`#2682`](https://github.com/th-ch/youtube-music/pull/2682)
- chore(deps): update dependency rollup to v4.28.1 [`#2683`](https://github.com/th-ch/youtube-music/pull/2683)
- fix(deps): update dependency conf to v13.1.0 [`#2686`](https://github.com/th-ch/youtube-music/pull/2686)
- chore(deps): update dependency @stylistic/eslint-plugin-js to v2.12.0 [`#2689`](https://github.com/th-ch/youtube-music/pull/2689)
- fix(deps): update dependency youtubei.js to v12 [`#2681`](https://github.com/th-ch/youtube-music/pull/2681)
- chore(deps): update dependency vite to v6.0.3 [`#2680`](https://github.com/th-ch/youtube-music/pull/2680)
- fix(album-actions): Fixed #2312 [`#2676`](https://github.com/th-ch/youtube-music/pull/2676)
- chore(deps): update dependency eslint-import-resolver-typescript to v3.7.0 [`#2672`](https://github.com/th-ch/youtube-music/pull/2672)
- chore(deps): update dependency node-gyp to v11 [`#2678`](https://github.com/th-ch/youtube-music/pull/2678)
- fix(deps): update dependency i18next to v24.0.5 [`#2669`](https://github.com/th-ch/youtube-music/pull/2669)
- fix(deps): update dependency i18next to v24.0.4 [`#2668`](https://github.com/th-ch/youtube-music/pull/2668)
- chore(deps): update dependency vite to v6.0.2 [`#2662`](https://github.com/th-ch/youtube-music/pull/2662)
- chore(deps): update dependency node-gyp to v10.3.1 [`#2665`](https://github.com/th-ch/youtube-music/pull/2665)
- chore(deps): update dependency typescript-eslint to v8.17.0 [`#2664`](https://github.com/th-ch/youtube-music/pull/2664)
- chore(deps): update dependency vite-plugin-inspect to v0.10.3 [`#2667`](https://github.com/th-ch/youtube-music/pull/2667)
- chore(deps): update dependency rollup to v4.28.0 [`#2661`](https://github.com/th-ch/youtube-music/pull/2661)
- chore(deps): update dependency discord-api-types to v0.37.110 [`#2653`](https://github.com/th-ch/youtube-music/pull/2653)
- fix(deps): update dependency @hono/zod-openapi to v0.18.3 [`#2654`](https://github.com/th-ch/youtube-music/pull/2654)
- chore(deps): update eslint monorepo to v9.16.0 [`#2656`](https://github.com/th-ch/youtube-music/pull/2656)
- chore(deps): update dependency vite-plugin-inspect to v0.10.2 [`#2657`](https://github.com/th-ch/youtube-music/pull/2657)
- fix(youtube-music.css): Fixed #2514 [`#2659`](https://github.com/th-ch/youtube-music/pull/2659)
- fix: Fixed Skip Disliked Song not working [`#2651`](https://github.com/th-ch/youtube-music/pull/2651)
- fix(deps): update dependency @hono/zod-openapi to v0.18.2 [`#2650`](https://github.com/th-ch/youtube-music/pull/2650)
- chore(deps): update dependency vite-plugin-inspect to v0.10.1 [`#2652`](https://github.com/th-ch/youtube-music/pull/2652)
- chore(deps): update dependency electron to v33.2.1 [`#2649`](https://github.com/th-ch/youtube-music/pull/2649)
- chore(deps): update dependency vite-plugin-inspect to v0.10.0 [`#2646`](https://github.com/th-ch/youtube-music/pull/2646)
- chore(deps): update dependency vite to v6 [`#2644`](https://github.com/th-ch/youtube-music/pull/2644)
- fix(deps): update dependency @hono/swagger-ui to v0.5.0 [`#2643`](https://github.com/th-ch/youtube-music/pull/2643)
- chore(deps): update dependency discord-api-types to v0.37.109 [`#2642`](https://github.com/th-ch/youtube-music/pull/2642)
- chore(deps): update dependency vite-plugin-solid to v2.11.0 [`#2641`](https://github.com/th-ch/youtube-music/pull/2641)
- fix(deps): update dependency hono to v4.6.12 [`#2636`](https://github.com/th-ch/youtube-music/pull/2636)
- fix(deps): update dependency i18next to v24.0.2 [`#2637`](https://github.com/th-ch/youtube-music/pull/2637)
- chore(deps): update dependency discord-api-types to v0.37.108 [`#2638`](https://github.com/th-ch/youtube-music/pull/2638)
- chore(deps): update dependency typescript-eslint to v8.16.0 [`#2639`](https://github.com/th-ch/youtube-music/pull/2639)
- chore(deps): update dependency rollup to v4.27.4 [`#2632`](https://github.com/th-ch/youtube-music/pull/2632)
- fix(deps): update dependency i18next to v24 [`#2633`](https://github.com/th-ch/youtube-music/pull/2633)
- chore(deps): update dependency typescript to v5.7.2 [`#2629`](https://github.com/th-ch/youtube-music/pull/2629)
- chore(deps): update dependency discord-api-types to v0.37.107 [`#2627`](https://github.com/th-ch/youtube-music/pull/2627)
- fix(deps): update dependency @hono/zod-openapi to v0.18.0 [`#2626`](https://github.com/th-ch/youtube-music/pull/2626)
- fix(deps): update dependency i18next to v23.16.8 [`#2625`](https://github.com/th-ch/youtube-music/pull/2625)
- chore(deps): update dependency vite-plugin-inspect to v0.8.8 [`#2623`](https://github.com/th-ch/youtube-music/pull/2623)
- fix(deps): update dependency hono to v4.6.11 [`#2624`](https://github.com/th-ch/youtube-music/pull/2624)
- chore(deps): update playwright monorepo to v1.49.0 [`#2617`](https://github.com/th-ch/youtube-music/pull/2617)
- chore(deps): update dependency rollup to v4.27.3 [`#2610`](https://github.com/th-ch/youtube-music/pull/2610)
- chore(deps): update dependency typescript-eslint to v8.15.0 [`#2611`](https://github.com/th-ch/youtube-music/pull/2611)
- chore(deps): update dependency @stylistic/eslint-plugin-js to v2.11.0 [`#2618`](https://github.com/th-ch/youtube-music/pull/2618)
- chore(deps): update dependency discord-api-types to v0.37.105 [`#2603`](https://github.com/th-ch/youtube-music/pull/2603)
- chore(deps): update dependency rollup to v4.27.2 [`#2604`](https://github.com/th-ch/youtube-music/pull/2604)
- chore(deps): update eslint monorepo to v9.15.0 [`#2607`](https://github.com/th-ch/youtube-music/pull/2607)
- fix(deps): update dependency @hono/zod-openapi to v0.17.1 [`#2608`](https://github.com/th-ch/youtube-music/pull/2608)
- fix(ambient-mode): fix ambient-mode overlapping other elements [`#2609`](https://github.com/th-ch/youtube-music/pull/2609)
- fix: Allow media playback control (MPRIS) for flatpak [`#2606`](https://github.com/th-ch/youtube-music/pull/2606)
- fix(deps): update dependency @hono/node-server to v1.13.7 [`#2598`](https://github.com/th-ch/youtube-music/pull/2598)
- chore(deps): update dependency rollup to v4.26.0 [`#2600`](https://github.com/th-ch/youtube-music/pull/2600)
- fix(deps): update dependency hono to v4.6.10 [`#2601`](https://github.com/th-ch/youtube-music/pull/2601)
- fix(deps): update dependency @hono/node-server to v1.13.6 [`#2594`](https://github.com/th-ch/youtube-music/pull/2594)
- chore(deps): update dependency vite to v5.4.11 [`#2595`](https://github.com/th-ch/youtube-music/pull/2595)
- chore(deps): update dependency typescript-eslint to v8.14.0 [`#2596`](https://github.com/th-ch/youtube-music/pull/2596)
- chore(deps): update dependency electron to v33.2.0 [`#2591`](https://github.com/th-ch/youtube-music/pull/2591)
- fix(deps): update dependency @hono/zod-openapi to v0.17.0 [`#2592`](https://github.com/th-ch/youtube-music/pull/2592)
- fix(deps): update dependency i18next to v23.16.5 [`#2589`](https://github.com/th-ch/youtube-music/pull/2589)
- fix(deps): update dependency @hono/node-server to v1.13.5 [`#2578`](https://github.com/th-ch/youtube-music/pull/2578)
- fix(deps): update dependency hono to v4.6.9 [`#2579`](https://github.com/th-ch/youtube-music/pull/2579)
- chore(deps): update dependency discord-api-types to v0.37.104 [`#2588`](https://github.com/th-ch/youtube-music/pull/2588)
- chore(deps): update dependency typescript-eslint to v8.13.0 [`#2581`](https://github.com/th-ch/youtube-music/pull/2581)
- chore(deps): update dependency rollup to v4.25.0 [`#2580`](https://github.com/th-ch/youtube-music/pull/2580)
- chore(docs): Update screenshot [`#2587`](https://github.com/th-ch/youtube-music/pull/2587)
- chore(docs): Specify full path to xattr for macOS, fixes #2583 [`#2586`](https://github.com/th-ch/youtube-music/pull/2586)
- fix: callback for time-changed event [`#2577`](https://github.com/th-ch/youtube-music/pull/2577)
- chore(deps): update eslint monorepo to v9.14.0 [`#2573`](https://github.com/th-ch/youtube-music/pull/2573)
- chore(deps): update dependency utf-8-validate to v6.0.5 [`#2572`](https://github.com/th-ch/youtube-music/pull/2572)
- chore(deps): update dependency @stylistic/eslint-plugin-js to v2.10.1 [`#2571`](https://github.com/th-ch/youtube-music/pull/2571)
- fix(deps): update dependency @hono/node-server to v1.13.4 [`#2570`](https://github.com/th-ch/youtube-music/pull/2570)
- chore(deps): update dependency @stylistic/eslint-plugin-js to v2.10.0 [`#2569`](https://github.com/th-ch/youtube-music/pull/2569)
- fix(deps): update dependency @floating-ui/dom to v1.6.12 [`#2568`](https://github.com/th-ch/youtube-music/pull/2568)
- chore(deps): update dependency rollup to v4.24.3 [`#2565`](https://github.com/th-ch/youtube-music/pull/2565)
- fix(deps): update dependency hono to v4.6.8 [`#2564`](https://github.com/th-ch/youtube-music/pull/2564)
- chore(deps): update dependency typescript-eslint to v8.12.2 [`#2563`](https://github.com/th-ch/youtube-music/pull/2563)
- chore(deps): update dependency typescript-eslint to v8.12.0 [`#2561`](https://github.com/th-ch/youtube-music/pull/2561)
- fix(deps): update dependency youtubei.js to v11 [`#2562`](https://github.com/th-ch/youtube-music/pull/2562)
- chore(deps): update dependency rollup to v4.24.2 [`#2559`](https://github.com/th-ch/youtube-music/pull/2559)
- fix(deps): update dependency @hono/node-server to v1.13.3 [`#2560`](https://github.com/th-ch/youtube-music/pull/2560)
- fix(deps): update dependency i18next to v23.16.4 [`#2550`](https://github.com/th-ch/youtube-music/pull/2550)
- chore(deps): update playwright monorepo to v1.48.2 [`#2551`](https://github.com/th-ch/youtube-music/pull/2551)
- fix(deps): update dependency hono to v4.6.7 [`#2552`](https://github.com/th-ch/youtube-music/pull/2552)
- chore(deps): update dependency @babel/runtime to v7.26.0 [`#2548`](https://github.com/th-ch/youtube-music/pull/2548)
- chore(deps): update dependency @types/color to v4 [`#2547`](https://github.com/th-ch/youtube-music/pull/2547)
- fix(deps): update dependency i18next to v23.16.3 [`#2545`](https://github.com/th-ch/youtube-music/pull/2545)
- fix(deps): update dependency solid-js to v1.9.3 [`#2541`](https://github.com/th-ch/youtube-music/pull/2541)
- chore(deps): update dependency vite to v5.4.10 [`#2542`](https://github.com/th-ch/youtube-music/pull/2542)
- chore(deps): update dependency electron to v33.0.2 [`#2537`](https://github.com/th-ch/youtube-music/pull/2537)
- chore(deps): update dependency @babel/runtime to v7.25.9 [`#2538`](https://github.com/th-ch/youtube-music/pull/2538)
- chore(deps): update dependency discord-api-types to v0.37.103 [`#2532`](https://github.com/th-ch/youtube-music/pull/2532)
- chore(deps): update dependency typescript-eslint to v8.11.0 [`#2534`](https://github.com/th-ch/youtube-music/pull/2534)
- fix(deps): update dependency hono to v4.6.6 [`#2536`](https://github.com/th-ch/youtube-music/pull/2536)
- fix(tuna-obs): Added song url to tuna-obs plugin [`#2524`](https://github.com/th-ch/youtube-music/pull/2524)
- fix(deps): update dependency i18next to v23.16.2 [`#2530`](https://github.com/th-ch/youtube-music/pull/2530)
- fix(deps): update dependency i18next to v23.16.1 [`#2529`](https://github.com/th-ch/youtube-music/pull/2529)
- chore(deps): update eslint monorepo to v9.13.0 [`#2528`](https://github.com/th-ch/youtube-music/pull/2528)
- chore(deps): update dependency typescript-eslint to v8.10.0 [`#2527`](https://github.com/th-ch/youtube-music/pull/2527)
- chore(deps): update playwright monorepo to v1.48.1 [`#2516`](https://github.com/th-ch/youtube-music/pull/2516)
- chore(deps): update dependency electron to v33.0.1 [`#2523`](https://github.com/th-ch/youtube-music/pull/2523)
- fix: disable gpu memory buffer video frames [`#2519`](https://github.com/th-ch/youtube-music/pull/2519)
- fix: use HEAD instead of GET in songInfo.imageSrc validation step [`#2766`](https://github.com/th-ch/youtube-music/issues/2766)
- fix: Fixed #1796 (#2736) [`#1796`](https://github.com/th-ch/youtube-music/issues/1796)
- fix(album-actions): Fixed #2312 (#2676) [`#2312`](https://github.com/th-ch/youtube-music/issues/2312) [`#2312`](https://github.com/th-ch/youtube-music/issues/2312)
- fix(youtube-music.css): Fixed #2514 (#2659) [`#2514`](https://github.com/th-ch/youtube-music/issues/2514)
- chore(docs): Specify full path to xattr for macOS, fixes #2583 (#2586) [`#2583`](https://github.com/th-ch/youtube-music/issues/2583)
- fix: fix pnpm-lock.yaml [`3208bf4`](https://github.com/th-ch/youtube-music/commit/3208bf4a6d47d824875b06bd031299694482f02d)
- Revert "feat: use swc and lightningcss" [`3b50cbc`](https://github.com/th-ch/youtube-music/commit/3b50cbcb6e3163115d52f05075af5d6f25b80660)
- feat: use swc and lightningcss [`ae3a289`](https://github.com/th-ch/youtube-music/commit/ae3a28900576ea388666747bc4794577e1d57e23)
#### [v3.6.2](https://github.com/th-ch/youtube-music/compare/v3.6.1...v3.6.2)
> 16 October 2024
- fix(deps): update dependency serve to v14.2.4 [`#2515`](https://github.com/th-ch/youtube-music/pull/2515)
- fix(deps): update dependency hono to v4.6.5 [`#2509`](https://github.com/th-ch/youtube-music/pull/2509)
- chore(deps): update dependency vite to v5.4.9 [`#2500`](https://github.com/th-ch/youtube-music/pull/2500)

374
docs/readme/README-hu.md Normal file
View File

@ -0,0 +1,374 @@
<div align="center">
# YouTube Music
[![GitHub release](https://img.shields.io/github/release/th-ch/youtube-music.svg?style=for-the-badge&logo=youtube-music)](https://github.com/th-ch/youtube-music/releases/)
[![GitHub license](https://img.shields.io/github/license/th-ch/youtube-music.svg?style=for-the-badge)](https://github.com/th-ch/youtube-music/blob/master/LICENSE)
[![eslint code style](https://img.shields.io/badge/code_style-eslint-5ed9c7.svg?style=for-the-badge)](https://github.com/th-ch/youtube-music/blob/master/eslint.config.mjs)
[![Build status](https://img.shields.io/github/actions/workflow/status/th-ch/youtube-music/build.yml?branch=master&style=for-the-badge&logo=youtube-music)](https://GitHub.com/th-ch/youtube-music/releases/)
[![GitHub All Releases](https://img.shields.io/github/downloads/th-ch/youtube-music/total?style=for-the-badge&logo=youtube-music)](https://GitHub.com/th-ch/youtube-music/releases/)
[![AUR](https://img.shields.io/aur/version/youtube-music-bin?color=blueviolet&style=for-the-badge&logo=youtube-music)](https://aur.archlinux.org/packages/youtube-music-bin)
[![Known Vulnerabilities](https://snyk.io/test/github/th-ch/youtube-music/badge.svg)](https://snyk.io/test/github/th-ch/youtube-music)
</div>
![Bannerkep](https://i.imgur.com/UaZPHqX.png)
<div align="center">
<a href="https://github.com/th-ch/youtube-music/releases/latest">
<img src="../../web/youtube-music-hu.svg" width="400" height="100" alt="YouTube Music SVG">
</a>
</div>
Olvasd el más nyelveken: [🏴 Angol](./blob/master/README.md), [🇰🇷 Korea](./docs/readme/README-ko.md), [🇮🇸 Izland](./docs/readme/README-is.md), [🇪🇸 Spanyol](./docs/readme/README-es.md), [🇷🇺 Orosz](./docs/readme/README-ru.md)
**Electron keretrendszerre épülő alkalmazás a YouTube Music számára, amely a következőket kínálja:**
- Natív megjelenés és élmény, amely az eredeti felület megtartására törekszik
- Egyedi bővítmények keretrendszere: alakítsd át a YouTube Music-ot igényeid szerint (stílus, tartalom, funkciók), engedélyezd/tiltsd le a bővítményeket egy kattintással
## Bemutató kép
| Lejátszó ablak (album színtéma és környezeti fény) |
|:---------------------------------------------------------------------------------------------------------:|
|![Bemutatókép1](https://i.imgur.com/Tj4LBwf.png)|
## Tartalom
- [Funkciók](#Funkciók)
- [Elérhető bővítmények](#Elérhető-bővítmények)
- [Fordítás](#Fordítás)
- [Letöltés](#Letöltés)
- [Arch Linux](#arch-linux)
- [MacOS](#macos)
- [Windows](#windows)
- [Hogyan telepítsük hálózati kapcsolat nélkül? (Windows alatt)](#Hogyan-telepítsd-hálózati-kapcsolat-nélkül-Windows)
- [Témák](#Témák)
- [Fejlesztés](#Fejlesztés)
- [Saját bővítmények készítése](#Saját-bővítmények-készítése)
- [Bővítmény létrehozása](#Bővítmény-létrehozása)
- [Gyakori használati esetek](#Gyakori-használati-esetek)
- [Build](#build)
- [Gyártás előnézete](#Gyártás-előnézete)
- [Tesztelés](#Tesztelés)
- [Licenc](#Licenc)
- [GYIK](#GYIK)
## Funkciók:
- **Automatikus megerősítés a lejátszás szüneteltetésekor** (Alapból engedélyezve): Kikapcsolja a ["Folytatja a nézést?"](https://i.imgur.com/z2mG0QN.png)
felugró ablakot, amely bizonyos idő után leállítja a zenét.
- És még sok más ...
## Elérhető bővítmények:
- **Reklámblokkoló**: Blokkolja az összes hirdetést és nyomkövetőt.
- **Album műveletek**: Dislike, Undislike, Like, Unlike gombok hozzáadása, amivel ezt a lejátszási listán vagy albumban lévő összes dalra alkalmazza.
- **Album színtéma**: Dinamikus téma és vizuális effektek alkalmazása az album színpalettája alapján.
- **Ambient mód**: Fényhatás alkalmazása a videóból származó lágy színek vetítésével a képernyő hátterére.
- **Hangtömörítő**: Hang tömörítés alkalmazása. (csökkenti a jel legzajosabb részeinek hangerősségét, és emeli a legcsendesebb részek hangerősségét)
- **Navigációs sáv elmosása**: Átlátszóvá és elmosódottá teszi a navigációs sávot.
- **Korellenőrzés kihagyása**: A YouTube korellenőrzését kihagyja, ezáltal nem kel meg erősíteni a zene meghallgatása elött. (automatikusan megerősítve lesz)
- **Feliratválasztó**: Felirat választó a YouTube Music zenékhez.
- **Kompakt oldalsáv**: Mindig becsukva tartja a bal oldali sávot, ahol a Kezdőlap, Felfedezés, Könyvtár és egyebek láthatók. (amit bármikor ki lehet nyitni)
- **Áttünés**: Áttünést biztosít a dalok között, ami folytonossá teszi a zenehallgatást anélkül, hogy érezhető lenne a váltás.
- **Automatikus lejátszás letiltása**: Ez a funkció kikapcsolja az automatikus lejátszást, így a zenék nem indulnak el maguktól. Amikor egy album vagy egy dal lejátszása véget ér, a következő szám nem kezdődik el automatikusan. (a bővítmény használata során minden zenét manuálisan kell elindítani)
- **[Discord](https://discord.com/) Rich Presence**: Mutassa meg barátainak, hogy mit hallgat a [Rich Presence](https://i.imgur.com/nCeVQB2.png) segítségével. (Ehez a Discord-on is engedélyezve kel lennie a Tevékenységállapot megosztásának [DC Beállítások -> Tevékenyég-adatvédelem -> Megoszthatod az észlelt tevékenységeidet másokkal])
- **Letöltő**: MP3/forrás hanganyag letöltése [közvetlenül az interfészről](https://i.imgur.com/ghqBNVe.png). [(youtube-dl)](https://github.com/ytdl-org/youtube-dl)
- **Hangszínszabályzó**: Szűrőket ad hozzá, hogy erősítsd vagy csökkentsd bizonyos frekvenciatartományokat. (pl. basszuskiemelés)
- **Exponenciális hangerő**: A hangerő csúszka [exponenciálissá](https://greasyfork.org/en/scripts/397686-youtube-music-fix-volume-ratio/) tételével könnyebbé válik az alacsony hangerő kiválasztása.
- **Alkalmazáson belüli menü**: [A Menüsáv stílusos, sötét vagy album-színű megjelenítése](https://i.imgur.com/vWvO7Xt.png).
> (Lásd ezt a [bejegyzést](https://github.com/th-ch/youtube-music/issues/410#issuecomment-952060709), ha problémád van a menü elérésével, miután engedélyezted ezt a bővítményt és a "menü elrejtése" opciót.
- **Scrobbler**: Scrobbling támogatást biztosít [Last.fm](https://www.last.fm/) és [ListenBrainz](https://listenbrainz.org/) számára.
- **Lumia Stream**: [Lumia Stream](https://lumiastream.com/) támogatás hozzáadása.
- **Lyrics Genius**: Dalszöveg támogatást nyújt a legtöbb dalhoz.
- **Zene együtt**: Lehetővé teszi a lejátszási listák, dalok megosztását másokkal. Amikor a házigazda lejátszik egy dalt, mindenki ugyanazt a dalt fogja hallani.
- **Navigáció**: Következő/Vissza navigációs nyilak közvetlenül az interfészbe integrálva, mint a kedvenc böngésződben.
- **Nincs Google bejelentkezés**: A Bejelentkezés gomb eltávolítása az interfészről (Jobb fentről eltünik a bejelentkezés gomb.)
- **Értesítések**: Értesítés megjelenítése, amikor egy dal elindul. ([interaktív értesítések](https://user-images.githubusercontent.com/78568641/114102651-63ce0e00-98d0-11eb-9dfe-c5a02bb54f9c.png) elérhetők Windows-on)
- **Kép a képben**: Lehetővé teszi az alkalmazás kép a képben módra váltását.
- **Lejátszás sebessége**: Hallgassd gyorsan, hallgassd lassan! [Hozzáad egy csúszkát, amely szabályozza a dal sebességét](https://i.imgur.com/uaNOWOt.png)
- **Precíz hangerő**: A hangerő precíz szabályozása egérgörgővel/gyorsbillentyűkkel, egy egyedi HUD és testreszabható hangerő csuszka segítségével.
- **Gyorsbillentyűk (& MPRIS)**: Lehetővé teszi globális gyorsbillentyűk beállítását a lejátszáshoz (lejátszás/szünet/következő/előző), valamint a [média OSD](https://i.imgur.com/o13SpAE.png) kikapcsolását a médiagombok felülírásával. Bekapcsolja a Ctrl/CMD + F billentyűkombinációt a kereséshez, a Linux MPRIS támogatását a médiagombokhoz, és [egyedi gyorsbillentyűket](https://github.com/Araxeus/youtube-music/blob/1e591d6a3df98449bcda6e63baab249b28026148/providers/song-controls.js#L13-L50) a [haladó felhasználók](https://github.com/th-ch/youtube-music/issues/106#issuecomment-952156902) számára.
- **Nem kedvelt dal kihagyása**: Kihagyja a nem kedvelt dalokat.
- **Csend kihagyása**: Automatikusan átugorja a csendes szakaszokat.
- [**SzponzorBlokk**](https://github.com/ajayyy/SponsorBlock): Automatikusan átugorja a nem zenei részeket, például az intrókat/outrokat vagy a zenei videók azon részeit, ahol a dal nem szól.
- **Szinkronizált dalszövegek**: Szinkronizált dalszövegeket biztosít dalokhoz, [LRClib](https://lrclib.net)-hez hasonló szolgáltatókat használva.
- **Médiavezérlés a tálcán**: Lejátszás vezérlése a [Windows tálcáról](https://i.imgur.com/eolQfnA.png).
- **TouchBar**: Egyedi TouchBar elrendezés macOS-hoz.
- **Tuna OBS**: Integráció az [OBS](https://obsproject.com/) [Tuna](https://obsproject.com/forum/resources/tuna.843/) pluginjával.
- **Videóminőség modosító**: Lehetővé teszi a videó minőségének megváltoztatását egy [gombbal](https://i.imgur.com/UgpgtHL.png) a videó fedvényen.
- **Videó váltó**: Hozzáad egy [gombot](https://i.imgur.com/288QE1k.png) a Videó/Dal mód közötti váltáshoz. (opcionálisan teljesen eltávolíthatja a videó fület is)
- **Vizualizáció**: Különböző zenei vizualizációk.
## Fordítás
Segíthetsz a fordításban a [Hosted Weblate](https://hosted.weblate.org/projects/youtube-music/) oldalán.
<a href="https://hosted.weblate.org/engage/youtube-music/">
<img src="https://hosted.weblate.org/widget/youtube-music/i18n/multi-auto.svg" alt="Fordítás állapota" />
<img src="https://hosted.weblate.org/widget/youtube-music/i18n/287x66-black.png" alt="Fordítás állapota" />
<img src="https://hosted.weblate.org/widget/youtube-music/i18n/hu/287x66-white.png" alt="Fordítás állapota" />
</a>
## Letöltés
A [legfrissebb kiadás](https://github.com/th-ch/youtube-music/releases/latest) megtekintésével gyorsan megtalálhatod a legújabb verziót.
### Arch Linux
Telepítsd a [`youtube-music-bin`](https://aur.archlinux.org/packages/youtube-music-bin) csomagot az AUR-ból. Az AUR telepítési útmutatóját megtalálod ezen a [wiki oldalon](https://wiki.archlinux.org/index.php/Arch_User_Repository#Installing_packages).
### macOS
Telepítheted az alkalmazást Homebrew segítségével (lásd a [cask definíciót](https://github.com/th-ch/homebrew-youtube-music)):
```bash
brew install th-ch/youtube-music/youtube-music
```
Ha manuálisan telepítetted az alkalmazást, és a következő hibát kapod indításkor: "sérült, és nem nyitható meg./is damaged and cant be opened.", futtasd az alábbi parancsot a Terminálban:
```bash
/usr/bin/xattr -cr /Applications/YouTube\ Music.app
```
### Windows
A [Scoop csomagkezelő](https://scoop.sh) segítségével telepítheted a `youtube-music` csomagot az [`extras` tárolóból](https://github.com/ScoopInstaller/Extras).
```bash
scoop bucket add extras
scoop install extras/youtube-music
```
Alternatívaként használhatod a [Winget](https://learn.microsoft.com/en-us/windows/package-manager/winget/) eszközt, a Windows 11 hivatalos CLI csomagkezelőjét, hogy telepítsd a `th-ch.YouTubeMusic` csomagot.
*Megjegyzés: A Microsoft Defender SmartScreen figyelmeztethet vagy blokkolhatja a telepítést, mivel az alkalmazás "ismeretlen kiadótól" származik. Ez a figyelmeztetés akkor is megjelenhet, ha manuálisan töltöd le és próbálod futtatni a GitHubról letöltött (.exe) fájlt. Ebben az esetben kattints a "További információ" gombra, majd válaszd a "Futtatás mindenképp" opciót a telepítés folytatásához.*
```bash
winget install th-ch.YouTubeMusic
```
#### Hogyan telepítsd hálózati kapcsolat nélkül? (Windows)
- Töltsd le a `*.nsis.7z` fájlt a [kiadás oldal](https://github.com/th-ch/youtube-music/releases/latest)ról, amely megfelel az eszközöd architektúrájának:
- `x64` 64 bites Windows-hoz
- `ia32` 32 bites Windows-hoz
- `arm64` ARM64 Windows-hoz
- Töltsd le a telepítőt a [kiadás oldal](https://github.com/th-ch/youtube-music/releases/latest)ról. (`*-Setup.exe`)
- Helyezd mindkét fájlt **ugyanabba a könyvtárba**.
- Futtasd a telepítőt.
## Témák
CSS fájlokat tölthetsz be az alkalmazás megjelenésének megváltoztatásához. (Beállítások > Kinézeti beállítások > Téma)
Néhány előre definiált téma elérhető itt: https://github.com/kerichdev/themes-for-ytmdesktop-player.
## Fejlesztés
```bash
git clone https://github.com/th-ch/youtube-music
cd youtube-music
pnpm install --frozen-lockfile
pnpm dev
```
## Saját bővítmények készítése
A bővítmények segítségével a következőket teheted:
- Az alkalmazás manipulálása: Az Electron `BrowserWindow` objektuma átadásra kerül a bővítménykezelőnek.
- Az interfész módosítása: HTML és CSS manipulációval megváltoztathatod az alkalmazás kinézetét.
### Bővítmény létrehozása
Hozz létre egy mappát a `src/plugins/YOUR-PLUGIN-NAME` útvonalon:
- `index.ts`: a bővítmény fő fájlja
```typescript
import style from './style.css?inline'; // import style as inline
import { createPlugin } from '@/utils';
export default createPlugin({
name: 'Plugin Label',
restartNeeded: true, // if value is true, ytmusic show restart dialog
config: {
enabled: false,
}, // your custom config
stylesheets: [style], // your custom style,
menu: async ({ getConfig, setConfig }) => {
// All *Config methods are wrapped Promise<T>
const config = await getConfig();
return [
{
label: 'menu',
submenu: [1, 2, 3].map((value) => ({
label: `value ${value}`,
type: 'radio',
checked: config.value === value,
click() {
setConfig({ value });
},
})),
},
];
},
backend: {
start({ window, ipc }) {
window.maximize();
// you can communicate with renderer plugin
ipc.handle('some-event', () => {
return 'hello';
});
},
// it fired when config changed
onConfigChange(newConfig) { /* ... */ },
// it fired when plugin disabled
stop(context) { /* ... */ },
},
renderer: {
async start(context) {
console.log(await context.ipc.invoke('some-event'));
},
// Only renderer available hook
onPlayerApiReady(api: YoutubePlayer, context: RendererContext) {
// set plugin config easily
context.setConfig({ myConfig: api.getVolume() });
},
onConfigChange(newConfig) { /* ... */ },
stop(_context) { /* ... */ },
},
preload: {
async start({ getConfig }) {
const config = await getConfig();
},
onConfigChange(newConfig) {},
stop(_context) {},
},
});
```
### Gyakori használati esetek
- Egyedi CSS injektálása: hozz létre egy `style.css` fájlt ugyanabban a mappában, majd:
```typescript
// index.ts
import style from './style.css?inline'; // import style as inline
import { createPlugin } from '@/utils';
export default createPlugin({
name: 'Plugin Label',
restartNeeded: true, // if value is true, ytmusic will show a restart dialog
config: {
enabled: false,
}, // your custom config
stylesheets: [style], // your custom style
renderer() {} // define renderer hook
});
```
- Ha módosítani szeretnéd a HTML-t:
```typescript
import { createPlugin } from '@/utils';
export default createPlugin({
name: 'Plugin Label',
restartNeeded: true, // if value is true, ytmusic will show the restart dialog
config: {
enabled: false,
}, // your custom config
renderer() {
// Remove the login button
document.querySelector(".sign-in-link.ytmusic-nav-bar").remove();
} // define renderer hook
});
```
- Az elülső és hátsó rész közötti kommunikáció: Az Electron ipcMain moduljának használatával valósítható meg. Lásd az `index.ts` fájlt és a `sponsorblock` bővítmény példáját.
## Build
1. Klónozd a repót
2. Kövesd ezt az [útmutatót](https://pnpm.io/installation), hogy telepítsd a `pnpm` csomagkezelőt.
3. Futtasd a következő parancsot `pnpm install --frozen-lockfile` a kellékek telepítéséhez.
4. Építsd meg az alkalmazást az operációs rendszerednek megfelelő paranccsal: `pnpm build:OS`
- `pnpm dist:win` - Windows
- `pnpm dist:linux` - Linux (amd64)
- `pnpm dist:linux:deb-arm64` - Linux (arm64 Debiánhoz)
- `pnpm dist:linux:rpm-arm64` - Linux (arm64 Fedorához)
- `pnpm dist:mac` - macOS (amd64)
- `pnpm dist:mac:arm64` - macOS (arm64)
Az alkalmazás építéséhez a [electron-builder](https://github.com/electron-userland/electron-builder) eszközt használáld, amely támogatja a macOS, Linux és Windows platformokat.
## Gyártás előnézete
```bash
pnpm start
```
## Tesztelés
```bash
pnpm test
```
A [Playwright](https://playwright.dev/) tesztelési keretrendszert használd az alkalmazás teszteléséhez.
## Licenc
MIT © [th-ch](https://github.com/th-ch/youtube-music)
## GYIK
### Miért nem jelenik meg az alkalmazás menüje?
Ha a `menü elrejtése` opció be van kapcsolva, a menüt az <kbd>alt</kbd> billentyűvel jelenítheted meg (vagy az <kbd>`</kbd> [fordított idézőjel] billentyűvel, ha az alkalmazáson belüli menü bővítményt használod).

View File

@ -17,7 +17,7 @@
<div align="center">
<a href="https://github.com/th-ch/youtube-music/releases/latest">
<img src="web/youtube-music.svg" width="400" height="100" alt="YouTube Music SVG">
<img src="../../web/youtube-music.svg" width="400" height="100" alt="YouTube Music SVG">
</a>
</div>
@ -36,24 +36,24 @@
## Содержание
- [Возможности](#features)
- [Доступные плагины](#available-plugins)
- [Перевод](#translation)
- [Скачать](#download)
- [Возможности](#Возможности)
- [Доступные плагины](#Доступные-плагины)
- [Перевод](#Перевод)
- [Скачать](#Скачать)
- [Arch Linux](#arch-linux)
- [MacOS](#macos)
- [Windows](#windows)
- [Как установить без подключения к интернету? (в Windows)](#how-to-install-without-a-network-connection-in-windows)
- [Темы](#themes)
- [Для разработчиков](#dev)
- [Создайте свои собственные плагины](#build-your-own-plugins)
- [Создание плагина](#creating-a-plugin)
- [Примеры использования](#common-use-cases)
- [Сборка](#build)
- [Предварительный просмотр](#production-preview)
- [Тестирование](#tests)
- [Лицензия](#license)
- [Часто задаваемые вопросы](#faq)
- [Как установить без подключения к интернету? (в Windows)](#Установка-без-подключения-к-Интернету-в-Windows)
- [Темы](#Темы)
- [Для разработчиков](#Для-разработчиков)
- [Создайте свои собственные плагины](#Создайте-свои-собственные-плагины)
- [Создание плагина](#Создание-плагина)
- [Примеры использования](#Примеры-использования)
- [Сборка](#Сборка)
- [Предварительный просмотр](#Предварительный-просмотр)
- [Тестирование](#Тестирование)
- [Лицензия](#Лицензия)
- [Часто задаваемые вопросы](#Часто-задаваемые-вопросы)
## Возможности:

View File

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

View File

@ -2,7 +2,7 @@
"name": "youtube-music",
"desktopName": "com.github.th_ch.youtube_music",
"productName": "YouTube Music",
"version": "3.7.0",
"version": "3.7.4",
"description": "YouTube Music Desktop App - including custom plugins",
"main": "./dist/main/index.js",
"license": "MIT",
@ -73,7 +73,9 @@
"icon": "assets/generated/icons/png",
"category": "AudioVideo",
"desktop": {
"StartupWMClass": "com.github.th_ch.youtube_music"
"entry": {
"StartupWMClass": "com.github.th_ch.youtube_music"
}
},
"target": [
{
@ -220,116 +222,119 @@
},
"pnpm": {
"overrides": {
"usocket": "1.0.1",
"node-gyp": "11.0.0",
"vite": "6.1.0",
"node-gyp": "11.1.0",
"xml2js": "0.6.2",
"node-fetch": "3.3.2",
"@electron/universal": "2.0.1",
"@babel/runtime": "7.26.0"
"@babel/runtime": "7.26.9"
},
"patchedDependencies": {
"vudio@2.1.1": "patches/vudio@2.1.1.patch",
"app-builder-lib@24.13.3": "patches/app-builder-lib@24.13.3.patch",
"app-builder-lib@26.0.6": "patches/app-builder-lib@26.0.6.patch",
"@malept/flatpak-bundler": "patches/@malept__flatpak-bundler.patch"
}
},
"neverBuiltDependencies": []
},
"dependencies": {
"@electron-toolkit/tsconfig": "1.0.1",
"@electron/remote": "2.1.2",
"@ffmpeg.wasm/core-mt": "0.12.0",
"@ffmpeg.wasm/main": "0.12.0",
"@floating-ui/dom": "1.6.12",
"@floating-ui/dom": "1.6.13",
"@foobar404/wave": "2.0.5",
"@ghostery/adblocker-electron": "2.3.1",
"@ghostery/adblocker-electron-preload": "2.3.1",
"@hono/node-server": "1.13.7",
"@ghostery/adblocker-electron": "2.5.0",
"@ghostery/adblocker-electron-preload": "2.5.0",
"@hono/node-server": "1.13.8",
"@hono/swagger-ui": "0.5.0",
"@hono/zod-openapi": "0.18.3",
"@hono/zod-validator": "0.4.2",
"@hono/zod-openapi": "0.18.4",
"@hono/zod-validator": "0.4.3",
"@jellybrick/dbus-next": "0.10.3",
"@jellybrick/electron-better-web-request": "1.0.4",
"@jellybrick/mpris-service": "2.1.4",
"@jimp/plugin-invert": "0.22.12",
"@jellybrick/mpris-service": "2.1.5",
"@jimp/plugin-color": "1.6.0",
"@skyra/jaro-winkler": "1.1.1",
"@xhayper/discord-rpc": "1.2.0",
"async-mutex": "0.5.0",
"bgutils-js": "3.1.3",
"butterchurn": "3.0.0-beta.4",
"butterchurn-presets": "3.0.0-beta.4",
"color": "4.2.3",
"color": "5.0.0",
"conf": "13.1.0",
"custom-electron-prompt": "1.5.8",
"dbus-next": "0.10.2",
"deepmerge-ts": "7.1.3",
"deepmerge-ts": "7.1.4",
"electron-debug": "4.1.0",
"electron-is": "3.0.0",
"electron-localshortcut": "3.2.1",
"electron-store": "10.0.0",
"electron-store": "10.0.1",
"electron-unhandled": "4.0.1",
"electron-updater": "6.3.9",
"fast-average-color": "9.4.0",
"fast-equals": "5.0.1",
"fast-equals": "5.2.2",
"filenamify": "6.0.0",
"hono": "4.6.14",
"happy-dom": "17.1.0",
"hono": "4.7.1",
"howler": "2.2.4",
"html-to-text": "9.0.5",
"i18next": "24.2.0",
"i18next": "24.2.2",
"jimp": "1.6.0",
"keyboardevent-from-electron-accelerator": "2.0.0",
"keyboardevents-areequal": "0.2.2",
"node-html-parser": "6.1.13",
"node-id3": "0.2.6",
"node-html-parser": "7.0.1",
"node-id3": "0.2.7",
"peerjs": "1.5.4",
"semver": "7.6.3",
"semver": "7.7.1",
"serve": "14.2.4",
"simple-youtube-age-restriction-bypass": "github:organization/Simple-YouTube-Age-Restriction-Bypass#v2.5.9",
"solid-floating-ui": "0.3.1",
"solid-js": "1.9.3",
"solid-js": "1.9.4",
"solid-styled-components": "0.28.5",
"solid-transition-group": "0.2.3",
"ts-morph": "24.0.0",
"solid-transition-group": "0.3.0",
"ts-morph": "25.0.1",
"vudio": "2.1.1",
"x11": "2.3.0",
"youtubei.js": "12.2.0",
"zod": "3.24.1"
"youtubei.js": "13.0.0",
"zod": "3.24.2"
},
"devDependencies": {
"@eslint/js": "9.17.0",
"@playwright/test": "1.49.1",
"@stylistic/eslint-plugin-js": "2.12.1",
"@eslint/js": "9.20.0",
"@playwright/test": "1.50.1",
"@stylistic/eslint-plugin-js": "3.1.0",
"@total-typescript/ts-reset": "0.6.1",
"@types/color": "4.2.0",
"@types/electron-localshortcut": "3.1.3",
"@types/eslint__js": "8.42.3",
"@types/howler": "2.2.12",
"@types/html-to-text": "9.0.4",
"@types/semver": "7.5.8",
"@types/trusted-types": "2.0.7",
"bufferutil": "4.0.8",
"bufferutil": "4.0.9",
"builtin-modules": "4.0.0",
"cross-env": "7.0.3",
"del-cli": "6.0.0",
"discord-api-types": "0.37.114",
"electron": "33.2.1",
"electron-builder": "24.13.3",
"discord-api-types": "0.37.119",
"electron": "34.2.0",
"electron-builder": "26.0.6",
"electron-builder-squirrel-windows": "26.0.6",
"electron-devtools-installer": "4.0.0",
"electron-vite": "2.3.0",
"esbuild": "0.24.2",
"eslint": "9.17.0",
"eslint-config-prettier": "9.1.0",
"electron-vite": "3.0.0",
"esbuild": "0.25.0",
"eslint": "9.20.1",
"eslint-config-prettier": "10.0.1",
"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-prettier": "5.2.1",
"glob": "11.0.0",
"node-gyp": "11.0.0",
"playwright": "1.49.1",
"rollup": "4.29.1",
"typescript": "5.7.2",
"typescript-eslint": "8.18.2",
"eslint-plugin-prettier": "5.2.3",
"glob": "11.0.1",
"node-gyp": "11.1.0",
"playwright": "1.50.1",
"rollup": "4.34.8",
"typescript": "5.7.3",
"typescript-eslint": "8.24.0",
"utf-8-validate": "6.0.5",
"vite": "6.0.5",
"vite-plugin-inspect": "0.10.6",
"vite": "6.1.0",
"vite-plugin-inspect": "10.2.1",
"vite-plugin-resolve": "2.5.2",
"vite-plugin-solid": "2.11.0",
"vite-plugin-solid": "2.11.1",
"ws": "8.18.0"
},
"auto-changelog": {

4439
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",
"extends": [
"config:base"
],
"extends": ["config:recommended"],
"labels": ["dependencies"],
"postUpdateOptions": ["pnpmDedupe"]
}

View File

@ -279,6 +279,13 @@
},
"name": "Ambiente-Modus"
},
"amuse": {
"description": "Fügt Unterstützung für das Amuse \"Spielt gerade\"-Widget von 6K Labs hinzu",
"name": "Amuse",
"response": {
"query": "Amuse API-Server läuft. /query für Liedinformationen."
}
},
"api-server": {
"description": "Fügt einen API-Server hinzu, um die Wiedergabe zu steuern",
"dialog": {
@ -484,6 +491,18 @@
"button": "Herunterladen"
}
},
"equalizer": {
"description": "Fügt einen Equalizer zum Player hinzu",
"menu": {
"presets": {
"label": "Vorgaben",
"list": {
"bass-booster": "Bass-Verstärker"
}
}
},
"name": "Equalizer"
},
"exponential-volume": {
"description": "Macht den Lautstärkeregler exponentiell, damit es einfacher ist leise Lautstärken zu wählen.",
"name": "Exponentielle Lautstärke"
@ -714,8 +733,8 @@
"synced-lyrics": {
"description": "Bietet synchronisierte Liedtexte zu Songs, verwendet Anbieter wie LRClib.",
"errors": {
"fetch": "⚠️ - Beim Abrufen des Liedtexts ist ein Fehler aufgetreten. Bitte versuchen Sie es später nochmal.",
"not-found": "⚠️ - Kein Text für diesen Song gefunden."
"fetch": "⚠️ - \tBeim Abrufen des Liedtexts ist ein Fehler aufgetreten. \n\tBitte versuchen Sie es später nochmal.",
"not-found": "⚠️ Kein Text für diesen Song gefunden."
},
"menu": {
"default-text-string": {
@ -725,6 +744,10 @@
"line-effect": {
"label": "Zeileneffekt",
"submenu": {
"fancy": {
"label": "schick",
"tooltip": "Verwende große, app-ähnliche Effekte in der aktuellen Zeile"
},
"focus": {
"label": "Fokussieren",
"tooltip": "Nur aktive Zeile weiß darstellen"

View File

@ -279,6 +279,13 @@
},
"name": "Modo ambiente"
},
"amuse": {
"description": "Agrega soporte de YouTube Music para el widget Amuse de reproduciendo ahora de 6K Labs",
"name": "Amuse",
"response": {
"query": "El servidor API de Amuse se está ejecutando. Usa GET /query para obtener información de la canción."
}
},
"api-server": {
"description": "Añade un servidor API para controlar el reproductor",
"dialog": {
@ -737,6 +744,10 @@
"line-effect": {
"label": "Efecto de la línea",
"submenu": {
"fancy": {
"label": "Elegante",
"tooltip": "Usar efectos grandes, similares a los de una aplicación, en la línea actual"
},
"focus": {
"label": "Enfoque",
"tooltip": "Mostrar solo la línea actual en blanco"

View File

@ -81,12 +81,12 @@
"menu": {
"about": "درباره",
"navigation": {
"label": "ناوبری",
"label": "کنترل‌های رابط",
"submenu": {
"copy-current-url": "کپی کردن URL فعلی",
"go-back": "بازگشت",
"go-forward": "حرکت به جلو",
"quit": "خروجی",
"copy-current-url": "کپی کردن لینک صفحه فعلی",
"go-back": "صفحه قبل",
"go-forward": "صفحه بعدی",
"quit": "خروج از برنامه",
"restart": "راه‌اندازی مجدد برنامه"
}
},
@ -98,8 +98,8 @@
"submenu": {
"auto-reset-app-cache": "ریست کردن حافظه کش برنامه هنگام شروع",
"disable-hardware-acceleration": "غیرفعال کردن شتاب سخت‌افزاری",
"edit-config-json": "ویرایش config.json",
"override-user-agent": "تغییر User-Agent",
"edit-config-json": "config.json ویرایش",
"override-user-agent": "User-Agent تغییر",
"restart-on-config-changes": "راه‌اندازی مجدد در صورت تغییرات در پیکربندی",
"set-proxy": {
"label": "تنظیم پراکسی",
@ -109,7 +109,7 @@
"title": "تنظیم پراکسی"
}
},
"toggle-dev-tools": "باز کردن DevTools"
"toggle-dev-tools": "DevTools باز کردن"
}
},
"always-on-top": "همیشه در بالا",
@ -168,7 +168,7 @@
},
"label": "تم",
"submenu": {
"import-css-file": "وارد کردن فایل CSS سفارشی",
"import-css-file": "سفارشی CSS وارد کردن فایل",
"no-theme": "بدون تم"
}
}
@ -177,7 +177,7 @@
}
},
"plugins": {
"enabled": "فعال",
"enabled": "فعال/غیرفعال کردن",
"label": "افزونه‌ها",
"new": "جدید"
},
@ -187,7 +187,7 @@
"force-reload": "اجبار به بارگذاری مجدد",
"reload": "بارگذاری مجدد",
"reset-zoom": "اندازه واقعی",
"toggle-fullscreen": "تغییر به تمام‌صفحه",
"toggle-fullscreen": "تغییر به تمام‌ صفحه",
"zoom-in": "بزرگنمایی",
"zoom-out": "کوچکنمایی"
}
@ -219,7 +219,7 @@
"name": "مسدودکننده تبلیغات"
},
"album-actions": {
"description": "افزودن دکمه‌های \"برگرفتن ناپسند\"، \"ناپسند\"، \"پسند\"، و \"حذف پسند\" برای اعمال آنها روی همه آهنگ‌ها در یک فهرست پخش یا آلبوم",
"description": "اضافه کردن دکمه‌های عدم پسندیدن، پسندیدن و لغو پسندیدن برای اعمال این تغییرات به تمامی آهنگ‌های یک فهرست پخش یا آلبوم",
"name": "عملیات آلبوم"
},
"album-color-theme": {
@ -250,7 +250,10 @@
}
},
"opacity": {
"label": "شفافیت"
"label": "شفافیت",
"submenu": {
"percent": "{{opacity}}%"
}
},
"quality": {
"label": "کیفیت",
@ -259,7 +262,10 @@
}
},
"size": {
"label": "اندازه"
"label": "اندازه",
"submenu": {
"percent": "{{size}}%"
}
},
"smoothness-transition": {
"label": "انتقال نرمی",
@ -273,8 +279,15 @@
},
"name": "حالت محیطی"
},
"amuse": {
"description": "حالا ویجت Amuse از YouTube Music هم پشتیبانی می‌کنه! (توسط 6K Labs)",
"name": "Amuse",
"response": {
"query": "سرور Amuse فعال است. برای دریافت اطلاعات آهنگ، از آدرس /query استفاده کنید."
}
},
"api-server": {
"description": "افزودن یک سرور API برای کنترل پخش‌کننده",
"description": "برای کنترل پخش‌کننده API افزودن یک سرور",
"dialog": {
"request": {
"buttons": {
@ -304,14 +317,14 @@
"label": "پورت"
}
},
"name": "سرور API [بتا]",
"name": "[بتا]API سرور",
"prompt": {
"hostname": {
"label": "نام میزبان را برای سرور API وارد کنید (مثل 0.0.0.0):",
"label": "وارد کنید (مثل 0.0.0.0): API نام میزبان را برای سرور",
"title": "نام میزبان"
},
"port": {
"label": "پورت را برای سرور API وارد کنید:",
"label": "وارد کنید: API پورت را برای سرور",
"title": "پورت"
}
}
@ -321,8 +334,8 @@
"name": "فشرده‌ساز صدا"
},
"blur-nav-bar": {
"description": "شفاف و محو کردن نوار ناوبری",
"name": "محو کردن نوار ناوبری"
"description": "شفاف و محو کردن نوار کنترل",
"name": "محو کردن نوار کنترل"
},
"bypass-age-restrictions": {
"description": "دور زدن تأیید سن یوتیوب",
@ -381,27 +394,27 @@
},
"discord": {
"backend": {
"already-connected": "تلاش برای اتصال با اتصال فعال",
"connected": "متصل به Discord",
"disconnected": "قطع اتصال از Discord"
"already-connected": "تلاش برای برقراری ارتباط با اتصال فعال",
"connected": "متصل به دیسکورد",
"disconnected": "ارتباط با دیسکورد قطع شد"
},
"description": "نمایش آنچه گوش می‌دهید به دوستان با Rich Presence",
"description": "Rich Presence نمایش آنچه گوش می‌دهید به دوستان با",
"menu": {
"auto-reconnect": "اتصال خودکار مجدد",
"auto-reconnect": "اتصال خودکار",
"clear-activity": "پاک کردن فعالیت",
"clear-activity-after-timeout": "پاک کردن فعالیت پس از تایم‌اوت",
"connected": "متصل",
"disconnected": "قطع شده",
"clear-activity-after-timeout": "حذف فعالیت پس از اتمام زمان تعیین‌شده",
"connected": "اتصال برقرار شد",
"disconnected": "اتصال قطع شد",
"hide-duration-left": "مخفی کردن مدت زمان باقی‌مانده",
"hide-github-button": "مخفی کردن دکمه لینک GitHub",
"play-on-youtube-music": "پخش در یوتیوب موسیقی",
"set-inactivity-timeout": "تنظیم تایم‌اوت عدم فعالیت"
"hide-github-button": "مخفی کردن دکمه لینک گیت هاب",
"play-on-youtube-music": "پخش در یوتیوب موزیک",
"set-inactivity-timeout": "تنظیم زمان عدم فعالیت"
},
"name": "Rich Presence در Discord",
"name": "Discord Rich Presence",
"prompt": {
"set-inactivity-timeout": {
"label": "ورود تایم‌اوت عدم فعالیت به ثانیه:",
"title": "تنظیم تایم‌اوت عدم فعالیت"
"label": "محدودیت زمان عدم فعالیت را به ثانیه وارد کنید:",
"title": "تنظیم زمان عدم فعالیت"
}
}
},
@ -478,6 +491,18 @@
"button": "دانلود"
}
},
"equalizer": {
"description": "اضافه کردن یک اکولایزر به پخش‌کننده",
"menu": {
"presets": {
"label": "تنظیمات از پیش تعیین شده",
"list": {
"bass-booster": "تقویت‌کننده باس صدا"
}
}
},
"name": "اکولایزر"
},
"exponential-volume": {
"description": "نوار لغزنده حجم را به صورت نمایی می‌سازد تا انتخاب حجم‌های پایین‌تر آسان‌تر شود.",
"name": "حجم نمایی"
@ -490,17 +515,17 @@
"name": "منوی داخل برنامه"
},
"lumiastream": {
"description": "افزودن پشتیبانی از Lumia Stream",
"description": "Lumia Stream افزودن پشتیبانی از",
"name": "Lumia Stream [بتا]"
},
"lyrics-genius": {
"description": "افزودن پشتیبانی از متن آهنگ برای بیشتر آهنگ‌ها",
"description": "افزودن متن ترانه پشتیبان برای اکثر ترانه ها",
"menu": {
"romanized-lyrics": "متن رومی‌شده"
"romanized-lyrics": "الفبای لاتین برای آهنگ‌هایی با الفبای شرقی (فینگلیش)"
},
"name": "متن آهنگ Genius",
"name": "Genius متن آهنگ",
"renderer": {
"fetched-lyrics": "متن آهنگ از Genius بازیابی شد"
"fetched-lyrics": "بازیابی شد Genius متن ترانه توسط"
}
},
"music-together": {
@ -536,13 +561,13 @@
"name": "Music Together [بتا]",
"toast": {
"add-song-failed": "افزودن آهنگ با شکست مواجه شد",
"closed": "Music Together بسته شد",
"disconnected": "قطع اتصال Music Together",
"host-failed": "میزبانی Music Together با شکست مواجه شد",
"closed": "بسته شد Music Together",
"disconnected": "Music Together قطع اتصال",
"host-failed": "با شکست مواجه شد Music Together میزبانی",
"id-copied": "شناسه میزبان به کلیپ‌بورد کپی شد",
"id-copy-failed": "کپی شناسه میزبان به کلیپ‌بورد با شکست مواجه شد",
"join-failed": "پیوستن به Music Together با شکست مواجه شد",
"joined": "به Music Together پیوست",
"join-failed": "با شکست مواجه شد Music Together پیوستن به",
"joined": "پیوست Music Together به",
"permission-changed": "مجوز Music Together به \"{{permission}}\" تغییر یافت",
"remove-song-failed": "حذف آهنگ با شکست مواجه شد",
"user-connected": "{{name}} به Music Together پیوست",
@ -551,11 +576,11 @@
},
"navigation": {
"description": "بعدی/قبلی به طور مستقیم در رابط یکپارچه شده‌اند، مانند مرورگر مورد علاقه شما",
"name": "ناوبری"
"name": "کنترل های رابط"
},
"no-google-login": {
"description": "حذف دکمه‌های ورود به سیستم Google و لینک‌ها از رابط",
"name": "بدون ورود به Google"
"description": "حذف دکمه‌ها و لینک‌های ورود به گوگل از رابط کاربری",
"name": "بدون ورود به گوگل"
},
"notifications": {
"description": "نمایش اعلان هنگامی که آهنگی شروع به پخش می‌کند (اعلان‌های تعاملی در ویندوز در دسترس هستند)",
@ -566,11 +591,11 @@
"submenu": {
"hide-button-text": "مخفی کردن متن دکمه",
"refresh-on-play-pause": "تازه‌سازی در پخش/توقف",
"tray-controls": "باز/بسته شدن با کلیک روی سینی"
"tray-controls": "باز/بسته شدن با کلیک روی آیکون در نوار وظیفه"
}
},
"priority": "اولویت اعلان",
"toast-style": "سبک Toast",
"toast-style": "Toast سبک",
"unpause-notification": "نمایش اعلان هنگام از سرگیری پخش"
},
"name": "اعلان‌ها"
@ -578,8 +603,234 @@
"picture-in-picture": {
"description": "اجازه می‌دهد تا برنامه به حالت تصویر در تصویر تغییر کند",
"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"
},
"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": {
"description": "Nagdadagdag ng API Server upang kontrolin ang player",
"dialog": {
@ -468,6 +474,14 @@
"button": "Mag-download"
}
},
"equalizer": {
"description": "Nagdaragdag ng equalizer sa player",
"menu": {
"presets": {
"label": "Mga Preset"
}
}
},
"exponential-volume": {
"description": "Ginagawang exponential ang volume slider para mas madaling pumili ng mas mababang volume."
},
@ -674,8 +688,8 @@
"synced-lyrics": {
"description": "Nagbibigay ng naka-sync na lyrics sa mga kanta, gamit ang mga provider tulad ng LRClib.",
"errors": {
"fetch": "⚠️ - Nagkaroon ng error habang kinukuha ang lyrics. Subukang muli mamaya.",
"not-found": "⚠️ - Walang nakitang lyrics para sa kantang ito."
"fetch": "⚠️\t Nagkaroon ng error habang kinukuha ang lyrics.\n\t Subukang muli mamaya.",
"not-found": "⚠️ Walang nakitang lyrics para sa kantang ito."
},
"menu": {
"default-text-string": {
@ -685,6 +699,10 @@
"line-effect": {
"label": "Effect ng Linya",
"submenu": {
"fancy": {
"label": "Magarbo",
"tooltip": "Gumamit ng malaki, mala-app na effect sa kasalukuyang linya"
},
"focus": {
"tooltip": "Gawing puti lamang ang kasalukuyang linya"
},

View File

@ -317,7 +317,7 @@
"title": "Nom d'hôte"
},
"port": {
"label": "Entrer le port du serveur de l'API :",
"label": "Entrez le port du serveur de l'API:",
"title": "Port"
}
}
@ -726,7 +726,7 @@
"synced-lyrics": {
"description": "Ajoute des paroles synchronisées aux chansons, grâce à LRClib par exemple.",
"errors": {
"fetch": "⚠️ - Une erreur s'est produite en allant chercher les paroles. Merci de réessayer plus tard.",
"fetch": "⚠️\tUne erreur s'est produite en allant chercher les paroles.\n\tMerci de réessayer plus tard.",
"not-found": "⚠️ - Aucune paroles trouvées pour cette musique."
},
"menu": {

View File

@ -53,6 +53,8 @@
"later": "אחר כך",
"restart-now": "מתחיל את התוכנה מחדש עכשיו"
},
"detail": "\"{{pluginName}}\" מצריך אתחול",
"message": "\"{{pluginName}}\" דורש אתחול",
"title": "נדרשת הפעלה מחדש"
},
"unresponsive": {
@ -69,9 +71,10 @@
"buttons": {
"disable": "בטל עדכונים",
"download": "הורדה",
"ok": "אוקי"
"ok": "אוקיי"
},
"message": ירסא חדשה זמינה כעת",
"detail": "גרסה חדשה זמינה, ניתן להוריד אותה ב-{{downloadLink}}",
"message": "גירסה חדשה זמינה כעת",
"title": "קיים עדכון חדש"
}
},
@ -85,6 +88,18 @@
"go-forward": "לך קדימה",
"quit": "יציאה"
}
},
"options": {
"label": "אפשרויות",
"submenu": {
"advanced-options": {
"label": "אפשרויות מתקדמות",
"submenu": {
"auto-reset-app-cache": "אפס את מטמון האפליקציה כאשר האפליקציה מתחילה",
"disable-hardware-acceleration": "השבת האצת החומרה"
}
}
}
}
}
}

View File

@ -194,10 +194,84 @@
}
},
"tray": {
"next": "अगला"
"next": "अगला",
"play-pause": "चलाएँ/रोकें",
"previous": "पिछला",
"quit": "निकास",
"restart": "ऐप पुनः प्रारंभ करें",
"show": "ऐप दिखाए",
"tooltip": {
"default": "यूट्यूब म्यूजिक",
"with-song-info": "यूट्यूब म्यूजिक: {{artist}} - {{title}}"
}
}
},
"plugins": {
"ad-speedup": {
"description": "यदि कोई विज्ञापन चलता है तो यह ऑडियो को म्यूट कर देता है और प्लेबैक गति 16x पर सेट कर देता है",
"name": "विज्ञापन की गति बढ़ाना"
},
"adblocker": {
"description": "डिफ़ॉल्ट रूप से सभी विज्ञापनों और ट्रैकिंग को ब्लॉक करें",
"menu": {
"blocker": "ब्लॉकर"
},
"name": "विज्ञापन अवरोधक"
},
"album-actions": {
"description": "प्लेलिस्ट या एल्बम के सभी गानों पर लागू करने के लिए \"अंडिसलाइक,\" \"डिसलाइक,\" \"लाइक,\" और \"अनलाइक\" बटन जोड़ता है",
"name": "एल्बम एक्शन"
},
"album-color-theme": {
"description": "एल्बम रंग पैलेट के आधार पर एक गतिशील थीम और दृश्य प्रभाव लागू करता है",
"menu": {
"color-mix-ratio": {
"label": "रंग मिश्रण अनुपात",
"submenu": {
"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": {
"menu": {
"align": {

View File

@ -2,12 +2,12 @@
"common": {
"console": {
"plugins": {
"execute-failed": "Nem sikerült futtatni a plugint {{pluginName}}::{{contextName}}",
"executed-at-ms": "Plugin {{pluginName}}::{{contextName}} a {{ms}}ms időpontban lefutott",
"initialize-failed": "Nem sikerült inicializálni a \"{{pluginName}}\" plugint",
"execute-failed": "Nem sikerült futtatni a bővítményt {{pluginName}}::{{contextName}}",
"executed-at-ms": "A {{pluginName}}::{{contextName}} bővítmény végrehajtva {{ms}} ms alatt",
"initialize-failed": "Nem sikerült inicializálni a \"{{pluginName}}\" bővítményt",
"load-all": "Összes bővítmény betöltése",
"load-failed": "Nem sikerült betölteni a \"{{pluginName}}\" plugint",
"loaded": "\"{{pluginName}}\" plugin betöltve",
"load-failed": "Nem sikerült betölteni a \"{{pluginName}}\" bővítményt",
"loaded": "\"{{pluginName}}\" bővítmény betöltve",
"unload-failed": "Nem sikerült a \"{{pluginName}}\" bővítményt kikapcsolni",
"unloaded": "A \"{{pluginName}}\" bővítmény kikapcsolva"
}
@ -33,13 +33,13 @@
"css-file-not-found": "CSS fájl \"{{cssFile}}\" nem létezik, figyelmen kívül hagyva"
},
"unresponsive": {
"details": "Nem reagál hiba!\n{{error}}"
"details": "Nem válaszol!\n{{error}}"
},
"when-ready": {
"clearing-cache-after-20s": "Alkalmazás gyorsítótárának törlése"
},
"window": {
"tried-to-render-offscreen": "Az ablak a képernyőn kívül próbált betölteni, windowSize={{windowSize}}, displaySize={{displaySize}}, position={{position}}"
"tried-to-render-offscreen": "Az ablak a képernyőn kívül próbált betölteni, ablakMéret={{windowSize}}, kijelzőMéret={{displaySize}}, pozíció={{position}}"
}
},
"dialog": {
@ -53,8 +53,8 @@
"later": "Később",
"restart-now": "Újraindítás most"
},
"detail": "A \"{{pluginName}}\" plugin újraindítást igényel a bekapcsoláshoz",
"message": "\"{{pluginName}}\" nevű plugin-t újra kell indítani",
"detail": "A \"{{pluginName}}\" bővítmény bekapcsolása az alkalmazás újraindítását igényli",
"message": "\"{{pluginName}}\" nevű bővítményt újra kell indítani",
"title": "Újraindítás szükséges"
},
"unresponsive": {
@ -73,7 +73,7 @@
"download": "Letöltés",
"ok": "OK"
},
"detail": "Az új verzió elérhető, és letölthető az alábbi linken {{downloadLink}}",
"detail": "Új verzió elérhető, amely letölthető az alábbi linken {{downloadLink}}",
"message": "Új verzió áll rendelkezésre",
"title": "Frissítés elérhető"
}
@ -96,7 +96,7 @@
"advanced-options": {
"label": "Speciális beállítások",
"submenu": {
"auto-reset-app-cache": "Az alkalmazás gyorsítótárának törlése indításkor",
"auto-reset-app-cache": "Alkalmazás gyorsítótárának törlése indításkor",
"disable-hardware-acceleration": "Hardveres gyorsítás kikapcsolása",
"edit-config-json": "config.json szerkesztése",
"override-user-agent": "Kliens felülírása",
@ -123,38 +123,38 @@
},
"language": {
"dialog": {
"message": "Az Újraindítást követően változik meg a nyelv",
"title": "Megváltozott a nyelv"
"message": "A nyelv az allkalmazás újraindítása után megváltozik",
"title": "Nyelv megváltoztatva"
},
"label": "Nyelv",
"submenu": {
"to-help-translate": "Szeretne a fordításban segíteni? Kattintson ide"
"to-help-translate": "Szeretnél segíteni a fordításban? Kattints ide"
}
},
"resume-on-start": "Indításkor az utolsó zene folytatása",
"single-instance-lock": "Csak egy példány engedélyezése",
"resume-on-start": "Zene folytatása az alkalmazás indításakor",
"single-instance-lock": "Csak egy példány",
"start-at-login": "Futtatás rendszerindításkor",
"starting-page": {
"label": "Indítási hely",
"label": "Induláskor",
"unset": "Visszaállítás"
},
"tray": {
"label": "Tálca",
"label": "Tálca ikon",
"submenu": {
"disabled": "Letiltva",
"enabled-and-hide-app": "Aktív és az alkalmazás elrejtve",
"enabled-and-show-app": "Aktív és az alkalmazás megjelenítve",
"enabled-and-hide-app": "Engedélyezve és alkalmazás elrejtése",
"enabled-and-show-app": "Engedélyezve és alkalmazás megjelenítése",
"play-pause-on-click": "Lejátszás/Szünet az ikonra kattintással"
}
},
"visual-tweaks": {
"label": "Kinézeti beállítások",
"label": "Megjelenési beállítások",
"submenu": {
"like-buttons": {
"default": "Alapértelmezett",
"force-show": "Megjelenítés kényszerítése",
"hide": "Elrejtése",
"label": "Kedvelés gombok"
"label": "Reakció gombok"
},
"remove-upgrade-button": "Előfizetés gombjának eltávolítása",
"theme": {
@ -163,8 +163,8 @@
"cancel": "Mégse",
"remove": "Eltávolít"
},
"remove-theme": "Biztos, hogy el akarja távolítani az egyéni témát?",
"remove-theme-message": "Ez el fogja távolítani az egyéni témát"
"remove-theme": "Biztos, hogy el szeretnéd távolítani az egyéni témát?",
"remove-theme-message": "Ez eltávolítja az egyéni témát"
},
"label": "Téma",
"submenu": {
@ -186,10 +186,10 @@
"submenu": {
"force-reload": "Kényszerített újratöltés",
"reload": "Újratöltés",
"reset-zoom": "Valós méret",
"reset-zoom": "Alapértelmezett méret visszaállítása",
"toggle-fullscreen": "Teljes képernyő be/ki",
"zoom-in": "Nagyítás",
"zoom-out": "Kicsinyítés"
"zoom-in": "Szöveg nagyítása",
"zoom-out": "Szöveg kicsinyítése"
}
}
},
@ -208,25 +208,25 @@
},
"plugins": {
"ad-speedup": {
"description": "Ha reklám szól, elnémítja a hangot és a lejátszási sebességet 16x-ra állítja",
"name": "Gyorsítás hozzáadása"
"description": "Ha egy hirdetés elindul, elnémítja a hangot, és a lejátszási sebességet 16x-ra állítja",
"name": "Hirdetésgyorsítás"
},
"adblocker": {
"description": "Alapértelmezés szerint blokkolja az összes hirdetést és nyomkövetést",
"description": "Alapértelmezetten minden hirdetés és nyomkövetés blokkolása",
"menu": {
"blocker": "Blokkoló"
"blocker": "Blokkolási módszer"
},
"name": "Reklámblokkoló"
},
"album-actions": {
"description": "Dislike, Undislike, Like, Unlike gombok hozzáadása, amivel ezt a lejátszási listán vagy albumon lévő összes dalra alkalmazza",
"description": "Hozzáadja a Tetszik, Nem tetszik és ezek visszavonására szolgáló gombokat, hogy ezeket az összes dalra alkalmazhasd egy lejátszási listán vagy albumban",
"name": "Album műveletek"
},
"album-color-theme": {
"description": "Dinamikus téma és vizuális effektek alkalmazása az album színpalettája alapján",
"description": "Dinamikus témát és vizuális effekteket alkalmaz az album színpalettája alapján",
"menu": {
"color-mix-ratio": {
"label": "Szín keverés aránya",
"label": "Színkeverés mértéke",
"submenu": {
"percent": "{{ratio}}%"
}
@ -235,12 +235,12 @@
"name": "Album színtéma"
},
"ambient-mode": {
"description": "Fényhatás alkalmazása a videóból származó lágy színek vetítésével a képernyő hátterére",
"description": "Fényhatás effektust alkalmaz, amely a videóból származó lágy színeket vetíti a képernyő hátterére",
"menu": {
"blur-amount": {
"label": "Elmosódás mértéke",
"submenu": {
"pixels": "{{blurAmount}} képpontok"
"pixels": "{{blurAmount}} pixel"
}
},
"buffer": {
@ -258,7 +258,7 @@
"quality": {
"label": "Minőség",
"submenu": {
"pixels": "{{quality}} képpont"
"pixels": "{{quality}} pixel"
}
},
"size": {
@ -270,14 +270,64 @@
"smoothness-transition": {
"label": "Sima átmenet",
"submenu": {
"during": "{{interpolationTime}}s alatt"
"during": "{{interpolationTime}} másodperc alatt"
}
},
"use-fullscreen": {
"label": "Teljes képernyő használata"
}
},
"name": "Természetes mód"
"name": "Ambient mód"
},
"amuse": {
"description": "Hozzáadja a YouTube Music támogatását az Amuse \"now playing\" widgethez a 6K Labs által",
"name": "Amuse",
"response": {
"query": "Az Amuse API szerver fut. Használja a GET /query kérést a dalinformációk lekéréséhez."
}
},
"api-server": {
"description": "Hozzáad egy API szervert a lejátszó vezérléséhez",
"dialog": {
"request": {
"buttons": {
"allow": "Engedélyez",
"deny": "Megtagad"
},
"message": "Engedélyezi, hogy {{ID}} ({{origin}}) hozzáférjen az API-hoz?",
"title": "API-hozzáférési kérelem"
}
},
"menu": {
"auth-strategy": {
"label": "Engedélyezési módszer",
"submenu": {
"auth-at-first": {
"label": "Engedélyezés az első kérésnél"
},
"none": {
"label": "Nincs engedélyezés"
}
}
},
"hostname": {
"label": "Kiszolgáló név"
},
"port": {
"label": "Port"
}
},
"name": "API szerver [Béta]",
"prompt": {
"hostname": {
"label": "Adja meg az API szerver kiszolgáló nevét (például 0.0.0.0):",
"title": "Kiszolgáló neve"
},
"port": {
"label": "Adja meg az API szerver portját:",
"title": "Port"
}
}
},
"audio-compressor": {
"description": "Hang tömörítés alkalmazása (csökkenti a jel legzajosabb részeinek hangerősségét, és emeli a legcsendesebb részek hangerősségét)",
@ -325,7 +375,7 @@
"fade-in-duration": "Áttünés időtartama (ms)",
"fade-out-duration": "Fokozatos halkítás időtartama (ms)",
"fade-scaling": {
"label": "Áttünés értéke",
"label": "Áttünés mértéke",
"linear": "Lineáris",
"logarithmic": "Logaritmikus"
},
@ -336,7 +386,7 @@
}
},
"disable-autoplay": {
"description": "A Zenék nem fognak maguktól elindulni, a bővítmény használata során kézileg kel indítani a zenéket",
"description": "Ez a funkció kikapcsolja az automatikus lejátszást, így a zenék nem indulnak el maguktól. Amikor egy album vagy egy dal lejátszása véget ér, a következő szám nem kezdődik el automatikusan. A bővítmény használata során minden zenét manuálisan kell elindítani",
"menu": {
"apply-once": "Csak induláskor alkalmazza"
},
@ -356,7 +406,7 @@
"connected": "Kapcsolódva",
"disconnected": "Nincs Kapcsolódva",
"hide-duration-left": "Hátralévő idő elrejtése",
"hide-github-button": "GitHub link gombjának elrejtése",
"hide-github-button": "GitHub url gombjának elrejtése",
"play-on-youtube-music": "Lejátszás a YouTube Music-on",
"set-inactivity-timeout": "Inaktivitási időkorlát beállítása"
},
@ -418,7 +468,8 @@
"label": "Letöltés befejezéskor",
"prompt": {
"last-percent": "x százalék után",
"last-seconds": "Utolsó x másodperc"
"last-seconds": "Utolsó x másodperc",
"title": "Letöltés idejének beállítása"
},
"submenu": {
"advanced": "Speciális",
@ -440,6 +491,18 @@
"button": "Letöltés"
}
},
"equalizer": {
"description": "Hangszínszabályzót ad hozzá a zenelejátszóhoz",
"menu": {
"presets": {
"label": "Hangprofil",
"list": {
"bass-booster": "Basszuskiemelés"
}
}
},
"name": "Hangszínszabályzó"
},
"exponential-volume": {
"description": "Az hangerő csúszka exponenciálissá tételével könnyebbé válik az alacsony hangerő kiválasztása.",
"name": "Exponenciális hangerő"
@ -488,6 +551,7 @@
"host-only": "Csak a házigazda tudja vezérelni a lejátszási listát és a lejátszót",
"playlist": "Engedélyezi a vendégeknek a lejátszási lista vezérlését"
},
"set-permission": "Vezérlési engedély módosítása",
"status": {
"disconnected": "Kapcsolat bontva",
"guest": "Csatlakozva vendégként",
@ -560,7 +624,7 @@
}
},
"playback-speed": {
"description": "Hallgass gyorsan, hallgass lassan! Hozzáad egy csúszkát, amely szabályozza a dal sebességét",
"description": "Hallgassd gyorsan, hallgassd lassan! Hozzáad egy csúszkát, amely szabályozza a dal sebességét",
"name": "Lejátszás sebessége",
"templates": {
"button": "Sebesség"
@ -618,8 +682,10 @@
},
"listenbrainz": {
"token": "Add meg a ListenBrainz felhasználói tokenedet"
}
},
"scrobble-other-media": "Más média scrobbelése"
},
"name": "Scrobbler",
"prompt": {
"lastfm": {
"api-key": "Last.fm API kulcs",
@ -667,19 +733,47 @@
"synced-lyrics": {
"description": "Szinkronizált dalszövegeket biztosít dalokhoz, LRClib-hez hasonló szolgáltatókat használva.",
"errors": {
"fetch": "⚠️ - Hiba történt a dalszövegek lekérése közben. Kérlek, próbáld újra később.",
"not-found": "⚠️ - Nem található dalszöveg ehhez a zenéhez."
"fetch": "⚠️\tHiba történt a dalszöveg lekérése közben.\n\tKérk, próbálja meg később újra.",
"not-found": "⚠️ - Ehhez a dalhoz nem található dalszöveg."
},
"menu": {
"default-text-string": {
"label": "Alapértelmezett karakter a dalszövegek között",
"tooltip": "Válassza ki az alapértelmezett karaktert, amelyet a dalszövegek közötti szünethez használni szeretne"
},
"line-effect": {
"label": "Soreffekt",
"submenu": {
"fancy": {
"label": "Díszes",
"tooltip": "Használj nagy, alkalmazásszerű effektusokat az aktuális sorhoz"
},
"focus": {
"label": "Fókuszált",
"tooltip": "Az aktuális sor kijelőlése fehérrel"
},
"offset": {
"label": "Eltolás",
"tooltip": "Az aktuális sort jobbra tolja. (mintha tabulálnád)"
},
"scale": {
"label": "Mérték"
"label": "Méretezett",
"tooltip": "Az aktuális sort kissé nagyobbra méretezi, kiemelve azt a többi sor közül"
}
}
},
"tooltip": "Válassza ki az aktuális sorra alkalmazandó effektust"
},
"precise-timing": {
"label": "Dalszöveg tökéletes szinkronizálása"
"label": "Dalszöveg tökéletes szinkronizálása",
"tooltip": "Számítsa ki az aktuális sor megjelenítésének idejét ezredmásodperc pontossággal (ez kis mértékben befolyásolhatja a teljesítményt)"
},
"show-lyrics-even-if-inexact": {
"label": "Pontatlan időzítésű dalszövegek megjelenítése",
"tooltip": "Ha a dalt nem találja, a bővítmény újra próbálkozik egy másik keresési lekérdezéssel.\nAz eredmény a második próbálkozás után nem biztos, hogy pontos lesz."
},
"show-time-codes": {
"label": "Időkódok megjelenítése",
"tooltip": "Az időkódok megjelenítése a dalszövegek mellett"
}
},
"name": "Szinkronizált dalszövegek",

View File

@ -279,6 +279,13 @@
},
"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": {
"description": "Menambahkan server API untuk mengontrol pemutar",
"dialog": {
@ -726,8 +733,8 @@
"synced-lyrics": {
"description": "Menyediakan lirik lagu yang disinkronkan, menggunakan penyedia seperti LRClib.",
"errors": {
"fetch": "⚠️ - Terjadi kesalahan saat mengambil lirik. Coba lagi nanti.",
"not-found": "⚠️ - Tidak ada lirik yang ditemukan untuk lagu ini."
"fetch": "⚠️\tTerjadi kesalahan saat mengambil lirik.\n\tSilakan coba lagi nanti.",
"not-found": "⚠️ Tidak ada lirik yang ditemukan untuk lagu ini."
},
"menu": {
"default-text-string": {
@ -737,6 +744,10 @@
"line-effect": {
"label": "Efek garis",
"submenu": {
"fancy": {
"label": "Mewah",
"tooltip": "Gunakan efek besar seperti aplikasi pada baris saat ini"
},
"focus": {
"label": "Fokus",
"tooltip": "Jadikan hanya baris saat ini berwarna putih"

View File

@ -279,6 +279,13 @@
},
"name": "Modalità Ambiente"
},
"amuse": {
"description": "Aggiunge il supporto a YouTube Music per il widget Amuse Now Playing di 6K Labs",
"name": "Amuse",
"response": {
"query": "Il server API di Amuse è in funzione. GET /query per ottenere informazioni sui brani."
}
},
"api-server": {
"description": "Aggiunge un server API per controllare il player",
"dialog": {
@ -484,6 +491,18 @@
"button": "Scarica"
}
},
"equalizer": {
"description": "Aggiunge un equalizzatore al player",
"menu": {
"presets": {
"label": "Presets",
"list": {
"bass-booster": "Booster dei bassi"
}
}
},
"name": "Equalizzatore"
},
"exponential-volume": {
"description": "Rende esponenziale il cursore del volume, in modo da facilitare la selezione di volumi più bassi.",
"name": "Volume esponenziale"

274
src/i18n/resources/ka.json Normal file
View File

@ -0,0 +1,274 @@
{
"language": {
"code": "ka",
"local-name": "ქართული",
"name": "Georgian"
},
"main": {
"dialog": {
"need-to-restart": {
"buttons": {
"later": "მოგვიანებით"
}
},
"unresponsive": {
"buttons": {
"quit": "გასვლა",
"relaunch": "თავიდან გაშვება",
"wait": "მოცდა"
}
},
"update-available": {
"buttons": {
"download": "გადმოწერა",
"ok": "დიახ"
}
}
},
"menu": {
"about": "შესახებ",
"navigation": {
"label": "ნავიგაცია",
"submenu": {
"quit": "გასვლა"
}
},
"options": {
"label": "მორგება",
"submenu": {
"language": {
"label": "ენა"
},
"starting-page": {
"unset": "მოხსნა"
},
"tray": {
"submenu": {
"disabled": "გამორთულია"
}
},
"visual-tweaks": {
"submenu": {
"like-buttons": {
"default": "ნაგულისხმევი",
"hide": "დამალვა"
},
"theme": {
"dialog": {
"button": {
"cancel": "გაუქმება",
"remove": "წაშლა"
}
},
"label": "თემა"
}
}
}
}
},
"plugins": {
"enabled": "ჩართულია",
"label": "დამატებები",
"new": "ახალი"
},
"view": {
"label": "ხედი",
"submenu": {
"reload": "თავიდან ჩატვირთვა"
}
}
},
"tray": {
"next": "შემდეგი",
"play-pause": "დაკვრა/შეჩერება",
"previous": "წინა",
"quit": "გასვლა"
}
},
"plugins": {
"adblocker": {
"menu": {
"blocker": "დამბლოკავი"
}
},
"album-color-theme": {
"menu": {
"color-mix-ratio": {
"submenu": {
"percent": "{{ratio}}%"
}
}
}
},
"ambient-mode": {
"menu": {
"buffer": {
"label": "ბუფერი",
"submenu": {
"buffer": "{{buffer}}"
}
},
"opacity": {
"label": "გაუმჭვირვალობა",
"submenu": {
"percent": "{{opacity}}%"
}
},
"quality": {
"label": "ხარისხი"
},
"size": {
"label": "ზომა",
"submenu": {
"percent": "{{size}}%"
}
}
}
},
"amuse": {
"name": "Amuse"
},
"api-server": {
"dialog": {
"request": {
"buttons": {
"allow": "დაშვება",
"deny": "აკრძალვა"
}
}
},
"menu": {
"hostname": {
"label": "ჰოსტის სახელი"
},
"port": {
"label": "პორტი"
}
},
"prompt": {
"hostname": {
"title": "ჰოსტის სახელი"
},
"port": {
"title": "პორტი"
}
}
},
"captions-selector": {
"prompt": {
"selector": {
"none": "არცერთი"
}
}
},
"crossfade": {
"menu": {
"advanced": "დამატებით"
},
"prompt": {
"options": {
"multi-input": {
"fade-scaling": {
"linear": "წრფივი",
"logarithmic": "ლოგარითმული"
}
}
}
}
},
"discord": {
"menu": {
"connected": "დაკავშირებული",
"disconnected": "გათიშული"
}
},
"downloader": {
"backend": {
"dialog": {
"error": {
"buttons": {
"ok": "დიახ"
}
},
"start-download-playlist": {
"buttons": {
"ok": "დიახ"
}
}
},
"feedback": {
"converting": "გადაყვანა…",
"downloading": "გადმოწერა…",
"loading": "ჩატვირთვა…",
"saving": "შენახვა…"
}
},
"menu": {
"download-finish-settings": {
"submenu": {
"advanced": "დამატებით",
"enabled": "ჩართულია",
"percent": "პროცენტი",
"seconds": "წამი"
}
},
"presets": "პრესეტი"
},
"name": "გადმომწერი",
"templates": {
"button": "გადმოწერა"
}
},
"music-together": {
"internal": {
"save": "შენახვა"
},
"menu": {
"status": {
"disconnected": "გათიშული"
}
}
},
"navigation": {
"name": "ნავიგაცია"
},
"notifications": {
"name": "გაფრთხილებები"
},
"picture-in-picture": {
"name": "სურათი სურათში",
"templates": {
"button": "სურათი სურათში"
}
},
"playback-speed": {
"templates": {
"button": "სიჩქარე"
}
},
"shortcuts": {
"prompt": {
"keybind": {
"keybind-options": {
"next": "შემდეგი",
"previous": "წინა"
}
}
}
},
"sponsorblock": {
"name": "სარეკლამო ბლოკი"
},
"synced-lyrics": {
"menu": {
"line-effect": {
"submenu": {
"focus": {
"label": "ფოკუსი"
}
}
}
}
}
}
}

View File

@ -33,7 +33,7 @@
"css-file-not-found": "CSS-bestand \"{{cssFile}}\" bestaat niet, wordt genegeerd"
},
"unresponsive": {
"details": "Onverantwoordelijkheidsfout!\n{{error}}"
"details": "Niet-reagerende fout!\n{{error}}"
},
"when-ready": {
"clearing-cache-after-20s": "App-cache wissen"
@ -46,7 +46,7 @@
"hide-menu-enabled": {
"detail": "Menu is verborgen, gebruik 'Alt' om het weer te geven (of 'Escape' als u de In-App Menu gebruikt)",
"message": "Menu verbergen is ingeschakeld",
"title": "Menu Verbergen Ingeschakeld"
"title": "Menu verbergen ingeschakeld"
},
"need-to-restart": {
"buttons": {
@ -65,7 +65,7 @@
},
"detail": "Het programma reageert niet! Kies wat u wilt doen:",
"message": "De applicatie reageert niet",
"title": "Venster Niet Reagerend"
"title": "Venster reageert niet"
},
"update-available": {
"buttons": {
@ -250,7 +250,7 @@
}
},
"opacity": {
"label": "Dekking",
"label": "Transparantie",
"submenu": {
"percent": "{{opacity}}%"
}
@ -268,7 +268,7 @@
}
},
"smoothness-transition": {
"label": "Soepelheid overgang",
"label": "Vloeiende overgang",
"submenu": {
"during": "Tijdens {{interpolationTime}} s"
}
@ -279,6 +279,56 @@
},
"name": "Omgevingsmodus"
},
"amuse": {
"description": "Voegt YouTube Music ondersteuning toe voor de Amuse now playing widget van 6K Labs",
"name": "Amuse",
"response": {
"query": "Amuse API server loopt. Gebruik /query voor nummer informatie."
}
},
"api-server": {
"description": "Voegt een API server toe om de speler te besturen",
"dialog": {
"request": {
"buttons": {
"allow": "Toestaan",
"deny": "Weigeren"
},
"message": "Sta {{ID}} {{origin}} toegang toe tot de API?",
"title": "API autorisatieverzoek"
}
},
"menu": {
"auth-strategy": {
"label": "Autorisatie strategie",
"submenu": {
"auth-at-first": {
"label": "Autoriseer bij eerste verzoek"
},
"none": {
"label": "geen autorisatie"
}
}
},
"hostname": {
"label": "Hostnaam"
},
"port": {
"label": "Poort"
}
},
"name": "API Server [Beta]",
"prompt": {
"hostname": {
"label": "Voeg de hostnaam (bv. 0.0.0.0) voor de API server in:",
"title": "Hostnaam"
},
"port": {
"label": "Voeg de poort voor de API server in:",
"title": "Poort"
}
}
},
"audio-compressor": {
"description": "Past compressie toe op audio (verlaagt het volume van de luidste delen van het signaal en verhoogt het volume van de zachtste delen)",
"name": "Audiocompressor"
@ -395,7 +445,7 @@
"download-progress": "Downloaden: {{percent}}%",
"downloading": "Aan het downloaden…",
"downloading-counter": "{{current}}/{{total}} aan het downloaden…",
"downloading-playlist": "Afspeellijst \"{{playlistTitle}}\" {{playlistId}} aan het downloaden ({{playlistSize}} liederen)",
"downloading-playlist": "Afspeellijst \"{{playlistTitle}}\" {{playlistId}} aan het downloaden ({{playlistSize}} nummers)",
"error-while-downloading": "Er is een fout opgetreden tijdens het downloaden van \"{{author}} - {{title}}\": {{error}}",
"folder-already-exists": "De map \"{{playlistFolder}}\" bestaat al",
"getting-playlist-info": "Afspeellijst informatie ophalen…",
@ -441,6 +491,18 @@
"button": "Download"
}
},
"equalizer": {
"description": "Voegt een equalizer toe aan de speler",
"menu": {
"presets": {
"label": "Voorinstellingen",
"list": {
"bass-booster": "Basversterker"
}
}
},
"name": "Equalizer"
},
"exponential-volume": {
"description": "Maakt de volumeschuif exponentieel zodat het gemakkelijker is om lagere volumes te selecteren.",
"name": "Exponentieel Volume"
@ -450,14 +512,14 @@
"menu": {
"hide-dom-window-controls": "Verberg DOM-vensterbedieningselementen"
},
"name": "In-App menu"
"name": "In-App Menu"
},
"lumiastream": {
"description": "Voegt ondersteuning voor Lumia Stream toe",
"name": "Lumia Stream [Beta]"
},
"lyrics-genius": {
"description": "Voegt tekstondersteuning toe voor de meeste nummers",
"description": "Voegt songtekstondersteuning toe voor de meeste nummers",
"menu": {
"romanized-lyrics": "Geromaniseerde Teksten"
},
@ -527,15 +589,19 @@
"interactive-settings": {
"label": "Interactieve instellingen",
"submenu": {
"hide-button-text": "Verberg tekst op de knop",
"refresh-on-play-pause": "Herlaad bij het afspelen/pauzeren"
"hide-button-text": "Knoptekst verbergen",
"refresh-on-play-pause": "Herlaad bij het afspelen/pauzeren",
"tray-controls": "Open/Sluit op tray klik"
}
},
"priority": "Meldingprioriteit",
"toast-style": "Toast stijl",
"unpause-notification": "Laat een notificatie zijn bij het depauzeren"
},
"name": "Meldingen"
},
"picture-in-picture": {
"description": "Laat de app toe om naar picture-in-picture modus om te schakelen",
"menu": {
"always-on-top": "Altijd bovenaan",
"hotkey": {
@ -543,26 +609,226 @@
"prompt": {
"keybind-options": {
"hotkey": "Sneltoets"
}
},
"label": "Kies een sneltoets om tussen picture-in-picture te schakelen",
"title": "Picture-in-picture sneltoets"
}
},
"save-window-position": "Sla schermpositie op",
"save-window-size": "Sla schermgrootte op"
"save-window-size": "Sla schermgrootte op",
"use-native-pip": "Gebruik browser ingebouwde PiP"
},
"name": "Picture-in-picture",
"templates": {
"button": "Picture-in-picture"
}
},
"video-toggle": {
"playback-speed": {
"description": "Luister snel, luister langzaam! Voegt een schuifregelaar toe die de muzieksnelheid regelt",
"name": "Afspeelsnelheid",
"templates": {
"button": "Snelheid"
}
},
"precise-volume": {
"description": "Regel het volume nauwkeurig met behulp van het muiswiel/sneltoetsen, met een aangepaste HUD en aanpasbare volumestappen",
"menu": {
"mode": {
"submenu": {
"disabled": "Uitgeschakeld"
"arrows-shortcuts": "Lokale Pijltjestoetsen bediening",
"custom-volume-steps": "Stel aangepaste volumestappen in",
"global-shortcuts": "Globale sneltoetsen"
},
"name": "Nauwkeurig volume",
"prompt": {
"global-shortcuts": {
"keybind-options": {
"decrease": "Volume verlagen",
"increase": "Volume verhogen"
},
"label": "Kies Globale Volume toetsen:",
"title": "Globale Volume toetsen"
},
"volume-steps": {
"label": "Kies Stappen voor volumeverhoging/-verlaging",
"title": "Volume Stappen"
}
}
},
"quality-changer": {
"backend": {
"dialog": {
"quality-changer": {
"detail": "Huidige kwaliteit: {{quality}}",
"message": "Kies videokwaliteit:",
"title": "Kies Videokwaliteit"
}
}
},
"description": "Maakt het mogelijk de videokwaliteit te wijzigen met een knop op de video-overlay",
"name": "Videokwaliteitwisselaar"
},
"scrobbler": {
"description": "Ondersteuning voor scrobbling toevoegen (etc. last.fm, Listenbrainz)",
"dialog": {
"lastfm": {
"auth-failed": {
"message": "Kan niet verifiëren bij Last.fm\nVerberg de pop-up tot de volgende herstart.",
"title": "Authenticatie mislukt"
}
}
},
"menu": {
"lastfm": {
"api-settings": "Last.fm API Instellingen"
},
"listenbrainz": {
"token": "Voer het ListenBrainz-gebruikerstoken in"
},
"scrobble-other-media": "Scrobble andere media"
},
"name": "Scrobbler",
"prompt": {
"lastfm": {
"api-key": "Last.fm API sleutel",
"api-secret": "Last.fm API geheim"
},
"listenbrainz": {
"token": {
"label": "Voer uw ListenBrainz-gebruikerstoken in:",
"title": "ListenBrainz token"
}
}
}
},
"visualizer": {
"description": "Voeg een visuele equalizer toe",
"shortcuts": {
"description": "Maakt het mogelijk algemene sneltoetsen in te stellen voor afspelen (afspelen/pauzeren/volgende/vorige) en het uitschakelen van media-OSD door mediatoetsen te overschrijven, het inschakelen van Ctrl/CMD + F om te zoeken, het inschakelen van Linux MPRIS-ondersteuning voor mediatoetsen en aangepaste sneltoetsen voor gevorderde gebruikers",
"menu": {
"visualizer-type": "Type visualisator"
"override-media-keys": "Media toetsen overschrijven",
"set-keybinds": "Stel de algemene songbediening in"
},
"name": "Snelkoppelingen (& MPRIS)",
"prompt": {
"keybind": {
"keybind-options": {
"next": "Volgende",
"play-pause": "Afspelen / Pauzeren",
"previous": "Vorige"
},
"label": "Kies globale toetsen voor nummer bediening:",
"title": "Globale toetsen"
}
}
},
"skip-disliked-songs": {
"description": "Slaat disliked nummers over",
"name": "Sla disliked nummers over"
},
"skip-silences": {
"description": "Sla automatisch stiltesecties in nummers over",
"name": "Stiltes overslaan"
},
"sponsorblock": {
"description": "Slaat automatisch niet-muziekgedeelten over, zoals intro/outro of gedeelten van muziekvideo's waarin het nummer niet wordt afgespeeld",
"name": "SponsorBlock"
},
"synced-lyrics": {
"description": "Biedt gesynchroniseerde songteksten voor nummers, met behulp van providers zoals LRClib.",
"errors": {
"fetch": "⚠️\tEr is een fout opgetreden bij het ophalen van de songtekst.\n\tProbeer het later opnieuw.",
"not-found": "⚠️ Er is geen songtekst gevonden voor dit nummer."
},
"menu": {
"default-text-string": {
"label": "Standaardteken tussen songteksten",
"tooltip": "Kies het standaardteken dat u wilt gebruiken voor de opening tussen de songteksten"
},
"line-effect": {
"label": "Lijneffect",
"submenu": {
"fancy": {
"label": "Luxe",
"tooltip": "Gebruik grote, app-achtige effecten op de huidige regel"
},
"focus": {
"label": "Focus",
"tooltip": "Maak alleen de huidige regel wit"
},
"offset": {
"label": "Offset",
"tooltip": "Offset aan de rechterkant van de huidige lijn"
},
"scale": {
"label": "Schaal",
"tooltip": "Schaal de huidige regel"
}
},
"tooltip": "Kies het effect dat u op de huidige regel wilt toepassen"
},
"precise-timing": {
"label": "Zorg ervoor dat de songteksten perfect gesynchroniseerd zijn",
"tooltip": "Bereken tot op de milliseconde de weergave van de volgende regel (kan een kleine impact hebben op de prestaties)"
},
"show-lyrics-even-if-inexact": {
"label": "Toon songteksten, zelfs als ze onnauwkeurig zijn",
"tooltip": "Als het nummer niet wordt gevonden, probeert de plug-in het opnieuw met een andere zoekopdracht.\nHet resultaat van de tweede poging is mogelijk niet exact."
},
"show-time-codes": {
"label": "Toon tijdcodes",
"tooltip": "Toon de tijdcodes naast de songtekst"
}
},
"name": "Gesynchroniseerde songteksten",
"refetch-btn": {
"fetching": "Ophalen...",
"normal": "Songteksten opnieuw ophalen"
},
"warnings": {
"duration-mismatch": "⚠️ - De songteksten zijn mogelijk niet synchroon vanwege een niet-overeenkomende duur.",
"inexact": "⚠️ - De songtekst van dit nummer is mogelijk niet exact",
"instrumental": "⚠️ - Dit is een instrumentaal nummer"
}
},
"taskbar-mediacontrol": {
"description": "Bedien het afspelen vanaf uw Windows-taakbalk",
"name": "Taakbalk Mediabediening"
},
"touchbar": {
"description": "Voegt een TouchBar-widget toe voor macOS-gebruikers",
"name": "TouchBar"
},
"tuna-obs": {
"description": "Integratie met OBS's plug-in Tuna",
"name": "Tuna OBS"
},
"video-toggle": {
"description": "Voegt een knop toe om te schakelen tussen de video-/nummermodus. kan optioneel ook het hele videotabblad verwijderen",
"menu": {
"align": {
"label": "Uitlijning",
"submenu": {
"left": "Links",
"middle": "Midden",
"right": "Rechts"
}
},
"force-hide": "Forceer het verwijderen van het videotabblad",
"mode": {
"label": "Modus",
"submenu": {
"custom": "Aangepaste schakelaar",
"disabled": "Uitgeschakeld",
"native": "Native schakelaar"
}
}
},
"name": "Videoschakelaar",
"templates": {
"button": "Nummer"
}
},
"visualizer": {
"description": "Voegt een visualisator toe aan de speler",
"menu": {
"visualizer-type": "Visualisatietype"
},
"name": "Visualisator"
}

View File

@ -279,6 +279,13 @@
},
"name": "Tryb otoczenia"
},
"amuse": {
"description": "Wspiera integrację YouTube Music z widgetami Amuse (od 6K Labs)",
"name": "Amuse",
"response": {
"query": "Serwer API Amuse działa. Użyj metody GET do /query, aby zdobyć informację o utworze."
}
},
"api-server": {
"description": "Pozwala na kontrolowanie YouTube Music poprzez podłączenie specjalnego serwera API",
"dialog": {
@ -484,6 +491,18 @@
"button": "Pobierz"
}
},
"equalizer": {
"description": "Dodaje equalizer do odtwarzacza",
"menu": {
"presets": {
"label": "Presety",
"list": {
"bass-booster": "Wzmacniacz basu"
}
}
},
"name": "Korektor"
},
"exponential-volume": {
"description": "Sprawia, że suwak głośności jest proporcjonalna, dzięki czemu łatwiej jest wybrać niższą głośność.",
"name": "Proporcjonalna głośność"
@ -714,8 +733,8 @@
"synced-lyrics": {
"description": "Dodaje zsynchronizowane napisy do utworów używając między innymi LRClib.",
"errors": {
"fetch": "⚠️ - Wystąpił błąd podczas pobierania tekstu utworu. Spróbuj ponownie później.",
"not-found": "⚠️ - Nie znaleziono napisów dla tego utworu."
"fetch": "⚠️\tWystąpił błąd podczas pobierania tekstu utworu.\n\tSpróbuj ponownie później.",
"not-found": "⚠️ Nie znaleziono napisów dla tego utworu."
},
"menu": {
"default-text-string": {
@ -725,6 +744,10 @@
"line-effect": {
"label": "Efekty linijki",
"submenu": {
"fancy": {
"label": "Facy",
"tooltip": "Użyj specjalnych efektów w stylu aplikacji na obecną linię"
},
"focus": {
"label": "Fokus",
"tooltip": "Spraw, aby tylko obecna linijka była biała"

View File

@ -279,6 +279,13 @@
},
"name": "Modo ambiente"
},
"amuse": {
"description": "Adiciona suporte ao YouTube Music ao widget 'Reproduzindo agora' do Amuse da 6K Labs",
"name": "Amuse",
"response": {
"query": "Servidor API do Amuse em execução. GET /query para obter informações da música."
}
},
"api-server": {
"description": "Adiciona um servidor API para controlar o player",
"dialog": {

View File

@ -268,38 +268,45 @@
}
},
"smoothness-transition": {
"label": "Fluenta tranzitiei",
"label": "Fluiditatea tranzitiei",
"submenu": {
"during": "In timpul {{interpolationTime}} s"
}
},
"use-fullscreen": {
"label": "Foloseste fullscreen"
"label": "Ecran Plin în utilizare"
}
},
"name": "Mod ambiental"
},
"amuse": {
"description": "Adauga suport Youtube Music pentru Amuse se redă acum widget de 6K Labs",
"name": "Amuse",
"response": {
"query": "Server-ul API-ului Amuse rulează. GET /query pentru a obține informații despre melodie."
}
},
"api-server": {
"description": "Adauga un server API pentru a controla player-ul",
"description": "Adaugă un server API pentru a controla player-ul",
"dialog": {
"request": {
"buttons": {
"allow": "Permite",
"deny": "Respinge"
},
"message": "Permite {{ID}} {{origin}} sa acceseze API-ul?",
"message": "Permite {{ID}} {{origin}} să acceseze API-ul?",
"title": "Cerere autorizare API"
}
},
"menu": {
"auth-strategy": {
"label": "Strategie autorizare",
"label": "Strategie de autorizare",
"submenu": {
"auth-at-first": {
"label": "Autorizare la prima cerere"
},
"none": {
"label": "Fara autorizare"
"label": "Fără autorizare"
}
}
},
@ -313,101 +320,101 @@
"name": "Server API [Beta]",
"prompt": {
"hostname": {
"label": "Introduceti nume host (0.0.0.0 de ex.) pentru server-ul API:",
"label": "Introduceți nume host (0.0.0.0 de ex.) pentru server-ul API:",
"title": "Nume host"
},
"port": {
"label": "Introduceti port-ul pentru server-ul API:",
"label": "Introduceți port-ul pentru server-ul API:",
"title": "Port"
}
}
},
"audio-compressor": {
"description": "Aplica compresie pe audio (scade volumul partilor cele mai sonore si creste volumul partilor mai putin sonore)",
"description": "Aplică compresie pe audio (scade volumul părților cele mai zgomotoase și crește volumul părților mai puțin zgomotoase)",
"name": "Compresor audio"
},
"blur-nav-bar": {
"description": "Fa bara de navigare semi-transparenta",
"name": "Bara de naviagtie semi-transparenta"
"description": "Face bara de navigare semi-transparentă",
"name": "Estompează Bara de Navigație"
},
"bypass-age-restrictions": {
"description": "Treci peste verificarea de varsta a YouTube-ului",
"name": "Ignora restrictiile de varsta"
"description": "Treci peste verificarea de vârstă a YouTube-ului",
"name": "Ignoră restricțiile de vârstă"
},
"captions-selector": {
"description": "Selector de subtitrari pentru piesele audio de pe YouTube Music",
"description": "Selector de subtitrări pentru piesele audio de pe YouTube Music",
"menu": {
"autoload": "Selecteaza automat ultima subtitrare folosita",
"disable-captions": "Fara subtitrari by default"
"autoload": "Selectează automat ultima subtitrare folosită",
"disable-captions": "Fără subtitrări în mod implicit"
},
"name": "Selector de subtitrari",
"name": "Selector de subtitrări",
"prompt": {
"selector": {
"label": "Limba curenta a subtitrarilor: {{language}}",
"label": "Limba curentă a subtitrărilor: {{language}}",
"none": "Niciuna",
"title": "Alege limba subtitrarilor"
"title": "Alege limba subtitrărilor"
}
},
"templates": {
"title": "Deschide selectorul de subtitrari"
"title": "Deschide selectorul de subtitrări"
}
},
"compact-sidebar": {
"description": "Pastreaza bara laterala mereu in modul compact",
"name": "Bara laterala compacta"
"description": "Păstrează bara laterală mereu în modul compact",
"name": "Bara Laterală Compactă"
},
"crossfade": {
"description": "Tranzitioneaza intre cantece",
"description": "Tranziționează între melodii",
"menu": {
"advanced": "Avansat"
},
"name": "Tranzitie [Beta]",
"name": "Tranziție [Beta]",
"prompt": {
"options": {
"multi-input": {
"fade-in-duration": "Durata tranzitie de inceput (ms)",
"fade-out-duration": "Durata tranzitie de sfarsit (ms)",
"fade-in-duration": "Durată tranziție de început (ms)",
"fade-out-duration": "Durată tranziției de sfârșit (ms)",
"fade-scaling": {
"label": "Scala tranzitiei",
"linear": "Linear",
"label": "Scalare de estompare",
"linear": "Liniar",
"logarithmic": "Logaritmic"
},
"seconds-before-end": "Tranzitie N secunde inainte de final"
"seconds-before-end": "Tranziție N secunde înainte de final"
},
"title": "Optiuni de tranzitie"
"title": "Opțiuni de tranziție"
}
}
},
"disable-autoplay": {
"description": "Fa cantecul sa inceapa in modul \"pauza\"",
"description": "Face cântecul să înceapă în modul \"pauză\"",
"menu": {
"apply-once": "Se aplica doar la pornirea aplicatiei"
"apply-once": "Se aplică doar la pornirea aplicației"
},
"name": "Dezactiveaza redarea automata"
"name": "Dezactivează redarea automată"
},
"discord": {
"backend": {
"already-connected": "S-a incercat conectarea cu o conexiune activa",
"already-connected": "S-a încercat conectarea cu o conexiune activă",
"connected": "Conectat la Discord",
"disconnected": "Deconectat de la Discord"
},
"description": "Arata-le prietenilor ce asculti cu Rich Presence",
"description": "Arată-le prietenilor ce asculți cu Rich Presence",
"menu": {
"auto-reconnect": "Reconectare automata",
"clear-activity": "Sterge activitatea",
"clear-activity-after-timeout": "Sterge activitatea dupa timeout",
"auto-reconnect": "Reconectare automată",
"clear-activity": "Șterge activitatea",
"clear-activity-after-timeout": "Șterge activitatea după timeout",
"connected": "Conectat",
"disconnected": "Deconectat",
"hide-duration-left": "Ascunde timpul ramas",
"hide-duration-left": "Ascunde timpul rămas",
"hide-github-button": "Ascunde butonul cu link-ul GitHub",
"play-on-youtube-music": "Reda pe YouTube Music",
"set-inactivity-timeout": "Seteaza intervalul de inactivitate"
"play-on-youtube-music": "Redă pe YouTube Music",
"set-inactivity-timeout": "Setează intervalul de inactivitate"
},
"name": "Discord Rich Presence",
"prompt": {
"set-inactivity-timeout": {
"label": "Introduceti perioada de inactivitate dorita in secunde:",
"title": "Seteaza timpul de inactivitate"
"label": "Introduceți perioada de inactivitate dorită în secunde:",
"title": "Setează timpul de inactivitate"
}
}
},
@ -418,51 +425,51 @@
"buttons": {
"ok": "OK"
},
"message": "Argh! Scuze, descarcarea a esuat…",
"title": "Eroare la descarcare!"
"message": "Ah! Scuze, descărcarea a eșuat…",
"title": "Eroare la descărcare!"
},
"start-download-playlist": {
"buttons": {
"ok": "OK"
},
"detail": "({{playlistSize}} cantece)",
"detail": "({{playlistSize}} melodii)",
"message": "Se descarca Playlist-ul {{playlistTitle}}",
"title": "Descarcarea a inceput"
"title": "Descărcarea a început"
}
},
"feedback": {
"conversion-progress": "Conversie: {{percent}}%",
"converting": "Se converteste…",
"done": "Descarcat: {{filePath}}",
"download-info": "Se descarca {{artist}} -{{title}} [{{videoId}}",
"download-progress": "Se descarca: {{percent}}%",
"downloading": "Se descarca…",
"downloading-counter": "Se descarca {{current}}/{{total}}…",
"downloading-playlist": "Se descarca playlist-ul \"{{playlistTitle}}\" - {{playlistSize}} piese ({{playlistId}})",
"error-while-downloading": "Eroare la descarcarea piesei \"{{author}} - {{title}}\":{{error}}",
"folder-already-exists": "Folderul {{playlistFolder}} exista deja",
"getting-playlist-info": "Se aduna informatiile despre playlist…",
"loading": "Se incarca…",
"playlist-has-only-one-song": "Playlist-ul are doar un element, acesta va fi descarcat direct",
"playlist-id-not-found": "Niciun ID al playlist-ului nu a fost gasit",
"playlist-is-empty": "Playlist-ul este gol",
"playlist-is-mix-or-private": "Eroare la colectarea informatiilor despre playlist: asigurati-va ca nu este privat sau un playlist \"Mixed for you\"\n\n{{error}}",
"preparing-file": "Se pregateste fisierul…",
"saving": "Se salveaza…",
"trying-to-get-playlist-id": "Se incearca obtinerea ID-ului playlist-ului: {{playlistId}}",
"video-id-not-found": "Video-ul nu a fost gasit",
"converting": "Se convertește…",
"done": "Descărcat: {{filePath}}",
"download-info": "Se descarcă {{artist}} -{{title}} [{{videoId}}",
"download-progress": "Se descarcă: {{percent}}%",
"downloading": "Se descarcă…",
"downloading-counter": "Se descarcă {{current}}/{{total}}…",
"downloading-playlist": "Se descarcă lista de redare \"{{playlistTitle}}\" - {{playlistSize}} piese ({{playlistId}})",
"error-while-downloading": "Eroare la descarcareă piesei \"{{author}} - {{title}}\":{{error}}",
"folder-already-exists": "Dosarul {{playlistFolder}} există deja",
"getting-playlist-info": "Se adună informațiile despre lista de redare…",
"loading": "Se incarcă…",
"playlist-has-only-one-song": "Lista de redare are doar un element, acesta va fi descărcat direct",
"playlist-id-not-found": "Niciun ID al listei de redare nu a fost gasit",
"playlist-is-empty": "Lista de redare este goală",
"playlist-is-mix-or-private": "Eroare la colectarea informațiilor despre lista de redare: asigurați-vă că nu este privat sau o listă de redare \"Mixed for you\"\n\n{{error}}",
"preparing-file": "Se pregătește fișierul…",
"saving": "Se salvează…",
"trying-to-get-playlist-id": "Se încearcă obținerea ID-ului listei de redare: {{playlistId}}",
"video-id-not-found": "Videoclipul nu a fost găsit",
"writing-id3": "Se scriu tag-urile ID3…"
}
},
"description": "Descarca MP3 / sursa audio direct din interfata",
"description": "Descarcă MP3 / sursa audio direct din interfață",
"menu": {
"choose-download-folder": "Alege folderul de descarcari",
"choose-download-folder": "Alege folderul de descărcări",
"download-finish-settings": {
"label": "Descarcare la finalizare",
"label": "Descărcare la finalizare",
"prompt": {
"last-percent": "Dupa x la suta",
"last-percent": "După x la sută",
"last-seconds": "Ultimele x secunde",
"title": "Configureaza cand sa descarce"
"title": "Configurează când să se descarce"
},
"submenu": {
"advanced": "Avansat",
@ -472,35 +479,47 @@
"seconds": "Secunde"
}
},
"download-playlist": "Descarca playlist-ul",
"presets": "Setari implicite",
"skip-existing": "Treci peste fisierele existente"
"download-playlist": "Descarcă lista de redare",
"presets": "Setări implicite",
"skip-existing": "Treci peste fișierele existente"
},
"name": "Downloader",
"name": "Descărcător",
"renderer": {
"can-not-update-progress": "Nu se poate actualiza progresul"
},
"templates": {
"button": "Descarca"
"button": "Descarcă"
}
},
"equalizer": {
"description": "Adauă un egalizator la player",
"menu": {
"presets": {
"label": "Setări implicite",
"list": {
"bass-booster": "Amplificator de bas"
}
}
},
"name": "Egalizator"
},
"exponential-volume": {
"description": "Fa slider-ul de volum exponential pentru a fi mai usor de selectat volumuri reduse.",
"name": "Volum exponential"
"description": "Face glisorul de volum exponențial pentru a fi mai ușor de selectat volume reduse.",
"name": "Volum exponențial"
},
"in-app-menu": {
"description": "Ofera barelor de meniu un aspect extravagant, intunecat sau de culoarea albumului",
"description": "Oferă barelor de meniu un aspect extravagant, întunecat sau de culoarea albumului",
"menu": {
"hide-dom-window-controls": "Ascunde controalele ferestrei DOM"
},
"name": "Meniul aplicatiei"
"name": "Meniul aplicației"
},
"lumiastream": {
"description": "Adauga asistenta pentru Lumia Stream",
"description": "Adaugă asistenta pentru Lumia Stream",
"name": "Lumia Stream [Beta]"
},
"lyrics-genius": {
"description": "Adauga versuri pentru majoritatea cantecelor",
"description": "Adaugă versuri pentru majoritatea cântecelor",
"menu": {
"romanized-lyrics": "Versuri romantizate"
},
@ -510,29 +529,29 @@
}
},
"music-together": {
"description": "Impartaseste playlist-ul cu altii. Cand gazda va pune o piesa, toti ceilalti vor auzi acelasi cantec",
"description": "Împărtășește lista de redare cu alții. Când gazda va pune o piesă, toți ceilalți vor auzi aceeași melodie",
"dialog": {
"enter-host": "Introdu ID-ul host-ului"
},
"internal": {
"save": "Salveaza",
"save": "Salvează",
"track-source": "Sursa piesei",
"unknown-user": "Utilizator necunoscut"
},
"menu": {
"click-to-copy-id": "Copiaza ID-ul host-ului",
"close": "Inchide Music Together",
"connected-users": "Utilizatori conectati",
"disconnect": "Deconecteaza Music Together",
"click-to-copy-id": "Copiază ID-ul host-ului",
"close": "Închide Music Together",
"connected-users": "Utilizatori conecțati",
"disconnect": "Deconectează Music Together",
"empty-user": "Niciun utilizator conectat",
"host": "Gazda Music Together",
"join": "Alatura-te Music Together",
"join": "Alătura-te Music Together",
"permission": {
"all": "Permite invitatilor sa controleze playlist-ul si player-ul",
"host-only": "Doar gazda poate controla playlist-ul si player-ul",
"playlist": "Permite invitatilor controlul asupra playlist-ului"
"all": "Permite invitaților să controleze lista de redare si player-ul",
"host-only": "Doar gazda poate controla lista de redare și player-ul",
"playlist": "Permite invitaților controlul asupra listei de redare"
},
"set-permission": "Schimba controlul permisiunilor",
"set-permission": "Schimbă controlul permisiunilor",
"status": {
"disconnected": "Deconectat",
"guest": "Conectat ca invitat",
@ -541,63 +560,63 @@
},
"name": "Music Together [Beta]",
"toast": {
"add-song-failed": "Adaugarea piesei a esuat",
"closed": "Music Together inchis",
"add-song-failed": "Adăugarea piesei a eșuat",
"closed": "Music Together închis",
"disconnected": "Music Together deconectat",
"host-failed": "Nu s-a reusit gazduirea Music Together",
"id-copied": "ID-ul host-ului a fost copiat in clipboard",
"id-copy-failed": "Eroare la copierea ID-ului host-ului in clipboard",
"join-failed": "Nu s-a reusit alaturarea la Music Together",
"joined": "V-ati alaturat Music Together",
"host-failed": "Nu s-a reușit găzduirea Music Together",
"id-copied": "ID-ul host-ului a fost copiat în clipboard",
"id-copy-failed": "Eroare la copierea ID-ului host-ului în clipboard",
"join-failed": "Nu s-a reușit alăturarea la Music Together",
"joined": "V-ați alăturat Music Together",
"permission-changed": "Permisiunile Music Together s-au schimbat la \"{{permission}}\"",
"remove-song-failed": "Eroare la indepartarea cantecului",
"user-connected": "{{name}} s-a alaturat la Music Together",
"user-disconnected": "{{name}} a parasit Music Together"
"remove-song-failed": "Eroare la îndepărtarea melodiei",
"user-connected": "{{name}} s-a alăturat la Music Together",
"user-disconnected": "{{name}} a părăsit Music Together"
}
},
"navigation": {
"description": "Sagetile pentru Urmatorul/Anteriorul integrate direct in interfata, ca in browser-ul tau preferat",
"name": "Navigatie"
"description": "Săgețile pentru Următorul/Anteriorul integrate direct în interfață, ca în browser-ul tău preferat",
"name": "Navigație"
},
"no-google-login": {
"description": "Elimina butonul de autentificare Google si link-urile din interfata",
"description": "Elimină butonul de autentificare Google și link-urile din interfață",
"name": "Nicio autentificare Google"
},
"notifications": {
"description": "Afiseaza o notificare cand incepe sa cante o piesa (notificarile interactive sunt disponibile pe Windows)",
"description": "Afișează o notificare când începe să cânte o piesă (notificările interactive sunt disponibile pe Windows)",
"menu": {
"interactive": "Notificari interactive",
"interactive": "Notificări interactive",
"interactive-settings": {
"label": "Setari interactive",
"label": "Setări interactive",
"submenu": {
"hide-button-text": "Ascunde textul butoanelor",
"refresh-on-play-pause": "Reimprospateaza la Reda/Pauza",
"tray-controls": "Deschide/Inchide la apasarea icnoitei pentru meniul Tray"
"refresh-on-play-pause": "Reîmprospătează la Redă/Pauză",
"tray-controls": "Deschide/Închide la apăsarea iconiței pentru meniul Tray"
}
},
"priority": "Prioritatea notificarilor",
"toast-style": "Stilul notificarilor",
"unpause-notification": "Arata notificarile la pauza"
"priority": "Prioritatea notificărilor",
"toast-style": "Stilul notificărilor",
"unpause-notification": "Arată notificările la pauză"
},
"name": "Notificari"
"name": "Notificări"
},
"picture-in-picture": {
"description": "Permite sa schimbi aplicatie la modul picture-in-picture",
"description": "Permite să schimbi aplicația la modul picture-in-picture",
"menu": {
"always-on-top": "Mereu deasupra",
"hotkey": {
"label": "Scurtaturi pe tastatura",
"label": "Scurtături pe tastatură",
"prompt": {
"keybind-options": {
"hotkey": "Scurtaturi din taste"
"hotkey": "Scurtături din taste"
},
"label": "Scurtaturi din taste pentru picture-in-picture",
"title": "Scurtatura Picture-in-picture"
"label": "Alege tasta pentru picture-in-picture",
"title": "Scurtătura Picture-in-picture"
}
},
"save-window-position": "Salveaza pozitia ferestrei",
"save-window-size": "Salveaza marimea ferestrei",
"use-native-pip": "Foloseste PiP-ul nativ pentru broswer"
"save-window-position": "Salvează poziția ferestrei",
"save-window-size": "Salvează mărimea ferestrei",
"use-native-pip": "Folosește PiP-ul nativ pentru broswer"
},
"name": "Picture-in-picture",
"templates": {
@ -605,31 +624,31 @@
}
},
"playback-speed": {
"description": "Asculta rapid, asculta lent! Adauga un slider pentru viteza de redare a cantecului",
"description": "Ascultă rapid, ascultă lent! Adaugă un slider pentru viteza de redare a melodiei",
"name": "Viteza de redare",
"templates": {
"button": "Viteza"
"button": "Viteză"
}
},
"precise-volume": {
"description": "Controleaza volumul precis folosind rotita mouse-ului/scurtaturi din tastatura, cu un HUD personalizat si incremente de volum personalizate",
"description": "Controlează volumul precis folosind rotița mouse-ului/scurtăturii din tastatură, cu un HUD personalizat și incremente de volum personalizate",
"menu": {
"arrows-shortcuts": "Control cu tastele sageti locale",
"custom-volume-steps": "Seteaza incrementele de volum",
"global-shortcuts": "Scurtaturi de tastatura globale"
"arrows-shortcuts": "Control cu tastele-săgeți locale",
"custom-volume-steps": "Setează incrementele de volum",
"global-shortcuts": "Scurtături de tastatură globale"
},
"name": "Volum precis",
"prompt": {
"global-shortcuts": {
"keybind-options": {
"decrease": "Redu volumul audio",
"increase": "Creste volumul audio"
"increase": "Crește volumul audio"
},
"label": "Alege combinatiile de taste globale pentru volumul audio:",
"title": "Combinatii globale de taste pentru volum"
"label": "Alege combinațiile de taste globale pentru volumul audio:",
"title": "Combinații globale de taste pentru volum"
},
"volume-steps": {
"label": "Alege pasii de increment pentru volum audio",
"label": "Alege pașii de increment pentru volum audio",
"title": "Incremente de volum"
}
}
@ -638,28 +657,28 @@
"backend": {
"dialog": {
"quality-changer": {
"detail": "Calitate actuala: {{quality}}",
"message": "Alegeti calitatea video:",
"title": "Alegeti calitatea video"
"detail": "Calitate actuală: {{quality}}",
"message": "Alegeți calitatea video:",
"title": "Alegeți calitatea video"
}
}
},
"description": "Permite schimbarea calitatii video cu un buton prezent peste video",
"name": "Modificator de calitate video"
"description": "Permite schimbarea calității video cu un buton prezent peste video",
"name": "Schimbător de calitate video"
},
"scrobbler": {
"description": "Adauga asistenta pentru scrobbling (etc. last.fm, Listenbrainz)",
"description": "Adaugă asistenta pentru scrobbling (etc. last.fm, Listenbrainz)",
"dialog": {
"lastfm": {
"auth-failed": {
"message": "Autentificarea cu Last.fm a esuat\nAscunde acest pop-up pana la urmatoarea repornire.",
"title": "Autentificare Esuata"
"message": "Autentificarea cu Last.fm a eșuat\nAscunde acest pop-up până la următoarea repornire.",
"title": "Autentificare Eșuată"
}
}
},
"menu": {
"lastfm": {
"api-settings": "Setari pentru API-ul Last.fm"
"api-settings": "Setări pentru API-ul Last.fm"
},
"listenbrainz": {
"token": "Introdu token-ul de utilizator ListenBrainz"
@ -674,102 +693,105 @@
},
"listenbrainz": {
"token": {
"label": "Introdu token-ul tau de utilizator ListenBrainz:",
"label": "Introdu token-ul tău de utilizator ListenBrainz:",
"title": "Token-ul ListenBrainz"
}
}
}
},
"shortcuts": {
"description": "Permite setari globale pentru scurtaturi pe tastatura pentru playback (reda/pauza/urmatorul/anteriorul), pentru oprirea media OSD prin suprascriera tastelor media, pentru folosirea combinatiei Ctrl/CMD + F pentru a cauta, pentru asistenta Linux MPRIS pentru taste media si pentru scurtaturi perosnalizate pentru utilizatori avansati",
"description": "Permite setari globale pentru scurtături pe tastatură pentru redare (redă/pauză/următorul/anteriorul) și pentru oprirea media OSD prin suprascriera tastelor media, pentru folosirea combinației Ctrl/CMD + F pentru a căuta, pentru pornirea asistenței Linux MPRIS pentru taste media și pentru scurtături personalizate pentru utilizatori avansați",
"menu": {
"override-media-keys": "Suprascrie tastele media",
"set-keybinds": "Seteaza scurtaturile globale pentru cantece"
"set-keybinds": "Setează scurtăturile globale pentru melodii"
},
"name": "Scurtaturi (& MPRIS)",
"name": "Scurtături (& MPRIS)",
"prompt": {
"keybind": {
"keybind-options": {
"next": "Urmatorul",
"play-pause": "Reda / Pauza",
"next": "Următorul",
"play-pause": "Redă / Pauză",
"previous": "Anteriorul"
},
"label": "Alege combinatia de taste globala pentru controlul cantecelor:",
"title": "Scurtaturi pe tastatura globale"
"label": "Alege combinația de taste globală pentru controlul melodiilor:",
"title": "Scurtături pe tastatură globale"
}
}
},
"skip-disliked-songs": {
"description": "Sari peste cantecele disliked",
"name": "Treci peste cantecele disliked"
"description": "Sari peste melodiile neplăcute",
"name": "Treci peste melodiile neplăcute"
},
"skip-silences": {
"description": "Treci automat peste sectiunile de liniste din cantece",
"name": "Treci peste liniste"
"description": "Treci automat peste secțiunile de liniște din melodii",
"name": "Treci peste liniște"
},
"sponsorblock": {
"description": "Treci automat peste partile non-muzicale precum intro/outro sau parti din video-ul catecului, cand nu se aude cantecul",
"description": "Treci automat peste părțile non-muzicale precum intro/outro sau părți din video-ul melodiei, când nu se aude melodia",
"name": "SponsorBlock"
},
"synced-lyrics": {
"description": "Furnizeaza versuri sincronizate melodiilor, folosind furnizori precum LRClib.",
"description": "Furnizează versuri sincronizate melodiilor, folosind furnizori precum LRClib.",
"errors": {
"fetch": "⚠️ - A aparut o eroare in timpul incarcarii versurilor. Te rog incearca din nou mai tarziu.",
"not-found": "⚠️ - Nu au fost gasite versuri pentru aceasta melodie."
"fetch": "⚠️ - A apărut o eroare în timpul încărcării versurilor. \nTe rog încearcă din nou mai târziu.",
"not-found": "⚠️ Nu au fost găsite versuri pentru această melodie."
},
"menu": {
"default-text-string": {
"label": "Caracter implicit intre versuri",
"tooltip": "Alege caracterul implicit folosit pentru spatiul dintre versuri"
"label": "Caracter implicit între versuri",
"tooltip": "Alege caracterul implicit folosit pentru spațiul dintre versuri"
},
"line-effect": {
"label": "Efect de linie",
"submenu": {
"fancy": {
"tooltip": "Folosește efecte largi pe linia curentă"
},
"focus": {
"label": "Focalizare",
"tooltip": "Doar linia curenta este alba"
"tooltip": "Doar linia curentă este albă"
},
"offset": {
"label": "Offset",
"tooltip": "Deplasare la dreapta pentru linia curenta"
"label": "Deplasare",
"tooltip": "Deplasare la dreapta pentru linia curentă"
},
"scale": {
"label": "Marime",
"tooltip": "Schimba dimensiunea liniei curente"
"label": "Mărime",
"tooltip": "Schimbă dimensiunea liniei curente"
}
},
"tooltip": "Alege efectul aplicat liniei curente"
},
"precise-timing": {
"label": "Sincronizeaza versurile perfect",
"tooltip": "Calculeaza afisarea urmatoarei linii pana la milisecunda (poate afecta performanta)"
"label": "Sincronizează versurile perfect",
"tooltip": "Calculează afisarea următoarei linii până la milisecundă (poate afecta performanța)"
},
"show-lyrics-even-if-inexact": {
"label": "Afiseaza versurile chiar daca sunt inexacte",
"tooltip": "Daca melodia nu este gasita, plugin-ul incearca din nou cu o cautare diferita.\nRezultatul acestei incercari poate sa nu fie exact."
"label": "Afișează versurile chiar dacă sunt inexacte",
"tooltip": "Dacă melodia nu este găsită, plugin-ul încearcă din nou cu o căutare diferită.\nRezultatul acestei încercări poate să nu fie exact."
},
"show-time-codes": {
"label": "Afiseaza timecode-urile",
"tooltip": "Afiseaza codurile de timp langa versuri"
"label": "Afișează codurile de timp",
"tooltip": "Afișează codurile de timp lângă versuri"
}
},
"name": "Versuri Sincronizate",
"refetch-btn": {
"fetching": "Incarcare...",
"normal": "Reincarcare versuri"
"fetching": "Încărcare...",
"normal": "Reîncărcare versuri"
},
"warnings": {
"duration-mismatch": "⚠️ - Versurile pot fi desincronizate din cauza unei nepotriviri de duratie.",
"inexact": "⚠️ - Versurile pentru aceasta melodie pot fi inexacte",
"instrumental": "⚠️ - Aceasta melodie este instrumentala"
"duration-mismatch": "⚠️ - Versurile pot fi desincronizate din cauza unei nepotriviri de durație.",
"inexact": "⚠️ - Versurile pentru această melodie pot fi inexacte",
"instrumental": "⚠️ - Această melodie este instrumentală"
}
},
"taskbar-mediacontrol": {
"description": "Controleaza redarea din Bara de Activitati Windows",
"description": "Controlează redarea din Bara de Activități Windows",
"name": "Control media in Bara de Activitate"
},
"touchbar": {
"description": "Adauga un widget TouchBar pentru utilizatorii macOS",
"description": "Adaugă un widget TouchBar pentru utilizatorii macOS",
"name": "TouchBar"
},
"tuna-obs": {
@ -777,17 +799,17 @@
"name": "Tuna OBS"
},
"video-toggle": {
"description": "Adauga un buton ce schimba intre modurile Video/Cantec. se poate optional elimia complet optiunea video",
"description": "Adaugă un buton ce schimbă între modurile Video/Melodie. De asemenea se poate elimina opțional toată fila video",
"menu": {
"align": {
"label": "Aliniere",
"submenu": {
"left": "Stanga",
"left": "Stânga",
"middle": "Mijloc",
"right": "Dreapta"
}
},
"force-hide": "Forteaza eliminarea tab-ului video",
"force-hide": "Forțează eliminarea filei video",
"mode": {
"label": "Mod",
"submenu": {
@ -799,15 +821,15 @@
},
"name": "Comutator video",
"templates": {
"button": "Cantec"
"button": "Melodie"
}
},
"visualizer": {
"description": "Adauga un visualizer la player",
"description": "Adaugă un vizualizator la player",
"menu": {
"visualizer-type": "Tip de visualizer"
"visualizer-type": "Tip de vizualizator"
},
"name": "Visualizer"
"name": "Vizualizator"
}
}
}

View File

@ -279,6 +279,13 @@
},
"name": "Режим Ambient"
},
"amuse": {
"description": "Добавляет поддержку виджета Amuse „сейчас играет“ от 6K Labs",
"name": "Amuse",
"response": {
"query": "Сервер Amuse API запущен. GET /query чтобы получить информацию о треке."
}
},
"api-server": {
"description": "Добавляет API сервер для контроля за плеером",
"dialog": {
@ -726,8 +733,8 @@
"synced-lyrics": {
"description": "Предоставляет синхронизированные слова для песен из таких источников, как LRClib.",
"errors": {
"fetch": "⚠️ - Возникла ошибка во время получения слов. Повторите попытку позже.",
"not-found": "⚠️ - Для этой песни не найдено слов."
"fetch": "⚠️\tПроизошла ошибка во время получения слов.\n\tПовторите попытку позже.",
"not-found": "⚠️ Для этой песни не найдено слов."
},
"menu": {
"default-text-string": {
@ -737,6 +744,10 @@
"line-effect": {
"label": "Эффект строки",
"submenu": {
"fancy": {
"label": "Красивый",
"tooltip": "Использовать большие эффекты строки, как в приложении"
},
"focus": {
"label": "Фокусировка",
"tooltip": "Делает только текущую строку белой"

View File

@ -279,6 +279,13 @@
},
"name": "Ambiyans Modu"
},
"amuse": {
"description": "6K Labs'ın Amuse oynatma widget'ı için YouTube Music desteği ekler",
"name": "Amuse",
"response": {
"query": "Amuse API sunucusu çalışıyor. Şarkı bilgilerini almak için GET /query kullanabilirsiniz."
}
},
"api-server": {
"description": "APİ ekle ve oynatıcıyı kontrol et",
"dialog": {
@ -484,6 +491,18 @@
"button": "İndir"
}
},
"equalizer": {
"description": "Oynatıcıya ekolayzer desteği ekler",
"menu": {
"presets": {
"label": "Ön Ayarlar",
"list": {
"bass-booster": "Bass güçlendirici"
}
}
},
"name": "Ekolayzer"
},
"exponential-volume": {
"description": "Ses seviyesi kaydırıcısını üstel hale getirir, böylece daha düşük ses seviyelerini seçmek daha kolay olur.",
"name": "Üstel Ses Seviyesi"
@ -714,8 +733,8 @@
"synced-lyrics": {
"description": "LRClib gibi sağlayıcıları kullanarak şarkılara senkronize şarkı sözleri sağlar.",
"errors": {
"fetch": "⚠️ - Şarkı sözleri getirilirken bir hata oluştu. Lütfen daha sonra tekrar deneyin.",
"not-found": "⚠️ - Bu şarkı için şarkı sözü bulunamadı."
"fetch": "⚠️ \tŞarkı sözleri alınırken bir hata oluştu.\n\tLütfen daha sonra tekrar deneyin.",
"not-found": "⚠️ Bu şarkı için şarkı sözleri bulunamadı."
},
"menu": {
"default-text-string": {
@ -725,6 +744,10 @@
"line-effect": {
"label": "Çizgi etkisi",
"submenu": {
"fancy": {
"label": "Süslü",
"tooltip": "Mevcut satırda büyük, uygulama benzeri efektler kullan"
},
"focus": {
"label": "odak",
"tooltip": "Yalnızca geçerli satırı beyaz yapın"

View File

@ -279,6 +279,13 @@
},
"name": "Режим навколишнього середовища"
},
"amuse": {
"description": "Додає підтримку YouTube Music для віджета Amuse now playing від 6K Labs",
"name": "Amuse",
"response": {
"query": "Сервер Amuse API запущено. Запит GET /query для отримання інформації про пісню."
}
},
"api-server": {
"description": "Додає API сервер для контролю плеєра",
"dialog": {
@ -484,6 +491,18 @@
"button": "Завантажити"
}
},
"equalizer": {
"description": "Додає еквалайзер до програвача",
"menu": {
"presets": {
"label": "Пресети",
"list": {
"bass-booster": "Підсилювач басів"
}
}
},
"name": "Еквалайзер"
},
"exponential-volume": {
"description": "Робить регулятор гучності експоненціальним, що полегшує вибір тихих рівнів гучності.",
"name": "Експоненціальна гучність"
@ -714,8 +733,8 @@
"synced-lyrics": {
"description": "Додає синхронізовані тексти до пісень використовуючи провайдери, такі як LRClib.",
"errors": {
"fetch": "⚠️ - При завантаженні тексту сталась помилка. Спробуйте ще раз пізніше.",
"not-found": "⚠️ - До цієї пісні текст не знайдено."
"fetch": "⚠️ - При завантаженні слів пісні сталась помилка. Спробуйте пізніше.",
"not-found": "⚠️ До цієї пісні текст не знайдено."
},
"menu": {
"default-text-string": {
@ -725,6 +744,10 @@
"line-effect": {
"label": "Лінійний ефект",
"submenu": {
"fancy": {
"label": "Fancy",
"tooltip": "Використовуйте великі, додаткоподібні ефекти на поточному рядку"
},
"focus": {
"label": "Зосереджитись",
"tooltip": "Зробити білим лише поточний рядок"

View File

@ -279,6 +279,12 @@
},
"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": {
"description": "Thêm máy chủ API để điều khiển trình phát",
"dialog": {
@ -299,7 +305,7 @@
"label": "Xác thực ngay yêu cầu đầu tiên"
},
"none": {
"label": "Không/Chưa xác thực (Need context)"
"label": "Không xác thực"
}
}
},

View File

@ -279,6 +279,13 @@
},
"name": "沉浸模式"
},
"amuse": {
"description": "为 6K Labs 的 Amuse 正在播放小部件添加 YouTube Music 支持",
"name": "Amuse",
"response": {
"query": "Amuse API服务器已在运行。使用 /query 以获取歌曲信息。"
}
},
"api-server": {
"description": "添加一个 API 服务器来控制播放器",
"dialog": {

View File

@ -279,6 +279,13 @@
},
"name": "微光效果"
},
"amuse": {
"description": "加入支援 6K Labs 的 Amuse OBS 外掛以取得 Youtube Music 現正播放資訊",
"name": "Amuse",
"response": {
"query": "Amuse API 伺服器正在運行中,使用 /query 以取得歌曲資訊。"
}
},
"api-server": {
"description": "新增伺服器以使用 API 控制播放器",
"dialog": {
@ -726,7 +733,7 @@
"synced-lyrics": {
"description": "使用 LRClib 等管道提供歌詞同步顯示。",
"errors": {
"fetch": "⚠️擷取歌詞時發生錯誤請稍後再試。",
"fetch": "⚠️\t擷取歌詞時發生錯誤\n請稍後再試。",
"not-found": "⚠️未找到該首歌曲的歌詞。"
},
"menu": {
@ -737,6 +744,10 @@
"line-effect": {
"label": "歌詞顯示效果",
"submenu": {
"fancy": {
"label": "絢麗",
"tooltip": "使用較為接近原生樣式並且放大當前該行歌詞"
},
"focus": {
"label": "高亮",
"tooltip": "高亮當前的歌詞"

View File

@ -134,14 +134,6 @@ if (is.linux()) {
// Overrides WM_CLASS for X11 to correspond to icon filename
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
if (config.plugins.isEnabled('shortcuts')) {
app.commandLine.appendSwitch('disable-features', 'MediaSessionService');
@ -505,10 +497,11 @@ app.once('browser-window-created', (_event, win) => {
// User agents are from https://developers.whatismybrowser.com/useragents/explore/
const originalUserAgent = win.webContents.userAgent;
const userAgents = {
mac: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 12.1; rv:95.0) Gecko/20100101 Firefox/95.0',
mac: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.152 Safari/537.36',
windows:
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:95.0) Gecko/20100101 Firefox/95.0',
linux: 'Mozilla/5.0 (Linux x86_64; rv:95.0) Gecko/20100101 Firefox/95.0',
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.152 Safari/537.36',
linux:
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.152 Safari/537.36',
};
const updatedUserAgent = is.macOS()
@ -912,10 +905,12 @@ function removeContentSecurityPolicy(
delete details.responseHeaders['content-security-policy'];
delete details.responseHeaders['Content-Security-Policy'];
// Only allow cross-origin requests from music.youtube.com
delete details.responseHeaders['access-control-allow-origin'];
delete details.responseHeaders['Access-Control-Allow-Origin'];
details.responseHeaders['access-control-allow-origin'] = ['https://music.youtube.com'];
if (
!details.responseHeaders['access-control-allow-origin'] &&
!details.responseHeaders['Access-Control-Allow-Origin']
) {
details.responseHeaders['access-control-allow-origin'] = ['https://music.youtube.com'];
}
}
callback({ cancel: false, responseHeaders: details.responseHeaders });

View File

@ -68,7 +68,7 @@ export const mainMenuTemplate = async (
win: BrowserWindow,
): Promise<MenuTemplate> => {
const innerRefreshMenu = () => refreshMenu(win);
const { navigationHistory } = win.webContents;
await loadAllMenuPlugins(win);
const menuResult = Object.entries(getAllMenuTemplate()).map(
@ -610,16 +610,16 @@ export const mainMenuTemplate = async (
{
label: t('main.menu.navigation.submenu.go-back'),
click() {
if (win.webContents.canGoBack()) {
win.webContents.goBack();
if (navigationHistory.canGoBack()) {
navigationHistory.goBack();
}
},
},
{
label: t('main.menu.navigation.submenu.go-forward'),
click() {
if (win.webContents.canGoForward()) {
win.webContents.goForward();
if (navigationHistory.canGoForward()) {
navigationHistory.goForward();
}
},
},

View File

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

View File

@ -24,15 +24,22 @@ export const backend = createBackend<BackendType, APIServerConfig>({
this.songInfo = songInfo;
});
ctx.ipc.on('ytmd:player-api-loaded', () =>
ctx.ipc.send('ytmd:setup-time-changed-listener'),
);
ctx.ipc.on('ytmd:player-api-loaded', () => {
ctx.ipc.send('ytmd:setup-time-changed-listener');
ctx.ipc.send('ytmd:setup-repeat-changed-listener');
ctx.ipc.send('ytmd:setup-volume-changed-listener');
});
ctx.ipc.on(
'ytmd:repeat-changed',
(mode: RepeatMode) => (this.currentRepeatMode = mode),
);
ctx.ipc.on(
'ytmd:volume-changed',
(newVolume: number) => (this.volume = newVolume),
);
this.run(config.hostname, config.port);
},
stop() {
@ -59,6 +66,12 @@ export const backend = createBackend<BackendType, APIServerConfig>({
this.app.use('*', cors());
// for web remote control
this.app.use('*', async (ctx, next) => {
ctx.header('Access-Control-Request-Private-Network', 'true');
await next();
});
// middlewares
this.app.use('/api/*', async (ctx, next) => {
if (config.authStrategy !== AuthStrategy.NONE) {
@ -88,6 +101,7 @@ export const backend = createBackend<BackendType, APIServerConfig>({
ctx,
() => this.songInfo,
() => this.currentRepeatMode,
() => this.volume,
);
registerAuth(this.app, ctx);

View File

@ -5,15 +5,19 @@ import { ipcMain } from 'electron';
import getSongControls from '@/providers/song-controls';
import {
AuthHeadersSchema,
type ResponseSongInfo,
SongInfoSchema,
SeekSchema,
GoForwardScheme,
AddSongToQueueSchema,
GoBackSchema,
SwitchRepeatSchema,
SetVolumeSchema,
GoForwardScheme,
MoveSongInQueueSchema,
QueueParamsSchema,
SearchSchema,
SeekSchema,
SetFullscreenSchema,
SetQueueIndexSchema,
SetVolumeSchema,
SongInfoSchema,
SwitchRepeatSchema,
type ResponseSongInfo,
} from '../scheme';
import type { RepeatMode } from '@/types/datahost-get-state';
@ -22,6 +26,7 @@ import type { BackendContext } from '@/types/contexts';
import type { APIServerConfig } from '../../config';
import type { HonoApp } from '../types';
import type { QueueResponse } from '@/types/youtube-music-desktop-internal';
import type { Context } from 'hono';
const API_VERSION = 'v1';
@ -110,7 +115,6 @@ const routes = {
summary: 'seek',
description: 'Seek to a specific time in the current song',
request: {
headers: AuthHeadersSchema,
body: {
description: 'seconds to seek to',
content: {
@ -132,7 +136,6 @@ const routes = {
summary: 'go back',
description: 'Move the current song back by a number of seconds',
request: {
headers: AuthHeadersSchema,
body: {
description: 'seconds to go back',
content: {
@ -155,7 +158,6 @@ const routes = {
summary: 'go forward',
description: 'Move the current song forward by a number of seconds',
request: {
headers: AuthHeadersSchema,
body: {
description: 'seconds to go forward',
content: {
@ -171,7 +173,24 @@ const routes = {
},
},
}),
getShuffleState: createRoute({
method: 'get',
path: `/api/${API_VERSION}/shuffle`,
summary: 'get shuffle state',
description: 'Get the current shuffle state',
responses: {
200: {
description: 'Success',
content: {
'application/json': {
schema: z.object({
state: z.boolean().nullable(),
}),
},
},
},
},
}),
shuffle: createRoute({
method: 'post',
path: `/api/${API_VERSION}/shuffle`,
@ -207,7 +226,6 @@ const routes = {
summary: 'switch repeat',
description: 'Switch the repeat mode',
request: {
headers: AuthHeadersSchema,
body: {
description: 'number of times to click the repeat button',
content: {
@ -229,7 +247,6 @@ const routes = {
summary: 'set volume',
description: 'Set the volume of the player',
request: {
headers: AuthHeadersSchema,
body: {
description: 'volume to set',
content: {
@ -245,13 +262,30 @@ const routes = {
},
},
}),
getVolumeState: createRoute({
method: 'get',
path: `/api/${API_VERSION}/volume`,
summary: 'get volume state',
description: 'Get the current volume state of the player',
responses: {
200: {
description: 'Success',
content: {
'application/json': {
schema: z.object({
state: z.number(),
}),
},
},
},
},
}),
setFullscreen: createRoute({
method: 'post',
path: `/api/${API_VERSION}/fullscreen`,
summary: 'set fullscreen',
description: 'Set the fullscreen state of the player',
request: {
headers: AuthHeadersSchema,
body: {
description: 'fullscreen state',
content: {
@ -297,7 +331,8 @@ const routes = {
},
},
}),
queueInfo: createRoute({
oldQueueInfo: createRoute({
deprecated: true,
method: 'get',
path: `/api/${API_VERSION}/queue-info`,
summary: 'get current queue info',
@ -316,7 +351,8 @@ const routes = {
},
},
}),
songInfo: createRoute({
oldSongInfo: createRoute({
deprecated: true,
method: 'get',
path: `/api/${API_VERSION}/song-info`,
summary: 'get current song info',
@ -335,6 +371,159 @@ const routes = {
},
},
}),
songInfo: createRoute({
method: 'get',
path: `/api/${API_VERSION}/song`,
summary: 'get current song info',
description: 'Get the current song info',
responses: {
200: {
description: 'Success',
content: {
'application/json': {
schema: SongInfoSchema,
},
},
},
204: {
description: 'No song info',
},
},
}),
queueInfo: createRoute({
method: 'get',
path: `/api/${API_VERSION}/queue`,
summary: 'get current queue info',
description: 'Get the current queue info',
responses: {
200: {
description: 'Success',
content: {
'application/json': {
schema: z.object({}),
},
},
},
204: {
description: 'No queue info',
},
},
}),
addSongToQueue: createRoute({
method: 'post',
path: `/api/${API_VERSION}/queue`,
summary: 'add song to queue',
description: 'Add a song to the queue',
request: {
body: {
description: 'video id of the song to add',
content: {
'application/json': {
schema: AddSongToQueueSchema,
},
},
},
},
responses: {
204: {
description: 'Success',
},
},
}),
moveSongInQueue: createRoute({
method: 'patch',
path: `/api/${API_VERSION}/queue/{index}`,
summary: 'move song in queue',
description: 'Move a song in the queue',
request: {
params: QueueParamsSchema,
body: {
description: 'index to move the song to',
content: {
'application/json': {
schema: MoveSongInQueueSchema,
},
},
},
},
responses: {
204: {
description: 'Success',
},
},
}),
removeSongFromQueue: createRoute({
method: 'delete',
path: `/api/${API_VERSION}/queue/{index}`,
summary: 'remove song from queue',
description: 'Remove a song from the queue',
request: {
params: QueueParamsSchema,
},
responses: {
204: {
description: 'Success',
},
},
}),
setQueueIndex: createRoute({
method: 'patch',
path: `/api/${API_VERSION}/queue`,
summary: 'set queue index',
description: 'Set the current index of the queue',
request: {
body: {
description: 'index to move the song to',
content: {
'application/json': {
schema: SetQueueIndexSchema,
},
},
},
},
responses: {
204: {
description: 'Success',
},
},
}),
clearQueue: createRoute({
method: 'delete',
path: `/api/${API_VERSION}/queue`,
summary: 'clear queue',
description: 'Clear the queue',
responses: {
204: {
description: 'Success',
},
},
}),
search: createRoute({
method: 'post',
path: `/api/${API_VERSION}/search`,
summary: 'search for a song',
description: 'search for a song',
request: {
body: {
description: 'search query',
content: {
'application/json': {
schema: SearchSchema,
},
},
},
},
responses: {
200: {
description: 'Success',
content: {
'application/json': {
schema: z.object({}),
},
},
},
},
}),
};
export const register = (
@ -342,6 +531,7 @@ export const register = (
{ window }: BackendContext<APIServerConfig>,
songInfoGetter: () => SongInfo | undefined,
repeatModeGetter: () => RepeatMode | undefined,
volumeGetter: () => number | undefined,
) => {
const controller = getSongControls(window);
@ -408,6 +598,25 @@ export const register = (
ctx.status(204);
return ctx.body(null);
});
app.openapi(routes.getShuffleState, async (ctx) => {
const stateResponsePromise = new Promise<boolean>((resolve) => {
ipcMain.once(
'ytmd:get-shuffle-response',
(_, isShuffled: boolean | undefined) => {
return resolve(!!isShuffled);
},
);
controller.requestShuffleInformation();
});
const isShuffled = await stateResponsePromise;
ctx.status(200);
return ctx.json({ state: isShuffled });
});
app.openapi(routes.shuffle, (ctx) => {
controller.shuffle();
@ -433,6 +642,10 @@ export const register = (
ctx.status(204);
return ctx.body(null);
});
app.openapi(routes.getVolumeState, (ctx) => {
ctx.status(200);
return ctx.json({ state: volumeGetter() ?? 0 });
});
app.openapi(routes.setFullscreen, (ctx) => {
const { state } = ctx.req.valid('json');
controller.setFullscreen(state);
@ -464,7 +677,26 @@ export const register = (
ctx.status(200);
return ctx.json({ state: fullscreen });
});
app.openapi(routes.queueInfo, async (ctx) => {
const songInfo = (ctx: Context) => {
const info = songInfoGetter();
if (!info) {
ctx.status(204);
return ctx.body(null);
}
const body = { ...info };
delete body.image;
ctx.status(200);
return ctx.json(body satisfies ResponseSongInfo);
};
app.openapi(routes.oldSongInfo, songInfo);
app.openapi(routes.songInfo, songInfo);
// Queue
const queueInfo = async (ctx: Context) => {
const queueResponsePromise = new Promise<QueueResponse>((resolve) => {
ipcMain.once('ytmd:get-queue-response', (_, queue: QueueResponse) => {
return resolve(queue);
@ -482,19 +714,50 @@ export const register = (
ctx.status(200);
return ctx.json(info);
};
app.openapi(routes.oldQueueInfo, queueInfo);
app.openapi(routes.queueInfo, queueInfo);
app.openapi(routes.addSongToQueue, (ctx) => {
const { videoId, insertPosition } = ctx.req.valid('json');
controller.addSongToQueue(videoId, insertPosition);
ctx.status(204);
return ctx.body(null);
});
app.openapi(routes.songInfo, (ctx) => {
const info = songInfoGetter();
app.openapi(routes.moveSongInQueue, (ctx) => {
const index = Number(ctx.req.param('index'));
const { toIndex } = ctx.req.valid('json');
controller.moveSongInQueue(index, toIndex);
if (!info) {
ctx.status(204);
return ctx.body(null);
}
ctx.status(204);
return ctx.body(null);
});
app.openapi(routes.removeSongFromQueue, (ctx) => {
const index = Number(ctx.req.param('index'));
controller.removeSongFromQueue(index);
const body = { ...info };
delete body.image;
ctx.status(204);
return ctx.body(null);
});
app.openapi(routes.setQueueIndex, (ctx) => {
const { index } = ctx.req.valid('json');
controller.setQueueIndex(index);
ctx.status(204);
return ctx.body(null);
});
app.openapi(routes.clearQueue, (ctx) => {
controller.clearQueue();
ctx.status(204);
return ctx.body(null);
});
app.openapi(routes.search, async (ctx) => {
const { query } = ctx.req.valid('json');
const response = await controller.search(query);
ctx.status(200);
return ctx.json(body satisfies ResponseSongInfo);
return ctx.json(response as object);
});
};

View File

@ -1,11 +1,5 @@
import { z } from '@hono/zod-openapi';
export const AuthHeadersSchema = z.object({
authorization: z.string().openapi({
example: 'Bearer token',
}),
});
export type JWTPayload = z.infer<typeof JWTPayloadSchema>;
export const JWTPayloadSchema = z.object({
id: z.string(),

View File

@ -6,3 +6,5 @@ export * from './go-forward';
export * from './switch-repeat';
export * from './set-volume';
export * from './set-fullscreen';
export * from './queue';
export * from './search';

View File

@ -0,0 +1,19 @@
import { z } from '@hono/zod-openapi';
export const QueueParamsSchema = z.object({
index: z.coerce.number().int().nonnegative(),
});
export const AddSongToQueueSchema = z.object({
videoId: z.string(),
insertPosition: z
.enum(['INSERT_AT_END', 'INSERT_AFTER_CURRENT_VIDEO'])
.optional()
.default('INSERT_AT_END'),
});
export const MoveSongInQueueSchema = z.object({
toIndex: z.number(),
});
export const SetQueueIndexSchema = z.object({
index: z.number().int().nonnegative(),
});

View File

@ -0,0 +1,5 @@
import { z } from '@hono/zod-openapi';
export const SearchSchema = z.object({
query: z.string(),
});

View File

@ -13,6 +13,7 @@ export type BackendType = {
oldConfig?: APIServerConfig;
songInfo?: SongInfo;
currentRepeatMode?: RepeatMode;
volume?: number;
init: (ctx: BackendContext<APIServerConfig>) => Promise<void>;
run: (hostname: string, port: number) => void;

View File

@ -154,15 +154,14 @@ export const backend = createBackend<
// @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
const hangulFillerUnicodeCharacter = '\u3164'; // This is an empty character
if (songInfo.title.length < 2) {
songInfo.title += hangulFillerUnicodeCharacter.repeat(
2 - songInfo.title.length,
);
}
if (songInfo.artist.length < 2) {
songInfo.artist += hangulFillerUnicodeCharacter.repeat(
2 - songInfo.title.length,
);
const paddedInfoKeys: (keyof SongInfo)[] = ['title', 'artist', 'album'];
for (const key of paddedInfoKeys) {
const keyLength = (songInfo[key] as string)?.length;
if (keyLength < 2) {
(songInfo[key] as string) += hangulFillerUnicodeCharacter.repeat(
2 - keyLength,
);
}
}
// see https://github.com/th-ch/youtube-music/issues/1664

View File

@ -3,26 +3,22 @@ import { join } from 'node:path';
import { randomBytes } from 'node:crypto';
import { app, BrowserWindow, dialog, ipcMain } from 'electron';
import {
ClientType,
Innertube,
UniversalCache,
Utils,
YTNodes,
} from 'youtubei.js';
import { Innertube, UniversalCache, Utils, YTNodes } from 'youtubei.js';
import is from 'electron-is';
import filenamify from 'filenamify';
import { Mutex } from 'async-mutex';
import { createFFmpeg } from '@ffmpeg.wasm/main';
import NodeID3, { TagConstants } from 'node-id3';
import { Window } from 'happy-dom';
import { BG, type BgConfig } from 'bgutils-js';
import {
cropMaxWidth,
getFolder,
sendFeedback as sendFeedback_,
setBadge,
} from './utils';
import { fetchFromGenius } from '@/plugins/lyrics-genius/main';
import { isEnabled } from '@/config/plugins';
import registerCallback, {
@ -33,21 +29,17 @@ import registerCallback, {
SongInfoEvent,
} from '@/providers/song-info';
import { getNetFetchAsFetch } from '@/plugins/utils/main';
import { t } from '@/i18n';
import { DefaultPresetList, type Preset, YoutubeFormatList } from '../types';
import type { DownloaderPluginConfig } from '../index';
import type { BackendContext } from '@/types/contexts';
import type { FormatOptions } from 'youtubei.js/dist/src/types/FormatUtils';
import type PlayerErrorMessage from 'youtubei.js/dist/src/parser/classes/PlayerErrorMessage';
import type { Playlist } from 'youtubei.js/dist/src/parser/ytmusic';
import type { VideoInfo } from 'youtubei.js/dist/src/parser/youtube';
import type TrackInfo from 'youtubei.js/dist/src/parser/ytmusic/TrackInfo';
import type { GetPlayerResponse } from '@/types/get-player-response';
type CustomSongInfo = SongInfo & { trackId?: string };
@ -63,6 +55,21 @@ let yt: Innertube;
let win: BrowserWindow;
let playingUrl: string;
const isYouTubeMusicPremium = async () => {
const upgradeBtnIconPathData = (await win.webContents.executeJavaScript(
'document.querySelector(\'iron-iconset-svg[name="yt-sys-icons"] #youtube_music_monochrome\')?.firstChild?.getAttribute("d")?.substring(0, 15)',
)) 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) => {
win.setProgressBar(-1); // Close progress bar
setBadge(0); // Close badge
@ -114,6 +121,66 @@ export const onMainLoad = async ({
generate_session_locally: true,
fetch: getNetFetchAsFetch(),
});
const requestKey = 'O43z0dpjhgX20SCx4KAo';
const visitorData = yt.session.context.client.visitorData;
if (visitorData) {
const cleanUp = (context: Partial<typeof globalThis>) => {
delete context.window;
delete context.document;
};
try {
const [width, height] = win.getSize();
// emulate jsdom using linkedom
const window = new Window({
width,
height,
console,
});
const document = window.document;
Object.assign(globalThis, {
window,
document,
});
const bgConfig: BgConfig = {
fetch: getNetFetchAsFetch(),
globalObj: globalThis,
identifier: visitorData,
requestKey,
};
const bgChallenge = await BG.Challenge.create(bgConfig);
const interpreterJavascript =
bgChallenge?.interpreterJavascript
.privateDoNotAccessOrElseSafeScriptWrappedValue;
if (interpreterJavascript) {
// This is a workaround to run the interpreterJavascript code
// Maybe there is a better way to do this (e.g. https://github.com/Siubaak/sval ?)
// eslint-disable-next-line @typescript-eslint/no-implied-eval,@typescript-eslint/no-unsafe-call
new Function(interpreterJavascript)();
const poTokenResult = await BG.PoToken.generate({
program: bgChallenge.program,
globalName: bgChallenge.globalName,
bgConfig,
}).finally(() => {
cleanUp(globalThis);
});
yt.session.po_token = poTokenResult.poToken;
} else {
cleanUp(globalThis);
}
} catch {
cleanUp(globalThis);
}
}
ipc.handle('download-song', (url: string) => downloadSong(url));
ipc.on('ytmd:video-src-changed', (data: GetPlayerResponse) => {
playingUrl = data.microformat.microformatDataRenderer.urlCanonical;
@ -313,7 +380,7 @@ async function downloadSongUnsafe(
}
const downloadOptions: FormatOptions = {
type: '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.
format: 'any', // Media container format
};
@ -585,20 +652,17 @@ export async function downloadPlaylist(givenUrl?: string | URL) {
return;
}
if (
!playlist ||
!playlist.items ||
playlist.items.length === 0 ||
!playlist.header ||
!('title' in playlist.header)
) {
if (!playlist || !playlist.items || playlist.items.length === 0) {
sendError(
new Error(t('plugins.downloader.backend.feedback.playlist-is-empty')),
);
return;
}
const normalPlaylistTitle = playlist.header?.title?.text;
const normalPlaylistTitle =
playlist.header && 'title' in playlist.header
? playlist.header?.title?.text
: undefined;
const playlistTitle =
normalPlaylistTitle ??
playlist.page.contents_memo
@ -774,13 +838,7 @@ const getMetadata = (info: TrackInfo): CustomSongInfo => ({
// This is used to bypass age restrictions
const getAndroidTvInfo = async (id: string): Promise<VideoInfo> => {
const innertube = await Innertube.create({
client_type: ClientType.TV_EMBEDDED,
generate_session_locally: true,
retrieve_player: true,
fetch: getNetFetchAsFetch(),
});
// GetInfo 404s with the bypass, so we use getBasicInfo instead
// that's fine as we only need the streaming data
return await innertube.getBasicInfo(id, 'TV_EMBEDDED');
return await yt.getBasicInfo(id, 'TV_EMBEDDED');
};

View File

@ -612,7 +612,9 @@ export default createPlugin<
const accountData = renderer.data as RawAccountData;
this.me = {
handleId: accountData.channelHandle.runs[0].text,
handleId:
accountData.channelHandle.runs[0].text ??
accountData.accountName.runs[0].text,
name: accountData.accountName.runs[0].text,
thumbnail: accountData.accountPhoto.thumbnails[0].url,
};

View File

@ -6,7 +6,7 @@ import { t } from '@/i18n';
import type { ConnectionEventUnion } from '@/plugins/music-together/connection';
import type { Profile, VideoData } from '../types';
import type { QueueItem } from '@/types/datahost-get-state';
import type { QueueElement } from '@/types/queue';
import type { QueueElement, Store } from '@/types/queue';
const getHeaderPayload = (() => {
let payload: {
@ -266,7 +266,8 @@ export class Queue {
}
if (this.originalDispatch)
this.queue.queue.store.store.dispatch = this.originalDispatch;
this.queue.queue.store.store.dispatch = this
.originalDispatch as Store['dispatch'];
}
injection() {
@ -295,7 +296,11 @@ export class Queue {
videoId: it!.videoId,
ownerId: this.owner!.id,
}) satisfies VideoData,
event.payload!.items!,
(
event.payload! as {
items: QueueItem[];
}
).items,
);
const index = this._videoList.length + videoList.length - 1;
@ -334,7 +339,11 @@ export class Queue {
videoId: it!.videoId,
ownerId: this.owner!.id,
}) satisfies VideoData,
event.payload!.items!,
(
event.payload! as {
items: QueueItem[];
}
).items,
),
},
});
@ -407,7 +416,13 @@ export class Queue {
},
},
};
this.originalDispatch?.call(fakeContext, event);
this.originalDispatch?.call(
fakeContext,
event as {
type: string;
payload?: { items?: QueueItem[] | undefined } | undefined;
},
);
};
}

View File

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

View File

@ -1,7 +1,7 @@
declare module '@jellybrick/mpris-service' {
import { EventEmitter } from 'events';
import { interface as dbusInterface } from 'dbus-next';
import { interface as dbusInterface } from '@jellybrick/dbus-next';
interface RootInterfaceOptions {
identity?: string;
@ -86,7 +86,7 @@ declare module '@jellybrick/mpris-service' {
supportedMimeTypes: string[];
canQuit: boolean;
canRaise: boolean;
canSetFullscreen?: boolean;
canUsePlayerControls?: boolean;
desktopEntry?: string;
hasTrackList: boolean;

View File

@ -77,7 +77,7 @@ function setupMPRIS() {
instance.canRaise = true;
instance.canQuit = false;
instance.canSetFullscreen = true;
instance.canUsePlayerControls = true;
instance.supportedUriSchemes = ['http', 'https'];
instance.desktopEntry = 'youtube-music';
return instance;
@ -93,6 +93,7 @@ function registerMPRIS(win: BrowserWindow) {
shuffle,
switchRepeat,
setFullscreen,
requestShuffleInformation,
requestFullscreenInformation,
requestQueueInformation,
} = songControls;
@ -126,8 +127,10 @@ function registerMPRIS(win: BrowserWindow) {
win.webContents.send('ytmd:setup-time-changed-listener', 'mpris');
win.webContents.send('ytmd:setup-repeat-changed-listener', 'mpris');
win.webContents.send('ytmd:setup-volume-changed-listener', 'mpris');
win.webContents.send('ytmd:setup-shuffle-changed-listener', 'mpris');
win.webContents.send('ytmd:setup-fullscreen-changed-listener', 'mpris');
win.webContents.send('ytmd:setup-autoplay-changed-listener', 'mpris');
requestShuffleInformation();
requestFullscreenInformation();
requestQueueInformation();
});
@ -156,8 +159,16 @@ function registerMPRIS(win: BrowserWindow) {
requestQueueInformation();
});
ipcMain.on('ytmd:shuffle-changed', (_, shuffleEnabled: boolean) => {
if (player.shuffle === undefined || !player.canUsePlayerControls) {
return;
}
player.shuffle = shuffleEnabled ?? !player.shuffle;
});
ipcMain.on('ytmd:fullscreen-changed', (_, changedTo: boolean) => {
if (player.fullscreen === undefined || !player.canSetFullscreen) {
if (player.fullscreen === undefined || !player.canUsePlayerControls) {
return;
}
@ -168,7 +179,7 @@ function registerMPRIS(win: BrowserWindow) {
ipcMain.on(
'ytmd:set-fullscreen',
(_, isFullscreen: boolean | undefined) => {
if (!player.canSetFullscreen || isFullscreen === undefined) {
if (!player.canUsePlayerControls || isFullscreen === undefined) {
return;
}
@ -179,7 +190,7 @@ function registerMPRIS(win: BrowserWindow) {
ipcMain.on(
'ytmd:fullscreen-changed-supported',
(_, isFullscreenSupported: boolean) => {
player.canSetFullscreen = isFullscreenSupported;
player.canUsePlayerControls = isFullscreenSupported;
},
);
ipcMain.on('ytmd:autoplay-changed', (_) => {
@ -272,6 +283,12 @@ function registerMPRIS(win: BrowserWindow) {
player.on('position', seekTo);
player.on('shuffle', (enableShuffle) => {
if (!player.canUsePlayerControls || enableShuffle === undefined) {
return;
}
player.shuffle = enableShuffle;
if (enableShuffle) {
shuffle();
requestQueueInformation();

View File

@ -1,4 +1,5 @@
import type { LyricProvider, LyricResult, SearchSongInfo } from '../types';
import type { YouTubeMusicAppElement } from '@/types/youtube-music-app-element';
const headers = {
'Accept': 'application/json',
@ -102,19 +103,17 @@ export class YTMusic implements LyricProvider {
.padStart(2, '0')}.${remaining.toString().padStart(2, '0')}`;
}
private ENDPOINT = 'https://youtubei.googleapis.com/youtubei/v1/';
// RATE LIMITED (2 req per sec)
private PROXIED_ENDPOINT = 'https://ytmbrowseproxy.zvz.be/';
private fetchNext(videoId: string) {
return fetch(this.ENDPOINT + 'next?prettyPrint=false', {
headers,
method: 'POST',
body: JSON.stringify({
videoId,
context: { client },
}),
}).then((res) => res.json()) as Promise<NextData>;
const app = document.querySelector<YouTubeMusicAppElement>('ytmusic-app');
if (!app) return null;
return app.networkManager.fetch('/next?prettyPrint=false', {
videoId,
}) as Promise<NextData>;
}
private fetchBrowse(browseId: string) {

View File

@ -50,7 +50,7 @@ export const SyncedLine = ({ line }: SyncedLineProps) => {
_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
text={{
runs: [{ text: config()?.showTimeCodes ? `[${line.time}] ` : '' }],

View File

@ -38,7 +38,10 @@ createEffect(() => {
root.style.setProperty('--lyrics-active-offset', '0');
break;
case 'scale':
root.style.setProperty('--lyrics-font-size', '1.4rem');
root.style.setProperty(
'--lyrics-font-size',
'clamp(1.4rem, 1.1vmax, 3rem)',
);
root.style.setProperty(
'--lyrics-line-height',
'var(--ytmusic-body-line-height)',
@ -58,7 +61,10 @@ createEffect(() => {
root.style.setProperty('--lyrics-active-offset', '0');
break;
case 'offset':
root.style.setProperty('--lyrics-font-size', '1.4rem');
root.style.setProperty(
'--lyrics-font-size',
'clamp(1.4rem, 1.1vmax, 3rem)',
);
root.style.setProperty(
'--lyrics-line-height',
'var(--ytmusic-body-line-height)',
@ -78,7 +84,10 @@ createEffect(() => {
root.style.setProperty('--lyrics-active-offset', '5%');
break;
case 'focus':
root.style.setProperty('--lyrics-font-size', '1.4rem');
root.style.setProperty(
'--lyrics-font-size',
'clamp(1.4rem, 1.1vmax, 3rem)',
);
root.style.setProperty(
'--lyrics-line-height',
'var(--ytmusic-body-line-height)',

View File

@ -17,7 +17,7 @@
/* Typography */
--lyrics-font-family: Satoshi, Avenir, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, Ubuntu, Cantarell,
Open Sans, Helvetica Neue, sans-serif;
--lyrics-font-size: 1.4rem;
--lyrics-font-size: clamp(1.4rem, 1.1vmax, 3rem);
--lyrics-line-height: var(--ytmusic-body-line-height);
--lyrics-width: 100%;
@ -190,7 +190,7 @@
position: sticky;
top: var(--top, 0);
z-index: 100;
background-color: var(--ytmusic-background);
backdrop-filter: blur(5px);
transition: top 325ms ease-in-out;
}

View File

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

View File

@ -1,5 +1,5 @@
// This is used for to control the songs
import { BrowserWindow } from 'electron';
import { BrowserWindow, ipcMain } from 'electron';
// see protocol-handler.ts
type ArgsType<T> = T | string[] | undefined;
@ -24,6 +24,16 @@ const parseBooleanFromArgsType = (args: ArgsType<boolean>) => {
}
};
const parseStringFromArgsType = (args: ArgsType<string>) => {
if (typeof args === 'string') {
return args;
} else if (Array.isArray(args)) {
return args[0];
} else {
return null;
}
};
export default (win: BrowserWindow) => {
return {
// Playback
@ -52,6 +62,9 @@ export default (win: BrowserWindow) => {
win.webContents.send('ytmd:seek-by', seconds);
}
},
requestShuffleInformation: () => {
win.webContents.send('ytmd:get-shuffle');
},
shuffle: () => win.webContents.send('ytmd:shuffle'),
switchRepeat: (n: ArgsType<number> = 1) => {
const repeat = parseNumberFromArgsType(n);
@ -80,11 +93,53 @@ export default (win: BrowserWindow) => {
win.webContents.send('ytmd:get-queue');
},
muteUnmute: () => win.webContents.send('ytmd:toggle-mute'),
search: () => {
openSearchBox: () => {
win.webContents.sendInputEvent({
type: 'keyDown',
keyCode: '/',
});
},
// Queue
addSongToQueue: (videoId: string, queueInsertPosition: string) => {
const videoIdValue = parseStringFromArgsType(videoId);
if (videoIdValue === null) return;
win.webContents.send(
'ytmd:add-to-queue',
videoIdValue,
queueInsertPosition,
);
},
moveSongInQueue: (
fromIndex: ArgsType<number>,
toIndex: ArgsType<number>,
) => {
const fromIndexValue = parseNumberFromArgsType(fromIndex);
const toIndexValue = parseNumberFromArgsType(toIndex);
if (fromIndexValue === null || toIndexValue === null) return;
win.webContents.send('ytmd:move-in-queue', fromIndexValue, toIndexValue);
},
removeSongFromQueue: (index: ArgsType<number>) => {
const indexValue = parseNumberFromArgsType(index);
if (indexValue === null) return;
win.webContents.send('ytmd:remove-from-queue', indexValue);
},
setQueueIndex: (index: ArgsType<number>) => {
const indexValue = parseNumberFromArgsType(index);
if (indexValue === null) return;
win.webContents.send('ytmd:set-queue-index', indexValue);
},
clearQueue: () => win.webContents.send('ytmd:clear-queue'),
search: (query: string) =>
new Promise((resolve) => {
ipcMain.once('ytmd:search-results', (_, result) => {
resolve(result as string);
});
win.webContents.send('ytmd:search', query);
}),
};
};

View File

@ -87,6 +87,28 @@ export const setupVolumeChangedListener = singleton((api: YoutubePlayer) => {
window.ipcRenderer.send('ytmd:volume-changed', api.getVolume());
});
export const setupShuffleChangedListener = singleton(() => {
const playerBar = document.querySelector('ytmusic-player-bar');
if (!playerBar) {
window.ipcRenderer.send('ytmd:shuffle-changed-supported', false);
return;
}
const observer = new MutationObserver(() => {
window.ipcRenderer.send(
'ytmd:shuffle-changed',
(playerBar?.attributes.getNamedItem('shuffle-on') ?? null) !== null,
);
});
observer.observe(playerBar, {
attributes: true,
childList: false,
subtree: false,
});
});
export const setupFullScreenChangedListener = singleton(() => {
const playerBar = document.querySelector('ytmusic-player-bar');
@ -139,6 +161,10 @@ export default (api: YoutubePlayer) => {
setupVolumeChangedListener(api);
});
window.ipcRenderer.on('ytmd:setup-shuffle-changed-listener', () => {
setupShuffleChangedListener();
});
window.ipcRenderer.on('ytmd:setup-fullscreen-changed-listener', () => {
setupFullScreenChangedListener();
});

View File

@ -22,6 +22,8 @@ import type { PluginConfig } from '@/types/plugins';
import type { YoutubePlayer } from '@/types/youtube-player';
import type { QueueElement } from '@/types/queue';
import type { QueueResponse } from '@/types/youtube-music-desktop-internal';
import type { YouTubeMusicAppElement } from '@/types/youtube-music-app-element';
import type { SearchBoxElement } from '@/types/search-box-element';
let api: (Element & YoutubePlayer) | null = null;
let isPluginLoaded = false;
@ -41,10 +43,6 @@ async function listenForApiLoad() {
}
}
interface YouTubeMusicAppElement extends HTMLElement {
navigate(page: string): void;
}
async function onApiLoaded() {
// Workaround for #2459
document
@ -82,6 +80,20 @@ async function onApiLoaded() {
>('ytmusic-player-bar')
?.queue.shuffle();
});
const isShuffled = () => {
const isShuffled =
document
.querySelector<HTMLElement>('ytmusic-player-bar')
?.attributes.getNamedItem('shuffle-on') ?? null;
return isShuffled !== null;
};
window.ipcRenderer.on('ytmd:get-shuffle', () => {
window.ipcRenderer.send('ytmd:get-shuffle-response', isShuffled());
});
window.ipcRenderer.on(
'ytmd:update-like',
(_, status: 'LIKE' | 'DISLIKE' = 'LIKE') => {
@ -145,9 +157,9 @@ async function onApiLoaded() {
window.ipcRenderer.on('ytmd:toggle-mute', (_) => {
document
.querySelector<
HTMLElement & { onVolumeTap: () => void }
HTMLElement & { onVolumeClick: () => void }
>('ytmusic-player-bar')
?.onVolumeTap();
?.onVolumeClick();
});
window.ipcRenderer.on('ytmd:get-queue', () => {
@ -159,6 +171,114 @@ async function onApiLoaded() {
} satisfies QueueResponse);
});
window.ipcRenderer.on(
'ytmd:add-to-queue',
(_, videoId: string, queueInsertPosition: string) => {
const queue = document.querySelector<QueueElement>('#queue');
const app = document.querySelector<YouTubeMusicAppElement>('ytmusic-app');
if (!app) return;
const store = queue?.queue.store.store;
if (!store) return;
app.networkManager
.fetch('/music/get_queue', {
queueContextParams: store.getState().queue.queueContextParams,
queueInsertPosition,
videoIds: [videoId],
})
.then((result) => {
if (
result &&
typeof result === 'object' &&
'queueDatas' in result &&
Array.isArray(result.queueDatas)
) {
const queueItems = store.getState().queue.items;
const queueItemsLength = queueItems.length ?? 0;
queue?.dispatch({
type: 'ADD_ITEMS',
payload: {
nextQueueItemId: store.getState().queue.nextQueueItemId,
index:
queueInsertPosition === 'INSERT_AFTER_CURRENT_VIDEO'
? queueItems.findIndex(
(it) =>
(
it.playlistPanelVideoRenderer ||
it.playlistPanelVideoWrapperRenderer
?.primaryRenderer.playlistPanelVideoRenderer
)?.selected,
) + 1 || queueItemsLength
: queueItemsLength,
items: result.queueDatas
.map((it) =>
typeof it === 'object' && it && 'content' in it
? it.content
: null,
)
.filter(Boolean),
shuffleEnabled: false,
shouldAssignIds: true,
},
});
}
});
},
);
window.ipcRenderer.on(
'ytmd:move-in-queue',
(_, fromIndex: number, toIndex: number) => {
const queue = document.querySelector<QueueElement>('#queue');
queue?.dispatch({
type: 'MOVE_ITEM',
payload: {
fromIndex,
toIndex,
},
});
},
);
window.ipcRenderer.on('ytmd:remove-from-queue', (_, index: number) => {
const queue = document.querySelector<QueueElement>('#queue');
queue?.dispatch({
type: 'REMOVE_ITEM',
payload: index,
});
});
window.ipcRenderer.on('ytmd:set-queue-index', (_, index: number) => {
const queue = document.querySelector<QueueElement>('#queue');
queue?.dispatch({
type: 'SET_INDEX',
payload: index,
});
});
window.ipcRenderer.on('ytmd:clear-queue', () => {
const queue = document.querySelector<QueueElement>('#queue');
queue?.queue.store.store.dispatch({
type: 'SET_PLAYER_PAGE_INFO',
payload: { open: false },
});
queue?.dispatch({
type: 'CLEAR',
});
});
window.ipcRenderer.on('ytmd:search', async (_, query: string) => {
const app = document.querySelector<YouTubeMusicAppElement>('ytmusic-app');
const searchBox =
document.querySelector<SearchBoxElement>('ytmusic-search-box');
if (!app || !searchBox) return;
const result = await app.networkManager.fetch('/search', {
query,
suggestStats: searchBox.getSearchboxStats(),
});
window.ipcRenderer.send('ytmd:search-results', result);
});
const video = document.querySelector('video')!;
const audioContext = new AudioContext();
const audioSource = audioContext.createMediaElementSource(video);

View File

@ -2,13 +2,8 @@ import type { YoutubePlayer } from '@/types/youtube-player';
import type { GetState, QueueItem } from '@/types/datahost-get-state';
type StoreState = GetState;
type Store = {
dispatch: (obj: {
type: string;
payload?: {
items?: QueueItem[];
};
}) => void;
export type Store = {
dispatch: (obj: { type: string; payload?: unknown }) => void;
getState: () => StoreState;
replaceReducer: (param1: unknown) => unknown;

View File

@ -0,0 +1,3 @@
export interface SearchBoxElement extends HTMLElement {
getSearchboxStats(): unknown;
}

View File

@ -0,0 +1,6 @@
export interface YouTubeMusicAppElement extends HTMLElement {
navigate(page: string): void;
networkManager: {
fetch: (url: string, data: unknown) => Promise<unknown>;
};
}

View File

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

View File

@ -7,17 +7,17 @@ import { Project } from 'ts-morph';
const snakeToCamel = (text: string) =>
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 = (
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 plugins = globSync([
'src/plugins/*/index.{js,ts}',
@ -35,35 +35,39 @@ export const pluginVirtualModuleGenerator = (
return { name, path };
});
const src = project.createSourceFile('vm:pluginIndexes', (writer) => {
// prettier-ignore
for (const { name, path } of plugins) {
const src = globalProject.createSourceFile(
'vm:pluginIndexes',
(writer) => {
// prettier-ignore
for (const { name, path } of plugins) {
const relativePath = relative(resolve(srcPath, '..'), path).replace(/\\/g, '/');
writer.writeLine(`import ${snakeToCamel(name)}Plugin, { pluginStub as ${snakeToCamel(name)}PluginStub } from "./${relativePath}";`);
}
writer.blankLine();
writer.blankLine();
// Context-specific exports
writer.writeLine(`export const ${mode}Plugins = {`);
for (const { name } of plugins) {
const checkMode = mode === 'main' ? 'backend' : mode;
// HACK: To avoid situation like importing renderer plugins in main
writer.writeLine(
` ...(${snakeToCamel(name)}Plugin['${checkMode}'] ? { "${name}": ${snakeToCamel(name)}Plugin } : {}),`,
);
}
writer.writeLine('};');
writer.blankLine();
// Context-specific exports
writer.writeLine(`export const ${mode}Plugins = {`);
for (const { name } of plugins) {
const checkMode = mode === 'main' ? 'backend' : mode;
// HACK: To avoid situation like importing renderer plugins in main
writer.writeLine(
` ...(${snakeToCamel(name)}Plugin['${checkMode}'] ? { "${name}": ${snakeToCamel(name)}Plugin } : {}),`,
);
}
writer.writeLine('};');
writer.blankLine();
// All plugins export (stub only) // Omit<Plugin, 'backend' | 'preload' | 'renderer'>
writer.writeLine('export const allPlugins = {');
for (const { name } of plugins) {
writer.writeLine(` "${name}": ${snakeToCamel(name)}PluginStub,`);
}
writer.writeLine('};');
writer.blankLine();
});
// All plugins export (stub only) // Omit<Plugin, 'backend' | 'preload' | 'renderer'>
writer.writeLine('export const allPlugins = {');
for (const { name } of plugins) {
writer.writeLine(` "${name}": ${snakeToCamel(name)}PluginStub,`);
}
writer.writeLine('};');
writer.blankLine();
},
{ overwrite: true },
);
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 { fileURLToPath } from 'node:url';
@ -8,10 +8,34 @@ import {
ts,
ObjectLiteralExpression,
VariableDeclarationKind,
Node,
type ObjectLiteralElementLike,
} from 'ts-morph';
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 (
mode: 'backend' | 'preload' | 'renderer' | 'none',
): PluginOption {
@ -22,131 +46,96 @@ export default function (
return {
name: 'ytm-plugin-loader',
async load(id) {
load(id) {
if (!pluginFilter(id)) return null;
const __dirname = dirname(fileURLToPath(import.meta.url));
const project = new Project({
tsConfigFilePath: resolve(__dirname, '..', 'tsconfig.json'),
skipAddingFilesFromTsConfig: true,
skipLoadingLibFiles: true,
skipFileDependencyResolution: true,
});
const src = project.createSourceFile(
// Read file asynchronously
const fileContent = readFileSync(id, 'utf8');
// Create or update source file in the global project instance
const src = globalProject.createSourceFile(
'_pf' + basename(id),
await readFile(id, 'utf8'),
fileContent,
{ overwrite: true },
);
const exports = src.getExportedDeclarations();
let objExpr: ObjectLiteralExpression | undefined = undefined;
let objExpr: ObjectLiteralExpression | undefined;
for (const [name, [expr]] of exports) {
if (name !== 'default') continue;
switch (expr.getKind()) {
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;
// Identify the default export as an object literal, or via a 'createPlugin' call
for (const [exportName, declarations] of exports) {
if (exportName !== 'default') continue;
const expr = declarations[0];
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];
if (arg.getKind() !== ts.SyntaxKind.ObjectLiteralExpression)
continue;
objExpr = arg.asKindOrThrow(ts.SyntaxKind.ObjectLiteralExpression);
break;
if (arg.getKind() === ts.SyntaxKind.ObjectLiteralExpression) {
objExpr = arg.asKindOrThrow(
ts.SyntaxKind.ObjectLiteralExpression,
);
break;
}
}
}
}
if (!objExpr) return null;
const properties = objExpr.getProperties();
const propertyNames = properties.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');
}
});
// Build a map of property names to their AST nodes for fast lookup
const propMap = new Map<string, ObjectLiteralElementLike>();
for (const prop of objExpr.getProperties()) {
const name = getPropertyName(prop);
if (name) propMap.set(name, prop);
}
const contexts = ['backend', 'preload', 'renderer', 'menu'];
for (const ctx of contexts) {
if (mode === 'none') {
const index = propertyNames.indexOf(ctx);
if (index === -1) continue;
objExpr.getProperty(propertyNames[index])?.remove();
if (mode === 'none' && propMap.has(ctx)) {
propMap.get(ctx)?.remove();
continue;
}
if (ctx === mode) continue;
if (ctx === 'menu' && mode === 'backend') continue;
const index = propertyNames.indexOf(ctx);
if (index === -1) continue;
objExpr.getProperty(propertyNames[index])?.remove();
if (ctx === mode || (ctx === 'menu' && mode === 'backend')) continue;
if (propMap.has(ctx)) propMap.get(ctx)?.remove();
}
const stubObjExpr = src
.addVariableStatement({
isExported: true,
declarationKind: VariableDeclarationKind.Const,
declarations: [
{
name: 'pluginStub',
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');
}
// Add an exported variable 'pluginStub' with the modified object literal's text
const varStmt = src.addVariableStatement({
isExported: true,
declarationKind: VariableDeclarationKind.Const,
declarations: [
{
name: 'pluginStub',
initializer: (writer) => writer.write(objExpr.getText()),
},
],
});
const stubObjExpr = varStmt
.getDeclarations()[0]
.getInitializerIfKindOrThrow(ts.SyntaxKind.ObjectLiteralExpression);
if (mode === 'backend') contexts.pop();
for (const ctx of contexts) {
const index = stubPropertyNames.indexOf(ctx);
if (index === -1) continue;
// Similarly build a map for the stub properties
const stubMap = new Map<string, ObjectLiteralElementLike>();
for (const prop of stubObjExpr.getProperties()) {
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 {

381
web/youtube-music-hu.svg Normal file
View File

@ -0,0 +1,381 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 800 300" width="800" height="300"><script xmlns="" id="eppiocemhmnlbhjplcgkofciiegomcon"/><script xmlns=""/><script xmlns=""/>
<foreignObject width="100%" height="100%">
<div xmlns="http://www.w3.org/1999/xhtml" bis_skin_checked="1">
<style>
.container {
width: 100%;
position: relative;
overflow: hidden;
}
a {
text-decoration: none;
}
h1.main, p.demos {
-webkit-animation-delay: 18s;
-moz-animation-delay: 18s;
-ms-animation-delay: 18s;
animation-delay: 18s;
}
.container {
position: fixed;
top: 0px;
left: 0px;
width: 100%;
height: 100%;
z-index: 0;
}
.content {
position: absolute;
width: 100%;
height: 100%;
left: 0px;
top: 0px;
z-index: 1000;
}
.container h2 {
position: absolute;
top: 50%;
line-height: 100px;
height: 90px;
margin-top: -90px;
font-size: 90px;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple
Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
width: 100%;
text-align: center;
color: transparent;
-webkit-animation: blurFadeInOut 3s ease-in backwards;
-moz-animation: blurFadeInOut 3s ease-in backwards;
-ms-animation: blurFadeInOut 3s ease-in backwards;
animation: blurFadeInOut 3s ease-in backwards;
}
.container h2.frame-1 {
-webkit-animation-delay: 0s;
-moz-animation-delay: 0s;
-ms-animation-delay: 0s;
animation-delay: 0s;
}
.container h2.frame-2 {
-webkit-animation-delay: 3s;
-moz-animation-delay: 3s;
-ms-animation-delay: 3s;
animation-delay: 3s;
}
.container h2.frame-3 {
-webkit-animation-delay: 6s;
-moz-animation-delay: 6s;
-ms-animation-delay: 6s;
animation-delay: 6s;
}
.container h2.frame-4 {
-webkit-animation-delay: 9s;
-moz-animation-delay: 9s;
-ms-animation-delay: 9s;
animation-delay: 9s;
}
.container h2.frame-5 {
-webkit-animation: none;
-moz-animation: none;
-ms-animation: none;
animation: none;
color: transparent;
text-shadow: 0px 0px 1px #fff;
}
.container h2.frame-5 span {
-webkit-animation: blurFadeIn 3s ease-in 12s backwards;
-moz-animation: blurFadeIn 1s ease-in 12s backwards;
-ms-animation: blurFadeIn 3s ease-in 12s backwards;
animation: blurFadeIn 3s ease-in 12s backwards;
color: transparent;
text-shadow: 0px 0px 1px #fff;
}
.container h2.frame-5 span:nth-child(2) {
-webkit-animation-delay: 13s;
-moz-animation-delay: 13s;
-ms-animation-delay: 13s;
animation-delay: 13s;
}
.container h2.frame-5 span:nth-child(3) {
-webkit-animation-delay: 14s;
-moz-animation-delay: 14s;
-ms-animation-delay: 14s;
animation-delay: 14s;
}
.circle-link {
position: absolute;
left: 50%;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple
Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
bottom: 50px;
margin-left: -100px;
text-align: center;
line-height: 200px;
width: 200px;
height: 200px;
background: #cc0000;
color: #fff;
font-size: 25px;
-webkit-border-radius: 50%;
-moz-border-radius: 50%;
border-radius: 50%;
box-shadow: 0px 8px 15px rgba(0, 0, 0, 0.1);
-webkit-animation: fadeInRotate 0.8s ease 16s backwards;
-moz-animation: fadeInRotate 0.8s ease 16s backwards;
-ms-animation: fadeInRotate 0.8s ease 16s backwards;
animation: fadeInRotate 0.8s ease 16s backwards;
-webkit-transform: scale(1) rotate(0deg);
-moz-transform: scale(1) rotate(0deg);
-o-transform: scale(1) rotate(0deg);
-ms-transform: scale(1) rotate(0deg);
transform: scale(1) rotate(0deg);
background-repeat: no-repeat;
background-position: -250px -250px, 0 0;
background-image: -webkit-linear-gradient(
top left,
rgba(255, 255, 255, 0.2) 0%,
rgba(255, 255, 255, 0.2) 37%,
rgba(255, 255, 255, 0.8) 45%,
rgba(255, 255, 255, 0.0) 50%
);
background-image: -moz-linear-gradient(
0 0,
rgba(255, 255, 255, 0.2) 0%,
rgba(255, 255, 255, 0.2) 37%,
rgba(255, 255, 255, 0.8) 45%,
rgba(255, 255, 255, 0.0) 50%
);
background-image: -o-linear-gradient(
0 0,
rgba(255, 255, 255, 0.2) 0%,
rgba(255, 255, 255, 0.2) 37%,
rgba(255, 255, 255, 0.8) 45%,
rgba(255, 255, 255, 0.0) 50%
);
background-image: linear-gradient(
0 0,
rgba(255, 255, 255, 0.2) 0%,
rgba(255, 255, 255, 0.2) 37%,
rgba(255, 255, 255, 0.8) 45%,
rgba(255, 255, 255, 0.0) 50%
);
-moz-background-size: 250% 250%, 100% 100%;
background-size: 250% 250%, 100% 100%;
-webkit-transition: background-position 0s ease;
-moz-transition: background-position 0s ease;
-o-transition: background-position 0s ease;
transition: background-position 0s ease;
}
.circle-link:hover {
background-position: 0 0, 0 0;
-webkit-transition-duration: 0.5s;
-moz-transition-duration: 0.5s;
transition-duration: 0.5s;
}
@-webkit-keyframes blurFadeInOut {
0% {
opacity: 0;
color: #cc0000;
text-shadow: 0px 0px 40px #fff;
-webkit-transform: scale(1.3);
}
20%, 75% {
opacity: 1;
color: #cc0000;
text-shadow: 0px 0px 1px #fff;
-webkit-transform: scale(1);
}
100% {
opacity: 0;
text-shadow: 0px 0px 50px #fff;
-webkit-transform: scale(0);
}
}
@-webkit-keyframes blurFadeIn {
0% {
opacity: 0;
color: #cc0000;
text-shadow: 0px 0px 40px #fff;
-webkit-transform: scale(1.3);
}
50% {
opacity: 0.5;
color: #cc0000;
text-shadow: 0px 0px 10px #fff;
-webkit-transform: scale(1.1);
}
100% {
opacity: 1;
text-shadow: 0px 0px 1px #fff;
-webkit-transform: scale(1);
}
}
@-webkit-keyframes fadeInBack {
0% {
opacity: 0;
-webkit-transform: scale(0);
}
50% {
opacity: 0.4;
-webkit-transform: scale(2);
}
100% {
opacity: 0.2;
-webkit-transform: scale(5);
}
}
@-webkit-keyframes fadeInRotate {
0% {
opacity: 0;
-webkit-transform: scale(0) rotate(360deg);
}
100% {
opacity: 1;
-webkit-transform: scale(1) rotate(0deg);
}
}
@-moz-keyframes blurFadeInOut {
0% {
opacity: 0;
color: #cc0000;
text-shadow: 0px 0px 40px #fff;
-moz-transform: scale(1.3);
}
20%, 75% {
opacity: 1;
color: #cc0000;
text-shadow: 0px 0px 1px #fff;
-moz-transform: scale(1);
}
100% {
opacity: 0;
text-shadow: 0px 0px 50px #fff;
-moz-transform: scale(0);
}
}
@-moz-keyframes blurFadeIn {
0% {
opacity: 0;
color: #cc0000;
text-shadow: 0px 0px 40px #fff;
-moz-transform: scale(1.3);
}
100% {
opacity: 1;
text-shadow: 0px 0px 1px #fff;
-moz-transform: scale(1);
}
}
@-moz-keyframes fadeInBack {
0% {
opacity: 0;
-moz-transform: scale(0);
}
50% {
opacity: 0.4;
-moz-transform: scale(2);
}
100% {
opacity: 0.2;
-moz-transform: scale(5);
}
}
@-moz-keyframes fadeInRotate {
0% {
opacity: 0;
-moz-transform: scale(0) rotate(360deg);
}
100% {
opacity: 1;
-moz-transform: scale(1) rotate(0deg);
}
}
@keyframes blurFadeInOut {
0% {
opacity: 0;
color: #cc0000;
text-shadow: 0px 0px 40px #fff;
transform: scale(1.3);
}
20%, 75% {
opacity: 1;
color: #cc0000;
text-shadow: 0px 0px 1px #fff;
transform: scale(1);
}
100% {
opacity: 0;
text-shadow: 0px 0px 50px #fff;
transform: scale(0);
}
}
@keyframes blurFadeIn {
0% {
opacity: 0;
color: #cc0000;
text-shadow: 0px 0px 40px #fff;
transform: scale(1.3);
}
50% {
opacity: 0.5;
color: #cc0000;
text-shadow: 0px 0px 10px #fff;
transform: scale(1.1);
}
100% {
opacity: 1;
text-shadow: 0px 0px 1px #fff;
transform: scale(1);
}
}
@keyframes fadeInBack {
0% {
opacity: 0;
transform: scale(0);
}
50% {
opacity: 0.4;
transform: scale(2);
}
100% {
opacity: 0.2;
transform: scale(5);
}
}
@keyframes fadeInRotate {
0% {
opacity: 0;
transform: scale(0) rotate(360deg);
}
100% {
opacity: 1;
transform: scale(1) rotate(0deg);
}
}
</style>
<div class="container" bis_skin_checked="1">
<div class="content" bis_skin_checked="1">
<h2 class="frame-1">YouTube Music Asztali Alkalmazás</h2>
<h2 class="frame-2">Beépített reklámblokkolóval</h2>
<h2 class="frame-3">És beépített letöltővel</h2>
<h2 class="frame-4">Többplatformos</h2>
<h2 class="frame-5">
<span>Ingyenes,</span>
<span>Nyílt forráskódú</span>
</h2>
<a class="circle-link" href="https://github.com/th-ch/youtube-music/releases/latest">
Letöltés
</a>
</div>
</div>
</div>
</foreignObject>
</svg>

After

Width:  |  Height:  |  Size: 10 KiB