Compare commits

...

268 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
8e21a10214 Bump version to 3.8.0 2025-03-26 20:51:20 +09:00
f787c2cc80 chore(deps): update dependency typescript-eslint to v8.28.0 (#3128)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-26 20:47:08 +09:00
ebf91f0977 chore(deps): update dependency eslint-plugin-prettier to v5.2.5 (#3123)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-26 20:45:53 +09:00
cae6d858fa fix(deps): update dependency @hono/node-server to v1.14.0 (#3131)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-26 20:45:09 +09:00
37f3e9ce89 chore(deps): update dependency electron to v35.1.0 (#3136)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-26 20:44:36 +09:00
f350fd7aae fix(deps): update dependency es-hangul to v2.3.2 (#3138)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-26 20:44:23 +09:00
67b001c6a0 chore(deps): update dependency eslint-import-resolver-typescript to v4.2.4 (#3135)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-26 20:44:10 +09:00
ccbe5da684 chore(deps): update eslint monorepo to v9.23.0 (#3130)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-26 20:41:07 +09:00
e3e5ae3a7f chore(deps): update dependency electron-vite to v3.1.0 (#3137)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-26 20:40:43 +09:00
972c3dba2f chore(i18n): Translated using Weblate (Korean)
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/ko/
2025-03-26 12:37:47 +01:00
b61151b2bd chore(deps): update dependency @babel/runtime to v7.27.0 (#3127)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-26 20:34:32 +09:00
4b35a96778 feat(synced-lyrics): romanization (#2790)
* feat(synced-lyrics): init romanization!

* remove debug logs and add TODO

* feat(synced-lyrics/romanization): Mandarin!

* feat(synced-lyrics/romanization): improve japanese detection

* feat(synced-lyrics/romanization): Korean!

* qol(synced-lyrics/romanization): canonicalize punctuation and symbols

* feat(synced-lyrics/romanization): handle japanese+korean and korean+chinese lyrics

* revert formatting on electron.vite.config.mts

* feat(synced-lyrics/romanization): romanize plain lyrics

* apply fix by @kimjammer

* fix lockfile due to rebase

* feat(synced-lyrics): improve lyric processing and formatting;

* feat(synced-lyrics/romanization): add option to enable/disable romanization

* chore: move default value for --lyrics-duration to the declaration

* update lockfile

* fix: improvement

1. improved language detection logic
2. changed code to work in the renderer process

* fix: fix regression (canonicalize)

---------

Co-authored-by: JellyBrick <shlee1503@naver.com>
2025-03-26 20:29:43 +09:00
19fd0d61c6 feat(plugin): add unobtrusive player plugin (#3104)
* feat(plugin): add unobtrusive player plugin

* fix(plugin): add removeEventListener once unobtrusive-player is disabled

* feat(plugin): prevent player page button animation when changing song

* fix(plugin): add removeEventListener for videodatachange once unobtrusive-player is disabled
2025-03-26 19:36:42 +09:00
77779938b9 chore(deps): update dependency vite to v6.2.3 [security] (#3134)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-26 13:24:04 +09:00
bb06d71fbb fix(deps): update dependency youtubei.js to v13.3.0 (#3133)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-26 13:22:11 +09:00
3cf955179d chore(i18n): Translated using Weblate (Polish)
Currently translated at 100.0% (407 of 407 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/pl/
2025-03-25 13:11:19 +01:00
606dd5a679 chore(deps): update dependency electron-builder-squirrel-windows to v26.0.12 (#3122)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-24 13:26:42 +09:00
31fe07ebd4 chore(deps): update dependency eslint-import-resolver-typescript to v4.2.2 (#3106)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-24 13:23:50 +09:00
d456d0db89 chore(deps): update dependency electron-builder to v26.0.12 (#3121)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-24 13:23:30 +09:00
Tix
e00c357fae fix: Discord Rich Presence Fix (#3074)
* discord presence fix

* Update src/plugins/discord/main.ts

Co-authored-by: JellyBrick <shlee1503@naver.com>

* variable length support

---------

Co-authored-by: JellyBrick <shlee1503@naver.com>
2025-03-24 13:20:50 +09:00
21b54ef6ff fix(deps): update dependency @xhayper/discord-rpc to v1.2.1 (#3107)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-24 13:20:21 +09:00
f96b650787 chore(deps): update dependency typescript-eslint to v8.27.0 (#3111)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-24 13:19:01 +09:00
ebbbf2a6b9 chore(deps): update dependency electron to v35.0.3 (#3112)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-24 13:18:54 +09:00
610ad59fdc fix(deps): update dependency hono to v4.7.5 (#3113)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-24 13:18:46 +09:00
4fe302d753 fix(deps): update dependency fast-average-color to v9.5.0 (#3114)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-24 13:18:38 +09:00
5983ae47bb chore(deps): update dependency rollup to v4.37.0 (#3103)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-24 13:18:28 +09:00
4442fc12ec chore(i18n): Translated using Weblate (Hebrew)
Currently translated at 22.3% (91 of 407 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/he/
2025-03-23 21:15:39 +01:00
b42de1c458 chore(i18n): Translated using Weblate (Swedish)
Currently translated at 19.9% (81 of 407 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/sv/
2025-03-22 16:28:39 +01:00
eec5a2352e chore(i18n): Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (407 of 407 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/zh_Hant/
2025-03-22 16:28:38 +01:00
303de7c0aa chore(i18n): Translated using Weblate (Italian)
Currently translated at 99.5% (405 of 407 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/it/
2025-03-22 04:41:58 +01:00
4bc57c2628 chore(i18n): Translated using Weblate (German)
Currently translated at 100.0% (407 of 407 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/de/
2025-03-19 17:35:05 +01:00
c74505ac90 chore(deps): update playwright monorepo to v1.51.1 (#3105)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-18 13:19:35 +09:00
7a3a803d72 chore(deps): update dependency eslint-import-resolver-typescript to v4 (#3102)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-18 13:19:18 +09:00
fec010c73f chore(i18n): Translated using Weblate (Georgian)
Currently translated at 20.1% (82 of 407 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/ka/
2025-03-18 04:51:11 +01:00
b093cc2c08 chore(deps): update dependency electron to v35.0.2 (#3099)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-17 03:16:38 +09:00
cb3cd74e9e fix(deps): update dependency i18next to v24.2.3 (#3100)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-17 03:14:01 +09:00
ae5b8038b2 chore(deps): update dependency electron-builder to v26.0.11 (#3095)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-17 03:13:28 +09:00
5f93c96901 chore(deps): update dependency @babel/runtime to v7.26.10 [security] (#3094)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-17 03:04:02 +09:00
ec81ac5e40 chore(deps): update dependency eslint-config-prettier to v10.1.1 (#3069)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-17 03:03:52 +09:00
8901a7768d chore(deps): update playwright monorepo to v1.51.0 (#3065)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-17 03:03:43 +09:00
22f14cce3e chore(deps): update dependency electron-builder-squirrel-windows to v26.0.11 (#3096)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-17 03:03:32 +09:00
4afb2276c1 chore(deps): update dependency esbuild to v0.25.1 (#3097)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-17 03:03:23 +09:00
77658035f5 chore(deps): update dependency typescript-eslint to v8.26.1 (#3098)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-17 03:03:12 +09:00
07aa9d5811 chore(i18n): Translated using Weblate (Russian)
Currently translated at 100.0% (407 of 407 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/ru/
2025-03-16 02:48:57 +01:00
7764bcabde chore(i18n): Translated using Weblate (Indonesian)
Currently translated at 100.0% (407 of 407 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/id/
2025-03-15 23:23:53 +01:00
69058a52ed chore(deps): update eslint monorepo to v9.22.0 (#3070)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-16 02:49:55 +09:00
1f7acbd219 chore(deps): update dependency rollup to v4.35.0 (#3071)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-16 02:49:41 +09:00
88dfaa98f3 chore(deps): update dependency electron to v35.0.1 (#3075)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-16 02:49:05 +09:00
504829f065 fix(deps): update dependency happy-dom to v17.4.4 (#3068)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-16 02:48:49 +09:00
e4e6dcb0cd chore(deps): update dependency vite to v6.2.2 (#3067)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-16 02:48:34 +09:00
78010793fb fix: Update winget-releaser version to main (#3091) 2025-03-16 02:45:10 +09:00
bcdd24d74b feat: Allow scrobbling using alternative song titles (#3093) 2025-03-16 02:42:58 +09:00
2d86d26701 chore(i18n): Translated using Weblate (Indonesian)
Currently translated at 100.0% (406 of 406 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/id/
2025-03-14 06:19:21 +01:00
77ca2b483f chore(i18n): Translated using Weblate (Portuguese)
Currently translated at 100.0% (406 of 406 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/pt/
2025-03-14 06:19:21 +01:00
13c71e8904 chore(i18n): Translated using Weblate (Portuguese)
Currently translated at 98.0% (398 of 406 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/pt/
2025-03-14 01:14:08 +01:00
3bfb853414 chore(i18n): Translated using Weblate (Portuguese)
Currently translated at 98.0% (398 of 406 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/pt/
2025-03-13 23:53:15 +01:00
25454689c0 chore(i18n): Translated using Weblate (Portuguese)
Currently translated at 98.0% (398 of 406 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/pt/
2025-03-13 20:14:57 +01:00
Tux
dd3e42c41e chore(i18n): Translated using Weblate (Swedish)
Currently translated at 14.2% (58 of 406 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/sv/
2025-03-13 12:27:52 +01:00
499ae2422c chore(i18n): Translated using Weblate (Portuguese)
Currently translated at 98.0% (398 of 406 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/pt/
2025-03-13 12:27:52 +01:00
a3601cece6 chore(i18n): Translated using Weblate (Tamil)
Currently translated at 100.0% (406 of 406 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/ta/
2025-03-13 05:49:12 +01:00
8168666720 chore(i18n): Translated using Weblate (Portuguese)
Currently translated at 98.0% (398 of 406 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/pt/
2025-03-13 03:08:32 +01:00
9f580a1d7d chore(i18n): Translated using Weblate (Bulgarian)
Currently translated at 25.8% (105 of 406 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/bg/
2025-03-12 18:59:47 +01:00
f8765fbdbb chore(i18n): Translated using Weblate (Portuguese)
Currently translated at 98.0% (398 of 406 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/pt/
2025-03-12 18:59:47 +01:00
2922a457cd chore(i18n): Translated using Weblate (Portuguese)
Currently translated at 95.3% (387 of 406 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/pt/
2025-03-12 09:50:43 +01:00
c2e4c32745 chore(i18n): Translated using Weblate (Portuguese)
Currently translated at 95.3% (387 of 406 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/pt/
2025-03-12 03:04:16 +01:00
5071183550 chore(i18n): Translated using Weblate (Nepali)
Currently translated at 100.0% (406 of 406 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/ne/
2025-03-11 09:51:11 +01:00
db736bcb23 chore(i18n): Translated using Weblate (Finnish)
Currently translated at 72.4% (294 of 406 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/fi/
2025-03-10 15:36:08 +01:00
fb29d62cf1 chore(i18n): Added translation using Weblate (Tamil) 2025-03-09 15:41:59 +01:00
28f5185d38 chore(i18n): Translated using Weblate (French)
Currently translated at 99.7% (405 of 406 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/fr/
2025-03-07 21:51:42 +01:00
c9178985f2 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-03-07 08:00:03 +01:00
297b94bdab chore(i18n): Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 98.5% (400 of 406 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/zh_Hans/
2025-03-07 08:00:02 +01:00
dbf8b1c5c5 chore(i18n): Translated using Weblate (Spanish)
Currently translated at 100.0% (406 of 406 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/es/
2025-03-06 23:04:01 +01:00
cec6339f9a chore(i18n): Translated using Weblate (Hindi)
Currently translated at 33.9% (138 of 406 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/hi/
2025-03-06 14:02:43 +01:00
e7edf30717 chore(deps): update dependency electron-builder-squirrel-windows to v26.0.10 (#3054)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-06 11:37:54 +09:00
441be69ca5 chore(deps): update dependency typescript-eslint to v8.26.0 (#3056)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-06 07:12:00 +09:00
2199391ec1 fix(deps): update dependency hono to v4.7.4 (#3062)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-06 07:11:51 +09:00
f43daa3805 chore(deps): update dependency electron-builder to v26.0.10 (#3053)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-06 07:11:24 +09:00
b4dc2ca88f chore(deps): update dependency electron to v35 (#3058)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-05 08:55:02 +09:00
4aae0d89cd fix(deps): update dependency happy-dom to v17.2.2 (#3060)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-05 08:53:38 +09:00
7c1eec03a5 fix(deps): update dependency happy-dom to v17.1.13 (#3057)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-05 08:50:09 +09:00
06aaba0c7f chore(i18n): Translated using Weblate (Arabic)
Currently translated at 100.0% (406 of 406 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/ar/
2025-03-04 17:02:32 +01:00
766268d9e4 chore(deps): update dependency typescript to v5.8.2 (#3040)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-03 22:48:37 +09:00
a7f47001c6 chore(deps): update dependency rollup to v4.34.9 (#3043)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-03 22:42:29 +09:00
104850e9b0 fix(deps): update dependency @hono/swagger-ui to v0.5.1 (#3045)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-03 22:42:15 +09:00
3139beec91 fix: added French link in general README file (#3047)
* fix: updated readme with fr link in general readme

* fix: updated all the other README files
2025-03-03 22:42:00 +09:00
30bc676fd2 fix(deps): update dependency @hono/zod-openapi to v0.19.2 (#3046)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-03 22:34:33 +09:00
c2cf5992f8 chore(deps): update dependency @stylistic/eslint-plugin-js to v4.2.0 (#3050)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-03 22:34:06 +09:00
50b265c3ea fix(deps): update dependency bgutils-js to v3.2.0 (#3049)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-03 18:04:16 +09:00
b6cf7070bf chore(i18n): Translated using Weblate (Arabic)
Currently translated at 72.1% (293 of 406 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/ar/
2025-03-03 08:06:51 +01:00
06f20cc84c chore(i18n): Translated using Weblate (French)
Currently translated at 99.5% (404 of 406 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/fr/
2025-03-03 08:06:50 +01:00
7453a5a06c chore(i18n): Translated using Weblate (French)
Currently translated at 99.5% (404 of 406 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/fr/
2025-03-03 08:06:50 +01:00
de1ccd80fc chore(i18n): Translated using Weblate (Arabic)
Currently translated at 57.3% (233 of 406 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/ar/
2025-03-02 16:34:17 +01:00
d1ee480452 chore(i18n): Translated using Weblate (Sinhala)
Currently translated at 12.0% (49 of 406 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/si/
2025-03-02 03:05:01 +01:00
c8397e4fb4 Update changelog for v3.7.5 2025-02-28 15:29:05 +00:00
65bcf20f97 Bump version to 3.7.5 2025-03-01 00:13:14 +09:00
1a214140fb chore(deps): update dependency builtin-modules to v5 (#3038)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-01 00:12:42 +09:00
ffdf7ac5a8 fix: remove unused deps (@types/eslint__js) 2025-03-01 00:07:08 +09:00
c8bb1f386d chore(deps): update deps 2025-03-01 00:04:00 +09:00
457e1bb48e chore(deps): update dependency electron-builder to v26.0.9 2025-03-01 00:01:57 +09:00
c1177adc08 Update package.json 2025-02-28 23:55:40 +09:00
cd976ee500 Delete patches/app-builder-lib@26.0.6.patch 2025-02-28 23:55:11 +09:00
b856884bba chore(deps): update dependency typescript-eslint to v8.25.0 (#2953)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-28 23:45:25 +09:00
2df8e58773 fix(deps): update dependency happy-dom to v17.1.8 (#3001)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-28 23:44:58 +09:00
da8e0106f6 chore(deps): update dependency eslint-config-prettier to v10.0.2 (#3035)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-28 23:44:43 +09:00
b0b2005e1c chore(deps): update dependency @electron/universal to v2.0.2 (#3034)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-28 23:44:24 +09:00
5817d9c3ae chore(deps): update dependency @stylistic/eslint-plugin-js to v4 (#2950)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-28 23:43:13 +09:00
08f9187cdf chore(deps): update dependency electron-builder-squirrel-windows to v26.0.9 (#2930)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-28 23:42:38 +09:00
49ac17a40e fix(no-google-login): Remove Library icon removal code (#3010) 2025-02-28 23:42:05 +09:00
Dvd
b1c4b04ebf fix: Updated tray pause icon for consistency (#3025) 2025-02-28 23:41:43 +09:00
10b8066126 chore(deps): update dependency eslint-import-resolver-typescript to v3.8.3 (#2992)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-28 23:31:55 +09:00
d86e454d3f fix(deps): update dependency hono to v4.7.2 (#2999)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-28 23:31:40 +09:00
4ea8fa2561 fix: Filter for only MusicResponsiveListItem in playlist items (#3022) 2025-02-28 23:25:01 +09:00
dbea4c5884 fix(deps): update dependency youtubei.js to v13.1.0 (#3015) 2025-02-28 23:20:46 +09:00
7d4e949f0c chore(i18n): Translated using Weblate (Danish)
Currently translated at 32.5% (132 of 406 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/da/
2025-02-27 10:03:56 +00:00
82917e7748 chore(i18n): Added translation using Weblate (Danish) 2025-02-26 10:15:21 +01:00
7c9544a528 chore(i18n): Translated using Weblate (Hebrew)
Currently translated at 16.0% (65 of 406 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/he/
2025-02-26 09:04:34 +01:00
960a20e899 chore(i18n): Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 99.2% (403 of 406 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/zh_Hans/
2025-02-22 14:28:49 +01:00
c9b7901681 chore(i18n): Translated using Weblate (Thai)
Currently translated at 100.0% (406 of 406 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/th/
2025-02-19 18:02:02 +01:00
95698aaf35 chore(i18n): Translated using Weblate (Italian)
Currently translated at 99.5% (404 of 406 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/it/
2025-02-19 18:02:00 +01:00
4dc87417ff chore(i18n): Translated using Weblate (Thai)
Currently translated at 69.4% (282 of 406 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/th/
2025-02-18 17:47:14 +01:00
7510f8eb08 chore(i18n): Translated using Weblate (Arabic)
Currently translated at 39.1% (159 of 406 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/ar/
2025-02-18 13:51:53 +01:00
59103df665 fix: Match engines.pnpm with the pnpm version used to create the lock files (#2995) 2025-02-18 12:14:53 +09:00
258d35e48d chore(i18n): Translated using Weblate (Czech)
Currently translated at 92.8% (377 of 406 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/cs/
2025-02-17 20:29:57 +01:00
5e419489d5 Update changelog for v3.7.4 2025-02-17 15:54:12 +00:00
0d462ac3a2 HOTFIX: Bump version to 3.7.4 2025-02-18 00:37:57 +09:00
bcb94f6de8 chore(deps): update dependency rollup to v4.34.8 (#2982)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-18 00:36:54 +09:00
fe925ec8ee fix(plugin-loader): update context filtering logic for backend mode (#2990) 2025-02-18 00:35:55 +09:00
99 changed files with 10112 additions and 2169 deletions

View File

@ -20,7 +20,7 @@ jobs:
TAG_NAME: ${{ inputs.tag_name || github.event.release.tag_name }}
run: echo "WINGET_TAG_NAME=$(echo ${TAG_NAME#v})" >> $GITHUB_ENV
- name: Submit package to Windows Package Manager Community Repository
uses: vedantmgoyal2009/winget-releaser@v2
uses: vedantmgoyal2009/winget-releaser@main
with:
identifier: th-ch.YouTubeMusic
installers-regex: '^YouTube-Music-Web-Setup-[\d\.]+\.exe$'

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-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:**

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -2,8 +2,169 @@
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)
- chore(deps): update dependency eslint-config-prettier to v10.0.2 [`#3035`](https://github.com/th-ch/youtube-music/pull/3035)
- chore(deps): update dependency @electron/universal to v2.0.2 [`#3034`](https://github.com/th-ch/youtube-music/pull/3034)
- chore(deps): update dependency @stylistic/eslint-plugin-js to v4 [`#2950`](https://github.com/th-ch/youtube-music/pull/2950)
- chore(deps): update dependency electron-builder-squirrel-windows to v26.0.9 [`#2930`](https://github.com/th-ch/youtube-music/pull/2930)
- fix(no-google-login): Remove Library icon removal code [`#3010`](https://github.com/th-ch/youtube-music/pull/3010)
- fix: Updated tray pause icon for consistency [`#3025`](https://github.com/th-ch/youtube-music/pull/3025)
- chore(deps): update dependency eslint-import-resolver-typescript to v3.8.3 [`#2992`](https://github.com/th-ch/youtube-music/pull/2992)
- fix(deps): update dependency hono to v4.7.2 [`#2999`](https://github.com/th-ch/youtube-music/pull/2999)
- fix: Filter for only `MusicResponsiveListItem` in playlist items [`#3022`](https://github.com/th-ch/youtube-music/pull/3022)
- fix(deps): update dependency youtubei.js to v13.1.0 [`#3015`](https://github.com/th-ch/youtube-music/pull/3015)
- fix: Match engines.pnpm with the pnpm version used to create the lock files [`#2995`](https://github.com/th-ch/youtube-music/pull/2995)
- chore(deps): update dependency electron-builder to v26.0.9 [`457e1bb`](https://github.com/th-ch/youtube-music/commit/457e1bb48e2bcc742680d22b7d34ffdbe7f33eae)
- chore(deps): update deps [`c8bb1f3`](https://github.com/th-ch/youtube-music/commit/c8bb1f386d7053d755c38ca2cac8708af7af1fb9)
- chore(i18n): Translated using Weblate (Thai) [`c9b7901`](https://github.com/th-ch/youtube-music/commit/c9b790168130d0cfc9b2ff23cdcb56ab082a4b66)
#### [v3.7.4](https://github.com/th-ch/youtube-music/compare/v3.7.3...v3.7.4)
> 18 February 2025
- chore(deps): update dependency rollup to v4.34.8 [`#2982`](https://github.com/th-ch/youtube-music/pull/2982)
- fix(plugin-loader): update context filtering logic for backend mode [`#2990`](https://github.com/th-ch/youtube-music/pull/2990)
- Update changelog for v3.7.3 [`86c77d1`](https://github.com/th-ch/youtube-music/commit/86c77d141f2bec62a4a578fff96d51ed388c05a5)
- HOTFIX: Bump version to 3.7.4 [`0d462ac`](https://github.com/th-ch/youtube-music/commit/0d462ac3a26caee23854014cbf5e4b91e2d385e2)
#### [v3.7.3](https://github.com/th-ch/youtube-music/compare/v3.7.2...v3.7.3)
> 17 February 2025
- fix(downloader): use the upgrade button to check for premium status [`#2987`](https://github.com/th-ch/youtube-music/pull/2987)
- chore(deps): update dependency electron-vite to v3 [`#2986`](https://github.com/th-ch/youtube-music/pull/2986)
- chore(deps): update dependency @babel/runtime to v7.26.9 [`#2980`](https://github.com/th-ch/youtube-music/pull/2980)

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,6 +21,8 @@
</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), [🇧🇷 Portugués](./README-pt.md), [🇯🇵 Japonés](./README-ja.md)
**Electron wrapper de YouTube Music con las siguientes características:**
- Apariencia y sensación nativa, tiene como objetivo mantener la interfaz original

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,6 +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), [🇧🇷 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](./blob/master/README.md), [🇰🇷 Korea](./docs/readme/README-ko.md), [🇮🇸 Izland](./docs/readme/README-is.md), [🇪🇸 Spanyol](./docs/readme/README-es.md), [🇷🇺 Orosz](./docs/readme/README-ru.md)
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,6 +21,8 @@
</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), [🇧🇷 Portúgalska](./README-pt.md), [🇯🇵 Japanska](./README-ja.md)
**Electron umbúðir utan um YouTube Tónlist sem inniheldur:**
- Innfæddur útlit og tilfinning, miðar að því að halda upprunalegu viðmótinu

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,6 +20,8 @@
</a>
</div>
다른 언어로 읽어보세요: [🏴 영어](../../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-pt.md)
**Клиент для YouTube Music основанный на Electron с поддержкой:**

View File

@ -1,4 +1,4 @@
import { resolve, dirname, join } from 'node:path';
import { dirname, join, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { UserConfig } from 'vite';

View File

@ -2,7 +2,7 @@
"name": "youtube-music",
"desktopName": "com.github.th_ch.youtube_music",
"productName": "YouTube Music",
"version": "3.7.3",
"version": "3.9.0",
"description": "YouTube Music Desktop App - including custom plugins",
"main": "./dist/main/index.js",
"license": "MIT",
@ -218,21 +218,21 @@
},
"engines": {
"node": ">=18",
"pnpm": ">=8"
"pnpm": ">=10"
},
"pnpm": {
"overrides": {
"vite": "6.1.0",
"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.1",
"@babel/runtime": "7.26.9"
"@electron/universal": "2.0.2",
"@babel/runtime": "7.27.0"
},
"patchedDependencies": {
"vudio@2.1.1": "patches/vudio@2.1.1.patch",
"app-builder-lib@26.0.6": "patches/app-builder-lib@26.0.6.patch",
"@malept/flatpak-bundler": "patches/@malept__flatpak-bundler.patch"
"@malept/flatpak-bundler@0.4.0": "patches/@malept__flatpak-bundler@0.4.0.patch",
"kuromoji@0.1.2": "patches/kuromoji@0.1.2.patch"
},
"neverBuiltDependencies": []
},
@ -243,99 +243,108 @@
"@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.13.8",
"@hono/swagger-ui": "0.5.0",
"@hono/zod-openapi": "0.18.4",
"@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.5",
"@hono/zod-validator": "0.4.3",
"@jellybrick/dbus-next": "0.10.3",
"@jellybrick/electron-better-web-request": "1.0.4",
"@jellybrick/mpris-service": "2.1.5",
"@jimp/plugin-color": "1.6.0",
"@skyra/jaro-winkler": "1.1.1",
"@xhayper/discord-rpc": "1.2.0",
"@xhayper/discord-rpc": "1.2.1",
"async-mutex": "0.5.0",
"bgutils-js": "3.1.3",
"bgutils-js": "3.2.0",
"butterchurn": "3.0.0-beta.4",
"butterchurn-presets": "3.0.0-beta.4",
"color": "5.0.0",
"conf": "13.1.0",
"custom-electron-prompt": "1.5.8",
"deepmerge-ts": "7.1.4",
"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",
"fast-average-color": "9.4.0",
"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",
"happy-dom": "17.1.0",
"hono": "4.7.1",
"hanja": "1.1.4",
"happy-dom": "17.4.4",
"hono": "4.7.7",
"howler": "2.2.4",
"html-to-text": "9.0.5",
"i18next": "24.2.2",
"i18next": "25.0.1",
"jimp": "1.6.0",
"keyboardevent-from-electron-accelerator": "2.0.0",
"keyboardevents-areequal": "0.2.2",
"kuromoji": "0.1.2",
"kuroshiro": "1.2.0",
"kuroshiro-analyzer-kuromoji": "1.1.0",
"lazy-var": "2.2.2",
"node-html-parser": "7.0.1",
"node-id3": "0.2.7",
"node-id3": "0.2.9",
"peerjs": "1.5.4",
"pinyin": "4.0.0-alpha.2",
"segmentit": "2.0.3",
"semver": "7.7.1",
"serve": "14.2.4",
"simple-youtube-age-restriction-bypass": "github:organization/Simple-YouTube-Age-Restriction-Bypass#v2.5.9",
"solid-floating-ui": "0.3.1",
"solid-js": "1.9.4",
"solid-js": "1.9.5",
"solid-styled-components": "0.28.5",
"solid-transition-group": "0.3.0",
"ts-morph": "25.0.1",
"vudio": "2.1.1",
"x11": "2.3.0",
"youtubei.js": "13.0.0",
"zod": "3.24.2"
"youtubei.js": "13.4.0",
"zod": "3.24.3"
},
"devDependencies": {
"@eslint/js": "9.20.0",
"@playwright/test": "1.50.1",
"@stylistic/eslint-plugin-js": "3.1.0",
"@eslint/js": "9.25.1",
"@malept/flatpak-bundler": "0.4.0",
"@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/eslint__js": "8.42.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": "4.0.0",
"builtin-modules": "5.0.0",
"cross-env": "7.0.3",
"del-cli": "6.0.0",
"discord-api-types": "0.37.119",
"electron": "34.2.0",
"electron-builder": "26.0.6",
"electron-builder-squirrel-windows": "26.0.6",
"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.0.0",
"esbuild": "0.25.0",
"eslint": "9.20.1",
"eslint-config-prettier": "10.0.1",
"electron-vite": "3.1.0",
"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": "3.8.0",
"eslint-import-resolver-typescript": "4.3.4",
"eslint-plugin-import": "2.31.0",
"eslint-plugin-prettier": "5.2.3",
"glob": "11.0.1",
"node-gyp": "11.1.0",
"playwright": "1.50.1",
"rollup": "4.34.7",
"typescript": "5.7.3",
"typescript-eslint": "8.24.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.1.0",
"vite-plugin-inspect": "10.2.1",
"vite": "6.3.3",
"vite-plugin-inspect": "11.0.1",
"vite-plugin-resolve": "2.5.2",
"vite-plugin-solid": "2.11.1",
"ws": "8.18.0"
"vite-plugin-solid": "2.11.6",
"ws": "8.18.1"
},
"auto-changelog": {
"hideCredit": true,

View File

@ -1,21 +0,0 @@
diff --git a/out/targets/snap.js b/out/targets/snap.js
index f72c36355d27cd2d69fc5fdf2d8bb2451db0287f..baae112fe25ebb49ab8e25aaa48efd6bc43b598f 100644
--- a/out/targets/snap.js
+++ b/out/targets/snap.js
@@ -212,14 +212,14 @@ class SnapTarget extends core_1.Target {
args.push("--template-url", `electron4:${snapArch}`);
}
await (0, builder_util_1.executeAppBuilder)(args);
- const publishConfig = findSnapPublishConfig(this.packager.config);
+
await packager.info.callArtifactBuildCompleted({
file: artifactPath,
safeArtifactName: packager.computeSafeArtifactName(artifactName, "snap", arch, false),
target: this,
arch,
packager,
- publishConfig: publishConfig == null ? { provider: "snapStore" } : publishConfig,
+ publishConfig: options.publish == null ? { provider: "snapStore" } : null,
});
}
isElectronVersionGreaterOrEqualThan(version) {

View File

@ -1,161 +0,0 @@
diff --git a/lib/importDeclaration.js b/lib/importDeclaration.js
index afb4de779034cfea080825a5f4320661c48bee32..f10b0a11a39577fbd42569e6b0e768255c1ef276 100644
--- a/lib/importDeclaration.js
+++ b/lib/importDeclaration.js
@@ -1,5 +1,5 @@
-"use strict";Object.defineProperty(exports, "__esModule", { value: true });exports["default"] = importDeclaration;function importDeclaration(context) {
- var ancestors = context.getAncestors();
+"use strict";Object.defineProperty(exports, "__esModule", { value: true });exports["default"] = importDeclaration;function importDeclaration(context, node) {
+ var ancestors = context.getSourceCode().getAncestors(node);
return ancestors[ancestors.length - 1];
}
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uL3NyYy9pbXBvcnREZWNsYXJhdGlvbi5qcyJdLCJuYW1lcyI6WyJpbXBvcnREZWNsYXJhdGlvbiIsImNvbnRleHQiLCJhbmNlc3RvcnMiLCJnZXRBbmNlc3RvcnMiLCJsZW5ndGgiXSwibWFwcGluZ3MiOiJnR0FBd0JBLGlCLENBQVQsU0FBU0EsaUJBQVQsQ0FBMkJDLE9BQTNCLEVBQW9DO0FBQ2pELE1BQU1DLFlBQVlELFFBQVFFLFlBQVIsRUFBbEI7QUFDQSxTQUFPRCxVQUFVQSxVQUFVRSxNQUFWLEdBQW1CLENBQTdCLENBQVA7QUFDRCIsImZpbGUiOiJpbXBvcnREZWNsYXJhdGlvbi5qcyIsInNvdXJjZXNDb250ZW50IjpbImV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIGltcG9ydERlY2xhcmF0aW9uKGNvbnRleHQpIHtcbiAgY29uc3QgYW5jZXN0b3JzID0gY29udGV4dC5nZXRBbmNlc3RvcnMoKTtcbiAgcmV0dXJuIGFuY2VzdG9yc1thbmNlc3RvcnMubGVuZ3RoIC0gMV07XG59XG4iXX0=
\ No newline at end of file
diff --git a/lib/rules/first.js b/lib/rules/first.js
index a77168660cf32c8c3e96f3ff4b8240a36d7de3a6..c0e00d75f9989916057fef3999eeee8d21820292 100644
--- a/lib/rules/first.js
+++ b/lib/rules/first.js
@@ -66,7 +66,7 @@ module.exports = {
}
}
if (nonImportCount > 0) {var _iteratorNormalCompletion = true;var _didIteratorError = false;var _iteratorError = undefined;try {
- for (var _iterator = context.getDeclaredVariables(node)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {var variable = _step.value;
+ for (var _iterator = sourceCode.getDeclaredVariables(node)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {var variable = _step.value;
if (!shouldSort) {break;}
var references = variable.references;
if (references.length) {var _iteratorNormalCompletion2 = true;var _didIteratorError2 = false;var _iteratorError2 = undefined;try {
diff --git a/lib/rules/namespace.js b/lib/rules/namespace.js
index 574d89a60d15c7e0e712956ea6a3ad2d0eac7f08..82e7cb3cff4246592d762cce86323f2b72de92e4 100644
--- a/lib/rules/namespace.js
+++ b/lib/rules/namespace.js
@@ -86,7 +86,7 @@ module.exports = {
// same as above, but does not add names to local map
ExportNamespaceSpecifier: function () {function ExportNamespaceSpecifier(namespace) {
- var declaration = (0, _importDeclaration2['default'])(context);
+ var declaration = (0, _importDeclaration2['default'])(context, namespace);
var imports = _ExportMap2['default'].get(declaration.source.value, context);
if (imports == null) {return null;}
diff --git a/lib/rules/newline-after-import.js b/lib/rules/newline-after-import.js
index 6cc15686464a17803a0b976c35b99627cdbfabee..520eec6d9a375527ab72c459960fe4416c046c17 100644
--- a/lib/rules/newline-after-import.js
+++ b/lib/rules/newline-after-import.js
@@ -194,7 +194,7 @@ module.exports = {
}return CallExpression;}(),
'Program:exit': function () {function ProgramExit() {
log('exit processing for', context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename());
- var scopeBody = getScopeBody(context.getScope());
+ var scopeBody = getScopeBody(context.getSourceCode().getScope(node));
log('got scope:', scopeBody);
requireCalls.forEach(function (node, index) {
diff --git a/lib/rules/no-amd.js b/lib/rules/no-amd.js
index 7ac108bf812ca4f78bfa6fe5ae8b9cf38e2ff497..346c3105dc70f72c4d76fcc6b96b946d1d4ec6d5 100644
--- a/lib/rules/no-amd.js
+++ b/lib/rules/no-amd.js
@@ -23,7 +23,7 @@ module.exports = {
create: function () {function create(context) {
return {
CallExpression: function () {function CallExpression(node) {
- if (context.getScope().type !== 'module') {return;}
+ if (context.getSourceCode().getScope(node).type !== 'module') {return;}
if (node.callee.type !== 'Identifier') {return;}
if (node.callee.name !== 'require' && node.callee.name !== 'define') {return;}
diff --git a/lib/rules/no-commonjs.js b/lib/rules/no-commonjs.js
index befeff0026d61d3ac1e6bbcea29f5c471dc1d353..e91c5ed34e968d5867e884ea800e166cda345aef 100644
--- a/lib/rules/no-commonjs.js
+++ b/lib/rules/no-commonjs.js
@@ -107,7 +107,7 @@ module.exports = {
// exports.
if (node.object.name === 'exports') {
- var isInScope = context.getScope().
+ var isInScope = context.getSourceCode().getScope(node).
variables.
some(function (variable) {return variable.name === 'exports';});
if (!isInScope) {
@@ -117,7 +117,7 @@ module.exports = {
}return MemberExpression;}(),
CallExpression: function () {function CallExpression(call) {
- if (!validateScope(context.getScope())) {return;}
+ if (!validateScope(context.getSourceCode().getScope(call))) {return;}
if (call.callee.type !== 'Identifier') {return;}
if (call.callee.name !== 'require') {return;}
diff --git a/lib/rules/no-mutable-exports.js b/lib/rules/no-mutable-exports.js
index 40bd1b4cfa95d41732bb13bba0ed1969a91cc7ff..8a25abfbfadb299204b36a6cbf283259bcc2e790 100644
--- a/lib/rules/no-mutable-exports.js
+++ b/lib/rules/no-mutable-exports.js
@@ -32,7 +32,7 @@ module.exports = {
}
function handleExportDefault(node) {
- var scope = context.getScope();
+ var scope = context.getSourceCode().getScope(node);
if (node.declaration.name) {
checkDeclarationsInScope(scope, node.declaration.name);
@@ -40,7 +40,7 @@ module.exports = {
}
function handleExportNamed(node) {
- var scope = context.getScope();
+ var scope = context.getSourceCode().getScope(node);
if (node.declaration) {
checkDeclaration(node.declaration);
diff --git a/lib/rules/no-named-as-default-member.js b/lib/rules/no-named-as-default-member.js
index 0c15051e027ad7d1d45f1b51c20be1c000b0af01..5b3d6ba415511b7f9f83a52e1acfebe5a1045a7b 100644
--- a/lib/rules/no-named-as-default-member.js
+++ b/lib/rules/no-named-as-default-member.js
@@ -35,7 +35,7 @@ module.exports = {
return {
ImportDefaultSpecifier: function () {function ImportDefaultSpecifier(node) {
- var declaration = (0, _importDeclaration2['default'])(context);
+ var declaration = (0, _importDeclaration2['default'])(context, node);
var exportMap = _ExportMap2['default'].get(declaration.source.value, context);
if (exportMap == null) {return;}
diff --git a/lib/rules/no-named-as-default.js b/lib/rules/no-named-as-default.js
index 63378a33a1c7da004c57a524cec1a1cddf23e210..c81b1f93b11628676158b79f1c4015911943cc7d 100644
--- a/lib/rules/no-named-as-default.js
+++ b/lib/rules/no-named-as-default.js
@@ -18,7 +18,7 @@ module.exports = {
// #566: default is a valid specifier
if (defaultSpecifier[nameKey].name === 'default') {return;}
- var declaration = (0, _importDeclaration2['default'])(context);
+ var declaration = (0, _importDeclaration2['default'])(context, defaultSpecifier);
var imports = _ExportMap2['default'].get(declaration.source.value, context);
if (imports == null) {return;}
diff --git a/lib/rules/no-namespace.js b/lib/rules/no-namespace.js
index 2b0c783adea788101b779b17f977bbcb582cfd3f..a7f7b202ac7c4a342febef2a993586c4cc84fc7a 100644
--- a/lib/rules/no-namespace.js
+++ b/lib/rules/no-namespace.js
@@ -43,7 +43,7 @@ var _docsUrl = require('../docsUrl');var _docsUrl2 = _interopRequireDefault(_doc
return;
}
- var scopeVariables = context.getScope().variables;
+ var scopeVariables = context.getSourceCode().getScope(node).variables;
var namespaceVariable = scopeVariables.find(function (variable) {return variable.defs[0].node === node;});
var namespaceReferences = namespaceVariable.references;
var namespaceIdentifiers = namespaceReferences.map(function (reference) {return reference.identifier;});
diff --git a/package.json b/package.json
index 5c0af48543483a21791fa23a4a583071d3551772..5deeac3d0accc3878ef0fc93dfb52a8ca7c46e84 100644
--- a/package.json
+++ b/package.json
@@ -72,7 +72,7 @@
"chai": "^4.3.10",
"cross-env": "^4.0.0",
"escope": "^3.6.0",
- "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8",
+ "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9",
"eslint-doc-generator": "^1.6.1",
"eslint-import-resolver-node": "file:./resolvers/node",
"eslint-import-resolver-typescript": "^1.0.2 || ^1.1.1",

View File

@ -0,0 +1,580 @@
diff --git a/build/kuromoji.js b/build/kuromoji.js
index f0f4ae9183ff8965fda64a2042f29936f76506d1..8912a754d184742d2768854c7bba83d66f9ff95f 100644
--- a/build/kuromoji.js
+++ b/build/kuromoji.js
@@ -1,5 +1,5 @@
-(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.kuromoji = f()}})(function(){var define,module,exports;return (function(){function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s}return e})()({1:[function(require,module,exports){
-(function (process,global){
+(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.kuromoji = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
+(function (process,global,setImmediate){(function (){
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
@@ -666,10 +666,13 @@ var reIsUint = /^(?:0|[1-9]\d*)$/;
* @returns {boolean} Returns `true` if `value` is a valid index, else `false`.
*/
function isIndex(value, length) {
+ var type = typeof value;
length = length == null ? MAX_SAFE_INTEGER$1 : length;
+
return !!length &&
- (typeof value == 'number' || reIsUint.test(value)) &&
- (value > -1 && value % 1 == 0 && value < length);
+ (type == 'number' ||
+ (type != 'symbol' && reIsUint.test(value))) &&
+ (value > -1 && value % 1 == 0 && value < length);
}
/** `Object#toString` result references. */
@@ -755,6 +758,14 @@ var freeProcess = moduleExports$1 && freeGlobal.process;
/** Used to access faster Node.js helpers. */
var nodeUtil = (function() {
try {
+ // Use `util.types` for Node.js 10+.
+ var types = freeModule$1 && freeModule$1.require && freeModule$1.require('util').types;
+
+ if (types) {
+ return types;
+ }
+
+ // Legacy `process.binding('util')` for Node.js < 10.
return freeProcess && freeProcess.binding && freeProcess.binding('util');
} catch (e) {}
}());
@@ -939,6 +950,9 @@ function createObjectIterator(obj) {
var len = okeys.length;
return function next() {
var key = okeys[++i];
+ if (key === '__proto__') {
+ return next();
+ }
return i < len ? {value: obj[key], key: key} : null;
};
}
@@ -970,6 +984,7 @@ function _eachOfLimit(limit) {
var nextElem = iterator(obj);
var done = false;
var running = 0;
+ var looping = false;
function iterateeCallback(err, value) {
running -= 1;
@@ -981,12 +996,13 @@ function _eachOfLimit(limit) {
done = true;
return callback(null);
}
- else {
+ else if (!looping) {
replenish();
}
}
function replenish () {
+ looping = true;
while (running < limit && !done) {
var elem = nextElem();
if (elem === null) {
@@ -999,6 +1015,7 @@ function _eachOfLimit(limit) {
running += 1;
iteratee(elem.value, elem.key, onlyOnce(iterateeCallback));
}
+ looping = false;
}
replenish();
@@ -3819,7 +3836,7 @@ function memoize(fn, hasher) {
/**
* Calls `callback` on a later loop around the event loop. In Node.js this just
- * calls `process.nextTicl`. In the browser it will use `setImmediate` if
+ * calls `process.nextTick`. In the browser it will use `setImmediate` if
* available, otherwise `setTimeout(callback, 0)`, which means other higher
* priority events may precede the execution of `callback`.
*
@@ -5596,8 +5613,8 @@ Object.defineProperty(exports, '__esModule', { value: true });
})));
-}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
-},{"_process":4}],2:[function(require,module,exports){
+}).call(this)}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},require("timers").setImmediate)
+},{"_process":3,"timers":4}],2:[function(require,module,exports){
// Copyright (c) 2014 Takuya Asano All Rights Reserved.
(function () {
@@ -6391,234 +6408,6 @@ Object.defineProperty(exports, '__esModule', { value: true });
})();
},{}],3:[function(require,module,exports){
-(function (process){
-// Copyright Joyent, Inc. and other Node contributors.
-//
-// 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.
-
-// resolves . and .. elements in a path array with directory names there
-// must be no slashes, empty elements, or device names (c:\) in the array
-// (so also no leading and trailing slashes - it does not distinguish
-// relative and absolute paths)
-function normalizeArray(parts, allowAboveRoot) {
- // if the path tries to go above the root, `up` ends up > 0
- var up = 0;
- for (var i = parts.length - 1; i >= 0; i--) {
- var last = parts[i];
- if (last === '.') {
- parts.splice(i, 1);
- } else if (last === '..') {
- parts.splice(i, 1);
- up++;
- } else if (up) {
- parts.splice(i, 1);
- up--;
- }
- }
-
- // if the path is allowed to go above the root, restore leading ..s
- if (allowAboveRoot) {
- for (; up--; up) {
- parts.unshift('..');
- }
- }
-
- return parts;
-}
-
-// Split a filename into [root, dir, basename, ext], unix version
-// 'root' is just a slash, or nothing.
-var splitPathRe =
- /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;
-var splitPath = function(filename) {
- return splitPathRe.exec(filename).slice(1);
-};
-
-// path.resolve([from ...], to)
-// posix version
-exports.resolve = function() {
- var resolvedPath = '',
- resolvedAbsolute = false;
-
- for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) {
- var path = (i >= 0) ? arguments[i] : process.cwd();
-
- // Skip empty and invalid entries
- if (typeof path !== 'string') {
- throw new TypeError('Arguments to path.resolve must be strings');
- } else if (!path) {
- continue;
- }
-
- resolvedPath = path + '/' + resolvedPath;
- resolvedAbsolute = path.charAt(0) === '/';
- }
-
- // At this point the path should be resolved to a full absolute path, but
- // handle relative paths to be safe (might happen when process.cwd() fails)
-
- // Normalize the path
- resolvedPath = normalizeArray(filter(resolvedPath.split('/'), function(p) {
- return !!p;
- }), !resolvedAbsolute).join('/');
-
- return ((resolvedAbsolute ? '/' : '') + resolvedPath) || '.';
-};
-
-// path.normalize(path)
-// posix version
-exports.normalize = function(path) {
- var isAbsolute = exports.isAbsolute(path),
- trailingSlash = substr(path, -1) === '/';
-
- // Normalize the path
- path = normalizeArray(filter(path.split('/'), function(p) {
- return !!p;
- }), !isAbsolute).join('/');
-
- if (!path && !isAbsolute) {
- path = '.';
- }
- if (path && trailingSlash) {
- path += '/';
- }
-
- return (isAbsolute ? '/' : '') + path;
-};
-
-// posix version
-exports.isAbsolute = function(path) {
- return path.charAt(0) === '/';
-};
-
-// posix version
-exports.join = function() {
- var paths = Array.prototype.slice.call(arguments, 0);
- return exports.normalize(filter(paths, function(p, index) {
- if (typeof p !== 'string') {
- throw new TypeError('Arguments to path.join must be strings');
- }
- return p;
- }).join('/'));
-};
-
-
-// path.relative(from, to)
-// posix version
-exports.relative = function(from, to) {
- from = exports.resolve(from).substr(1);
- to = exports.resolve(to).substr(1);
-
- function trim(arr) {
- var start = 0;
- for (; start < arr.length; start++) {
- if (arr[start] !== '') break;
- }
-
- var end = arr.length - 1;
- for (; end >= 0; end--) {
- if (arr[end] !== '') break;
- }
-
- if (start > end) return [];
- return arr.slice(start, end - start + 1);
- }
-
- var fromParts = trim(from.split('/'));
- var toParts = trim(to.split('/'));
-
- var length = Math.min(fromParts.length, toParts.length);
- var samePartsLength = length;
- for (var i = 0; i < length; i++) {
- if (fromParts[i] !== toParts[i]) {
- samePartsLength = i;
- break;
- }
- }
-
- var outputParts = [];
- for (var i = samePartsLength; i < fromParts.length; i++) {
- outputParts.push('..');
- }
-
- outputParts = outputParts.concat(toParts.slice(samePartsLength));
-
- return outputParts.join('/');
-};
-
-exports.sep = '/';
-exports.delimiter = ':';
-
-exports.dirname = function(path) {
- var result = splitPath(path),
- root = result[0],
- dir = result[1];
-
- if (!root && !dir) {
- // No dirname whatsoever
- return '.';
- }
-
- if (dir) {
- // It has a dirname, strip trailing slash
- dir = dir.substr(0, dir.length - 1);
- }
-
- return root + dir;
-};
-
-
-exports.basename = function(path, ext) {
- var f = splitPath(path)[2];
- // TODO: make this comparison case-insensitive on windows?
- if (ext && f.substr(-1 * ext.length) === ext) {
- f = f.substr(0, f.length - ext.length);
- }
- return f;
-};
-
-
-exports.extname = function(path) {
- return splitPath(path)[3];
-};
-
-function filter (xs, f) {
- if (xs.filter) return xs.filter(f);
- var res = [];
- for (var i = 0; i < xs.length; i++) {
- if (f(xs[i], i, xs)) res.push(xs[i]);
- }
- return res;
-}
-
-// String.prototype.substr - negative index don't work in IE8
-var substr = 'ab'.substr(-1) === 'b'
- ? function (str, start, len) { return str.substr(start, len) }
- : function (str, start, len) {
- if (start < 0) start = str.length + start;
- return str.substr(start, len);
- }
-;
-
-}).call(this,require('_process'))
-},{"_process":4}],4:[function(require,module,exports){
// shim for using process in browser
var process = module.exports = {};
@@ -6804,7 +6593,86 @@ process.chdir = function (dir) {
};
process.umask = function() { return 0; };
-},{}],5:[function(require,module,exports){
+},{}],4:[function(require,module,exports){
+(function (setImmediate,clearImmediate){(function (){
+var nextTick = require('process/browser.js').nextTick;
+var apply = Function.prototype.apply;
+var slice = Array.prototype.slice;
+var immediateIds = {};
+var nextImmediateId = 0;
+
+// DOM APIs, for completeness
+
+exports.setTimeout = function() {
+ return new Timeout(apply.call(setTimeout, window, arguments), clearTimeout);
+};
+exports.setInterval = function() {
+ return new Timeout(apply.call(setInterval, window, arguments), clearInterval);
+};
+exports.clearTimeout =
+exports.clearInterval = function(timeout) { timeout.close(); };
+
+function Timeout(id, clearFn) {
+ this._id = id;
+ this._clearFn = clearFn;
+}
+Timeout.prototype.unref = Timeout.prototype.ref = function() {};
+Timeout.prototype.close = function() {
+ this._clearFn.call(window, this._id);
+};
+
+// Does not start the time, just sets up the members needed.
+exports.enroll = function(item, msecs) {
+ clearTimeout(item._idleTimeoutId);
+ item._idleTimeout = msecs;
+};
+
+exports.unenroll = function(item) {
+ clearTimeout(item._idleTimeoutId);
+ item._idleTimeout = -1;
+};
+
+exports._unrefActive = exports.active = function(item) {
+ clearTimeout(item._idleTimeoutId);
+
+ var msecs = item._idleTimeout;
+ if (msecs >= 0) {
+ item._idleTimeoutId = setTimeout(function onTimeout() {
+ if (item._onTimeout)
+ item._onTimeout();
+ }, msecs);
+ }
+};
+
+// That's not how node.js implements it but the exposed api is the same.
+exports.setImmediate = typeof setImmediate === "function" ? setImmediate : function(fn) {
+ var id = nextImmediateId++;
+ var args = arguments.length < 2 ? false : slice.call(arguments, 1);
+
+ immediateIds[id] = true;
+
+ nextTick(function onNextTick() {
+ if (immediateIds[id]) {
+ // fn.call() is faster so we optimize for the common use-case
+ // @see http://jsperf.com/call-apply-segu
+ if (args) {
+ fn.apply(null, args);
+ } else {
+ fn.call(null);
+ }
+ // Prevent ids from leaking
+ exports.clearImmediate(id);
+ }
+ });
+
+ return id;
+};
+
+exports.clearImmediate = typeof clearImmediate === "function" ? clearImmediate : function(id) {
+ delete immediateIds[id];
+};
+}).call(this)}).call(this,require("timers").setImmediate,require("timers").clearImmediate)
+},{"process/browser.js":3,"timers":4}],5:[function(require,module,exports){
/** @license zlib.js 2012 - imaya [ https://github.com/imaya/zlib.js ] The MIT License */(function() {'use strict';function n(e){throw e;}var p=void 0,aa=this;function t(e,b){var d=e.split("."),c=aa;!(d[0]in c)&&c.execScript&&c.execScript("var "+d[0]);for(var a;d.length&&(a=d.shift());)!d.length&&b!==p?c[a]=b:c=c[a]?c[a]:c[a]={}};var x="undefined"!==typeof Uint8Array&&"undefined"!==typeof Uint16Array&&"undefined"!==typeof Uint32Array&&"undefined"!==typeof DataView;new (x?Uint8Array:Array)(256);var y;for(y=0;256>y;++y)for(var A=y,ba=7,A=A>>>1;A;A>>>=1)--ba;function B(e,b,d){var c,a="number"===typeof b?b:b=0,f="number"===typeof d?d:e.length;c=-1;for(a=f&7;a--;++b)c=c>>>8^C[(c^e[b])&255];for(a=f>>3;a--;b+=8)c=c>>>8^C[(c^e[b])&255],c=c>>>8^C[(c^e[b+1])&255],c=c>>>8^C[(c^e[b+2])&255],c=c>>>8^C[(c^e[b+3])&255],c=c>>>8^C[(c^e[b+4])&255],c=c>>>8^C[(c^e[b+5])&255],c=c>>>8^C[(c^e[b+6])&255],c=c>>>8^C[(c^e[b+7])&255];return(c^4294967295)>>>0}
var D=[0,1996959894,3993919788,2567524794,124634137,1886057615,3915621685,2657392035,249268274,2044508324,3772115230,2547177864,162941995,2125561021,3887607047,2428444049,498536548,1789927666,4089016648,2227061214,450548861,1843258603,4107580753,2211677639,325883990,1684777152,4251122042,2321926636,335633487,1661365465,4195302755,2366115317,997073096,1281953886,3579855332,2724688242,1006888145,1258607687,3524101629,2768942443,901097722,1119000684,3686517206,2898065728,853044451,1172266101,3705015759,
2882616665,651767980,1373503546,3369554304,3218104598,565507253,1454621731,3485111705,3099436303,671266974,1594198024,3322730930,2970347812,795835527,1483230225,3244367275,3060149565,1994146192,31158534,2563907772,4023717930,1907459465,112637215,2680153253,3904427059,2013776290,251722036,2517215374,3775830040,2137656763,141376813,2439277719,3865271297,1802195444,476864866,2238001368,4066508878,1812370925,453092731,2181625025,4111451223,1706088902,314042704,2344532202,4240017532,1658658271,366619977,
@@ -6984,7 +6852,7 @@ module.exports = Tokenizer;
"use strict";
var Tokenizer = require("./Tokenizer");
-var DictionaryLoader = require("./loader/NodeDictionaryLoader");
+var BrowserDictionaryLoader = require("./loader/BrowserDictionaryLoader");
/**
* TokenizerBuilder create Tokenizer instance.
@@ -7005,7 +6873,7 @@ function TokenizerBuilder(option) {
* @param {TokenizerBuilder~onLoad} callback Callback function
*/
TokenizerBuilder.prototype.build = function (callback) {
- var loader = new DictionaryLoader(this.dic_path);
+ var loader = new BrowserDictionaryLoader(this.dic_path);
loader.load(function (err, dic) {
callback(err, new Tokenizer(dic));
});
@@ -7020,7 +6888,7 @@ TokenizerBuilder.prototype.build = function (callback) {
module.exports = TokenizerBuilder;
-},{"./Tokenizer":6,"./loader/NodeDictionaryLoader":19}],8:[function(require,module,exports){
+},{"./Tokenizer":6,"./loader/BrowserDictionaryLoader":19}],8:[function(require,module,exports){
/*
* Copyright 2014 Takuya Asano
* Copyright 2010-2014 Atilika Inc. and contributors
@@ -8163,7 +8031,6 @@ module.exports = BrowserDictionaryLoader;
"use strict";
-var path = require("path");
var async = require("async");
var DynamicDictionaries = require("../dict/DynamicDictionaries");
@@ -8194,7 +8061,7 @@ DictionaryLoader.prototype.load = function (load_callback) {
// Trie
function (callback) {
async.map([ "base.dat.gz", "check.dat.gz" ], function (filename, _callback) {
- loadArrayBuffer(path.join(dic_path, filename), function (err, buffer) {
+ loadArrayBuffer(dic_path + filename, function (err, buffer) {
if(err) {
return _callback(err);
}
@@ -8214,7 +8081,7 @@ DictionaryLoader.prototype.load = function (load_callback) {
// Token info dictionaries
function (callback) {
async.map([ "tid.dat.gz", "tid_pos.dat.gz", "tid_map.dat.gz" ], function (filename, _callback) {
- loadArrayBuffer(path.join(dic_path, filename), function (err, buffer) {
+ loadArrayBuffer(dic_path + filename, function (err, buffer) {
if(err) {
return _callback(err);
}
@@ -8234,7 +8101,7 @@ DictionaryLoader.prototype.load = function (load_callback) {
},
// Connection cost matrix
function (callback) {
- loadArrayBuffer(path.join(dic_path, "cc.dat.gz"), function (err, buffer) {
+ loadArrayBuffer(dic_path + "cc.dat.gz", function (err, buffer) {
if(err) {
return callback(err);
}
@@ -8246,7 +8113,7 @@ DictionaryLoader.prototype.load = function (load_callback) {
// Unknown dictionaries
function (callback) {
async.map([ "unk.dat.gz", "unk_pos.dat.gz", "unk_map.dat.gz", "unk_char.dat.gz", "unk_compat.dat.gz", "unk_invoke.dat.gz" ], function (filename, _callback) {
- loadArrayBuffer(path.join(dic_path, filename), function (err, buffer) {
+ loadArrayBuffer(dic_path + filename, function (err, buffer) {
if(err) {
return _callback(err);
}
@@ -8282,7 +8149,7 @@ DictionaryLoader.prototype.load = function (load_callback) {
module.exports = DictionaryLoader;
-},{"../dict/DynamicDictionaries":11,"async":1,"path":3}],21:[function(require,module,exports){
+},{"../dict/DynamicDictionaries":11,"async":1}],21:[function(require,module,exports){
/*
* Copyright 2014 Takuya Asano
* Copyright 2010-2014 Atilika Inc. and contributors
diff --git a/src/TokenizerBuilder.js b/src/TokenizerBuilder.js
index 9ef5c6a2efc63e8b12735a8a9f1cb08d6c52c20c..98881e9fd731047c3fca848a71ede7e381e74f51 100644
--- a/src/TokenizerBuilder.js
+++ b/src/TokenizerBuilder.js
@@ -18,7 +18,7 @@
"use strict";
var Tokenizer = require("./Tokenizer");
-var DictionaryLoader = require("./loader/NodeDictionaryLoader");
+var BrowserDictionaryLoader = require("./loader/BrowserDictionaryLoader");
/**
* TokenizerBuilder create Tokenizer instance.
@@ -39,7 +39,7 @@ function TokenizerBuilder(option) {
* @param {TokenizerBuilder~onLoad} callback Callback function
*/
TokenizerBuilder.prototype.build = function (callback) {
- var loader = new DictionaryLoader(this.dic_path);
+ var loader = new BrowserDictionaryLoader(this.dic_path);
loader.load(function (err, dic) {
callback(err, new Tokenizer(dic));
});
diff --git a/src/loader/DictionaryLoader.js b/src/loader/DictionaryLoader.js
index 5f88c0b7f9a786dd8c072a7b84ae86a6f31412cb..3d6f8a67e16d251b3e4ba4dbbbc947679c364382 100644
--- a/src/loader/DictionaryLoader.js
+++ b/src/loader/DictionaryLoader.js
@@ -17,7 +17,6 @@
"use strict";
-var path = require("path");
var async = require("async");
var DynamicDictionaries = require("../dict/DynamicDictionaries");
@@ -48,7 +47,7 @@ DictionaryLoader.prototype.load = function (load_callback) {
// Trie
function (callback) {
async.map([ "base.dat.gz", "check.dat.gz" ], function (filename, _callback) {
- loadArrayBuffer(path.join(dic_path, filename), function (err, buffer) {
+ loadArrayBuffer(dic_path + filename, function (err, buffer) {
if(err) {
return _callback(err);
}
@@ -68,7 +67,7 @@ DictionaryLoader.prototype.load = function (load_callback) {
// Token info dictionaries
function (callback) {
async.map([ "tid.dat.gz", "tid_pos.dat.gz", "tid_map.dat.gz" ], function (filename, _callback) {
- loadArrayBuffer(path.join(dic_path, filename), function (err, buffer) {
+ loadArrayBuffer(dic_path + filename, function (err, buffer) {
if(err) {
return _callback(err);
}
@@ -88,7 +87,7 @@ DictionaryLoader.prototype.load = function (load_callback) {
},
// Connection cost matrix
function (callback) {
- loadArrayBuffer(path.join(dic_path, "cc.dat.gz"), function (err, buffer) {
+ loadArrayBuffer(dic_path + "cc.dat.gz", function (err, buffer) {
if(err) {
return callback(err);
}
@@ -100,7 +99,7 @@ DictionaryLoader.prototype.load = function (load_callback) {
// Unknown dictionaries
function (callback) {
async.map([ "unk.dat.gz", "unk_pos.dat.gz", "unk_map.dat.gz", "unk_char.dat.gz", "unk_compat.dat.gz", "unk_invoke.dat.gz" ], function (filename, _callback) {
- loadArrayBuffer(path.join(dic_path, filename), function (err, buffer) {
+ loadArrayBuffer(dic_path + filename, function (err, buffer) {
if(err) {
return _callback(err);
}
diff --git a/src/loader/NodeDictionaryLoader.js b/src/loader/NodeDictionaryLoader.js
deleted file mode 100644
index 26eb79249121efe39bd5ae77c17e1caa197fb4ce..0000000000000000000000000000000000000000

2705
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -202,7 +202,7 @@
"show": "عرض النافدة",
"tooltip": {
"default": "يوتيوب اغاني",
"with-song-info": "يوتيوب أغاني: {{الفنان}}-{{العنوان}}"
"with-song-info": "أغاني يوتيوب: {{artist}} - {{title}}"
}
}
},
@ -219,7 +219,7 @@
"name": "حاجب الإعلانات"
},
"album-actions": {
"description": "يضيف أزرار \"إلغاء عدم الإعجاب\"، \"عدم الإعجاب\"، \"الإعجاب\"، و\"إلغاء الإعجاب\" لتطبيقها على جميع الأغاني في قائمة التشغيل أو الألبوم",
"description": "يضيف أزرار \"إلغاء عدم الاعجاب\" و\"عدم الاعجاب\" و\"الإعجاب\" و\"إلغاء الإعجاب\" لتطبيق ذلك على جميع الأغاني في قائمة تشغيل أو ألبوم",
"name": "إجراءات الألبوم"
},
"album-color-theme": {
@ -279,6 +279,13 @@
},
"name": "الوضع المحيطي"
},
"amuse": {
"description": "تكامل دعم YouTube Music مع ويدجت Amuse لعرض الأغنية قيد التشغيل، من إنتاج 6K Labs",
"name": "تلسيه",
"response": {
"query": "خادم Amuse API قيد التشغيل. استخدم GET /query للحصول على معلومات الأغنية."
}
},
"api-server": {
"description": "يضيف خادم للتحكم في المشغل",
"dialog": {
@ -286,41 +293,553 @@
"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": "يجعل شريط التنقل شفاف و ضبابي"
"description": "يجعل شريط التنقل شفاف و ضبابي",
"name": "تغبيش شريط التنقل"
},
"bypass-age-restrictions": {
"description": "تجاوز تَحَقّق اليوتيوب من السن",
"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": "التداخل بين الأغاني [تجريبي]",
"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": "لم يتم العثور على معرف قائمة التشغيل",
"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-percent": "بعد ( عدد مجهول ) بالمئة",
"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": "أدخل معرف المضيف"
},
"internal": {
"save": "حفظ",
"track-source": "مصدر الاغنية",
"unknown-user": "مستخدم مجهول"
},
"menu": {
"click-to-copy-id": "نسخ معرف المستضيف",
"close": "إغلاق \"الموسيقى معًا\"",
"connected-users": "المستخدمون المتصلون",
"disconnect": "قطع اتصال من \"الموسيقى معًا\"",
"empty-user": "لا يوجد مستعملون متصلون",
"host": "مضيف \"الموسيقى معًا\"",
"join": "الانضمام إلى \"الموسيقى معا\"",
"permission": {
"all": "السماح للضيوف بالتحكم في قائمة التشغيل والمشغل",
"host-only": "فقط المضيف يستطيع التحكم بالقائمة و المشغل",
"playlist": "السماح للضيوف بالتحكم بقائمة التشغيل"
},
"set-permission": "تغيير إذن التحكم",
"status": {
"disconnected": "قطع الاتصال",
"guest": "متصل كضيف",
"host": "متصل كمضيف"
}
},
"name": "الموسيقى معا [بيتا]",
"toast": {
"add-song-failed": "فشل في إضافة أغنية",
"closed": "تم إغلاق \"الموسيقى معا\"",
"disconnected": "تم قطع اتصال \"الموسيقى معًا\"",
"host-failed": "فشل في استضافة \"الموسيقى معا\"",
"id-copied": "تم نسخ معرف المضيف",
"id-copy-failed": "لم يتم نسخ معرف المضيف",
"join-failed": "فشل الانضمام إلى \"الموسيقى معا\"",
"joined": "تم الانضمام إلى \"الموسيقى معا\"",
"permission-changed": "تم تغيير إذن \"الموسيقى معًا\" إلى \"{{permission}}\"",
"remove-song-failed": "فشل في إزالة الأغنية",
"user-connected": "{{name}} انضم إلى \"الموسيقى معًا\"",
"user-disconnected": "{{name}} غادر \"الموسيقى معًا\""
}
},
"navigation": {
"description": "أسهم التنقل \"التالي/السابق\" مدمجة مباشرة في الواجهة، كما في متصفحك",
"name": "التنقل"
},
"no-google-login": {
"description": "إزالة أزرار وروابط تسجيل الدخول بجوجل من الواجهة",
"name": "لا يوجد تسجيل دخول بجوجل"
},
"notifications": {
"description": "عرض إشعار عندما تبدأ الأغنية بالتشغيل (الإشعارات التفاعلية متوفرة على ويندوز)",
"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": {
"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": "التحكم في مستوى الصوت بدقة باستخدام عجلة الفأرة/مفاتيح الاختصار، مع واجهة مستخدم مخصصة وقابلة للتخصيص وخطوات صوتية قابلة للتعديل",
"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": "إضافة دعم Scrobbling (مثل Last.fm، ListenBrainz)",
"dialog": {
"lastfm": {
"auth-failed": {
"message": "فشل المصادقة مع Last.fm\nإخفاء النافذة المنبثقة حتى إعادة التشغيل التالية.",
"title": "فشلت المصادقة"
}
}
},
"menu": {
"choose-download-folder": "اختر مكان التحميل"
"lastfm": {
"api-settings": "إعدادات Last.fm API"
},
"listenbrainz": {
"token": "أدخل رمز مستخدم ListenBrainz"
},
"scrobble-alternative-title": "استخدم عناوين بديلة",
"scrobble-other-media": "Scrobble الوسائط الأخرى"
},
"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": "⚠️ حدث خطأ أثناء جلب كلمات الأغنية.\nيرجى المحاولة مرة أخرى لاحقًا.",
"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": "التحكم في المشغل من شريط المهام ويندوز",
"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": "معاين بصري"
}
}
}

View File

@ -158,6 +158,14 @@
},
"remove-upgrade-button": "Премахване на \"Ъпгрейд\" бутона",
"theme": {
"dialog": {
"button": {
"cancel": "Отказ",
"remove": "Премахни"
},
"remove-theme": "Сигурни ли сте, че искате да премахнете персонализираната тема?",
"remove-theme-message": "Това ще премахне персонализираната тема"
},
"label": "Тема",
"submenu": {
"import-css-file": "Импортиране на потребителски CSS файл",
@ -172,7 +180,666 @@
"enabled": "Активирани",
"label": "Плъгини",
"new": "НОВО"
},
"view": {
"label": "Преглед",
"submenu": {
"force-reload": "Принудително презареждане",
"reload": "Презареди",
"reset-zoom": "Действителен размер",
"toggle-fullscreen": "Превключване на цял екран",
"zoom-in": "Увеличаване",
"zoom-out": "Намаляване"
}
}
},
"tray": {
"next": "Следващ",
"play-pause": "Възпроизвеждане/Пауза",
"previous": "Предишен",
"quit": "Изход",
"restart": "Рестартирай приложението",
"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

@ -279,6 +279,9 @@
},
"name": "Ambientní režim"
},
"amuse": {
"description": "Přídá YouTube Music podporu pro Amuse právě těď hraje widget od 6k Labs"
},
"api-server": {
"description": "Vlož API server abys mohl ovládat přehrávač",
"dialog": {

315
src/i18n/resources/da.json Normal file
View File

@ -0,0 +1,315 @@
{
"common": {
"console": {
"plugins": {
"execute-failed": "Fejl ved udføring af plugin {{pluginName}}::{{contextName}}",
"executed-at-ms": "Plugin {{pluginName}}::{{contextName}} udført på {{ms}}ms",
"initialize-failed": "Fejl ved igangsætning af plugin \"{{pluginName}}\"",
"load-all": "Indlæser alle plugins",
"load-failed": "Fejl ved indlæsning af plugin \"{{pluginName}}\"",
"loaded": "Plugin \"{{pluginName}}\" indlæst",
"unload-failed": "Fejl ved unload af plugin \"{{pluginName}}\""
}
}
},
"language": {
"code": "dk",
"local-name": "Dansk",
"name": "Danish"
},
"main": {
"console": {
"did-finish-load": {
"dev-tools": "Indlæsning færdig. DevTools åbnet"
},
"i18n": {
"loaded": "i18n indlæst"
},
"second-instance": {
"receive-command": "Modtog kommando over protokol: \"{{command}}\""
},
"theme": {
"css-file-not-found": "CSS fil \"{{cssFile}}\" eksisterer ikke, ignorere"
},
"unresponsive": {
"details": "Uresponsiv fejl!\n{{error}}"
},
"when-ready": {
"clearing-cache-after-20s": "Rydder op i appens cache"
},
"window": {
"tried-to-render-offscreen": "Windows forsøgte at indlæse uden for skærmen, windowSize={{windowSize}}, displaySize={{displaySize}}, position={{position}}"
}
},
"dialog": {
"hide-menu-enabled": {
"detail": "Menuen er gemt, brug 'Alt' knappen for at vise den igen (eller 'Escape' hvis In-App menuen bruges)",
"message": "Skjul menuen er aktiveret",
"title": "Skjult menu aktiveret"
},
"need-to-restart": {
"buttons": {
"later": "Senere",
"restart-now": "Genstart nu"
},
"detail": "\"{{pluginName}}\" plugin kræver en genstart for at have en effekt",
"message": "\"{{pluginName}}\" skal genstarte",
"title": "Genstart krævet"
},
"unresponsive": {
"buttons": {
"quit": "Afslut",
"relaunch": "Genåben",
"wait": "Vent"
},
"detail": "Vi undskylder for ubelejligheden! Vælg næste handling:",
"message": "Appen svarer ikke",
"title": "Vindue svarer ikke"
},
"update-available": {
"buttons": {
"disable": "Slå opdateringer fra",
"download": "Hent",
"ok": "OK"
},
"detail": "En ny version er tilgængelig og kan downloades her: {{downloadLink}}",
"message": "En ny version er tilgængelig",
"title": "Opdatering tilgængelig"
}
},
"menu": {
"about": "Om",
"navigation": {
"label": "Navigering",
"submenu": {
"copy-current-url": "Kopier nuværende URL",
"go-back": "Tilbage",
"go-forward": "Frem",
"quit": "Afslut",
"restart": "Genstart Appen"
}
},
"options": {
"label": "Indstillinger",
"submenu": {
"advanced-options": {
"label": "Avancerede indstillinger",
"submenu": {
"auto-reset-app-cache": "Nulstil app cache når appen starter",
"disable-hardware-acceleration": "Deaktiver hardware acceleration",
"edit-config-json": "Rediger config.json",
"restart-on-config-changes": "Genstart ved config ændringer",
"set-proxy": {
"label": "Indstil proxy",
"prompt": {
"label": "Skriv proxy adresse: (Efterlad tom for at deaktivere)",
"placeholder": "Eksempel: SOCKS5://127.0.0.1:9999",
"title": "Sæt proxy"
}
},
"toggle-dev-tools": "Skift DevTools"
}
},
"always-on-top": "Altid øverst",
"auto-update": "Automatisk opdatering",
"hide-menu": {
"dialog": {
"message": "Menuen vil være lukket næste gang appen starter. Brug [Alt] for at vise den (Eller backtick [`] hvis in-app-menu bruges)",
"title": "Gemt menu aktiveret"
},
"label": "Skjul menu"
},
"language": {
"dialog": {
"message": "Sproget vil blive ændret efter genstart",
"title": "Sprog ændret"
},
"label": "Sprog",
"submenu": {
"to-help-translate": "Vil du hjælpe med at oversætte? Klik her"
}
},
"resume-on-start": "Genoptag sidste sang når appen starter",
"start-at-login": "Start ved login",
"starting-page": {
"label": "Startside",
"unset": "Ikke valgt"
},
"tray": {
"submenu": {
"disabled": "Deaktiveret",
"enabled-and-show-app": "Aktiver og vis app",
"play-pause-on-click": "Start/Stop ved klik"
}
},
"visual-tweaks": {
"submenu": {
"like-buttons": {
"default": "Standard",
"hide": "Skjul",
"label": "Like knapper"
},
"remove-upgrade-button": "Fjern opgrader knappen",
"theme": {
"dialog": {
"button": {
"remove": "Fjern"
}
},
"label": "Tema",
"submenu": {
"no-theme": "Intet tema"
}
}
}
}
}
},
"plugins": {
"enabled": "Aktiveret",
"label": "Plugins",
"new": "NY"
},
"view": {
"label": "Vis",
"submenu": {
"reload": "Genindlæs",
"zoom-in": "Zoom ind",
"zoom-out": "Zoom ud"
}
}
},
"tray": {
"next": "Næste",
"play-pause": "Afspil",
"previous": "Sidste",
"quit": "Luk",
"restart": "Genstart app",
"show": "Vis vindue",
"tooltip": {
"default": "YouTube Music",
"with-song-info": "YouTube Music: {{artist}} - {{title}}"
}
}
},
"plugins": {
"ad-speedup": {
"description": "Hvis en reklame afspilles, slår den lyden fra og sætter hastigheden til 16x",
"name": "Spol igennem reklamen"
},
"adblocker": {
"description": "Bloker alle reklamer og sporing fra starten af",
"menu": {
"blocker": "Bloker"
},
"name": "Bloker reklamer"
},
"album-color-theme": {
"menu": {
"color-mix-ratio": {
"submenu": {
"percent": "{{ratio}}%"
}
}
},
"name": "Albummets farve tema"
},
"ambient-mode": {
"menu": {
"blur-amount": {
"label": "Sløringsmængde",
"submenu": {
"pixels": "{{blurAmount}} pixel"
}
},
"buffer": {
"label": "Buffer",
"submenu": {
"buffer": "{{buffer}}"
}
},
"opacity": {
"label": "Gennemsigtighed",
"submenu": {
"percent": "{{opacity}}%"
}
},
"quality": {
"label": "Kvalitet",
"submenu": {
"pixels": "{{quality}} pixel"
}
},
"size": {
"label": "Størrelse",
"submenu": {
"percent": "{{size}}%"
}
},
"use-fullscreen": {
"label": "Bruger fuldskærm"
}
}
},
"api-server": {
"dialog": {
"request": {
"buttons": {
"allow": "Tillad",
"deny": "Afvis"
},
"message": "Tillad at {{ID}} ({{origin}}) får adgang til API'en?"
}
},
"menu": {
"auth-strategy": {
"label": "Godkendelsesstrategi"
},
"hostname": {
"label": "Hostname"
},
"port": {
"label": "Port"
}
},
"name": "API Server [Beta]",
"prompt": {
"hostname": {
"label": "Skriv API serverens hostname (f. eks. 0.0.0.0):",
"title": "Hostname"
},
"port": {
"label": "Skriv API serverens port:",
"title": "Port"
}
}
},
"audio-compressor": {
"name": "Lyd kompressor"
},
"blur-nav-bar": {
"description": "Gør navigationsbaren gennemsigtig og sløret",
"name": "Slør navigationsbar"
},
"captions-selector": {
"menu": {
"disable-captions": "Ingen undertekster som standard"
},
"name": "Vælg undertekster",
"prompt": {
"selector": {
"label": "Nuværende sprog på undertekster: {{language}}",
"none": "Ingen",
"title": "Vælg underteksternes sprog"
}
}
},
"crossfade": {
"description": "Fade imellem sange",
"menu": {
"advanced": "Avanceret"
},
"name": "Fade [Beta]"
}
}
}

View File

@ -683,6 +683,7 @@
"listenbrainz": {
"token": "ListenBrainz-Benutzer-Token eintragen"
},
"scrobble-alternative-title": "Nutze alternative Titel",
"scrobble-other-media": "Andere Medien scrobbeln"
},
"name": "Scrobbler",
@ -767,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."
@ -799,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": {
@ -671,7 +675,8 @@
"listenbrainz": {
"token": "Enter ListenBrainz user token"
},
"scrobble-other-media": "Scrobble other media"
"scrobble-other-media": "Scrobble other media",
"scrobble-alternative-title": "Use alternative titles"
},
"name": "Scrobbler",
"prompt": {
@ -755,6 +760,10 @@
"label": "Make the lyrics perfectly synced",
"tooltip": "Calculate to the milisecond the display of the next line (can have a small impact on performance)"
},
"romanization": {
"label": "Romanize lyrics",
"tooltip": "If the lyrics are in a different language, try to display a latin version."
},
"show-lyrics-even-if-inexact": {
"label": "Show lyrics even if inexact",
"tooltip": "If the song is not found, the plugin tries again with a different search query.\nThe result from the second attempt may not be exact."
@ -787,6 +796,10 @@
"description": "Integration with OBS's plugin Tuna",
"name": "Tuna OBS"
},
"unobtrusive-player": {
"description": "Prevents the player from popping up when playing a song",
"name": "Unobtrusive Player"
},
"video-toggle": {
"description": "Adds a button to switch between Video/Song mode. can also optionally remove the whole video tab",
"menu": {

View File

@ -3,8 +3,8 @@
"console": {
"plugins": {
"execute-failed": "Error al ejecutar el plugin {{pluginName}}::{{contextName}}",
"executed-at-ms": "Plugin {{pluginName}}: {{contextName}} ejecutado en {{ms}}ms",
"initialize-failed": "Error al inicializar plugin \"{{pluginName}}\"",
"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}}\"",
"loaded": "Plugin \"{{pluginName}}\" cargado",
@ -21,7 +21,7 @@
"main": {
"console": {
"did-finish-load": {
"dev-tools": "Carga finalizada. DevTools abiertos"
"dev-tools": "Carga finalizada. DevTools abierto"
},
"i18n": {
"loaded": "i18n cargado"
@ -36,7 +36,7 @@
"details": "¡Error sin repuesta!\n{{error}}"
},
"when-ready": {
"clearing-cache-after-20s": "Borrar caché de la aplicación"
"clearing-cache-after-20s": "Borrando caché de la aplicación"
},
"window": {
"tried-to-render-offscreen": "La ventana intentó mostrarse fuera de la pantalla, windowSize={{windowSize}}, displaySize={{displaySize}}, posicion={{position}}"
@ -45,21 +45,21 @@
"dialog": {
"hide-menu-enabled": {
"detail": "El menú está oculto, utiliza \"Alt\" para mostrarlo (o \"Escape\" si utilizas el menú integrado en la aplicación)",
"message": "Menu oculto esta deshabilitado",
"title": "Menú oculto activado"
"message": "Menú Oculto está habilitado",
"title": "Menú oculto habilitado"
},
"need-to-restart": {
"buttons": {
"later": "Más tarde",
"restart-now": "Reiniciar ahora"
},
"detail": "\"{{pluginName}}\" se requiere reiniciar para que el plugin tome efecto",
"detail": "El plugin \"{{pluginName}}\" requiere reiniciar para tomar efecto",
"message": "\"{{pluginName}}\" necesita reiniciar",
"title": "Se requiere reinicio"
},
"unresponsive": {
"buttons": {
"quit": "Dejar",
"quit": "Salir",
"relaunch": "Volver a abrir",
"wait": "Espera"
},
@ -74,7 +74,7 @@
"ok": "OK"
},
"detail": "Una nueva versión está disponible y puede descargarse en {{downloadLink}}",
"message": "Ya está disponible una nueva versión",
"message": "Hay una nueva versión disponible",
"title": "Actualización disponible"
}
},
@ -84,7 +84,7 @@
"label": "Navegación",
"submenu": {
"copy-current-url": "Copiar la URL actual",
"go-back": "Regresar",
"go-back": "Atrás",
"go-forward": "Adelante",
"quit": "Salir",
"restart": "Reiniciar la aplicación"
@ -99,10 +99,10 @@
"auto-reset-app-cache": "Restablecer la caché de la aplicación al iniciarla",
"disable-hardware-acceleration": "Desactivar la aceleración por hardware",
"edit-config-json": "Editar config.json",
"override-user-agent": "sobrescribir User-Agent",
"restart-on-config-changes": "Reinicie al cambiar la configuración",
"override-user-agent": "Sobrescribir User-Agent",
"restart-on-config-changes": "Reiniciar al modificar la configuración",
"set-proxy": {
"label": "Definir proxy",
"label": "Establecer proxy",
"prompt": {
"label": "Introduzca la dirección del proxy: (déjela vacía para desactivarla)",
"placeholder": "Ejemplo: SOCKS5://127.0.0.1:9999",
@ -112,28 +112,28 @@
"toggle-dev-tools": "Activar DevTools"
}
},
"always-on-top": "Siempre arriba",
"always-on-top": "Siempre al frente",
"auto-update": "Actualización automática",
"hide-menu": {
"dialog": {
"message": "El menú se ocultará la próxima vez que lo inicies, usa [Alt] para mostrarlo (o pulsa [`] si usas el menú dentro de la aplicación)",
"title": "Ocultar menú habilitado"
"message": "El menú se ocultará la próxima vez que inicies la aplicación, usa [Alt] para mostrarlo (o pulsa [`] si usas el menú dentro de la aplicación)",
"title": "Menú oculto habilitado"
},
"label": "Ocultar menú"
},
"language": {
"dialog": {
"message": "El idioma se cambiará después de reiniciar",
"title": "Se cambio el idioma"
"title": "Se cambió el idioma"
},
"label": "Idioma",
"submenu": {
"to-help-translate": "¿Quieres ayudar a traducir? Haz clic aquí"
}
},
"resume-on-start": "Reanudar la última canción al iniciar la aplicación",
"single-instance-lock": "Bloquear en una instancia unica",
"start-at-login": "Comenzar al iniciar sesión",
"resume-on-start": "Reanudar la última canción reproducida al iniciar la aplicación",
"single-instance-lock": "Limitar a una única instancia",
"start-at-login": "Iniciar al iniciar sesión",
"starting-page": {
"label": "Página de inicio",
"unset": "Sin configurar"
@ -142,8 +142,8 @@
"label": "Bandeja",
"submenu": {
"disabled": "Desactivado",
"enabled-and-hide-app": "Activar y ocultar la aplicación",
"enabled-and-show-app": "Activado y mostrar aplicación",
"enabled-and-hide-app": "Habilitado y ocultar la aplicación",
"enabled-and-show-app": "Habilitado y mostrar aplicación",
"play-pause-on-click": "Reproducir/Pausar al hacer clic"
}
},
@ -156,7 +156,7 @@
"hide": "Ocultar",
"label": "Botones de \"Me Gusta\""
},
"remove-upgrade-button": "Remover el botón de Actualización",
"remove-upgrade-button": "Eliminar el botón de Actualización",
"theme": {
"dialog": {
"button": {
@ -212,14 +212,14 @@
"name": "Aumento de la velocidad de anuncios"
},
"adblocker": {
"description": "Bloquear todos los anuncios y el rastreo",
"description": "Bloquear todos los anuncios y el rastreo por defecto",
"menu": {
"blocker": "Bloqueador"
},
"name": "Bloqueador de anuncios"
},
"album-actions": {
"description": "Añade los botones \"No me gusta\", \"No me gusta\", \"Me gusta\" y \"No me gusta\" para aplicarlos a todas las canciones de una lista de reproducción o un álbum",
"description": "Añade los botones \"Quitar no me gusta\", \"No me gusta\", \"Me gusta\" y \"Quitar me gusta\" para aplicarlos a todas las canciones de una lista de reproducción o un álbum",
"name": "Acciones en el álbum"
},
"album-color-theme": {
@ -232,15 +232,15 @@
}
}
},
"name": "Color del álbum"
"name": "Tema de color del álbum"
},
"ambient-mode": {
"description": "Aplica un efecto de iluminación mediante la proyección de colores suaves del vídeo en el fondo de la pantalla",
"description": "Aplica un efecto de iluminación mediante la proyección de colores suaves extraídos del vídeo sobre el fondo de pantalla",
"menu": {
"blur-amount": {
"label": "Cantidad de desenfoque",
"submenu": {
"pixels": "{{blurAmount}} pixeles"
"pixels": "{{blurAmount}} píxeles"
}
},
"buffer": {
@ -250,7 +250,7 @@
}
},
"opacity": {
"label": "Transparencia",
"label": "Opacidad",
"submenu": {
"percent": "{{opacity}}%"
}
@ -280,7 +280,7 @@
"name": "Modo ambiente"
},
"amuse": {
"description": "Agrega soporte de YouTube Music para el widget Amuse de reproduciendo ahora de 6K Labs",
"description": "Agrega soporte a YouTube Music para el widget \"reproduciendo\" de Amuse por 6K Labs",
"name": "Amuse",
"response": {
"query": "El servidor API de Amuse se está ejecutando. Usa GET /query para obtener información de la canción."
@ -294,7 +294,7 @@
"allow": "Permitir",
"deny": "Denegar"
},
"message": "¿Permitir {{ID}} ({{origin}}) acceder a la API?",
"message": "¿Permitir que {{ID}} ({{origin}}) acceda a la API?",
"title": "Petición de autorización API"
}
},
@ -303,7 +303,7 @@
"label": "Estrategia de autorización",
"submenu": {
"auth-at-first": {
"label": "Autorizar la primera solicitud"
"label": "Autorizar a la primera solicitud"
},
"none": {
"label": "Sin autorización"
@ -334,12 +334,12 @@
"name": "Compresor de audio"
},
"blur-nav-bar": {
"description": "Hace que la barra de navegación sea transparente y borrosa",
"description": "Hace que la barra de navegación se vea transparente y borrosa",
"name": "Desenfocar barra de navegación"
},
"bypass-age-restrictions": {
"description": "Saltar la verificación de edad de YouTube",
"name": "Saltar las restricciones de edad"
"description": "Saltarse la verificación de edad de YouTube",
"name": "Saltarse las restricciones de edad"
},
"captions-selector": {
"description": "Selector de subtítulos para pistas de audio de YouTube Music",
@ -350,17 +350,17 @@
"name": "Selector de subtítulos",
"prompt": {
"selector": {
"label": "Idioma actual: {{language}}",
"label": "Idioma actual de los subtítulos: {{language}}",
"none": "Ninguno",
"title": "Seleccionar idioma de los subtítulos"
}
},
"templates": {
"title": "Abra el selector de subtítulos"
"title": "Abrir el selector de subtítulos"
}
},
"compact-sidebar": {
"description": "Poner siempre la barra lateral en modo compacto",
"description": "Establecer siempre la barra lateral en modo compacto",
"name": "Barra lateral compacta"
},
"crossfade": {
@ -372,16 +372,16 @@
"prompt": {
"options": {
"multi-input": {
"fade-in-duration": "Duración del fundido (ms)",
"fade-in-duration": "Duración del fundido de entrada (ms)",
"fade-out-duration": "Duración del fundido de salida (ms)",
"fade-scaling": {
"label": "Escala de fundido",
"label": "Escala del fundido",
"linear": "Lineal",
"logarithmic": "Logarítmico"
},
"seconds-before-end": "Crossfade N segundos antes del final"
"seconds-before-end": "Activar Crossfade N segundos antes del final"
},
"title": "Opciones de crossfade"
"title": "Opciones de Crossfade"
}
}
},
@ -410,7 +410,7 @@
"play-on-youtube-music": "Reproducir en YouTube Music",
"set-inactivity-timeout": "Establecer tiempo de inactividad"
},
"name": "Estado de actividad de Discord",
"name": "Discord Rich Presence",
"prompt": {
"set-inactivity-timeout": {
"label": "Introduzca el tiempo de inactividad en segundos:",
@ -433,7 +433,7 @@
"ok": "OK"
},
"detail": "({{playlistSize}} canciones)",
"message": "Descargar Playlist {{playlistTitle}}",
"message": "Descargando Playlist {{playlistTitle}}",
"title": "Descarga iniciada"
}
},
@ -445,45 +445,45 @@
"download-progress": "Descarga: {{percent}}%",
"downloading": "Descargando…",
"downloading-counter": "Descargando {{current}}/{{total}}…",
"downloading-playlist": "Descargar lista de reproducción \"{{playlistTitle}}\" - {{playlistSize}} canciones ({{playlistId}})",
"downloading-playlist": "Descargando lista de reproducción \"{{playlistTitle}}\" - {{playlistSize}} canciones ({{playlistId}})",
"error-while-downloading": "Error al descargar \"{{author}} - {{title}}\": {{error}}",
"folder-already-exists": "La carpeta {{playlistFolder}} ya existe",
"getting-playlist-info": "Obteniendo información de la lista de reproducción…",
"loading": "Cargando…",
"playlist-has-only-one-song": "La lista de reproducción sólo tiene un elemento, descárgala directamente",
"playlist-id-not-found": "No se ha encontrado el ID de la lista de reproducción",
"playlist-has-only-one-song": "La lista de reproducción sólo tiene un elemento, descargándolo directamente",
"playlist-id-not-found": "No se ha encontrado la ID de la lista de reproducción",
"playlist-is-empty": "La lista de reproducción está vacía",
"playlist-is-mix-or-private": "Error obteniendo información de la lista de reproducción: asegúrese de que no es una lista privada o \"Mixed for you\"\n\n{{error}}",
"playlist-is-mix-or-private": "Error obteniendo la información de la lista de reproducción: asegúrese de que no es una lista privada o \"Mixed for you\"\n\n{{error}}",
"preparing-file": "Preparando archivo…",
"saving": "Guardando…",
"trying-to-get-playlist-id": "Intentando obtener el ID de la lista de reproducción: {{playlistId}}",
"trying-to-get-playlist-id": "Intentando obtener la ID de la lista de reproducción: {{playlistId}}",
"video-id-not-found": "Video no encontrado",
"writing-id3": "Escribiendo las etiquetas ID3…"
}
},
"description": "Descarga MP3 / audio fuente directamente desde la interfaz",
"description": "Descarga audio MP3 / fuente directamente desde la interfaz",
"menu": {
"choose-download-folder": "Elija la carpeta de descarga",
"download-finish-settings": {
"label": "Descargar al finalizar",
"prompt": {
"last-percent": "Después del x por ciento",
"last-percent": "Después de x porcentaje",
"last-seconds": "Últimos x segundos",
"title": "Configurar cuándo descargar"
},
"submenu": {
"advanced": "Avanzado",
"enabled": "Activado",
"enabled": "Habilitado",
"mode": "Modo de tiempo",
"percent": "Porcentaje",
"seconds": "Segundos"
}
},
"download-playlist": "Descargar lista de reproducción",
"presets": "Preajustes",
"presets": "Ajustes preestablecidos",
"skip-existing": "Saltar archivos existentes"
},
"name": "Descargador",
"name": "Gestor de descargas",
"renderer": {
"can-not-update-progress": "No se puede actualizar el progreso"
},
@ -504,7 +504,7 @@
"name": "Ecualizador"
},
"exponential-volume": {
"description": "Hace que el control deslizante de volumen sea exponencial para que sea más fácil seleccionar volúmenes más bajos.",
"description": "Hace que la barra de volumen sea exponencial para que sea más fácil seleccionar volúmenes más bajos.",
"name": "Volumen exponencial"
},
"in-app-menu": {
@ -519,19 +519,19 @@
"name": "Lumia Stream [Beta]"
},
"lyrics-genius": {
"description": "Añade el soporte para las letras para la mayoría de las canciones",
"description": "Añade soporte para letras para la mayoría de las canciones",
"menu": {
"romanized-lyrics": "Letras Romanizadas"
},
"name": "Letras Genius",
"renderer": {
"fetched-lyrics": "Letras recuperadas de Genius"
"fetched-lyrics": "Letras obtenidas de Genius"
}
},
"music-together": {
"description": "Comparte una lista de reproducción con los demás. Cuando el anfitrión reproduzca una canción, todos los demás escucharán la misma",
"dialog": {
"enter-host": "Introduzca el ID del host"
"enter-host": "Introduzca la ID del host"
},
"internal": {
"save": "Guardar",
@ -539,7 +539,7 @@
"unknown-user": "Usuario desconocido"
},
"menu": {
"click-to-copy-id": "Copiar el ID del host",
"click-to-copy-id": "Copiar la ID del host",
"close": "Cerrar Music Together",
"connected-users": "Usuarios conectados",
"disconnect": "Desactivar Music Together",
@ -549,7 +549,7 @@
"permission": {
"all": "Permite a los invitados controlar la lista de reproducción y el reproductor",
"host-only": "Sólo el anfitrión puede controlar la lista de reproducción y el reproductor",
"playlist": "Permita que los invitados controlen la lista de reproducción"
"playlist": "Permitir que los invitados controlen la lista de reproducción"
},
"set-permission": "Permiso de control de cambios",
"status": {
@ -562,11 +562,11 @@
"toast": {
"add-song-failed": "No se puede añadir la canción",
"closed": "Music Together cerrado",
"disconnected": "Music Together desconectados",
"host-failed": "Fallo el host de Music Together",
"id-copied": "ID del host copiado en el portapapeles",
"id-copy-failed": "No se ha podido copiar el ID del host en el portapapeles",
"join-failed": "Fallo en la unión a Music Together",
"disconnected": "Music Together desconectado",
"host-failed": "Fallo al hostear Music Together",
"id-copied": "ID del host copiada al portapapeles",
"id-copy-failed": "No se ha podido copiar la ID del host al portapapeles",
"join-failed": "Fallo al unirse a Music Together",
"joined": "Unido a Music Together",
"permission-changed": "Permiso de Music Together cambiado a \"{{permission}}\"",
"remove-song-failed": "Error al eliminar la canción",
@ -601,7 +601,7 @@
"name": "Notificaciones"
},
"picture-in-picture": {
"description": "Permite cambiar la aplicación al modo de imagen en imagen",
"description": "Permite cambiar la aplicación al modo picture-in-picture",
"menu": {
"always-on-top": "Siempre encima",
"hotkey": {
@ -610,17 +610,17 @@
"keybind-options": {
"hotkey": "Tecla de acceso rápido"
},
"label": "Elige una tecla de acceso rápido para activar la función de imagen en imagen",
"title": "Tecla de acceso directo a imagen en imagen"
"label": "Elige una tecla de acceso rápido para activar la función picture-in-picture",
"title": "Tecla de acceso directo a picture-in-picture"
}
},
"save-window-position": "Guardar la posición de la ventana",
"save-window-size": "Guardar tamaño de la ventana",
"use-native-pip": "Utilizar \"Dos imágenes a la vez\" PiP nativo del navegador"
"use-native-pip": "Utilizar PiP nativo del navegador"
},
"name": "Imagen en imagen",
"name": "Picture-in-picture",
"templates": {
"button": "Imagen en imagen"
"button": "Picture-in-picture"
}
},
"playback-speed": {
@ -631,7 +631,7 @@
}
},
"precise-volume": {
"description": "Controla el volumen de manera precisa utilizando la rueda del ratón/teclas de acceso rápido, con una interfaz personalizada y pasos de volumen personalizables",
"description": "Controla el volumen de manera precisa utilizando la rueda del ratón/teclas de acceso rápido, con una interfaz personalizada y niveles de volumen personalizables",
"menu": {
"arrows-shortcuts": "Controles de teclas de flechas locales",
"custom-volume-steps": "Establecer niveles de volumen personalizados",
@ -648,7 +648,7 @@
"title": "Combinaciones de teclas para el volumen"
},
"volume-steps": {
"label": "Escoge los pasos de aumento o disminución del volumen",
"label": "Escoge los niveles de aumento o disminución del volumen",
"title": "Niveles de volumen"
}
}
@ -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",
@ -700,7 +701,7 @@
}
},
"shortcuts": {
"description": "Permite configurar teclas de acceso rápido globales para la reproducción (reproducir/pausa/siguiente/anterior) y desactivar la OSD multimedia anulando las teclas multimedia, activar Ctrl/CMD + F para buscar, activar la compatibilidad con MPRIS de Linux para las teclas multimedia y teclas de acceso rápido personalizadas para usuarios avanzados",
"description": "Permite configurar teclas de acceso rápido globales para la reproducción (reproducir/pausa/siguiente/anterior) y desactivar el OSD multimedia anulando las teclas multimedia, activar Ctrl/CMD + F para buscar, activar la compatibilidad con MPRIS de Linux para las teclas multimedia y teclas de acceso rápido personalizadas para usuarios avanzados",
"menu": {
"override-media-keys": "Anular teclas de medios",
"set-keybinds": "Configurar controles globales de canciones"
@ -733,8 +734,8 @@
"synced-lyrics": {
"description": "Proporciona letras de canciones sincronizadas, utilizando proveedores como LRClib.",
"errors": {
"fetch": "⚠️ - Se ha producido un error al descargar la letra. Por favor, vuelve a intentarlo más tarde.",
"not-found": "⚠️ - No se ha encontrado ninguna letra para esta canción."
"fetch": "⚠️\tHa ocurrido un error al obtener la letra.\n\tPor favor, inténtalo de nuevo más tarde.",
"not-found": "⚠️ No se ha encontrado ninguna letra para esta canción."
},
"menu": {
"default-text-string": {
@ -758,18 +759,22 @@
},
"scale": {
"label": "Escala",
"tooltip": "Escala de la línea actual"
"tooltip": "Escalar la línea actual"
}
},
"tooltip": "Elige el efecto que deseas aplicar a la línea actual"
},
"precise-timing": {
"label": "Haz que la letra esté perfectamente sincronizada",
"tooltip": "Calcular al milisegundo la visualización de la línea siguiente (puede tener un pequeño impacto en el rendimiento)"
"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 complemento vuelve a intentarlo con una búsqueda diferente.\nEl resultado del segundo intento puede no ser exacto."
"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."
},
"show-time-codes": {
"label": "Visualización del código de tiempo",
@ -778,7 +783,7 @@
},
"name": "Letras sincronizadas",
"refetch-btn": {
"fetching": "Recuperando...",
"fetching": "Obteniendo...",
"normal": "Volver a buscar letras"
},
"warnings": {
@ -789,24 +794,28 @@
},
"taskbar-mediacontrol": {
"description": "Controla la reproducción desde la barra de tareas de Windows",
"name": "Control de medios de la barra de tareas"
"name": "Control de medios desde la barra de tareas"
},
"touchbar": {
"description": "Añade un widget TouchBar para los usuarios de macOS",
"name": "TouchBar"
},
"tuna-obs": {
"description": "Integración con el complemento Tuna de OBS",
"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",
"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": {
"align": {
"label": "Alineación",
"submenu": {
"left": "Izquierda",
"middle": "Medio",
"middle": "Centro",
"right": "Derecha"
}
},

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": {
@ -272,6 +276,41 @@
"use-fullscreen": {
"label": "Käytetään koko näytön tilaa"
}
},
"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": {
"request": {
"buttons": {
"allow": "Hyväksy",
"deny": "Kiellä"
},
"message": "Sallitaanko {{ID}} ({{origin}}) pääsy API:in?",
"title": "API vahvistuspyyntö"
}
},
"menu": {
"auth-strategy": {
"submenu": {
"none": {
"label": "Ei valtuuksia"
}
}
},
"port": {
"label": "Portti"
}
},
"name": "API Serveri [Beta]",
"prompt": {
"port": {
"title": "Portti"
}
}
},
"audio-compressor": {
@ -406,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"
@ -560,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

@ -33,7 +33,7 @@
"css-file-not-found": "Le fichier de CSS \"{{cssFile}}\" n'existe pas, ignorer"
},
"unresponsive": {
"details": "Erreur: ne répond pas!\n{{error}}"
"details": "Erreur : Aucune réponse!\n{{error}}"
},
"when-ready": {
"clearing-cache-after-20s": "Effacement du cache de l'application"
@ -44,7 +44,7 @@
},
"dialog": {
"hide-menu-enabled": {
"detail": "Le menu est masqué, utilisez « Alt » pour l'afficher (ou « Échap » si vous utilisez le menu de l'application)",
"detail": "Le menu est masqué, utilisez 'Alt' pour l'afficher (ou 'Échap' si vous utilisez le menu de l'application)",
"message": "Le masquage du menu est activé",
"title": "Masquer le menu activé"
},
@ -279,6 +279,13 @@
},
"name": "Mode ambiant"
},
"amuse": {
"description": "Ajoute la prise en charge de YouTube Music pour le widget de lecture en cours Amuse par 6K Labs",
"name": "Amuse",
"response": {
"query": "Le serveur API Amuse est en cours d'exécution. Envoyez une requête GET /query pour obtenir des informations sur la chanson."
}
},
"api-server": {
"description": "Ajouter un serveur API pour contrôler le lecteur",
"dialog": {
@ -676,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",
@ -727,7 +735,7 @@
"description": "Ajoute des paroles synchronisées aux chansons, grâce à LRClib par exemple.",
"errors": {
"fetch": "⚠️\tUne erreur s'est produite en allant chercher les paroles.\n\tMerci de réessayer plus tard.",
"not-found": "⚠️ - Aucune paroles trouvées pour cette musique."
"not-found": "⚠️ Aucune paroles trouvées pour cette musique."
},
"menu": {
"default-text-string": {
@ -737,6 +745,10 @@
"line-effect": {
"label": "Effet de ligne",
"submenu": {
"fancy": {
"label": "Raffiné",
"tooltip": "Utilise de grands effets de type application sur la ligne actuelle"
},
"focus": {
"label": "Focus",
"tooltip": "Rend seulement la ligne actuelle blanche"
@ -756,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."
@ -788,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

@ -86,7 +86,8 @@
"copy-current-url": "העתק את כתובת ה-URL",
"go-back": "חזור אחורה",
"go-forward": "לך קדימה",
"quit": "יציאה"
"quit": "יציאה",
"restart": "הפעל מחדש את היישום"
}
},
"options": {
@ -96,10 +97,232 @@
"label": "אפשרויות מתקדמות",
"submenu": {
"auto-reset-app-cache": "אפס את מטמון האפליקציה כאשר האפליקציה מתחילה",
"disable-hardware-acceleration": "השבת האצת החומרה"
"disable-hardware-acceleration": "השבת האצת החומרה",
"edit-config-json": "ערוך את config.json",
"override-user-agent": "עוקף את סוכן המשתמש",
"restart-on-config-changes": "הפעל מחדש בשינויי תצורה",
"set-proxy": {
"label": "הגדר שרת proxy",
"prompt": {
"label": "הזן כתובת פרוקסי: (להשאיר ריק כדי להשבית)",
"placeholder": "דוגמה: SOCKS5://127.0.0.1:9999",
"title": "הגדר שרת proxy"
}
},
"toggle-dev-tools": "שנה את מצב כלי המפתחים"
}
},
"always-on-top": "השאר מקדימה",
"auto-update": "עדכון אוטומטי",
"hide-menu": {
"dialog": {
"message": "התפריט יוסתר בהפעלה הבאה, השתמש ב-[Alt] כדי להציג אותו (או סמן את [`] אם אתה משתמש בתפריט בתוך האפליקציה)",
"title": "הסתר תפריט מופעל"
},
"label": "הסתר את התפריט"
},
"language": {
"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": "הפעל מחדש",
"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,7 +268,31 @@
}
},
"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": {
"dialog": {
"request": {
"buttons": {
"allow": "अनुमति दें",
"deny": "मना करना"
}
}
}
},

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

@ -2,7 +2,7 @@
"common": {
"console": {
"plugins": {
"execute-failed": "Gagal saat mengeksekusi plugin {{pluginName}}::{{contextName}}",
"execute-failed": "Plugin {{pluginName}}::{{contextName}} dieksekusi di {{ms}}ms",
"executed-at-ms": "Plugin {{pluginName}}::{{contextName}} dieksekusi pada {{ms}}ms",
"initialize-failed": "Gagal dalam menginisialisasi plugin \"{{pluginName}}\"",
"load-all": "Memuat semua plugin",
@ -683,6 +683,7 @@
"listenbrainz": {
"token": "Masukkan token pengguna ListenBrainz"
},
"scrobble-alternative-title": "Gunakan judul alternatif",
"scrobble-other-media": "Scrobble media lain"
},
"name": "Scrobbler",
@ -767,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."
@ -799,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"
}
@ -683,6 +683,7 @@
"listenbrainz": {
"token": "Inserire il token utente per ListenBrainz"
},
"scrobble-alternative-title": "Usa titoli alternativi",
"scrobble-other-media": "Scrobble altri media"
},
"name": "Scrobbler",
@ -733,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": {
@ -744,6 +745,9 @@
"line-effect": {
"label": "Effetto linea",
"submenu": {
"fancy": {
"tooltip": "Usa effetti grandi, simili a quelli di un'app sulla riga attuale"
},
"focus": {
"label": "Focus",
"tooltip": "Rendi bianca solo la riga corrente"
@ -763,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."
@ -795,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

@ -1,10 +1,44 @@
{
"common": {
"console": {
"plugins": {
"execute-failed": "პლაგინის დაყენების შეცდომა {{pluginName}}::{{contextName}}",
"executed-at-ms": "პლაგინი {{pluginName}}::{{contextName}} გაეშვა {{ms}} მილიწამში",
"initialize-failed": "პლაგინის ინიციალიზაცია ვერ მოხდა\"{{pluginName}}\"",
"load-all": "იტვირთება ყველა პლაგინი",
"load-failed": "პლაგინის ჩატვირთვა ვერ მოხდა \"{{pluginName}}\"",
"loaded": "პლაგინი \"{{pluginName}}\" ჩაიტვირთა",
"unload-failed": "პლაგინის {{pluginName}} გათიშვა ვერ მოხერხდა",
"unloaded": "პლაგინი {{pluginName}} გათიშულია"
}
}
},
"language": {
"code": "ka",
"local-name": "ქართული",
"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": {
@ -683,6 +687,7 @@
"listenbrainz": {
"token": "ListenBrainz 유저 토큰 입력"
},
"scrobble-alternative-title": "대체 제목 사용하기",
"scrobble-other-media": "다른 미디어 스크로블하기"
},
"name": "스크로블러",
@ -767,6 +772,10 @@
"label": "가사를 최대한 정교하게 동기화",
"tooltip": "다음 줄의 표시를 밀리초 단위로 계산합니다 (성능에 약간의 영향을 미칠 수 있음)"
},
"romanization": {
"label": "가사 로마자 변환",
"tooltip": "가사가 영어가 아닌 언어로 되어있는 경우, 로마자 표기를 표시합니다."
},
"show-lyrics-even-if-inexact": {
"label": "가사가 정확하지 않더라도 표시",
"tooltip": "노래를 찾을 수 없는 경우, 플러그인이 다른 검색어로 다시 검색합니다.\n두번째 검색 결과는 정확하지 않을 수 있습니다."
@ -799,6 +808,10 @@
"description": "OBS의 확장인 Tuna와의 통합을 활성화합니다",
"name": "Tuna OBS"
},
"unobtrusive-player": {
"description": "노래 재생 중 플레이어가 팝업되는 것을 방지합니다",
"name": "플레이어 방해 금지"
},
"video-toggle": {
"description": "영상/노래 모드를 전환하는 버튼을 추가합니다. 선택적으로 전체 영상 탭을 제거할 수도 있습니다",
"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

@ -207,6 +207,10 @@
}
},
"plugins": {
"ad-speedup": {
"description": "यदि कुनै विज्ञापन चल्छ भने, यसले अडियो म्यूट गर्छ र प्लेब्याक गतिको गति १६x मा सेट गर्छ",
"name": "विज्ञापन तीव्रगति"
},
"adblocker": {
"description": "सबै विज्ञापन र ट्र्याकइंगहरू ब्लक गर्नुहोस्",
"menu": {
@ -275,6 +279,56 @@
},
"name": "परिवेश मोड"
},
"amuse": {
"description": "६के ल्याब्सद्वारा अम्यूस नाउ प्लेइङ विजेटका लागि युट्युब म्युजिक समर्थन थप्दछ",
"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": "API सर्भरका लागि होस्टनेम प्रविष्ट गर्नुहोस् (उदाहरण: 0.0.0.0):",
"title": "होस्टनेम"
},
"port": {
"label": "API सर्भरका लागि पोर्ट प्रविष्ट गर्नुहोस्:",
"title": "पोर्ट"
}
}
},
"audio-compressor": {
"description": "अडियोमा कम्प्रेसन लागू गर्नुहोस् (सङ्केतको सबैभन्दा चर्को भागहरूको भोल्युम कम गर्दछ र नरम भागहरूको भोल्युम बढाउँछ)",
"name": "अडियो कम्प्रेसर"
@ -410,6 +464,21 @@
"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": "विद्यमान फाइलहरू स्किप गर्नुहोस्"
@ -422,6 +491,18 @@
"button": "डाउनलोड"
}
},
"equalizer": {
"description": "प्लेयरमा इक्वलाइजर थप्दछ",
"menu": {
"presets": {
"label": "पूर्वसेटहरू",
"list": {
"bass-booster": "बास बूस्टर"
}
}
},
"name": "इक्वलाइजर"
},
"exponential-volume": {
"description": "भोल्युम स्लाइडरलाई घातीय बनाउँछ त्यसैले कम भोल्युमहरू चयन गर्न सजिलो हुन्छ।",
"name": "एक्सपोनेन्सियल भोल्युम"
@ -649,6 +730,63 @@
"description": "स्वचालित रूपमा गैर-सङ्गीत भागहरू जस्तै इन्ट्रो/आउट्रो वा सङ्गीत भिडियोका भागहरू छोड्नुहोस्",
"name": "स्पन्सरब्लक"
},
"synced-lyrics": {
"description": "LRClib जस्ता प्रदायकहरू प्रयोग गरेर गीतहरूका लागि समक्रमित गीतहरू प्रदान गर्छ।",
"errors": {
"fetch": "⚠️ गीतहरूको जानकारी ल्याउने क्रममा त्रुटि भयो।\n\tकृपया केही समयपछि फेरि प्रयास गर्नुहोस्।",
"not-found": "⚠️ यो गीतका लागि कुनै गीतशब्द फेला परेन।"
},
"menu": {
"default-text-string": {
"label": "पूर्वनिर्धारित अक्षर गीतशब्दहरू बीचमा",
"tooltip": "गीतशब्दहरू बीचको खाली ठाउँका लागि प्रयोग हुने पूर्वनिर्धारित अक्षर छनोट गर्नुहोस्"
},
"line-effect": {
"label": "रेखा प्रभाव",
"submenu": {
"fancy": {
"label": "रमणीय",
"tooltip": "हालको लाइनमा ठूलो, एप-जस्तै प्रभावहरू प्रयोग गर्नुहोस्"
},
"focus": {
"label": "केन्द्रित गर्नुहोस्",
"tooltip": "मात्रै हालको लाइन सेतो बनाउनुहोस्"
},
"offset": {
"label": "अफसेट",
"tooltip": "हालको लाइनलाई दायाँतर्फ अफसेट गर्नुहोस्"
},
"scale": {
"label": "स्केल",
"tooltip": "हालको लाइनको आकार परिवर्तन गर्नुहोस्"
}
},
"tooltip": "हालको लाइनमा लागू गर्ने प्रभाव चयन गर्नुहोस्"
},
"precise-timing": {
"label": "गीतशब्दहरू पूर्ण रूपमा समक्रमित बनाउनुहोस्",
"tooltip": "अर्को लाइनको प्रदर्शनलाई मिलिसेकेन्डमा गणना गर्नुहोस् (यसले प्रदर्शनमा हल्का प्रभाव पार्न सक्छ)"
},
"show-lyrics-even-if-inexact": {
"label": "गीतशब्दहरू अपर्याप्त भए पनि देखाउनुहोस्",
"tooltip": "यदि गीत फेला परेन भने, प्लगिनले फरक खोजी सोधपुछसँग पुन: प्रयास गर्छ।\nदोस्रो प्रयासको परिणाम ठ्याक्कै मिल्न नपनि सक्छ।"
},
"show-time-codes": {
"label": "समय कोडहरू देखाउनुहोस्",
"tooltip": "गीतशब्दहरूको छेउमा समय कोडहरू देखाउनुहोस्"
}
},
"name": "समक्रमित गीतशब्दहरू",
"refetch-btn": {
"fetching": "ल्याउँदैछ...",
"normal": "गीतशब्दहरू पुनः ल्याउनुहोस्"
},
"warnings": {
"duration-mismatch": "⚠️ - अवधि असमानताको कारण गीतशब्दहरू असमक्रमित हुन सक्छन्।",
"inexact": "⚠️ - यो गीतका लागि गीतशब्दहरू ठ्याक्कै मिल्दैनन्",
"instrumental": "⚠️ - यो एउटा वाद्य संगीत (इन्स्ट्रुमेन्टल) गीत हो"
}
},
"taskbar-mediacontrol": {
"description": "तपाईँको विन्डोज टास्कबारबाट प्लेब्याक नियन्त्रण गर्नुहोस्",
"name": "टास्कबार मेडिया कन्ट्रोल"

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"
},
@ -683,6 +683,7 @@
"listenbrainz": {
"token": "Podaj token użytkownika ListenBrainz"
},
"scrobble-alternative-title": "Użyj alternatywnych tytułów",
"scrobble-other-media": "Scrobbluj pozostałe multimedia"
},
"name": "Scrobblowanie",
@ -767,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."
@ -799,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

@ -2,14 +2,14 @@
"common": {
"console": {
"plugins": {
"execute-failed": "Falha ao executar o plugin {{pluginName}}::{{contextName}}",
"executed-at-ms": "Plugin {{pluginName}}::{{contextName}} executado com {{ms}}ms",
"initialize-failed": "Falha ao inicializar o plugin \"{{pluginName}}\"",
"load-all": "Carregando todos os plugins",
"load-failed": "Falha ao carregar o plugin \"{{pluginName}}\"",
"loaded": "Plugin \"{{pluginName}}\" carregado",
"unload-failed": "Falha ao descarregar o plugin \"{{pluginName}}\"",
"unloaded": "Plugin \"{{pluginName}}\" descarregado"
"execute-failed": "Não foi possível executar o plugin {{pluginName}}::{{contextName}}",
"executed-at-ms": "Plugin {{pluginName}}::{{contextName}} executado em {{ms}}ms",
"initialize-failed": "Não foi possível iniciar o plugin \"{{pluginName}}\"",
"load-all": "A carregar todos os plugins",
"load-failed": "Não foi possível ativar o plugin \"{{pluginName}}\"",
"loaded": "Plugin \"{{pluginName}}\" ativado",
"unload-failed": "Não foi possível desativar o plugin \"{{pluginName}}\"",
"unloaded": "Plugin \"{{pluginName}}\" desativado"
}
}
},
@ -21,7 +21,7 @@
"main": {
"console": {
"did-finish-load": {
"dev-tools": "Carregamento finalizado. DevTools aberto"
"dev-tools": "Carregamento concluído. DevTools aberto"
},
"i18n": {
"loaded": "i18n carregado"
@ -30,64 +30,64 @@
"receive-command": "Comando recebido através do protocolo: \"{{command}}\""
},
"theme": {
"css-file-not-found": "Arquivo CSS \"{{cssFile}}\" não existe, ignorando"
"css-file-not-found": "O ficheiro CSS \"{{cssFile}}\" não existe, a ignorar"
},
"unresponsive": {
"details": "Erro sem resposta!\n{{error}}"
"details": "Erro de falta de resposta!\n{{error}}"
},
"when-ready": {
"clearing-cache-after-20s": "Limpando o cache do aplicativo"
"clearing-cache-after-20s": "A limpar a cache da aplicação"
},
"window": {
"tried-to-render-offscreen": "Janela tentou desenhar fora do ecrã, windowSize={{windowSize}}, displaySize={{displaySize}}, position={{position}}"
"tried-to-render-offscreen": "Tentativa de desenho fora do ecrã na janela, windowSize={{windowSize}}, displaySize={{displaySize}}, position={{position}}"
}
},
"dialog": {
"hide-menu-enabled": {
"detail": "O menu está oculto, use a tecla 'Alt' para mostra-lo (ou 'Esc' se estiver usando o menu do aplicativo)",
"message": "Ocultar menu está ativado",
"title": "Ocultar menu ativado"
"detail": "O menu está oculto, utilize \"Alt\" para o mostrar (ou \"Escape\" se estiver a utilizar o menu da aplicação)",
"message": "Ocultar Menu está ativado",
"title": "Ocultar Menu ativado"
},
"need-to-restart": {
"buttons": {
"later": "Depois",
"restart-now": "Reiniciar agora"
},
"detail": "O plugin {{pluginName}} precisa ser reiniciado para ter efeito",
"message": "\"{{pluginName}}\" precisa ser reiniciado",
"detail": "O plugin \"{{pluginName}}\" requer um reinício para ter efeito",
"message": "\"{{pluginName}}\" precisa de ser reiniciado",
"title": "É necessário reiniciar"
},
"unresponsive": {
"buttons": {
"quit": "Sair",
"relaunch": "Reiniciar",
"wait": "Espere"
"wait": "Esperar"
},
"detail": "Lamentamos o inconveniente! Por favor escolha o que fazer:",
"message": "A aplicação não está respondendo",
"title": "A janela não está respondendo"
"detail": "Lamentamos o incómodo! Por favor, escolha o que fazer:",
"message": "A aplicação não está a responder",
"title": "A janela não está a responder"
},
"update-available": {
"buttons": {
"disable": "Desabilitar atualizações",
"download": "Baixar",
"disable": "Desativar atualizações",
"download": "Transferir",
"ok": "Ok"
},
"detail": "Uma nova versão está disponível e pode ser baixada em {{downloadLink}}",
"message": "Uma nova versão está disponível",
"detail": "Está disponível uma nova versão que pode ser descarregada em {{downloadLink}}",
"message": "Está disponível uma nova versão",
"title": "Atualização disponível"
}
},
"menu": {
"about": "Sobre",
"about": "Acerca de",
"navigation": {
"label": "Navegação",
"submenu": {
"copy-current-url": "Copiar URL atual",
"go-back": "Voltar",
"go-back": "Retroceder",
"go-forward": "Avançar",
"quit": "Saída",
"restart": "Reiniciar aplicativo"
"quit": "Sair",
"restart": "Reiniciar a aplicação"
}
},
"options": {
@ -96,15 +96,15 @@
"advanced-options": {
"label": "Opções avançadas",
"submenu": {
"auto-reset-app-cache": "Reiniciar cache do aplicativo quando o aplicativo abrir",
"disable-hardware-acceleration": "Desabilitar aceleração por hardware",
"auto-reset-app-cache": "Repor a cache quando a aplicação é iniciada",
"disable-hardware-acceleration": "Desativar a aceleração de hardware",
"edit-config-json": "Editar config.json",
"override-user-agent": "Substituir User-Agent",
"restart-on-config-changes": "Reinicie as alterações de configurações feitas",
"restart-on-config-changes": "Reiniciar após alterações de configuração",
"set-proxy": {
"label": "Definir proxy",
"prompt": {
"label": "Inserir Endereço do Proxy: (deixe em branco para desativar)",
"label": "Introduza o endereço do proxy: (deixe em branco para desativar)",
"placeholder": "Exemplo: SOCKS5://127.0.0.1:9999",
"title": "Definir proxy"
}
@ -113,62 +113,62 @@
}
},
"always-on-top": "Sempre no topo",
"auto-update": "Atualização automática",
"auto-update": "Atualizações automáticas",
"hide-menu": {
"dialog": {
"message": "O menu será ocultado na próxima inicialização, use [Alt] para mostrá-lo (ou acento grave [`] se estiver usando o menu interno do aplicativo)",
"title": "Ocultar Menu Ativado"
"message": "O menu será ocultado da próxima vez que abrir a aplicação, utilize [Alt] para o mostrar (ou [`] se estiver a utilizar o menu interno da aplicação)",
"title": "Ocultar Menu ativado"
},
"label": "Ocultar Menu"
},
"language": {
"dialog": {
"message": "Idioma será alterado após reiniciar",
"title": "Idioma Alterado"
"message": "O idioma será alterado após o reinício",
"title": "Idioma alterado"
},
"label": "Idioma",
"submenu": {
"to-help-translate": "Quer ajudar na tradução? Clique aqui"
}
},
"resume-on-start": "Continuar última música ao iniciar o aplicativo",
"single-instance-lock": "Trava de instância única",
"start-at-login": "Iniciar no login",
"resume-on-start": "Retomar a última música quando a app é iniciada",
"single-instance-lock": "Limitar a uma única instância",
"start-at-login": "Iniciar com o sistema",
"starting-page": {
"label": "Página inicial",
"unset": "Indefinido"
"unset": "Não definida"
},
"tray": {
"label": "Bandeja",
"label": "Tabuleiro do sistema",
"submenu": {
"disabled": "Desabilitado",
"enabled-and-hide-app": "Ativado e esconder aplicativo",
"enabled-and-show-app": "Ativado e mostrar aplicativo",
"play-pause-on-click": "Play/Pausa ao clicar"
"disabled": "Desativado",
"enabled-and-hide-app": "Ativado e ocultar a aplicação",
"enabled-and-show-app": "Ativado e mostrar a aplicação",
"play-pause-on-click": "Reproduzir/Pausar ao clicar"
}
},
"visual-tweaks": {
"label": "Tweaks Visuais",
"label": "Ajustes visuais",
"submenu": {
"like-buttons": {
"default": "Padrão",
"force-show": "Forçar mostrar",
"force-show": "Mostrar sempre",
"hide": "Esconder",
"label": "Botões de curtida"
"label": "Botões do \"Gosto\""
},
"remove-upgrade-button": "Remover botão upgrade",
"remove-upgrade-button": "Remover o botão de upgrade",
"theme": {
"dialog": {
"button": {
"cancel": "Cancelar",
"remove": "Remover"
},
"remove-theme": "Você tem certeza que quer remover o tema customizado?",
"remove-theme-message": "Isso removerá o tema customizado"
"remove-theme": "Tem a certeza de que pretende remover o tema personalizado?",
"remove-theme-message": "Isto irá remover o tema personalizado"
},
"label": "Tema",
"submenu": {
"import-css-file": "Importar arquivo CSS personalizado",
"import-css-file": "Importar ficheiro CSS personalizado",
"no-theme": "Sem tema"
}
}
@ -186,19 +186,19 @@
"submenu": {
"force-reload": "Forçar Recarregamento",
"reload": "Recarregar",
"reset-zoom": "Tamanho Atual",
"toggle-fullscreen": "Ativar Tela Cheia",
"zoom-in": "Zoom Dentro",
"zoom-out": "Zoom Fora"
"reset-zoom": "Tamanho real",
"toggle-fullscreen": "Ativar ecrã inteiro",
"zoom-in": "Aumentar o zoom",
"zoom-out": "Diminuir o zoom"
}
}
},
"tray": {
"next": "Próximo",
"play-pause": "Play/Pausa",
"next": "Próxima",
"play-pause": "Reproduzir/Pausar",
"previous": "Anterior",
"quit": "Sair",
"restart": "Reiniciar aplicativo",
"restart": "Reiniciar aplicação",
"show": "Mostrar janela",
"tooltip": {
"default": "YouTube Music",
@ -208,11 +208,11 @@
},
"plugins": {
"ad-speedup": {
"description": "Se um anúncio for reproduzido, ele será silenciado o áudio e será definido a velocidade de reprodução para 16x",
"description": "Se um anúncio for reproduzido, silencia o áudio e define a velocidade de reprodução para 16x",
"name": "Acelerar os anúncios"
},
"adblocker": {
"description": "Bloquear todos os anúncios e rastreamento automaticamente",
"description": "Bloqueie todos os anúncios e monitorização automaticamente",
"menu": {
"blocker": "Bloqueador"
},
@ -223,10 +223,10 @@
"name": "Ações no álbum"
},
"album-color-theme": {
"description": "Aplica um tema dinâmico e efeitos visuais com base na paleta de cores do álbum",
"description": "Aplica um tema dinâmico e efeitos visuais baseados na paleta de cores do álbum",
"menu": {
"color-mix-ratio": {
"label": "Relação de mistura de cores",
"label": "Proporção de mistura de cores",
"submenu": {
"percent": "{{ratio}}%"
}
@ -235,7 +235,7 @@
"name": "Tema de cores do álbum"
},
"ambient-mode": {
"description": "Aplica um efeito de iluminação lançando cores suaves do vídeo no fundo da tela.",
"description": "Aplica um efeito de iluminação, projetando cores suaves do vídeo no fundo do ecrã",
"menu": {
"blur-amount": {
"label": "Quantidade de desfoque",
@ -274,45 +274,64 @@
}
},
"use-fullscreen": {
"label": "Tela cheia"
"label": "Utilizar o ecrã inteiro"
}
},
"name": "Modo Ambiente"
},
"amuse": {
"description": "Adiciona suporte ao YouTube Music para o widget Amuse now playing da 6K Labs",
"name": "Amuse",
"response": {
"query": "O servidor da API Amuse está a ser executado. GET /query para obter informações sobre uma música."
}
},
"api-server": {
"description": "Adiciona um servidor API para controlar o leitor",
"dialog": {
"request": {
"buttons": {
"allow": "Permitir",
"deny": "Negar"
}
},
"message": "Permitir que {{ID}} ({{origin}}) aceda à API?",
"title": "Pedido de autorização da API"
}
},
"menu": {
"auth-strategy": {
"label": "Estratégia de Autorização",
"label": "Estratégia de autorização",
"submenu": {
"auth-at-first": {
"label": "Autorizar ao primeiro pedido"
"label": "Autorizar no primeiro pedido"
},
"none": {
"label": "Sem autorização"
}
}
},
"hostname": {
"label": "Nome do anfitrião"
},
"port": {
"label": "Porta"
}
},
"name": "Servidor API [Beta]",
"prompt": {
"hostname": {
"label": "Introduza o nome do anfitrião (como 0.0.0.0) para o servidor API:",
"title": "Nome do anfitrião"
},
"port": {
"label": "Introduza a porta para o servidor API:",
"title": "Porta"
}
}
},
"audio-compressor": {
"description": "Aplicar compressão ao áudio (diminui o volume das partes mais altas do sinal e aumenta o volume das partes mais suaves)",
"name": "Compressor de áudio"
"name": "Compressor de Áudio"
},
"blur-nav-bar": {
"description": "Torna a barra de navegação transparente e desfocada",
@ -323,21 +342,21 @@
"name": "Ignorar restrições de idade"
},
"captions-selector": {
"description": "Seletor de legenda para faixas de áudio do YouTube Music",
"description": "Seletor de legendas para as faixas de áudio do YouTube Music",
"menu": {
"autoload": "Selecionar automaticamente a última legenda usada",
"disable-captions": "Sem legendas por padrão"
"autoload": "Selecionar automaticamente a última legenda utilizada",
"disable-captions": "Sem legendas por defeito"
},
"name": "Seletor de legendas",
"prompt": {
"selector": {
"label": "Idioma da legenda atual: {{language}}",
"label": "Idioma atual das legendas: {{language}}",
"none": "Nenhuma",
"title": "Selecione o idioma da legenda"
"title": "Selecione o idioma das legendas"
}
},
"templates": {
"title": "Seletor de legendas aberto"
"title": "Abrir o seletor de legendas"
}
},
"compact-sidebar": {
@ -353,41 +372,41 @@
"prompt": {
"options": {
"multi-input": {
"fade-in-duration": "Duração da transição no início (ms)",
"fade-out-duration": "Duração da transição no final (ms)",
"fade-in-duration": "Duração da transição (fade-in) no início (ms)",
"fade-out-duration": "Duração da transição (fade-out) no final (ms)",
"fade-scaling": {
"label": "Escala da transição",
"linear": "Linear",
"logarithmic": "Logarítmica"
},
"seconds-before-end": "Realizar transição N segundos antes do final"
"seconds-before-end": "Realizar transição N segundos antes do fim"
},
"title": "Opções de transição"
"title": "Opções da transição"
}
}
},
"disable-autoplay": {
"description": "Faz a música começar no modo \"pausado\"",
"description": "Faz com que a música comece no modo \"pausado\"",
"menu": {
"apply-once": "Aplicar apenas na inicialização"
"apply-once": "Aplicar apenas no arranque"
},
"name": "Desativar reprodução automática"
},
"discord": {
"backend": {
"already-connected": "Tentativa de conexão com conexão já ativa",
"already-connected": "Tentativa de conexão com ligação já ativa",
"connected": "Conectado ao Discord",
"disconnected": "Desconectado do Discord"
},
"description": "Mostre aos seus amigos o que você ouve com Rich Presence",
"description": "Mostre aos seus amigos o que está a ouvir com a Rich Presence",
"menu": {
"auto-reconnect": "Reconexão automática",
"auto-reconnect": "Reconectar automaticamente",
"clear-activity": "Limpar atividade",
"clear-activity-after-timeout": "Limpar atividade após o tempo limite",
"connected": "Conectado",
"disconnected": "Desconectado",
"hide-duration-left": "Ocultar duração restante",
"hide-github-button": "Ocultar botão de link do GitHub",
"hide-github-button": "Ocultar botão do GitHub",
"play-on-youtube-music": "Reproduzir no YouTube Music",
"set-inactivity-timeout": "Definir tempo limite de inatividade"
},
@ -406,85 +425,97 @@
"buttons": {
"ok": "OK"
},
"message": "Poxa! Desculpe, o download falhou…",
"title": "Erro no download!"
"message": "Ah! Desculpas, o download falhou…",
"title": "Erro ao transferir!"
},
"start-download-playlist": {
"buttons": {
"ok": "OK"
},
"detail": "({{playlistSize}} músicas)",
"message": "Baixando lista de reprodução {{playlistTitle}}",
"message": "A descarregar a lista de reprodução {{playlistTitle}}",
"title": "Download iniciado"
}
},
"feedback": {
"conversion-progress": "Conversão: {{percent}}%",
"converting": "Convertendo…",
"done": "Finalizado: {{filePath}}",
"download-info": "Baixando {{artist}} - {{title}} {{videoId}}",
"download-progress": "Baixando: {{percent}}%",
"downloading": "Baixando…",
"downloading-counter": "Baixando {{current}}/{{total}}…",
"downloading-playlist": "Baixando lista de reprodução \"{{playlistTitle}}\" - {{playlistSize}} músicas ({{playlistId}})",
"error-while-downloading": "Erro ao baixar \"{{author}} - {{title}}\": {{error}}",
"converting": "A converter…",
"done": "Concluído: {{filePath}}",
"download-info": "A descarregar {{artist}} - {{title}} {{videoId}}",
"download-progress": "A transferir: {{percent}}%",
"downloading": "A transferir…",
"downloading-counter": "A transferir {{current}}/{{total}}…",
"downloading-playlist": "A descarregar a lista de reprodução \"{{playlistTitle}}\" - {{playlistSize}} músicas ({{playlistId}})",
"error-while-downloading": "Erro ao descarregar \"{{author}} - {{title}}\": {{error}}",
"folder-already-exists": "A pasta {{playlistFolder}} já existe",
"getting-playlist-info": "Obtendo informações da lista de reprodução…",
"loading": "Carregando…",
"playlist-has-only-one-song": "A lista de reprodução possui apenas um item, baixando-o diretamente",
"playlist-id-not-found": "ID da lista de reprodução não encontrado",
"getting-playlist-info": "A obter informações da playlist…",
"loading": "A carregar…",
"playlist-has-only-one-song": "A lista de reprodução tem apenas um item, descarregando-o diretamente",
"playlist-id-not-found": "Não foi encontrado nenhum ID da lista de reprodução",
"playlist-is-empty": "Lista de reprodução vazia",
"playlist-is-mix-or-private": "Erro ao obter informações da lista de reprodução: tenha certeza que ela não está privada ou não seja \"Mixtapes criadas para você\"\n\n{{error}}",
"preparing-file": "Preparando arquivos…",
"saving": "Salvando…",
"trying-to-get-playlist-id": "Tentando pegar ID da lista de reprodução: {{playlistId}}",
"playlist-is-mix-or-private": "Erro ao obter informações da playlist: certifique-se de que ela não seja privada ou uma mix personalizada\n\n{{error}}",
"preparing-file": "A preparar o ficheiro…",
"saving": "A guardar…",
"trying-to-get-playlist-id": "A tentar obter o ID da playlist: {{playlistId}}",
"video-id-not-found": "Vídeo não encontrado",
"writing-id3": "Escrevendo tags ID3…"
"writing-id3": "A guardar etiquetas ID3…"
}
},
"description": "Baixa MP3 / fonte de áudio diretamente da interface",
"description": "Descarregue MP3 / fonte de áudio diretamente da interface",
"menu": {
"choose-download-folder": "Escolha a pasta de download",
"download-finish-settings": {
"label": "Baixar ao terminar",
"label": "Transferir ao terminar",
"prompt": {
"last-percent": "Depois de x por cento",
"last-seconds": "Últimos x segundos",
"title": "Configurar quando baixar"
"title": "Configurar quando descarregar"
},
"submenu": {
"advanced": "Avançado",
"enabled": "Ativado",
"mode": "Modo de tempo",
"percent": "Porcentagem",
"percent": "Percentagem",
"seconds": "Segundos"
}
},
"download-playlist": "Baixar lista de reprodução",
"presets": "Predefinições",
"skip-existing": "Ignorar arquivos existentes"
"download-playlist": "Descarregar playlist",
"presets": "Pré-configurações",
"skip-existing": "Ignorar ficheiros existentes"
},
"name": "Downloader",
"renderer": {
"can-not-update-progress": "Não é possível atualizar o progresso"
},
"templates": {
"button": "Baixar"
"button": "Descarregar"
}
},
"equalizer": {
"description": "Adiciona um equalizador ao leitor",
"menu": {
"presets": {
"label": "Pré-configurações",
"list": {
"bass-booster": "Amplificador de graves"
}
}
},
"name": "Equalizador"
},
"exponential-volume": {
"description": "Torna o controle deslizante de volume exponencial, facilitando a seleção de volumes mais baixos.",
"description": "Torna o controlo do volume exponencial para que seja mais fácil selecionar volumes mais baixos.",
"name": "Volume Exponencial"
},
"in-app-menu": {
"description": "Dá às barras de menu uma aparência sofisticada, escura ou com a cor do álbum",
"menu": {
"hide-dom-window-controls": "Ocultar controles da janela DOM"
"hide-dom-window-controls": "Ocultar os controlos da janela DOM"
},
"name": "Menu no aplicativo"
"name": "Menu da aplicação"
},
"lumiastream": {
"description": "Adiciona suporte Lumia Stream",
"description": "Adiciona suporte a Lumia Stream",
"name": "Lumia Stream [Beta]"
},
"lyrics-genius": {
@ -494,61 +525,61 @@
},
"name": "Letras Genius",
"renderer": {
"fetched-lyrics": "Buscar letras no Genius"
"fetched-lyrics": "Letras encontradas no Genius"
}
},
"music-together": {
"description": "Compartilha a playlist com outros. Quando o host tocar uma música, todos poderão ouvir a mesma canção",
"description": "Partilhe uma playlist com outros. Quando o anfitrião tocar uma música, todos os outros ouvirão a mesma música",
"dialog": {
"enter-host": "Digite o ID do Host"
"enter-host": "Introduza o ID do anfitrião"
},
"internal": {
"save": "Salvar",
"track-source": "Fonte da faixa",
"unknown-user": "Usuário Desconhecido"
"save": "Guardar",
"track-source": "Origem da faixa",
"unknown-user": "Utilizador desconhecido"
},
"menu": {
"click-to-copy-id": "Copiar ID do Host",
"close": "Fechar o Música Juntos",
"connected-users": "Usuários Conectados",
"disconnect": "Desconectar do Música Juntos",
"empty-user": "Sem usuários conectados",
"host": "Host do Música Juntos",
"join": "Juntar ao Música Juntos",
"click-to-copy-id": "Copiar o ID do anfitrião",
"close": "Fechar o Music Together",
"connected-users": "Utilizadores conectados",
"disconnect": "Desconectar Music Together",
"empty-user": "Sem utilizadores conectados",
"host": "Anfitrião do Music Together",
"join": "Juntar-se ao Music Together",
"permission": {
"all": "Permita que outros controlem a playlist e ao player",
"host-only": "Apenas o host pode controlar a playlist e ao player",
"playlist": "Permitir que outros controlem a playlist"
"all": "Permitir que os convidados controlem a playlist e o leitor",
"host-only": "Apenas o anfitrião pode controlar a playlist e o leitor",
"playlist": "Permitir que os convidados controlem a playlist"
},
"set-permission": "Alterar permissões de controle",
"set-permission": "Alterar permissões de controlo",
"status": {
"disconnected": "Desconectado",
"guest": "Conectado como Convidado",
"host": "Conectado como Host"
"guest": "Conectado como convidado",
"host": "Conectado como anfitrião"
}
},
"name": "Música Juntos [Beta]",
"name": "Music Together [Beta]",
"toast": {
"add-song-failed": "Falha ao adicionar canção",
"closed": "Música Juntos encerrado",
"disconnected": "Música Juntos foi desconectado",
"add-song-failed": "Falha ao adicionar música",
"closed": "Music Together fechado",
"disconnected": "Music Together desconectado",
"host-failed": "Falha ao hospedar o Music Together",
"id-copied": "ID de anfitrião copiado para a área de transferência",
"id-copy-failed": "Falha ao copiar o ID de anfitrião para a área de transferência",
"join-failed": "Falha ao entrar em Music Together",
"joined": "Entrou em Music Together",
"id-copied": "ID do anfitrião copiado para a Área de Transferência",
"id-copy-failed": "Falha ao copiar o ID do anfitrião para a Área de Transferência",
"join-failed": "Falha ao entrar no Music Together",
"joined": "Entrou no Music Together",
"permission-changed": "A permissão do Music Together foi alterada para \"{{permission}}\"",
"remove-song-failed": "Falha ao remover música",
"user-connected": "{{name}} entrou em Music Together",
"user-connected": "{{name}} entrou no Music Together",
"user-disconnected": "{{name}} saiu do Music Together"
}
},
"navigation": {
"description": "Setas de navegação Próximo/Voltar integradas diretamente na interface, como no seu navegador favorito",
"description": "Setas de navegação Avançar/Retroceder integradas diretamente na interface, como no seu navegador favorito",
"name": "Navegação"
},
"no-google-login": {
"description": "Remove os botões e links de login do Google da interface",
"description": "Remove os botões de login do Google e links da interface",
"name": "Sem login do Google"
},
"notifications": {
@ -556,36 +587,36 @@
"menu": {
"interactive": "Notificações interativas",
"interactive-settings": {
"label": "Configurações interativas",
"label": "Definições interativas",
"submenu": {
"hide-button-text": "Ocultar texto do botão",
"hide-button-text": "Ocultar o texto do botão",
"refresh-on-play-pause": "Atualizar ao reproduzir/pausar",
"tray-controls": "Abrir/Fechar no clique da bandeja"
"tray-controls": "Abrir/Fechar com um clique no tabuleiro do sistema"
}
},
"priority": "Prioridade de notificação",
"toast-style": "Estilo de alerta",
"unpause-notification": "Mostrar notificação ao despausar"
"priority": "Prioridade da notificação",
"toast-style": "Estilo da notificação",
"unpause-notification": "Mostrar notificação ao desativar a pausa"
},
"name": "Notificações"
},
"picture-in-picture": {
"description": "Permite mudar o aplicativo para o modo picture-in-picture",
"description": "Permite mudar a aplicação para o modo picture-in-picture",
"menu": {
"always-on-top": "Sempre no topo",
"always-on-top": "Sempre em cima",
"hotkey": {
"label": "Tecla de atalho",
"prompt": {
"keybind-options": {
"hotkey": "Tecla de atalho"
},
"label": "Escolha uma tecla de atalho para alternar o picture-in-picture",
"label": "Escolha uma tecla de atalho para ativar o picture-in-picture",
"title": "Tecla de atalho picture-in-picture"
}
},
"save-window-position": "Salvar posição da janela",
"save-window-size": "Salvar tamanho da janela",
"use-native-pip": "Use PiP nativo do navegador"
"save-window-position": "Guardar posição da janela",
"save-window-size": "Guardar tamanho da janela",
"use-native-pip": "Utilizar PiP nativo do navegador"
},
"name": "Picture-in-picture",
"templates": {
@ -593,17 +624,17 @@
}
},
"playback-speed": {
"description": "Ouça rápido, ouça devagar! Adiciona um controle deslizante que controla a velocidade da música",
"description": "Ouça rápido, ouça devagar! Adiciona um controlo deslizante que controla a velocidade da música",
"name": "Velocidade de reprodução",
"templates": {
"button": "Velocidade"
}
},
"precise-volume": {
"description": "Controle o volume com precisão usando a roda do mouse/teclas de atalho, com um HUD personalizado e etapas de volume personalizáveis",
"description": "Controle o volume com precisão utilizando a roda do rato/teclas de atalho, com um HUD personalizado e incrementos de volume personalizáveis",
"menu": {
"arrows-shortcuts": "Controles locais das teclas de seta",
"custom-volume-steps": "Definir etapas de volume personalizadas",
"arrows-shortcuts": "Controlo preciso com as teclas de seta esq./dir.",
"custom-volume-steps": "Definir incrementos de volume personalizados",
"global-shortcuts": "Teclas de atalho globais"
},
"name": "Volume preciso",
@ -613,12 +644,12 @@
"decrease": "Diminuir o volume",
"increase": "Aumentar o volume"
},
"label": "Escolha atalhos de teclado de volume global:",
"title": "Atalhos de teclado de volume global"
"label": "Escolha atalhos de teclado para o volume global:",
"title": "Atalhos de teclado para o volume global"
},
"volume-steps": {
"label": "Escolha as etapas de aumento/diminuição de volume",
"title": "Etapas de volume"
"label": "Escolha o tamanho dos incrementos/decrementos de volume",
"title": "Incrementos de volume"
}
}
},
@ -632,90 +663,95 @@
}
}
},
"description": "Permite alterar a qualidade do vídeo com um botão na sobreposição de vídeo",
"name": "Trocador de qualidade do vídeo"
"description": "Permite alterar a qualidade do vídeo com um botão sobreposto ao vídeo",
"name": "Alterador de qualidade de vídeo"
},
"scrobbler": {
"description": "Adicionar suporte para scrobbling (Last.fm, ListenBrainz)",
"description": "Adicionar suporte para scrobbling (last.fm, Listenbrainz, etc.)",
"dialog": {
"lastfm": {
"auth-failed": {
"message": "Falha ao autenticar com a Last.fm\nOculte o pop-up até a próxima reinicialização.",
"message": "Falha ao autenticar com a Last.fm\nO pop-up será ocultado até reiniciar a aplicação.",
"title": "Falha na autenticação"
}
}
},
"menu": {
"lastfm": {
"api-settings": "Configurações de API Last.fm"
"api-settings": "Definições da API da Last.fm"
},
"listenbrainz": {
"token": "Insira o token de utilizador ListenBrainz"
"token": "Introduza o token de utilizador do ListenBrainz"
},
"scrobble-other-media": "Scrobble outros mídia"
"scrobble-alternative-title": "Usar títulos alternativos",
"scrobble-other-media": "Scrobble de outros conteúdos"
},
"name": "Scrobbler",
"prompt": {
"lastfm": {
"api-key": "Chave de API Last.fm",
"api-secret": "Segredo da API Last.fm"
"api-key": "Chave da API da Last.fm",
"api-secret": "Segredo da API da Last.fm"
},
"listenbrainz": {
"token": {
"label": "Insira seu token de usuário do ListenBrainz:",
"label": "Introduza o seu token de utilizador ListenBrainz:",
"title": "Token ListenBrainz"
}
}
}
},
"shortcuts": {
"description": "Permite definir teclas de atalho globais para reprodução (reproduzir/pausar/próximo/anterior) e desligar o OSD de mídia substituindo as teclas de mídia, ativando Ctrl/CMD + F para pesquisar, ativando o suporte Linux MPRIS para teclas de mídia e teclas de atalho personalizadas para usuários avançados",
"description": "Permite definir teclas de atalho globais para a reprodução (reproduzir/pausar/próximo/anterior) e desativar o OSD multimédia substituindo as teclas multimédia, ativar Ctrl/CMD + F para pesquisar, ativar o suporte Linux MPRIS para teclas multimédia e teclas de atalho personalizadas para utilizadores avançados",
"menu": {
"override-media-keys": "Substituir teclas de mídia",
"set-keybinds": "Definir controles globais de música"
"override-media-keys": "Substituir as teclas de multimédia",
"set-keybinds": "Definir controlos globais para a música"
},
"name": "Atalhos (& MPRIS)",
"prompt": {
"keybind": {
"keybind-options": {
"next": "Próximo",
"play-pause": "Reproduzir/Pausar",
"play-pause": "Reproduzir / Pausar",
"previous": "Anterior"
},
"label": "Escolha atalhos de teclado globais para controle de músicas:",
"label": "Escolha teclas globais para o controlo da música:",
"title": "Atalhos globais do teclado"
}
}
},
"skip-disliked-songs": {
"description": "Pula músicas com 'não gostei'",
"name": "Pular músicas com 'não gostei'"
"description": "Salta as canções de que não gosta",
"name": "Saltar músicas que não gostei"
},
"skip-silences": {
"description": "Pular automaticamente seções de silêncio nas músicas",
"name": "Pular silêncios"
"description": "Saltar automaticamente as partes silenciosas das canções",
"name": "Saltar silêncios"
},
"sponsorblock": {
"description": "Ignora automaticamente partes não musicais, como introdução/final ou partes de videoclipes onde a música não está tocando",
"name": "SponsorBlock (bloqueador de patrocínios)"
"description": "Salta automaticamente partes que não são música, como a intro/outro ou partes de vídeos de música em que a música não está a ser reproduzida",
"name": "SponsorBlock"
},
"synced-lyrics": {
"description": "Fornece letras sincronizadas de músicas, utilizando fornecedores como o LRClib.",
"description": "Fornece letras de músicas sincronizadas, utilizando fornecedores como o LRClib.",
"errors": {
"fetch": "⚠️ - Ocorreu um erro ao obter as letras da música. Por favor, tenta novamente mais tarde.",
"not-found": "⚠️ - Não foram encontradas letras para esta música."
"fetch": "⚠️ Ocorreu um erro ao obter a letra da música.\n\tPor favor, tente novamente mais tarde.",
"not-found": "⚠️ Não foram encontradas letras para esta canção."
},
"menu": {
"default-text-string": {
"label": "Caractere padrão entre as letras",
"tooltip": "Escolha o caractere padrão para usar no espaço entre as letras"
"label": "Carácter predefinido entre letras",
"tooltip": "Escolha o carácter predefinido a utilizar para o intervalo entre letras"
},
"line-effect": {
"label": "Efeito de linha",
"label": "Efeito da linha",
"submenu": {
"fancy": {
"label": "Elegante",
"tooltip": "Utilizar grandes efeitos semelhantes aos da aplicação na linha atual"
},
"focus": {
"label": "Foco",
"tooltip": "Deixe apenas a linha atual branca"
"tooltip": "Tornar branca apenas a linha atual"
},
"offset": {
"label": "Deslocamento",
@ -729,11 +765,15 @@
"tooltip": "Escolha o efeito a ser aplicado à linha atual"
},
"precise-timing": {
"label": "Sincronize perfeitamente as letras",
"tooltip": "Calcule até o milissegundo a exibição da próxima linha (pode ter um pequeno impacto no desempenho)"
"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 letras mesmo que imprecisas",
"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."
},
"show-time-codes": {
@ -743,29 +783,33 @@
},
"name": "Letras Sincronizadas",
"refetch-btn": {
"fetching": "Buscando...",
"fetching": "A obter...",
"normal": "Buscar as letras novamente"
},
"warnings": {
"duration-mismatch": "⚠️ - As letras da música pode estar dessincronizada devido a um erro de duração.",
"inexact": "⚠️ - As letras desta música podem não ser exactas.",
"instrumental": "⚠️ - Esta é uma música instrumental."
"duration-mismatch": "⚠️ - A letra da música pode estar dessincronizada devido a um erro de duração.",
"inexact": "⚠️ - A letra desta canção pode não ser exata",
"instrumental": "⚠️ - Esta é uma música instrumental"
}
},
"taskbar-mediacontrol": {
"description": "Controle a reprodução na barra de tarefas do Windows",
"name": "Controle de mídia da barra de tarefas"
"description": "Controle a reprodução a partir da barra de tarefas do Windows",
"name": "Controlo multimédia da barra de tarefas"
},
"touchbar": {
"description": "Adiciona um widget TouchBar para usuários do macOS",
"name": "Barra de toque"
"description": "Adiciona um widget TouchBar para utilizadores do macOS",
"name": "TouchBar"
},
"tuna-obs": {
"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. Também pode, opcionalmente, remover toda a guia de vídeo",
"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": {
"align": {
"label": "Alinhamento",
@ -775,12 +819,12 @@
"right": "Direita"
}
},
"force-hide": "Forçar remoção da guia de vídeo",
"force-hide": "Forçar a remoção do separador de vídeo",
"mode": {
"label": "Modo",
"submenu": {
"custom": "Alternar personalizado",
"disabled": "Desabilitado",
"disabled": "Desativado",
"native": "Alternar nativo"
}
}
@ -791,11 +835,11 @@
}
},
"visualizer": {
"description": "Adiciona um visualizador ao player",
"description": "Adiciona um visualizador ao leitor",
"menu": {
"visualizer-type": "Tipo de visualizador"
},
"name": "Visualizer"
"name": "Visualizador"
}
}
}

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

@ -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": "ප්‍රතිචාර නොදක්ව දෝෂයක් {{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": "මෙනුව සැගවීම සාර්තකයි",
"detail": "මෙනුව සැගවී ඇත, නැවත පෙන්වීමට 'Alt' යතුර භාවිතා කරන්න. (හෝ In-App මෙනුවේ 'Escape')",
"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

@ -1,9 +1,131 @@
{
"common": {
"console": {
"plugins": {
"execute-failed": "Misslyckades med att köra plugin {{pluginName}}::{{contextName}}",
"executed-at-ms": "Plugin {{pluginName}}::{{contextName}} kördes på {{ms}} ms",
"initialize-failed": "Misslyckades med att initialisera pluginen \"{{pluginName}}\"",
"load-all": "Laddar alla pluginer",
"load-failed": "Misslyckades med att ladda pluginen \"{{pluginName}}\"",
"loaded": "Pluginen \"{{pluginName}}\" laddad",
"unload-failed": "Misslyckades med att avlasta pluginen \"{{pluginName}}\"",
"unloaded": "Pluginen \"{{pluginName}}\" avlastad"
}
}
},
"language": {
"code": "sv",
"local-name": "Svenska",
"name": "Swedish"
},
"main": {
"console": {
"did-finish-load": {
"dev-tools": "Laddning klar. DevTools öppnad"
},
"i18n": {
"loaded": "i18n laddad"
},
"second-instance": {
"receive-command": "Mottog kommando via protokoll: \"{{command}}\""
},
"theme": {
"css-file-not-found": "CSS-filen \"{{cssFile}}\" finns inte, ignorerar"
},
"unresponsive": {
"details": "Oresponsivt fel!\n{{error}}"
},
"when-ready": {
"clearing-cache-after-20s": "Rensar appens cache"
},
"window": {
"tried-to-render-offscreen": "Fönstret försökte rendera utanför skärmen, windowSize={{windowSize}}, displaySize={{displaySize}}, position={{position}}"
}
},
"dialog": {
"hide-menu-enabled": {
"detail": "Menyn är dold, använd 'Alt' för att visa den (eller 'Escape' om du använder inbyggd meny)",
"message": "Dölj Meny är aktiverat",
"title": "Dölj Meny aktiverad"
},
"need-to-restart": {
"buttons": {
"later": "Senare",
"restart-now": "Starta om nu"
},
"detail": "\"{{pluginName}}\" pluginen kräver en omstart för att träda i kraft",
"message": "\"{{pluginName}}\" behöver startas om",
"title": "Omstart krävs"
},
"unresponsive": {
"buttons": {
"quit": "Avsluta",
"relaunch": "Starta om",
"wait": "Vänta"
},
"detail": "Vi beklagar besväret! Välj vad du vill göra:",
"message": "Programmet svarar inte",
"title": "Fönstret svarar inte"
},
"update-available": {
"buttons": {
"disable": "Stäng av uppdateringar",
"download": "Ladda ned",
"ok": "OK"
},
"detail": "En ny version är tillgänglig och kan laddas ned på {{downloadLink}}",
"message": "En ny version är tillgänglig",
"title": "Uppdatering tillgänglig"
}
},
"menu": {
"about": "Om",
"navigation": {
"label": "Navigering",
"submenu": {
"copy-current-url": "Kopiera nuvarande länk",
"go-back": "Föregående",
"go-forward": "Nästa",
"quit": "Lämna",
"restart": "Starta om appen"
}
},
"options": {
"label": "Valmöjligheter",
"submenu": {
"advanced-options": {
"label": "Avancerade valmöjligheter",
"submenu": {
"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",
"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"
}
}
}
},
"plugins": {
"navigation": {
"name": "Navigering"

836
src/i18n/resources/ta.json Normal file
View File

@ -0,0 +1,836 @@
{
"common": {
"console": {
"plugins": {
"execute-failed": "சொருகி செயல்படுத்துவதில் தோல்வி {{pluginName}} :: {{contextName}}",
"executed-at-ms": "சொருகி {{pluginName}} :: {{contextName}} {{ms}} ms இல் செயல்படுத்தப்படுகிறது",
"initialize-failed": "சொருகி தொடங்குவதில் தோல்வி \"{{pluginName}}\"",
"load-all": "அனைத்து செருகுநிரல்களையும் ஏற்றுகிறது",
"load-failed": "சொருகி ஏற்றுவதில் தோல்வி \"{{pluginName}}\"",
"loaded": "சொருகி \"{{pluginName}}\" ஏற்றப்பட்டது",
"unload-failed": "சொருகி \"{{pluginName}}\"",
"unloaded": "சொருகி \"{{pluginName}}\" இறக்கப்பட்டது"
}
}
},
"language": {
"code": "ta",
"local-name": "தமிழ்",
"name": "Tamil"
},
"main": {
"console": {
"did-finish-load": {
"dev-tools": "ஏற்றுதல் முடிந்தது. டெவ்டூல்ச் திறக்கப்பட்டது"
},
"i18n": {
"loaded": "ப18ல் ஏற்றப்பட்டது"
},
"second-instance": {
"receive-command": "நெறிமுறை மீது கட்டளை பெறப்பட்டது: \"{{command}}\""
},
"theme": {
"css-file-not-found": "சிஎச்எச் கோப்பு \"{{cssFile}}\" புறக்கணிக்கிறது"
},
"unresponsive": {
"details": "பதிலளிக்காத பிழை!\n {{error}}"
},
"when-ready": {
"clearing-cache-after-20s": "பயன்பாட்டு தற்காலிக சேமிப்பை அழித்தல்"
},
"window": {
"tried-to-render-offscreen": "சாளரம் ஆஃப்ச்கிரீன், சாளரங்கள் = {{windowSize}}, டிச்ப்ளேச்ச் = {{displaySize}}, நிலை = {{position}}"
}
},
"dialog": {
"hide-menu-enabled": {
"detail": "பட்டியல் மறைக்கப்பட்டுள்ளது, அதைக் காட்ட 'ஆல்ட்' ஐப் பயன்படுத்தவும் (அல்லது பயன்பாட்டில் மெனுவைப் பயன்படுத்தினால் 'தப்பிக்க')",
"message": "மறை பட்டியல் இயக்கப்பட்டது",
"title": "பட்டியல் இயக்கப்பட்டது"
},
"need-to-restart": {
"buttons": {
"later": "பின்னர்",
"restart-now": "இப்போது மறுதொடக்கம் செய்யுங்கள்"
},
"detail": "\"{{pluginName}}\" சொருகி நடைமுறைக்கு மறுதொடக்கம் தேவை",
"message": "\"{{pluginName}}\" மறுதொடக்கம் செய்ய வேண்டும்",
"title": "மறுதொடக்கம் தேவை"
},
"unresponsive": {
"buttons": {
"quit": "வெளியேறு",
"relaunch": "மீண்டும் தொடங்கவும்",
"wait": "காத்திருங்கள்"
},
"detail": "சிரமத்திற்கு வருந்துகிறோம்! என்ன செய்ய வேண்டும் என்பதைத் தேர்வுசெய்க:",
"message": "பயன்பாடு பதிலளிக்கவில்லை",
"title": "சாளரம் பதிலளிக்கவில்லை"
},
"update-available": {
"buttons": {
"disable": "புதுப்பிப்புகளை முடக்கு",
"download": "பதிவிறக்கம்",
"ok": "சரி"
},
"detail": "ஒரு புதிய பதிப்பு கிடைக்கிறது மற்றும் {{downloadLink}} இல் பதிவிறக்கம் செய்யலாம்",
"message": "புதிய பதிப்பு கிடைக்கிறது",
"title": "புதுப்பிப்பு கிடைக்கிறது"
}
},
"menu": {
"about": "பற்றி",
"navigation": {
"label": "வானோடல்",
"submenu": {
"copy-current-url": "தற்போதைய முகவரி ஐ நகலெடுக்கவும்",
"go-back": "திரும்பிச் செல்லுங்கள்",
"go-forward": "முன்னோக்கிச் செல்லுங்கள்",
"quit": "வெளியேறு",
"restart": "பயன்பாட்டை மறுதொடக்கம் செய்யுங்கள்"
}
},
"options": {
"label": "விருப்பங்கள்",
"submenu": {
"advanced-options": {
"label": "மேம்பட்ட விருப்பங்கள்",
"submenu": {
"auto-reset-app-cache": "பயன்பாடு தொடங்கும் போது பயன்பாட்டு தற்காலிக சேமிப்பை மீட்டமைக்கவும்",
"disable-hardware-acceleration": "வன்பொருள் முடுக்கம் முடக்கு",
"edit-config-json": "Config.json ஐத் திருத்து",
"override-user-agent": "பயனர்-முகவர் மீறவும்",
"restart-on-config-changes": "கட்டமைப்பு மாற்றங்களை மறுதொடக்கம் செய்யுங்கள்",
"set-proxy": {
"label": "பதிலாள் அமைக்கவும்",
"prompt": {
"label": "பதிலாள் முகவரியை உள்ளிடவும்: (முடக்க காலியாக விடவும்)",
"placeholder": "எடுத்துக்காட்டு: SOCKS5: //127.0.0.1: 9999",
"title": "பதிலாள் அமைக்கவும்"
}
},
"toggle-dev-tools": "டெவ்டூல்சை மாற்றவும்"
}
},
"always-on-top": "எப்போதும் மேலே",
"auto-update": "ஆட்டோ புதுப்பிப்பு",
"hide-menu": {
"dialog": {
"message": "அடுத்த துவக்கத்தில் பட்டியல் மறைக்கப்படும், அதைக் காட்ட [Alt] ஐப் பயன்படுத்தவும் (அல்லது App-menu ஐப் பயன்படுத்தினால் [அல்லது பின்னணி [`])",
"title": "பட்டியல் இயக்கப்பட்டது"
},
"label": "மெனுவை மறைக்கவும்"
},
"language": {
"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": "தனிப்பயன் சிஎச்எச் கோப்பை இறக்குமதி செய்க",
"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": "பயன்பாட்டை மறுதொடக்கம் செய்யுங்கள்",
"show": "சாளரத்தைக் காட்டு",
"tooltip": {
"default": "யூடியூப் இசை",
"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}} s இன் போது"
}
},
"use-fullscreen": {
"label": "முழுத்திரை பயன்படுத்துதல்"
}
},
"name": "சுற்றுப்புற முறை"
},
"amuse": {
"description": "6K ஆய்வகங்களால் இப்போது விட்செட்டில் விளையாடும் Amuse க்கு YouTube இசை ஆதரவை சேர்க்கிறது",
"name": "பொழுதுபோக்கு",
"response": {
"query": "பநிஇ சேவையகம் இயங்குகிறது. பாடல் தகவலைப் பெற /வினவல்."
}
},
"api-server": {
"description": "பிளேயரைக் கட்டுப்படுத்த பநிஇ சேவையகத்தைச் சேர்க்கிறது",
"dialog": {
"request": {
"buttons": {
"allow": "இசைவு",
"deny": "மறுக்கவும்"
},
"message": "பநிஇ ஐ அணுக {{ID}} ({{origin}}) அனுமதிக்கவா?",
"title": "பநிஇ அங்கீகார கோரிக்கை"
}
},
"menu": {
"auth-strategy": {
"label": "அங்கீகார உத்தி",
"submenu": {
"auth-at-first": {
"label": "முதல் கோரிக்கையின் பேரில் ஏற்பு"
},
"none": {
"label": "ஏற்பு இல்லை"
}
}
},
"hostname": {
"label": "புரவலன்பெயர்"
},
"port": {
"label": "துறைமுகம்"
}
},
"name": "பநிஇ சேவையகம் [பீட்டா]",
"prompt": {
"hostname": {
"label": "பநிஇ சேவையகத்திற்கு ஓச்ட்பெயரை (0.0.0.0 போன்றவை) உள்ளிடவும்:",
"title": "புரவலன்பெயர்"
},
"port": {
"label": "பநிஇ சேவையகத்திற்கான துறைமுகத்தை உள்ளிடவும்:",
"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": "எதுவுமில்லை",
"title": "தலைப்பு மொழியைத் தேர்ந்தெடுக்கவும்"
}
},
"templates": {
"title": "திறந்த தலைப்புகள் தேர்வாளர்"
}
},
"compact-sidebar": {
"description": "எப்போதும் பக்கப்பட்டியை சிறிய பயன்முறையில் அமைக்கவும்",
"name": "சிறிய பக்கப்பட்டி"
},
"crossfade": {
"description": "பாடல்களுக்கு இடையில் கிராச்ஃபேட்",
"menu": {
"advanced": "மேம்பட்ட"
},
"name": "கிராச்ஃபேட் [பீட்டா]",
"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": "முரண்பாட்டுடன் இணைக்கப்பட்டுள்ளது",
"disconnected": "முரண்பாட்டிலிருந்து துண்டிக்கப்பட்டது"
},
"description": "பணக்கார இருப்பைக் கொண்டு நீங்கள் கேட்பதை உங்கள் நண்பர்களுக்குக் காட்டுங்கள்",
"menu": {
"auto-reconnect": "ஆட்டோ மீண்டும் இணைக்கவும்",
"clear-activity": "தெளிவான செயல்பாடு",
"clear-activity-after-timeout": "காலக்கெடுவுக்குப் பிறகு தெளிவான செயல்பாடு",
"connected": "இணைக்கப்பட்டுள்ளது",
"disconnected": "துண்டிக்கப்பட்டது",
"hide-duration-left": "காலம் மீதமுள்ளதை மறைக்கவும்",
"hide-github-button": "அறிவிலிமையம் இணைப்பு பொத்தானை மறைக்கவும்",
"play-on-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": "பிளேலிச்ட் ஐடி எதுவும் கிடைக்கவில்லை",
"playlist-is-empty": "பிளேலிச்ட் காலியாக உள்ளது",
"playlist-is-mix-or-private": "பிளேலிச்ட் தகவலைப் பெறுவதில் பிழை: இது ஒரு தனிப்பட்ட அல்லது \"உங்களுக்காக கலப்பு\" பிளேலிச்ட் அல்ல என்பதை உறுதிப்படுத்திக் கொள்ளுங்கள்\n\n {{error}}",
"preparing-file": "கோப்பைத் தயாரித்தல்…",
"saving": "சேமிப்பு…",
"trying-to-get-playlist-id": "பிளேலிச்ட் ஐடியைப் பெற முயற்சிக்கிறது: {{playlistId}}}",
"video-id-not-found": "வீடியோ கிடைக்கவில்லை",
"writing-id3": "ஐடி 3 குறிச்சொற்களை எழுதுதல்…"
}
},
"description": "எம்பி 3 / மூல ஆடியோவை இடைமுகத்திலிருந்து நேரடியாக பதிவிறக்குகிறது",
"menu": {
"choose-download-folder": "பதிவிறக்க கோப்புறையைத் தேர்வுசெய்க",
"download-finish-settings": {
"label": "முடிவில் பதிவிறக்கவும்",
"prompt": {
"last-percent": "ஃச் சதவீதத்திற்குப் பிறகு",
"last-seconds": "கடைசி ஃச் விநாடிகள்",
"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": "டோம் சாளர கட்டுப்பாடுகளை மறைக்கவும்"
},
"name": "பயன்பாட்டில் பட்டியல்"
},
"lumiastream": {
"description": "லூமியா ச்ட்ரீம் ஆதரவை சேர்க்கிறது",
"name": "லூமியா ச்ட்ரீம் [பீட்டா]"
},
"lyrics-genius": {
"description": "பெரும்பாலான பாடல்களுக்கு பாடல் ஆதரவை சேர்க்கிறது",
"menu": {
"romanized-lyrics": "ரோமானிய பாடல்"
},
"name": "பாடல் சீனியச்",
"renderer": {
"fetched-lyrics": "சீனியசுக்கு பாடல் வரிகள்"
}
},
"music-together": {
"description": "ஒரு பிளேலிச்ட்டை மற்றவர்களுடன் பகிர்ந்து கொள்ளுங்கள். புரவலன் ஒரு பாடலை விளையாடும்போது, மற்றவர்கள் அனைவரும் ஒரே பாடலைக் கேட்பார்கள்",
"dialog": {
"enter-host": "புரவலன் ஐடியை உள்ளிடவும்"
},
"internal": {
"save": "சேமி",
"track-source": "ட்ராக் சோர்ச்",
"unknown-user": "தெரியாத பயனர்"
},
"menu": {
"click-to-copy-id": "புரவலன் ஐடியை நகலெடுக்கவும்",
"close": "ஒன்றாக இசையை மூடு",
"connected-users": "இணைக்கப்பட்ட பயனர்கள்",
"disconnect": "ஒன்றாக இசையை துண்டிக்கவும்",
"empty-user": "இணைக்கப்பட்ட பயனர்கள் இல்லை",
"host": "இசை ஒன்றாக புரவலன்",
"join": "ஒன்றாக இசையில் சேரவும்",
"permission": {
"all": "பிளேலிச்ட் மற்றும் பிளேயரைக் கட்டுப்படுத்த விருந்தினர்களை அனுமதிக்கவும்",
"host-only": "ஓச்டால் மட்டுமே பிளேலிச்ட் மற்றும் பிளேயரை கட்டுப்படுத்த முடியும்",
"playlist": "பிளேலிச்ட்டைக் கட்டுப்படுத்த விருந்தினர்களை அனுமதிக்கவும்"
},
"set-permission": "கட்டுப்பாட்டு அனுமதியை மாற்றவும்",
"status": {
"disconnected": "துண்டிக்கப்பட்டது",
"guest": "விருந்தினராக இணைக்கப்பட்டுள்ளது",
"host": "ஓச்டாக இணைக்கப்பட்டுள்ளது"
}
},
"name": "இசை ஒன்றாக [பீட்டா]",
"toast": {
"add-song-failed": "பாடல் சேர்க்கத் தவறிவிட்டது",
"closed": "இசை ஒன்றாக மூடப்பட்டது",
"disconnected": "இசை ஒன்றாக துண்டிக்கப்பட்டது",
"host-failed": "ஒன்றாக இசையை புரவலன் செய்யத் தவறிவிட்டது",
"id-copied": "இடைநிலைப்பலகைக்கு புரவலன் ஐடி நகலெடுக்கப்பட்டது",
"id-copy-failed": "புரவலன் ஐடியை இடைநிலைப்பலகைக்கு நகலெடுப்பதில் தோல்வி",
"join-failed": "ஒன்றாக இசையில் சேரத் தவறிவிட்டது",
"joined": "ஒன்றாக இசையில் சேர்ந்தார்",
"permission-changed": "இசை ஒன்றாக இசைவு \"{{permission}}\" என மாற்றப்பட்டது",
"remove-song-failed": "பாடலை அகற்றுவதில் தோல்வி",
"user-connected": "{{name}} ஒன்றாக இசையில் சேர்ந்தார்",
"user-disconnected": "{{name}}} இடது இசையை ஒன்றாக"
}
},
"navigation": {
"description": "உங்களுக்கு பிடித்த உலாவியைப் போலவே இடைமுகத்தில் நேரடியாக ஒருங்கிணைக்கப்பட்ட அடுத்த/பின் வழிசெலுத்தல் அம்புகள்",
"name": "வானோடல்"
},
"no-google-login": {
"description": "இடைமுகத்திலிருந்து Google உள்நுழைவு பொத்தான்கள் மற்றும் இணைப்புகளை அகற்று",
"name": "கூகிள் உள்நுழைவு இல்லை"
},
"notifications": {
"description": "ஒரு பாடல் இயக்கத் தொடங்கும் போது அறிவிப்பைக் காண்பி (விண்டோசில் ஊடாடும் அறிவிப்புகள் கிடைக்கின்றன)",
"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": {
"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 பநிஇ அமைப்புகள்"
},
"listenbrainz": {
"token": "WeskBrainz பயனர் கிள்ளாக்கை உள்ளிடவும்"
},
"scrobble-other-media": "மற்ற ஊடகங்களை வெல்லுங்கள்"
},
"name": "ச்க்ரோபிளர்",
"prompt": {
"lastfm": {
"api-key": "Last.fm பநிஇ key",
"api-secret": "Last.fm பநிஇ மறைபொருள்"
},
"listenbrainz": {
"token": {
"label": "உங்கள் கயிட் பிரைன்ச் பயனர் கிள்ளாக்கை உள்ளிடவும்:",
"title": "கேளுங்கள் பிரெய்ன்ச் கிள்ளாக்கு"
}
}
}
},
"shortcuts": {
"description": "பிளேபேக்கிற்கான உலகளாவிய ஆட்கீசை அமைக்க அனுமதிக்கிறது (நாடகம்/இடைநிறுத்தம்/அடுத்த/முந்தைய) மீடியா விசைகளை மீறுவதன் மூலம் மீடியா ஓஎச்டி",
"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": "ம n னங்களைத் தவிர்க்கவும்"
},
"sponsorblock": {
"description": "அறிமுகம்/அவுட்ரோ போன்ற இசை அல்லாத பகுதிகள் அல்லது பாடல் இசைக்காத இசை வீடியோக்களின் பகுதிகளை தானாகவே தவிர்க்கிறது",
"name": "ஒப்புரவாளர் தொகுதி"
},
"synced-lyrics": {
"description": "எல்.ஆர்.சி.எல்.ஐ.பி போன்ற வழங்குநர்களைப் பயன்படுத்தி பாடல்களுக்கு ஒத்திசைக்கப்பட்ட பாடல்களை வழங்குகிறது.",
"errors": {
"fetch": "The பாடல் வரிகளைப் பெறும்போது பிழை ஏற்பட்டது.\n தயவுசெய்து பின்னர் மீண்டும் முயற்சிக்கவும்.",
"not-found": "Ling இந்த பாடலுக்கு வரிகள் எதுவும் கிடைக்கவில்லை."
},
"menu": {
"default-text-string": {
"label": "பாடல் வரிகளுக்கு இடையில் இயல்புநிலை எழுத்து",
"tooltip": "பாடல் வரிகளுக்கு இடையிலான இடைவெளியைப் பயன்படுத்த இயல்புநிலை எழுத்தைத் தேர்வுசெய்க"
},
"line-effect": {
"label": "வரி விளைவு",
"submenu": {
"fancy": {
"label": "ஆடம்பரமான",
"tooltip": "தற்போதைய வரியில் பெரிய, பயன்பாடு போன்ற விளைவுகளைப் பயன்படுத்தவும்"
},
"focus": {
"label": "குவி",
"tooltip": "தற்போதைய வரியை மட்டுமே வெள்ளை செய்யுங்கள்"
},
"offset": {
"label": "ஈடுசெய்யும்",
"tooltip": "வலதுபுறத்தில் தற்போதைய வரியை ஈடுசெய்யவும்"
},
"scale": {
"label": "அளவு",
"tooltip": "தற்போதைய வரியை அளவிடவும்"
}
},
"tooltip": "தற்போதைய வரிக்கு பொருந்தக்கூடிய விளைவைத் தேர்வுசெய்க"
},
"precise-timing": {
"label": "பாடல் வரிகளை சரியாக ஒத்திசைக்கவும்",
"tooltip": "அடுத்த வரியின் காட்சியைக் கணக்கிடுங்கள் (செயல்திறனில் ஒரு சிறிய தாக்கத்தை ஏற்படுத்தும்)"
},
"show-lyrics-even-if-inexact": {
"label": "துல்லியமாக இருந்தாலும் பாடல்களைக் காட்டு",
"tooltip": "பாடல் காணப்படாவிட்டால், சொருகி மீண்டும் வேறு தேடல் வினவலுடன் முயற்சிக்கிறது.\n இரண்டாவது முயற்சியின் முடிவு துல்லியமாக இருக்காது."
},
"show-time-codes": {
"label": "நேரக் குறியீடுகளைக் காட்டு",
"tooltip": "பாடல் வரிகளுக்கு அடுத்த நேர குறியீடுகளைக் காட்டு"
}
},
"name": "ஒத்திசைக்கப்பட்ட பாடல்",
"refetch-btn": {
"fetching": "பெறுதல் ...",
"normal": "ரீஃபட்ச் பாடல்"
},
"warnings": {
"duration-mismatch": "⚠.எம் - கால பொருத்தமின்மை காரணமாக பாடல் வரிகள் ஒத்திசைக்கப்படாமல் இருக்கலாம்.",
"inexact": "- - இந்த பாடலுக்கான வரிகள் துல்லியமாக இருக்காது",
"instrumental": "- இது ஒரு கருவி பாடல்"
}
},
"taskbar-mediacontrol": {
"description": "உங்கள் சாளரங்கள் பணிப்பட்டியிலிருந்து பிளேபேக்கைக் கட்டுப்படுத்தவும்",
"name": "பணிப்பட்டு மீடியா கட்டுப்பாடு"
},
"touchbar": {
"description": "MACOS பயனர்களுக்கான டச்ச்பார் விட்செட்டை சேர்க்கிறது",
"name": "டக்பார்"
},
"tuna-obs": {
"description": "OBS இன் சொருகி டுனாவுடன் ஒருங்கிணைப்பு",
"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": "காட்சிப்படுத்தல்"
}
}
}

View File

@ -39,29 +39,29 @@
"clearing-cache-after-20s": "กำลังล้างแคชแอป"
},
"window": {
"tried-to-render-offscreen": "หน้าต่างพยายามแสดงผลเกินขนาดหน้าจอ windowSize={{windowSize}}, displaySize={{displaySize}}, position={{position}}"
"tried-to-render-offscreen": "หน้าต่างพยายามแสดงผลเกินขนาดหน้าจอ ขนาดหน้าต่าง={{windowSize}}, ขนาดหน้าจอ={{displaySize}}, ตำแหน่ง={{position}}"
}
},
"dialog": {
"hide-menu-enabled": {
"detail": "เมนูถูกซ่อนไว้ กด'Alt' เพื่อแสดงเมนู (หรือ 'Escape' หากอยู่ในเมนูแอป)",
"message": "การซ่อนเมนูถูกเปิดใช้งาน",
"detail": "เมนูถูกซ่อนไว้ กด'Alt' เพื่อแสดงเมนู (หรือ 'Escape' หากกำลังใช้เมนูบนแอป)",
"message": "การซ่อนเมนูเปิดใช้งานอยู่",
"title": "เปิดใช้งานการซ่อนเมนู"
},
"need-to-restart": {
"buttons": {
"later": "ภายหลัง",
"restart-now": "รีสตาร์ตอนนี้"
"later": "รีสตาร์ตภายหลัง",
"restart-now": "รีสตาร์ตอนนี้"
},
"detail": "\"{{pluginName}}\" ปลั๊กอินต้องการการรีสตาร์เพื่อแสดงผล",
"message": "\"{{pluginName}}\" ต้องการรีสตาร์",
"title": "แนะนำให้รีสตาร์"
"detail": "ปลั๊กอิน \"{{pluginName}}\" ต้องการการรีสตาร์เพื่อจะทำงานได้",
"message": "\"{{pluginName}}\" ต้องการรีสตาร์",
"title": "แนะนำให้รีสตาร์"
},
"unresponsive": {
"buttons": {
"quit": "ออก",
"relaunch": "เปิดใหม่",
"wait": "รอซักครู่"
"relaunch": "ปิดแล้วเปิดใหม่",
"wait": "รอให้ตอบสนอง"
},
"detail": "ขออภัยในความไม่สะดวก! โปรดเลือกสิ่งที่ต้องการจะทำ:",
"message": "แอปพลิเคชันไม่ตอบสนอง",
@ -73,7 +73,7 @@
"download": "ดาวน์โหลด",
"ok": "ตกลง"
},
"detail": "มีเวอร์ชันใหม่ให้ดาวน์โหลดแล้วที่ {{downloadLink}}",
"detail": "มีเวอร์ชันใหม่ให้ใช้งานได้สามารถดาวน์โหลดได้ที่ {{downloadLink}}",
"message": "มีเวอร์ชันใหม่ให้ใช้งานแล้ว",
"title": "อัปเดตพร้อมใช้งาน"
}
@ -87,7 +87,7 @@
"go-back": "ก่อนหน้า",
"go-forward": "ถัดไป",
"quit": "ออก",
"restart": "รีสตาร์แอป"
"restart": "รีสตาร์แอป"
}
},
"options": {
@ -96,23 +96,23 @@
"advanced-options": {
"label": "ตัวเลือกขั้นสูง",
"submenu": {
"auto-reset-app-cache": "รีเซตแอปแคชเมื่อเริ่มแอป",
"auto-reset-app-cache": "รีเซตแคชของแอปเมื่อเริ่มแอป",
"disable-hardware-acceleration": "ปิดการใช้งานตัวเร่งประสิทธิภาพด้วยฮาร์ดแวร์",
"edit-config-json": "แก้ไข config.json",
"override-user-agent": "แทนที่ User-Agent",
"restart-on-config-changes": "รีสตาร์เมื่อมีการเปลี่ยนแปลงคอนฟิก",
"restart-on-config-changes": "รีสตาร์เมื่อมีการเปลี่ยนแปลงคอนฟิก",
"set-proxy": {
"label": "ตั้งค่าพร็อกซี่",
"prompt": {
"label": "ใส่ที่อยู่พร็อกซี่: (ปล่อยให้ว่างเพื่อปิดใช้งาน)",
"label": "ใส่ที่อยู่ของพร็อกซี่: (เว้นว่างเพื่อปิดใช้งาน)",
"placeholder": "ตัวอย่าง: SOCKS5://127.0.0.1:9999",
"title": "ตั้งค่าพร็อกซี่"
}
},
"toggle-dev-tools": "เปิด-ปิด DevTools"
"toggle-dev-tools": "เปิด/ปิดเครื่องมือสำหรับนักพัฒนา"
}
},
"always-on-top": "อยู่ด้านบนตลอดเวลา",
"always-on-top": "อยู่ด้านบนหน้าต่างอื่นตลอดเวลา",
"auto-update": "อัปเดตอัตโนมัติ",
"hide-menu": {
"dialog": {
@ -123,17 +123,17 @@
},
"language": {
"dialog": {
"message": "ภาษาจะเปลี่ยนหลังจากทำการรีสตาร์",
"message": "ภาษาจะเปลี่ยนหลังจากทำการรีสตาร์",
"title": "ภาษาถูกเปลี่ยนแล้ว"
},
"label": "ภาษา",
"submenu": {
"to-help-translate": "ต้องการช่วยแปล? คลิกที่นี่"
"to-help-translate": "ต้องการช่วยแปลภาษา? คลิกที่นี่"
}
},
"resume-on-start": "เล่นเพลงล่าสุดต่อ เมื่อแอปเริ่มต้น",
"single-instance-lock": "ล็อกการทำงานเพียงรายการเดียว",
"start-at-login": "เริ่มต้นที่การเข้าสู่ระบบ",
"resume-on-start": "เล่นเพลงที่เล่นล่าสุดต่อ เมื่อเปิดแอป",
"single-instance-lock": "ล็อกไม่ให้เปิดซำ้ซ้อน",
"start-at-login": "เริ่มต้นเมื่อเข้าสู่ระบบ",
"starting-page": {
"label": "หน้าเริ่มต้น",
"unset": "ยกเลิกการตั้งค่า"
@ -148,7 +148,7 @@
}
},
"visual-tweaks": {
"label": "การปรับแต่งหน้าตาแอป",
"label": "ปรับแต่งหน้าตาแอป",
"submenu": {
"like-buttons": {
"default": "ค่าเริ่มต้น",
@ -169,7 +169,7 @@
"label": "ธีม",
"submenu": {
"import-css-file": "นำเข้าไฟล์ CSS ที่กำหนดเอง",
"no-theme": "ไม่มีธีม"
"no-theme": "ไม่ใช้ธีม"
}
}
}
@ -187,28 +187,28 @@
"force-reload": "บังคับโหลดใหม่",
"reload": "โหลดใหม่",
"reset-zoom": "ขนาดจริง",
"toggle-fullscreen": "สลับเต็มหน้าจอ",
"toggle-fullscreen": "เปิด/ปิดโหมดเต็มหน้าจอ",
"zoom-in": "ซูมเข้า",
"zoom-out": "ซูมออก"
}
}
},
"tray": {
"next": "่อไป",
"play-pause": "เล่น/พัก",
"next": "่อไป",
"play-pause": "เล่น/หยุดชั่วคราว",
"previous": "ก่อนหน้า",
"quit": "ออก",
"restart": "รีสตาร์แอป",
"restart": "รีสตาร์แอป",
"show": "แสดงหน้าต่าง",
"tooltip": {
"default": "ยูทุปมิวสิค",
"with-song-info": "ยูทูปมิวสิค: {{artist}} - {{title}}"
"default": "YouTube Music",
"with-song-info": "YouTube Music: {{artist}} - {{title}}"
}
}
},
"plugins": {
"ad-speedup": {
"description": "หากมีการเล่นโฆษณา เสียงจะถูกปิดและตั้งค่าความเร็วในการเล่นเป็น 16x",
"description": "หากมีโฆษณาเล่นขึ้น เสียงจะถูกปิดและตั้งความเร็วในการเล่นเป็น 16 เท่า",
"name": "เพิ่มความเร็วโฆษณา"
},
"adblocker": {
@ -219,7 +219,7 @@
"name": "ตัวบล็อกโฆษณา"
},
"album-actions": {
"description": "เพิ่ม ยกเลิกไม่ชอบ, ไม่ชอบ, 'ถูกใจ', และ 'ยกเลิกถูกใจ' เพื่อให้สามารถใช้งานกับเพลงทั้งหมดในรายการเพลงหรืออัลบั้ม",
"description": "เพิ่ม ยกเลิกไม่ชอบ, ไม่ชอบ, ถูกใจ, และ ยกเลิกถูกใจ ที่มีผลกับเพลงทั้งหมดในรายการเพลงหรืออัลบั้ม",
"name": "การกระทำที่เกี่ยวกับอัลบั้ม"
},
"album-color-theme": {
@ -228,14 +228,14 @@
"color-mix-ratio": {
"label": "สัดส่วนการผสมสี",
"submenu": {
"percent": "{{ratio}}เปอร์เซ็น"
"percent": "{{ratio}}%"
}
}
},
"name": "ธีมสีของอัลบั้ม"
},
"ambient-mode": {
"description": "เอฟเฟกต์แสงจะใช้สีอ่อนๆจากวีดิโอมาเป็พื้นหลังสกรีน",
"description": "เอฟเฟกต์แสงที่ดึงสีอ่อนๆจากวีดิโอมาบนพื้นหลังแอป",
"menu": {
"blur-amount": {
"label": "ระดับความเบลอ",
@ -268,9 +268,9 @@
}
},
"smoothness-transition": {
"label": "การเปลี่ยนแบบสมูท",
"label": "ความเรียบการเปลี่ยน",
"submenu": {
"during": "ระหว่าง {{interpolationTime}} วินาที"
"during": "ในระยะเวลา {{interpolationTime}} วินาที"
}
},
"use-fullscreen": {
@ -279,6 +279,13 @@
},
"name": "โหมดสภาพแวดล้อม"
},
"amuse": {
"description": "เพิ่มการรองรับ Youtube Music สำหรับ Widget Amuse Now Playing ของ 6K Labs",
"name": "Amuse",
"response": {
"query": "API Server ของ Amuse ทำงานอยู่ ส่ง GET ไปที่ /query เพื่อขอข้อมูลเกี่ยวกับเพลง"
}
},
"api-server": {
"description": "เพิ่มเซิร์ฟเวอร์ API เพื่อควบคุมการเล่น",
"dialog": {
@ -287,7 +294,38 @@
"allow": "อนุญาต",
"deny": "ปฏิเสธ"
},
"message": "อนุญาตให้ {{ID}} ({{origin}}) เข้าถึง API หรือไม่?"
"message": "อนุญาตให้ {{ID}} ({{origin}}) เข้าถึง API หรือไม่?",
"title": "คำขอลงชื่อเข้าใช้งาน API"
}
},
"menu": {
"auth-strategy": {
"label": "วิธีการลงชื่อเข้าใช้งาน",
"submenu": {
"auth-at-first": {
"label": "ลงชื่อเข้าใช้งานในคำขอแรก"
},
"none": {
"label": "ไม่ต้องลงชื่อเข้าใช้งาน"
}
}
},
"hostname": {
"label": "ชื่อโฮสต์ (Hostname)"
},
"port": {
"label": "พอร์ต (Port) ของ Server"
}
},
"name": "เซิร์ฟเวอร์ API [เบต้า]",
"prompt": {
"hostname": {
"label": "ใส่ชื่อโฮสต์ (Hostname) เช่น 0.0.0.0 สำหรับเซิร์ฟเวอร์ API:",
"title": "ชื่อโฮสต์ (Hostname)"
},
"port": {
"label": "ใส่พอร์ต (Port) ในการเข้าถึงเซิร์ฟเวอร์ API:",
"title": "พอร์ต (Port) ของ Server"
}
}
},
@ -300,11 +338,11 @@
"name": "เบลอแถบนำทาง"
},
"bypass-age-restrictions": {
"description": "ข้ามการตรวจสอบอายุของยูทูป",
"description": "ข้ามการตรวจสอบอายุของ YouTube",
"name": "ข้ามข้อจำกัดอายุ"
},
"captions-selector": {
"description": "ตัวเลือกคำบรรยายสำหรับเพลงในYoutube Music",
"description": "ตัวเลือกคำบรรยายสำหรับเพลงใน YouTube Music",
"menu": {
"autoload": "เลือกคำบรรยายที่ใช้ครั้งล่าสุดโดยอัตโนมัติ",
"disable-captions": "ไม่มีคำบรรยายเป็นค่าเริ่มต้น"
@ -348,19 +386,19 @@
}
},
"disable-autoplay": {
"description": "เริ่มเพลงในโหมดหยุด",
"description": "เริ่มเพลงในโหมดหยุดชั่วคราว",
"menu": {
"apply-once": "ใช้เฉพาะเมื่อเริ่มต้น"
"apply-once": "ใช้เฉพาะเมื่อเริ่มต้นแอป"
},
"name": "ปิดใช้งานการเล่นอัตโนมัติ"
},
"discord": {
"backend": {
"already-connected": "พยายามเชื่อมต่อกับการเชื่อมต่อที่ทำงานอยู่",
"already-connected": "กำลังพยายามเชื่อมต่อด้วยการเชื่อมต่อที่มีอยู่",
"connected": "เชื่อมต่อกับดิสคอร์ดแล้ว",
"disconnected": "ตัดการเชื่อมต่อออกจากดิสคอร์ด"
"disconnected": "ไม่มีการเชื่อมต่อกับดิสคอร์ดอยู่"
},
"description": "แสดงให้เพื่อนเห็นว่าคุณกำลังฟังอะไรด้วย Rich Presence",
"description": "แสดงให้เพื่อนเห็นว่าคุณกำลังฟังอะไรด้วย Rich Presence บนดิสคอร์ด",
"menu": {
"auto-reconnect": "เชื่อมต่อใหม่โดยอัตโนมัติ",
"clear-activity": "ล้างกิจกรรม",
@ -369,10 +407,10 @@
"disconnected": "ตัดการเชื่อมต่อ",
"hide-duration-left": "ซ่อนระยะเวลาที่เหลือ",
"hide-github-button": "ซ่อนปุ่มลิงก์ GitHub",
"play-on-youtube-music": "เล่นบนยูทูปมิวสุค",
"play-on-youtube-music": "เล่นบน YouTube Music",
"set-inactivity-timeout": "ตั้งระยะเวลาไม่มีกิจกรรม"
},
"name": "Discord Rich Presence",
"name": "แสดงกิจกรรมบนดิสคอร์ด",
"prompt": {
"set-inactivity-timeout": {
"label": "ป้อนระยะเวลาไม่มีกิจกรรมเป็นวินาที:",
@ -426,6 +464,21 @@
"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": "ข้ามไฟล์ที่มีอยู่แล้ว"
@ -438,12 +491,24 @@
"button": "ดาวน์โหลด"
}
},
"equalizer": {
"description": "เพิ่มอีควอไลเซอร์ให้ที่เล่นเพลง",
"menu": {
"presets": {
"label": "การตั้งค่าล่วงหน้า",
"list": {
"bass-booster": "เพิ่มเบส"
}
}
},
"name": "อีควอไลเซอร์"
},
"exponential-volume": {
"description": "ทำให้ตัวเลือกความดังมีลักษณะเอ็กซ์โปเนนเชียล เพื่อให้ง่ายต่อการเลือกระดับความดังที่ต่ำลง",
"name": "ระดับเสียงแบบเอ็กซโปเนนเชียล"
},
"in-app-menu": {
"description": "ให้เมนูบาร์ดูทันสมัย มืดหรือเป็นสีของอัลบั้มอย่างน่าสนใจ",
"description": "ให้เแถบเมนูดูทันสมัย มืดหรือเป็นสีของอัลบั้มอย่างน่าสนใจ",
"menu": {
"hide-dom-window-controls": "ซ่อนตัวควบคุมหน้าต่าง DOM"
},
@ -530,6 +595,7 @@
}
},
"priority": "ลำดับความสำคัญของการแจ้งเตือน",
"toast-style": "แบบไม่ถาวร",
"unpause-notification": "แสดงการแจ้งเตือนเมื่อหยุดพัก"
},
"name": "การแจ้งเตือน"
@ -549,8 +615,231 @@
}
},
"save-window-position": "บันทึกตำแหน่งหน้าต่าง",
"save-window-size": "บันทึกขนาดหน้าต่าง"
"save-window-size": "บันทึกขนาดหน้าต่าง",
"use-native-pip": "ใช้โหมดภาพซ้อนภาพของเบราเซอร์"
},
"name": "โหมดภาพซ้อนภาพ",
"templates": {
"button": "เปิดโหมดภาพซ้อนภาพ"
}
},
"playback-speed": {
"description": "ฟังเพลงได้ทั้งช้าและเร็ว เพิ่มที่เลื่อนปรับความเร็วของเพลง",
"name": "ความเร็วเพลง",
"templates": {
"button": "ความเร็ว"
}
},
"precise-volume": {
"description": "ควบคุมระดับเสียงได้อย่างแม่นยำด้วยที่เลื่อนเมาส์หรือปุ่มลัด พร้อมด้วยหน้าจอแสดงข้อมูลและขั้นระดับเสียงที่ปรับแต่งได้",
"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": "การตั้งค่า API Last.fm"
},
"listenbrainz": {
"token": "ใส่ user token ของ ListenBrainz"
},
"scrobble-alternative-title": "ใช้ชื่ออื่น",
"scrobble-other-media": "บันทึกการเล่นสื่ออื่นๆ"
},
"name": "บันทึกการเล่น (Scrobbler)",
"prompt": {
"lastfm": {
"api-key": "API Key ของ Last.fm",
"api-secret": "API secret ของ Last.fm"
},
"listenbrainz": {
"token": {
"label": "ใส่ user token ListenBrainz ของคุณ:",
"title": "token ListenBrainz"
}
}
}
},
"shortcuts": {
"description": "อนุญาตให้ตั้งปุ่มลัดทั่วระบบสำหรับการเล่น (เล่น/หยุดชั่วคราว/ถัดไป/ก่อนหน้า) และปิดการแสดงผลสื่อโดยทับปุ่มควบคุมสื่อ เปิด Ctrl/CMD + F เพื่อค้นหา เปิดการใช้งานการควบคุมเพลงผ่าน MPRIS สำหรับ Linux และปุ่มลัดที่กำหนดเองได้สำหรับผู้ใช้ขั้นสูง",
"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": "ข้ามช่วงที่ไม่ใช่เพลงเช่น intro/outro หรือ ช่วงที่ไม่มีเพลงเล่นใน mv โดยอัตโนมัติ",
"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": "ควบคุมการเล่นผ่าน taskbar ของ Windows",
"name": "ควบคุมสื่อผ่าน Taskbar"
},
"touchbar": {
"description": "เพิ่ม Widget บน 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": "วิชวลไลเซอร์"
}
}
}

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

@ -683,6 +683,7 @@
"listenbrainz": {
"token": "输入 ListenBrainz 用户令牌"
},
"scrobble-alternative-title": "使用替代标题",
"scrobble-other-media": "记录其他媒体文件"
},
"name": "歌曲记录器",
@ -733,8 +734,8 @@
"synced-lyrics": {
"description": "透过 LRClib 等服务提供滚动歌词显示。",
"errors": {
"fetch": "⚠️ - 获取歌词时发生错误。请稍后再试。",
"not-found": "⚠️ - 未找到此歌曲的歌词。"
"fetch": "⚠️ 获取歌词时发生错误。\n 请稍后再试。",
"not-found": "⚠️ 未找到此歌曲的歌词。"
},
"menu": {
"default-text-string": {
@ -744,6 +745,10 @@
"line-effect": {
"label": "歌词行特效",
"submenu": {
"fancy": {
"label": "Fancy",
"tooltip": "在当前行上使用大的、类似应用的效果"
},
"focus": {
"label": "高亮",
"tooltip": "仅将当前歌词行显示为白色"
@ -763,6 +768,10 @@
"label": "让滚动歌词完全同步",
"tooltip": "以毫秒精度估算下句歌词的显示时间(可能对性能有小幅影响)"
},
"romanization": {
"label": "将歌词罗马化",
"tooltip": "如果歌词以不同语言显示,试着展示拉丁字母版本。"
},
"show-lyrics-even-if-inexact": {
"label": "即使时值不精确依然显示歌词",
"tooltip": "若首次搜索未找到该歌曲的歌词,插件将尝试用不同的查询方式重新获取。\n重试查询的结果可能不精确。"
@ -795,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,7 +683,8 @@
"listenbrainz": {
"token": "輸入 ListenBrainz 使用者憑證"
},
"scrobble-other-media": "紀錄其他媒體文件"
"scrobble-alternative-title": "使用替代歌曲標題",
"scrobble-other-media": "紀錄其他媒體檔案"
},
"name": "Scrobbler",
"prompt": {
@ -700,7 +701,7 @@
}
},
"shortcuts": {
"description": "使用全域快捷鍵控制音樂 (播放/暫停/下一首/上一首) + 透過覆寫媒體快捷鍵停用媒體OSD + 允許Ctrl/CMD + F來搜尋 + 支援Linux MPRIS媒體快捷鍵 + 更多自訂快捷鍵給進階使用者",
"description": "使用全域快捷鍵控制音樂 (播放/暫停/下一首/上一首) + 透過覆寫媒體快捷鍵停用媒體 OSD + 允許 Ctrl/CMD + F 來搜尋 + 支援 Linux MPRIS 媒體快捷鍵 + 更多自訂快捷鍵給進階使用者",
"menu": {
"override-media-keys": "覆寫媒體快捷鍵",
"set-keybinds": "設定全域歌曲控制"
@ -728,7 +729,7 @@
},
"sponsorblock": {
"description": "自動跳過贊助片段",
"name": "贊助阻擋"
"name": "SponsorBlock"
},
"synced-lyrics": {
"description": "使用 LRClib 等管道提供歌詞同步顯示。",
@ -746,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": "即使不精確依然強制顯示歌詞",
@ -792,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": {
@ -812,7 +821,7 @@
},
"force-hide": "強制移除整個影片頁面",
"mode": {
"label": "式",
"label": "式",
"submenu": {
"custom": "自訂樣式",
"disabled": "停用",
@ -826,11 +835,11 @@
}
},
"visualizer": {
"description": "新增一個視化音樂效果",
"description": "新增一個視化音樂效果",
"menu": {
"visualizer-type": "樣式"
"visualizer-type": "視覺化效果類型"
},
"name": "可視化音樂效果"
"name": "視覺化效果"
}
}
}

View File

@ -825,7 +825,7 @@ app.whenReady().then(async () => {
// Optimized for Mac OS X
if (is.macOS() && !config.get('options.appVisible')) {
app.dock.hide();
app.dock?.hide();
}
let forceQuit = false;

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

@ -38,6 +38,12 @@ const info: Info = {
*/
const refreshCallbacks: (() => void)[] = [];
const truncateString = (str: string, length: number): string => {
if (str.length > length)
return `${str.substring(0, length - 3)}...`;
return str;
}
const resetInfo = () => {
info.ready = false;
clearTimeout(clearActivity);
@ -184,8 +190,8 @@ export const backend = createBackend<
const activityInfo: SetActivity = {
type: ActivityType.Listening,
details: songInfo.title,
state: songInfo.artist,
details: truncateString(songInfo.title, 128),
state: truncateString(songInfo.artist, 128),
largeImageKey: songInfo.imageSrc ?? '',
largeImageText: songInfo.album ?? '',
buttons,

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;
};
@ -639,7 +647,12 @@ export async function downloadPlaylist(givenUrl?: string | URL) {
try {
playlist = await yt.music.getPlaylist(playlistId);
if (playlist?.items) {
items.push(...playlist.items.as(YTNodes.MusicResponsiveListItem));
const filteredItems = playlist.items.filter(
(item): item is YTNodes.MusicResponsiveListItem =>
item instanceof YTNodes.MusicResponsiveListItem,
);
items.push(...filteredItems);
}
} catch (error: unknown) {
sendError(
@ -674,9 +687,13 @@ export async function downloadPlaylist(givenUrl?: string | URL) {
while (playlist.has_continuation) {
playlist = await playlist.getContinuation();
if (playlist?.items) {
items.push(...playlist.items.as(YTNodes.MusicResponsiveListItem));
}
const filteredItems = playlist.items.filter(
(item): item is YTNodes.MusicResponsiveListItem =>
item instanceof YTNodes.MusicResponsiveListItem,
);
items.push(...filteredItems);
}
if (items.length === 1) {

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

@ -22,27 +22,5 @@ export default createPlugin({
node.remove();
}
}
// Remove the library button
const libraryIconPath =
'M16,6v2h-2v5c0,1.1-0.9,2-2,2s-2-0.9-2-2s0.9-2,2-2c0.37,0,0.7,0.11,1,0.28V6H16z M18,20H4V6H3v15h15V20z M21,3H6v15h15V3z M7,4h13v13H7V4z';
const observer = new MutationObserver(() => {
const menuEntries = document.querySelectorAll(
'#items ytmusic-guide-entry-renderer',
);
menuEntries.forEach((item) => {
const icon = item.querySelector('path');
if (icon) {
observer.disconnect();
if (icon.getAttribute('d') === libraryIconPath) {
item.remove();
}
}
});
});
observer.observe(document.documentElement, {
childList: true,
subtree: true,
});
},
});
});

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

@ -12,6 +12,12 @@ export interface ScrobblerPluginConfig {
* @default true
*/
scrobbleOtherMedia: boolean;
/**
* Use alternative titles for scrobbling (Useful for non-roman song titles)
*
* @default false
*/
alternativeTitles: boolean;
scrobblers: {
lastfm: {
/**
@ -71,6 +77,7 @@ export interface ScrobblerPluginConfig {
export const defaultConfig: ScrobblerPluginConfig = {
enabled: false,
scrobbleOtherMedia: true,
alternativeTitles: false,
scrobblers: {
lastfm: {
enabled: false,

View File

@ -96,6 +96,15 @@ export const onMenu = async ({
setConfig(config);
},
},
{
label: t('plugins.scrobbler.menu.scrobble-alternative-title'),
type: 'checkbox',
checked: Boolean(config.alternativeTitles),
click(item) {
config.alternativeTitles = item.checked;
setConfig(config);
},
},
{
label: 'Last.fm',
submenu: [

View File

@ -127,8 +127,13 @@ export class LastFmScrobbler extends ScrobblerBase {
await this.createSession(config, setConfig);
}
const title =
config.alternativeTitles && songInfo.alternativeTitle !== undefined
? songInfo.alternativeTitle
: songInfo.title;
const postData: LastFmSongData = {
track: songInfo.title,
track: title,
duration: songInfo.songDuration,
artist: songInfo.artist,
...(songInfo.album ? { album: songInfo.album } : undefined), // Will be undefined if current song is a video

View File

@ -48,7 +48,7 @@ export class ListenbrainzScrobbler extends ScrobblerBase {
return;
}
const body = createRequestBody('playing_now', songInfo);
const body = createRequestBody('playing_now', songInfo, config);
submitListen(body, config);
}
@ -64,7 +64,7 @@ export class ListenbrainzScrobbler extends ScrobblerBase {
return;
}
const body = createRequestBody('single', songInfo);
const body = createRequestBody('single', songInfo, config);
body.payload[0].listened_at = Math.trunc(Date.now() / 1000);
submitListen(body, config);
@ -74,10 +74,16 @@ export class ListenbrainzScrobbler extends ScrobblerBase {
function createRequestBody(
listenType: string,
songInfo: SongInfo,
config: ScrobblerPluginConfig,
): ListenbrainzRequestBody {
const title =
config.alternativeTitles && songInfo.alternativeTitle !== undefined
? songInfo.alternativeTitle
: songInfo.title;
const trackMetadata = {
artist_name: songInfo.artist,
track_name: songInfo.title,
track_name: title,
release_name: songInfo.album ?? undefined,
additional_info: {
media_player: 'YouTube Music Desktop App',

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

@ -20,7 +20,8 @@ export default createPlugin({
showTimeCodes: false,
defaultTextString: '♪',
lineEffect: 'fancy',
} satisfies SyncedLyricsPluginConfig,
romanization: true,
} satisfies SyncedLyricsPluginConfig as SyncedLyricsPluginConfig,
menu,
renderer,

View File

@ -1,7 +1,6 @@
import { MenuItemConstructorOptions } from 'electron';
import { t } from '@/i18n';
import type { MenuItemConstructorOptions } from 'electron';
import type { MenuContext } from '@/types/contexts';
import type { SyncedLyricsPluginConfig } from './types';
@ -136,6 +135,17 @@ export const menu = async (
},
],
},
{
label: t('plugins.synced-lyrics.menu.romanization.label'),
toolTip: t('plugins.synced-lyrics.menu.romanization.tooltip'),
type: 'checkbox',
checked: config.romanization,
click(item) {
ctx.setConfig({
romanization: item.checked,
});
},
},
{
label: t('plugins.synced-lyrics.menu.show-time-codes.label'),
toolTip: t('plugins.synced-lyrics.menu.show-time-codes.tooltip'),

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

@ -30,15 +30,16 @@ export class LyricsGenius implements LyricProvider {
title: titleA,
primary_artist: { name: artistA },
},
},
{
}, {
result: {
title: titleB,
primary_artist: { name: artistB },
},
}) => {
const pointsA = (titleA === title ? 1 : 0) + (artistA.includes(artist) ? 1 : 0);
const pointsB = (titleB === title ? 1 : 0) + (artistB.includes(artist) ? 1 : 0);
const pointsA = (titleA === title ? 1 : 0) +
(artistA.includes(artist) ? 1 : 0);
const pointsB = (titleB === title ? 1 : 0) +
(artistB.includes(artist) ? 1 : 0);
return pointsB - pointsA;
},
@ -51,14 +52,21 @@ export class LyricsGenius implements LyricProvider {
const { result: { path } } = closestHit;
const html = await fetch(`${this.baseUrl}${path}`).then((res) => res.text());
const html = await fetch(`${this.baseUrl}${path}`).then((res) =>
res.text()
);
const doc = this.domParser.parseFromString(html, 'text/html');
const preloadedStateScript = Array.prototype.find.call(doc.querySelectorAll('script'), (script: HTMLScriptElement) => {
return script.textContent?.includes('window.__PRELOADED_STATE__');
}) as HTMLScriptElement;
const preloadedStateScript = Array.prototype.find.call(
doc.querySelectorAll('script'),
(script: HTMLScriptElement) => {
return script.textContent?.includes('window.__PRELOADED_STATE__');
},
) as HTMLScriptElement;
const preloadedState = preloadedStateScript.textContent?.match(preloadedStateRegex)?.[1]?.replace(/\\"/g, '"');
const preloadedState = preloadedStateScript.textContent?.match(
preloadedStateRegex,
)?.[1]?.replace(/\\"/g, '"');
const lyricsHtml = preloadedState?.match(preloadHtmlRegex)?.[1]
?.replace(/\\\//g, '/')
@ -67,12 +75,19 @@ export class LyricsGenius implements LyricProvider {
?.replace(/\\'/g, "'")
?.replace(/\\"/g, '"');
if (!lyricsHtml) throw new Error('Failed to extract lyrics from preloaded state.');
const hasUnreleasedPlaceholder = preloadedState &&
/lyricsPlaceholderReason.{1,5}unreleased/.test(preloadedState);
if (!lyricsHtml) {
if (hasUnreleasedPlaceholder) return null;
throw new Error('Failed to extract lyrics from preloaded state.');
}
const lyricsDoc = this.domParser.parseFromString(lyricsHtml, 'text/html');
const lyrics = lyricsDoc.body.innerText;
if (lyrics.trim().toLowerCase().replace(/[[\]]/g, '') === 'instrumental') return null;
if (lyrics.trim().toLowerCase().replace(/[[\]]/g, '') === 'instrumental') {
return null;
}
return {
title: closestHit.result.title,

View File

@ -1,4 +1,4 @@
import { createSignal, For, Match, Show, Switch } from 'solid-js';
import { createEffect, createSignal, For, Match, Show, Switch } from 'solid-js';
import { SyncedLine } from './SyncedLine';
@ -6,6 +6,7 @@ import { ErrorDisplay } from './ErrorDisplay';
import { LoadingKaomoji } from './LoadingKaomoji';
import { PlainLyrics } from './PlainLyrics';
import { hasJapaneseInString, hasKoreanInString } from '../utils';
import { currentLyrics, lyricsStore } from '../../providers';
export const [debugInfo, setDebugInfo] = createSignal<string>();
@ -13,6 +14,20 @@ export const [currentTime, setCurrentTime] = createSignal<number>(-1);
// prettier-ignore
export const LyricsContainer = () => {
const [hasJapanese, setHasJapanese] = createSignal<boolean>(false);
const [hasKorean, setHasKorean] = createSignal<boolean>(false);
createEffect(() => {
const data = currentLyrics()?.data;
if (data) {
setHasKorean(hasKoreanInString(data));
setHasJapanese(hasJapaneseInString(data));
} else {
setHasKorean(false);
setHasJapanese(false);
}
});
return (
<div class="lyric-container">
<Switch>
@ -42,12 +57,12 @@ export const LyricsContainer = () => {
<Switch>
<Match when={currentLyrics().data?.lines}>
<For each={currentLyrics().data?.lines}>
{(item) => <SyncedLine line={item} />}
{(item) => <SyncedLine line={item} hasJapanese={hasJapanese()} hasKorean={hasKorean()} />}
</For>
</Match>
<Match when={currentLyrics().data?.lyrics}>
<PlainLyrics lyrics={currentLyrics().data?.lyrics!} />
<PlainLyrics lyrics={currentLyrics().data!.lyrics!} hasJapanese={hasJapanese()} hasKorean={hasKorean()} />
</Match>
</Switch>
</div>

View File

@ -1,28 +1,91 @@
import { createMemo, For } from 'solid-js';
import { createEffect, createMemo, createSignal, For, Show } from 'solid-js';
import {
canonicalize,
romanizeChinese,
romanizeHangul,
romanizeJapanese,
romanizeJapaneseOrHangul,
simplifyUnicode,
} from '../utils';
import { config } from '../renderer';
interface PlainLyricsProps {
lyrics: string;
hasJapanese: boolean;
hasKorean: boolean;
}
export const PlainLyrics = (props: PlainLyricsProps) => {
const lines = createMemo(() => props.lyrics.split('\n'));
const lines = props.lyrics.split('\n').filter((line) => line.trim());
const [romanizedLines, setRomanizedLines] = createSignal<
Record<string, string>
>({});
const combinedLines = createMemo(() => {
const out = [];
for (let i = 0; i < lines.length; i++) {
out.push([lines[i], romanizedLines()[i]]);
}
return out;
});
createEffect(async () => {
if (!config()?.romanization) return;
for (let i = 0; i < lines.length; i++) {
let romanized: string;
if (props.hasJapanese) {
if (props.hasKorean)
romanized = await romanizeJapaneseOrHangul(lines[i]);
else romanized = await romanizeJapanese(lines[i]);
} else if (props.hasKorean) romanized = romanizeHangul(lines[i]);
else romanized = romanizeChinese(lines[i]);
setRomanizedLines((prev) => ({
...prev,
[i]: canonicalize(romanized),
}));
}
});
return (
<div class="plain-lyrics">
<For each={lines()}>
{(line) => {
if (line.trim() === '') {
return <br />;
} else {
return (
<For each={combinedLines()}>
{([line, romanized]) => {
return (
<div
class={`${
line.match(/^\[.+\]$/s) ? 'lrc-header' : ''
} text-lyrics description ytmusic-description-shelf-renderer`}
style={{
'display': 'flex',
'flex-direction': 'column',
}}
>
<yt-formatted-string
class="text-lyrics description ytmusic-description-shelf-renderer"
text={{
runs: [{ text: line }],
}}
/>
);
}
<Show
when={
config()?.romanization &&
simplifyUnicode(line) !== simplifyUnicode(romanized)
}
>
<yt-formatted-string
class="romaji"
text={{
runs: [{ text: romanized }],
}}
/>
</Show>
</div>
);
}}
</For>
</div>

View File

@ -1,22 +1,33 @@
import { createEffect, createMemo, For } from 'solid-js';
import { createEffect, createMemo, For, Show, createSignal } from 'solid-js';
import { currentTime } from './LyricsContainer';
import { config } from '../renderer';
import { _ytAPI } from '..';
import {
canonicalize,
romanizeChinese,
romanizeHangul,
romanizeJapanese,
romanizeJapaneseOrHangul,
simplifyUnicode,
} from '../utils';
import type { LineLyrics } from '../../types';
interface SyncedLineProps {
line: LineLyrics;
hasJapanese: boolean;
hasKorean: boolean;
}
export const SyncedLine = ({ line }: SyncedLineProps) => {
export const SyncedLine = (props: SyncedLineProps) => {
const status = createMemo(() => {
const current = currentTime();
if (line.timeInMs >= current) return 'upcoming';
if (current - line.timeInMs >= line.duration) return 'previous';
if (props.line.timeInMs >= current) return 'upcoming';
if (current - props.line.timeInMs >= props.line.duration) return 'previous';
return 'current';
});
@ -28,8 +39,28 @@ export const SyncedLine = ({ line }: SyncedLineProps) => {
});
const text = createMemo(() => {
if (line.text.trim()) return line.text;
return config()?.defaultTextString ?? '';
if (!props.line.text.trim()) {
return config()?.defaultTextString ?? '';
}
return props.line.text;
});
const [romanization, setRomanization] = createSignal('');
createEffect(async () => {
if (!config()?.romanization) return;
const input = canonicalize(text());
let result: string;
if (props.hasJapanese) {
if (props.hasKorean) result = await romanizeJapaneseOrHangul(input);
else result = await romanizeJapanese(input);
} else if (props.hasKorean) result = romanizeHangul(input);
else result = romanizeChinese(input);
setRomanization(canonicalize(result));
});
if (!text()) {
@ -47,35 +78,79 @@ export const SyncedLine = ({ line }: SyncedLineProps) => {
ref={ref}
class={`synced-line ${status()}`}
onClick={() => {
_ytAPI?.seekTo(line.timeInMs / 1000);
_ytAPI?.seekTo(props.line.timeInMs / 1000);
}}
>
<div dir="auto" class="text-lyrics description ytmusic-description-shelf-renderer">
<div dir="auto" class="description ytmusic-description-shelf-renderer">
<yt-formatted-string
text={{
runs: [{ text: config()?.showTimeCodes ? `[${line.time}] ` : '' }],
runs: [
{ text: config()?.showTimeCodes ? `[${props.line.time}] ` : '' },
],
}}
/>
<For each={text().split(' ')}>
{(word, index) => {
return (
<span
style={{
'transition-delay': `${index() * 0.05}s`,
'animation-delay': `${index() * 0.05}s`,
'--lyrics-duration:': `${line.duration / 1000}s;`,
}}
>
<yt-formatted-string
text={{
runs: [{ text: `${word} ` }],
}}
/>
</span>
<div
class="text-lyrics"
ref={(div: HTMLDivElement) => {
// TODO: Investigate the animation, even though the duration is properly set, all lines have the same animation duration
div.style.setProperty(
'--lyrics-duration',
`${props.line.duration / 1000}s`,
'important',
);
}}
</For>
style={{ 'display': 'flex', 'flex-direction': 'column' }}
>
<span>
<For each={text().split(' ')}>
{(word, index) => {
return (
<span
style={{
'transition-delay': `${index() * 0.05}s`,
'animation-delay': `${index() * 0.05}s`,
}}
>
<yt-formatted-string
text={{
runs: [{ text: `${word} ` }],
}}
/>
</span>
);
}}
</For>
</span>
<Show
when={
config()?.romanization &&
simplifyUnicode(text()) !== simplifyUnicode(romanization())
}
>
<span class="romaji">
<For each={romanization().split(' ')}>
{(word, index) => {
return (
<span
style={{
'transition-delay': `${index() * 0.05}s`,
'animation-delay': `${index() * 0.05}s`,
}}
>
<yt-formatted-string
text={{
runs: [{ text: `${word} ` }],
}}
/>
</span>
);
}}
</For>
</span>
</Show>
</div>
</div>
</div>
);

View File

@ -1,8 +1,20 @@
import { render } from 'solid-js/web';
import KuromojiAnalyzer from 'kuroshiro-analyzer-kuromoji';
import Kuroshiro from 'kuroshiro';
import { romanize as esHangulRomanize } from 'es-hangul';
import hanja from 'hanja';
import pinyin from 'pinyin/esm/pinyin';
import { lazy } from 'lazy-var';
import { waitForElement } from '@/utils/wait-for-element';
import { LyricsRenderer, setIsVisible } from './renderer';
import type { LyricResult } from '@/plugins/synced-lyrics/types';
export const selectors = {
head: '#tabsContent > .tab-header:nth-of-type(2)',
body: {
@ -33,3 +45,148 @@ export const tabStates: Record<string, () => void> = {
setIsVisible(false);
},
};
export const canonicalize = (text: string) => {
return (
text
// `hi there` => `hi there`
.replaceAll(/\s+/g, ' ')
// `( a )` => `(a)`
.replaceAll(/([([]) ([^ ])/g, (_, symbol, a) => `${symbol}${a}`)
.replaceAll(/([^ ]) ([)\]])/g, (_, a, symbol) => `${a}${symbol}`)
// `can ' t` => `can't`
.replaceAll(
/([Ii]) (') ([^ ])|(n) (') (t)(?= |$)|(t) (') (s)|([^ ]) (') (re)|([^ ]) (') (ve)|([^ ]) (-) ([^ ])/g,
(m, ...groups) => {
for (let i = 0; i < groups.length; i += 3) {
if (groups[i]) {
return groups.slice(i, i + 3).join('');
}
}
return m;
},
)
// `Stayin ' still` => `Stayin' still`
.replaceAll(/in ' ([^ ])/g, (_, char) => `in' ${char}`)
.replaceAll("in ',", "in',")
.replaceAll(", ' cause", ", 'cause")
// `hi , there` => `hi, there`
.replaceAll(/([^ ]) ([.,!?])/g, (_, a, symbol) => `${a}${symbol}`)
// `hi " there "` => `hi "there"`
.replaceAll(
/"([^"]+)"/g,
(_, content) =>
`"${typeof content === 'string' ? content.trim() : content}"`,
)
.trim()
);
};
export const simplifyUnicode = (text?: string) =>
text
? text
.replaceAll(/\u0020|\u00A0|[\u2000-\u200A]|\u202F|\u205F|\u3000/g, ' ')
.trim()
: text;
// Japanese Shinjitai
const shinjitai = [
20055, 20081, 20120, 20124, 20175, 26469, 20341, 20206, 20253, 20605, 20385,
20537, 20816, 20001, 20869, 23500, 28092, 20956, 21104, 21091, 21092, 21172,
21234, 21169, 21223, 21306, 24059, 21363, 21442, 21782, 21336, 22107, 21427,
22065, 22287, 22269, 22258, 20870, 22259, 22243, 37326, 23597, 22679, 22549,
22311, 22593, 22730, 22732, 22766, 22769, 23551, 22885, 22888, 23330, 23398,
23517, 23455, 20889, 23515, 23453, 23558, 23554, 23550, 23626, 23631, 23646,
23792, 23777, 23798, 23731, 24012, 24035, 24111, 24182, 24259, 24195, 24193,
24382, 24357, 24367, 24452, 24467, 24500, 24499, 24658, 24693, 24746, 24745,
24910, 24808, 24540, 25040, 24651, 25126, 25135, 25144, 25147, 25173, 25244,
25309, 25375, 25407, 25522, 25531, 25594, 25436, 25246, 25731, 25285, 25312,
25369, 25313, 25666, 25785, 21454, 21177, 21465, 21189, 25968, 26029, 26179,
26217, 26172, 26278, 26241, 26365, 20250, 26465, 26719, 26628, 27097, 27010,
27005, 27004, 26530, 27096, 27178, 26727, 26908, 26716, 27177, 27431, 27475,
27497, 27508, 24112, 27531, 27579, 27572, 27598, 27671, 28169, 28057, 27972,
27973, 28167, 28179, 28201, 28382, 28288, 28300, 28508, 28171, 27810, 28287,
28168, 27996, 27818, 28381, 28716, 28286, 28948, 28783, 28988, 21942, 28809,
20105, 28858, 29344, 29366, 29421, 22888, 29420, 29471, 29539, 29486, 24321,
29942, 30011, 24403, 30067, 30185, 30196, 30330, 26479, 30423, 23613, 30495,
30740, 30741, 30783, 31192, 31108, 31109, 31036, 31074, 31095, 31216, 31282,
38964, 31298, 31311, 31331, 31363, 20006, 31883, 31992, 32076, 32209, 32210,
32257, 30476, 32294, 32207, 32333, 32260, 32117, 32331, 32153, 32154, 32330,
27424, 32566, 22768, 32884, 31899, 33075, 32966, 33235, 21488, 19982, 26087,
33398, 33624, 33550, 33804, 19975, 33931, 22290, 34219, 34101, 33464, 34220,
33446, 20966, 34394, 21495, 34509, 34411, 34635, 34453, 34542, 34907, 35013,
35090, 35226, 35239, 35251, 35302, 35617, 35388, 35379, 35465, 35501, 22793,
35698, 35715, 35914, 33398, 20104, 24336, 22770, 38972, 36059, 36341, 36527,
36605, 36620, 36578, 24321, 36766, 24321, 36965, 36883, 36933, 36794, 37070,
37111, 37204, 21307, 37284, 37271, 37304, 37320, 37682, 37549, 37676, 37806,
37444, 37619, 37489, 38306, 38501, 38543, 38522, 38560, 21452, 38609, 35207,
38666, 38745, 39003, 38997, 32763, 20313, 39173, 39366, 39442, 39366, 39443,
39365, 39620, 20307, 39658, 38360, 40335, 40206, 40568, 22633, 40614, 40633,
40634, 40644, 40658, 40665, 28857, 20826, 25993, 25998, 27503, 40802, 31452,
20096,
].map((codePoint) => String.fromCodePoint(codePoint));
const shinjitaiRegex = new RegExp(`[${shinjitai.join('')}]`);
const kuroshiro = lazy(async () => {
const _kuroshiro = new Kuroshiro();
await _kuroshiro.init(
new KuromojiAnalyzer({
dictPath: 'https://cdn.jsdelivr.net/npm/kuromoji@0.1.2/dict/',
}),
);
return _kuroshiro;
});
const hasJapanese = (lines: string[]) =>
lines.some(
(line) => Kuroshiro.Util.hasKana(line) || shinjitaiRegex.test(line),
);
// tests for Hangul characters, sufficient for our use case
const hasKorean = (lines: string[]) =>
lines.some((line) => /[ㄱ-ㅎㅏ-ㅣ가-힣]+/.test(line));
export const hasJapaneseInString = (lyric: LyricResult) => {
if (!lyric || (!lyric.lines && !lyric.lyrics)) return false;
const lines = Array.isArray(lyric.lines)
? lyric.lines.map(({ text }) => text)
: lyric.lyrics!.split('\n');
return hasJapanese(lines);
};
export const hasKoreanInString = (lyric: LyricResult) => {
if (!lyric || (!lyric.lines && !lyric.lyrics)) return false;
const lines = Array.isArray(lyric.lines)
? lyric.lines.map(({ text }) => text)
: lyric.lyrics!.split('\n');
return hasKorean(lines);
};
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'));
export const romanizeJapaneseOrHangul = async (line: string) =>
romanizeHangul(await romanizeJapanese(line));
export const romanizeChinese = (line: string) =>
pinyin(line, {
heteronym: true,
segment: true,
group: true,
})
.flat()
.join(' ');

View File

@ -8,6 +8,17 @@
display: block !important;
}
/* Hide the scrollbar in the lyrics-tab */
#tab-renderer[page-type='MUSIC_PAGE_TYPE_TRACK_LYRICS'] {
scrollbar-width: none;
}
@property --lyrics-duration {
syntax: '<time>';
inherits: false;
initial-value: 2s;
}
/* Variables are overridden by selected line effect */
:root {
/* Layout */
@ -15,8 +26,9 @@
--lyrics-padding: 0;
/* Typography */
--lyrics-font-family: Satoshi, Avenir, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, Ubuntu, Cantarell,
Open Sans, Helvetica Neue, sans-serif;
--lyrics-font-family: Satoshi, Avenir, -apple-system, BlinkMacSystemFont,
Segoe UI, Roboto, Oxygen, Ubuntu, Cantarell, Open Sans, Helvetica Neue,
sans-serif;
--lyrics-font-size: clamp(1.4rem, 1.1vmax, 3rem);
--lyrics-line-height: var(--ytmusic-body-line-height);
--lyrics-width: 100%;
@ -33,12 +45,15 @@
--lyrics-active-scale: 1;
--lyrics-active-offset: 0;
--lyrics-duration: 2s;
/* Animations */
--lyrics-animations: lyrics-glow var(--lyrics-glow-duration) forwards, lyrics-wobble var(--lyrics-wobble-duration) forwards;
--lyrics-animations: lyrics-glow var(--lyrics-glow-duration) forwards,
lyrics-wobble var(--lyrics-wobble-duration) forwards;
--lyrics-scale-duration: 0.166s;
--lyrics-opacity-transition: 0.33s;
--lyrics-glow-duration: var(--lyrics-duration, 2s);
--lyrics-wobble-duration: calc(var(--lyrics-duration, 2s) / 2);
--lyrics-glow-duration: var(--lyrics-duration);
--lyrics-wobble-duration: calc(var(--lyrics-duration) / 2);
/* Colors */
--glow-color: rgba(255, 255, 255, 0.5);
@ -56,9 +71,31 @@
.synced-line {
width: var(--lyrics-width, 100%);
& > .text-lyrics {
& .text-lyrics {
cursor: pointer;
/*fix cuted lyrics-glow and romanized j at line start */
padding-left: 1.5rem;
}
& .text-lyrics > .romaji {
color: var(--ytmusic-text-secondary) !important;
font-size: calc(var(--lyrics-font-size) * 0.7) !important;
font-style: italic !important;
}
}
.plain-lyrics .romaji {
color: var(--ytmusic-text-secondary) !important;
font-size: calc(var(--lyrics-font-size) * 0.7) !important;
font-style: italic !important;
}
.plain-lyrics .lrc-header {
color: var(--ytmusic-color-grey5) !important;
scale: 0.9;
height: fit-content;
padding: 0;
padding-block: 0.2em;
}
.synced-lyrics {
@ -83,9 +120,7 @@
padding-bottom: var(--lyrics-padding);
scale: var(--lyrics-inactive-scale);
translate: var(--lyrics-inactive-offset);
transition:
scale var(--lyrics-scale-duration),
translate 0.3s ease-in-out;
transition: scale var(--lyrics-scale-duration), translate 0.3s ease-in-out;
display: block;
text-align: left;
@ -93,30 +128,24 @@
transform-origin: 0 50%;
}
.text-lyrics > span {
.text-lyrics > span > span {
display: inline-block;
white-space: pre-wrap;
opacity: var(--lyrics-inactive-opacity);
transition: opacity var(--lyrics-opacity-transition);
}
.previous > .text-lyrics {
}
.current > .text-lyrics {
.current .text-lyrics {
font-weight: var(--lyrics-active-font-weight) !important;
scale: var(--lyrics-active-scale);
translate: var(--lyrics-active-offset);
}
.current > .text-lyrics > span {
.current .text-lyrics > span > span {
opacity: var(--lyrics-active-opacity);
animation: var(--lyrics-animations);
}
.upcoming > .text-lyrics {
}
.lyrics-renderer {
display: flex;
flex-direction: column;

View File

@ -1,4 +1,4 @@
import { SongInfo } from '@/providers/song-info';
import type { SongInfo } from '@/providers/song-info';
export type SyncedLyricsPluginConfig = {
enabled: boolean;
@ -7,6 +7,7 @@ export type SyncedLyricsPluginConfig = {
defaultTextString: string;
showLyricsEvenIfInexact: boolean;
lineEffect: LineEffect;
romanization: boolean;
};
export type LineLyricsStatus = 'previous' | 'current' | 'upcoming';
@ -31,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

@ -0,0 +1,99 @@
import style from './style.css?inline';
import { createPlugin } from '@/utils';
import { t } from '@/i18n';
const handlePlay = (e: MouseEvent) => {
if (!(e.target instanceof HTMLElement)) return;
// 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;
}
};
const handleVideoDataChange = () => {
const isPlayerPageOpen =
document
.querySelector('ytmusic-app-layout')
?.attributes.getNamedItem('player-ui-state')?.value ===
'PLAYER_PAGE_OPEN';
if (
document.body.classList.contains('unobtrusive-player--did-play') &&
isPlayerPageOpen
) {
document.body.classList.add('unobtrusive-player--auto-closing');
document
.querySelector<HTMLButtonElement>('.toggle-player-page-button')
?.click();
// prevent animation flickering
setTimeout(() => {
document.body.classList.remove('unobtrusive-player--auto-closing');
}, 500);
}
};
export default createPlugin({
name: () => t('plugins.unobtrusive-player.name'),
description: () => t('plugins.unobtrusive-player.description'),
addedVersion: '3.8.x',
restartNeeded: false,
config: {
enabled: false,
},
stylesheets: [style],
renderer: {
start: () => {
document.body.classList.add('unobtrusive-player');
document.addEventListener('click', handlePlay);
},
onPlayerApiReady: () => {
// Close player page when video changes while
// `unobtrusive-player--did-play` className is present.
document.addEventListener('videodatachange', handleVideoDataChange);
},
stop: () => {
document.removeEventListener('click', handlePlay);
document.removeEventListener('videodatachange', handleVideoDataChange);
[
'unobtrusive-player',
'unobtrusive-player--did-play',
'unobtrusive-player--auto-closing',
].forEach((className) => document.body.classList.remove(className));
},
},
});

View File

@ -0,0 +1,27 @@
body.unobtrusive-player.unobtrusive-player--did-play {
overflow: visible !important;
& ytmusic-player-page,
ytmusic-player-page * {
visibility: hidden !important;
}
& #content {
visibility: visible !important;
}
&
ytmusic-app-layout:not(.content-scrolled)
#nav-bar-background.ytmusic-app-layout,
ytmusic-app-layout:not(.content-scrolled) #nav-bar-divider.ytmusic-app-layout,
ytmusic-app-layout[is-bauhaus-sidenav-enabled][player-page-open]:not(
.content-scrolled
)
#mini-guide-background.ytmusic-app-layout {
opacity: 0 !important;
}
& .toggle-player-page-button {
transform: rotate(180deg) !important;
}
}

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

@ -28,6 +28,7 @@ export enum MediaType {
export interface SongInfo {
title: string;
alternativeTitle?: string;
artist: string;
views: number;
uploadDate?: string;
@ -41,6 +42,7 @@ export interface SongInfo {
videoId: string;
playlistId?: string;
mediaType: MediaType;
tags?: string[];
}
// Grab the native image using the src
@ -68,6 +70,7 @@ const handleData = async (
// Fill songInfo with empty values
const songInfo: SongInfo = {
title: '',
alternativeTitle: '',
artist: '',
views: 0,
uploadDate: '',
@ -81,6 +84,7 @@ const handleData = async (
videoId: '',
playlistId: '',
mediaType: MediaType.Audio,
tags: [],
} satisfies SongInfo;
const microformat = data.microformat?.microformatDataRenderer;
@ -91,6 +95,10 @@ const handleData = async (
new URL(microformat.urlCanonical).searchParams.get('list') ?? '';
// Used for options.resumeOnStart
config.set('url', microformat.urlCanonical);
songInfo.alternativeTitle = microformat.linkAlternates.find(
(link) => link.title,
)?.title;
songInfo.tags = Array.isArray(microformat.tags) ? microformat.tags : [];
}
const { videoDetails } = data;

View File

@ -0,0 +1,12 @@
// Stolen from https://github.com/hexenq/kuroshiro-analyzer-kuromoji/pull/7
// Credit goes to https://github.com/ALOHACREPES345
declare class KuromojiAnalyzer {
constructor(dictPath?: { dictPath: string });
init(): Promise<void>;
parse(str: string): Promise<unknown>;
}
declare module 'kuroshiro-analyzer-kuromoji' {
export = KuromojiAnalyzer;
}

40
src/ts-declarations/kuroshiro.d.ts vendored Normal file
View File

@ -0,0 +1,40 @@
// Stolen from https://github.com/hexenq/kuroshiro/pull/93
// Credit goes to https://github.com/ALOHACREPES345 and https://github.com/lcsvcn
declare class Kuroshiro {
constructor();
_analyzer: import('kuroshiro-analyzer-kuromoji') | null;
init(analyzer: import('kuroshiro-analyzer-kuromoji')): Promise<void>;
convert(
str: string,
options?: {
to?: 'hiragana' | 'katakana' | 'romaji';
mode?: 'normal' | 'spaced' | 'okurigana' | 'furigana';
romajiSystem?: 'nippon' | 'passport' | 'hepburn';
delimiter_start?: string;
delimiter_end?: string;
},
): Promise<string>;
static Util: {
isHiragana: (ch: string) => boolean;
isKatakana: (ch: string) => boolean;
isKana: (ch: string) => boolean;
isKanji: (ch: string) => boolean;
isJapanese: (ch: string) => boolean;
hasHiragana: (str: string) => boolean;
hasKatakana: (str: string) => boolean;
hasKana: (str: string) => boolean;
hasKanji: (str: string) => boolean;
hasJapanese: (str: string) => boolean;
kanaToHiragana: (str: string) => string;
kanaToKatakana: (str: string) => string;
kanaToRomaji: (
str: string,
system: 'nippon' | 'passport' | 'hepburn',
) => string;
};
}
declare module 'kuroshiro' {
export = Kuroshiro;
}

View File

@ -130,7 +130,7 @@ export default function (
const stubContexts =
mode === 'backend'
? contexts.filter((ctx) => ctx !== 'backend')
? contexts.filter((ctx) => ctx !== 'menu')
: contexts;
for (const ctx of stubContexts) {
if (stubMap.has(ctx)) {