Compare commits

...

141 Commits

Author SHA1 Message Date
2e33bc8f96 Bump version to 3.9.0 2025-04-27 04:26:17 +09:00
0d6da0681a chore(i18n): Translated using Weblate (Korean)
Currently translated at 100.0% (413 of 413 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/ko/
2025-04-26 21:25:35 +02:00
1c76415846 feat(performance-improvement): added "performance improvement" plugin 2025-04-27 04:23:48 +09:00
a3d620ba52 chore(i18n): Translated using Weblate (Bosnian)
Currently translated at 15.3% (63 of 411 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/bs/
2025-04-26 20:39:24 +02:00
82bf407323 fix: rollback electron version to v34 (for fix #3216) 2025-04-27 03:17:53 +09:00
d83556e9fa feat(tuna-obs): added alternativeTitle and tags to tuna (#3288)
thanks to 3206
2025-04-27 02:37:16 +09:00
f70ae4f7c4 fix(synced-lyrics): fix #3157 2025-04-27 02:35:34 +09:00
268e7be15d fix(api-server): disable api-server as default 2025-04-27 02:30:21 +09:00
accd2bf350 fix(music-together): set connection as reliable, add retry logic 2025-04-27 02:19:01 +09:00
58cf1a543d Update changelog for v3.8.1 2025-04-25 14:30:19 +00:00
183461a968 Bump version to 3.8.1 2025-04-25 23:13:32 +09:00
4fe0fb9845 chore(i18n): Translated using Weblate (Malayalam)
Currently translated at 5.1% (21 of 411 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/ml/
2025-04-25 16:11:41 +02:00
0e5e1b8700 chore(i18n): Translated using Weblate (Serbian)
Currently translated at 0.7% (3 of 411 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/sr/
2025-04-25 16:11:41 +02:00
1ab1c86909 chore(i18n): Translated using Weblate (Kannada)
Currently translated at 0.7% (3 of 411 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/kn/
2025-04-25 16:11:41 +02:00
5e0f03dcd3 chore(i18n): Translated using Weblate (Tamil)
Currently translated at 98.7% (406 of 411 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/ta/
2025-04-25 16:11:40 +02:00
d4b5778fdd chore(deps): update dependency glob to v11.0.2 (#3283)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-25 23:04:39 +09:00
943b806bbb fix(deps): update dependency i18next to v25.0.1 (#3284)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-25 23:04:29 +09:00
a5d6c32f77 chore(deps): update dependency typescript-eslint to v8.31.0 (#3286)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-25 23:04:21 +09:00
4efa243ed0 chore(deps): update dependency discord-api-types to v0.38.1 (#3285)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-25 23:04:13 +09:00
8d09f27c59 fix(deps): update dependency youtubei.js to v13.4.0 (#3287)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-25 23:03:48 +09:00
47a2cde673 fix(deps): update dependency zod to v3.24.3 (#3250)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-25 22:38:56 +09:00
67051bb5ce chore(deps): update dependency vite to v6.3.3 (#3251)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-25 22:38:31 +09:00
233f6dbdea fix(deps): update dependency @hono/zod-openapi to v0.19.5 (#3258)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-25 22:36:47 +09:00
2c7a46240d chore(deps): update dependency vite-plugin-inspect to v11.0.1 (#3259)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-25 22:36:33 +09:00
e0edc0c1dd chore(deps): update dependency esbuild to v0.25.3 (#3282)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-25 22:35:30 +09:00
2001fb9254 chore(deps): update eslint monorepo to v9.25.1 (#3260)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-25 22:33:32 +09:00
206efaa7f7 chore(deps): update dependency electron to v35.2.1 (#3281)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-25 22:31:39 +09:00
8bb1d63cb0 chore(deps): update playwright monorepo to v1.52.0 (#3256)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-25 22:29:41 +09:00
841de026ce chore(deps): update dependency eslint-import-resolver-typescript to v4.3.4 (#3273)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-25 22:27:39 +09:00
6fac8b409e fix: fix cuted "j" and glow in lyrics (#3277)
* fix cuted "j" and glow in lyrics

* added comment in glow lyrics fix
2025-04-25 22:27:29 +09:00
b5ce1f566d chore(i18n): Translated using Weblate (Croatian)
Currently translated at 4.3% (18 of 411 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/hr/
2025-04-24 16:02:22 +00:00
8bb4f4426f chore(i18n): Translated using Weblate (Greek)
Currently translated at 100.0% (411 of 411 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/el/
2025-04-24 16:02:21 +00:00
c962d60c6d chore(i18n): Translated using Weblate (Greek)
Currently translated at 62.2% (256 of 411 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/el/
2025-04-23 17:01:43 +02:00
1d29e6be1a chore(i18n): Translated using Weblate (Georgian)
Currently translated at 21.8% (90 of 411 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/ka/
2025-04-22 11:01:45 +02:00
970205954b chore(i18n): Translated using Weblate (Hebrew)
Currently translated at 35.2% (145 of 411 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/he/
2025-04-22 11:01:44 +02:00
89fe072c0e chore(i18n): Translated using Weblate (Bulgarian)
Currently translated at 100.0% (411 of 411 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/bg/
2025-04-22 11:01:43 +02:00
93d68a94a3 chore(deps): update dependency electron to v35.2.0 (#3263)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-21 15:39:02 +09:00
f7afe1b994 chore(i18n): Translated using Weblate (Tamil)
Currently translated at 98.7% (406 of 411 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/ta/
2025-04-21 01:01:45 +02:00
5507f1acee chore(i18n): Translated using Weblate (Hebrew)
Currently translated at 34.7% (143 of 411 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/he/
2025-04-21 01:01:44 +02:00
d513302a9a fix(unobtrusive-player): handle shuffle play (#3247) 2025-04-16 23:18:14 +09:00
f31351be9c fix(deps): update dependency @ghostery/adblocker-electron to v2.5.1 (#3238)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-16 23:16:25 +09:00
99fdd027c3 chore(deps): update dependency vite to v6.3.0 (#3248)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-16 23:16:15 +09:00
0b143ec3a2 chore(deps): update dependency typescript-eslint to v8.30.1 (#3234)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-16 23:13:28 +09:00
588992f84b fix(deps): update dependency @ghostery/adblocker-electron-preload to v2.5.1 (#3239)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-16 23:13:03 +09:00
7306b0bc4e fix(deps): update dependency i18next to v25 (#3243)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-16 23:12:45 +09:00
9365f6edbc fix(deps): update dependency hono to v4.7.7 (#3245)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-16 23:12:31 +09:00
2c94cc760a chore(deps): update dependency vite to v6.2.6 (#3189)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-16 23:12:21 +09:00
53193cda98 feat(Synced-Lyrics): Also search for lyrics with the original title language (#3206)
* Add tags array for song info

* Implement original title and original artist search for LRCBLib

* comment cleanup

* Check if microformat.tags is an array
2025-04-16 23:12:04 +09:00
5853523074 chore(deps): update dependency eslint-config-prettier to v10.1.2 (#3219)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-13 14:57:31 +09:00
3a18eb8097 chore(deps): update dependency discord-api-types to v0.37.120 (#3221)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-13 14:57:23 +09:00
042a7df771 fix(deps): update dependency @hono/node-server to v1.14.1 (#3223)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-13 14:57:15 +09:00
5c2a6598b9 chore(deps): update dependency vite to v6.2.6 [security] (#3224)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-13 14:57:00 +09:00
2e4e9faf53 chore(deps): update dependency rollup to v4.40.0 (#3227)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-13 14:56:50 +09:00
a581ddf80d fix(mpris): keep MPRIS volume in sync with the actual volume (#3226) 2025-04-13 14:56:36 +09:00
500f770da8 chore(i18n): Translated using Weblate (Bosnian)
Currently translated at 5.5% (23 of 411 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/bs/
2025-04-12 21:06:01 +02:00
7cfe93312f chore(i18n): Translated using Weblate (Turkish)
Currently translated at 100.0% (411 of 411 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/tr/
2025-04-12 21:06:00 +02:00
f4bc90bf2d fix(deps): update dependency @hono/zod-openapi to v0.19.4 (#3215)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-10 07:24:49 +09:00
587fcdf23a chore(deps): update dependency electron to v35.1.5 (#3218)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-10 07:24:35 +09:00
c375e73781 chore(i18n): Translated using Weblate (Malayalam)
Currently translated at 5.1% (21 of 411 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/ml/
2025-04-09 04:40:53 +02:00
8cf045b450 fix(deps): update dependency hono to v4.7.6 (#3217)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-08 23:46:52 +09:00
e91f2d386d docs: add Portuguese README translation and update language shortcuts (#3192)
- Added README-pt.md with the Portuguese translation
- Updated README-(fr,hu,is,ko,ru,es).md and README.md to include the shortcut for the new language
2025-04-08 23:41:43 +09:00
505465e745 chore(i18n): Added translation using Weblate (Malayalam) 2025-04-08 10:19:45 +02:00
662d3af81b chore(i18n): Translated using Weblate (Hindi)
Currently translated at 34.7% (143 of 411 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/hi/
2025-04-08 10:19:43 +02:00
fef3b63581 fix: custom Video/Audio switcher in Plugins menu (#3174)
Related to #3167

Update `setVideoState` function and `switchButtonDiv` event listener to handle transition back to audio mode.

* **Update `setVideoState` function**
  - Correctly handle the transition back to audio mode.
  - Update the `hideVideo` property in the plugin configuration.
  - Use `this.config` instead of `config` to set options and update checkbox state.

---

For more details, open the [Copilot Workspace session](https://copilot-workspace.githubnext.com/th-ch/youtube-music/issues/3167?shareId=XXXX-XXXX-XXXX-XXXX).
2025-04-08 07:46:51 +09:00
0d3311ff5d chore(deps): update dependency typescript-eslint to v8.29.1 (#3214)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-08 07:38:13 +09:00
7370b56767 chore(deps): update eslint monorepo to v9.24.0 (#3195)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-08 07:34:10 +09:00
c0f3f136bb chore(deps): update dependency typescript to v5.8.3 (#3196)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-08 07:30:51 +09:00
62c2dcba95 chore(deps): update dependency vite to v6.2.5 [security] (#3194)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-08 07:30:39 +09:00
3e1abd984f fix(deps): update dependency node-id3 to v0.2.9 (#3191)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-08 07:29:04 +09:00
b689ba272b chore(deps): update dependency electron to v35.1.4 (#3184)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-08 07:28:39 +09:00
2775063cc8 chore(deps): update dependency eslint-plugin-prettier to v5.2.6 (#3182)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-08 07:27:52 +09:00
d557d1cbf0 chore(deps): update dependency eslint-import-resolver-typescript to v4.3.2 (#3208)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-08 07:25:09 +09:00
e965ac2004 chore(i18n): Translated using Weblate (Bulgarian)
Currently translated at 79.5% (327 of 411 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/bg/
2025-04-07 13:01:45 +02:00
a1a0705c07 chore(i18n): Translated using Weblate (Vietnamese)
Currently translated at 98.0% (403 of 411 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/vi/
2025-04-07 13:01:44 +02:00
29dfef1518 chore(i18n): Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (411 of 411 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/zh_Hant/
2025-04-07 13:01:43 +02:00
2c5b09e488 chore(i18n): Translated using Weblate (Japanese)
Currently translated at 100.0% (411 of 411 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/ja/
2025-04-07 13:01:42 +02:00
5a2b5ae522 chore(i18n): Translated using Weblate (Hungarian)
Currently translated at 100.0% (411 of 411 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/hu/
2025-04-05 15:01:42 +02:00
5dc3b09b7b chore(i18n): Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (411 of 411 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/zh_Hant/
2025-04-05 15:01:41 +02:00
1a1a764d64 docs: add Japanese README (#3180)
I created Japanese translated README.
2025-04-04 22:21:37 +09:00
fc47c9f590 chore(i18n): Translated using Weblate (Swedish)
Currently translated at 21.4% (88 of 411 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/sv/
2025-04-02 23:58:32 +02:00
470bc761ef chore(i18n): Translated using Weblate (Dutch)
Currently translated at 99.5% (409 of 411 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/nl/
2025-04-02 23:58:32 +02:00
a60dde2bdb chore(i18n): Translated using Weblate (Spanish)
Currently translated at 100.0% (411 of 411 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/es/
2025-04-02 23:58:31 +02:00
8b1332ee38 chore(i18n): Translated using Weblate (Bosnian)
Currently translated at 3.4% (14 of 411 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/bs/
2025-04-02 21:09:11 +02:00
12eb7c7d9b chore(i18n): Translated using Weblate (Hindi)
Currently translated at 34.3% (141 of 411 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/hi/
2025-04-02 21:09:11 +02:00
aecac6be6c chore(i18n): Translated using Weblate (Swedish)
Currently translated at 20.6% (85 of 411 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/sv/
2025-04-02 21:09:11 +02:00
44913173eb chore(i18n): Translated using Weblate (Bulgarian)
Currently translated at 62.5% (257 of 411 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/bg/
2025-04-02 21:09:11 +02:00
2829108e32 chore(i18n): Translated using Weblate (Bulgarian)
Currently translated at 62.5% (257 of 411 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/bg/
2025-04-02 21:09:03 +02:00
0eb10017c9 chore(i18n): Translated using Weblate (Vietnamese)
Currently translated at 97.8% (402 of 411 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/vi/
2025-04-02 21:09:02 +02:00
c36745e79b chore(deps): update dependency node-gyp to v11.2.0 (#3177)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-02 22:08:47 +09:00
e33269bce6 chore(deps): update dependency rollup to v4.39.0 (#3179)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-02 22:08:36 +09:00
7c4894a5f3 chore(i18n): Translated using Weblate (Persian)
Currently translated at 100.0% (411 of 411 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/fa/
2025-04-02 07:00:03 +02:00
84e3cf895f chore(i18n): Translated using Weblate (German)
Currently translated at 100.0% (411 of 411 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/de/
2025-04-02 03:41:57 +02:00
9d7281179b chore(i18n): Translated using Weblate (Vietnamese)
Currently translated at 97.5% (401 of 411 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/vi/
2025-04-02 02:22:17 +02:00
85c0995323 chore(i18n): Translated using Weblate (Finnish)
Currently translated at 75.1% (309 of 411 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/fi/
2025-04-01 21:35:06 +02:00
21990e71db chore(i18n): Update translation files
Updated by "Remove blank strings" hook in Weblate.

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/
2025-04-01 18:57:47 +02:00
9d99ff4095 chore(i18n): Translated using Weblate (Estonian)
Currently translated at 22.3% (92 of 411 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/et/
2025-04-01 18:57:46 +02:00
075fcde92a chore(i18n): Translated using Weblate (Bulgarian)
Currently translated at 36.0% (148 of 411 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/bg/
2025-04-01 18:57:46 +02:00
e654b7e695 chore(i18n): Translated using Weblate (Bosnian)
Currently translated at 2.9% (12 of 411 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/bs/
2025-04-01 16:18:59 +02:00
df78d13076 chore(i18n): Translated using Weblate (Sinhala)
Currently translated at 10.4% (43 of 411 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/si/
2025-04-01 16:18:58 +02:00
7bc24116da chore(i18n): Translated using Weblate (Croatian)
Currently translated at 2.1% (9 of 411 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/hr/
2025-04-01 16:18:58 +02:00
3225cfe5dd chore(i18n): Added translation using Weblate (Bosnian) 2025-04-01 12:09:28 +02:00
9de8bfcdc4 chore(i18n): Translated using Weblate (Sinhala)
Currently translated at 10.2% (42 of 411 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/si/
2025-04-01 12:09:27 +02:00
ab01c80a13 chore(i18n): Translated using Weblate (Indonesian)
Currently translated at 100.0% (411 of 411 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/id/
2025-04-01 12:09:27 +02:00
b6fb3d6316 chore(i18n): Translated using Weblate (Japanese)
Currently translated at 100.0% (411 of 411 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/ja/
2025-04-01 12:09:27 +02:00
6287d10a82 chore(i18n): Translated using Weblate (German)
Currently translated at 99.7% (410 of 411 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/de/
2025-04-01 12:09:26 +02:00
ed79631d66 chore(i18n): Translated using Weblate (Hebrew)
Currently translated at 24.8% (102 of 411 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/he/
2025-04-01 04:30:21 +02:00
b7f607bdbd chore(deps): update dependency typescript-eslint to v8.29.0 (#3169)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-01 09:02:07 +09:00
546c89e175 chore(i18n): Translated using Weblate (Finnish)
Currently translated at 73.9% (304 of 411 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/fi/
2025-03-31 18:00:41 +02:00
486a43f4b7 chore(i18n): Translated using Weblate (Ukrainian)
Currently translated at 100.0% (411 of 411 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/uk/
2025-03-31 18:00:40 +02:00
8d51137a18 chore(i18n): Translated using Weblate (Turkish)
Currently translated at 99.7% (410 of 411 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/tr/
2025-03-31 18:00:40 +02:00
cf67540a1f chore(i18n): Translated using Weblate (Italian)
Currently translated at 99.7% (410 of 411 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/it/
2025-03-31 18:00:40 +02:00
edad53e989 fix(downloader): allow downloads for signed out users (#3145)
* fix(downloader): allow downloads for signed out users

* refactor(downloader): use yt.config_.LOGGED_IN for checking sign in status

* Apply suggestions from code review

---------

Co-authored-by: JellyBrick <shlee1503@naver.com>
2025-03-31 22:48:14 +09:00
cc6c0bdff1 fix(README): Fixed typos in some hyperlinks (#3158)
* Update README-es.md

* Update README-fr.md

* Update README-hu.md

* Update README-is.md

* Update README-ko.md

* Update README-ru.md

* Update README.md
2025-03-31 22:28:15 +09:00
dbdc3c60a5 chore(i18n): Translated using Weblate (Dutch)
Currently translated at 99.5% (409 of 411 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/nl/
2025-03-31 14:12:30 +02:00
db4134f40d chore(deps): update dependency vite to v6.2.4 (#3124) 2025-03-31 20:07:52 +09:00
21c3e6a5f2 chore(deps): update dependency eslint-import-resolver-typescript to v4.3.1 (#3151) 2025-03-31 20:01:59 +09:00
e5b4ba9f15 chore(deps): update dependency rollup to v4.38.0 (#3154) 2025-03-31 20:01:33 +09:00
8a51468671 chore(deps): update dependency esbuild to v0.25.2 (#3160) 2025-03-31 20:01:19 +09:00
083403bf80 chore(i18n): Added translation using Weblate (Serbian) 2025-03-31 11:17:33 +02:00
886f3203e0 chore(i18n): Translated using Weblate (Arabic)
Currently translated at 100.0% (411 of 411 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/ar/
2025-03-31 05:45:20 +02:00
253fe0055f chore(i18n): Translated using Weblate (Portuguese)
Currently translated at 100.0% (411 of 411 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/pt/
2025-03-31 05:45:20 +02:00
6637f6a0da chore(i18n): Translated using Weblate (Spanish)
Currently translated at 100.0% (411 of 411 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/es/
2025-03-31 05:45:19 +02:00
f7e359a740 chore(i18n): Added translation using Weblate (Kannada) 2025-03-30 20:33:50 +02:00
3c88d171ab chore(i18n): Translated using Weblate (Sinhala)
Currently translated at 9.7% (40 of 411 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/si/
2025-03-30 20:33:49 +02:00
8e27be48c6 chore(i18n): Translated using Weblate (Thai)
Currently translated at 100.0% (411 of 411 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/th/
2025-03-30 20:33:49 +02:00
700b2a3669 chore(i18n): Translated using Weblate (Polish)
Currently translated at 100.0% (411 of 411 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/pl/
2025-03-28 06:22:46 +01:00
329327d491 chore(i18n): Translated using Weblate (French)
Currently translated at 99.7% (410 of 411 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/fr/
2025-03-28 06:22:46 +01:00
829a06db5a chore(deps): update dependency electron to v35.1.2 (#3147)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-28 09:27:03 +09:00
98b4053a3a chore(i18n): Translated using Weblate (Urdu)
Currently translated at 25.3% (104 of 411 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/ur/
2025-03-27 15:01:59 +00:00
d340828da7 chore(i18n): Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (411 of 411 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/pt_BR/
2025-03-27 15:01:58 +00:00
fb18f26964 chore(i18n): Translated using Weblate (Filipino)
Currently translated at 87.8% (361 of 411 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/fil/
2025-03-27 15:01:58 +00:00
b37fee1e1b chore(i18n): Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (411 of 411 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/zh_Hant/
2025-03-27 15:01:57 +00:00
fb2c4f0a09 chore(i18n): Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (411 of 411 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/zh_Hans/
2025-03-27 15:01:56 +00:00
d58897ba4c chore(i18n): Translated using Weblate (Russian)
Currently translated at 100.0% (411 of 411 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/ru/
2025-03-27 15:01:54 +00:00
c30fbe41a6 chore(i18n): Translated using Weblate (Italian)
Currently translated at 99.7% (410 of 411 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/it/
2025-03-27 15:01:53 +00:00
5a7daaf2f6 chore(i18n): Translated using Weblate (Greek)
Currently translated at 61.3% (252 of 411 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/el/
2025-03-27 15:01:52 +00:00
61c0217b8f chore(deps): update dependency electron to v35.1.1 (#3143)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-27 13:33:58 +09:00
d5ea182a50 chore(deps): update dependency eslint-import-resolver-typescript to v4.2.5 (#3144)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-27 13:33:51 +09:00
33133de7c7 chore(deps): update dependency @types/semver to v7.7.0 (#3141)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-27 05:35:56 +09:00
b544e18cea fix(deps): update dependency electron-updater to v6.6.2 (#3142)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-27 03:40:37 +09:00
808f802efd Update changelog for v3.8.0 2025-03-26 12:09:00 +00:00
72 changed files with 5307 additions and 938 deletions

View File

@ -3,7 +3,7 @@
# 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)
[![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/)
@ -21,7 +21,7 @@
</a>
</div>
Read this in other languages: [🇰🇷](./docs/readme/README-ko.md), [🇫🇷](./docs/readme/README-fr.md), [🇮🇸](./docs/readme/README-is.md), [🇨🇱 🇪🇸](./docs/readme/README-es.md), [🇷🇺](./docs/readme/README-ru.md), [🇭🇺](./docs/readme/README-hu.md)
Read this in other languages: [🇰🇷](./docs/readme/README-ko.md), [🇫🇷](./docs/readme/README-fr.md), [🇮🇸](./docs/readme/README-is.md), [🇨🇱 🇪🇸](./docs/readme/README-es.md), [🇷🇺](./docs/readme/README-ru.md), [🇭🇺](./docs/readme/README-hu.md), [🇧🇷](./docs/readme/README-pt.md), [🇯🇵](./docs/readme/README-ja.md)
**Electron wrapper around YouTube Music featuring:**

View File

@ -2,8 +2,138 @@
All notable changes to this project will be documented in this file. Dates are displayed in UTC.
#### [v3.8.1](https://github.com/th-ch/youtube-music/compare/v3.8.0...v3.8.1)
- chore(deps): update dependency glob to v11.0.2 [`#3283`](https://github.com/th-ch/youtube-music/pull/3283)
- fix(deps): update dependency i18next to v25.0.1 [`#3284`](https://github.com/th-ch/youtube-music/pull/3284)
- chore(deps): update dependency typescript-eslint to v8.31.0 [`#3286`](https://github.com/th-ch/youtube-music/pull/3286)
- chore(deps): update dependency discord-api-types to v0.38.1 [`#3285`](https://github.com/th-ch/youtube-music/pull/3285)
- fix(deps): update dependency youtubei.js to v13.4.0 [`#3287`](https://github.com/th-ch/youtube-music/pull/3287)
- fix(deps): update dependency zod to v3.24.3 [`#3250`](https://github.com/th-ch/youtube-music/pull/3250)
- chore(deps): update dependency vite to v6.3.3 [`#3251`](https://github.com/th-ch/youtube-music/pull/3251)
- fix(deps): update dependency @hono/zod-openapi to v0.19.5 [`#3258`](https://github.com/th-ch/youtube-music/pull/3258)
- chore(deps): update dependency vite-plugin-inspect to v11.0.1 [`#3259`](https://github.com/th-ch/youtube-music/pull/3259)
- chore(deps): update dependency esbuild to v0.25.3 [`#3282`](https://github.com/th-ch/youtube-music/pull/3282)
- chore(deps): update eslint monorepo to v9.25.1 [`#3260`](https://github.com/th-ch/youtube-music/pull/3260)
- chore(deps): update dependency electron to v35.2.1 [`#3281`](https://github.com/th-ch/youtube-music/pull/3281)
- chore(deps): update playwright monorepo to v1.52.0 [`#3256`](https://github.com/th-ch/youtube-music/pull/3256)
- chore(deps): update dependency eslint-import-resolver-typescript to v4.3.4 [`#3273`](https://github.com/th-ch/youtube-music/pull/3273)
- fix: fix cuted "j" and glow in lyrics [`#3277`](https://github.com/th-ch/youtube-music/pull/3277)
- chore(deps): update dependency electron to v35.2.0 [`#3263`](https://github.com/th-ch/youtube-music/pull/3263)
- fix(unobtrusive-player): handle shuffle play [`#3247`](https://github.com/th-ch/youtube-music/pull/3247)
- fix(deps): update dependency @ghostery/adblocker-electron to v2.5.1 [`#3238`](https://github.com/th-ch/youtube-music/pull/3238)
- chore(deps): update dependency vite to v6.3.0 [`#3248`](https://github.com/th-ch/youtube-music/pull/3248)
- chore(deps): update dependency typescript-eslint to v8.30.1 [`#3234`](https://github.com/th-ch/youtube-music/pull/3234)
- fix(deps): update dependency @ghostery/adblocker-electron-preload to v2.5.1 [`#3239`](https://github.com/th-ch/youtube-music/pull/3239)
- fix(deps): update dependency i18next to v25 [`#3243`](https://github.com/th-ch/youtube-music/pull/3243)
- fix(deps): update dependency hono to v4.7.7 [`#3245`](https://github.com/th-ch/youtube-music/pull/3245)
- chore(deps): update dependency vite to v6.2.6 [`#3189`](https://github.com/th-ch/youtube-music/pull/3189)
- feat(Synced-Lyrics): Also search for lyrics with the original title language [`#3206`](https://github.com/th-ch/youtube-music/pull/3206)
- chore(deps): update dependency eslint-config-prettier to v10.1.2 [`#3219`](https://github.com/th-ch/youtube-music/pull/3219)
- chore(deps): update dependency discord-api-types to v0.37.120 [`#3221`](https://github.com/th-ch/youtube-music/pull/3221)
- fix(deps): update dependency @hono/node-server to v1.14.1 [`#3223`](https://github.com/th-ch/youtube-music/pull/3223)
- chore(deps): update dependency vite to v6.2.6 [security] [`#3224`](https://github.com/th-ch/youtube-music/pull/3224)
- chore(deps): update dependency rollup to v4.40.0 [`#3227`](https://github.com/th-ch/youtube-music/pull/3227)
- fix(mpris): keep MPRIS volume in sync with the actual volume [`#3226`](https://github.com/th-ch/youtube-music/pull/3226)
- fix(deps): update dependency @hono/zod-openapi to v0.19.4 [`#3215`](https://github.com/th-ch/youtube-music/pull/3215)
- chore(deps): update dependency electron to v35.1.5 [`#3218`](https://github.com/th-ch/youtube-music/pull/3218)
- fix(deps): update dependency hono to v4.7.6 [`#3217`](https://github.com/th-ch/youtube-music/pull/3217)
- docs: add Portuguese README translation and update language shortcuts [`#3192`](https://github.com/th-ch/youtube-music/pull/3192)
- fix: custom Video/Audio switcher in Plugins menu [`#3174`](https://github.com/th-ch/youtube-music/pull/3174)
- chore(deps): update dependency typescript-eslint to v8.29.1 [`#3214`](https://github.com/th-ch/youtube-music/pull/3214)
- chore(deps): update eslint monorepo to v9.24.0 [`#3195`](https://github.com/th-ch/youtube-music/pull/3195)
- chore(deps): update dependency typescript to v5.8.3 [`#3196`](https://github.com/th-ch/youtube-music/pull/3196)
- chore(deps): update dependency vite to v6.2.5 [security] [`#3194`](https://github.com/th-ch/youtube-music/pull/3194)
- fix(deps): update dependency node-id3 to v0.2.9 [`#3191`](https://github.com/th-ch/youtube-music/pull/3191)
- chore(deps): update dependency electron to v35.1.4 [`#3184`](https://github.com/th-ch/youtube-music/pull/3184)
- chore(deps): update dependency eslint-plugin-prettier to v5.2.6 [`#3182`](https://github.com/th-ch/youtube-music/pull/3182)
- chore(deps): update dependency eslint-import-resolver-typescript to v4.3.2 [`#3208`](https://github.com/th-ch/youtube-music/pull/3208)
- docs: add Japanese README [`#3180`](https://github.com/th-ch/youtube-music/pull/3180)
- chore(deps): update dependency node-gyp to v11.2.0 [`#3177`](https://github.com/th-ch/youtube-music/pull/3177)
- chore(deps): update dependency rollup to v4.39.0 [`#3179`](https://github.com/th-ch/youtube-music/pull/3179)
- chore(deps): update dependency typescript-eslint to v8.29.0 [`#3169`](https://github.com/th-ch/youtube-music/pull/3169)
- fix(downloader): allow downloads for signed out users [`#3145`](https://github.com/th-ch/youtube-music/pull/3145)
- fix(README): Fixed typos in some hyperlinks [`#3158`](https://github.com/th-ch/youtube-music/pull/3158)
- chore(deps): update dependency vite to v6.2.4 [`#3124`](https://github.com/th-ch/youtube-music/pull/3124)
- chore(deps): update dependency eslint-import-resolver-typescript to v4.3.1 [`#3151`](https://github.com/th-ch/youtube-music/pull/3151)
- chore(deps): update dependency rollup to v4.38.0 [`#3154`](https://github.com/th-ch/youtube-music/pull/3154)
- chore(deps): update dependency esbuild to v0.25.2 [`#3160`](https://github.com/th-ch/youtube-music/pull/3160)
- chore(deps): update dependency electron to v35.1.2 [`#3147`](https://github.com/th-ch/youtube-music/pull/3147)
- chore(deps): update dependency electron to v35.1.1 [`#3143`](https://github.com/th-ch/youtube-music/pull/3143)
- chore(deps): update dependency eslint-import-resolver-typescript to v4.2.5 [`#3144`](https://github.com/th-ch/youtube-music/pull/3144)
- chore(deps): update dependency @types/semver to v7.7.0 [`#3141`](https://github.com/th-ch/youtube-music/pull/3141)
- fix(deps): update dependency electron-updater to v6.6.2 [`#3142`](https://github.com/th-ch/youtube-music/pull/3142)
- chore(i18n): Translated using Weblate (Greek) [`8bb4f44`](https://github.com/th-ch/youtube-music/commit/8bb4f4426f6a82b1b5c13a385a6e2b94c25f88a2)
- chore(i18n): Translated using Weblate (Bulgarian) [`89fe072`](https://github.com/th-ch/youtube-music/commit/89fe072c0e719026212bb506bb66baf37b31ceb4)
- chore(i18n): Translated using Weblate (Greek) [`5a7daaf`](https://github.com/th-ch/youtube-music/commit/5a7daaf2f6d1211c4b9461facf4de1962714bacf)
#### [v3.8.0](https://github.com/th-ch/youtube-music/compare/v3.7.5...v3.8.0)
> 26 March 2025
- chore(deps): update dependency typescript-eslint to v8.28.0 [`#3128`](https://github.com/th-ch/youtube-music/pull/3128)
- chore(deps): update dependency eslint-plugin-prettier to v5.2.5 [`#3123`](https://github.com/th-ch/youtube-music/pull/3123)
- fix(deps): update dependency @hono/node-server to v1.14.0 [`#3131`](https://github.com/th-ch/youtube-music/pull/3131)
- chore(deps): update dependency electron to v35.1.0 [`#3136`](https://github.com/th-ch/youtube-music/pull/3136)
- fix(deps): update dependency es-hangul to v2.3.2 [`#3138`](https://github.com/th-ch/youtube-music/pull/3138)
- chore(deps): update dependency eslint-import-resolver-typescript to v4.2.4 [`#3135`](https://github.com/th-ch/youtube-music/pull/3135)
- chore(deps): update eslint monorepo to v9.23.0 [`#3130`](https://github.com/th-ch/youtube-music/pull/3130)
- chore(deps): update dependency electron-vite to v3.1.0 [`#3137`](https://github.com/th-ch/youtube-music/pull/3137)
- chore(deps): update dependency @babel/runtime to v7.27.0 [`#3127`](https://github.com/th-ch/youtube-music/pull/3127)
- feat(synced-lyrics): romanization [`#2790`](https://github.com/th-ch/youtube-music/pull/2790)
- feat(plugin): add unobtrusive player plugin [`#3104`](https://github.com/th-ch/youtube-music/pull/3104)
- chore(deps): update dependency vite to v6.2.3 [security] [`#3134`](https://github.com/th-ch/youtube-music/pull/3134)
- fix(deps): update dependency youtubei.js to v13.3.0 [`#3133`](https://github.com/th-ch/youtube-music/pull/3133)
- chore(deps): update dependency electron-builder-squirrel-windows to v26.0.12 [`#3122`](https://github.com/th-ch/youtube-music/pull/3122)
- chore(deps): update dependency eslint-import-resolver-typescript to v4.2.2 [`#3106`](https://github.com/th-ch/youtube-music/pull/3106)
- chore(deps): update dependency electron-builder to v26.0.12 [`#3121`](https://github.com/th-ch/youtube-music/pull/3121)
- fix: Discord Rich Presence Fix [`#3074`](https://github.com/th-ch/youtube-music/pull/3074)
- fix(deps): update dependency @xhayper/discord-rpc to v1.2.1 [`#3107`](https://github.com/th-ch/youtube-music/pull/3107)
- chore(deps): update dependency typescript-eslint to v8.27.0 [`#3111`](https://github.com/th-ch/youtube-music/pull/3111)
- chore(deps): update dependency electron to v35.0.3 [`#3112`](https://github.com/th-ch/youtube-music/pull/3112)
- fix(deps): update dependency hono to v4.7.5 [`#3113`](https://github.com/th-ch/youtube-music/pull/3113)
- fix(deps): update dependency fast-average-color to v9.5.0 [`#3114`](https://github.com/th-ch/youtube-music/pull/3114)
- chore(deps): update dependency rollup to v4.37.0 [`#3103`](https://github.com/th-ch/youtube-music/pull/3103)
- chore(deps): update playwright monorepo to v1.51.1 [`#3105`](https://github.com/th-ch/youtube-music/pull/3105)
- chore(deps): update dependency eslint-import-resolver-typescript to v4 [`#3102`](https://github.com/th-ch/youtube-music/pull/3102)
- chore(deps): update dependency electron to v35.0.2 [`#3099`](https://github.com/th-ch/youtube-music/pull/3099)
- fix(deps): update dependency i18next to v24.2.3 [`#3100`](https://github.com/th-ch/youtube-music/pull/3100)
- chore(deps): update dependency electron-builder to v26.0.11 [`#3095`](https://github.com/th-ch/youtube-music/pull/3095)
- chore(deps): update dependency @babel/runtime to v7.26.10 [security] [`#3094`](https://github.com/th-ch/youtube-music/pull/3094)
- chore(deps): update dependency eslint-config-prettier to v10.1.1 [`#3069`](https://github.com/th-ch/youtube-music/pull/3069)
- chore(deps): update playwright monorepo to v1.51.0 [`#3065`](https://github.com/th-ch/youtube-music/pull/3065)
- chore(deps): update dependency electron-builder-squirrel-windows to v26.0.11 [`#3096`](https://github.com/th-ch/youtube-music/pull/3096)
- chore(deps): update dependency esbuild to v0.25.1 [`#3097`](https://github.com/th-ch/youtube-music/pull/3097)
- chore(deps): update dependency typescript-eslint to v8.26.1 [`#3098`](https://github.com/th-ch/youtube-music/pull/3098)
- chore(deps): update eslint monorepo to v9.22.0 [`#3070`](https://github.com/th-ch/youtube-music/pull/3070)
- chore(deps): update dependency rollup to v4.35.0 [`#3071`](https://github.com/th-ch/youtube-music/pull/3071)
- chore(deps): update dependency electron to v35.0.1 [`#3075`](https://github.com/th-ch/youtube-music/pull/3075)
- fix(deps): update dependency happy-dom to v17.4.4 [`#3068`](https://github.com/th-ch/youtube-music/pull/3068)
- chore(deps): update dependency vite to v6.2.2 [`#3067`](https://github.com/th-ch/youtube-music/pull/3067)
- fix: Update winget-releaser version to main [`#3091`](https://github.com/th-ch/youtube-music/pull/3091)
- feat: Allow scrobbling using alternative song titles [`#3093`](https://github.com/th-ch/youtube-music/pull/3093)
- chore(deps): update dependency electron-builder-squirrel-windows to v26.0.10 [`#3054`](https://github.com/th-ch/youtube-music/pull/3054)
- chore(deps): update dependency typescript-eslint to v8.26.0 [`#3056`](https://github.com/th-ch/youtube-music/pull/3056)
- fix(deps): update dependency hono to v4.7.4 [`#3062`](https://github.com/th-ch/youtube-music/pull/3062)
- chore(deps): update dependency electron-builder to v26.0.10 [`#3053`](https://github.com/th-ch/youtube-music/pull/3053)
- chore(deps): update dependency electron to v35 [`#3058`](https://github.com/th-ch/youtube-music/pull/3058)
- fix(deps): update dependency happy-dom to v17.2.2 [`#3060`](https://github.com/th-ch/youtube-music/pull/3060)
- fix(deps): update dependency happy-dom to v17.1.13 [`#3057`](https://github.com/th-ch/youtube-music/pull/3057)
- chore(deps): update dependency typescript to v5.8.2 [`#3040`](https://github.com/th-ch/youtube-music/pull/3040)
- chore(deps): update dependency rollup to v4.34.9 [`#3043`](https://github.com/th-ch/youtube-music/pull/3043)
- fix(deps): update dependency @hono/swagger-ui to v0.5.1 [`#3045`](https://github.com/th-ch/youtube-music/pull/3045)
- fix: added French link in general README file [`#3047`](https://github.com/th-ch/youtube-music/pull/3047)
- fix(deps): update dependency @hono/zod-openapi to v0.19.2 [`#3046`](https://github.com/th-ch/youtube-music/pull/3046)
- chore(deps): update dependency @stylistic/eslint-plugin-js to v4.2.0 [`#3050`](https://github.com/th-ch/youtube-music/pull/3050)
- fix(deps): update dependency bgutils-js to v3.2.0 [`#3049`](https://github.com/th-ch/youtube-music/pull/3049)
- chore(i18n): Translated using Weblate (Tamil) [`a3601ce`](https://github.com/th-ch/youtube-music/commit/a3601cece6a1d291f9887e1a5049fb3c6ad09eb1)
- chore(i18n): Translated using Weblate (Arabic) [`06aaba0`](https://github.com/th-ch/youtube-music/commit/06aaba0c7fe9173051701c626d44a347b1bd7574)
- chore(i18n): Translated using Weblate (Spanish) [`dbf8b1c`](https://github.com/th-ch/youtube-music/commit/dbf8b1c5c53d88f676c12b7ffca0e23b3efaa541)
#### [v3.7.5](https://github.com/th-ch/youtube-music/compare/v3.7.4...v3.7.5)
> 1 March 2025
- chore(deps): update dependency builtin-modules to v5 [`#3038`](https://github.com/th-ch/youtube-music/pull/3038)
- chore(deps): update dependency typescript-eslint to v8.25.0 [`#2953`](https://github.com/th-ch/youtube-music/pull/2953)
- fix(deps): update dependency happy-dom to v17.1.8 [`#3001`](https://github.com/th-ch/youtube-music/pull/3001)

View File

@ -3,7 +3,7 @@
# 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)
[![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/.eslintrc.js)
[![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/)
@ -21,7 +21,7 @@
</a>
</div>
Lee esto en otros idiomas: [🏴 Inglés](../../README.md), [🇰🇷 Coreano](./README-ko.md), [🇫🇷 Francés](./README-fr.md), [🇮🇸 Islandés](./README-is.md), [🇪🇸 Español](./README-es.md), [🇷🇺 Ruso](./README-ru.md)
Lee esto en otros idiomas: [🏴 Inglés](../../README.md), [🇰🇷 Coreano](./README-ko.md), [🇫🇷 Francés](./README-fr.md), [🇮🇸 Islandés](./README-is.md), [🇪🇸 Español](./README-es.md), [🇷🇺 Ruso](./README-ru.md), [🇧🇷 Portugués](./README-pt.md), [🇯🇵 Japonés](./README-ja.md)
**Electron wrapper de YouTube Music con las siguientes características:**

View File

@ -3,7 +3,7 @@
# 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/)
[![Licence GitHub](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)
[![Licence GitHub](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)
[![style de code eslint](https://img.shields.io/badge/style_de_code-eslint-5ed9c7.svg?style=for-the-badge)](https://github.com/th-ch/youtube-music/blob/master/.eslintrc.js)
[![Statut de la construction](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/)
[![Toutes les versions GitHub](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/)
@ -21,7 +21,7 @@
</a>
</div>
Lisez ceci dans d'autres langues: [🏴 Anglais](../../README.md), [🇰🇷 Coréen](./README-ko.md), [🇫🇷 Français](./README-fr.md), [🇮🇸 Islandais](./README-is.md), [🇪🇸 Espagnol](./README-es.md), [🇷🇺 Russe](./README-ru.md)
Lisez ceci dans d'autres langues: [🏴 Anglais](../../README.md), [🇰🇷 Coréen](./README-ko.md), [🇫🇷 Français](./README-fr.md), [🇮🇸 Islandais](./README-is.md), [🇪🇸 Espagnol](./README-es.md), [🇷🇺 Russe](./README-ru.md), [🇧🇷 Portugais](./README-pt.md), [🇯🇵 Japonais](./README-ja.md)
**Enveloppe Electron autour de YouTube Music offrant :**

View File

@ -3,7 +3,7 @@
# 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)
[![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/)
@ -21,7 +21,7 @@
</a>
</div>
Olvasd el más nyelveken: [🏴 Angol](../../README.md), [🇰🇷 Korea](./README-ko.md), [🇫🇷 Francia](./README-fr.md), [🇮🇸 Izland](./README-is.md), [🇪🇸 Spanyol](./README-es.md), [🇷🇺 Orosz](./README-ru.md)
Olvasd el más nyelveken: [🏴 Angol](../../README.md), [🇰🇷 Korea](./README-ko.md), [🇫🇷 Francia](./README-fr.md), [🇮🇸 Izland](./README-is.md), [🇪🇸 Spanyol](./README-es.md), [🇷🇺 Orosz](./README-ru.md), [🇧🇷 Portugál](./README-pt.md), [🇯🇵 Japán](./README-ja.md)
**Electron keretrendszerre épülő alkalmazás a YouTube Music számára, amely a következőket kínálja:**

View File

@ -3,7 +3,7 @@
# YouTube Tónlist
[![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)
[![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/.eslintrc.js)
[![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/)
@ -21,7 +21,7 @@
</a>
</div>
Lestu þetta á öðrum tungumálum: [🏴 Ensku](../../README.md), [🇰🇷 Kóreska](./README-ko.md), [🇫🇷 Franska](./README-fr.md), [🇮🇸 Íslenskur](./README-is.md), [🇪🇸 Spænska](./README-es.md), [🇷🇺 Rússneska](./README-ru.md)
Lestu þetta á öðrum tungumálum: [🏴 Ensku](../../README.md), [🇰🇷 Kóreska](./README-ko.md), [🇫🇷 Franska](./README-fr.md), [🇮🇸 Íslenskur](./README-is.md), [🇪🇸 Spænska](./README-es.md), [🇷🇺 Rússneska](./README-ru.md), [🇧🇷 Portúgalska](./README-pt.md), [🇯🇵 Japanska](./README-ja.md)
**Electron umbúðir utan um YouTube Tónlist sem inniheldur:**

371
docs/readme/README-ja.md Normal file
View File

@ -0,0 +1,371 @@
<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/.eslintrc.js)
[![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>
![Screenshot](/web/screenshot.png "Screenshot")
<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">
</a>
</div>
他の言語で読む: [🏴 英語](../../README.md), [🇰🇷 韓国語](./README-ko.md), [🇫🇷 フランス語](./README-fr.md), [🇮🇸 アイスランド語](./README-is.md), [🇪🇸 スペイン語](./README-es.md), [🇷🇺 ロシア語](./README-ru.md)
**YouTube MusicのElectronラッパーには以下の機能があります:**
- ネイティブの外観と操作感、元のインターフェースを維持することを目指しています
- カスタムプラグインのフレームワーク: スタイル、コンテンツ、機能など、YouTube Musicをニーズに合わせて変更し、ワンクリックでプラグインを有効/無効にできます
## デモ画像
| プレーヤースクリーン (アルバムカラーテーマ & アンビエントライト) |
|:---------------------------------------------------------------------------------------------------------:|
|![Screenshot1](https://github.com/th-ch/youtube-music/assets/16558115/53efdf73-b8fa-4d7b-a235-b96b91ea77fc)|
## コンテンツ
- [機能](#機能)
- [利用可能なプラグイン](#利用可能なプラグイン)
- [翻訳](#翻訳)
- [ダウンロード](#ダウンロード)
- [Arch Linux](#arch-linux)
- [MacOS](#macos)
- [Windows](#windows)
- [ネットワーク接続なしでインストールする方法 (Windows)](#ネットワーク接続なしでインストールする方法-windows)
- [テーマ](#テーマ)
- [開発](#開発)
- [独自のプラグインを作成する](#独自のプラグインを作成する)
- [プラグインの作成](#プラグインの作成)
- [一般的な使用例](#一般的な使用例)
- [ビルド](#ビルド)
- [プロダクションプレビュー](#プロダクションプレビュー)
- [テスト](#テスト)
- [ライセンス](#ライセンス)
- [FAQ](#faq)
## 機能:
- **一時停止時の自動確認** (常に有効): 一定時間後に音楽を一時停止する["視聴を続けますか?"](https://user-images.githubusercontent.com/61631665/129977894-01c60740-7ec6-4bf0-9a2c-25da24491b0e.png)ポップアップを無効にします
- その他の機能...
## 利用可能なプラグイン:
- **広告ブロッカー**: すべての広告とトラッキングをブロックします
- **アルバムアクション**: プレイリストやアルバム内のすべての曲に「嫌いではない」「嫌い」「好き」「好きではない」ボタンを追加します
- **アルバムカラーテーマ**: アルバムのカラーパレットに基づいて動的なテーマと視覚効果を適用します
- **アンビエントモード**: 動画から柔らかい色を画面の背景に投影するライティング効果を適用します
- **<2A><><EFBFBD>ーディオコンプレッサー**: オーディオにコンプレッションを適用します(信号の最も大きな部分の音量を下げ、最も小さな部分の音量を上げます)
- **ナビゲーションバーのぼかし**: ナビゲーションバーを透明でぼやけたものにします
- **年齢制限の回避**: YouTubeの年齢確認を回避します
- **字幕選択**: 字幕を有効にします
- **コンパクトサイドバー**: サイドバーを常にコンパクトモードに設定します
- **クロスフェード**: 曲間にクロスフェードを適用します
- **自動再生の無効化**: すべての曲を「一時停止」モードで開始します
- **[Discord](https://discord.com/) リッチプレゼンス**: [リッチプレゼンス](https://user-images.githubusercontent.com/28219076/104362104-a7a0b980-5513-11eb-9744-bb89eabe0016.png)を使用して、友達にあなたが聴いている曲を表示します
- **ダウンローダー**: UIから直接MP3/ソースオーディオをダウンロードします
- **イコライザー**: 特定の周波数範囲をブーストまたはカットするフィルターを追加します(例: ベースブースター)
- **指数音量**: 音量スライダーを[指数関数的](https://greasyfork.org/en/scripts/397686-youtube-music-fix-volume-ratio/)にして、低い音量を選択しやすくします
- **アプリ内メニュー**: [メニューバーをおしゃれで暗い外観にします](https://user-images.githubusercontent.com/78568641/112215894-923dbf00-8c29-11eb-95c3-3ce15db27eca.png)
> (このプラグインとメニュー非表示オプションを有効にした後、メニューにアクセスする際に問題がある場合は、[この投稿](https://github.com/th-ch/youtube-music/issues/410#issuecomment-952060709)を参照してください)
- **スクロブラー**: [Last.fm](https://www.last.fm/)や[ListenBrainz](https://listenbrainz.org/)のスクロブリングサポートを追加します
- **Lumia Stream**: [Lumia Stream](https://lumiastream.com/)のサポートを追加します
- **Genius 歌詞**: ほとんどの曲に歌詞サポートを追加します
- **Music Together**: プレイリストを他の人と共有します。ホストが曲を再生すると、他の全員が同じ曲を聴くことができます
- **ナビゲーション**: お気に入りのブラウザのように、UIに直接統合された次/前のナビゲーション矢印を追加します
- **Googleログインなし**: インターフェースからGoogleログインボタンとリンクを削除します
- **通知**: 曲の再生が開始されると通知を表示しますWindowsでは[インタラクティブ通知](https://user-images.githubusercontent.com/78568641/114102651-63ce0e00-98d0-11eb-9dfe-c5a02bb54f9c.png)が利用可能です)
- **ピクチャーインピクチャー**: アプリをピクチャーインピクチャーモードに切り替えることができます
- **再生速度**: 速く聴いたり、遅く聴いたりできます!曲の速度を制御するスライダーを追加します
- **正確な音量**: カスタムHUDとカスタマイズ可能な音量ステップを使用して、マウスホイール/ホットキーで音量を正確に制御します
- **ショートカット (& MPRIS)**: 再生用のグローバルホットキー(再生/一時停止/次/前を設定し、メディアキーをオーバーライドしてメディアOSDを無効にし、Ctrl/CMD + Fで検索を有効にし、LinuxのMPRISサポートを有効にし、[上級ユーザー](https://github.com/th-ch/youtube-music/issues/106#issuecomment-952156902)向けの[カスタムホットキー](https://github.com/Araxeus/youtube-music/blob/1e591d6a3df98449bcda6e63baab249b28026148/providers/song-controls.js#L13-L50)を追加します
- **嫌いな曲をスキップ**: 嫌いな曲をスキップします
- **無音部分をスキップ**: 無音部分を自動的にスキップします
- [**SponsorBlock**](https://github.com/ajayyy/SponsorBlock): イントロ/アウトロなどの音楽以外の部分や、曲が再生されていないミュージックビデオの部分を自動的にスキップします
- **同期歌詞**: [LRClib](https://lrclib.net)のようなプロバイダーを使用して、曲に同期した歌詞を提供します
- **タスクバーメディアコントロール**: [Windowsタスクバー](https://user-images.githubusercontent.com/78568641/111916130-24a35e80-8a82-11eb-80c8-5021c1aa27f4.png)から再生を制御します
- **TouchBar**: macOS用のカスタムTouchBarレイアウト
- **Tuna OBS**: [OBS](https://obsproject.com/)のプラグイン[Tuna](https://obsproject.com/forum/resources/tuna.843/)との統合
- **ビデオ品質チェンジャー**: ビデオオーバーレイの[ボタン](https://user-images.githubusercontent.com/78568641/138574366-70324a5e-2d64-4f6a-acdd-dc2a2b9cecc5.png)を使用してビデオ品質を変更できます
- **ビデオ切り替え**: ビデオ/ソングモードを切り替える[ボタン](https://user-images.githubusercontent.com/28893833/173663950-63e6610e-a532-49b7-9afa-54cb57ddfc15.png)を追加します。オプションでビデオタブ全体を削除することもできます
- **ビジュアライザー**: プレイヤーにさまざまな音楽ビジュアライザーを追加します
## 翻訳
[Hosted Weblate](https://hosted.weblate.org/projects/youtube-music/)で翻訳を手伝うことができます。
<a href="https://hosted.weblate.org/engage/youtube-music/">
<img src="https://hosted.weblate.org/widget/youtube-music/i18n/multi-auto.svg" alt="翻訳状況" />
<img src="https://hosted.weblate.org/widget/youtube-music/i18n/287x66-black.png" alt="翻訳状況 2" />
</a>
## ダウンロード
[最新リリース](https://github.com/th-ch/youtube-music/releases/latest)を確認して、最新バージョンをすばやく見つけることができます。
### Arch Linux
AURから[`youtube-music-bin`](https://aur.archlinux.org/packages/youtube-music-bin)パッケージをインストールします。AURのインストール手順については、この[wikiページ](https://wiki.archlinux.org/index.php/Arch_User_Repository#Installing_packages)を参照してください。
### macOS
Homebrewを使用してアプリをインストールできます[cask定義](https://github.com/th-ch/homebrew-youtube-music)を参照)。
```bash
brew install th-ch/youtube-music/youtube-music
```
アプリを手動でインストールし、アプリの起動時に「破損しているため開けません」というエラーが表示される場合は、ターミナルで次のコマンドを実行します。
```bash
/usr/bin/xattr -cr /Applications/YouTube\ Music.app
```
### Windows
[Scoopパッケージマネージャー](https://scoop.sh)を使用して、[`extras`バケット](https://github.com/ScoopInstaller/Extras)から`youtube-music`パッケージをインストールできます。
```bash
scoop bucket add extras
scoop install extras/youtube-music
```
または、Windows 11の公式CLIパッケージマネージャーである[Winget](https://learn.microsoft.com/en-us/windows/package-manager/winget/)を使用して、`th-ch.YouTubeMusic`パッケージをインストールできます。
*注: 「不明な発行元」からのものであるため、Microsoft Defender SmartScreenがインストールをブロックする場合があります。これは、GitHubで手動でダウンロードした後に実行ファイル.exeを実行しようとする場合にも当てはまります。*
```bash
winget install th-ch.YouTubeMusic
```
#### ネットワーク接続なしでインストールする方法 (Windows)
- [リリースページ](https://github.com/th-ch/youtube-music/releases/latest)で_デバイスのアーキテクチャ_に対応する`*.nsis.7z`ファイルをダウンロードします。
- `x64`は64ビットWindows用
- `ia32`は32ビットWindows用
- `arm64`はARM64 Windows用
- リリースページでインストーラーをダウンロードします。(`*-Setup.exe`)
- それらを**同じディレクトリ**に配置します。
- インストーラーを実行します。
## テーマ
CSSファイルを読み込んでアプリケーションの外観を変更できますオプション > 視覚的調整 > テーマ)。
いくつかの事前定義されたテーマは、https://github.com/kerichdev/themes-for-ytmdesktop-player で利用できます。
## 開発
```bash
git clone https://github.com/th-ch/youtube-music
cd youtube-music
pnpm install --frozen-lockfile
pnpm dev
```
## 独自のプラグインを作成する
プラグインを使用すると、次のことができます。
- アプリを操作する - Electronの`BrowserWindow`がプラグインハンドラーに渡されます
- HTML/CSSを操作してフロントエンドを変更する
### プラグインの作成
`src/plugins/YOUR-PLUGIN-NAME`にフォルダーを作成します。
- `index.ts`: プラグインのメインファイル
```typescript
import style from './style.css?inline'; // スタイルをインラインとしてインポート
import { createPlugin } from '@/utils';
export default createPlugin({
name: 'プラグインラベル',
restartNeeded: true, // 値がtrueの場合、ytmusicは再起動ダイアログを表示します
config: {
enabled: false,
}, // カスタム設定
stylesheets: [style], // カスタムスタイル
menu: async ({ getConfig, setConfig }) => {
// すべての*ConfigメソッドはPromise<T>でラップされています
const config = await getConfig();
return [
{
label: 'メニュー',
submenu: [1, 2, 3].map((value) => ({
label: `値 ${value}`,
type: 'radio',
checked: config.value === value,
click() {
setConfig({ value });
},
})),
},
];
},
backend: {
start({ window, ipc }) {
window.maximize();
// レンダラープラグインと通信できます
ipc.handle('some-event', () => {
return 'hello';
});
},
// 設定が変更されたときに発生します
onConfigChange(newConfig) { /* ... */ },
// プラグインが無効になったときに発生します
stop(context) { /* ... */ },
},
renderer: {
async start(context) {
console.log(await context.ipc.invoke('some-event'));
},
// レンダラーでのみ使用可能なフック
onPlayerApiReady(api: YoutubePlayer, context: RendererContext) {
// プラグイン設定を簡単に設定
context.setConfig({ myConfig: api.getVolume() });
},
onConfigChange(newConfig) { /* ... */ },
stop(_context) { /* ... */ },
},
preload: {
async start({ getConfig }) {
const config = await getConfig();
},
onConfigChange(newConfig) {},
stop(_context) {},
},
});
```
### 一般的な使用例
- カスタムCSSの挿入: 同じフォルダーに`style.css`ファイルを作成し、次のようにします。
```typescript
// index.ts
import style from './style.css?inline'; // スタイルをインラインとしてインポート
import { createPlugin } from '@/utils';
export default createPlugin({
name: 'プラグインラベル',
restartNeeded: true, // 値がtrueの場合、ytmusicは再起動ダイアログを表示します
config: {
enabled: false,
}, // カスタム設定
stylesheets: [style], // カスタムスタイル
renderer() {} // レンダラーフックを定義
});
```
- HTMLを変更したい場合:
```typescript
import { createPlugin } from '@/utils';
export default createPlugin({
name: 'プラグインラベル',
restartNeeded: true, // 値がtrueの場合、ytmusicは再起動ダイアログを表示します
config: {
enabled: false,
}, // カスタム設定
renderer() {
// ログインボタンを削除
document.querySelector(".sign-in-link.ytmusic-nav-bar").remove();
} // レンダラーフックを定義
});
```
- フロントエンドとバックエンドの通信: ElectronのipcMainモジュールを使用して行うことができます。`index.ts`ファイルと`sponsorblock`プラグインの例を参照してください。
## ビルド
1. リポジトリをクローン
2. [このガイド](https://pnpm.io/installation)に従って`pnpm`をインストール
3. `pnpm install --frozen-lockfile`を実行して依存関係をインストール
4. `pnpm build:OS`を実行
- `pnpm dist:win` - Windows
- `pnpm dist:linux` - Linux (amd64)
- `pnpm dist:linux:deb-arm64` - Linux (Debian用arm64)
- `pnpm dist:linux:rpm-arm64` - Linux (Fedora用arm64)
- `pnpm dist:mac` - macOS (amd64)
- `pnpm dist:mac:arm64` - macOS (arm64)
[electron-builder](https://github.com/electron-userland/electron-builder)を使用して、macOS、Linux、およびWindows用のアプリをビルドします。
## プロダクションプレビュー
```bash
pnpm start
```
## テスト
```bash
pnpm test
```
[Playwright](https://playwright.dev/)を使用してアプリをテストします。
## ライセンス
MIT © [th-ch](https://github.com/th-ch/youtube-music)
## FAQ
### アプリのメニューが表示されないのはなぜですか?
`メニューを非表示`オプションがオンの場合 - <kbd>alt</kbd>キー(またはアプリ内メニュープラグインを使用している場合は<kbd>\`</kbd> [バックティック]キー)でメニューを表示できます

View File

@ -3,7 +3,7 @@
# 유튜브 뮤직 (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)
[![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/.eslintrc.js)
[![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/)
@ -20,7 +20,7 @@
</a>
</div>
다른 언어로 읽어보세요: [🏴 영어](../../README.md), [🇰🇷 한국인](./README-ko.md), [🇫🇷 프랑스 국민](./README-fr.md), [🇮🇸 아이슬란드어](./README-is.md), [🇪🇸 스페인 사람](./README-es.md), [🇷🇺 러시아인](./README-ru.md)
다른 언어로 읽어보세요: [🏴 영어](../../README.md), [🇰🇷 한국인](./README-ko.md), [🇫🇷 프랑스 국민](./README-fr.md), [🇮🇸 아이슬란드어](./README-is.md), [🇪🇸 스페인 사람](./README-es.md), [🇷🇺 러시아인](./README-ru.md), [🇧🇷 포르투갈어](./README-pt.md), [🇯🇵 일본어](./README-ja.md)
**유튜브 뮤직의 Electron 래퍼; 기능:**

375
docs/readme/README-pt.md Normal file
View File

@ -0,0 +1,375 @@
<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/.eslintrc.js)
[![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>
![Screenshot](/web/screenshot.png "Screenshot")
<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">
</a>
</div>
Leia em outros idiomas: [🏴 Inglês](../../README.md), [🇰🇷 Coreano](./README-ko.md), [🇫🇷 Francês](./README-fr.md), [🇮🇸 Islandês](./README-is.md), [🇪🇸 Espanhol](./README-es.md), [🇷🇺 Russo](./README-ru.md), [🇧🇷 Português](./README-pt.md)
**Wrapper do Electron para o YouTube Music com os seguintes recursos:**
- Visual e comportamento nativos: Mantém a interface original do YouTube Music.
- Estrutura para plugins personalizados: Adapte o YouTube Music às suas necessidades (estilo, conteúdo, funcionalidades). Ative/desative plugins com um clique.
## Imagem de demonstração
| Tela do Player (tema de cores do álbum e luz ambiente) |
|:---------------------------------------------------------------------------------------------------------:|
|![Screenshot1](https://github.com/th-ch/youtube-music/assets/16558115/53efdf73-b8fa-4d7b-a235-b96b91ea77fc)|
## Conteúdo
- [Recursos](#recursos)
- [Plugins disponíveis](#plugins-disponíveis)
- [Tradução](#tradução)
- [Download](#download)
- [Arch Linux](#arch-linux)
- [MacOS](#macos)
- [Windows](#windows)
- [Como instalar sem conexão à internet? (no Windows)](#como-instalar-sem-conexão-à-internet-no-windows)
- [Temas](#temas)
- [Dev](#dev)
- [Crie seus próprios plugins](#crie-seus-próprios-plugins)
- [Criando um plugin](#criando-um-plugin)
- [Casos de uso comuns](#casos-de-uso-comuns)
- [Compilar](#compilar)
- [Prévia de produção](#prévia-de-produção)
- [Testes](#testes)
- [Licença](#licença)
- [Perguntas Frequentes](#perguntas-frequentes)
## Recursos:
- **Confirmação automática quando pausado** (Sempre ativado): desativa
o popup ["Continuar assistindo?"](https://user-images.githubusercontent.com/61631665/129977894-01c60740-7ec6-4bf0-9a2c-25da24491b0e.png)
que pausa a música após um certo tempo
- E mais...
## Plugins disponíveis:
- **Bloqueador de anúncios**: Bloqueia todos os anúncios e rastreamentos automaticamente
- **Ações de Álbum**: Adiciona botões para Remover dislike, Dar dislike, Curtir e Remover curtida em todas as músicas de uma playlist ou álbum
- **Tema de cores do álbum**: Aplica um tema dinâmico e efeitos visuais baseados na paleta de cores do álbum
- **Modo ambiente**: Cria um efeito de iluminação projetando cores suaves do vídeo no fundo da tela
- **Compressor de áudio**: Aplica compressão ao áudio (reduz o volume das partes mais altas e aumenta o das mais baixas)
- **Barra de navegação desfocada**: Torna a barra de navegação transparente e desfocada
- **Contornar restrições de idade**: Ignora a verificação de idade do YouTube
- **Seletor de legendas**: Ativa legendas
- **Barra lateral compacta**: Mantém a barra lateral sempre no modo compacto
- **Crossfade**: Transição suave entre músicas
- **Desativar reprodução automática**: Faz com que todas as músicas iniciem no modo "pausado"
- **[Discord](https://discord.com/) Rich Presence**: Mostra para seus amigos o que você está ouvindo com [Rich Presence](https://user-images.githubusercontent.com/28219076/104362104-a7a0b980-5513-11eb-9744-bb89eabe0016.png)
- **Downloader**: Baixa MP3 [diretamente da interface](https://user-images.githubusercontent.com/61631665/129977677-83a7d067-c192-45e1-98ae-b5a4927393be.png) [(youtube-dl)](https://github.com/ytdl-org/youtube-dl)
- **Equalizador**: Adiciona filtros para aumentar ou reduzir faixas específicas de frequência (ex: reforço de graves)
- **Volume exponencial**: Torna o controle de volume [exponencial](https://greasyfork.org/en/scripts/397686-youtube-music-fix-volume-ratio/) para facilitar a seleção de volumes mais baixos
- **Menu integrado**: [Dá às barras um visual elegante e escuro](https://user-images.githubusercontent.com/78568641/112215894-923dbf00-8c29-11eb-95c3-3ce15db27eca.png)
> (veja [este post](https://github.com/th-ch/youtube-music/issues/410#issuecomment-952060709) se tiver problemas para acessar o menu após ativar este plugin e a opção de ocultar menu)
- **Scrobbler**: Adiciona suporte para scrobbling no [Last.fm](https://www.last.fm/) e [ListenBrainz](https://listenbrainz.org/)
- **Lumia Stream**: Adiciona suporte para [Lumia Stream](https://lumiastream.com/)
- **Letras Genius**: Adiciona suporte a letras para a maioria das músicas
- **Música Juntos**: Compartilhe uma playlist com outros. Quando o host toca uma música, todos ouvem a mesma música
- **Navegação**: Botões de avançar/voltar integrados diretamente na interface, como no seu navegador favorito
- **Sem login do Google**: Remove botões e links de login do Google da interface
- **Notificações**: Exibe uma notificação quando uma música começa a tocar ([notificações interativas](https://user-images.githubusercontent.com/78568641/114102651-63ce0e00-98d0-11eb-9dfe-c5a02bb54f9c.png) disponíveis no Windows)
- **Picture-in-picture**: Permite alternar o aplicativo para o modo picture-in-picture
- **Velocidade de reprodução**: Ouça rápido, ouça devagar! [Adiciona um controle deslizante para ajustar a velocidade](https://user-images.githubusercontent.com/61631665/129976003-e55db5ba-bf42-448c-a059-26a009775e68.png)
- **Volume preciso**: Controle o volume com precisão usando roda do mouse/atalhos, com HUD personalizado e níveis de volume customizáveis
- **Atalhos (& MPRIS)**: Permite configurar teclas de atalho globais para controle (play/pause/próxima/anterior) + desativa [OSD de mídia](https://user-images.githubusercontent.com/84923831/128601225-afa38c1f-dea8-4209-9f72-0f84c1dd8b54.png) sobrescrevendo teclas de mídia + ativa Ctrl/CMD + F para busca + suporte a MPRIS no Linux para teclas de mídia + [atalhos personalizados](https://github.com/Araxeus/youtube-music/blob/1e591d6a3df98449bcda6e63baab249b28026148/providers/song-controls.js#L13-L50) para [usuários avançados](https://github.com/th-ch/youtube-music/issues/106#issuecomment-952156902)
- **Pular músicas marcadas com "não gostei"**: Ignora automaticamente músicas que você deu dislike
- **Pular silêncios**: Ignora automaticamente seções silenciosas
- [**SponsorBlock**](https://github.com/ajayyy/SponsorBlock): Ignora automaticamente partes não musicais como introduções/outros ou partes de clipes onde a música não está tocando
- **Letras sincronizadas**: Fornece letras sincronizadas para músicas, usando serviços como [LRClib](https://lrclib.net)
- **Controle de mídia na barra de tarefas**: Controle a reprodução pela [barra de tarefas do Windows](https://user-images.githubusercontent.com/78568641/111916130-24a35e80-8a82-11eb-80c8-5021c1aa27f4.png)
- **TouchBar**: Layout personalizado para a TouchBar do macOS
- **Tuna OBS**: Integração com o plugin [Tuna](https://obsproject.com/forum/resources/tuna.843/) do [OBS](https://obsproject.com/)
- **Seletor de qualidade de vídeo**: Permite alterar a qualidade do vídeo com um [botão](https://user-images.githubusercontent.com/78568641/138574366-70324a5e-2d64-4f6a-acdd-dc2a2b9cecc5.png) na sobreposição do vídeo
- **Alternar vídeo**: Adiciona um [botão](https://user-images.githubusercontent.com/28893833/173663950-63e6610e-a532-49b7-9afa-54cb57ddfc15.png) para alternar entre modos Vídeo/Música. Pode também remover completamente a aba de vídeo
- **Visualizador**: Diferentes visualizadores de música
## Tradução
Você pode ajudar com as traduções no [Hosted Weblate](https://hosted.weblate.org/projects/youtube-music/).
<a href="https://hosted.weblate.org/engage/youtube-music/">
<img src="https://hosted.weblate.org/widget/youtube-music/i18n/multi-auto.svg" alt="status da tradução" />
<img src="https://hosted.weblate.org/widget/youtube-music/i18n/287x66-black.png" alt="status da tradução 2" />
</a>
## Download
Você pode verificar o [último lançamento](https://github.com/th-ch/youtube-music/releases/latest) para encontrar rapidamente a versão mais recente.
### Arch Linux
Instale o pacote [`youtube-music-bin`](https://aur.archlinux.org/packages/youtube-music-bin) do AUR. Para instruções de instalação do AUR, consulte esta [página da wiki](https://wiki.archlinux.org/index.php/Arch_User_Repository#Installing_packages).
### macOS
Você pode instalar o aplicativo usando Homebrew (veja a [definição do cask](https://github.com/th-ch/homebrew-youtube-music)):
```bash
brew install th-ch/youtube-music/youtube-music
```
Se você instalar o aplicativo manualmente e receber o erro "is damaged and cant be opened." ao abrir o app, execute o seguinte no Terminal:
```bash
/usr/bin/xattr -cr /Applications/YouTube\ Music.app
```
### Windows
Você pode usar o [gerenciador de pacotes Scoop](https://scoop.sh) para instalar o pacote `youtube-music` do [`extras bucket`](https://github.com/ScoopInstaller/Extras).
```bash
scoop bucket add extras
scoop install extras/youtube-music
```
Alternativamente, você pode usar o [Winget](https://learn.microsoft.com/en-us/windows/package-manager/winget/), o gerenciador de pacotes CLI oficial do Windows 11, para instalar o pacote `th-ch.YouTubeMusic`.
*Nota: O Microsoft Defender SmartScreen pode bloquear a instalação por ser de um "publicador desconhecido". Isso também acontece na instalação manual ao tentar executar o arquivo .exe após download manual aqui no GitHub (mesmo arquivo).*
```bash
winget install th-ch.YouTubeMusic
```
#### Como instalar sem conexão à internet? (no Windows)
- Baixe o arquivo `*.nsis.7z` para _sua arquitetura de dispositivo_ na [página de lançamentos](https://github.com/th-ch/youtube-music/releases/latest).
- `x64` para Windows 64-bit
- `ia32` para Windows 32-bit
- `arm64` para Windows ARM64
- Baixe o instalador na página de lançamentos (`*-Setup.exe`)
- Coloque os arquivos no **mesmo diretório**
- Execute o instalador
## Temas
Você pode carregar arquivos CSS para alterar a aparência do aplicativo (Opções > Ajustes Visuais > Temas).
Alguns temas pré-definidos estão disponíveis em https://github.com/kerichdev/themes-for-ytmdesktop-player.
## Dev
```bash
git clone https://github.com/th-ch/youtube-music
cd youtube-music
pnpm install --frozen-lockfile
pnpm dev
```
## Crie seus próprios plugins
Usando plugins, você pode:
- Manipular o aplicativo - o `BrowserWindow` do electron é passado para o manipulador de plugins
- Alterar a interface manipulando o HTML/CSS
### Criando um plugin
Crie uma pasta em `src/plugins/NOMBRE-DEL-PLUGIN`:
- `index.ts`: o arquivo principal do plugin
```typescript
import style from './style.css?inline'; // importar estilo como inline
import { createPlugin } from '@/utils';
export default createPlugin({
name: "Plugin Label",
restartNeeded: true, // se true, o ytmusic mostra diálogo de reinício
config: {
enabled: false,
}, // sua configuração personalizada
stylesheets: [style], // seu estilo personalizado
menu: async ({ getConfig, setConfig }) => {
// Todos os métodos *Config são wrappers 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();
// você pode se comunicar com o plugin renderer
ipc.handle("some-event", () => {
return "hello";
});
},
// disparado quando a configuração muda
onConfigChange(newConfig) { /* ... */ },
// disparado quando o plugin é desativado
stop(context) { /* ... */ },
},
renderer: {
async start(context) {
console.log(await context.ipc.invoke("some-event"));
},
// Hook disponível apenas no renderer
onPlayerApiReady(api: YoutubePlayer, context: RendererContext) {
// establecer la configuración del plugin fácilmente
context.setConfig({ myConfig: api.getVolume() });
},
onConfigChange(newConfig) { /* ... */ },
stop(_context) { /* ... */ },
},
preload: {
async start({ getConfig }) {
const config = await getConfig();
},
onConfigChange(newConfig) {},
stop(_context) {},
},
});
```
### Casos de uso comuns
- **Injetar CSS personalizado**: crie um arquivo `style.css` na mesma pasta e então:
```typescript
// index.ts
import style from './style.css?inline'; // importa estilo como inline
import { createPlugin } from '@/utils';
export default createPlugin({
name: 'Plugin Label',
restartNeeded: true, // se true, o ytmusic mostrará um diálogo de reinício
config: {
enabled: false,
}, // sua configuração personalizada
stylesheets: [style], // seu estilo personalizado
renderer() {} // define o hook renderer
});
```
- Se quiser alterar o HTML:
```typescript
import { createPlugin } from '@/utils';
export default createPlugin({
name: 'Plugin Label',
restartNeeded: true, // se true, o ytmusic mostrará o diálogo de reinício
config: {
enabled: false,
}, // sua configuração personalizada
renderer() {
// Remove o botão de login
document.querySelector(".sign-in-link.ytmusic-nav-bar").remove();
} // define o hook renderer
});
```
- Comunicação entre front-end e back-end: pode ser feita usando o módulo ipcMain do Electron. Consulte o arquivo `index.ts` e o exemplo no plugin `sponsorblock`.
## Compilar
1. Clone o repositório
2. Siga [este guia](https://pnpm.io/installation) para instalar o `pnpm`
3. Execute `pnpm install --frozen-lockfile` para instalar as dependências
4. Execute `pnpm build:OS`
- `pnpm dist:win` - Windows
- `pnpm dist:linux` - Linux (amd64)
- `pnpm dist:linux:deb-arm64` - Linux (arm64 para Debian)
- `pnpm dist:linux:rpm-arm64` - Linux (arm64 para Fedora)
- `pnpm dist:mac` - macOS (amd64)
- `pnpm dist:mac:arm64` - macOS (arm64)
Compila o aplicativo para macOS, Linux e Windows,
usando [electron-builder](https://github.com/electron-userland/electron-builder).
## Prévia de Produção
```bash
pnpm start
```
## Testes
```bash
pnpm test
```
Utiliza [Playwright](https://playwright.dev/) para testar o aplicativo.
## Licença
MIT © [th-ch](https://github.com/th-ch/youtube-music)
## Perguntas Frequentes
### Por que o menu do aplicativo não aparece?
Se a opção `Ocultar menu` estiver ativada - você pode exibir o menu com a tecla <kbd>alt</kbd> (ou <kbd>\`</kbd> [acento grave] se estiver usando o plugin in-app-menu)

View File

@ -3,7 +3,7 @@
# 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)
[![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/.eslintrc.js)
[![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/)
@ -21,7 +21,7 @@
</a>
</div>
Прочтите это на других языках: [🏴 Английский](../../README.md), [🇰🇷 корейский](./README-ko.md), [🇫🇷 Французский](./README-fr.md), [🇮🇸 исландский](./README-is.md), [🇪🇸 испанский](./README-es.md), [🇷🇺 Русский](./README-ru.md)
Прочтите это на других языках: [🏴 Английский](../../README.md), [🇰🇷 корейский](./README-ko.md), [🇫🇷 Французский](./README-fr.md), [🇮🇸 исландский](./README-is.md), [🇪🇸 испанский](./README-es.md), [🇷🇺 Русский](./README-ru.md), [🇧🇷 Португальский](./README-pt.md)
**Клиент для YouTube Music основанный на Electron с поддержкой:**

View File

@ -2,7 +2,7 @@
"name": "youtube-music",
"desktopName": "com.github.th_ch.youtube_music",
"productName": "YouTube Music",
"version": "3.8.0",
"version": "3.9.0",
"description": "YouTube Music Desktop App - including custom plugins",
"main": "./dist/main/index.js",
"license": "MIT",
@ -222,8 +222,8 @@
},
"pnpm": {
"overrides": {
"vite": "6.2.3",
"node-gyp": "11.1.0",
"vite": "6.3.3",
"node-gyp": "11.2.0",
"xml2js": "0.6.2",
"node-fetch": "3.3.2",
"@electron/universal": "2.0.2",
@ -243,11 +243,11 @@
"@ffmpeg.wasm/main": "0.12.0",
"@floating-ui/dom": "1.6.13",
"@foobar404/wave": "2.0.5",
"@ghostery/adblocker-electron": "2.5.0",
"@ghostery/adblocker-electron-preload": "2.5.0",
"@hono/node-server": "1.14.0",
"@ghostery/adblocker-electron": "2.5.1",
"@ghostery/adblocker-electron-preload": "2.5.1",
"@hono/node-server": "1.14.1",
"@hono/swagger-ui": "0.5.1",
"@hono/zod-openapi": "0.19.2",
"@hono/zod-openapi": "0.19.5",
"@hono/zod-validator": "0.4.3",
"@jellybrick/dbus-next": "0.10.3",
"@jellybrick/electron-better-web-request": "1.0.4",
@ -263,22 +263,23 @@
"conf": "13.1.0",
"custom-electron-prompt": "1.5.8",
"deepmerge-ts": "7.1.5",
"delay": "6.0.0",
"electron-debug": "4.1.0",
"electron-is": "3.0.0",
"electron-localshortcut": "3.2.1",
"electron-store": "10.0.1",
"electron-unhandled": "4.0.1",
"electron-updater": "6.3.9",
"electron-updater": "6.6.2",
"es-hangul": "2.3.2",
"fast-average-color": "9.5.0",
"fast-equals": "5.2.2",
"filenamify": "6.0.0",
"hanja": "1.1.4",
"happy-dom": "17.4.4",
"hono": "4.7.5",
"hono": "4.7.7",
"howler": "2.2.4",
"html-to-text": "9.0.5",
"i18next": "24.2.3",
"i18next": "25.0.1",
"jimp": "1.6.0",
"keyboardevent-from-electron-accelerator": "2.0.0",
"keyboardevents-areequal": "0.2.2",
@ -287,7 +288,7 @@
"kuroshiro-analyzer-kuromoji": "1.1.0",
"lazy-var": "2.2.2",
"node-html-parser": "7.0.1",
"node-id3": "0.2.8",
"node-id3": "0.2.9",
"peerjs": "1.5.4",
"pinyin": "4.0.0-alpha.2",
"segmentit": "2.0.3",
@ -301,46 +302,46 @@
"ts-morph": "25.0.1",
"vudio": "2.1.1",
"x11": "2.3.0",
"youtubei.js": "13.3.0",
"zod": "3.24.2"
"youtubei.js": "13.4.0",
"zod": "3.24.3"
},
"devDependencies": {
"@eslint/js": "9.23.0",
"@eslint/js": "9.25.1",
"@malept/flatpak-bundler": "0.4.0",
"@playwright/test": "1.51.1",
"@playwright/test": "1.52.0",
"@stylistic/eslint-plugin-js": "4.2.0",
"@total-typescript/ts-reset": "0.6.1",
"@types/electron-localshortcut": "3.1.3",
"@types/howler": "2.2.12",
"@types/html-to-text": "9.0.4",
"@types/semver": "7.5.8",
"@types/semver": "7.7.0",
"@types/trusted-types": "2.0.7",
"bufferutil": "4.0.9",
"builtin-modules": "5.0.0",
"cross-env": "7.0.3",
"del-cli": "6.0.0",
"discord-api-types": "0.37.119",
"electron": "35.1.0",
"discord-api-types": "0.38.1",
"electron": "34.5.3",
"electron-builder": "26.0.12",
"electron-builder-squirrel-windows": "26.0.12",
"electron-devtools-installer": "4.0.0",
"electron-vite": "3.1.0",
"esbuild": "0.25.1",
"eslint": "9.23.0",
"eslint-config-prettier": "10.1.1",
"esbuild": "0.25.3",
"eslint": "9.25.1",
"eslint-config-prettier": "10.1.2",
"eslint-import-resolver-exports": "1.0.0-beta.5",
"eslint-import-resolver-typescript": "4.2.4",
"eslint-import-resolver-typescript": "4.3.4",
"eslint-plugin-import": "2.31.0",
"eslint-plugin-prettier": "5.2.5",
"glob": "11.0.1",
"node-gyp": "11.1.0",
"playwright": "1.51.1",
"rollup": "4.37.0",
"typescript": "5.8.2",
"typescript-eslint": "8.28.0",
"eslint-plugin-prettier": "5.2.6",
"glob": "11.0.2",
"node-gyp": "11.2.0",
"playwright": "1.52.0",
"rollup": "4.40.0",
"typescript": "5.8.3",
"typescript-eslint": "8.31.0",
"utf-8-validate": "6.0.5",
"vite": "6.2.3",
"vite-plugin-inspect": "11.0.0",
"vite": "6.3.3",
"vite-plugin-inspect": "11.0.1",
"vite-plugin-resolve": "2.5.2",
"vite-plugin-solid": "2.11.6",
"ws": "8.18.1"

1355
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -683,6 +683,7 @@
"listenbrainz": {
"token": "أدخل رمز مستخدم ListenBrainz"
},
"scrobble-alternative-title": "استخدم عناوين بديلة",
"scrobble-other-media": "Scrobble الوسائط الأخرى"
},
"name": "أداة تتبع الاستماع",
@ -767,6 +768,10 @@
"label": "اجعل كلمات الأغنية متزامنة بشكل مثالي",
"tooltip": "احسب بدقة الملي ثانية عرض السطر التالي (قد يكون له تأثير طفيف على الأداء)"
},
"romanization": {
"label": "اجعل الكلمات رومانية",
"tooltip": "إذا كانت كلمات الأغنية بلغة مختلفة، حاول عرض نسخة بالحروف اللاتينية."
},
"show-lyrics-even-if-inexact": {
"label": "أظهر كلمات الأغنية حتى لو كانت غير دقيقة",
"tooltip": "إذا لم يتم العثور على الأغنية، سوف يتم البحث مرة أخرى باستخدام استعلام بحث مختلف.\nقد لا تكون النتيجة من المحاولة الثانية دقيقة."
@ -799,6 +804,10 @@
"description": "التكامل مع الإضافة\" Tuna\" الخاصة بـ OBS",
"name": "إضافة Tuna OBS"
},
"unobtrusive-player": {
"description": "يمنع المشغل من الظهور عند تشغيل أغنية",
"name": "مشغل غير مزعج"
},
"video-toggle": {
"description": "يضيف زرًا للتبديل بين وضع الفيديو/الأغنية. يمكن أيضًا اختياريًا إزالة علامة الفيديو بالكامل",
"menu": {

View File

@ -160,7 +160,7 @@
"theme": {
"dialog": {
"button": {
"cancel": "Откажи",
"cancel": "Отказ",
"remove": "Премахни"
},
"remove-theme": "Сигурни ли сте, че искате да премахнете персонализираната тема?",
@ -182,24 +182,664 @@
"new": "НОВО"
},
"view": {
"label": "Изглед",
"label": "Преглед",
"submenu": {
"force-reload": "Презареди принудително",
"force-reload": "Принудително презареждане",
"reload": "Презареди",
"reset-zoom": "Истински размер",
"toggle-fullscreen": "Превключи на пълен екран",
"zoom-in": "Увеличи",
"zoom-out": "Намали"
"reset-zoom": "Действителен размер",
"toggle-fullscreen": "Превключване на цял екран",
"zoom-in": "Увеличаване",
"zoom-out": "Намаляване"
}
}
},
"tray": {
"next": "Следващ",
"play-pause": "Пусни/Паузирай",
"play-pause": "Възпроизвеждане/Пауза",
"previous": "Предишен",
"quit": "Изход",
"restart": "Рестартирай приложението",
"show": "Покажи прозорец"
"show": "Покажи прозорец",
"tooltip": {
"default": "YouTube Музика",
"with-song-info": "YouTube Музика: {{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": "Плавен преход",
"submenu": {
"during": "{{interpolationTime}} секунди"
}
},
"use-fullscreen": {
"label": "Използване на цял екран"
}
},
"name": "Атмосферен режим"
},
"amuse": {
"description": "Добавя поддръжка на YouTube Music за джаджата Amuse Now Play от 6K Labs",
"name": "Забавление",
"response": {
"query": "Сървърът на Amuse API работи. Изпратете GET /query за информация за песента."
}
},
"api-server": {
"description": "Добавя API сървър за контрол на плейъра",
"dialog": {
"request": {
"buttons": {
"allow": "Разрешавам",
"deny": "Отказвам"
},
"message": "Позволяваш ли {{ID}} {{origin}} да достъпва API-то?",
"title": "Заявка за авторизация на API"
}
},
"menu": {
"auth-strategy": {
"label": "Стратегия за авторизация",
"submenu": {
"auth-at-first": {
"label": "Авторизиране при първата заявка"
},
"none": {
"label": "Без авторизация"
}
}
},
"hostname": {
"label": "Име на хост"
},
"port": {
"label": "Порт"
}
},
"name": "API сървър [Бета]",
"prompt": {
"hostname": {
"label": "Въведете името на хоста (като 0.0.0.0) за API сървъра:",
"title": "Име на хост"
},
"port": {
"label": "Въведете порта за API сървъра:",
"title": "Порт"
}
}
},
"audio-compressor": {
"description": "Прилага компресия на аудиото (намалява обема на най-силните части от сигнала и увеличава обема на най-тихите части)",
"name": "Аудио компресор"
},
"blur-nav-bar": {
"description": "Прави навигационната лента прозрачна и размазана",
"name": "Размазанa навигационна лента"
},
"bypass-age-restrictions": {
"description": "Избягване на възрастова верификация на YouTube",
"name": "Избягване на възрастови ограничения"
},
"captions-selector": {
"description": "Избор на надписи за аудио тракове в YouTube Music",
"menu": {
"autoload": "Автоматично избиране на последно използвания надпис",
"disable-captions": "Без надписи по подразбиране"
},
"name": "Избор на надписи",
"prompt": {
"selector": {
"label": "Език на надписи: {{language}}",
"none": "Нищо",
"title": "Избери език на надписите"
}
},
"templates": {
"title": "Отвори избора на надписи"
}
},
"compact-sidebar": {
"description": "Винаги настройвай страничната лента в компактен режим",
"name": "Компактна странична лента"
},
"crossfade": {
"description": "Плавно преминаване през песните",
"menu": {
"advanced": "Разширено"
},
"name": "Плавно преминаване [Beta]",
"prompt": {
"options": {
"multi-input": {
"fade-in-duration": "Продължителност на преливането (милисекунди)",
"fade-out-duration": "Продължителност на затихването (милисекунди)",
"fade-scaling": {
"label": "Скалиране на избледняването",
"linear": "Линейно",
"logarithmic": "Логаритмично"
},
"seconds-before-end": "Преливане N секунди преди края"
},
"title": "Опции за преливане"
}
}
},
"disable-autoplay": {
"description": "Започва песента в паузиран режим",
"menu": {
"apply-once": "Важи само на стартиране"
},
"name": "Изключи автоматичното пускане"
},
"discord": {
"backend": {
"already-connected": "Опит за свързване с активна връзка",
"connected": "Свързано с Discord",
"disconnected": "Прекъсната връзка с Discord"
},
"description": "Покажи на приятелите си какво слушате с Rich Presence",
"menu": {
"auto-reconnect": "Автоматично повторно свързване",
"clear-activity": "Изчистване на активността",
"clear-activity-after-timeout": "Изчистване на активността след изтичане на времето",
"connected": "Свързано",
"disconnected": "Прекъснато",
"hide-duration-left": "Скрий оставащото време",
"hide-github-button": "Скрий бутона за линк към GitHub",
"play-on-youtube-music": "Възпроизведи в YouTube Music",
"set-inactivity-timeout": "Задай таймаут за неактивност"
},
"name": "Дискорд Разширен статус",
"prompt": {
"set-inactivity-timeout": {
"label": "Въведете таймаута за неактивност в секунди:",
"title": "Задайте таймаут за неактивност"
}
}
},
"downloader": {
"backend": {
"dialog": {
"error": {
"buttons": {
"ok": "ОК"
},
"message": "Ох! Извинявайте, изтеглянето не успя…",
"title": "Грешка при изтегляне!"
},
"start-download-playlist": {
"buttons": {
"ok": "ОК"
},
"detail": "({{playlistSize}} песни)",
"message": "Изтегляне на плейлист {{playlistTitle}}",
"title": "Изтеглянето започна"
}
},
"feedback": {
"conversion-progress": "Конвертиране: {{percent}}%",
"converting": "Превръщане…",
"done": "Готово: {{filePath}}",
"download-info": "Изтегляне на {{artist}} - {{title}} [{{videoId}}",
"download-progress": "Изтегляне: {{percent}}%",
"downloading": "Изтегляне…",
"downloading-counter": "Изтегляне {{current}}/{{total}}…",
"downloading-playlist": "Изтегляне на плейлист \"{{playlistTitle}}\" - {{playlistSize}} песни ({{playlistId}})",
"error-while-downloading": "Грешка при изтегляне на \"{{author}} - {{title}}\": {{error}}",
"folder-already-exists": "Папката {{playlistFolder}} вече съществува",
"getting-playlist-info": "Получаване на информация за плейлист…",
"loading": "Зареждане…",
"playlist-has-only-one-song": "Плейлистът съдържа само един елемент, изтегля се директно",
"playlist-id-not-found": "Не е намерен ID на плейлист",
"playlist-is-empty": "Плейлистът е празен",
"playlist-is-mix-or-private": "Грешка при получаване на информация за плейлист: уверете се, че не е частен или \"Смесено за вас\" плейлист\n\n{{error}}",
"preparing-file": "Подготвяне на файла…",
"saving": "Записване…",
"trying-to-get-playlist-id": "Опитвам се да получа ID на плейлист: {{playlistId}}",
"video-id-not-found": "Видео не е намерено",
"writing-id3": "Записване на ID3 тагове…"
}
},
"description": "Изтегля MP3 / източниково аудио директно от интерфейса",
"menu": {
"choose-download-folder": "Изберете папка за изтегляне",
"download-finish-settings": {
"label": "Изтегляне при завършване",
"prompt": {
"last-percent": "След x процента",
"last-seconds": "Последни x секунди",
"title": "Конфигурирайте кога да изтеглите"
},
"submenu": {
"advanced": "Разширени настройки",
"enabled": "Активирано",
"mode": "Режим на време",
"percent": "Процент",
"seconds": "Секунди"
}
},
"download-playlist": "Изтегляне на плейлист",
"presets": "Предварително зададени настройки",
"skip-existing": "Пропусни съществуващите файлове"
},
"name": "Изтегляч",
"renderer": {
"can-not-update-progress": "Не може да се актуализира напредъкът"
},
"templates": {
"button": "Изтегляне"
}
},
"equalizer": {
"description": "Добавя еквалайзер към плеъра",
"menu": {
"presets": {
"label": "Предварителни настройки",
"list": {
"bass-booster": "Усилвател на басове"
}
}
},
"name": "Еквалайзер"
},
"exponential-volume": {
"description": "Прави плъзгача за сила на звука експоненциален, така че да е по-лесно да се избират по-ниски нива на звук.",
"name": "Експоненциален звук"
},
"in-app-menu": {
"description": "Придава на меню баровете стилен, тъмен или с цвят на албума вид",
"menu": {
"hide-dom-window-controls": "Скрий контролните елементи на DOM прозореца"
},
"name": "Меню в приложението"
},
"lumiastream": {
"description": "Добавя поддръжка за Lumia Stream",
"name": "Lumia Stream [Бета]"
},
"lyrics-genius": {
"description": "Добавя поддръжка за текстове за повечето песни",
"menu": {
"romanized-lyrics": "Романизирани текстове"
},
"name": "Текстове от Genius",
"renderer": {
"fetched-lyrics": "Изтеглени текстове от Genius"
}
},
"music-together": {
"description": "Сподели плейлист с други. Когато хостът пусне песен, всички останали ще чуят същата песен",
"dialog": {
"enter-host": "Въведи ID на хоста"
},
"internal": {
"save": "Запазване",
"track-source": "Източник на трак",
"unknown-user": "Неизвестен потребител"
},
"menu": {
"click-to-copy-id": "Копирай ID на хост",
"close": "Затвори Music Together",
"connected-users": "Свързани потребители",
"disconnect": "Прекъсни Music Together",
"empty-user": "Няма свързани потребители",
"host": "Хост на Music Together",
"join": "Присъедини се към Music Together",
"permission": {
"all": "Позволи на гостите да управляват плейлист и плеър",
"host-only": "Само хостът може да управлява плейлист и плеър",
"playlist": "Позволи на гостите да управляват плейлист"
},
"set-permission": "Промени разрешението за управление",
"status": {
"disconnected": "Прекъснато",
"guest": "Свързан като гост",
"host": "Свързан като хост"
}
},
"name": "Music Together [Бета]",
"toast": {
"add-song-failed": "Неуспешно добавяне на песен",
"closed": "Music Together е затворена",
"disconnected": "Music Together е прекъсната",
"host-failed": "Неуспешно хостване на Music Together",
"id-copied": "ID на хоста е копиран в клипборда",
"id-copy-failed": "Неуспешно копиране на ID на хоста в клипборда",
"join-failed": "Неуспешно присъединяване към Music Together",
"joined": "Присъединен към Music Together",
"permission-changed": "Разрешението за Music Together е променено на \"{{permission}}\"",
"remove-song-failed": "Неуспешно премахване на песен",
"user-connected": "{{name}} се присъедини към Music Together",
"user-disconnected": "{{name}} напусна Music Together"
}
},
"navigation": {
"description": "Навигационни стрелки Напред/Назад, директно интегрирани в интерфейса, както в любимия ви браузър",
"name": "Навигация"
},
"no-google-login": {
"description": "Премахни бутоните за вход с Google и връзките от интерфейса",
"name": "Няма вход с Google"
},
"notifications": {
"description": "Показване на известие при стартиране на песен (интерактивни известия са налични за Windows)",
"menu": {
"interactive": "Интерактивни известия",
"interactive-settings": {
"label": "Интерактивни настройки",
"submenu": {
"hide-button-text": "Скрий текста на бутоните",
"refresh-on-play-pause": "Обновяване при Възпроизвеждане/Пауза",
"tray-controls": "Отваряне/Затваряне при клик в тавата"
}
},
"priority": "Приоритет на известията",
"toast-style": "Стил на toast (кратки изскачащи известия)",
"unpause-notification": "Показване на известие при възобновяване"
},
"name": "Известия"
},
"picture-in-picture": {
"description": "Позволява превключване на приложението в режим картинка във картинка",
"menu": {
"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": "Добавяне на скробблинг поддръжка (last.fm, Listenbrainz и т.н.)",
"dialog": {
"lastfm": {
"auth-failed": {
"message": "Грешка при удостоверяване с Last.fm\nСкрий изкачащия прозорец до следващо пускане.",
"title": "Грешка при удостоверяване"
}
}
},
"menu": {
"lastfm": {
"api-settings": "Настройки за Last.fm API"
},
"listenbrainz": {
"token": "Въведете ListenBrainz потребителски токен"
},
"scrobble-alternative-title": "Използвай алтернативни заглавия",
"scrobble-other-media": "Скробъл на други медии"
},
"name": "Скробълър",
"prompt": {
"lastfm": {
"api-key": "Last.fm API ключ",
"api-secret": "Last.fm API тайна"
},
"listenbrainz": {
"token": {
"label": "Въведете вашия ListenBrainz потребителски токен:",
"title": "ListenBrainz токен"
}
}
}
},
"shortcuts": {
"description": "Позволява задаване на глобални бързи клавиши за възпроизвеждане (пускане/пауза/следваща/предишна), изключване на медиен OSD чрез презаписване на медийни клавиши, включване на Ctrl/CMD + F за търсене, включване на Linux 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": "SponsorBlock"
},
"synced-lyrics": {
"description": "Предоставя синхронизирани текстове на песни, използвайки доставчици като LRClib.",
"errors": {
"fetch": "⚠️\tВъзникна грешка при извличане на текста.\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": "Изчислете до милисекунда показването на следващия ред (може да има малък ефект върху производителността)"
},
"romanization": {
"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": "Управление на възпроизвеждането от лентата с задачи на Windows",
"name": "Управление на медията от лентата със задачи"
},
"touchbar": {
"description": "Добавя уиджет за TouchBar за потребители на macOS",
"name": "TouchBar"
},
"tuna-obs": {
"description": "Интеграция с плъгина Tuna за OBS",
"name": "Tuna OBS"
},
"unobtrusive-player": {
"description": "Предотвратява изскачането на плеъра при възпроизвеждане на песен",
"name": "Неназойлив плеър"
},
"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": "Визуализатор"
}
}
}

133
src/i18n/resources/bs.json Normal file
View File

@ -0,0 +1,133 @@
{
"common": {
"console": {
"plugins": {
"execute-failed": "Greška u izvršavanju dodatka {{pluginName}}::{{contextName}}",
"executed-at-ms": "Dodatak {{pluginName}}::{{contextName}} se izvršio za {{ms}}ms",
"initialize-failed": "Greška prilikom inicijalizacije dodatka \"{{pluginName}}\"",
"load-all": "Učitavanje svih dodataka",
"load-failed": "Greška u učitavanju dodatka \"{{pluginName}}\"",
"loaded": "Dodatak \"{{pluginName}}\" učitan",
"unload-failed": "Greška prilikom onesposobljavanja dodatka \"{{pluginName}}\"",
"unloaded": "Dodatak \"{{pluginName}}\" ugašen"
}
}
},
"language": {
"code": "ba",
"local-name": "Bosanski",
"name": "Bosnian"
},
"main": {
"console": {
"did-finish-load": {
"dev-tools": "Završeno učitavanje. DevTools otvoren"
},
"i18n": {
"loaded": "i18n učitan"
},
"second-instance": {
"receive-command": "Comanda primljena preko protokola \"{{command}}\""
},
"theme": {
"css-file-not-found": "CSS datoteka \"{{cssFile}}\" ne postoji, ignorišem"
},
"unresponsive": {
"details": "Greška u aplikaciji!\n{{error}}"
},
"when-ready": {
"clearing-cache-after-20s": "Čistim predmemoriju aplikacije"
},
"window": {
"tried-to-render-offscreen": "Prozor se pokušao prikazati van okvira ekrana, windowSize={{windowSize}}, displaySize={{displaySize}}, position={{position}}"
}
},
"dialog": {
"hide-menu-enabled": {
"detail": "Meni je sakriven, koristite 'Alt' da ga prikazete (ili 'ESC' ako koristite meni u aplikaciji)",
"message": "Sakrivanje menija je uključeno",
"title": "Meni sakriven"
},
"need-to-restart": {
"buttons": {
"later": "Kasnije",
"restart-now": "Pokreni ponovo odmah"
},
"detail": "\"{{pluginName}}\" dodatak zahtjeva ponovno pokretanje kako bi se uključio",
"message": "\"{{pluginName}}\" potrebno je resetovat",
"title": "Restart je potreban"
},
"unresponsive": {
"buttons": {
"quit": "Napusti",
"relaunch": "Ponovo otvori",
"wait": "Pricekajte"
},
"detail": "Izvinjavamo se zbog zabune! molimo vas da odaberete sta zelite uciniti",
"message": "Aplikacija ne reagira",
"title": "Prozor ne reagira"
},
"update-available": {
"buttons": {
"disable": "Ugasite Nadogradnje",
"download": "Skinuti",
"ok": "OK"
},
"detail": "Nova verzija je dostupna i može biti skinuta na {{downloadLink}}",
"message": "Nova verzija je dostupna",
"title": "Azuriranje dostupno"
}
},
"menu": {
"about": "O nama",
"navigation": {
"label": "Plejer",
"submenu": {
"copy-current-url": "Kopirajte trenutni link",
"go-back": "Idi Nazad",
"go-forward": "Idi Naprijed",
"quit": "Izadji",
"restart": "Restartujte Aplikaciju"
}
},
"options": {
"label": "Opcije",
"submenu": {
"advanced-options": {
"label": "Napredne opcije",
"submenu": {
"auto-reset-app-cache": "Resetuje kes memoriju kad se aplikacija pokrene",
"disable-hardware-acceleration": "Ugasite hardversko ubrzanje",
"edit-config-json": "Uredite config.json",
"override-user-agent": "Nadjacaj User-Agent",
"restart-on-config-changes": "Ponovno pokretanje nakon promjena konfiguracije",
"set-proxy": {
"label": "Postavi proxy",
"prompt": {
"label": "Unesite adresu proxyja: (ostavite prazno za onemogućavanje)",
"placeholder": "Primjer: SOCKS5://127.0.0.1:9999",
"title": "Postavi proxy"
}
},
"toggle-dev-tools": "Uključi/isključi DevTools"
}
},
"always-on-top": "Uvijek na vrhu",
"auto-update": "Automatski Update",
"hide-menu": {
"dialog": {
"message": "Meni će biti skriven pri sljedećem pokretanju, koristite [Alt] da ga prikažete (ili upotrijebite [`] ako koristite meni u aplikaciji)",
"title": "Sakrij meni omogućen"
},
"label": "Sakrij meni"
},
"language": {
"dialog": {
"message": "Jezik će se promijeniti nakon ponovnog pokretanja"
}
}
}
}
}
}
}

View File

@ -768,6 +768,10 @@
"label": "Den Songtext perfekt synchronisieren",
"tooltip": "Auf die Millisekunde genau berechnen, wann die nächste Zeile angezeigt werden soll (Kann Einfluss auf die Leistung haben)"
},
"romanization": {
"label": "Lateinische Umschrift anzeigen",
"tooltip": "Wenn der Liedtext in einer anderen Schrift ist, zeige nach Möglichkeit eine Version in lateinischer Schrift an."
},
"show-lyrics-even-if-inexact": {
"label": "Zeige die Liedtexte, auch wenn sie ungenau sind.",
"tooltip": "Die Erweiterung sucht mit anderen Suchparameter nochmals, wenn der Song nicht gefunden wurde.\nEs kann sein, dass das Ergebnis von der zweiten Anfrage nicht genau ist."
@ -800,6 +804,10 @@
"description": "Integration mit dem OBS-Plugin Tuna",
"name": "Tuna OBS"
},
"unobtrusive-player": {
"description": "Verhindert das Aufpoppen des Spielers während ein Song gespielt wird",
"name": "Unauffälliger Player"
},
"video-toggle": {
"description": "Fügt einen Knopf hinzu, um zwischen Video-/Liedmodus zu wechseln. kann auch genutzt werden, um den ganzen Videoabschnitt zu entfernen",
"menu": {

View File

@ -59,11 +59,11 @@
},
"unresponsive": {
"buttons": {
"quit": "Έξοδος",
"quit": "Παραιτηθείτε",
"relaunch": "Επανεκκίνηση",
"wait": "Περιμένετε"
},
"detail": "Λυπούμαστε για την ταλαιπωρία! Παρακαλούμε επιλέξτε τι να κάνετε:",
"detail": "Λυπούμαστε για την ταλαιπωρία! Παρακαλώ επιλέξτε τι να κάνετε:",
"message": "Η εφαρμογή δεν ανταποκρίνεται",
"title": "Το παράθυρο δεν ανταποκρίνεται"
},
@ -75,7 +75,7 @@
},
"detail": "Μια νέα έκδοση είναι διαθέσιμη και μπορεί να ληφθεί από τον σύνδεσμο {{downloadLink}}",
"message": "Μια νέα έκδοση είναι διαθέσιμη",
"title": "Υπάρχει διαθέσιμη ενημέρωση"
"title": "Διατίθεται ενημέρωση"
}
},
"menu": {
@ -84,8 +84,8 @@
"label": "Πλοήγηση",
"submenu": {
"copy-current-url": "Αντιγραφή τρέχουσας διεύθυνσης URL",
"go-back": ήγαινε πίσω",
"go-forward": "Πήγαινε μπροστά",
"go-back": "Πίσω",
"go-forward": "Εμπρός",
"quit": "Έξοδος",
"restart": "Επανεκκίνηση εφαρμογής"
}
@ -94,17 +94,17 @@
"label": "Επιλογές",
"submenu": {
"advanced-options": {
"label": "Προηγμένες επιλογές",
"label": "Επιλογές για προχωρημένους",
"submenu": {
"auto-reset-app-cache": "Επαναφορά της cache της εφαρμογής κατά την εκκίνηση της εφαρμογής",
"auto-reset-app-cache": "Επαναφορά μνήμης cache εφαρμογής όταν η εφαρμογή ξεκινά",
"disable-hardware-acceleration": "Απενεργοποίηση επιτάχυνσης υλικού",
"edit-config-json": "Επεξεργασία του config.json",
"override-user-agent": "Αντικατάσταση του User-Agent",
"restart-on-config-changes": "Επανεκκίνηση κατά τις αλλαγές στο config",
"restart-on-config-changes": "Επανεκκίνηση σε αλλαγές του config",
"set-proxy": {
"label": "Ορισμός διακομιστή μεσολάβησης (proxy)",
"label": "Ορισμός μεσολάβησης",
"prompt": {
"label": "Εισαγωγή διεύθυνσης διακομιστή μεσολάβησης (proxy): (αφήστε κενό για απενεργοποίηση)",
"label": "Εισαγωγή διεύθυνσης μεσολάβησης: (αφήστε κενό για απενεργοποίηση)",
"placeholder": "Παράδειγμα: SOCKS5://127.0.0.1:9999",
"title": "Ορισμός μεσολάβησης"
}
@ -112,14 +112,14 @@
"toggle-dev-tools": "Εναλλαγή DevTools"
}
},
"always-on-top": "Πάντα επάνω",
"auto-update": "Αυτόματη Ενημέρωση",
"always-on-top": "Πάντα σε πρώτο πλάνο",
"auto-update": "Αυτόματη ενημέρωση",
"hide-menu": {
"dialog": {
"message": "Το μενού θα κρυφτεί στην επόμενη εκκίνηση, χρησιμοποιήστε [Alt] για να το εμφανίσετε (ή το πλήκτρο backtick [`] αν χρησιμοποιείτε το μενού εφαρμογής)",
"title": "Η Δυνατότητα Απόκρυψης του Μενού ενεργοποιήθηκε"
},
"label": "Απόκρυψη Μενού"
"label": "Απόκρυψη μενού"
},
"language": {
"dialog": {
@ -131,7 +131,7 @@
"to-help-translate": "Θέλετε να βοηθήσετε στη μετάφραση; Κάντε κλικ εδώ"
}
},
"resume-on-start": "Συνέχιση του τελευταίου τραγουδιού όταν η εφαρμογή ξεκινά",
"resume-on-start": "Συνέχιση τελευταίου τραγουδιού όταν η εφαρμογή ξεκινά",
"single-instance-lock": "Κλείδωμα Μοναδικής Εκδοχής",
"start-at-login": "Έναρξη κατά την σύνδεση",
"starting-page": {
@ -148,11 +148,11 @@
}
},
"visual-tweaks": {
"label": "Τροποποιήσεις Εμφάνισης",
"label": "Τροποποιήσεις εμφάνισης",
"submenu": {
"like-buttons": {
"default": "Default",
"force-show": "Αναγκαστική Εμφάνιση",
"default": "Προεπιλογή",
"force-show": "Επιβολή εμφάνισης",
"hide": "Απόκρυψη",
"label": "Μου αρέσει"
},
@ -169,7 +169,7 @@
"label": "Θέμα",
"submenu": {
"import-css-file": "Εισαγωγή προσαρμοσμένου αρχείου CSS",
"no-theme": "No theme"
"no-theme": "Κανένα θέμα"
}
}
}
@ -184,10 +184,10 @@
"view": {
"label": "Προβολή",
"submenu": {
"force-reload": "Αναγκαστική Eπαναφόρτωση",
"force-reload": "Επιβολή επαναφόρτωσης",
"reload": "Επαναφόρτωση",
"reset-zoom": "Πραγματικό μέγεθος",
"toggle-fullscreen": "Εναλλαγή Πλήρους Οθόνης",
"toggle-fullscreen": "Εναλλαγή πλήρους οθόνης",
"zoom-in": "Μεγέθυνση",
"zoom-out": "Σμίκρυνση"
}
@ -201,15 +201,15 @@
"restart": "Επανεκκίνηση εφαρμογής",
"show": "Εμφάνιση παραθύρου",
"tooltip": {
"default": "YouTube Music",
"with-song-info": "YouTube Music: {{artist}} - {{title}}"
"default": "YouTube Μουσική",
"with-song-info": "YouTube Μουσική: {{artist}} - {{title}}"
}
}
},
"plugins": {
"ad-speedup": {
"description": "Εαν παίξει διαφήμιση κάνει σίγαση του ήχου και θέτει την ταχύτητα αναπαραγωγής στο 16x",
"name": "Γρήγορη Προώθηση Διαφημίσεων"
"description": "Εάν παίξει διαφήμιση κάνει σίγαση του ήχου και θέτει την ταχύτητα αναπαραγωγής στο 16x",
"name": "Γρήγορη προώθηση διαφημίσεων"
},
"adblocker": {
"description": "Αποκλεισμός όλων των διαφημίσεων και tracker",
@ -226,16 +226,16 @@
"description": "Εφαρμόζει ένα δυναμικό θέμα και εφέ με βάση τη χρωματική παλέτα του άλμπουμ",
"menu": {
"color-mix-ratio": {
"label": "Αναλογία μίξης χρώματος",
"label": "Αναλογία μίξης χρωμάτων",
"submenu": {
"percent": "{{ratio}}%"
}
}
},
"name": "Album Color Theme"
"name": "Θέμα χρώματος άλμπουμ"
},
"ambient-mode": {
"description": "Εφαρμόζει ένα εφέ φωτισμού ρίχνοντας απαλά χρώματα από το βίντεο, στο φόντο της οθόνης σας.",
"description": "Εφαρμόζει ένα εφέ φωτισμού ρίχνοντας απαλά χρώματα από το βίντεο στο φόντο της οθόνης σας",
"menu": {
"blur-amount": {
"label": "Ένταση θαμπώματος",
@ -268,6 +268,7 @@
}
},
"smoothness-transition": {
"label": "Ομαλή μετάβαση",
"submenu": {
"during": "Σε {{interpolationTime}} δευτερόλεπτα"
}
@ -275,39 +276,112 @@
"use-fullscreen": {
"label": "Χρήση πλήρους οθόνης"
}
},
"name": "Λειτουργία περιβάλλοντος"
},
"amuse": {
"description": "Προσθέτει υποστήριξη μουσικής YouTube για το widget Amuse now playing από την 6K Labs",
"name": "Διασκέδαση",
"response": {
"query": "Ο διακομιστής Amuse API εκτελείται. GET /query για να λάβετε πληροφορίες για το τραγούδι."
}
},
"audio-compressor": {
"description": "Συμπίεση ήχου (μειώνει την ένταση των πιο δυνατών τμημάτων του κύματος και αυξάνει την ένταση των πιο μαλακών τμημάτων)"
},
"blur-nav-bar": {
"description": "Κάνει τη γραμμή πλοήγησης διαφανή και θολή"
},
"bypass-age-restrictions": {
"description": "Παράκαμψη της επαλήθευσης ηλικίας του YouTube"
},
"captions-selector": {
"api-server": {
"description": "Προσθέτει έναν διακομιστή API για τον έλεγχο του παίκτη",
"dialog": {
"request": {
"buttons": {
"allow": "Αποδοχή",
"deny": "Άρνηση"
},
"message": "Επιτρέψτε {{ID}} ({{origin}}) να έχει πρόσβαση στο API;",
"title": "Αίτημα εξουσιοδότησης API"
}
},
"menu": {
"auth-strategy": {
"label": "Στρατηγική εξουσιοδότησης",
"submenu": {
"auth-at-first": {
"label": "Εξουσιοδότηση στο πρώτο αίτημα"
},
"none": {
"label": "Χωρίς εξουσιοδότηση"
}
}
},
"hostname": {
"label": "Όνομα κεντρικού υπολογιστή"
},
"port": {
"label": "Θύρα"
}
},
"name": "Διακομιστής API [Beta]",
"prompt": {
"selector": {
"none": "None"
"hostname": {
"label": "Εισάγετε το όνομα κεντρικού υπολογιστή (όπως 0.0.0.0.0) για τον διακομιστή API:",
"title": "Όνομα κεντρικού υπολογιστή"
},
"port": {
"label": "Εισάγετε τη θύρα για το διακομιστή API:",
"title": "Θύρα"
}
}
},
"audio-compressor": {
"description": "Συμπίεση ήχου (μειώνει την ένταση των πιο δυνατών τμημάτων του κύματος και αυξάνει την ένταση των πιο μαλακών τμημάτων)",
"name": "Συμπιεστής ήχου"
},
"blur-nav-bar": {
"description": "θέτει τη γραμμή πλοήγησης διαφανή και θολή",
"name": "Θόλωμα γραμμής πλοήγησης"
},
"bypass-age-restrictions": {
"description": "Παράκαμψη επαλήθευσης ηλικίας στο YouTube",
"name": "Παράκαμψη ηλικιακών περιορισμών"
},
"captions-selector": {
"description": "Επιλογέας λεζάντας για μουσικά κομμάτια ήχου του YouTube",
"menu": {
"autoload": "Αυτόματη επιλογή της τελευταίας χρησιμοποιούμενης λεζάντας",
"disable-captions": "Χωρίς λεζάντες από προεπιλογή"
},
"name": "Επιλογέας λεζάντες",
"prompt": {
"selector": {
"label": "Τρέχουσα γλώσσα λεζάντας: {{language}}",
"none": "None",
"title": "Επιλογή γλώσσας λεζάντας"
}
},
"templates": {
"title": "Ανοίξτε τον επιλογέα λεζάντας"
}
},
"compact-sidebar": {
"description": "Να είναι πάντα συμπαγές το sidebar"
"description": "Να είναι πάντα συμπαγές το sidebar",
"name": "Συμπαγής πλευρική μπάρα"
},
"crossfade": {
"description": "Crossfade μεταξύ τραγουδιών",
"menu": {
"advanced": "Για προχωρημένους"
},
"name": "Crossfade [Beta]",
"prompt": {
"options": {
"multi-input": {
"fade-in-duration": "Διάρκεια εξασθένισης (ms)",
"fade-out-duration": "Διάρκεια σβήσιμου (ms)",
"fade-scaling": {
"label": "Κλιμάκωση εξασθένισης",
"linear": "Γραμμική",
"logarithmic": "Λογαριθμική"
}
}
},
"seconds-before-end": "Crossfade N δευτερόλεπτα πριν το τέλος"
},
"title": "Επιλογές Crossfade"
}
}
},
@ -319,13 +393,29 @@
"name": "Απενεργοποίηση αυτόματης αναπαραγωγής"
},
"discord": {
"backend": {
"already-connected": "Προσπάθεια σύνδεσης με ενεργή σύνδεση",
"connected": "Συνδεδεμένος με το Discord",
"disconnected": "Αποσυνδεδεμένος από το Discord"
},
"description": "Δείξτε στους φίλους σας τι ακούτε με το Rich Presence",
"menu": {
"auto-reconnect": "Αυτόματη επανασύνδεση",
"clear-activity": "Εκκαθάριση δραστηριότητας",
"clear-activity-after-timeout": "Εκκαθάριση δραστηριότητας μετά από χρονικό όριο",
"connected": "Συνδεδεμένο",
"disconnected": "Αποσυνδεδεμένο",
"hide-duration-left": "Απόκρυψη της διάρκειας που απομένει",
"hide-github-button": "Απόκρυψη του συνδέσμου προς GitHub",
"hide-github-button": "Απόκρυψη κουμπιού συνδέσμου GitHub",
"play-on-youtube-music": "Αναπαραγωγή στο YouTube Music",
"set-inactivity-timeout": "Ορισμός χρονικού ορίου αδράνειας"
},
"name": "Discord Πλούσια παρουσία",
"prompt": {
"set-inactivity-timeout": {
"label": "Εισαγωγή χρονικού ορίου αδράνειας σε δευτερόλεπτα:",
"title": "Ορισμός χρονικού ορίου αδράνειας"
}
}
},
"downloader": {
@ -335,7 +425,8 @@
"buttons": {
"ok": "OK"
},
"title": "Error in download!"
"message": "Ωχ! Λυπούμαστε, η λήψη απέτυχε…",
"title": "Σφάλμα στη λήψη!"
},
"start-download-playlist": {
"buttons": {
@ -349,59 +440,168 @@
"feedback": {
"conversion-progress": "Μετατροπή: {{percent}}%",
"converting": "Μετατροπή…",
"done": "Τέλος: {{filePath}}",
"download-info": "Λήψη του {{artist}} - {{title}} [{{videoId}}",
"download-progress": "Λήψη: {{percent}}%",
"downloading": "Λήψη…",
"downloading-counter": "Λήψη {{current}}/{{total}}…",
"downloading-playlist": "Λήψη της λίστας αναπαραγωγής \"{{playlistTitle}}\" - {{playlistSize}} τραγούδια ({{playlistId}})",
"error-while-downloading": "Σφάλμα λήψης \"{{author}} - {{title}}\": {{error}}",
"folder-already-exists": "Ο φάκελος {{playlistFolder}} υπάρχει ήδη",
"getting-playlist-info": "Λήψη πληροφοριών λίστας αναπαραγωγής…",
"loading": "Φόρτωση…",
"playlist-has-only-one-song": "Η λίστα αναπαραγωγής έχει μόνο ένα στοιχείο, κατεβάζοντάς το απευθείας",
"playlist-id-not-found": "Δεν βρέθηκε ID λίστας αναπαραγωγής",
"playlist-is-empty": "Η λίστα αναπραγωγής είναι άδεια",
"playlist-is-mix-or-private": "Σφάλμα λήψης πληροφοριών λίστας αναπαραγωγής: βεβαιωθείτε ότι δεν είναι ιδιωτική ή «Μικτή για εσάς» λίστα αναπαραγωγής\n\n{{error}}",
"preparing-file": "Προετοιμασία αρχείου…",
"saving": "Αποθήκευση…",
"video-id-not-found": "Το βίντεο δεν βρέθηκε"
"trying-to-get-playlist-id": "Προσπαθώ να πάρω το αναγνωριστικό της λίστας αναπαραγωγής: {{playlistId}}",
"video-id-not-found": "Το βίντεο δεν βρέθηκε",
"writing-id3": "Εγγραφή ετικετών ID3…"
}
},
"description": "Λήψεις MP3 / ήχου πηγής απευθείας από τη διεπαφή",
"menu": {
"choose-download-folder": "Επιλογή φακέλου λήψης",
"download-finish-settings": {
"label": "Λήψη στο τέλος",
"prompt": {
"last-seconds": "Τελευταία x δευτερόλεπτα"
"last-percent": "Μετά από x ποσοστό",
"last-seconds": "Τελευταία x δευτερόλεπτα",
"title": "Ρύθμιση του πότε θα γίνεται λήψη"
},
"submenu": {
"advanced": "Για προχωρημένους",
"enabled": "Ενεργοποιημένο",
"mode": "Λειτουργία χρόνου",
"percent": "Ποσοστό",
"seconds": "Δευτερόλεπτα"
}
},
"download-playlist": "Λήψη λίστας αναπαραγωγής",
"presets": "Προεπιλογές",
"skip-existing": "Παράλειψη υπάρχοντων αρχείων"
},
"name": "Κατεβαστής",
"renderer": {
"can-not-update-progress": "Δεν μπορεί να ενημερωθεί η πρόοδος"
},
"templates": {
"button": "Λήψη"
}
},
"equalizer": {
"description": "Προσθέτει έναν ισοσταθμιστή στο πρόγραμμα αναπαραγωγής",
"menu": {
"presets": {
"label": "Προεπιλογές",
"list": {
"bass-booster": "Ενίσχυση μπάσου"
}
}
},
"name": "Ισοσταθμιστής"
},
"exponential-volume": {
"description": "Κάνει το ρυθμιστικό έντασης εκθετικό, ώστε να είναι ευκολότερη η επιλογή χαμηλότερων εντάσεων.",
"name": "Εκθετικός όγκος"
},
"in-app-menu": {
"description": "Δίνει στις γραμμές μενού μια φανταχτερή, σκοτεινή ή άλμπουμ-χρωματική εμφάνιση",
"menu": {
"hide-dom-window-controls": "Απόκρυψη στοιχείων ελέγχου παραθύρου DOM"
},
"name": "Μενού εντός της εφαρμογής"
},
"lumiastream": {
"description": "Προσθέτει υποστήριξη Lumia Stream",
"name": "Lumia Stream [Beta]"
},
"lyrics-genius": {
"description": "Προσθέτει υποστήριξη στίχων για τα περισσότερα τραγούδια",
"menu": {
"romanized-lyrics": "Ρομαντικοποιημένοι στίχοι"
},
"name": "Στίχοι Genius",
"renderer": {
"fetched-lyrics": "Στίχοι για το Genius"
}
},
"music-together": {
"description": "Μοιραστείτε μια λίστα αναπαραγωγής με άλλους. Όταν ο οικοδεσπότης παίζει ένα τραγούδι, όλοι οι άλλοι θα ακούσουν το ίδιο τραγούδι",
"dialog": {
"enter-host": "Εισαγωγή ID κεντρικού υπολογιστή"
},
"internal": {
"save": "Αποθήκευση",
"track-source": "Πηγή διαδρομής",
"unknown-user": "Άγνωστος χρήστης"
},
"menu": {
"connected-users": "Συνδεδεμένοι χρήστες"
"click-to-copy-id": "Αντιγραφή ID κεντρικού υπολογιστή",
"close": "Κλείσιμο Music Together",
"connected-users": "Συνδεδεμένοι χρήστες",
"disconnect": "Αποσύνδεση Music Together",
"empty-user": "Κανένας συνδεδεμένος χρήστης",
"host": "Κεντρικός υπολογιστής Music Together",
"join": "Γίνετε μέλος της Μουσικής Μαζί",
"permission": {
"all": "Επιτρέψτε στους επισκέπτες να ελέγχουν τη λίστα αναπαραγωγής και τον παίκτη",
"host-only": "Μόνο ο οικοδεσπότης μπορεί να ελέγχει τη λίστα αναπαραγωγής και τον παίκτη",
"playlist": "Επιτρέψτε στους επισκέπτες να ελέγχουν τη λίστα αναπαραγωγής"
},
"set-permission": "Άδεια ελέγχου αλλαγής",
"status": {
"disconnected": "Αποσυνδεδεμένο",
"guest": "Συνδεδεμένος ως επισκέπτης",
"host": "Συνδεδεμένος ως οικοδεσπότης"
}
},
"name": "Music Together [Beta]",
"toast": {
"add-song-failed": "Απέτυχε η προσθήκη τραγουδιού",
"remove-song-failed": "Απέτυχε η αφαίρεση τραγουδιού"
"closed": "Το Music Together έκλεισε",
"disconnected": "Το Music Together αποσυνδέθηκε",
"host-failed": "Απέτυχε να φιλοξενήσει το Μουσική Μαζί",
"id-copied": "Το ID κεντρικού υπολογιστή αντιγράφηκε στο πρόχειρο",
"id-copy-failed": "Απέτυχε η αντιγραφή ID κεντρικού υπολογιστή στο πρόχειρο",
"join-failed": "Απέτυχε να ενταχθεί στη Μουσική Μαζί",
"joined": "Ενωμένη μουσική μαζί",
"permission-changed": "Η άδεια «Μουσική Μαζί» άλλαξε σε «{{permission}}»",
"remove-song-failed": "Απέτυχε η αφαίρεση τραγουδιού",
"user-connected": "{{name}} εντάχθηκε στη Μουσική Μαζί",
"user-disconnected": "{{name}} αριστερά Μουσική Μαζί"
}
},
"navigation": {
"description": "Βέλη πλοήγησης Επόμενο/Πίσω ενσωματωμένα απευθείας στο περιβάλλον εργασίας, όπως στο αγαπημένο σας πρόγραμμα περιήγησης",
"name": "Πλοήγηση"
},
"no-google-login": {
"description": "Αφαίρεση των κουμπιών και των συνδέσμων σύνδεσης Google από το περιβάλλον εργασίας",
"name": "No Google Login"
},
"notifications": {
"description": "Εμφάνιση ειδοποίησης όταν ξεκινάει η αναπαραγωγή ενός τραγουδιού (οι διαδραστικές ειδοποιήσεις είναι διαθέσιμες στα Windows)",
"menu": {
"interactive": "Διαδραστικές ειδοποιήσεις",
"interactive-settings": {
"label": "Διαδραστικές ρυθμίσεις",
"submenu": {
"hide-button-text": "Απόκρυψη κειμένου κουμπιού",
"refresh-on-play-pause": "Ανανέωση σε Αναπαραγωγή/Παύση",
"tray-controls": "Άνοιγμα/κλείσιμο με κλικ στο δίσκο"
}
},
"priority": "Προτεραιότητα κοινοποίησης",
"toast-style": "Στυλ τοστ",
"unpause-notification": "Εμφάνιση ειδοποίησης κατά την κατάργηση της παύσης"
},
"name": "Ειδοποιήσεις"
},
"picture-in-picture": {
"description": "Επιτρέπει την εναλλαγή της εφαρμογής σε λειτουργία εικόνας σε εικόνα",
"menu": {
"always-on-top": "Πάντα σε πρώτο πλάνο",
"hotkey": {
@ -409,26 +609,47 @@
"prompt": {
"keybind-options": {
"hotkey": "Πλήκτρο πρόσβασης"
}
},
"label": "Επιλέξτε ένα πλήκτρο συντόμευσης για να ενεργοποιήσετε την εικόνα στην εικόνα",
"title": "Πλήκτρο Hotkey Εικόνα-σε-Εικόνα"
}
},
"save-window-position": "Αποθήκευση θέσης παραθύρου",
"save-window-size": "Αποθήκευση μεγέθους παραθύρου"
"save-window-size": "Αποθήκευση μεγέθους παραθύρου",
"use-native-pip": "Χρήση εγγενούς 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": "Βήματα έντασης"
}
}
},
@ -436,46 +657,189 @@
"backend": {
"dialog": {
"quality-changer": {
"detail": "Τρέχουσα ποιότητα: {{quality}}"
"detail": "Τρέχουσα ποιότητα: {{quality}}",
"message": "Επιλογή ποιότητας βίντεο:",
"title": "Επιλογή ποιότητας βίντεο"
}
}
},
"description": "Επιτρέπει την αλλαγή της ποιότητας βίντεο με ένα κουμπί στην επικάλυψη βίντεο",
"name": "Αλλαγή ποιότητας βίντεο"
},
"scrobbler": {
"description": "Προσθήκη υποστήριξης scrobbling (κ.λπ. last.fm, Listenbrainz)",
"dialog": {
"lastfm": {
"auth-failed": {
"message": "Απέτυχε η πιστοποίηση ταυτότητας στο Last.fm\nΚρύψτε το αναδυόμενο παράθυρο μέχρι την επόμενη επανεκκίνηση.",
"title": "Αποτυχία ελέγχου ταυτότητας"
}
}
},
"menu": {
"lastfm": {
"api-settings": "Ρυθμίσεις API Last.fm"
},
"listenbrainz": {
"token": "Εισάγετε το διακριτικό χρήστη ListenBrainz"
},
"scrobble-alternative-title": "Χρήση εναλλακτικών τίτλων",
"scrobble-other-media": "Scrobble άλλα μέσα ενημέρωσης"
},
"name": "Σκρόμπλερ",
"prompt": {
"lastfm": {
"api-key": "Κλειδί API Last.fm",
"api-secret": "Μυστικό API του Last.fm"
},
"listenbrainz": {
"token": {
"label": "Εισάγετε το διακριτικό χρήστη ListenBrainz:",
"title": "Κουπόνι ListenBrainz"
}
}
}
},
"shortcuts": {
"description": "Επιτρέπετε τον καθορισμό παγκόσμιων πλήκτρων άμεσης πρόσβασης για την παρακολούθηση (αναπαραγωγή/παύση/επόμενη/προηγούμενη) και την απενεργοποίηση του OSD πολυμέσων με παράκαμψη των πλήκτρων πολυμέσων, την ενεργοποίηση του Ctrl/CMD + F για αναζήτηση, την ενεργοποίηση της υποστήριξης Linux MPRIS για τα πλήκτρα πολυμέσων και προσαρμοσμένα πλήκτρα άμεσης πρόσβασης για προχωρημένους χρήστες",
"menu": {
"override-media-keys": "Παράκαμψη κλειδιών πολυμέσων",
"set-keybinds": "Ορισμός παγκόσμιων ελέγχων τραγουδιού"
},
"name": "Συντομεύσεις (& MPRIS)",
"prompt": {
"keybind": {
"keybind-options": {
"next": "Επόμενο",
"play-pause": "Αναπαραγωγή / Παύση",
"previous": "Προηγούμενο"
}
},
"label": "Επιλέξτε Global Keybinds για το τραγούδι Έλεγχος:",
"title": "Παγκόσμια δέσμευση πλήκτρων"
}
}
},
"skip-disliked-songs": {
"description": "Παραλείπει τα αρεστά τραγούδια",
"name": "Παραλείψτε τα τραγούδια που δεν άρεσαν"
},
"skip-silences": {
"description": "Αυτόματη παράλειψη τμημάτων σιωπής σε τραγούδια",
"name": "Παραλείψτε τις σιωπές"
},
"sponsorblock": {
"description": "Παραλείπει αυτόματα μέρη που δεν είναι μουσικά, όπως intro/outro ή μέρη μουσικών βίντεο όπου δεν παίζεται το τραγούδι",
"name": "SponsorBlock"
},
"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": "Υπολογίζει με ακρίβεια χιλιοστού του δευτερολέπτου την εμφάνιση της επόμενης γραμμής (μπορεί να έχει μικρή επίπτωση στην απόδοση)"
},
"romanization": {
"label": "Στίχοι Ρομαντικοποίηση",
"tooltip": "Αν οι στίχοι είναι σε διαφορετική γλώσσα, προσπαθήστε να εμφανίσετε μια λατινική έκδοση."
},
"show-lyrics-even-if-inexact": {
"label": "Εμφάνιση στίχων ακόμα και αν είναι ανακριβείς",
"tooltip": "Εάν το τραγούδι δεν βρεθεί, το πρόσθετο προσπαθεί ξανά με διαφορετικό ερώτημα αναζήτησης.\nΤο αποτέλεσμα της δεύτερης προσπάθειας μπορεί να μην είναι ακριβές."
},
"show-time-codes": {
"label": "Εμφάνιση κωδικών ώρας",
"tooltip": "Εμφάνιση των κωδικών ώρας δίπλα στους στίχους"
}
},
"name": "Συγχρονισμένοι στίχοι",
"refetch-btn": {
"fetching": "Φέρνοντας...",
"normal": "Στίχοι Refetch"
},
"warnings": {
"duration-mismatch": "⚠️ - Οι στίχοι ενδέχεται να μην είναι συγχρονισμένοι λόγω αναντιστοιχίας διάρκειας.",
"inexact": "⚠️ - Οι στίχοι για αυτό το τραγούδι μπορεί να μην είναι ακριβείς",
"instrumental": "⚠️ - Αυτό είναι ένα ορχηστρικό τραγούδι"
}
},
"taskbar-mediacontrol": {
"description": "Έλεγχος αναπαραγωγής από τη γραμμή εργασιών των Windows",
"name": "Έλεγχος μέσων γραμμής εργασιών"
},
"touchbar": {
"description": "Προσθέτει ένα γραφικό στοιχείο TouchBar για χρήστες macOS",
"name": "TouchBar"
},
"tuna-obs": {
"description": "Ενσωμάτωση με το plugin Tuna του OBS",
"name": "Tuna OBS"
},
"unobtrusive-player": {
"description": "Αποτρέπει την εμφάνιση του προγράμματος αναπαραγωγής κατά την αναπαραγωγή ενός τραγουδιού",
"name": "Ανεπαίσθητος παίκτης"
},
"video-toggle": {
"description": "Προσθέτει ένα κουμπί για εναλλαγή μεταξύ της λειτουργίας βίντεο/τραγουδιού. μπορεί επίσης προαιρετικά να αφαιρέσει ολόκληρη την καρτέλα βίντεο",
"menu": {
"align": {
"label": "Στοίχιση",
"submenu": {
"left": "Αριστερά",
"middle": "Middle",
"right": "Right"
"right": "Δεξιά"
}
},
"force-hide": "Αναγκαστική αφαίρεση καρτέλας βίντεο",
"mode": {
"label": "Mode"
"label": "Mode",
"submenu": {
"custom": "Προσαρμοσμένη εναλλαγή",
"disabled": "Απενεργοποιημένο",
"native": "Γηγενής εναλλαγή"
}
}
},
"name": "Εναλλαγή βίντεο",
"templates": {
"button": "Song"
"button": "Τραγούδι"
}
},
"visualizer": {
"description": "Προσθέτει έναν απεικονιστή στο πρόγραμμα αναπαραγωγής",
"menu": {
"visualizer-type": "Τύπος απεικονιστή"
},
"name": "Απεικονιστής"
}
}
}

View File

@ -588,6 +588,10 @@
},
"name": "Notifications"
},
"performance-improvement": {
"description": "Improve performance by enabling dangerous scripts",
"name": "Performance improvement [Beta]"
},
"picture-in-picture": {
"description": "Allows to switch the app to picture-in-picture mode",
"menu": {

View File

@ -3,7 +3,7 @@
"console": {
"plugins": {
"execute-failed": "Error al ejecutar el plugin {{pluginName}}::{{contextName}}",
"executed-at-ms": "Plugin {{pluginName}}: {{contextName}} ejecutado en {{ms}}ms",
"executed-at-ms": "Plugin {{pluginName}}::{{contextName}} Ejecutó en {{ms}}ms",
"initialize-failed": "Error al inicializar el plugin \"{{pluginName}}\"",
"load-all": "Cargando todos los plugins",
"load-failed": "Error al cargar el plugin \"{{pluginName}}\"",
@ -683,6 +683,7 @@
"listenbrainz": {
"token": "Introduzca el token de usuario de ListenBrainz"
},
"scrobble-alternative-title": "Usar títulos alternativos",
"scrobble-other-media": "Scrobble en otros medios"
},
"name": "Scrobbler",
@ -767,6 +768,10 @@
"label": "Haz que la letra esté perfectamente sincronizada",
"tooltip": "Calcular al milisegundo la visualización de la siguiente línea (puede tener un pequeño impacto en el rendimiento)"
},
"romanization": {
"label": "Romanizar letras",
"tooltip": "Si la letra está en un idioma diferente, intenta mostrar una versión en latín."
},
"show-lyrics-even-if-inexact": {
"label": "Mostrar la letra aunque sea inexacta",
"tooltip": "Si no se encuentra la canción, el plugin vuelve a intentarlo con una búsqueda diferente.\nEl resultado del segundo intento puede no ser exacto."
@ -799,6 +804,10 @@
"description": "Integración con el plugin Tuna de OBS",
"name": "Tuna OBS"
},
"unobtrusive-player": {
"description": "Evita que el reproductor aparezca al reproducir una canción",
"name": "Jugador discreto"
},
"video-toggle": {
"description": "Añade un botón para cambiar entre el modo Vídeo/Canción. También puede eliminar opcionalmente toda la pestaña de vídeo",
"menu": {

View File

@ -53,7 +53,19 @@
"quit": "Välju",
"relaunch": "Käivita uuesti",
"wait": "Oota"
}
},
"detail": "Vabandame ebamugavuste pärast! Palun vali kuidas jätkata:",
"message": "Rakendus ei vasta ega reageeri",
"title": "Aken ei vasta ega reageeri"
},
"update-available": {
"buttons": {
"disable": "Lülita uuendused välja",
"download": "Laadi alla",
"ok": "Sobib"
},
"detail": "Saadaval on uus versioon, ning seda saad alla laadida siit {{downloadLink}}",
"message": "Uus versioon on saadaval"
}
},
"menu": {
@ -148,6 +160,23 @@
"navigation": {
"name": "Liikumine"
},
"no-google-login": {
"description": "Eemalda kasutajaliidesest Google'i sisselogimisnupud",
"name": "Elu ilma Google'i sisselogimiseta"
},
"quality-changer": {
"backend": {
"dialog": {
"quality-changer": {
"detail": "Praegune kvaliteet: {{quality}}",
"message": "Vali video kvaliteet:",
"title": "Videokvaliteedi valik"
}
}
},
"description": "Võimaldab muuta video kvaliteeti nupust, mis asub video ülekattes",
"name": "Videokvaliteedi muutja"
},
"scrobbler": {
"description": "Lisa kraasimise tugi (last.fm, Listenbrainz, jne)",
"dialog": {

View File

@ -683,6 +683,7 @@
"listenbrainz": {
"token": "توکن کاربری ListenBrainz را وارد کنید"
},
"scrobble-alternative-title": "از عناوین جایگزین استفاده کنید",
"scrobble-other-media": "ردیابی رسانه‌های دیگر"
},
"name": "ابزار ثبت‌کننده‌ی آهنگ",
@ -767,6 +768,10 @@
"label": "هماهنگ‌سازی کامل متن ترانه‌",
"tooltip": "محاسبه دقیق نمایش خط بعدی تا میلی‌ثانیه (ممکن است تاثیر کمی بر عملکرد داشته باشد)"
},
"romanization": {
"label": "اشعار رومی شده",
"tooltip": "اگر اشعار به زبانی متفاوت هستند، سعی کنید نسخه لاتین را نمایش دهید."
},
"show-lyrics-even-if-inexact": {
"label": "نمایش متن ترانه ها حتی اگر دقیق نباشد",
"tooltip": "اگر آهنگ پیدا نشد، افزونه دوباره با یک جستجوی متفاوت امتحان می‌کند.\nنتیجهی این تلاش ممکن است دقیق نباشد."
@ -799,6 +804,10 @@
"description": "ادغام با پلاگین Tuna در OBS",
"name": "Tuna OBS"
},
"unobtrusive-player": {
"description": "هنگام پخش یک آهنگ از پخش کننده جلوگیری می کند",
"name": "پخش‌کننده بی نظیر"
},
"video-toggle": {
"description": "دکمه‌ای اضافه می‌کند برای جابجایی بین حالت ویدیو/آهنگ. همچنین به صورت اختیاری می‌تواند تب ویدیو را حذف کند",
"menu": {

View File

@ -207,6 +207,10 @@
}
},
"plugins": {
"ad-speedup": {
"description": "Jos mainos toistuu, mykistä ääni ja aseta toistonopeus 16x:een",
"name": "Mainoksen nopeutus"
},
"adblocker": {
"description": "Estä kaikki mainokset ja seuranta",
"menu": {
@ -275,6 +279,9 @@
},
"name": "Tunnelmallinen Tila"
},
"amuse": {
"description": "Lisää YouTube Music tuen Amusen nyt soitetaan -widgetille, kehittäjänä 6K Labs"
},
"api-server": {
"description": "Lisää API-palvelimen hallitsemaan soitinta",
"dialog": {
@ -282,7 +289,9 @@
"buttons": {
"allow": "Hyväksy",
"deny": "Kiellä"
}
},
"message": "Sallitaanko {{ID}} ({{origin}}) pääsy API:in?",
"title": "API vahvistuspyyntö"
}
},
"menu": {
@ -297,7 +306,12 @@
"label": "Portti"
}
},
"name": "API Serveri [Beta]"
"name": "API Serveri [Beta]",
"prompt": {
"port": {
"title": "Portti"
}
}
},
"audio-compressor": {
"description": "Lisää äänen kompressointia (hiljentää voimakkaimpien äänien voimakkuutta ja tehostaa pehmeämpien äänien voimakkuutta)",
@ -431,6 +445,15 @@
"description": "Lataa MP3- tai lähdetiedoston suoraan käyttöliittymästä",
"menu": {
"choose-download-folder": "Valitse latauskansio",
"download-finish-settings": {
"prompt": {
"last-seconds": "Viimeiset x sekuntia"
},
"submenu": {
"enabled": "Päällä",
"percent": "Prosentti"
}
},
"download-playlist": "Lataa soittolista",
"presets": "Esiasetukset",
"skip-existing": "Ohita olemassa olevat tiedostot"
@ -585,6 +608,46 @@
"label": "Valitse yleiset äänenvoimakkuuden pikanäppäimet:"
}
}
},
"quality-changer": {
"backend": {
"dialog": {
"quality-changer": {
"detail": "Nykyinen laatu: {{quality}}"
}
}
}
},
"scrobbler": {
"dialog": {
"lastfm": {
"auth-failed": {
"title": "Todennus epäonnistui"
}
}
}
},
"shortcuts": {
"prompt": {
"keybind": {
"keybind-options": {
"previous": "Edellinen"
}
}
}
},
"tuna-obs": {
"name": "Tuna OBS"
},
"video-toggle": {
"menu": {
"align": {
"submenu": {
"left": "Vasen",
"right": "Oikea"
}
}
}
}
}
}

View File

@ -584,6 +584,10 @@
"save-window-position": "I-save ang posisyon ng window",
"save-window-size": "I-save ang laki ng window",
"use-native-pip": "Gamitin ang browser native na PiP"
},
"name": "Picture-na-picture",
"templates": {
"button": "Picture-na-picture"
}
},
"playback-speed": {
@ -641,6 +645,7 @@
"listenbrainz": {
"token": "Ilagay ang user token ng ListenBrainz"
},
"scrobble-alternative-title": "Gumamit ng alternatibong mga title",
"scrobble-other-media": "Mag-Scrobble ng ibang media"
},
"prompt": {
@ -719,6 +724,10 @@
"label": "Gawing perpektong naka-sync ang lyrics",
"tooltip": "Kalkulahin sa millisecond ang pagpapakita ng susunod na linya (maaaring magkaroon ng maliit na epekto sa performance)"
},
"romanization": {
"label": "I-romanize ang lyrics",
"tooltip": "Kung ang lyrics ay nasa ibang wika, subukang magpakita ng latin na bersyon."
},
"show-lyrics-even-if-inexact": {
"label": "Ipakita ang lyrics kahit di-eksakto",
"tooltip": "Kung hindi matagpuan ang kanta, susubukan muli ng plugin gamit ang ibang query sa paghahanap.\nAng resulta mula sa pangalawang pagsubok ay maaaring hindi eksakto."
@ -728,6 +737,7 @@
"tooltip": "Ipakita ang mga time code kasunod sa lyrics"
}
},
"name": "Pag-sync ng Lyrics",
"refetch-btn": {
"fetching": "Nag-fe-fetch...",
"normal": "I-fetch muli ang lyrics"
@ -747,10 +757,15 @@
"tuna-obs": {
"description": "Integrasyon kasama ang Tuna na OBS plugin"
},
"unobtrusive-player": {
"description": "Pinipigilan ang player na mag-pop up kapag nagpe-play ng kanta",
"name": "Hindi mapanghimasok na Player"
},
"video-toggle": {
"description": "Idaragdag ng button na magpalit sa Video/Kanta na mode. maaari ding opsyonal na alisin ang tab ng video",
"menu": {
"align": {
"label": "Pag-align",
"submenu": {
"left": "Kaliwa",
"middle": "Gitna",

View File

@ -683,6 +683,7 @@
"listenbrainz": {
"token": "Entrer le token utilisateur de ListenBrainz"
},
"scrobble-alternative-title": "Utiliser des titres alternatifs",
"scrobble-other-media": "Scrobbler d'autres médias"
},
"name": "Scrobble",
@ -767,6 +768,10 @@
"label": "Rend les paroles parfaitement synchronisées",
"tooltip": "Calcul à la milliseconde près l'affichage de la ligne suivante (peut avoir un faible impact sur les performances)"
},
"romanization": {
"label": "Romaniser les paroles",
"tooltip": "Si les paroles sont dans une autre langue, essayez de les afficher dans une version latine."
},
"show-lyrics-even-if-inexact": {
"label": "Afficher les paroles même si inexactes",
"tooltip": "Si la musique n'est pas trouvé, le plugin essaye à nouveau avec une différence requête.\nLe résultat du deuxième essais peut ne pas être exacte."
@ -799,6 +804,10 @@
"description": "Intégration avec le plugin OBS Tuna",
"name": "Tuna OBS"
},
"unobtrusive-player": {
"description": "Empêche le lecteur de s'afficher quand un chanson est en lecture",
"name": "Lecteur Non-Intrusif"
},
"video-toggle": {
"description": "Ajoute un bouton pour basculer entre le mode Vidéo/Chanson. peut également supprimer tout l'onglet vidéo",
"menu": {

View File

@ -112,7 +112,7 @@
"toggle-dev-tools": "שנה את מצב כלי המפתחים"
}
},
"always-on-top": "תמיד מעל הכל",
"always-on-top": "השאר מקדימה",
"auto-update": "עדכון אוטומטי",
"hide-menu": {
"dialog": {
@ -185,9 +185,145 @@
"label": "מראה",
"submenu": {
"force-reload": "התחל מחדש בכוח",
"reload": "טען מחדש"
"reload": "טען מחדש",
"reset-zoom": "גודל אמיתי",
"toggle-fullscreen": "מסך מלא",
"zoom-in": "התקרב",
"zoom-out": "התרחק"
}
}
},
"tray": {
"next": "הבא",
"play-pause": "נגן/הפסק",
"previous": "הקודם",
"quit": "יציאה",
"restart": "הפעל מחדש",
"show": "הראה חלון",
"tooltip": {
"default": "יוטיוב מיוזיק",
"with-song-info": "יוטיוב מיוזיק: {{artist}} - {{title}}"
}
}
},
"plugins": {
"ad-speedup": {
"description": "במקרה של פרסומת, הסאונד מושתק ומהירות הוידאו מוכפלת ב-16",
"name": "הגבר מהירות פרסומת"
},
"adblocker": {
"description": "חסום את כל המודעות והמעקב מחוץ לקופסה",
"menu": {
"blocker": "חוסם"
},
"name": "חוסם פרסומות"
},
"album-actions": {
"description": "מוסיף לחצני ביטול אהבתי, דיסלייק, 'אהבתי' ו'לא אהבתי' כדי להחיל זאת על כל השירים ברשימת השמעה או אלבום",
"name": "פעולות אלבום"
},
"album-color-theme": {
"description": "מחיל נושא דינמי ואפקטים חזותיים המבוססים על לוח הצבעים של האלבום",
"menu": {
"color-mix-ratio": {
"submenu": {
"percent": "{{ratio}}%"
}
}
},
"name": "ערכת נושא צבע אלבום"
},
"ambient-mode": {
"description": "מחיל אפקט תאורה על ידי הטלת צבעים עדינים מהסרטון, אל הרקע של המסך",
"menu": {
"blur-amount": {
"label": "כמות טשטוש",
"submenu": {
"pixels": "{{blurAmount}} פיקסלים"
}
},
"buffer": {
"submenu": {
"buffer": "{{buffer}}"
}
},
"opacity": {
"label": "אֲטִימוּת",
"submenu": {
"percent": "{{opacity}}%"
}
},
"quality": {
"label": "אֵיכוּת",
"submenu": {
"pixels": "{{quality}} פיקסלים"
}
},
"size": {
"label": "גוֹדֶל",
"submenu": {
"percent": "{{size}}%"
}
},
"smoothness-transition": {
"submenu": {
"during": "במהלך {{interpolationTime}} שניות"
}
},
"use-fullscreen": {
"label": "שימוש במסך מלא"
}
},
"name": "מצב אווירה"
},
"amuse": {
"description": "מוסיף תמיכה ב-YouTube Music עבור הווידג'ט של Amuse המתנגן כעת על ידי 6K Labs"
},
"discord": {
"menu": {
"hide-github-button": "הסתר את לחצן הקישור של GitHub",
"play-on-youtube-music": "הפעל ביוטיוב מיוזיק",
"set-inactivity-timeout": "הגדר פסק זמן לחוסר פעילות"
}
},
"downloader": {
"backend": {
"dialog": {
"error": {
"title": "שגיאה בהורדה!"
}
},
"feedback": {
"downloading": "מוריד…",
"loading": "בטְעִינָה…",
"playlist-has-only-one-song": "לפלייליסט יש רק פריט אחד, מוריד אותו ישירות",
"playlist-id-not-found": "לא נמצא מזהה ID פלייליסט",
"preparing-file": "מכין קובץ…",
"saving": "שומר…",
"trying-to-get-playlist-id": "מנסה להשיג מזהה פלייליסט: {{playlistId}}",
"video-id-not-found": "הסרטון לא נמצא"
}
},
"description": "מוריד MP3 / אודיו מקור ישירות מהממשק",
"menu": {
"choose-download-folder": "בחר תיקיית הורדה",
"download-finish-settings": {
"label": "הורדה בסיום",
"prompt": {
"last-percent": "אחרי x אחוזים",
"last-seconds": "נשארו x שניות",
"title": "הגדר מתי להוריד"
},
"submenu": {
"advanced": "מִתקַדֵם",
"enabled": "מופעל",
"percent": "אָחוּז",
"seconds": "שניות"
}
},
"presets": "הגדרות קבועות מראש",
"skip-existing": "דלג על קבצים קיימים"
}
}
}
}

View File

@ -268,11 +268,22 @@
}
},
"smoothness-transition": {
"label": "चिकनाई संक्रमण"
"label": "चिकनाई संक्रमण",
"submenu": {
"during": "दौरान {{interpolationTime}}"
}
},
"use-fullscreen": {
"label": "पूर्णस्क्रीन का उपयोग"
}
},
"name": "अम्बिएन्ट मोड्"
},
"amuse": {
"description": "6K लैब्स द्वारा Amuse now playing विजेट के लिए YouTube म्यूजिक समर्थन जोड़ा गया",
"name": "मन बहलाना",
"response": {
"query": "अमयूस ए.पि.ऐ. चल रहा है। गाने की जान्कारि होने के लिये GET /query कीजिये।"
}
},
"api-server": {

View File

@ -3,7 +3,11 @@
"console": {
"plugins": {
"execute-failed": "Neuspjelo izvršenje plugina {{pluginName}}::{{contextName}}",
"executed-at-ms": "Plugin{{pluginName}}::{{contextName}}{{je izvršen za {{ms}}ms"
"executed-at-ms": "Plugin{{pluginName}}::{{contextName}}{{je izvršen za {{ms}}ms",
"initialize-failed": "Nije uspilo inicijalitirati plugin \"{{pluginName}}\"",
"load-all": "Učitavaju se svi plugini",
"load-failed": "Neuspješno ućitavanje plugina \"{{pluginName}}\"",
"loaded": "Plugin \"{{pluginName}}\" je učitan"
}
}
},
@ -11,5 +15,34 @@
"code": "hr",
"local-name": "Hrvatski",
"name": "Croatian"
},
"main": {
"console": {
"i18n": {
"loaded": "i18n je učitan"
},
"second-instance": {
"receive-command": "Zaprimljena komanda preko protokola: \"{{command}}\""
},
"theme": {
"css-file-not-found": "CSS fajl \"{{cssFile}}\" ne postoji, ignorišem"
},
"when-ready": {
"clearing-cache-after-20s": "Brisanje cache memorije u toku"
}
},
"dialog": {
"hide-menu-enabled": {
"detail": "Meni je sakriven, pritisni 'Alt' da bi se prikazao (ili 'Escape' ako se koristi In-App Menu)",
"message": "Sakriveni Meni je uključen"
},
"need-to-restart": {
"buttons": {
"later": "Kasnije",
"restart-now": "Pokreni ponovo"
},
"detail": "\"{{pluginName}}\" dodatak zahtjeva pokretanje aplikacije ponovo da bi promjene bile vidljive"
}
}
}
}

View File

@ -683,6 +683,7 @@
"listenbrainz": {
"token": "Add meg a ListenBrainz felhasználói tokenedet"
},
"scrobble-alternative-title": "Alternatív címek használata",
"scrobble-other-media": "Más média scrobbelése"
},
"name": "Scrobbler",
@ -767,6 +768,10 @@
"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)"
},
"romanization": {
"label": "Latin betűs szöveg",
"tooltip": "Idegennyelvű szöveg esetén próbálkozás a szöveglatin betűs megjelenítésével."
},
"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."
@ -799,6 +804,10 @@
"description": "Integráció az OBS Tuna pluginjával",
"name": "Tuna OBS"
},
"unobtrusive-player": {
"description": "Megakadályozza a lejátszó felugrását zenehallgatás közben",
"name": "Rejtett lejátszó"
},
"video-toggle": {
"description": "Hozzáad egy gombot a Videó/Dal mód közötti váltáshoz. Opcionálisan teljesen eltávolíthatja a videó fület is",
"menu": {

View File

@ -768,6 +768,10 @@
"label": "Buat liriknya tersinkronisasi dengan sempurna",
"tooltip": "Hitung hingga milidetik tampilan baris berikutnya (dapat berdampak kecil pada kinerja)"
},
"romanization": {
"label": "Romanize Liriknya",
"tooltip": "Apabila lirik berada dalam bahasa berbeda, cobalah untuk menampilkan versi latinnya."
},
"show-lyrics-even-if-inexact": {
"label": "Tampilkan lirik meskipun tidak tepat",
"tooltip": "Jika lagu tidak ditemukan, plugin akan mencoba lagi dengan kueri pencarian yang berbeda.\nHasil dari percobaan kedua mungkin tidak tepat."
@ -800,6 +804,10 @@
"description": "Integrasi dengan plugin Tuna OBS",
"name": "Tuna OBS"
},
"unobtrusive-player": {
"description": "Cegah pemutar musik muncul ketika memutar musik",
"name": "Pemutar simpel (tidak menganggu)"
},
"video-toggle": {
"description": "Tambahkan tombol untuk beralih antara mode Lagu/Video. secara opsional juga dapat menghapus keseluruhan tab video",
"menu": {

View File

@ -160,10 +160,10 @@
"theme": {
"dialog": {
"button": {
"cancel": "Cancella",
"cancel": "Annulla",
"remove": "Rimuovi"
},
"remove-theme": "Sicuro di voler rimuovere il tema personalizzato?",
"remove-theme": "Sei sicuro di voler rimuovere il tema personalizzato?",
"remove-theme-message": "Questo rimuoverà il tema personalizzato"
},
"label": "Tema",
@ -495,7 +495,7 @@
"description": "Aggiunge un equalizzatore al player",
"menu": {
"presets": {
"label": "Presets",
"label": "Preset",
"list": {
"bass-booster": "Booster dei bassi"
}
@ -734,8 +734,8 @@
"synced-lyrics": {
"description": "Fornisce testi sincronizzati alle canzoni, utilizzando provider come LRClib.",
"errors": {
"fetch": "⚠️ - Si è verificato un errore nel recuperare il testo. Per favore riprova più tardi.",
"not-found": "⚠️ - Nessun testo trovato per questa canzone."
"fetch": "⚠️ \tSi è verificato un errore nel recuperare il testo.\n\tPer favore riprova più tardi.",
"not-found": "⚠️ Nessun testo trovato per questa canzone."
},
"menu": {
"default-text-string": {
@ -767,6 +767,10 @@
"label": "Rendi i testi perfettamente sincronizzati",
"tooltip": "Calcola al millisecondo la visualizzazione della riga successiva (può avere un piccolo impatto sulle prestazioni)"
},
"romanization": {
"label": "Testi in caratteri occidentali",
"tooltip": "Qualora il testo fosse scritto in una lingua non occidentale, prova a visualizzarlo in caratteri latini."
},
"show-lyrics-even-if-inexact": {
"label": "Mostra le lyric anche se incorrette",
"tooltip": "Se il brano non viene trovato, il plugin riprova con un'altra query di ricerca.\nIl risultato del secondo tentativo potrebbe non essere esatto."
@ -799,6 +803,10 @@
"description": "Integrazione con il plugin OBS Tuna",
"name": "Tuna OBS"
},
"unobtrusive-player": {
"description": "Evita che il player si apra automaticamente durante la riproduzione di un brano",
"name": "Player Discreto"
},
"video-toggle": {
"description": "Aggiunge un pulsante per passare dalla modalità Video a quella Brano. Può anche rimuovere l'intera scheda Brano/Video",
"menu": {

View File

@ -279,6 +279,13 @@
},
"name": "アンビエント モード"
},
"amuse": {
"description": "6K LabsのAmuse再生中ウィジェットがYouTube Musicに対応しました",
"name": "Amuse",
"response": {
"query": "AmuseのAPIサーバーが稼働中です。GET /query で楽曲情報を取得できます。"
}
},
"api-server": {
"description": "プレイヤーを制御するAPIサーバーを追加",
"dialog": {
@ -485,7 +492,7 @@
}
},
"equalizer": {
"description": "プレイヤーにイコライザーを追加",
"description": "イコライザーを追加",
"menu": {
"presets": {
"label": "プリセット",
@ -676,6 +683,7 @@
"listenbrainz": {
"token": "ListenBrainzユーザートークンを入力してください"
},
"scrobble-alternative-title": "代替タイトルを使用する",
"scrobble-other-media": "他のメディアをScrobbleする"
},
"name": "スクロブラー",
@ -726,8 +734,8 @@
"synced-lyrics": {
"description": "LRClibのようなプロバイダを使って、楽曲に同期した歌詞を使用する。",
"errors": {
"fetch": "⚠️ - 歌詞の取得中にエラーが発生しました。 後でもう一度お試しください。",
"not-found": "⚠️ - この曲の歌詞は見つかりませんでした。"
"fetch": "⚠️ \t歌詞の取得中にエラーが発生しました。\n\t後でもう一度お試しください。",
"not-found": "⚠️ この曲の歌詞は見つかりませんでした。"
},
"menu": {
"default-text-string": {
@ -737,6 +745,10 @@
"line-effect": {
"label": "歌詞表示のエフェクト",
"submenu": {
"fancy": {
"label": "ファンシー",
"tooltip": "現在の行にアプリのような大きなエフェクトを使う"
},
"focus": {
"label": "フォーカス",
"tooltip": "現在の行だけを白くする"
@ -756,6 +768,10 @@
"label": "歌詞を完璧に同期させる",
"tooltip": "次の行の表示をミリ秒単位で計算する(パフォーマンスに若干の影響を与える可能性があります)"
},
"romanization": {
"label": "ローマ字歌詞",
"tooltip": "歌詞が異なる言語で書かれている場合は、ラテン語バージョンを表示するようにしてください。"
},
"show-lyrics-even-if-inexact": {
"label": "歌詞が不正確でも表示する",
"tooltip": "曲が見つからなかった場合、プラグインは別の検索クエリで再試行します。\nただし、再試行の結果は正確でない可能性があります。"
@ -788,6 +804,10 @@
"description": "OBSのプラグインTunaの統合",
"name": "Tuna OBS"
},
"unobtrusive-player": {
"description": "曲の再生時にプレーヤーがポップアップしないようにする",
"name": "控えめなプレーヤー"
},
"video-toggle": {
"description": "ビデオ/ソングモードを切り替えるボタンを追加します。オプションでビデオタブ全体を削除することもできます",
"menu": {

View File

@ -7,7 +7,9 @@
"initialize-failed": "პლაგინის ინიციალიზაცია ვერ მოხდა\"{{pluginName}}\"",
"load-all": "იტვირთება ყველა პლაგინი",
"load-failed": "პლაგინის ჩატვირთვა ვერ მოხდა \"{{pluginName}}\"",
"loaded": "პლაგინი \"{{pluginName}}\" ჩაიტვირთა"
"loaded": "პლაგინი \"{{pluginName}}\" ჩაიტვირთა",
"unload-failed": "პლაგინის {{pluginName}} გათიშვა ვერ მოხერხდა",
"unloaded": "პლაგინი {{pluginName}} გათიშულია"
}
}
},
@ -17,6 +19,26 @@
"name": "Georgian"
},
"main": {
"console": {
"did-finish-load": {
"dev-tools": "ჩატვირთვა დასრულებულია. DevTools გახსნილია"
},
"i18n": {
"loaded": "i18n ჩართულია"
},
"second-instance": {
"receive-command": "მიღებულია ბრძანება პროტოკოლზე: {{command}}"
},
"theme": {
"css-file-not-found": "CSS ფაილი {{cssFile}} არ არსებობს, იგნორირება"
},
"unresponsive": {
"details": "უპასუხო შეცდომა!\n{{error}}"
},
"when-ready": {
"clearing-cache-after-20s": "აპლიკაციის ქეშის გაწმენდვა"
}
},
"dialog": {
"need-to-restart": {
"buttons": {

View File

@ -0,0 +1,7 @@
{
"language": {
"code": "kn",
"local-name": "ಕನ್ನಡ",
"name": "Kannada"
}
}

View File

@ -600,6 +600,10 @@
},
"name": "알림"
},
"performance-improvement": {
"description": "위험한 스크립트를 활성화하여 성능을 개선합니다",
"name": "성능 개선 [베타]"
},
"picture-in-picture": {
"description": "앱을 PiP 모드로 전환할 수 있게 허용합니다",
"menu": {

View File

@ -0,0 +1,57 @@
{
"common": {
"console": {
"plugins": {
"initialize-failed": "{{pluginName}}എന്ന പ്ലഗിൻ തുടങ്ങുന്നതിൽ പരാജയപെട്ടു",
"load-all": "എല്ലാ പ്ലേഗിനും ലോഡ് ചെയ്യുന്നു",
"loaded": "{{pluginName}} എന്ന പ്ലഗിൻ ലോഡ് ചെയ്തു",
"unloaded": "{{pluginName}} എന്ന പ്ലഗിൻ അൺലോഡ് ചെയ്തു"
}
}
},
"language": {
"code": "ml",
"local-name": "മലയാളം",
"name": "Malayalam"
},
"main": {
"console": {
"did-finish-load": {
"dev-tools": "ലോഡിങ് തീർത്തു. DevTools തുറന്നു"
},
"i18n": {
"loaded": "i18n ലോഡ് ചെയ്തു"
},
"second-instance": {
"receive-command": "പ്രോട്ടോക്കോളിലൂടെ കമാൻഡ് ലഭിച്ചു : \"{{command}}\""
},
"theme": {
"css-file-not-found": "\"{{cssFile}}\" എന്ന CSS file നിലവിൽ ഇല്ല. ഉപേക്ഷിക്കുന്നു"
},
"unresponsive": {
"details": "പ്രതികാരമില്ലാത്ത എറർ \n{{error}}"
},
"when-ready": {
"clearing-cache-after-20s": "ആപ്പ് cache ക്ലിയർ ചെയ്യുന്നു"
},
"window": {
"tried-to-render-offscreen": "Window സ്ക്രീനിനു വെളിയിൽ render ചെയ്യാൻ ശ്രമിച്ചു, windowSize={{windowSize}}, displaySize={{displaySize}}, position={{position}}"
}
},
"dialog": {
"hide-menu-enabled": {
"detail": "Menu മറച്ചിരിക്കുന്നു, അവതരിപ്പിക്കാൻ 'Alt' ഉപയോഗിക്കു (In-App Menu ഉപയോഗിക്കുന്നെങ്കിൽ 'Escape' )",
"message": "Hide Menu പ്രവർത്തിയിൽ",
"title": "Hide Menu പ്രവർത്തിയിൽ"
},
"need-to-restart": {
"buttons": {
"later": "പിന്നീട",
"restart-now": "ഇപ്പോൾ പുനരാരംഭിക്കുക"
},
"detail": "\"{{pluginName}}\" പ്രവർത്തിയിൽ ആകാൻ restart വേണ്ടിയിരിക്കുന്നു",
"message": "\"{{pluginName}}\" restart ആവശ്യപെടുന്നു"
}
}
}
}

View File

@ -683,6 +683,7 @@
"listenbrainz": {
"token": "Voer het ListenBrainz-gebruikerstoken in"
},
"scrobble-alternative-title": "Gebruik alternatieve titels",
"scrobble-other-media": "Scrobble andere media"
},
"name": "Scrobbler",
@ -767,6 +768,10 @@
"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)"
},
"romanization": {
"label": "Romaniseer songtekst",
"tooltip": "Als de songtekst in een andere taal is, probeer dan een Latijnse versie weer te geven."
},
"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."
@ -799,6 +804,10 @@
"description": "Integratie met OBS's plug-in Tuna",
"name": "Tuna OBS"
},
"unobtrusive-player": {
"description": "Voorkomt dat de speler bij het afspelen van een nummer verschijnt",
"name": "Minder opdringerige speler"
},
"video-toggle": {
"description": "Voegt een knop toe om te schakelen tussen de video-/nummermodus. kan optioneel ook het hele videotabblad verwijderen",
"menu": {

View File

@ -364,7 +364,7 @@
"name": "Kompaktowy pasek boczny"
},
"crossfade": {
"description": "Przenikanie pomiędzy utworami",
"description": "Pozwól odtwarzaczowi płynnie przechodzić między utworami",
"menu": {
"advanced": "Zaawansowane"
},
@ -768,6 +768,10 @@
"label": "Zsynchronizuj tekst utworu do perfekcji",
"tooltip": "Wylicz czas wyświetlania następnej linijki co do milisekundy (może mieć mały wpływ na wydajność systemu)"
},
"romanization": {
"label": "Romanizacja utworów",
"tooltip": "Jeżeli tekst piosenki nie jest w alfabecie łacińskim, poddaje ją romanizacji, czyli przedstawia mowy za pomocą owych liter."
},
"show-lyrics-even-if-inexact": {
"label": "Pokaż teksty, mimo niezgodności",
"tooltip": "Jeżeli nie znaleziono tekstu piosenki z bazy danych, wtyczka spróbuje ponownie przez wyszukanie przybliżonej frazy.\nNależy jednak pamiętać, że następne próby mogą nie być trafne co do oryginału."
@ -800,6 +804,10 @@
"description": "Integracja z wtyczką OBS Tuna",
"name": "Tuna OBS"
},
"unobtrusive-player": {
"description": "Zapobiega wyświetlaniu się ekranu z utworem po wybraniu innego tytułu",
"name": "Niewidoczny odtwarzacz"
},
"video-toggle": {
"description": "Dodaje przycisk do przełączania między trybem wideo a piosenki. Może również opcjonalnie usunąć całą kartę wideo",
"menu": {

View File

@ -683,6 +683,7 @@
"listenbrainz": {
"token": "Insira o token de usuário ListenBrainz"
},
"scrobble-alternative-title": "Usar títulos alternativos",
"scrobble-other-media": "Scrobble outras mídias"
},
"name": "Scrobbler",
@ -767,6 +768,10 @@
"label": "Deixa as letras perfeitamente sincronizadas",
"tooltip": "Calcular até o milissegundo a exibição da próxima linha (pode ter um pequeno impacto no desempenho)"
},
"romanization": {
"label": "Letras romanizadas",
"tooltip": "Se as letras estiverem em um idioma diferente, tente exibir uma versão latina."
},
"show-lyrics-even-if-inexact": {
"label": "Mostrar letras mesmo que não sejam exatas",
"tooltip": "Se a música não for encontrada, o plugin tenta novamente com uma consulta de pesquisa diferente.\nO resultado da segunda tentativa pode não ser exato."
@ -799,6 +804,10 @@
"description": "Integração com o plugin Tuna do OBS",
"name": "Tuna OBS"
},
"unobtrusive-player": {
"description": "Evita que o player apareça ao tocar uma música",
"name": "Player discreto"
},
"video-toggle": {
"description": "Adiciona um botão para alternar entre o modo Vídeo/Música. Também é possível remover opcionalmente toda a aba de vídeo",
"menu": {

View File

@ -683,6 +683,7 @@
"listenbrainz": {
"token": "Introduza o token de utilizador do ListenBrainz"
},
"scrobble-alternative-title": "Usar títulos alternativos",
"scrobble-other-media": "Scrobble de outros conteúdos"
},
"name": "Scrobbler",
@ -767,6 +768,10 @@
"label": "Fazer com que as letras estejam perfeitamente sincronizadas",
"tooltip": "Calcular ao milissegundo a visualização da linha seguinte (pode ter um pequeno impacto no desempenho)"
},
"romanization": {
"label": "Letras Romanas",
"tooltip": "Se as letras estiverem numa linguagem diferente, tente mostrar uma versão em latim."
},
"show-lyrics-even-if-inexact": {
"label": "Mostrar as letras, mesmo que sejam imprecisas",
"tooltip": "Se a música não for encontrada, o plugin tenta novamente com uma consulta de pesquisa diferente.\nO resultado da segunda tentativa pode não ser exato."
@ -799,6 +804,10 @@
"description": "Integração com o plugin Tuna do OBS",
"name": "Tuna OBS"
},
"unobtrusive-player": {
"description": "Previne o programa de aparecer enquanto toca uma música",
"name": "Reprodutor não obstrutivo"
},
"video-toggle": {
"description": "Adiciona um botão para alternar entre o modo Vídeo/ Música. Opcionalmente, também pode remover completamente o separador do vídeo",
"menu": {

View File

@ -768,6 +768,10 @@
"label": "Идеально синхронизировать слова",
"tooltip": "До миллисекунды рассчитывает отображение следующей строки(может оказать небольшое влияние на производительность)"
},
"romanization": {
"label": "Романизировать слова",
"tooltip": "Если слова на другом языке, пытаться отображать версию на латинице."
},
"show-lyrics-even-if-inexact": {
"label": "Показывать слова, даже если неточные",
"tooltip": "Если песня не найдена, плагин попытается снова с другим поисковым запросом.\nСо второй попытки результат может быть неточным."
@ -800,6 +804,10 @@
"description": "Интеграция с плагином Tuna от OBS",
"name": "Tuna OBS"
},
"unobtrusive-player": {
"description": "Предотвращает выскакивание плеера при воспроизведении",
"name": "Ненавязчивый плеер"
},
"video-toggle": {
"description": "Добавляет кнопку для переключения между режимами видео и песни. Также можно удалить всю вкладку с видео",
"menu": {

View File

@ -7,9 +7,9 @@
"initialize-failed": "\"{{pluginName}}\" ප්ලගිනය ආරම්භ කිරීමට අසමත් විය",
"load-all": "සියලුම ප්ලගින පූරණය කරමින්",
"load-failed": "\"{{pluginName}}\" ප්ලගිනය පූරණය කිරීමට අසමත් විය",
"loaded": "ප්ලගිනය \"{{pluginName}}\" පූරණය කරන ලදී",
"unload-failed": "ප්ලගින් \"{{pluginName}}\" ගලවන්න අසාර්ථක වුන",
"unloaded": "ප්ලගින් \"{{pluginName}}\" ගැලෙව්වා"
"loaded": "\"{{pluginName}}\" ප්ලගිනය පූරණය විය",
"unload-failed": "\"{{pluginName}}\" ප්ලගිනය යළි ඉවත් කිරීමට අසාර්ථක විය",
"unloaded": "\"{{pluginName}}\" ප්ලගිනය යළි ඉවත් කරන ලදී"
}
}
},
@ -21,36 +21,39 @@
"main": {
"console": {
"did-finish-load": {
"dev-tools": "පූරණය සම්පුර්නි. ඩෙව්ටූල්ස් ඇරිලා"
"dev-tools": "පූරණය අවසන්. DevTools විවෘත වී ඇත"
},
"i18n": {
"loaded": "i18n පූරණය කර ඇත"
"loaded": "i18n පූරණය කරන ලදී"
},
"second-instance": {
"receive-command": "ප්‍රෝටෝකාල් හරහා විධානය ලැබුණි: \"{{command}}\""
},
"theme": {
"css-file-not-found": "සීඑස්එස් ගොනුව \"{{cssFile}}\" නොපවතී, නොසලකා හරී"
"css-file-not-found": "css ගොනුව \"{{cssFile}}\" නොපවතී, නොසලකා හරී"
},
"unresponsive": {
"details": "ප්‍රතිචාර නොදක්ව දෝෂයක්\n{{error}}"
"details": "ප්‍රතිචාර නොදක්වයි, දෝෂයක්\n{{error}}"
},
"when-ready": {
"clearing-cache-after-20s": "යෙදුම් කෑශ් නිදහස් කරමින්"
"clearing-cache-after-20s": "යෙදුමේ දත්ත සංචිතය හිස් කරමින්"
},
"window": {
"tried-to-render-offscreen": "වින්ඩෝව තිරයෙන් පිටත පෙන්වීමට උත්සාහ කළේය, වින්ඩෝවේ ප්‍රමාණය={{windowSize}}, තිරයෙ ප්‍රමාණය={{displaySize}}, පිහිටීම={{position}}"
}
},
"dialog": {
"hide-menu-enabled": {
"detail": "මෙනුව සැගවී ඇත, නැවත පෙන්වීමට 'Alt' යතුර භාවිතා කරන්න. (හෝ In-App මෙනුවේ 'Escape')",
"message": "මෙනුව සැගවීම සාර්තකයි",
"message": "මෙනුව සැගවීම සක්‍රීය කර ඇත",
"title": "මෙනුව සැගවීම සක්‍රීයයි"
},
"need-to-restart": {
"buttons": {
"later": "පසුව",
"restart-now": "යෙදුම වසා නැවත ආරම්භ කරන්න"
"restart-now": "නැවත ආරම්භ කරන්න"
},
"detail": "\"{{pluginName}}\" ප්ලගිනය යෙදුම නැවත ආරම්භ කිරීමක් ඉල්ලයි",
"detail": "\"{{pluginName}}\" ප්ලගිනය ක්‍රියාත්මක වීමට නැවත ආරම්භ කිරීමක් අවශ්‍යයි",
"message": "\"{{pluginName}}\" නැවත ආරම්භ කළ යුතුය",
"title": "නැවත ආරම්භ කිරීම අවශ්‍යයි"
},
@ -66,8 +69,8 @@
},
"update-available": {
"buttons": {
"disable": "යාවත්කාලීන කිරීම් නවතන්න",
"download": "බාගත කිරීම",
"disable": "යාවත්කාලීන කිරීම් අක්‍රිය කරන්න",
"download": "බාගත කරන්න",
"ok": "හරි"
},
"detail": "නව අනුවාදයක් ඇති අතර එය මෙයින් බාගන්න {{downloadLink}}",

View File

@ -0,0 +1,7 @@
{
"language": {
"code": "sr",
"local-name": "Српски",
"name": "Serbian"
}
}

View File

@ -99,17 +99,29 @@
"auto-reset-app-cache": "Nollställ appcache när appen startar",
"disable-hardware-acceleration": "Stäng av hårdvaruacceleration",
"edit-config-json": "Redigera config.json",
"override-user-agent": "Ersätt User-Agent",
"restart-on-config-changes": "Starta om vid konfigurationsändringar",
"set-proxy": {
"label": "Ställ in proxy",
"prompt": {
"label": "Ange Proxy-adress: (lämna tomt för att inaktivera)",
"placeholder": "Exempel: SOCKS5://127.0.0.1:9999",
"title": "Ställ in proxy"
}
}
},
"toggle-dev-tools": "Utvecklarverktyg"
}
},
"always-on-top": "Alltid överst",
"auto-update": "Uppdatera automatiskt"
"auto-update": "Uppdatera automatiskt",
"language": {
"dialog": {
"message": "Språket ändras efter omstart",
"title": "Språket har ändrats"
},
"label": "Språk"
},
"resume-on-start": "Fortsätt spela när appen öppnas"
}
}
}

View File

@ -14,9 +14,9 @@
}
},
"language": {
"code": "என்",
"local-name": "ஆங்கிலம்",
"name": "ஆங்கிலம்"
"code": "ta",
"local-name": "தமிழ்",
"name": "Tamil"
},
"main": {
"console": {
@ -444,7 +444,7 @@
"download-info": "பதிவிறக்கம் {{artist}} - {{title}} [{{videoId}}}",
"download-progress": "பதிவிறக்கம்: {{percent}}%",
"downloading": "பதிவிறக்கம்…",
"downloading-counter": "பதிவிறக்கம் {{current}}/{{{total}}…",
"downloading-counter": "பதிவிறக்கம் {{current}}/{{total}}…",
"downloading-playlist": "பிளேலிச்ட்டைப் பதிவிறக்குதல் \"{{playlistTitle}}\" - {{playlistSize}} பாடல்கள் ({{playlistId}})",
"error-while-downloading": "பிழை \"{{author}} - {{title}}\": {{error}}",
"folder-already-exists": "{{playlistFolder}}} ஏற்கனவே உள்ளது",
@ -667,7 +667,7 @@
"name": "வீடியோ தர மாற்றி"
},
"scrobbler": {
"description": "ச்க்ரோப்ளிங் ஆதரவைச் சேர்க்கவும் (முதலியன.",
"description": "ச்க்ரோப்ளிங் ஆதரவைச் சேர் (last.fm, Listenbrainz முதலியன)",
"dialog": {
"lastfm": {
"auth-failed": {
@ -700,7 +700,7 @@
}
},
"shortcuts": {
"description": "பிளேபேக்கிற்கான உலகளாவிய ஆட்கீசை அமைக்க அனுமதிக்கிறது (நாடகம்/இடைநிறுத்தம்/அடுத்த/முந்தைய) மீடியா விசைகளை மீறுவதன் மூலம் மீடியா ஓ.எச்.டி.",
"description": "பிளேபேக்கிற்கான உலகளாவிய ஆட்கீசை அமைக்க அனுமதிக்கிறது (நாடகம்/இடைநிறுத்தம்/அடுத்த/முந்தைய) மீடியா விசைகளை மீறுவதன் மூலம் மீடியா ஓஎச்டி",
"menu": {
"override-media-keys": "மீடியா விசைகளை மேலெழுதவும்",
"set-keybinds": "உலகளாவிய பாடல் கட்டுப்பாடுகளை அமைக்கவும்"

View File

@ -683,6 +683,7 @@
"listenbrainz": {
"token": "ใส่ user token ของ ListenBrainz"
},
"scrobble-alternative-title": "ใช้ชื่ออื่น",
"scrobble-other-media": "บันทึกการเล่นสื่ออื่นๆ"
},
"name": "บันทึกการเล่น (Scrobbler)",
@ -767,6 +768,10 @@
"label": "ให้เนื้อเพลงตรงกับเพลงเป๊ะๆ",
"tooltip": "คำนวณมิลิวินาทีในการแสดงบรรทัดถัดไป (มีผลเล็กน้อยกับประสิทธิภาพการทำงาน)"
},
"romanization": {
"label": "เนื้อเพลงตัวด้วยอักษรโรมัน",
"tooltip": "ถ้าหากเนื้อเพลงอยู่ในภาษาอื่น ลองเปลี่ยนการแสดงผลโดยใช้เวอร์ชั่นลาติน"
},
"show-lyrics-even-if-inexact": {
"label": "แสดงเนื้อเพลงแม้ไม่ตรงเป๊ะ",
"tooltip": "ถ้าหาเนื้อเพลงไม่เจอจะลองหาด้วยคำค้นหาที่ต่างกัน\nอาจได้เนื้อเพลงไม่ตรง"
@ -799,6 +804,10 @@
"description": "ใช้งานร่วมกันกับปลั้กอิน Tuna บน OBS",
"name": "Tuna OBS"
},
"unobtrusive-player": {
"description": "ป้องกันเครื่องเล่นแสดงขึ้นเมื่อเริ่มเล่นเพลง",
"name": "เครื่องเล่นที่ไม่รบกวน"
},
"video-toggle": {
"description": "เพิ่มปุ่มสลับระหว่างโหมดเพลงกับโหมดวิดีโอ หรือลบแถบวิดีโอออกทั้งแถบ",
"menu": {

View File

@ -683,6 +683,7 @@
"listenbrainz": {
"token": "ListenBrainz kullanıcı kimliğinizi girin"
},
"scrobble-alternative-title": "Alternatif başlıklar kullan",
"scrobble-other-media": "Diğer medya ortamlarında listele"
},
"name": "Listeleyici",
@ -767,6 +768,10 @@
"label": "Şarkı sözlerini mükemmel şekilde senkronize edin",
"tooltip": "Bir sonraki satırın görüntülenmesini milisaniyesine kadar hesaplayın (performans üzerinde küçük bir etkisi olabilir)"
},
"romanization": {
"label": "Sözleri Romanize Et",
"tooltip": "Sözler başka bir dilde gözüküyorsa, Latin versiyonunu dene."
},
"show-lyrics-even-if-inexact": {
"label": "Kesin olmasa bile şarkı sözlerini gösterin",
"tooltip": "Şarkı bulunamazsa, eklenti farklı bir arama sorgusuyla tekrar dener. \nİkinci denemenin sonucu tam olmayabilir."
@ -799,6 +804,10 @@
"description": "OBS eklentisi Tuna ile entegrasyon sağlar",
"name": "Tuna OBS"
},
"unobtrusive-player": {
"description": "Müzik oynatıcının şarkı çalarken saçmalamasını engeller",
"name": "Göze Batmayan Çalar"
},
"video-toggle": {
"description": "Video/Şarkı modu arasında geçiş yapmak için bir düğme ekler. ayrıca isteğe bağlı olarak tüm video sekmesini kaldırabilir",
"menu": {

View File

@ -683,6 +683,7 @@
"listenbrainz": {
"token": "Ввести токен користувача ListenBrainz"
},
"scrobble-alternative-title": "Використовувати альтернативні назви",
"scrobble-other-media": "Скробилити інші медіа"
},
"name": "Скроблер",
@ -758,7 +759,7 @@
},
"scale": {
"label": "Масштабувати",
"tooltip": "Масштабуваты поточну лінію"
"tooltip": "Масштабувати поточну лінію"
}
},
"tooltip": "Виберіть ефект, який потрібно застосувати до поточної лінії"
@ -767,13 +768,17 @@
"label": "Зробити текст пісні ідеально синхронізованим",
"tooltip": "Обчисли до мілісекунд відображення наступного рядка (може мати невеликий вплив на продуктивність)"
},
"romanization": {
"label": "Транслітерувати текст пісень",
"tooltip": "Якщо текст пісні іншою мовою, спробувати його відобразити латинською версією."
},
"show-lyrics-even-if-inexact": {
"label": "Показувати текст пісні, навіть якщо він неточний",
"tooltip": "Якщо пісня не знайдена, плагін повторює спробу з іншим пошуковим запитом.\nРезультат з другої спроби може бути не точним."
},
"show-time-codes": {
"label": "Показувати часові марки",
"tooltip": "Показує часові маркы поруч із текстом пісні"
"label": "Відображати часові коди",
"tooltip": "Відображати часові коди біля тексту"
}
},
"name": "Синхронізовані тексти",
@ -799,6 +804,10 @@
"description": "Інтеграція з плагіном Tuna для OBS",
"name": "Tuna OBS"
},
"unobtrusive-player": {
"description": "Запобігання спливання плеєру під час відтворення пісні",
"name": "Ненав'язливий програвач"
},
"video-toggle": {
"description": "Додає кнопку для перемикання між режимом відео і режимом пісні. Також може опціонально видаляти вкладку відео",
"menu": {

View File

@ -125,10 +125,80 @@
"dialog": {
"message": "دوبارہ شروع کرنے کے بعد زبان بدل دی جائے گی",
"title": "زبان بدل گئی ہے"
},
"label": "زبان",
"submenu": {
"to-help-translate": "ترجمہ میں مدد کرنا چاہتے ہیں؟ یہاں دبائیں"
}
},
"resume-on-start": "ایپ شروع ہونے پر آخری گانا دوبارہ شروع کریں",
"single-instance-lock": "ایک واحد مثال لاک",
"start-at-login": "لاگ ان پر شروع کریں",
"starting-page": {
"label": "شروعاتی صفحہ",
"unset": "غیر متعین"
},
"tray": {
"label": "سسٹم ٹرے",
"submenu": {
"disabled": "غیر فعال",
"enabled-and-hide-app": "فعال اور ایپ کو چھپائیں",
"enabled-and-show-app": "فعال اور ایپ دکھائیں",
"play-pause-on-click": "دبانے پر چلائیں/روکیں"
}
},
"visual-tweaks": {
"label": "بصری تبدیلیاں",
"submenu": {
"like-buttons": {
"default": "پہلے سے طے شدہ",
"force-show": "زبردستی دکھائیں",
"hide": "چھپائیں",
"label": "لائیک بٹن"
},
"remove-upgrade-button": "اپ گریڈ بٹن ہٹائیں",
"theme": {
"dialog": {
"button": {
"cancel": "منسوخ کریں",
"remove": "ہٹائیں"
},
"remove-theme": "کیا آپ واقعی کسٹم تھیم کو ہٹانا چاہتے ہیں؟",
"remove-theme-message": "یہ کسٹم تھیم کو ہٹا دے گا"
},
"label": "تھیم",
"submenu": {
"import-css-file": "کسٹم CSS فائل درآمد کریں",
"no-theme": "کوئی تھیم نہیں"
}
}
}
}
}
},
"plugins": {
"enabled": "فعال",
"label": "پلگ انز",
"new": "نیا"
},
"view": {
"label": "دیکھیں",
"submenu": {
"force-reload": "زبردستی دوبارہ لوڈ کریں",
"reload": "دوبارہ لوڈ کریں",
"reset-zoom": "اصل سائز",
"toggle-fullscreen": "پوری سکرین ٹوگل کریں",
"zoom-in": "زوم ان کریں",
"zoom-out": "زوم آؤٹ کریں"
}
}
},
"tray": {
"next": "اگلا",
"play-pause": "چلائیں/روکیں",
"previous": "پچھلا",
"quit": "باہر نکلیں",
"restart": "ایپ دوبارہ شروع کریں"
}
}
}

View File

@ -490,6 +490,9 @@
"button": "Tải xuống"
}
},
"equalizer": {
"name": "Cân bằng âm thanh"
},
"exponential-volume": {
"description": "Làm cho thanh trượt âm lượng theo cấp số nhân để dễ dàng chọn âm lượng thấp hơn.",
"name": "Âm lượng theo cấp số nhân"
@ -670,6 +673,7 @@
"listenbrainz": {
"token": "Nhập mã người dùng ListenBrainz"
},
"scrobble-alternative-title": "Dùng tiêu đề thay thế",
"scrobble-other-media": "Scrobber nội dung khác"
},
"name": "Scrobbler",
@ -720,8 +724,8 @@
"synced-lyrics": {
"description": "Cung cấp lời bài hát được đồng bộ hoá với các bài hát, sử dụng những nhà cung cấp như LRClib.",
"errors": {
"fetch": "⚠️ - Đã xảy ra lỗi khi tìm lời bài hát, Vui lòng thử lại sau.",
"not-found": "⚠️ - Không tìm thấy lời cho bài hát này."
"fetch": "⚠️\t\tĐã xảy ra lỗi khi tìm lời bài hát.\n\tVui lòng thử lại sau.",
"not-found": "⚠️ Không tìm thấy lời cho bài hát này."
},
"menu": {
"default-text-string": {
@ -731,6 +735,9 @@
"line-effect": {
"label": "Kiểu đường thẳng",
"submenu": {
"fancy": {
"label": "Màu mè"
},
"focus": {
"label": "Tập trung",
"tooltip": "Chỉ làm cho dòng hiện tại có màu trắng"
@ -750,6 +757,9 @@
"label": "Làm cho lời bài hát được đồng bộ hoàn hảo",
"tooltip": "Tính toán chính xác đến mili giây thời gian hiển thị dòng tiếp theo (có thể có tác động nhỏ đến hiệu suất)"
},
"romanization": {
"tooltip": "Nếu lời bài hát đang ở ngôn ngữ khác, thử hiển thị phiên bản bảng chữ cái La-tinh."
},
"show-lyrics-even-if-inexact": {
"label": "Hiển thị lời bài hát ngay cả khi không chính xác",
"tooltip": "Nếu không tìm thấy bài hát, plugin sẽ thử lại bằng truy vấn tìm kiếm khác.\nKết quả từ lần thử thứ hai có thể không chính xác."

View File

@ -199,7 +199,11 @@
"previous": "上一首",
"quit": "退出",
"restart": "重启应用",
"show": "显示窗口"
"show": "显示窗口",
"tooltip": {
"default": "YouTube Music",
"with-song-info": "YouTube Music: {{artist}} - {{title}}"
}
}
},
"plugins": {
@ -277,6 +281,7 @@
},
"amuse": {
"description": "为 6K Labs 的 Amuse 正在播放小部件添加 YouTube Music 支持",
"name": "Amuse",
"response": {
"query": "Amuse API服务器已在运行。使用 /query 以获取歌曲信息。"
}
@ -678,6 +683,7 @@
"listenbrainz": {
"token": "输入 ListenBrainz 用户令牌"
},
"scrobble-alternative-title": "使用替代标题",
"scrobble-other-media": "记录其他媒体文件"
},
"name": "歌曲记录器",
@ -728,7 +734,7 @@
"synced-lyrics": {
"description": "透过 LRClib 等服务提供滚动歌词显示。",
"errors": {
"fetch": "⚠️ - 获取歌词时发生错误。请稍后再试。",
"fetch": "⚠️ 获取歌词时发生错误。\n 请稍后再试。",
"not-found": "⚠️ 未找到此歌曲的歌词。"
},
"menu": {
@ -739,6 +745,10 @@
"line-effect": {
"label": "歌词行特效",
"submenu": {
"fancy": {
"label": "Fancy",
"tooltip": "在当前行上使用大的、类似应用的效果"
},
"focus": {
"label": "高亮",
"tooltip": "仅将当前歌词行显示为白色"
@ -758,6 +768,10 @@
"label": "让滚动歌词完全同步",
"tooltip": "以毫秒精度估算下句歌词的显示时间(可能对性能有小幅影响)"
},
"romanization": {
"label": "将歌词罗马化",
"tooltip": "如果歌词以不同语言显示,试着展示拉丁字母版本。"
},
"show-lyrics-even-if-inexact": {
"label": "即使时值不精确依然显示歌词",
"tooltip": "若首次搜索未找到该歌曲的歌词,插件将尝试用不同的查询方式重新获取。\n重试查询的结果可能不精确。"
@ -790,6 +804,10 @@
"description": "与 OBS 的 Tuna 插件集成",
"name": "Tuna OBS"
},
"unobtrusive-player": {
"description": "防止播放器在播放歌曲时弹出",
"name": "非侵扰式播放器"
},
"video-toggle": {
"description": "增加视频/音频模式间的切换按钮。兼具移除整个视频页面的功能",
"menu": {

View File

@ -27,7 +27,7 @@
"loaded": "i18n 已載入"
},
"second-instance": {
"receive-command": "使用協定來接收指令:「{{command}}」"
"receive-command": "使用通訊協定來接收指令:「{{command}}」"
},
"theme": {
"css-file-not-found": "CSS 檔案「{{cssFile}}」不存在,已忽略"
@ -53,13 +53,13 @@
"later": "稍後",
"restart-now": "立即重啟"
},
"detail": "\"{{pluginName}}\" 外掛需要重啟應用之後才會生效",
"message": "\"{{pluginName}}\" 需要重啟應用",
"title": "需要重啟應用"
"detail": "\"{{pluginName}}\" 外掛需要重新啟動之後才會生效",
"message": "\"{{pluginName}}\" 需要重新啟動",
"title": "需要重新啟動"
},
"unresponsive": {
"buttons": {
"quit": "離開",
"quit": "結束",
"relaunch": "重新啟動",
"wait": "等一下"
},
@ -83,11 +83,11 @@
"navigation": {
"label": "導覽列",
"submenu": {
"copy-current-url": "複製前頁面的網址",
"copy-current-url": "複製前頁面的網址",
"go-back": "回到上一頁",
"go-forward": "回到下一頁",
"quit": "退出",
"restart": "重啟應用"
"quit": "結束",
"restart": "重新啟動"
}
},
"options": {
@ -100,7 +100,7 @@
"disable-hardware-acceleration": "關閉硬體加速",
"edit-config-json": "編輯 config.json",
"override-user-agent": "覆寫使用者代理",
"restart-on-config-changes": "設定變更時自動重啟應用",
"restart-on-config-changes": "設定變更時自動重新啟動",
"set-proxy": {
"label": "設定代理伺服器",
"prompt": {
@ -123,7 +123,7 @@
},
"language": {
"dialog": {
"message": "語言會在重啟應用後變更",
"message": "語言會在重新啟動後變更",
"title": "語言已變更"
},
"label": "語言",
@ -142,9 +142,9 @@
"label": "系統匣",
"submenu": {
"disabled": "已停用",
"enabled-and-hide-app": "啟用並最小化應用",
"enabled-and-show-app": "啟用並顯示應用",
"play-pause-on-click": "點時播放/暫停"
"enabled-and-hide-app": "啟用並最小化應用程式",
"enabled-and-show-app": "啟用並顯示應用程式",
"play-pause-on-click": "點時播放/暫停"
}
},
"visual-tweaks": {
@ -177,17 +177,17 @@
}
},
"plugins": {
"enabled": "啟用",
"enabled": "啟用",
"label": "外掛功能",
"new": "新的"
},
"view": {
"label": "視",
"label": "視",
"submenu": {
"force-reload": "強制重新整理",
"reload": "重新整理",
"reset-zoom": "重設大小",
"toggle-fullscreen": "全螢幕",
"toggle-fullscreen": "切換全螢幕",
"zoom-in": "放大",
"zoom-out": "縮小"
}
@ -198,10 +198,10 @@
"play-pause": "播放/暫停",
"previous": "上一首",
"quit": "關閉",
"restart": "重程式",
"restart": "重新啟動應用程式",
"show": "顯示視窗",
"tooltip": {
"default": "Youtube Music",
"default": "YouTube Music",
"with-song-info": "YouTube Music: {{artist}} - {{title}}"
}
}
@ -235,7 +235,7 @@
"name": "隨歌曲色調變更主題"
},
"ambient-mode": {
"description": "影片周圍背景根據影片內容改變顏色, 讓觀眾在觀賞影片時更有臨場感",
"description": "影片周圍背景根據影片內容改變顏色讓觀眾在觀賞影片時更有臨場感",
"menu": {
"blur-amount": {
"label": "模糊等級",
@ -280,10 +280,10 @@
"name": "微光效果"
},
"amuse": {
"description": "加入支援 6K Labs 的 Amuse OBS 外掛以取得 Youtube Music 現正播放資訊",
"description": "加入支援 6K Labs 的 Amuse OBS 外掛以取得 YouTube Music 現正播放資訊",
"name": "Amuse",
"response": {
"query": "Amuse API 伺服器正在運中,使用 /query 以取得歌曲資訊。"
"query": "Amuse API 伺服器正在運中,使用 /query 以取得歌曲資訊。"
}
},
"api-server": {
@ -294,7 +294,7 @@
"allow": "允許",
"deny": "拒絕"
},
"message": "允許 {{ID}} ({{origin}}) 訪問 API 嗎?",
"message": "允許 {{ID}} ({{origin}}) 存取 API 嗎?",
"title": "API 驗證請求"
}
},
@ -314,7 +314,7 @@
"label": "主機名稱"
},
"port": {
"label": "接口"
"label": "連接埠"
}
},
"name": "API 伺服器 [Beta]",
@ -324,13 +324,13 @@
"title": "主機名稱"
},
"port": {
"label": "輸入 API 伺服器接口",
"title": "接口"
"label": "輸入 API 伺服器連接埠",
"title": "連接埠"
}
}
},
"audio-compressor": {
"description": "使用音效壓縮 (大聲部份的音量降低, 柔和部份的音量提高)",
"description": "使用音效壓縮 (大聲部份的音量降低柔和部份的音量提高)",
"name": "音效壓縮器"
},
"blur-nav-bar": {
@ -338,21 +338,21 @@
"name": "模糊導覽列"
},
"bypass-age-restrictions": {
"description": "繞過Youtube年齡驗證",
"description": "繞過 YouTube 年齡驗證",
"name": "繞過年齡驗證"
},
"captions-selector": {
"description": "音軌標題選擇",
"description": "YouTube Music 音軌字幕選擇",
"menu": {
"autoload": "自動選擇最後使用的標題",
"autoload": "自動選擇上次使用的字幕",
"disable-captions": "預設無標題"
},
"name": "標題選擇器",
"prompt": {
"selector": {
"label": "目前標題語言:{{language}}",
"label": "目前字幕語言:{{language}}",
"none": "無",
"title": "選擇標題語言"
"title": "選擇字幕語言"
}
},
"templates": {
@ -394,19 +394,19 @@
},
"discord": {
"backend": {
"already-connected": "已嘗試可用連",
"connected": "已連至 Discord",
"disconnected": "與 Discord 斷開連接"
"already-connected": "已嘗試可用連",
"connected": "已連至 Discord",
"disconnected": "與 Discord 中斷連線"
},
"description": "使用 Discord 狀態與你的好友分享你正在收聽的音樂",
"menu": {
"auto-reconnect": "自動重新連",
"auto-reconnect": "自動重新連",
"clear-activity": "清除狀態",
"clear-activity-after-timeout": "在音樂暫停後清除狀態",
"connected": "已連",
"disconnected": "已斷開連",
"connected": "已連",
"disconnected": "已斷開連",
"hide-duration-left": "隱藏音樂剩餘時間狀態",
"hide-github-button": "隱藏 Github 頁面按鈕",
"hide-github-button": "隱藏 GitHub 頁面按鈕",
"play-on-youtube-music": "顯示 Play on YouTube Music 按鈕",
"set-inactivity-timeout": "設定閒置狀態時長"
},
@ -425,8 +425,8 @@
"buttons": {
"ok": "完成"
},
"message": "啊!抱歉,下載失敗了…",
"title": "下載出現錯誤!"
"message": "啊抱歉下載失敗了…",
"title": "下載出現錯誤"
},
"start-download-playlist": {
"buttons": {
@ -448,15 +448,15 @@
"downloading-playlist": "正在下載播放清單 \"{{playlistTitle}}\" - 共 {{playlistSize}} 首歌 ({{playlistId}})",
"error-while-downloading": "無法下載 \"{{author}} - {{title}}\": {{error}}",
"folder-already-exists": "資料夾 {{playlistFolder}} 已經存在",
"getting-playlist-info": "正在取播放清單資訊…",
"getting-playlist-info": "正在取播放清單資訊…",
"loading": "載入中…",
"playlist-has-only-one-song": "播放清單內只有一首歌曲, 將直接下載",
"playlist-has-only-one-song": "播放清單內只有一首歌曲將直接下載",
"playlist-id-not-found": "沒有找到播放清單 ID",
"playlist-is-empty": "播放清單是空的",
"playlist-is-mix-or-private": "取播放清單資訊時發生錯誤:請確認非私人播放清單或是\"為你推薦的合輯\"\n\n{{error}}",
"playlist-is-mix-or-private": "取播放清單資訊時發生錯誤:請確認非私人播放清單或是\"為你推薦的合輯\"\n\n{{error}}",
"preparing-file": "正在準備檔案…",
"saving": "儲存中…",
"trying-to-get-playlist-id": "正在嘗試取播放清單 ID{{playlistId}}",
"trying-to-get-playlist-id": "正在嘗試取播放清單 ID{{playlistId}}",
"video-id-not-found": "未能找到該影片",
"writing-id3": "正在寫入 ID3 標籤…"
}
@ -510,7 +510,7 @@
"in-app-menu": {
"description": "使選單列變更為黑色或隨主題變色",
"menu": {
"hide-dom-window-controls": "隱藏DOM視窗控制"
"hide-dom-window-controls": "隱藏 DOM 視窗控制"
},
"name": "程式內選單列"
},
@ -525,13 +525,13 @@
},
"name": "第三方字幕",
"renderer": {
"fetched-lyrics": "為 Genius 取字幕"
"fetched-lyrics": "為 Genius 取字幕"
}
},
"music-together": {
"description": "與他人共享播放清單。當發起人播放歌曲時,其他成員也會同步收聽",
"description": "與他人共享播放清單。當主持人播放歌曲時,其他成員也會同步收聽",
"dialog": {
"enter-host": "輸入發起人 ID"
"enter-host": "輸入主持人 ID"
},
"internal": {
"save": "儲存",
@ -539,11 +539,11 @@
"unknown-user": "未知使用者"
},
"menu": {
"click-to-copy-id": "複製發起人 ID",
"click-to-copy-id": "複製主持人 ID",
"close": "同步關閉音樂",
"connected-users": "已連的使用者",
"disconnect": "斷開連共享音樂",
"empty-user": "無已連的使用者",
"connected-users": "已連的使用者",
"disconnect": "斷開連共享音樂",
"empty-user": "無已連的使用者",
"host": "發起共享音樂",
"join": "加入共享音樂",
"permission": {
@ -551,23 +551,23 @@
"host-only": "不允許加入的使用者控制播放清單及播放控制",
"playlist": "只允許加入的使用者控制播放清單"
},
"set-permission": "切換共享音樂播放權",
"set-permission": "切換共享音樂播放許可權",
"status": {
"disconnected": "已斷開連",
"guest": "以使用者身份加入",
"host": "以發起人身份加入"
"disconnected": "已斷開連",
"guest": "以訪客身份加入",
"host": "以主持人身份加入"
}
},
"name": "共享音樂 [Beta]",
"toast": {
"add-song-failed": "歌曲加入失敗",
"closed": "關閉共享音樂",
"disconnected": "共享音樂已斷開連",
"disconnected": "共享音樂已斷開連",
"host-failed": "發起共享音樂失敗",
"id-copied": "已複製發起人 ID",
"id-copy-failed": "複製發起人 ID 失敗",
"id-copied": "已複製主持人 ID",
"id-copy-failed": "複製主持人 ID 失敗",
"join-failed": "加入共享音樂失敗",
"joined": "加入共享音樂",
"joined": "加入共享音樂",
"permission-changed": "共享音樂播放權限已切換至 \"{{permission}}\"",
"remove-song-failed": "歌曲移除失敗",
"user-connected": "{{name}} 已加入共享音樂",
@ -583,20 +583,20 @@
"name": "停用 Google 登入"
},
"notifications": {
"description": "在歌曲播放時送一個系統通知 (可互動通知僅限Windows)",
"description": "在歌曲播放時送一個系統通知 (可互動通知僅限 Windows)",
"menu": {
"interactive": "可互動通知",
"interactive-settings": {
"label": "通知互動設定",
"submenu": {
"hide-button-text": "隱藏按鈕文字",
"refresh-on-play-pause": "在播放/暫停時刷新",
"tray-controls": "點系統閘圖示時開/關閉"
"refresh-on-play-pause": "在播放/暫停時重新整理",
"tray-controls": "點系統閘圖示時開/關閉"
}
},
"priority": "通知優先權",
"toast-style": "通知樣式",
"unpause-notification": "在取消暫停時送通知"
"unpause-notification": "在取消暫停時送通知"
},
"name": "歌曲播放通知"
},
@ -624,7 +624,7 @@
}
},
"playback-speed": {
"description": "傷心的人別聽慢歌, 新增一個滑桿控制歌曲速度",
"description": "傷心的人別聽慢歌新增一個滑桿控制歌曲速度",
"name": "控制歌曲速度",
"templates": {
"button": "速度"
@ -671,7 +671,7 @@
"dialog": {
"lastfm": {
"auth-failed": {
"message": "Last.fm認證失敗\n將隱藏彈窗直到重啟。",
"message": "Last.fm 認證失敗\n將隱藏彈窗直到重啟。",
"title": "認證失敗"
}
}
@ -683,8 +683,8 @@
"listenbrainz": {
"token": "輸入 ListenBrainz 使用者憑證"
},
"scrobble-alternative-title": "使用另類歌曲標題",
"scrobble-other-media": "紀錄其他媒體文件"
"scrobble-alternative-title": "使用替代歌曲標題",
"scrobble-other-media": "紀錄其他媒體檔案"
},
"name": "Scrobbler",
"prompt": {
@ -701,7 +701,7 @@
}
},
"shortcuts": {
"description": "使用全域快捷鍵控制音樂 (播放/暫停/下一首/上一首) + 透過覆寫媒體快捷鍵停用媒體OSD + 允許Ctrl/CMD + F來搜尋 + 支援Linux MPRIS媒體快捷鍵 + 更多自訂快捷鍵給進階使用者",
"description": "使用全域快捷鍵控制音樂 (播放/暫停/下一首/上一首) + 透過覆寫媒體快捷鍵停用媒體 OSD + 允許 Ctrl/CMD + F 來搜尋 + 支援 Linux MPRIS 媒體快捷鍵 + 更多自訂快捷鍵給進階使用者",
"menu": {
"override-media-keys": "覆寫媒體快捷鍵",
"set-keybinds": "設定全域歌曲控制"
@ -729,7 +729,7 @@
},
"sponsorblock": {
"description": "自動跳過贊助片段",
"name": "贊助阻擋"
"name": "SponsorBlock"
},
"synced-lyrics": {
"description": "使用 LRClib 等管道提供歌詞同步顯示。",
@ -747,26 +747,30 @@
"submenu": {
"fancy": {
"label": "絢麗",
"tooltip": "使用較為接近原生樣式並且放大前該行歌詞"
"tooltip": "使用較為接近原生樣式並且放大前該行歌詞"
},
"focus": {
"label": "高亮",
"tooltip": "高亮當前的歌詞"
"label": "聚焦",
"tooltip": "聚焦目前的歌詞"
},
"offset": {
"label": "凸行",
"tooltip": "凸行前的歌詞"
"tooltip": "凸行前的歌詞"
},
"scale": {
"label": "放大",
"tooltip": "放大前的歌詞"
"tooltip": "放大前的歌詞"
}
},
"tooltip": "選擇要使用的歌詞顯示效果"
},
"precise-timing": {
"label": "使歌詞完美同步",
"tooltip": "更精確的計算下一行歌詞的顯示(將會降低些許效能)"
"tooltip": "更精確的計算下一行歌詞的顯示 (將會降低些許效能)"
},
"romanization": {
"label": "羅馬拼音化歌詞",
"tooltip": "如果歌詞使用不同語言,嘗試使用拉丁文顯示。"
},
"show-lyrics-even-if-inexact": {
"label": "即使不精確依然強制顯示歌詞",
@ -793,13 +797,17 @@
"name": "工作列媒體控制"
},
"touchbar": {
"description": "為macOS使用者新增觸控列支援",
"description": "為 macOS 使用者新增觸控列支援",
"name": "觸控列 (Touchbar) 支援"
},
"tuna-obs": {
"description": "與 OBS 的 Tuna 外掛連",
"description": "與 OBS 的 Tuna 外掛連",
"name": "Tuna OBS"
},
"unobtrusive-player": {
"description": "防止播放器介面在點選歌曲後彈出",
"name": "低干擾播放器介面"
},
"video-toggle": {
"description": "新增一個按鈕可以控制影片/歌曲切換和完全移除整個影片頁面的功能",
"menu": {
@ -813,7 +821,7 @@
},
"force-hide": "強制移除整個影片頁面",
"mode": {
"label": "式",
"label": "式",
"submenu": {
"custom": "自訂樣式",
"disabled": "停用",
@ -827,11 +835,11 @@
}
},
"visualizer": {
"description": "新增一個視化音樂效果",
"description": "新增一個視化音樂效果",
"menu": {
"visualizer-type": "樣式"
"visualizer-type": "視覺化效果類型"
},
"name": "可視化音樂效果"
"name": "視覺化效果"
}
}
}

View File

@ -14,7 +14,7 @@ export interface APIServerConfig {
}
export const defaultAPIServerConfig: APIServerConfig = {
enabled: true,
enabled: false,
hostname: '0.0.0.0',
port: 26538,
authStrategy: AuthStrategy.AUTH_AT_FIRST,

View File

@ -56,6 +56,14 @@ let win: BrowserWindow;
let playingUrl: string;
const isYouTubeMusicPremium = async () => {
// If signed out, it is understood as non-premium
const isSignedIn = (await win.webContents.executeJavaScript(
'!!yt.config_.LOGGED_IN',
)) as boolean;
if (!isSignedIn) return false;
// If signed in, check if the upgrade button is present
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;
@ -63,10 +71,10 @@ const isYouTubeMusicPremium = async () => {
// 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}"])`;
const upgradeButton = `ytmusic-guide-entry-renderer:has(> tp-yt-paper-item > yt-icon path[d^="${upgradeBtnIconPathData}"])`;
return (await win.webContents.executeJavaScript(
`!document.querySelector('${selector}')`,
`!document.querySelector('${upgradeButton}')`,
)) as boolean;
};

View File

@ -1,4 +1,5 @@
import { DataConnection, Peer } from 'peerjs';
import { DataConnection, Peer, PeerErrorType } from 'peerjs';
import delay from 'delay';
import type { Permission, Profile, VideoData } from './types';
@ -54,16 +55,16 @@ export class Connection {
this._mode = 'host';
this.waitOpen.resolve(id);
});
this.peer.on('connection', (conn) => {
this.peer.on('connection', async (conn) => {
this._mode = 'host';
this.registerConnection(conn);
await this.registerConnection(conn);
});
this.peer.on('error', (err) => {
this._mode = 'disconnected';
this.waitOpen.reject(err);
this.connectionListeners.forEach((listener) => listener());
console.log(err);
console.error(err);
});
}
@ -74,7 +75,9 @@ export class Connection {
async connect(id: string) {
this._mode = 'guest';
const conn = this.peer.connect(id);
const conn = this.peer.connect(id, {
reliable: true,
});
await this.registerConnection(conn);
return conn;
}
@ -120,7 +123,17 @@ export class Connection {
/* privates */
private async registerConnection(conn: DataConnection) {
return new Promise<DataConnection>((resolve, reject) => {
this.peer.once('error', (err) => {
this.peer.once('error', async (err) => {
if (err.type === PeerErrorType.Network) {
// retrying after 10 seconds
await delay(10000);
try {
this.peer.reconnect();
return;
} catch {
//ignored
}
}
this._mode = 'disconnected';
reject(err);

View File

@ -0,0 +1,19 @@
import { createPlugin } from '@/utils';
import { t } from '@/i18n';
import { injectRm3 } from './scripts/rm3';
import { injectCpuTamer } from './scripts/cpu-tamer';
export default createPlugin({
name: () => t('plugins.performance-improvement.name'),
description: () => t('plugins.performance-improvement.description'),
restartNeeded: true,
addedVersion: '3.9.X',
config: {
enabled: true,
},
renderer() {
injectRm3();
injectCpuTamer();
},
});

View File

@ -0,0 +1,3 @@
export declare const injectCpuTamerByAnimationFrame: (
__CONTEXT__: unknown,
) => void;

View File

@ -0,0 +1,286 @@
/*
MIT License
Copyright 2021-2025 CY Fung
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
/* eslint-disable */
export const injectCpuTamerByAnimationFrame = ((__CONTEXT__) => {
'use strict';
const win = this instanceof Window ? this : window;
// Create a unique key for the script and check if it is already running
const hkey_script = 'nzsxclvflluv';
if (win[hkey_script]) throw new Error('Duplicated Userscript Calling'); // avoid duplicated scripting
win[hkey_script] = true;
/** @type {globalThis.PromiseConstructor} */
const Promise = (async () => { })().constructor; // YouTube hacks Promise in WaterFox Classic and "Promise.resolve(0)" nevers resolve.
const PromiseExternal = ((resolve_, reject_) => {
const h = (resolve, reject) => { resolve_ = resolve; reject_ = reject };
return class PromiseExternal extends Promise {
constructor(cb = h) {
super(cb);
if (cb === h) {
/** @type {(value: any) => void} */
this.resolve = resolve_;
/** @type {(reason?: any) => void} */
this.reject = reject_;
}
}
};
})();
const isGPUAccelerationAvailable = (() => {
// https://gist.github.com/cvan/042b2448fcecefafbb6a91469484cdf8
try {
const canvas = document.createElement('canvas');
return !!(canvas.getContext('webgl') || canvas.getContext('experimental-webgl'));
} catch (e) {
return false;
}
})();
if (!isGPUAccelerationAvailable) {
throw new Error('Your browser does not support GPU Acceleration. YouTube CPU Tamer by AnimationFrame is skipped.');
}
const timeupdateDT = (() => {
window.__j6YiAc__ = 1;
document.addEventListener('timeupdate', () => {
window.__j6YiAc__ = Date.now();
}, true);
let kz = -1;
try {
kz = top.__j6YiAc__;
} catch (e) {
}
return kz >= 1 ? () => top.__j6YiAc__ : () => window.__j6YiAc__;
})();
const cleanContext = async (win) => {
const waitFn = requestAnimationFrame; // shall have been binded to window
try {
let mx = 16; // MAX TRIAL
const frameId = 'vanillajs-iframe-v1'
let frame = document.getElementById(frameId);
let removeIframeFn = null;
if (!frame) {
frame = document.createElement('iframe');
frame.id = frameId;
const blobURL = typeof webkitCancelAnimationFrame === 'function' && typeof kagi === 'undefined' ? (frame.src = URL.createObjectURL(new Blob([], { type: 'text/html' }))) : null; // avoid Brave Crash
frame.sandbox = 'allow-same-origin'; // script cannot be run inside iframe but API can be obtained from iframe
let n = document.createElement('noscript'); // wrap into NOSCRPIT to avoid reflow (layouting)
n.appendChild(frame);
while (!document.documentElement && mx-- > 0) await new Promise(waitFn); // requestAnimationFrame here could get modified by YouTube engine
const root = document.documentElement;
root.appendChild(n); // throw error if root is null due to exceeding MAX TRIAL
if (blobURL) Promise.resolve().then(() => URL.revokeObjectURL(blobURL));
removeIframeFn = (setTimeout) => {
const removeIframeOnDocumentReady = (e) => {
e && win.removeEventListener("DOMContentLoaded", removeIframeOnDocumentReady, false);
e = n;
n = win = removeIframeFn = 0;
setTimeout ? setTimeout(() => e.remove(), 200) : e.remove();
}
if (!setTimeout || document.readyState !== 'loading') {
removeIframeOnDocumentReady();
} else {
win.addEventListener("DOMContentLoaded", removeIframeOnDocumentReady, false);
}
}
}
while (!frame.contentWindow && mx-- > 0) await new Promise(waitFn);
const fc = frame.contentWindow;
if (!fc) throw "window is not found."; // throw error if root is null due to exceeding MAX TRIAL
try {
const { requestAnimationFrame, setInterval, setTimeout, clearInterval, clearTimeout } = fc;
const res = { requestAnimationFrame, setInterval, setTimeout, clearInterval, clearTimeout };
for (let k in res) res[k] = res[k].bind(win); // necessary
if (removeIframeFn) Promise.resolve(res.setTimeout).then(removeIframeFn);
return res;
} catch (e) {
if (removeIframeFn) removeIframeFn();
return null;
}
} catch (e) {
console.warn(e);
return null;
}
};
cleanContext(win).then(__CONTEXT__ => {
if (!__CONTEXT__) return null;
const { requestAnimationFrame, setTimeout, setInterval, clearTimeout, clearInterval } = __CONTEXT__;
/** @type {Function|null} */
let afInterupter = null;
const getRAFHelper = () => {
const asc = document.createElement('a-f');
if (!('onanimationiteration' in asc)) {
return (resolve) => requestAnimationFrame(afInterupter = resolve);
}
asc.id = 'a-f';
let qr = null;
asc.onanimationiteration = function () {
if (qr !== null) qr = (qr(), null);
}
if (!document.getElementById('afscript')) {
const style = document.createElement('style');
style.id = 'afscript';
style.textContent = `
@keyFrames aF1 {
0% {
order: 0;
}
100% {
order: 1;
}
}
#a-f[id] {
visibility: collapse !important;
position: fixed !important;
display: block !important;
top: -100px !important;
left: -100px !important;
margin:0 !important;
padding:0 !important;
outline:0 !important;
border:0 !important;
z-index:-1 !important;
width: 0px !important;
height: 0px !important;
contain: strict !important;
pointer-events: none !important;
animation: 1ms steps(2, jump-none) 0ms infinite alternate forwards running aF1 !important;
}
`;
(document.head || document.documentElement).appendChild(style);
}
document.documentElement.insertBefore(asc, document.documentElement.firstChild);
return (resolve) => (qr = afInterupter = resolve);
};
/** @type {(resolve: () => void)} */
const rafPN = getRAFHelper(); // rAF will not execute if document is hidden
(() => {
let afPromiseP, afPromiseQ; // non-null
afPromiseP = afPromiseQ = { resolved: true }; // initial state for !uP && !uQ
let afix = 0;
const afResolve = async (rX) => {
await new Promise(rafPN);
rX.resolved = true;
const t = afix = (afix & 1073741823) + 1;
return rX.resolve(t), t;
};
const eFunc = async () => {
const uP = !afPromiseP.resolved ? afPromiseP : null;
const uQ = !afPromiseQ.resolved ? afPromiseQ : null;
let t = 0;
if (uP && uQ) {
const t1 = await uP;
const t2 = await uQ;
t = ((t1 - t2) & 536870912) === 0 ? t1 : t2; // = 0 for t1 - t2 = [0, 536870911], [1073741824, -536870913]
} else {
const vP = !uP ? (afPromiseP = new PromiseExternal()) : null;
const vQ = !uQ ? (afPromiseQ = new PromiseExternal()) : null;
if (uQ) await uQ; else if (uP) await uP;
if (vP) t = await afResolve(vP);
if (vQ) t = await afResolve(vQ);
}
return t;
}
const inExec = new Set();
const wFunc = async (handler, wStore) => {
try {
const ct = Date.now();
if (ct - timeupdateDT() < 800 && ct - wStore.dt < 800) {
const cid = wStore.cid;
inExec.add(cid);
const t = await eFunc();
const didNotRemove = inExec.delete(cid); // true for valid key
if (!didNotRemove || t === wStore.lastExecution) return;
wStore.lastExecution = t;
}
wStore.dt = ct;
handler();
} catch (e) {
console.error(e);
throw e;
}
};
const sFunc = (propFunc) => {
return (func, ms = 0, ...args) => {
if (typeof func === 'function') { // ignore all non-function parameter (e.g. string)
const wStore = { dt: Date.now() };
return (wStore.cid = propFunc(wFunc, ms, (args.length > 0 ? func.bind(null, ...args) : func), wStore));
} else {
return propFunc(func, ms, ...args);
}
};
};
win.setTimeout = sFunc(setTimeout);
win.setInterval = sFunc(setInterval);
const dFunc = (propFunc) => {
return (cid) => {
if (cid) inExec.delete(cid) || propFunc(cid);
};
};
win.clearTimeout = dFunc(clearTimeout);
win.clearInterval = dFunc(clearInterval);
try {
win.setTimeout.toString = setTimeout.toString.bind(setTimeout);
win.setInterval.toString = setInterval.toString.bind(setInterval);
win.clearTimeout.toString = clearTimeout.toString.bind(clearTimeout);
win.clearInterval.toString = clearInterval.toString.bind(clearInterval);
} catch (e) { console.warn(e) }
})();
let mInterupter = null;
setInterval(() => {
if (mInterupter === afInterupter) {
if (mInterupter !== null) afInterupter = mInterupter = (mInterupter(), null);
} else {
mInterupter = afInterupter;
}
}, 125);
});
});

View File

@ -0,0 +1,3 @@
export declare const injectCpuTamerByDomMutation: (
__CONTEXT__: unknown,
) => void;

View File

@ -0,0 +1,281 @@
/*
MIT License
Copyright 2024-2025 CY Fung
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
/* eslint-disable */
export const injectCpuTamerByDomMutation = ((__CONTEXT__) => {
'use strict';
const win = this instanceof Window ? this : window;
// Create a unique key for the script and check if it is already running
const hkey_script = 'nzsxclvflluv';
if (win[hkey_script]) throw new Error('Duplicated Userscript Calling'); // avoid duplicated scripting
win[hkey_script] = true;
/** @type {globalThis.PromiseConstructor} */
const Promise = (async () => { })().constructor; // YouTube hacks Promise in WaterFox Classic and "Promise.resolve(0)" nevers resolve.
const PromiseExternal = ((resolve_, reject_) => {
const h = (resolve, reject) => { resolve_ = resolve; reject_ = reject };
return class PromiseExternal extends Promise {
constructor(cb = h) {
super(cb);
if (cb === h) {
/** @type {(value: any) => void} */
this.resolve = resolve_;
/** @type {(reason?: any) => void} */
this.reject = reject_;
}
}
};
})();
// for future use
/*
const timeupdateDT = (() => {
window.__j6YiAc__ = 1;
document.addEventListener('timeupdate', () => {
window.__j6YiAc__ = Date.now();
}, true);
let kz = -1;
try {
kz = top.__j6YiAc__;
} catch (e) {
}
return kz >= 1 ? () => top.__j6YiAc__ : () => window.__j6YiAc__;
})();
*/
const cleanContext = async (win) => {
const waitFn = requestAnimationFrame; // shall have been binded to window
try {
let mx = 16; // MAX TRIAL
const frameId = 'vanillajs-iframe-v1'
let frame = document.getElementById(frameId);
let removeIframeFn = null;
if (!frame) {
frame = document.createElement('iframe');
frame.id = frameId;
const blobURL = typeof webkitCancelAnimationFrame === 'function' && typeof kagi === 'undefined' ? (frame.src = URL.createObjectURL(new Blob([], { type: 'text/html' }))) : null; // avoid Brave Crash
frame.sandbox = 'allow-same-origin'; // script cannot be run inside iframe but API can be obtained from iframe
let n = document.createElement('noscript'); // wrap into NOSCRPIT to avoid reflow (layouting)
n.appendChild(frame);
while (!document.documentElement && mx-- > 0) await new Promise(waitFn); // requestAnimationFrame here could get modified by YouTube engine
const root = document.documentElement;
root.appendChild(n); // throw error if root is null due to exceeding MAX TRIAL
if (blobURL) Promise.resolve().then(() => URL.revokeObjectURL(blobURL));
removeIframeFn = (setTimeout) => {
const removeIframeOnDocumentReady = (e) => {
e && win.removeEventListener("DOMContentLoaded", removeIframeOnDocumentReady, false);
e = n;
n = win = removeIframeFn = 0;
setTimeout ? setTimeout(() => e.remove(), 200) : e.remove();
}
if (!setTimeout || document.readyState !== 'loading') {
removeIframeOnDocumentReady();
} else {
win.addEventListener("DOMContentLoaded", removeIframeOnDocumentReady, false);
}
}
}
while (!frame.contentWindow && mx-- > 0) await new Promise(waitFn);
const fc = frame.contentWindow;
if (!fc) throw "window is not found."; // throw error if root is null due to exceeding MAX TRIAL
try {
const { requestAnimationFrame, setInterval, setTimeout, clearInterval, clearTimeout } = fc;
const res = { requestAnimationFrame, setInterval, setTimeout, clearInterval, clearTimeout };
for (let k in res) res[k] = res[k].bind(win); // necessary
if (removeIframeFn) Promise.resolve(res.setTimeout).then(removeIframeFn);
return res;
} catch (e) {
if (removeIframeFn) removeIframeFn();
return null;
}
} catch (e) {
console.warn(e);
return null;
}
};
const { _setAttribute, _insertBefore, _hasAttribute } = (() => {
let _setAttribute = Element.prototype.setAttribute;
try {
_setAttribute = ShadyDOM.nativeMethods.setAttribute || _setAttribute;
} catch (e) { }
let _hasAttribute = Element.prototype.hasAttribute;
try {
_hasAttribute = ShadyDOM.nativeMethods.hasAttribute || _hasAttribute;
} catch (e) { }
let _insertBefore = Node.prototype.insertBefore;
try {
_insertBefore = ShadyDOM.nativeMethods.insertBefore || _insertBefore;
} catch (e) { }
return { _setAttribute, _insertBefore, _hasAttribute};
})();
cleanContext(win).then(__CONTEXT__ => {
if (!__CONTEXT__) return null;
const { setTimeout, setInterval, clearTimeout, clearInterval } = __CONTEXT__;
/*
/-** @type {Function|null} *-/
// let afInterupter = null;
*/
const getDMHelper = () => {
let _dm = document.getElementById('d-m');
if (!_dm) {
_dm = document.createElementNS('http://www.w3.org/2000/svg', 'defs');
_dm.id = 'd-m';
_insertBefore.call(document.documentElement, _dm, document.documentElement.firstChild);
}
const dm = _dm;
dm._setAttribute = _setAttribute;
dm._hasAttribute = _hasAttribute;
let j = 0;
let attributeName_;
while (dm._hasAttribute(attributeName_ = `dm-${Math.floor(Math.random() * 314159265359 + 314159265359).toString(36)}`)) {
// none
}
const attributeName = attributeName_;
let sr = null;
const mo = new MutationObserver(() => {
const sr_ = sr;
if (sr_ !== null) {
sr = null;
if (j > 8) j = 0;
sr_.resolve();
}
});
mo.observe(document, { childList: true, subtree: true, attributes: true });
return () => {
return sr || (sr = (dm._setAttribute(attributeName, ++j), (new PromiseExternal()))); // mutationcallback in next macrotask
};
};
/** @type {(resolve: () => void)} */
const dmSN = getDMHelper(); // dm will execute even if document is hidden
(() => {
let dmPromiseP, dmPromiseQ; // non-null
dmPromiseP = dmPromiseQ = { resolved: true }; // initial state for !uP && !uQ
let dmix = 0;
const dmResolve = async (rX) => {
await dmSN();
rX.resolved = true;
const t = dmix = (dmix & 1073741823) + 1;
return rX.resolve(t), t;
};
const eFunc = async () => {
const uP = !dmPromiseP.resolved ? dmPromiseP : null;
const uQ = !dmPromiseQ.resolved ? dmPromiseQ : null;
let t = 0;
if (uP && uQ) {
const t1 = await uP;
const t2 = await uQ;
t = ((t1 - t2) & 536870912) === 0 ? t1 : t2; // = 0 for t1 - t2 = [0, 536870911], [1073741824, -536870913]
} else {
const vP = !uP ? (dmPromiseP = new PromiseExternal()) : null;
const vQ = !uQ ? (dmPromiseQ = new PromiseExternal()) : null;
if (uQ) await uQ; else if (uP) await uP;
if (vP) t = await dmResolve(vP);
if (vQ) t = await dmResolve(vQ);
}
return t;
}
const inExec = new Set();
const wFunc = async (handler, wStore) => {
try {
const ct = Date.now();
if (ct - wStore.dt < 800) {
const cid = wStore.cid;
inExec.add(cid);
const t = await eFunc();
const didNotRemove = inExec.delete(cid); // true for valid key
if (!didNotRemove || t === wStore.lastExecution) return;
wStore.lastExecution = t;
}
wStore.dt = ct;
handler();
} catch (e) {
console.error(e);
throw e;
}
};
const sFunc = (propFunc) => {
return (func, ms = 0, ...args) => {
if (typeof func === 'function') { // ignore all non-function parameter (e.g. string)
const wStore = { dt: Date.now() };
return (wStore.cid = propFunc(wFunc, ms, (args.length > 0 ? func.bind(null, ...args) : func), wStore));
} else {
return propFunc(func, ms, ...args);
}
};
};
win.setTimeout = sFunc(setTimeout);
win.setInterval = sFunc(setInterval);
const dFunc = (propFunc) => {
return (cid) => {
if (cid) inExec.delete(cid) || propFunc(cid);
};
};
win.clearTimeout = dFunc(clearTimeout);
win.clearInterval = dFunc(clearInterval);
try {
win.setTimeout.toString = setTimeout.toString.bind(setTimeout);
win.setInterval.toString = setInterval.toString.bind(setInterval);
win.clearTimeout.toString = clearTimeout.toString.bind(clearTimeout);
win.clearInterval.toString = clearInterval.toString.bind(clearInterval);
} catch (e) { console.warn(e) }
})();
/*
let mInterupter = null;
setInterval(() => {
if (mInterupter === afInterupter) {
if (mInterupter !== null) afInterupter = mInterupter = (mInterupter(), null);
} else {
mInterupter = afInterupter;
}
}, 125);
*/
});
});

View File

@ -0,0 +1,22 @@
import { injectCpuTamerByAnimationFrame } from './cpu-tamer-by-animationframe';
import { injectCpuTamerByDomMutation } from './cpu-tamer-by-dom-mutation';
const isGPUAccelerationAvailable = () => {
// https://gist.github.com/cvan/042b2448fcecefafbb6a91469484cdf8
try {
const canvas = document.createElement('canvas');
return !!(
canvas.getContext('webgl') || canvas.getContext('experimental-webgl')
);
} catch {
return false;
}
};
export const injectCpuTamer = () => {
if (isGPUAccelerationAvailable()) {
injectCpuTamerByAnimationFrame(null);
} else {
injectCpuTamerByDomMutation(null);
}
};

View File

@ -0,0 +1 @@
export * from './rm3';

View File

@ -0,0 +1,121 @@
declare class Rm3LinkedArrayNode<T> {
value: T;
next: Rm3LinkedArrayNode<T> | null;
prev: Rm3LinkedArrayNode<T> | null;
constructor(value: T);
}
declare class Rm3LinkedArray<T> {
head: Rm3LinkedArrayNode<T> | null;
tail: Rm3LinkedArrayNode<T> | null;
length: number;
constructor();
push(value: T): number;
pop(): T | undefined;
unshift(value: T): number;
shift(): T | undefined;
size(): number;
getNode(index: number): Rm3LinkedArrayNode<T> | null;
get(index: number): T | undefined;
findNode(value: T): { node: Rm3LinkedArrayNode<T> | null; index: number };
toArray(): T[];
insertBeforeNode(node: Rm3LinkedArrayNode<T> | null, newValue: T): boolean;
insertAfterNode(node: Rm3LinkedArrayNode<T> | null, newValue: T): boolean;
insertBefore(existingValue: T, newValue: T): boolean;
insertAfter(existingValue: T, newValue: T): boolean;
deleteNode(node: Rm3LinkedArrayNode<T>): boolean; // Note: Original JS allowed deleting null, but TS implies non-null here
static Node: typeof Rm3LinkedArrayNode;
}
// Define the structure of the internal LimitedSizeSet class
declare class Rm3LimitedSizeSet<T> extends Set<T> {
limit: number;
constructor(n: number);
add(key: T): this;
removeAdd(key: T): void;
}
// Define the structure of the entryRecord tuple used internally
// [ WeakRef<HTMLElement>, attached time, detached time, time of change, inside availablePool, reuse count ]
type Rm3EntryRecord = [
WeakRef<HTMLElement>,
number,
number,
number,
boolean,
number,
];
// Define the interface for the exported rm3 object
export interface Rm3 {
/**
* Removes duplicate values from an array.
* @param array The input array.
* @returns A new array with unique values.
*/
uniq: <T>(array: T[]) => T[];
/**
* [Debug only] The current page URL. Only available if DEBUG_OPT was true.
*/
location?: string;
/**
* [Debug only] Inspects the document for elements with a polymerController and returns their unique node names.
* @returns An array of unique node names.
*/
inspect: () => string[];
/**
* A Set containing records of element operations (attach/detach).
* Each record tracks an element's lifecycle state.
*/
operations: Set<Rm3EntryRecord>;
/**
* A Map where keys are component identifiers (e.g., "creatorTag.componentTag")
* and values are LinkedArrays of potentially reusable EntryRecords for detached elements.
*/
availablePools: Map<string, Rm3LinkedArray<Rm3EntryRecord>>;
/**
* Checks the parent status of elements tracked in the operations set.
* Primarily for elements that have been detached (detached time > 0).
* @returns An array of tuples: [elementExists: boolean, nodeName: string | undefined, isParentNull: boolean]
*/
checkWhetherUnderParent: () => [boolean, string | undefined, boolean][];
/**
* Gets a list of unique element tag names (from `element.is`) that have been tracked.
* @returns An array of unique tag names.
*/
hookTags: () => string[];
/**
* [Debug only] A Set containing tags that have had their methods hooked. Only available if DEBUG_OPT was true.
*/
hookTos?: Set<string>;
/**
* [Debug only] A function that returns an array representation of the reuse record log. Only available if DEBUG_OPT was true.
* @returns An array of tuples: [timestamp, tagName, entryRecord]
*/
reuseRecord?: () => [number, string, Rm3EntryRecord][];
/**
* [Debug only] A Map tracking the reuse count per component tag name. Only available if DEBUG_OPT was true.
*/
reuseCount_?: Map<string, number>;
/**
* A counter for the total number of times elements have been reused.
*/
reuseCount: number;
}
export const rm3: Rm3;
export function injectRm3(): void;

View File

@ -0,0 +1,828 @@
/*
MIT License
Copyright 2024-2025 CY Fung
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
/* eslint-disable */
export const rm3 = {};
export const injectRm3 = () => {
const DEBUG_OPT = false;
const CONFIRM_TIME = 4000;
const CHECK_INTERVAL = 400;
const DEBUG_dataChangeReflection = true;
/** @type {globalThis.PromiseConstructor} */
const Promise = (async () => {})().constructor; // YouTube hacks Promise in WaterFox Classic and "Promise.resolve(0)" nevers resolve.
// https://qiita.com/piroor/items/02885998c9f76f45bfa0
// https://gist.github.com/piroor/829ecb32a52c2a42e5393bbeebe5e63f
function uniq(array) {
return [...new Set(array)];
}
rm3.uniq = uniq; // [[debug]]
DEBUG_OPT && (rm3.location = location.href);
rm3.inspect = () => {
return uniq(
[...document.getElementsByTagName('*')]
.filter((e) => e?.polymerController?.createComponent_)
.map((e) => e.nodeName),
); // [[debug]]
};
const insp = (o) => o ? o.polymerController || o.inst || o || 0 : o || 0;
const indr = (o) => insp(o).$ || o.$ || 0;
const getProto = (element) => {
if (element) {
const cnt = insp(element);
return cnt.constructor.prototype || null;
}
return null;
};
const LinkedArray = (() => {
class Node {
constructor(value) {
this.value = value;
this.next = null;
this.prev = null;
}
}
class LinkedArray {
constructor() {
this.head = null;
this.tail = null;
this.length = 0;
}
push(value) {
const newNode = new Node(value);
if (this.length === 0) {
this.head = newNode;
this.tail = newNode;
} else {
this.tail.next = newNode;
newNode.prev = this.tail;
this.tail = newNode;
}
this.length++;
return this.length;
}
pop() {
if (this.length === 0) return undefined;
const removedNode = this.tail;
if (this.length === 1) {
this.head = null;
this.tail = null;
} else {
this.tail = removedNode.prev;
this.tail.next = null;
removedNode.prev = null;
}
this.length--;
return removedNode.value;
}
unshift(value) {
const newNode = new Node(value);
if (this.length === 0) {
this.head = newNode;
this.tail = newNode;
} else {
newNode.next = this.head;
this.head.prev = newNode;
this.head = newNode;
}
this.length++;
return this.length;
}
shift() {
if (this.length === 0) return undefined;
const removedNode = this.head;
if (this.length === 1) {
this.head = null;
this.tail = null;
} else {
this.head = removedNode.next;
this.head.prev = null;
removedNode.next = null;
}
this.length--;
return removedNode.value;
}
size() {
return this.length;
}
// Get a node by index (0-based)
getNode(index) {
if (index < 0 || index >= this.length) return null;
let current;
let counter;
// Optimization: start from closest end
if (index < this.length / 2) {
current = this.head;
counter = 0;
while (counter !== index) {
current = current.next;
counter++;
}
} else {
current = this.tail;
counter = this.length - 1;
while (counter !== index) {
current = current.prev;
counter--;
}
}
return current;
}
// Get value by index
get(index) {
const node = this.getNode(index);
return node ? node.value : undefined;
}
// Find the first node with the given value and return both node and index
findNode(value) {
let current = this.head;
let idx = 0;
while (current) {
if (current.value === value) {
return { node: current, index: idx };
}
current = current.next;
idx++;
}
return { node: null, index: -1 };
}
toArray() {
const arr = [];
let current = this.head;
while (current) {
arr.push(current.value);
current = current.next;
}
return arr;
}
// Insert a new value before a given node (provided you already have the node reference)
insertBeforeNode(node, newValue) {
if (!node) {
this.unshift(newValue);
return true;
}
if (node === this.head) {
// If the target is the head, just unshift
this.unshift(newValue);
} else {
const newNode = new Node(newValue);
const prevNode = node.prev;
prevNode.next = newNode;
newNode.prev = prevNode;
newNode.next = node;
node.prev = newNode;
this.length++;
}
return true;
}
// Insert a new value after a given node (provided you already have the node reference)
insertAfterNode(node, newValue) {
if (!node) {
this.push(newValue);
return true;
}
if (node === this.tail) {
// If the target is the tail, just push
this.push(newValue);
} else {
const newNode = new Node(newValue);
const nextNode = node.next;
node.next = newNode;
newNode.prev = node;
newNode.next = nextNode;
nextNode.prev = newNode;
this.length++;
}
return true;
}
// Insert a new value before the first occurrence of an existing value (search by value)
insertBefore(existingValue, newValue) {
const { node } = this.findNode(existingValue);
if (!node) return false; // Not found
return this.insertBeforeNode(node, newValue);
}
// Insert a new value after the first occurrence of an existing value (search by value)
insertAfter(existingValue, newValue) {
const { node } = this.findNode(existingValue);
if (!node) return false; // Not found
return this.insertAfterNode(node, newValue);
}
// Delete a given node from the list
deleteNode(node) {
if (!node) return false;
if (this.length === 1 && node === this.head && node === this.tail) {
// Only one element in the list
this.head = null;
this.tail = null;
} else if (node === this.head) {
// Node is the head
this.head = node.next;
this.head.prev = null;
node.next = null;
} else if (node === this.tail) {
// Node is the tail
this.tail = node.prev;
this.tail.next = null;
node.prev = null;
} else {
// Node is in the middle
const prevNode = node.prev;
const nextNode = node.next;
prevNode.next = nextNode;
nextNode.prev = prevNode;
node.prev = null;
node.next = null;
}
this.length--;
return true;
}
}
LinkedArray.Node = Node;
return LinkedArray;
})();
class LimitedSizeSet extends Set {
constructor(n) {
super();
this.limit = n;
}
add(key) {
if (!super.has(key)) {
super.add(key);
let n = super.size - this.limit;
if (n > 0) {
const iterator = super.values();
do {
const firstKey = iterator.next().value; // Get the first (oldest) key
super.delete(firstKey); // Delete the oldest key
} while (--n > 0);
}
}
}
removeAdd(key) {
super.delete(key);
this.add(key);
}
}
if (
!document.createElement9512 &&
typeof document.createElement === 'function' &&
document.createElement.length === 1
) {
// sizing of Map / Set. Shall limit ?
const hookTos = new Set(); // [[debug]]
DEBUG_OPT && (rm3.hookTos = hookTos);
// const reusePool = new Map(); // xx858
const entryRecords = new WeakMap(); // a weak link between element and record
// rm3.list = [];
const operations = rm3.operations = new Set(); // to find out the "oldest elements"
const availablePools = rm3.availablePools = new Map(); // those "old elements" can be used
let lastTimeCheck = 0;
const reuseRecord_ = new LimitedSizeSet(256); // [[debug]]
const reuseCount_ = new Map();
let noTimeCheck = false;
// const defaultValues = new Map();
// const noValues = new Map();
const timeCheck = () => {
// regularly check elements are old enough to put into the available pools
// note: the characterists of YouTube components are non-volatile. So don't need to waste time to check weakRef.deref() is null or not for removing in operations.
const ct = Date.now();
if (ct - lastTimeCheck < CHECK_INTERVAL || noTimeCheck) return;
lastTimeCheck = ct;
noTimeCheck = true;
// 16,777,216
if (hookTos.size > 777216) hookTos.clear(); // just debug usage, dont concern
if (operations.size > 7777216) {
// extremely old elements in operations mean they have no attach/detach action. so no reuse as well. they are just trash in memory.
// as no checking of the weakRef.deref() being null or not, those trash could be already cleaned. However we don't concern this.
// (not to count whether they are actual memory trash or not)
const half = operations.size >>> 1;
let i = 0;
for (const value of operations) {
if (i++ > half) break;
operations.delete(value);
}
}
// {
// // smallest to largest
// // past to recent
// const iterator = operations[Symbol.iterator]();
// console.log(1831, '------------------------')
// while (true) {
// const iteratorResult = iterator.next(); // 順番に値を取りだす
// if (iteratorResult.done) break; // 取り出し終えたなら、break
// console.log(1835, iteratorResult.value[3])
// }
// console.log(1839, '------------------------')
// }
// Set iterator
// s.add(2) s.add(6) s.add(1) s.add(3)
// next: 2 -> 6 -> 1 -> 3
// op1 (oldest) -> op2 -> op3 -> op4 (latest)
const iterator = operations[Symbol.iterator]();
const targetTime = ct - CONFIRM_TIME;
const pivotNodes = new WeakMap();
while (true) {
const iteratorResult = iterator.next(); // 順番に値を取りだす
if (iteratorResult.done) break; // 取り出し終えたなら、break
const entryRecord = iteratorResult.value;
if (entryRecord[3] > targetTime) break;
if (!entryRecord[4] && entryRecord[1] < 0 && entryRecord[2] > 0) {
const element = entryRecord[0].deref();
const eKey = (element || 0).__rm3Tag003__;
if (!eKey) {
operations.delete(entryRecord);
} else if (
element.isConnected === false &&
insp(element).isAttached === false
) {
entryRecord[4] = true;
let availablePool = availablePools.get(eKey);
if (!availablePool)
availablePools.set(eKey, availablePool = new LinkedArray());
if (!(availablePool instanceof LinkedArray)) throw new Error();
DEBUG_OPT &&
console.log(3885, 'add key', eKey, availablePools.size);
// rm3.showSize = ()=>availablePools.size
// setTimeout(()=>{
// // window?.euu1 = availablePools
// // window?.euu2 = availablePools.size
// console.log(availablePools.size)
// }, 8000)
let pivotNode = pivotNodes.get(availablePool);
if (!pivotNode)
pivotNodes.set(availablePool, pivotNode = availablePool.head); // cached the previous newest node (head) as pivotNode
availablePool.insertBeforeNode(pivotNode, entryRecord); // head = newest, tail = oldest
}
}
}
noTimeCheck = false;
};
const attachedDefine = function () {
Promise.resolve().then(timeCheck);
try {
const hostElement = this?.hostElement;
if (hostElement instanceof HTMLElement) {
const entryRecord = entryRecords.get(hostElement);
if (
entryRecord &&
entryRecord[0].deref() === hostElement &&
hostElement.isConnected === true &&
this?.isAttached === true
) {
noTimeCheck = true;
const ct = Date.now();
entryRecord[1] = ct;
entryRecord[2] = -1;
entryRecord[3] = ct;
operations.delete(entryRecord);
operations.add(entryRecord);
noTimeCheck = false;
// note: because of performance prespective, deletion for availablePools[eKey]'s linked element would not be done here.
// entryRecord[4] is not required to be updated here.
}
}
} catch (e) {}
return this.attached9512();
};
const detachedDefine = function () {
Promise.resolve().then(timeCheck);
try {
const hostElement = this?.hostElement;
if (hostElement instanceof HTMLElement) {
const entryRecord = entryRecords.get(hostElement);
if (
entryRecord &&
entryRecord[0].deref() === hostElement &&
hostElement.isConnected === false &&
this?.isAttached === false
) {
noTimeCheck = true;
const ct = Date.now();
entryRecord[2] = ct;
entryRecord[1] = -1;
entryRecord[3] = ct;
operations.delete(entryRecord);
operations.add(entryRecord);
noTimeCheck = false;
// note: because of performance prespective, deletion for availablePools[eKey]'s linked element would not be done here.
// entryRecord[4] is not required to be updated here.
}
}
} catch (e) {}
return this.detached9512();
};
// function cpy(x) {
// if (!x) return x;
// try {
// if (typeof x === 'object' && typeof x.length ==='number' && typeof x.slice === 'function') {
// x = x.slice(0)
// } else if (typeof x === 'object' && !x.length) {
// x = JSON.parse(JSON.stringify(x));
// } else {
// return Object.assign({}, x);
// }
// } catch (e) { }
// return x;
// }
async function digestMessage(message) {
const msgUint8 = new TextEncoder().encode(message); // (utf-8 の) Uint8Array にエンコードする
const hashBuffer = await crypto.subtle.digest('SHA-256', msgUint8); // メッセージをハッシュする
const hashArray = Array.from(new Uint8Array(hashBuffer)); // バッファーをバイト列に変換する
const hashHex = hashArray
.map((b) => b.toString(16).padStart(2, '0'))
.join(''); // バイト列を 16 進文字列に変換する
return hashHex.toUpperCase();
}
let onPageContainer = null;
const createComponentDefine_ = function (a, b, c) {
Promise.resolve().then(timeCheck);
const creatorTag = this?.is || this?.nodeName?.toLowerCase() || '';
const componentTag = typeof a === 'string' ? a : (a || 0).component || '';
const eKey =
creatorTag && componentTag ? `${creatorTag}.${componentTag}` : '*'; // '*' for play-safe
const availablePool = availablePools.get(eKey);
try {
if (availablePool instanceof LinkedArray) {
noTimeCheck = true;
let node = availablePool.tail; // oldest
while (node instanceof LinkedArray.Node) {
const entryRecord = node.value;
const prevNode = node.prev;
let ok = false;
let elm = null;
if (entryRecord[1] < 0 && entryRecord[2] > 0 && entryRecord[4]) {
elm = entryRecord[0].deref();
// elm && console.log(3882, (elm.__shady_native_textContent || elm.textContent))
if (
elm &&
elm instanceof HTMLElement &&
elm.isConnected === false &&
insp(elm).isAttached === false &&
elm.parentNode === null
) {
ok = true;
}
}
if (ok) {
// useEntryRecord = entryRecord;
entryRecord[4] = false;
// console.log('nodeDeleted', 1, entryRecord[0].deref().nodeName)
availablePool.deleteNode(node);
// break;
if (!onPageContainer) {
let p = document.createElement('noscript');
document.body.prepend(p);
onPageContainer = p;
}
onPageContainer.appendChild(elm); // to fix some issues for the rendered elements
const cnt = insp(elm);
cnt.__dataInvalid = false;
// cnt._initializeProtoProperties(cnt.data)
// window.meaa = cnt.$.container;
if (typeof (cnt.__data || 0) === 'object') {
cnt.__data = Object.assign({}, cnt.__data);
}
cnt.__dataPending = {};
cnt.__dataOld = {};
try {
cnt.markDirty();
} catch (e) {}
try {
cnt.markDirtyVisibilityObserver();
} catch (e) {}
try {
cnt.wasPrescan = cnt.wasVisible = !1;
} catch (e) {}
// try{
// cnt._setPendingProperty('data', Object.assign({}, cntData), !0);
// }catch(e){}
// try {
// cnt._flushProperties();
// } catch (e) { }
if (DEBUG_OPT && DEBUG_dataChangeReflection) {
let jC1 = null;
let jC2 = null;
const jKey = `${Math.floor(Math.random() * 314159265359 + 314159265359).toString(36)}`;
try {
jC1 =
cnt.hostElement.__shady_native_textContent ||
cnt.hostElement.textContent;
// console.log(83802, jKey, (cnt.hostElement.__shady_native_textContent || cnt.hostElement.textContent))
} catch (e) {
console.warn(e);
}
setTimeout(() => {
try {
jC2 =
cnt.hostElement.__shady_native_textContent ||
cnt.hostElement.textContent;
// console.log(83804, jKey, (cnt.hostElement.__shady_native_textContent || cnt.hostElement.textContent))
} catch (e) {
console.warn(e);
}
(async () => {
jC1 = await digestMessage(jC1);
jC2 = await digestMessage(jC2);
console.log(
83804,
jKey,
jC1.substring(0, 7),
jC2.substring(0, 7),
);
})();
}, 1000);
}
if (entryRecord[5] < 1e9) entryRecord[5] += 1;
DEBUG_OPT &&
Promise.resolve().then(() =>
console.log(`${eKey} reuse`, entryRecord),
); // give some time for attach process
DEBUG_OPT && reuseRecord_.add([Date.now(), cnt.is, entryRecord]);
DEBUG_OPT &&
reuseCount_.set(cnt.is, (reuseCount_.get(cnt.is) || 0) + 1);
if (rm3.reuseCount < 1e9) rm3.reuseCount++;
return elm;
}
// console.log('condi88', entryRecord[1] < 0 , entryRecord[2] > 0 , !!entryRecord[4], !!entryRecord[0].deref())
entryRecord[4] = false;
// console.log(entryRecord);
// console.log('nodeDeleted',2, entryRecord[0]?.deref()?.nodeName)
availablePool.deleteNode(node);
node = prevNode;
}
// for(const ) availablePool
// noTimeCheck = false;
}
} catch (e) {
console.warn(e);
}
noTimeCheck = false;
// console.log('createComponentDefine_', a, b, c)
// if (!reusePool.has(componentTag)) reusePool.set(componentTag, new LinkedArray()); // xx858
// const pool = reusePool.get(componentTag); // xx858
// if (!(pool instanceof LinkedArray)) throw new Error(); // xx858
const newElement = this.createComponent9512_(a, b, c);
// if(componentTag.indexOf( 'ticker')>=0)console.log(1883, a,newElement)
try {
const cntE = insp(newElement);
if (!cntE.attached9512 && cntE.attached) {
const cProtoE = getProto(newElement);
if (cProtoE.attached === cntE.attached) {
if (
!cProtoE.attached9512 &&
typeof cProtoE.attached === 'function' &&
cProtoE.attached.length === 0
) {
cProtoE.attached9512 = cProtoE.attached;
cProtoE.attached = attachedDefine;
// hookTos.add(a);
}
} else {
if (
typeof cntE.attached === 'function' &&
cntE.attached.length === 3
) {
cntE.attached9512 = cntE.attached;
cntE.attached = attachedDefine;
// hookTos.add(a);
}
}
}
if (!cntE.detached9512 && cntE.detached) {
const cProtoE = getProto(newElement);
if (cProtoE.detached === cntE.detached) {
if (
!cProtoE.detached9512 &&
typeof cProtoE.detached === 'function' &&
cProtoE.detached.length === 0
) {
cProtoE.detached9512 = cProtoE.detached;
cProtoE.detached = detachedDefine;
// hookTos.add(a);
}
} else {
if (
typeof cntE.detached === 'function' &&
cntE.detached.length === 3
) {
cntE.detached9512 = cntE.detached;
cntE.detached = detachedDefine;
// hookTos.add(a);
}
}
}
const acceptance = true;
// const acceptance = !cntE.__dataReady && cntE.__dataInvalid !== false; // we might need to change the acceptance condition along with YouTube Coding updates.
if (acceptance) {
// [[ weak ElementNode, attached time, detached time, time of change, inside availablePool, reuse count ]]
const entryRecord = [new WeakRef(newElement), -1, -1, -1, false, 0];
newElement.__rm3Tag003__ = eKey;
entryRecords.set(newElement, entryRecord);
} else {
}
} catch (e) {
console.warn(e);
}
return newElement;
};
document.createElement9512 = document.createElement;
document.createElement = function (a) {
const r = document.createElement9512(a);
try {
const cnt = insp(r);
if (cnt.createComponent_ && !cnt.createComponent9512_) {
const cProto = getProto(r);
if (cProto.createComponent_ === cnt.createComponent_) {
if (
!cProto.createComponent9512_ &&
typeof cProto.createComponent_ === 'function' &&
cProto.createComponent_.length === 3
) {
cProto.createComponent9512_ = cProto.createComponent_;
cProto.createComponent_ = createComponentDefine_;
DEBUG_OPT && hookTos.add(a);
}
} else {
if (
typeof cnt.createComponent_ === 'function' &&
cnt.createComponent_.length === 3
) {
cnt.createComponent9512_ = cnt.createComponent_;
cnt.createComponent_ = createComponentDefine_;
DEBUG_OPT && hookTos.add(a);
}
}
}
} catch (e) {
console.warn(e);
}
return r;
};
rm3.checkWhetherUnderParent = () => {
const r = [];
for (const operation of operations) {
const elm = operation[0].deref();
if (operation[2] > 0) {
r.push([
!!elm,
elm?.nodeName.toLowerCase(),
elm?.parentNode === null,
]);
}
}
return r;
};
rm3.hookTags = () => {
const r = new Set();
for (const operation of operations) {
const elm = operation[0].deref();
if (elm && elm.is) {
r.add(elm.is);
}
}
return [...r];
};
DEBUG_OPT &&
(rm3.reuseRecord = () => {
return [...reuseRecord_]; // [[debug]]
});
DEBUG_OPT && (rm3.reuseCount_ = reuseCount_);
}
rm3.reuseCount = 0;
};

View File

@ -305,31 +305,14 @@ function registerMPRIS(win: BrowserWindow) {
console.trace(error);
});
let mprisVolNewer = false;
let autoUpdate = false;
ipcMain.on('ytmd:volume-changed', (_, newVol) => {
if (~~(player.volume * 100) !== newVol) {
if (mprisVolNewer) {
mprisVolNewer = false;
autoUpdate = false;
} else {
autoUpdate = true;
player.volume = Number.parseFloat((newVol / 100).toFixed(2));
mprisVolNewer = false;
autoUpdate = false;
}
}
player.volume = Number.parseFloat((newVol / 100).toFixed(2));
});
player.on('volume', (newVolume: number) => {
if (config.plugins.isEnabled('precise-volume')) {
// With precise volume we can set the volume to the exact value.
const newVol = ~~(newVolume * 100);
if (~~(player.volume * 100) !== newVol && !autoUpdate) {
mprisVolNewer = true;
autoUpdate = false;
win.webContents.send('setVolume', newVol);
}
win.webContents.send('setVolume', ~~(newVolume * 100));
} else {
setVolume(newVolume * 100);
}

View File

@ -11,10 +11,13 @@ export class LRCLib implements LyricProvider {
async search({
title,
alternativeTitle,
artist,
album,
songDuration,
tags,
}: SearchSongInfo): Promise<LyricResult | null> {
let query = new URLSearchParams({
artist_name: artist,
track_name: title,
@ -42,7 +45,9 @@ export class LRCLib implements LyricProvider {
return null;
}
query = new URLSearchParams({ q: title });
// Try to search with the alternative title (original language)
const trackName = alternativeTitle || title;
query = new URLSearchParams({ q: `${trackName}` });
url = `${this.baseUrl}/api/search?${query.toString()}`;
response = await fetch(url);
@ -54,6 +59,22 @@ export class LRCLib implements LyricProvider {
if (!Array.isArray(data)) {
throw new Error(`Expected an array, instead got ${typeof data}`);
}
// If still no results, try with the original title as fallback
if (data.length === 0 && alternativeTitle) {
query = new URLSearchParams({ q: title });
url = `${this.baseUrl}/api/search?${query.toString()}`;
response = await fetch(url);
if (!response.ok) {
throw new Error(`bad HTTPStatus(${response.statusText})`);
}
data = (await response.json()) as LRCLIBSearchResponse;
if (!Array.isArray(data)) {
throw new Error(`Expected an array, instead got ${typeof data}`);
}
}
}
const filteredResults = [];
@ -63,6 +84,7 @@ export class LRCLib implements LyricProvider {
const artists = artist.split(/[&,]/g).map((i) => i.trim());
const itemArtists = artistName.split(/[&,]/g).map((i) => i.trim());
// Try to match using artist name first
const permutations = [];
for (const artistA of artists) {
for (const artistB of itemArtists) {
@ -76,10 +98,40 @@ export class LRCLib implements LyricProvider {
}
}
const ratio = Math.max(
let ratio = Math.max(
...permutations.map(([x, y]) => jaroWinkler(x, y)),
);
// If direct artist match is below threshold and we have tags, try matching with tags
if (ratio <= 0.9 && tags && tags.length > 0) {
// Filter out the artist from tags to avoid duplicate comparisons
const filteredTags = tags.filter(tag => tag.toLowerCase() !== artist.toLowerCase());
const tagPermutations = [];
// Compare each tag with each item artist
for (const tag of filteredTags) {
for (const itemArtist of itemArtists) {
tagPermutations.push([tag.toLowerCase(), itemArtist.toLowerCase()]);
}
}
// Compare each item artist with each tag
for (const itemArtist of itemArtists) {
for (const tag of filteredTags) {
tagPermutations.push([itemArtist.toLowerCase(), tag.toLowerCase()]);
}
}
if (tagPermutations.length > 0) {
const tagRatio = Math.max(
...tagPermutations.map(([x, y]) => jaroWinkler(x, y)),
);
// Use the best match ratio between direct artist match and tag match
ratio = Math.max(ratio, tagRatio);
}
}
if (ratio <= 0.9) continue;
filteredResults.push(item);
}

View File

@ -174,7 +174,7 @@ export const romanizeJapanese = async (line: string) =>
(await kuroshiro.get()).convert(line, {
to: 'romaji',
mode: 'spaced',
});
}) ?? '';
export const romanizeHangul = (line: string) =>
esHangulRomanize(hanja.translate(line, 'SUBSTITUTION'));

View File

@ -73,6 +73,8 @@
& .text-lyrics {
cursor: pointer;
/*fix cuted lyrics-glow and romanized j at line start */
padding-left: 1.5rem;
}
& .text-lyrics > .romaji {

View File

@ -32,7 +32,7 @@ export interface LyricResult {
}
// prettier-ignore
export type SearchSongInfo = Pick<SongInfo, 'title' | 'artist' | 'album' | 'songDuration' | 'videoId'>;
export type SearchSongInfo = Pick<SongInfo, 'title' | 'alternativeTitle' | 'artist' | 'album' | 'songDuration' | 'videoId' | 'tags'>;
export interface LyricProvider {
name: string;

View File

@ -16,7 +16,9 @@ interface Data {
progress: number;
status: string;
title: string;
alternativeTitle: string;
url: string;
tags: string[];
}
export default createPlugin({
@ -86,10 +88,12 @@ export default createPlugin({
cover_url: songInfo.imageSrc ?? '',
album_url: songInfo.imageSrc ?? '',
title: songInfo.title,
alternativeTitle: songInfo.alternativeTitle ?? '',
artists: [songInfo.artist],
status: songInfo.isPaused ? 'stopped' : 'playing',
album: songInfo.album,
url: songInfo.url ?? '',
tags: songInfo.tags ?? [],
});
});
},

View File

@ -6,18 +6,39 @@ import { t } from '@/i18n';
const handlePlay = (e: MouseEvent) => {
if (!(e.target instanceof HTMLElement)) return;
if (
e.target.closest('ytmusic-play-button-renderer') &&
!e.target.closest('ytmusic-player-page')
) {
document.body.classList.add('unobtrusive-player--did-play');
}
// Player bar handler
if (
e.target.closest('ytmusic-player-bar') &&
!document.body.classList.contains('unobtrusive-player--auto-closing')
) {
document.body.classList.remove('unobtrusive-player--did-play');
return;
}
// Song play button handler
if (
e.target.closest('ytmusic-play-button-renderer') &&
!e.target.closest('ytmusic-player-page')
) {
document.body.classList.add('unobtrusive-player--did-play');
return;
}
// Shuffle play menu item handler
const shuffleIconPathData = (
document.querySelector('iron-iconset-svg[name="yt-sys-icons"] #shuffle')
?.firstChild as SVGElement | null
)
?.getAttribute('d')
?.substring(0, 15);
if (
e.target.closest(
`ytmusic-menu-navigation-item-renderer:has(yt-icon path[d^="${shuffleIconPathData}"])`,
)
) {
document.body.classList.add('unobtrusive-player--did-play');
return;
}
};

View File

@ -179,12 +179,12 @@ export default createPlugin({
if (this.config) {
this.config.hideVideo = !showVideo;
}
window.mainConfig.plugins.setOptions('video-toggle', config);
window.mainConfig.plugins.setOptions('video-toggle', this.config);
const checkbox = document.querySelector<HTMLInputElement>(
'.video-switch-button-checkbox',
); // custom mode
if (checkbox) checkbox.checked = !config.hideVideo;
if (checkbox) checkbox.checked = !this.config.hideVideo;
if (player) {
player.style.margin = showVideo ? '' : 'auto 0px';

View File

@ -42,6 +42,7 @@ export interface SongInfo {
videoId: string;
playlistId?: string;
mediaType: MediaType;
tags?: string[];
}
// Grab the native image using the src
@ -83,6 +84,7 @@ const handleData = async (
videoId: '',
playlistId: '',
mediaType: MediaType.Audio,
tags: [],
} satisfies SongInfo;
const microformat = data.microformat?.microformatDataRenderer;
@ -96,6 +98,7 @@ const handleData = async (
songInfo.alternativeTitle = microformat.linkAlternates.find(
(link) => link.title,
)?.title;
songInfo.tags = Array.isArray(microformat.tags) ? microformat.tags : [];
}
const { videoDetails } = data;