mirror of
https://github.com/th-ch/youtube-music.git
synced 2026-01-12 11:01:45 +00:00
Compare commits
70 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 754ca3caaa | |||
| 61ea104d7b | |||
| 573bdfae03 | |||
| cca493b7d5 | |||
| f47262d27b | |||
| 65bf9129ea | |||
| 87e9b9f7a8 | |||
| 07bc4f05fd | |||
| e3a6808087 | |||
| e9184e5d60 | |||
| a5b32d96f8 | |||
| 040db7539c | |||
| da646c1d53 | |||
| 5f5917f972 | |||
| eccb0d2f08 | |||
| 4cd9dd17df | |||
| 5de07b9a96 | |||
| 151f067beb | |||
| c68a7bd19f | |||
| b87e5e31df | |||
| 03229d61c8 | |||
| b6330eed18 | |||
| b254812ac2 | |||
| 7e243e2fbf | |||
| 307e52cc89 | |||
| f7b7ea916f | |||
| 3eccf8daca | |||
| aa48944212 | |||
| 4d51f1a412 | |||
| d3c9f76582 | |||
| d638a6cf28 | |||
| f93651b219 | |||
| cb8c6c69fe | |||
| 4e7266fb1b | |||
| 8de75ff3a5 | |||
| 3236c88eb2 | |||
| c9f0ad14c2 | |||
| 0a9199c92b | |||
| 3ffcff7d9c | |||
| ddf614d362 | |||
| 6f1a77bbb9 | |||
| 8595f9761e | |||
| cc442182fd | |||
| b827a05eea | |||
| 250abab8bc | |||
| 8e45518ccf | |||
| 7485e065ed | |||
| c1d88f91d4 | |||
| 124a2bd8d0 | |||
| f8f94f9665 | |||
| 5e98a82b23 | |||
| a5c20a66b3 | |||
| cc84116ad1 | |||
| a2e2031708 | |||
| 5001eabf23 | |||
| aac2974430 | |||
| f7f005bb3d | |||
| e1f6d5b7f2 | |||
| b6b607897e | |||
| 651ebb2b1a | |||
| 9fa24deed2 | |||
| c81022d373 | |||
| b726dc7580 | |||
| 471aa7d0a6 | |||
| f34d645ac3 | |||
| d2a11a560e | |||
| 9d185872db | |||
| d0ff71aa66 | |||
| bc8999585f | |||
| 1e1582e31f |
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
@ -23,7 +23,7 @@ jobs:
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9
|
||||
version: 10
|
||||
run_install: false
|
||||
|
||||
- name: Setup NodeJS
|
||||
@ -98,7 +98,7 @@ jobs:
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9
|
||||
version: 10
|
||||
run_install: false
|
||||
|
||||
- name: Setup NodeJS
|
||||
|
||||
49
changelog.md
49
changelog.md
@ -2,8 +2,57 @@
|
||||
|
||||
All notable changes to this project will be documented in this file. Dates are displayed in UTC.
|
||||
|
||||
#### [v3.7.2](https://github.com/th-ch/youtube-music/compare/v3.7.1...v3.7.2)
|
||||
|
||||
- feat(api-server): add endpoint to get shuffle state [`#2792`](https://github.com/th-ch/youtube-music/pull/2792)
|
||||
- chore(deps): update dependency discord-api-types to v0.37.116 [`#2877`](https://github.com/th-ch/youtube-music/pull/2877)
|
||||
- chore(deps): update dependency eslint-plugin-prettier to v5.2.2 [`#2875`](https://github.com/th-ch/youtube-music/pull/2875)
|
||||
- chore(deps): update eslint monorepo to v9.18.0 [`#2858`](https://github.com/th-ch/youtube-music/pull/2858)
|
||||
- chore(deps): update dependency glob to v11.0.1 [`#2857`](https://github.com/th-ch/youtube-music/pull/2857)
|
||||
- chore(deps): update dependency electron-builder-squirrel-windows to v26.0.0-alpha.9 [`#2874`](https://github.com/th-ch/youtube-music/pull/2874)
|
||||
- chore(deps): update dependency electron to v34 [`#2867`](https://github.com/th-ch/youtube-music/pull/2867)
|
||||
- chore(deps): update dependency eslint-config-prettier to v10 [`#2866`](https://github.com/th-ch/youtube-music/pull/2866)
|
||||
- chore(deps): update dependency @stylistic/eslint-plugin-js to v2.13.0 [`#2864`](https://github.com/th-ch/youtube-music/pull/2864)
|
||||
- chore(deps): update dependency typescript-eslint to v8.20.0 [`#2865`](https://github.com/th-ch/youtube-music/pull/2865)
|
||||
- chore(deps): update dependency electron-builder to v26.0.0-alpha.9 [`#2869`](https://github.com/th-ch/youtube-music/pull/2869)
|
||||
- fix: fix build.linux.desktop.entry [`#2859`](https://github.com/th-ch/youtube-music/pull/2859)
|
||||
- feat(api-server): add endpoint to get volume state [`#2813`](https://github.com/th-ch/youtube-music/pull/2813)
|
||||
- chore(deps): update dependency vite-plugin-inspect to v10 [`#2856`](https://github.com/th-ch/youtube-music/pull/2856)
|
||||
- chore(deps): update dependency typescript to v5.7.3 [`#2855`](https://github.com/th-ch/youtube-music/pull/2855)
|
||||
- fix(deps): update dependency @floating-ui/dom to v1.6.13 [`#2846`](https://github.com/th-ch/youtube-music/pull/2846)
|
||||
- chore(deps): bump nanoid from 3.3.7 to 3.3.8 [`#2854`](https://github.com/th-ch/youtube-music/pull/2854)
|
||||
- chore(deps): update dependency electron to v33.3.1 [`#2841`](https://github.com/th-ch/youtube-music/pull/2841)
|
||||
- fix(deps): update dependency i18next to v24.2.1 [`#2840`](https://github.com/th-ch/youtube-music/pull/2840)
|
||||
- chore(deps): update dependency typescript-eslint to v8.19.1 [`#2836`](https://github.com/th-ch/youtube-music/pull/2836)
|
||||
- chore(deps): update dependency rollup to v4.30.1 [`#2833`](https://github.com/th-ch/youtube-music/pull/2833)
|
||||
- fix(deps): update dependency solid-js to v1.9.4 [`#2849`](https://github.com/th-ch/youtube-music/pull/2849)
|
||||
- fix(deps): update dependency fast-equals to v5.2.2 [`#2842`](https://github.com/th-ch/youtube-music/pull/2842)
|
||||
- chore: Update README.md [`#2845`](https://github.com/th-ch/youtube-music/pull/2845)
|
||||
- chore: Fixing the Content section in the README-ru.md file. [`#2847`](https://github.com/th-ch/youtube-music/pull/2847)
|
||||
- chore: Create youtube-music-hu.svg [`#2844`](https://github.com/th-ch/youtube-music/pull/2844)
|
||||
- chore: Create Transalated README-hu.md [`#2843`](https://github.com/th-ch/youtube-music/pull/2843)
|
||||
- chore(deps): update dependency vite to v6.0.7 [`#2819`](https://github.com/th-ch/youtube-music/pull/2819)
|
||||
- chore(deps): update dependency discord-api-types to v0.37.115 [`#2818`](https://github.com/th-ch/youtube-music/pull/2818)
|
||||
- fix(deps): update dependency hono to v4.6.16 [`#2829`](https://github.com/th-ch/youtube-music/pull/2829)
|
||||
- chore(deps): update dependency rollup to v4.29.2 [`#2832`](https://github.com/th-ch/youtube-music/pull/2832)
|
||||
- fix(deps): update dependency fast-equals to v5.2.0 [`#2822`](https://github.com/th-ch/youtube-music/pull/2822)
|
||||
- feat(api-server): add `insertPosition` for `addSongToQueue` [`#2808`](https://github.com/th-ch/youtube-music/pull/2808)
|
||||
- chore(deps): update dependency typescript-eslint to v8.19.0 [`#2812`](https://github.com/th-ch/youtube-music/pull/2812)
|
||||
- fix(deps): update dependency ts-morph to v25 [`#2810`](https://github.com/th-ch/youtube-music/pull/2810)
|
||||
- fix(renderer): update event handler from onVolumeTap to onVolumeClick [`#2791`](https://github.com/th-ch/youtube-music/pull/2791)
|
||||
- fix(deps): update dependency hono to v4.6.15 [`#2796`](https://github.com/th-ch/youtube-music/pull/2796)
|
||||
- chore(deps): update dependency bufferutil to v4.0.9 [`#2787`](https://github.com/th-ch/youtube-music/pull/2787)
|
||||
- feat: Refactor Menu Navigation and Update Media Control Icons [`#2783`](https://github.com/th-ch/youtube-music/pull/2783)
|
||||
- fix(synced-lyrics): Revert font-size behavior for non-fancy modes [`#2788`](https://github.com/th-ch/youtube-music/pull/2788)
|
||||
- fix(downloader): apply poToken [`#2863`](https://github.com/th-ch/youtube-music/issues/2863) [`#2780`](https://github.com/th-ch/youtube-music/issues/2780)
|
||||
- chore(deps): update dependency electron-builder to v26 [`67fc0a4`](https://github.com/th-ch/youtube-music/commit/67fc0a415cae231a11f2846aadf01edb04f5c677)
|
||||
- fix: fix lock file [`3339f99`](https://github.com/th-ch/youtube-music/commit/3339f997e3c2d4d2c32b3aee95c65d561f123fcb)
|
||||
- chore(i18n): Translated using Weblate (Romanian) [`845dac3`](https://github.com/th-ch/youtube-music/commit/845dac3c0393dadea8efdd03ba1f41b1b36e6191)
|
||||
|
||||
#### [v3.7.1](https://github.com/th-ch/youtube-music/compare/v3.7.0...v3.7.1)
|
||||
|
||||
> 27 December 2024
|
||||
|
||||
- fix(deps): update dependency node-html-parser to v7 [`#2776`](https://github.com/th-ch/youtube-music/pull/2776)
|
||||
- chore(deps): update dependency vite to v6.0.6 [`#2774`](https://github.com/th-ch/youtube-music/pull/2774)
|
||||
- feat(api-server): Add queue api [`#2767`](https://github.com/th-ch/youtube-music/pull/2767)
|
||||
|
||||
@ -147,6 +147,11 @@ export default defineConfig({
|
||||
resolve: {
|
||||
alias: resolveAlias,
|
||||
},
|
||||
server: {
|
||||
cors: {
|
||||
origin: 'https://music.youtube.com',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
if (mode === 'development') {
|
||||
|
||||
88
package.json
88
package.json
@ -2,7 +2,7 @@
|
||||
"name": "youtube-music",
|
||||
"desktopName": "com.github.th_ch.youtube_music",
|
||||
"productName": "YouTube Music",
|
||||
"version": "3.7.2",
|
||||
"version": "3.7.3",
|
||||
"description": "YouTube Music Desktop App - including custom plugins",
|
||||
"main": "./dist/main/index.js",
|
||||
"license": "MIT",
|
||||
@ -222,18 +222,19 @@
|
||||
},
|
||||
"pnpm": {
|
||||
"overrides": {
|
||||
"vite": "6.0.7",
|
||||
"node-gyp": "11.0.0",
|
||||
"vite": "6.1.0",
|
||||
"node-gyp": "11.1.0",
|
||||
"xml2js": "0.6.2",
|
||||
"node-fetch": "3.3.2",
|
||||
"@electron/universal": "2.0.1",
|
||||
"@babel/runtime": "7.26.0"
|
||||
"@babel/runtime": "7.26.9"
|
||||
},
|
||||
"patchedDependencies": {
|
||||
"vudio@2.1.1": "patches/vudio@2.1.1.patch",
|
||||
"app-builder-lib@26.0.0-alpha.9": "patches/app-builder-lib@26.0.0-alpha.9.patch",
|
||||
"app-builder-lib@26.0.6": "patches/app-builder-lib@26.0.6.patch",
|
||||
"@malept/flatpak-bundler": "patches/@malept__flatpak-bundler.patch"
|
||||
}
|
||||
},
|
||||
"neverBuiltDependencies": []
|
||||
},
|
||||
"dependencies": {
|
||||
"@electron-toolkit/tsconfig": "1.0.1",
|
||||
@ -242,12 +243,12 @@
|
||||
"@ffmpeg.wasm/main": "0.12.0",
|
||||
"@floating-ui/dom": "1.6.13",
|
||||
"@foobar404/wave": "2.0.5",
|
||||
"@ghostery/adblocker-electron": "2.3.1",
|
||||
"@ghostery/adblocker-electron-preload": "2.3.1",
|
||||
"@hono/node-server": "1.13.7",
|
||||
"@ghostery/adblocker-electron": "2.5.0",
|
||||
"@ghostery/adblocker-electron-preload": "2.5.0",
|
||||
"@hono/node-server": "1.13.8",
|
||||
"@hono/swagger-ui": "0.5.0",
|
||||
"@hono/zod-openapi": "0.18.3",
|
||||
"@hono/zod-validator": "0.4.2",
|
||||
"@hono/zod-openapi": "0.18.4",
|
||||
"@hono/zod-validator": "0.4.3",
|
||||
"@jellybrick/dbus-next": "0.10.3",
|
||||
"@jellybrick/electron-better-web-request": "1.0.4",
|
||||
"@jellybrick/mpris-service": "2.1.5",
|
||||
@ -255,52 +256,51 @@
|
||||
"@skyra/jaro-winkler": "1.1.1",
|
||||
"@xhayper/discord-rpc": "1.2.0",
|
||||
"async-mutex": "0.5.0",
|
||||
"bgutils-js": "3.1.2",
|
||||
"bgutils-js": "3.1.3",
|
||||
"butterchurn": "3.0.0-beta.4",
|
||||
"butterchurn-presets": "3.0.0-beta.4",
|
||||
"color": "4.2.3",
|
||||
"color": "5.0.0",
|
||||
"conf": "13.1.0",
|
||||
"custom-electron-prompt": "1.5.8",
|
||||
"deepmerge-ts": "7.1.3",
|
||||
"deepmerge-ts": "7.1.4",
|
||||
"electron-debug": "4.1.0",
|
||||
"electron-is": "3.0.0",
|
||||
"electron-localshortcut": "3.2.1",
|
||||
"electron-store": "10.0.0",
|
||||
"electron-store": "10.0.1",
|
||||
"electron-unhandled": "4.0.1",
|
||||
"electron-updater": "6.3.9",
|
||||
"fast-average-color": "9.4.0",
|
||||
"fast-equals": "5.2.2",
|
||||
"filenamify": "6.0.0",
|
||||
"happy-dom": "16.6.0",
|
||||
"hono": "4.6.16",
|
||||
"happy-dom": "17.1.0",
|
||||
"hono": "4.7.1",
|
||||
"howler": "2.2.4",
|
||||
"html-to-text": "9.0.5",
|
||||
"i18next": "24.2.1",
|
||||
"i18next": "24.2.2",
|
||||
"jimp": "1.6.0",
|
||||
"keyboardevent-from-electron-accelerator": "2.0.0",
|
||||
"keyboardevents-areequal": "0.2.2",
|
||||
"node-html-parser": "7.0.1",
|
||||
"node-id3": "0.2.6",
|
||||
"node-id3": "0.2.7",
|
||||
"peerjs": "1.5.4",
|
||||
"semver": "7.6.3",
|
||||
"semver": "7.7.1",
|
||||
"serve": "14.2.4",
|
||||
"simple-youtube-age-restriction-bypass": "github:organization/Simple-YouTube-Age-Restriction-Bypass#v2.5.9",
|
||||
"solid-floating-ui": "0.3.1",
|
||||
"solid-js": "1.9.4",
|
||||
"solid-styled-components": "0.28.5",
|
||||
"solid-transition-group": "0.2.3",
|
||||
"ts-morph": "25.0.0",
|
||||
"solid-transition-group": "0.3.0",
|
||||
"ts-morph": "25.0.1",
|
||||
"vudio": "2.1.1",
|
||||
"x11": "2.3.0",
|
||||
"youtubei.js": "12.2.0",
|
||||
"zod": "3.24.1"
|
||||
"youtubei.js": "13.0.0",
|
||||
"zod": "3.24.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "9.18.0",
|
||||
"@playwright/test": "1.49.1",
|
||||
"@stylistic/eslint-plugin-js": "2.13.0",
|
||||
"@eslint/js": "9.20.0",
|
||||
"@playwright/test": "1.50.1",
|
||||
"@stylistic/eslint-plugin-js": "3.1.0",
|
||||
"@total-typescript/ts-reset": "0.6.1",
|
||||
"@types/color": "4.2.0",
|
||||
"@types/electron-localshortcut": "3.1.3",
|
||||
"@types/eslint__js": "8.42.3",
|
||||
"@types/howler": "2.2.12",
|
||||
@ -311,30 +311,30 @@
|
||||
"builtin-modules": "4.0.0",
|
||||
"cross-env": "7.0.3",
|
||||
"del-cli": "6.0.0",
|
||||
"discord-api-types": "0.37.116",
|
||||
"electron": "34.0.0",
|
||||
"electron-builder": "26.0.0-alpha.9",
|
||||
"electron-builder-squirrel-windows": "26.0.0-alpha.9",
|
||||
"discord-api-types": "0.37.119",
|
||||
"electron": "34.2.0",
|
||||
"electron-builder": "26.0.6",
|
||||
"electron-builder-squirrel-windows": "26.0.6",
|
||||
"electron-devtools-installer": "4.0.0",
|
||||
"electron-vite": "2.3.0",
|
||||
"esbuild": "0.24.2",
|
||||
"eslint": "9.18.0",
|
||||
"electron-vite": "3.0.0",
|
||||
"esbuild": "0.25.0",
|
||||
"eslint": "9.20.1",
|
||||
"eslint-config-prettier": "10.0.1",
|
||||
"eslint-import-resolver-exports": "1.0.0-beta.5",
|
||||
"eslint-import-resolver-typescript": "3.7.0",
|
||||
"eslint-import-resolver-typescript": "3.8.0",
|
||||
"eslint-plugin-import": "2.31.0",
|
||||
"eslint-plugin-prettier": "5.2.2",
|
||||
"eslint-plugin-prettier": "5.2.3",
|
||||
"glob": "11.0.1",
|
||||
"node-gyp": "11.0.0",
|
||||
"playwright": "1.49.1",
|
||||
"rollup": "4.30.1",
|
||||
"node-gyp": "11.1.0",
|
||||
"playwright": "1.50.1",
|
||||
"rollup": "4.34.7",
|
||||
"typescript": "5.7.3",
|
||||
"typescript-eslint": "8.20.0",
|
||||
"typescript-eslint": "8.24.0",
|
||||
"utf-8-validate": "6.0.5",
|
||||
"vite": "6.0.7",
|
||||
"vite-plugin-inspect": "10.0.6",
|
||||
"vite": "6.1.0",
|
||||
"vite-plugin-inspect": "10.2.1",
|
||||
"vite-plugin-resolve": "2.5.2",
|
||||
"vite-plugin-solid": "2.11.0",
|
||||
"vite-plugin-solid": "2.11.1",
|
||||
"ws": "8.18.0"
|
||||
},
|
||||
"auto-changelog": {
|
||||
|
||||
3399
pnpm-lock.yaml
generated
3399
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -1,8 +1,6 @@
|
||||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": [
|
||||
"config:base"
|
||||
],
|
||||
"extends": ["config:recommended"],
|
||||
"labels": ["dependencies"],
|
||||
"postUpdateOptions": ["pnpmDedupe"]
|
||||
}
|
||||
|
||||
@ -745,7 +745,8 @@
|
||||
"label": "Efecto de la línea",
|
||||
"submenu": {
|
||||
"fancy": {
|
||||
"label": "Elegante"
|
||||
"label": "Elegante",
|
||||
"tooltip": "Usar efectos grandes, similares a los de una aplicación, en la línea actual"
|
||||
},
|
||||
"focus": {
|
||||
"label": "Enfoque",
|
||||
|
||||
@ -81,12 +81,12 @@
|
||||
"menu": {
|
||||
"about": "درباره",
|
||||
"navigation": {
|
||||
"label": "ناوبری",
|
||||
"label": "کنترلهای رابط",
|
||||
"submenu": {
|
||||
"copy-current-url": "کپی کردن URL فعلی",
|
||||
"go-back": "بازگشت",
|
||||
"go-forward": "حرکت به جلو",
|
||||
"quit": "خروجی",
|
||||
"copy-current-url": "کپی کردن لینک صفحه فعلی",
|
||||
"go-back": "صفحه قبل",
|
||||
"go-forward": "صفحه بعدی",
|
||||
"quit": "خروج از برنامه",
|
||||
"restart": "راهاندازی مجدد برنامه"
|
||||
}
|
||||
},
|
||||
@ -98,8 +98,8 @@
|
||||
"submenu": {
|
||||
"auto-reset-app-cache": "ریست کردن حافظه کش برنامه هنگام شروع",
|
||||
"disable-hardware-acceleration": "غیرفعال کردن شتاب سختافزاری",
|
||||
"edit-config-json": "ویرایش config.json",
|
||||
"override-user-agent": "تغییر User-Agent",
|
||||
"edit-config-json": "config.json ویرایش",
|
||||
"override-user-agent": "User-Agent تغییر",
|
||||
"restart-on-config-changes": "راهاندازی مجدد در صورت تغییرات در پیکربندی",
|
||||
"set-proxy": {
|
||||
"label": "تنظیم پراکسی",
|
||||
@ -109,7 +109,7 @@
|
||||
"title": "تنظیم پراکسی"
|
||||
}
|
||||
},
|
||||
"toggle-dev-tools": "باز کردن DevTools"
|
||||
"toggle-dev-tools": "DevTools باز کردن"
|
||||
}
|
||||
},
|
||||
"always-on-top": "همیشه در بالا",
|
||||
@ -168,7 +168,7 @@
|
||||
},
|
||||
"label": "تم",
|
||||
"submenu": {
|
||||
"import-css-file": "وارد کردن فایل CSS سفارشی",
|
||||
"import-css-file": "سفارشی CSS وارد کردن فایل",
|
||||
"no-theme": "بدون تم"
|
||||
}
|
||||
}
|
||||
@ -177,7 +177,7 @@
|
||||
}
|
||||
},
|
||||
"plugins": {
|
||||
"enabled": "فعال",
|
||||
"enabled": "فعال/غیرفعال کردن",
|
||||
"label": "افزونهها",
|
||||
"new": "جدید"
|
||||
},
|
||||
@ -187,7 +187,7 @@
|
||||
"force-reload": "اجبار به بارگذاری مجدد",
|
||||
"reload": "بارگذاری مجدد",
|
||||
"reset-zoom": "اندازه واقعی",
|
||||
"toggle-fullscreen": "تغییر به تمامصفحه",
|
||||
"toggle-fullscreen": "تغییر به تمام صفحه",
|
||||
"zoom-in": "بزرگنمایی",
|
||||
"zoom-out": "کوچکنمایی"
|
||||
}
|
||||
@ -219,7 +219,7 @@
|
||||
"name": "مسدودکننده تبلیغات"
|
||||
},
|
||||
"album-actions": {
|
||||
"description": "افزودن دکمههای \"برگرفتن ناپسند\"، \"ناپسند\"، \"پسند\"، و \"حذف پسند\" برای اعمال آنها روی همه آهنگها در یک فهرست پخش یا آلبوم",
|
||||
"description": "اضافه کردن دکمههای عدم پسندیدن، پسندیدن و لغو پسندیدن برای اعمال این تغییرات به تمامی آهنگهای یک فهرست پخش یا آلبوم",
|
||||
"name": "عملیات آلبوم"
|
||||
},
|
||||
"album-color-theme": {
|
||||
@ -250,7 +250,10 @@
|
||||
}
|
||||
},
|
||||
"opacity": {
|
||||
"label": "شفافیت"
|
||||
"label": "شفافیت",
|
||||
"submenu": {
|
||||
"percent": "{{opacity}}%"
|
||||
}
|
||||
},
|
||||
"quality": {
|
||||
"label": "کیفیت",
|
||||
@ -259,7 +262,10 @@
|
||||
}
|
||||
},
|
||||
"size": {
|
||||
"label": "اندازه"
|
||||
"label": "اندازه",
|
||||
"submenu": {
|
||||
"percent": "{{size}}%"
|
||||
}
|
||||
},
|
||||
"smoothness-transition": {
|
||||
"label": "انتقال نرمی",
|
||||
@ -273,8 +279,15 @@
|
||||
},
|
||||
"name": "حالت محیطی"
|
||||
},
|
||||
"amuse": {
|
||||
"description": "حالا ویجت Amuse از YouTube Music هم پشتیبانی میکنه! (توسط 6K Labs)",
|
||||
"name": "Amuse",
|
||||
"response": {
|
||||
"query": "سرور Amuse فعال است. برای دریافت اطلاعات آهنگ، از آدرس /query استفاده کنید."
|
||||
}
|
||||
},
|
||||
"api-server": {
|
||||
"description": "افزودن یک سرور API برای کنترل پخشکننده",
|
||||
"description": "برای کنترل پخشکننده API افزودن یک سرور",
|
||||
"dialog": {
|
||||
"request": {
|
||||
"buttons": {
|
||||
@ -304,14 +317,14 @@
|
||||
"label": "پورت"
|
||||
}
|
||||
},
|
||||
"name": "سرور API [بتا]",
|
||||
"name": "[بتا]API سرور",
|
||||
"prompt": {
|
||||
"hostname": {
|
||||
"label": "نام میزبان را برای سرور API وارد کنید (مثل 0.0.0.0):",
|
||||
"label": "وارد کنید (مثل 0.0.0.0): API نام میزبان را برای سرور",
|
||||
"title": "نام میزبان"
|
||||
},
|
||||
"port": {
|
||||
"label": "پورت را برای سرور API وارد کنید:",
|
||||
"label": "وارد کنید: API پورت را برای سرور",
|
||||
"title": "پورت"
|
||||
}
|
||||
}
|
||||
@ -321,8 +334,8 @@
|
||||
"name": "فشردهساز صدا"
|
||||
},
|
||||
"blur-nav-bar": {
|
||||
"description": "شفاف و محو کردن نوار ناوبری",
|
||||
"name": "محو کردن نوار ناوبری"
|
||||
"description": "شفاف و محو کردن نوار کنترل",
|
||||
"name": "محو کردن نوار کنترل"
|
||||
},
|
||||
"bypass-age-restrictions": {
|
||||
"description": "دور زدن تأیید سن یوتیوب",
|
||||
@ -381,27 +394,27 @@
|
||||
},
|
||||
"discord": {
|
||||
"backend": {
|
||||
"already-connected": "تلاش برای اتصال با اتصال فعال",
|
||||
"connected": "متصل به Discord",
|
||||
"disconnected": "قطع اتصال از Discord"
|
||||
"already-connected": "تلاش برای برقراری ارتباط با اتصال فعال",
|
||||
"connected": "متصل به دیسکورد",
|
||||
"disconnected": "ارتباط با دیسکورد قطع شد"
|
||||
},
|
||||
"description": "نمایش آنچه گوش میدهید به دوستان با Rich Presence",
|
||||
"description": "Rich Presence نمایش آنچه گوش میدهید به دوستان با",
|
||||
"menu": {
|
||||
"auto-reconnect": "اتصال خودکار مجدد",
|
||||
"auto-reconnect": "اتصال خودکار",
|
||||
"clear-activity": "پاک کردن فعالیت",
|
||||
"clear-activity-after-timeout": "پاک کردن فعالیت پس از تایماوت",
|
||||
"connected": "متصل",
|
||||
"disconnected": "قطع شده",
|
||||
"clear-activity-after-timeout": "حذف فعالیت پس از اتمام زمان تعیینشده",
|
||||
"connected": "اتصال برقرار شد",
|
||||
"disconnected": "اتصال قطع شد",
|
||||
"hide-duration-left": "مخفی کردن مدت زمان باقیمانده",
|
||||
"hide-github-button": "مخفی کردن دکمه لینک GitHub",
|
||||
"play-on-youtube-music": "پخش در یوتیوب موسیقی",
|
||||
"set-inactivity-timeout": "تنظیم تایماوت عدم فعالیت"
|
||||
"hide-github-button": "مخفی کردن دکمه لینک گیت هاب",
|
||||
"play-on-youtube-music": "پخش در یوتیوب موزیک",
|
||||
"set-inactivity-timeout": "تنظیم زمان عدم فعالیت"
|
||||
},
|
||||
"name": "Rich Presence در Discord",
|
||||
"name": "Discord Rich Presence",
|
||||
"prompt": {
|
||||
"set-inactivity-timeout": {
|
||||
"label": "ورود تایماوت عدم فعالیت به ثانیه:",
|
||||
"title": "تنظیم تایماوت عدم فعالیت"
|
||||
"label": "محدودیت زمان عدم فعالیت را به ثانیه وارد کنید:",
|
||||
"title": "تنظیم زمان عدم فعالیت"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -478,6 +491,18 @@
|
||||
"button": "دانلود"
|
||||
}
|
||||
},
|
||||
"equalizer": {
|
||||
"description": "اضافه کردن یک اکولایزر به پخشکننده",
|
||||
"menu": {
|
||||
"presets": {
|
||||
"label": "تنظیمات از پیش تعیین شده",
|
||||
"list": {
|
||||
"bass-booster": "تقویتکننده باس صدا"
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": "اکولایزر"
|
||||
},
|
||||
"exponential-volume": {
|
||||
"description": "نوار لغزنده حجم را به صورت نمایی میسازد تا انتخاب حجمهای پایینتر آسانتر شود.",
|
||||
"name": "حجم نمایی"
|
||||
@ -490,17 +515,17 @@
|
||||
"name": "منوی داخل برنامه"
|
||||
},
|
||||
"lumiastream": {
|
||||
"description": "افزودن پشتیبانی از Lumia Stream",
|
||||
"description": "Lumia Stream افزودن پشتیبانی از",
|
||||
"name": "Lumia Stream [بتا]"
|
||||
},
|
||||
"lyrics-genius": {
|
||||
"description": "افزودن پشتیبانی از متن آهنگ برای بیشتر آهنگها",
|
||||
"description": "افزودن متن ترانه پشتیبان برای اکثر ترانه ها",
|
||||
"menu": {
|
||||
"romanized-lyrics": "متن رومیشده"
|
||||
"romanized-lyrics": "الفبای لاتین برای آهنگهایی با الفبای شرقی (فینگلیش)"
|
||||
},
|
||||
"name": "متن آهنگ Genius",
|
||||
"name": "Genius متن آهنگ",
|
||||
"renderer": {
|
||||
"fetched-lyrics": "متن آهنگ از Genius بازیابی شد"
|
||||
"fetched-lyrics": "بازیابی شد Genius متن ترانه توسط"
|
||||
}
|
||||
},
|
||||
"music-together": {
|
||||
@ -536,13 +561,13 @@
|
||||
"name": "Music Together [بتا]",
|
||||
"toast": {
|
||||
"add-song-failed": "افزودن آهنگ با شکست مواجه شد",
|
||||
"closed": "Music Together بسته شد",
|
||||
"disconnected": "قطع اتصال Music Together",
|
||||
"host-failed": "میزبانی Music Together با شکست مواجه شد",
|
||||
"closed": "بسته شد Music Together",
|
||||
"disconnected": "Music Together قطع اتصال",
|
||||
"host-failed": "با شکست مواجه شد Music Together میزبانی",
|
||||
"id-copied": "شناسه میزبان به کلیپبورد کپی شد",
|
||||
"id-copy-failed": "کپی شناسه میزبان به کلیپبورد با شکست مواجه شد",
|
||||
"join-failed": "پیوستن به Music Together با شکست مواجه شد",
|
||||
"joined": "به Music Together پیوست",
|
||||
"join-failed": "با شکست مواجه شد Music Together پیوستن به",
|
||||
"joined": "پیوست Music Together به",
|
||||
"permission-changed": "مجوز Music Together به \"{{permission}}\" تغییر یافت",
|
||||
"remove-song-failed": "حذف آهنگ با شکست مواجه شد",
|
||||
"user-connected": "{{name}} به Music Together پیوست",
|
||||
@ -551,11 +576,11 @@
|
||||
},
|
||||
"navigation": {
|
||||
"description": "بعدی/قبلی به طور مستقیم در رابط یکپارچه شدهاند، مانند مرورگر مورد علاقه شما",
|
||||
"name": "ناوبری"
|
||||
"name": "کنترل های رابط"
|
||||
},
|
||||
"no-google-login": {
|
||||
"description": "حذف دکمههای ورود به سیستم Google و لینکها از رابط",
|
||||
"name": "بدون ورود به Google"
|
||||
"description": "حذف دکمهها و لینکهای ورود به گوگل از رابط کاربری",
|
||||
"name": "بدون ورود به گوگل"
|
||||
},
|
||||
"notifications": {
|
||||
"description": "نمایش اعلان هنگامی که آهنگی شروع به پخش میکند (اعلانهای تعاملی در ویندوز در دسترس هستند)",
|
||||
@ -566,11 +591,11 @@
|
||||
"submenu": {
|
||||
"hide-button-text": "مخفی کردن متن دکمه",
|
||||
"refresh-on-play-pause": "تازهسازی در پخش/توقف",
|
||||
"tray-controls": "باز/بسته شدن با کلیک روی سینی"
|
||||
"tray-controls": "باز/بسته شدن با کلیک روی آیکون در نوار وظیفه"
|
||||
}
|
||||
},
|
||||
"priority": "اولویت اعلان",
|
||||
"toast-style": "سبک Toast",
|
||||
"toast-style": "Toast سبک",
|
||||
"unpause-notification": "نمایش اعلان هنگام از سرگیری پخش"
|
||||
},
|
||||
"name": "اعلانها"
|
||||
@ -578,8 +603,234 @@
|
||||
"picture-in-picture": {
|
||||
"description": "اجازه میدهد تا برنامه به حالت تصویر در تصویر تغییر کند",
|
||||
"menu": {
|
||||
"always-on-top": "همیشه در بالا"
|
||||
"always-on-top": "همیشه در بالا",
|
||||
"hotkey": {
|
||||
"label": "کلید میانبر",
|
||||
"prompt": {
|
||||
"keybind-options": {
|
||||
"hotkey": "کلید میانبر"
|
||||
},
|
||||
"label": "یک کلید میانبر انتخاب کنید برای فعال/غیرفعال کردن حالت تصویر در تصویر",
|
||||
"title": "کلید میانبر برای حالت تصویر در تصویر"
|
||||
}
|
||||
},
|
||||
"save-window-position": "ذخیره موقعیت پنجره",
|
||||
"save-window-size": "ذخیره اندازه پنجره",
|
||||
"use-native-pip": "استفاده از حالت تصویر در تصویر اصلی مرورگر"
|
||||
},
|
||||
"name": "تصویر در تصویر",
|
||||
"templates": {
|
||||
"button": "تصویر در تصویر"
|
||||
}
|
||||
},
|
||||
"playback-speed": {
|
||||
"description": "به سرعت گوش بده، به آرامی گوش بده! یک دکمه کشویی برای تنظیم سرعت آهنگ اضافه شد",
|
||||
"name": "سرعت پخش",
|
||||
"templates": {
|
||||
"button": "سرعت"
|
||||
}
|
||||
},
|
||||
"precise-volume": {
|
||||
"description": "کنترل دقیق صدا با استفاده از چرخ موس/میانبرها، همراه با HUD سفارشی و مراحل تنظیم حجم قابل تنظیم",
|
||||
"menu": {
|
||||
"arrows-shortcuts": "میانبرهای کلیدهای فلشی",
|
||||
"custom-volume-steps": "مراحل تنظیم صدای دلخواه",
|
||||
"global-shortcuts": "کلید های میانبر جهانی"
|
||||
},
|
||||
"name": "صدای دقیق",
|
||||
"prompt": {
|
||||
"global-shortcuts": {
|
||||
"keybind-options": {
|
||||
"decrease": "کاهش صدا",
|
||||
"increase": "افزایش صدا"
|
||||
},
|
||||
"label": "انتخاب کلیدهای میانبر سراسری صدا:",
|
||||
"title": "میانبرهای کلید سراسری صدا"
|
||||
},
|
||||
"volume-steps": {
|
||||
"label": "مراحل انتخاب افزایش/کاهش صدا",
|
||||
"title": "سطح صدا"
|
||||
}
|
||||
}
|
||||
},
|
||||
"quality-changer": {
|
||||
"backend": {
|
||||
"dialog": {
|
||||
"quality-changer": {
|
||||
"detail": "کیفیت کنونی: {{quality}}",
|
||||
"message": "انتخاب کیفیت ویدیو:",
|
||||
"title": "انتخاب کیفیت ویدیو"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "امکان تغییر کیفیت ویدیو با استفاده از دکمه در رابط پخش ویدیو",
|
||||
"name": "تغییر دهنده کیفیت ویدیو"
|
||||
},
|
||||
"scrobbler": {
|
||||
"description": "اضافه کردن پشتیبانی از اسکرابلینگ (etc. last.fm, Listenbrainz)",
|
||||
"dialog": {
|
||||
"lastfm": {
|
||||
"auth-failed": {
|
||||
"message": "احراز هویت با Last.fm ناموفق بود\nپنجره شناور را تا راهاندازی مجدد بعدی مخفی کن.",
|
||||
"title": "احراز هویت ناموفق بود"
|
||||
}
|
||||
}
|
||||
},
|
||||
"menu": {
|
||||
"lastfm": {
|
||||
"api-settings": "تنظیمات \"Last.fm \"API"
|
||||
},
|
||||
"listenbrainz": {
|
||||
"token": "توکن کاربری ListenBrainz را وارد کنید"
|
||||
},
|
||||
"scrobble-other-media": "ردیابی رسانههای دیگر"
|
||||
},
|
||||
"name": "ابزار ثبتکنندهی آهنگ",
|
||||
"prompt": {
|
||||
"lastfm": {
|
||||
"api-key": "کلید Last.fm API",
|
||||
"api-secret": "API مخفی Last.fm"
|
||||
},
|
||||
"listenbrainz": {
|
||||
"token": {
|
||||
"label": "توکن کاربری ListenBrainz خود را وارد کنید:",
|
||||
"title": "توکن ListenBrainz"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"shortcuts": {
|
||||
"description": "امکان تنظیم میانبرهای سراسری برای کنترل (پخش/توقف/بعدی/قبلی) و خاموش کردن OSD رسانه با بازنویسی کلیدهای رسانهای، فعالسازی Ctrl/CMD + F برای جستجو، فعالسازی پشتیبانی MPRIS در لینوکس برای کلیدهای رسانهای، و میانبرهای سفارشی برای کاربران پیشرفته",
|
||||
"menu": {
|
||||
"override-media-keys": "تغییر عملکرد کلیدهای رسانه",
|
||||
"set-keybinds": "تنظیم کنترلهای سراسری آهنگ"
|
||||
},
|
||||
"name": "میانبرها (& MPRIS)",
|
||||
"prompt": {
|
||||
"keybind": {
|
||||
"keybind-options": {
|
||||
"next": "بعدی",
|
||||
"play-pause": "پخش / توقف",
|
||||
"previous": "قبلی"
|
||||
},
|
||||
"label": "انتخاب میانبرهای سراسری برای کنترل آهنگها:",
|
||||
"title": "میانبرهای کلیدی سراسری"
|
||||
}
|
||||
}
|
||||
},
|
||||
"skip-disliked-songs": {
|
||||
"description": "خودکار آهنگ های غیر موردعلاقه رد میشن",
|
||||
"name": "رد آهنگهای غیر مورد علاقه"
|
||||
},
|
||||
"skip-silences": {
|
||||
"description": "رد خودکار بخشهای بی صدا آهنگ ها",
|
||||
"name": "رد بخشهای بیصدا"
|
||||
},
|
||||
"sponsorblock": {
|
||||
"description": "بهطور خودکار بخشهای غیرموسیقی مانند مقدمه/پایان یا قسمتهایی از ویدیوهای موسیقی که آهنگ در آن پخش نمیشود را رد میکند",
|
||||
"name": "مسدودکننده اسپانسر"
|
||||
},
|
||||
"synced-lyrics": {
|
||||
"description": "ارائه متن ترانهها به صورت هماهنگ با آهنگها، با استفاده از ارائهدهندگانی مانند LRClib.",
|
||||
"errors": {
|
||||
"fetch": "⚠️هنگام بارگیری متن ترانه خطایی رخ داده است.\n\tلطفاً بعداً دوباره تلاش کنید.",
|
||||
"not-found": "⚠️ متنی برای این ترانه پیدا نشد."
|
||||
},
|
||||
"menu": {
|
||||
"default-text-string": {
|
||||
"label": "حرف/کاراکتر پیشفرض بین متنهای ترانه",
|
||||
"tooltip": "حرف/کاراکتر پیشفرض را برای فاصله بین متنهای ترانه انتخاب کنید"
|
||||
},
|
||||
"line-effect": {
|
||||
"label": "افکت خط متن",
|
||||
"submenu": {
|
||||
"fancy": {
|
||||
"label": "شیک",
|
||||
"tooltip": "استفاده از افکتهای بزرگ و شبیه به اپلیکیشنها برای خط فعلی"
|
||||
},
|
||||
"focus": {
|
||||
"label": "تمرکز",
|
||||
"tooltip": "فقط خط فعلی رو سفید کن"
|
||||
},
|
||||
"offset": {
|
||||
"label": "جابجایی",
|
||||
"tooltip": "جابجایی خط فعلی به سمت راست"
|
||||
},
|
||||
"scale": {
|
||||
"label": "مقیاس",
|
||||
"tooltip": "تغییر اندازه خط فعلی"
|
||||
}
|
||||
},
|
||||
"tooltip": "افکت مورد نظر را برای خط فعلی انتخاب کنید"
|
||||
},
|
||||
"precise-timing": {
|
||||
"label": "هماهنگسازی کامل متن ترانه",
|
||||
"tooltip": "محاسبه دقیق نمایش خط بعدی تا میلیثانیه (ممکن است تاثیر کمی بر عملکرد داشته باشد)"
|
||||
},
|
||||
"show-lyrics-even-if-inexact": {
|
||||
"label": "نمایش متن ترانه ها حتی اگر دقیق نباشد",
|
||||
"tooltip": "اگر آهنگ پیدا نشد، افزونه دوباره با یک جستجوی متفاوت امتحان میکند.\nنتیجهی این تلاش ممکن است دقیق نباشد."
|
||||
},
|
||||
"show-time-codes": {
|
||||
"label": "نمایش زمانبندیها",
|
||||
"tooltip": "نمایش زمانبندیها کنار متن ترانه"
|
||||
}
|
||||
},
|
||||
"name": "متن ترانه هماهنگ شد",
|
||||
"refetch-btn": {
|
||||
"fetching": "در حال بارگذاری...",
|
||||
"normal": "دریافت مجدد متن ترانه"
|
||||
},
|
||||
"warnings": {
|
||||
"duration-mismatch": "⚠️ - ممکن است متن ترانه به دلیل عدم تطابق زمان با مشکل هماهنگی مواجه شود.",
|
||||
"inexact": "⚠️ - ممکن است متن ترانه برای این آهنگ دقیق نباشد",
|
||||
"instrumental": "⚠️ - این آهنگ بی کلام است"
|
||||
}
|
||||
},
|
||||
"taskbar-mediacontrol": {
|
||||
"description": "کنترل پخش از نوار وظیفه ویندوز(taskbar)",
|
||||
"name": "کنترل رسانه از نوار وظیفه (taskbar)"
|
||||
},
|
||||
"touchbar": {
|
||||
"description": "افزودن ویجت TouchBar برای کاربران macOS",
|
||||
"name": "نوار لمسی"
|
||||
},
|
||||
"tuna-obs": {
|
||||
"description": "ادغام با پلاگین Tuna در OBS",
|
||||
"name": "Tuna OBS"
|
||||
},
|
||||
"video-toggle": {
|
||||
"description": "دکمهای اضافه میکند برای جابجایی بین حالت ویدیو/آهنگ. همچنین به صورت اختیاری میتواند تب ویدیو را حذف کند",
|
||||
"menu": {
|
||||
"align": {
|
||||
"label": "چینش",
|
||||
"submenu": {
|
||||
"left": "چپ",
|
||||
"middle": "میانه",
|
||||
"right": "راست"
|
||||
}
|
||||
},
|
||||
"force-hide": "حذف اجباری تب ویدیو",
|
||||
"mode": {
|
||||
"label": "حالت",
|
||||
"submenu": {
|
||||
"custom": "حالت شخصیسازی شده",
|
||||
"disabled": "غیرفعال",
|
||||
"native": "حالت پیشفرض"
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": "ویدیو به آهنگ",
|
||||
"templates": {
|
||||
"button": "ترانه"
|
||||
}
|
||||
},
|
||||
"visualizer": {
|
||||
"description": "اضافه کردن نمایشدهنده تصویری به پخشکننده",
|
||||
"menu": {
|
||||
"visualizer-type": "نوع نمایشدهنده تصویری"
|
||||
},
|
||||
"name": "نمایشدهنده تصویری"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -279,6 +279,12 @@
|
||||
},
|
||||
"name": "Ambient Mode"
|
||||
},
|
||||
"amuse": {
|
||||
"description": "Nagdaragdag ng suporta sa YouTube Music para sa Amuse now playing widget ng 6K Labs",
|
||||
"response": {
|
||||
"query": "Tumatakbo ang Amuse API server. Gamitin ang GET /query para makuha ang impo ng kanta."
|
||||
}
|
||||
},
|
||||
"api-server": {
|
||||
"description": "Nagdadagdag ng API Server upang kontrolin ang player",
|
||||
"dialog": {
|
||||
@ -468,6 +474,14 @@
|
||||
"button": "Mag-download"
|
||||
}
|
||||
},
|
||||
"equalizer": {
|
||||
"description": "Nagdaragdag ng equalizer sa player",
|
||||
"menu": {
|
||||
"presets": {
|
||||
"label": "Mga Preset"
|
||||
}
|
||||
}
|
||||
},
|
||||
"exponential-volume": {
|
||||
"description": "Ginagawang exponential ang volume slider para mas madaling pumili ng mas mababang volume."
|
||||
},
|
||||
@ -674,8 +688,8 @@
|
||||
"synced-lyrics": {
|
||||
"description": "Nagbibigay ng naka-sync na lyrics sa mga kanta, gamit ang mga provider tulad ng LRClib.",
|
||||
"errors": {
|
||||
"fetch": "⚠️ - Nagkaroon ng error habang kinukuha ang lyrics. Subukang muli mamaya.",
|
||||
"not-found": "⚠️ - Walang nakitang lyrics para sa kantang ito."
|
||||
"fetch": "⚠️\t Nagkaroon ng error habang kinukuha ang lyrics.\n\t Subukang muli mamaya.",
|
||||
"not-found": "⚠️ Walang nakitang lyrics para sa kantang ito."
|
||||
},
|
||||
"menu": {
|
||||
"default-text-string": {
|
||||
@ -685,6 +699,10 @@
|
||||
"line-effect": {
|
||||
"label": "Effect ng Linya",
|
||||
"submenu": {
|
||||
"fancy": {
|
||||
"label": "Magarbo",
|
||||
"tooltip": "Gumamit ng malaki, mala-app na effect sa kasalukuyang linya"
|
||||
},
|
||||
"focus": {
|
||||
"tooltip": "Gawing puti lamang ang kasalukuyang linya"
|
||||
},
|
||||
|
||||
@ -53,7 +53,8 @@
|
||||
"later": "אחר כך",
|
||||
"restart-now": "מתחיל את התוכנה מחדש עכשיו"
|
||||
},
|
||||
"message": "נדרש אתחול",
|
||||
"detail": "\"{{pluginName}}\" מצריך אתחול",
|
||||
"message": "\"{{pluginName}}\" דורש אתחול",
|
||||
"title": "נדרשת הפעלה מחדש"
|
||||
},
|
||||
"unresponsive": {
|
||||
@ -70,9 +71,10 @@
|
||||
"buttons": {
|
||||
"disable": "בטל עדכונים",
|
||||
"download": "הורדה",
|
||||
"ok": "אוקי"
|
||||
"ok": "אוקיי"
|
||||
},
|
||||
"message": "גירסא חדשה זמינה כעת",
|
||||
"detail": "גרסה חדשה זמינה, ניתן להוריד אותה ב-{{downloadLink}}",
|
||||
"message": "גירסה חדשה זמינה כעת",
|
||||
"title": "קיים עדכון חדש"
|
||||
}
|
||||
},
|
||||
|
||||
@ -226,10 +226,50 @@
|
||||
"description": "एल्बम रंग पैलेट के आधार पर एक गतिशील थीम और दृश्य प्रभाव लागू करता है",
|
||||
"menu": {
|
||||
"color-mix-ratio": {
|
||||
"label": "रंग मिश्रण अनुपात",
|
||||
"submenu": {
|
||||
"percent": "{{ratio}}%"
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": "एल्बम रंग थीम"
|
||||
},
|
||||
"ambient-mode": {
|
||||
"description": "वीडियो से हल्के रंगों को आपकी स्क्रीन की पृष्ठभूमि में डालकर एक प्रकाश प्रभाव लागू करता है",
|
||||
"menu": {
|
||||
"blur-amount": {
|
||||
"label": "धुंधलापन मात्रा",
|
||||
"submenu": {
|
||||
"pixels": "{{blurAmount}} पिक्सल"
|
||||
}
|
||||
},
|
||||
"buffer": {
|
||||
"label": "बफर",
|
||||
"submenu": {
|
||||
"buffer": "{{buffer}}"
|
||||
}
|
||||
},
|
||||
"opacity": {
|
||||
"label": "अस्पष्टता",
|
||||
"submenu": {
|
||||
"percent": "{{opacity}}%"
|
||||
}
|
||||
},
|
||||
"quality": {
|
||||
"label": "गुणवत्ता",
|
||||
"submenu": {
|
||||
"pixels": "{{quality}} पिक्सल"
|
||||
}
|
||||
},
|
||||
"size": {
|
||||
"label": "माप",
|
||||
"submenu": {
|
||||
"percent": "{{size}}%"
|
||||
}
|
||||
},
|
||||
"smoothness-transition": {
|
||||
"label": "चिकनाई संक्रमण"
|
||||
}
|
||||
}
|
||||
},
|
||||
"video-toggle": {
|
||||
|
||||
@ -279,6 +279,13 @@
|
||||
},
|
||||
"name": "Mode ambient"
|
||||
},
|
||||
"amuse": {
|
||||
"description": "Menambahkan dukungan YouTube Music untuk widget Amuse yang sedang diputar oleh 6K Labs",
|
||||
"name": "Amuse",
|
||||
"response": {
|
||||
"query": "Server API Amuse sedang berjalan. GET /query untuk mendapatkan info lagu."
|
||||
}
|
||||
},
|
||||
"api-server": {
|
||||
"description": "Menambahkan server API untuk mengontrol pemutar",
|
||||
"dialog": {
|
||||
@ -726,8 +733,8 @@
|
||||
"synced-lyrics": {
|
||||
"description": "Menyediakan lirik lagu yang disinkronkan, menggunakan penyedia seperti LRClib.",
|
||||
"errors": {
|
||||
"fetch": "⚠️ - Terjadi kesalahan saat mengambil lirik. Coba lagi nanti.",
|
||||
"not-found": "⚠️ - Tidak ada lirik yang ditemukan untuk lagu ini."
|
||||
"fetch": "⚠️\tTerjadi kesalahan saat mengambil lirik.\n\tSilakan coba lagi nanti.",
|
||||
"not-found": "⚠️ Tidak ada lirik yang ditemukan untuk lagu ini."
|
||||
},
|
||||
"menu": {
|
||||
"default-text-string": {
|
||||
@ -737,6 +744,10 @@
|
||||
"line-effect": {
|
||||
"label": "Efek garis",
|
||||
"submenu": {
|
||||
"fancy": {
|
||||
"label": "Mewah",
|
||||
"tooltip": "Gunakan efek besar seperti aplikasi pada baris saat ini"
|
||||
},
|
||||
"focus": {
|
||||
"label": "Fokus",
|
||||
"tooltip": "Jadikan hanya baris saat ini berwarna putih"
|
||||
|
||||
@ -279,6 +279,12 @@
|
||||
},
|
||||
"name": "Chế độ Môi trường xung quanh"
|
||||
},
|
||||
"amuse": {
|
||||
"name": "Amuse",
|
||||
"response": {
|
||||
"query": "Máy chủ API của Amuse đang chạy. GET /query để lấy thông tin về bài hát."
|
||||
}
|
||||
},
|
||||
"api-server": {
|
||||
"description": "Thêm máy chủ API để điều khiển trình phát",
|
||||
"dialog": {
|
||||
@ -299,7 +305,7 @@
|
||||
"label": "Xác thực ngay yêu cầu đầu tiên"
|
||||
},
|
||||
"none": {
|
||||
"label": "Không/Chưa xác thực (Need context)"
|
||||
"label": "Không xác thực"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -134,14 +134,6 @@ if (is.linux()) {
|
||||
// Overrides WM_CLASS for X11 to correspond to icon filename
|
||||
app.setName('com.github.th_ch.youtube_music');
|
||||
|
||||
// Workaround for issue #2248
|
||||
if (
|
||||
process.env.XDG_SESSION_TYPE === 'wayland' ||
|
||||
process.env.WAYLAND_DISPLAY
|
||||
) {
|
||||
app.commandLine.appendSwitch('disable-gpu-memory-buffer-video-frames');
|
||||
}
|
||||
|
||||
// Stops chromium from launching its own MPRIS service
|
||||
if (config.plugins.isEnabled('shortcuts')) {
|
||||
app.commandLine.appendSwitch('disable-features', 'MediaSessionService');
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { FastAverageColor } from 'fast-average-color';
|
||||
import Color from 'color';
|
||||
import Color, { ColorInstance } from 'color';
|
||||
|
||||
import style from './style.css?inline';
|
||||
|
||||
@ -14,8 +14,8 @@ export default createPlugin<
|
||||
unknown,
|
||||
unknown,
|
||||
{
|
||||
color?: Color;
|
||||
darkColor?: Color;
|
||||
color?: ColorInstance;
|
||||
darkColor?: ColorInstance;
|
||||
|
||||
playerPage: HTMLElement | null;
|
||||
navBarBackground: HTMLElement | null;
|
||||
|
||||
@ -154,15 +154,14 @@ export const backend = createBackend<
|
||||
// @see https://discord.com/developers/docs/topics/gateway#activity-object
|
||||
// not all options are transfered through https://github.com/discordjs/RPC/blob/6f83d8d812c87cb7ae22064acd132600407d7d05/src/client.js#L518-530
|
||||
const hangulFillerUnicodeCharacter = '\u3164'; // This is an empty character
|
||||
if (songInfo.title.length < 2) {
|
||||
songInfo.title += hangulFillerUnicodeCharacter.repeat(
|
||||
2 - songInfo.title.length,
|
||||
);
|
||||
}
|
||||
if (songInfo.artist.length < 2) {
|
||||
songInfo.artist += hangulFillerUnicodeCharacter.repeat(
|
||||
2 - songInfo.title.length,
|
||||
);
|
||||
const paddedInfoKeys: (keyof SongInfo)[] = ['title', 'artist', 'album'];
|
||||
for (const key of paddedInfoKeys) {
|
||||
const keyLength = (songInfo[key] as string)?.length;
|
||||
if (keyLength < 2) {
|
||||
(songInfo[key] as string) += hangulFillerUnicodeCharacter.repeat(
|
||||
2 - keyLength,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// see https://github.com/th-ch/youtube-music/issues/1664
|
||||
|
||||
@ -55,10 +55,20 @@ let yt: Innertube;
|
||||
let win: BrowserWindow;
|
||||
let playingUrl: string;
|
||||
|
||||
const isYouTubePremium = () =>
|
||||
win.webContents.executeJavaScript(
|
||||
'!document.querySelector(\'#endpoint[href="/music_premium"]\')',
|
||||
) as Promise<boolean>;
|
||||
const isYouTubeMusicPremium = async () => {
|
||||
const upgradeBtnIconPathData = (await win.webContents.executeJavaScript(
|
||||
'document.querySelector(\'iron-iconset-svg[name="yt-sys-icons"] #youtube_music_monochrome\')?.firstChild?.getAttribute("d")?.substring(0, 15)',
|
||||
)) as string | null;
|
||||
|
||||
// Fallback to non-premium if the icon is not found
|
||||
if (!upgradeBtnIconPathData) return false;
|
||||
|
||||
const selector = `ytmusic-guide-entry-renderer:has(> tp-yt-paper-item > yt-icon path[d^="${upgradeBtnIconPathData}"])`;
|
||||
|
||||
return (await win.webContents.executeJavaScript(
|
||||
`!document.querySelector('${selector}')`,
|
||||
)) as boolean;
|
||||
};
|
||||
|
||||
const sendError = (error: Error, source?: string) => {
|
||||
win.setProgressBar(-1); // Close progress bar
|
||||
@ -116,6 +126,11 @@ export const onMainLoad = async ({
|
||||
const visitorData = yt.session.context.client.visitorData;
|
||||
|
||||
if (visitorData) {
|
||||
const cleanUp = (context: Partial<typeof globalThis>) => {
|
||||
delete context.window;
|
||||
delete context.document;
|
||||
};
|
||||
|
||||
try {
|
||||
const [width, height] = win.getSize();
|
||||
// emulate jsdom using linkedom
|
||||
@ -153,16 +168,16 @@ export const onMainLoad = async ({
|
||||
program: bgChallenge.program,
|
||||
globalName: bgChallenge.globalName,
|
||||
bgConfig,
|
||||
}).finally(() => {
|
||||
cleanUp(globalThis);
|
||||
});
|
||||
|
||||
yt.session.po_token = poTokenResult.poToken;
|
||||
} else {
|
||||
cleanUp(globalThis);
|
||||
}
|
||||
} finally {
|
||||
// Bypass TypeScript checks
|
||||
((x: Partial<typeof globalThis>) => {
|
||||
delete x.window;
|
||||
delete x.document;
|
||||
})(globalThis);
|
||||
} catch {
|
||||
cleanUp(globalThis);
|
||||
}
|
||||
}
|
||||
|
||||
@ -365,7 +380,7 @@ async function downloadSongUnsafe(
|
||||
}
|
||||
|
||||
const downloadOptions: FormatOptions = {
|
||||
type: (await isYouTubePremium()) ? 'audio' : 'video+audio', // Audio, video or video+audio
|
||||
type: (await isYouTubeMusicPremium()) ? 'audio' : 'video+audio', // Audio, video or video+audio
|
||||
quality: 'best', // Best, bestefficiency, 144p, 240p, 480p, 720p and so on.
|
||||
format: 'any', // Media container format
|
||||
};
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
align-items: center;
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
cursor: pointer;
|
||||
margin: 0 var(--ytd-rich-grid-item-margin);
|
||||
margin: 0 var(--ytd-margin-2x, 8px);
|
||||
}
|
||||
|
||||
.navigation-item:hover {
|
||||
@ -32,4 +32,5 @@
|
||||
width: var(--iron-icon-width, 24px);
|
||||
height: var(--iron-icon-height, 24px);
|
||||
animation: var(--iron-icon_-_animation);
|
||||
padding: var(--ytd-margin-base, 4px) var(--ytd-margin-2x, 8px);
|
||||
}
|
||||
|
||||
@ -50,7 +50,7 @@ export const SyncedLine = ({ line }: SyncedLineProps) => {
|
||||
_ytAPI?.seekTo(line.timeInMs / 1000);
|
||||
}}
|
||||
>
|
||||
<div class="text-lyrics description ytmusic-description-shelf-renderer">
|
||||
<div dir="auto" class="text-lyrics description ytmusic-description-shelf-renderer">
|
||||
<yt-formatted-string
|
||||
text={{
|
||||
runs: [{ text: config()?.showTimeCodes ? `[${line.time}] ` : '' }],
|
||||
|
||||
@ -71,7 +71,7 @@ export function throttle<T extends (...params: unknown[]) => unknown>(
|
||||
}) as T;
|
||||
}
|
||||
|
||||
function memoize<T extends (...params: unknown[]) => unknown>(fn: T): T {
|
||||
export function memoize<T extends (...params: unknown[]) => unknown>(fn: T): T {
|
||||
const cache = new Map();
|
||||
|
||||
return ((...args) => {
|
||||
@ -84,7 +84,7 @@ function memoize<T extends (...params: unknown[]) => unknown>(fn: T): T {
|
||||
}) as T;
|
||||
}
|
||||
|
||||
function retry<T extends (...params: unknown[]) => Promise<unknown>>(
|
||||
export function retry<T extends (...params: unknown[]) => Promise<unknown>>(
|
||||
fn: T,
|
||||
{ retries = 3, delay = 1000 } = {},
|
||||
) {
|
||||
@ -102,12 +102,3 @@ function retry<T extends (...params: unknown[]) => Promise<unknown>>(
|
||||
throw latestError;
|
||||
};
|
||||
}
|
||||
|
||||
export default {
|
||||
singleton,
|
||||
debounce,
|
||||
cache,
|
||||
throttle,
|
||||
memoize,
|
||||
retry,
|
||||
};
|
||||
|
||||
@ -7,15 +7,15 @@ import { Project } from 'ts-morph';
|
||||
const snakeToCamel = (text: string) =>
|
||||
text.replace(/-(\w)/g, (_, letter: string) => letter.toUpperCase());
|
||||
|
||||
export const i18nImporter = () => {
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const project = new Project({
|
||||
tsConfigFilePath: resolve(__dirname, '..', 'tsconfig.json'),
|
||||
skipAddingFilesFromTsConfig: true,
|
||||
skipLoadingLibFiles: true,
|
||||
skipFileDependencyResolution: true,
|
||||
});
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const globalProject = new Project({
|
||||
tsConfigFilePath: resolve(__dirname, '..', 'tsconfig.json'),
|
||||
skipAddingFilesFromTsConfig: true,
|
||||
skipLoadingLibFiles: true,
|
||||
skipFileDependencyResolution: true,
|
||||
});
|
||||
|
||||
export const i18nImporter = () => {
|
||||
const srcPath = resolve(__dirname, '..', 'src');
|
||||
const plugins = globSync(['src/i18n/resources/*.json']).map((path) => {
|
||||
const nameWithExt = basename(path);
|
||||
@ -24,24 +24,28 @@ export const i18nImporter = () => {
|
||||
return { name, path };
|
||||
});
|
||||
|
||||
const src = project.createSourceFile('vm:i18n', (writer) => {
|
||||
// prettier-ignore
|
||||
for (const { name, path } of plugins) {
|
||||
const src = globalProject.createSourceFile(
|
||||
'vm:i18n',
|
||||
(writer) => {
|
||||
// prettier-ignore
|
||||
for (const { name, path } of plugins) {
|
||||
const relativePath = relative(resolve(srcPath, '..'), path).replace(/\\/g, '/');
|
||||
writer.writeLine(`import ${snakeToCamel(name)}Json from "./${relativePath}";`);
|
||||
}
|
||||
|
||||
writer.blankLine();
|
||||
writer.blankLine();
|
||||
|
||||
writer.writeLine('export const languageResources = {');
|
||||
for (const { name } of plugins) {
|
||||
writer.writeLine(` "${name}": {`);
|
||||
writer.writeLine(` translation: ${snakeToCamel(name)}Json,`);
|
||||
writer.writeLine(' },');
|
||||
}
|
||||
writer.writeLine('};');
|
||||
writer.blankLine();
|
||||
});
|
||||
writer.writeLine('export const languageResources = {');
|
||||
for (const { name } of plugins) {
|
||||
writer.writeLine(` "${name}": {`);
|
||||
writer.writeLine(` translation: ${snakeToCamel(name)}Json,`);
|
||||
writer.writeLine(' },');
|
||||
}
|
||||
writer.writeLine('};');
|
||||
writer.blankLine();
|
||||
},
|
||||
{ overwrite: true },
|
||||
);
|
||||
|
||||
return src.getText();
|
||||
};
|
||||
|
||||
@ -7,17 +7,17 @@ import { Project } from 'ts-morph';
|
||||
const snakeToCamel = (text: string) =>
|
||||
text.replace(/-(\w)/g, (_, letter: string) => letter.toUpperCase());
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const globalProject = new Project({
|
||||
tsConfigFilePath: resolve(__dirname, '..', 'tsconfig.json'),
|
||||
skipAddingFilesFromTsConfig: true,
|
||||
skipLoadingLibFiles: true,
|
||||
skipFileDependencyResolution: true,
|
||||
});
|
||||
|
||||
export const pluginVirtualModuleGenerator = (
|
||||
mode: 'main' | 'preload' | 'renderer',
|
||||
) => {
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const project = new Project({
|
||||
tsConfigFilePath: resolve(__dirname, '..', 'tsconfig.json'),
|
||||
skipAddingFilesFromTsConfig: true,
|
||||
skipLoadingLibFiles: true,
|
||||
skipFileDependencyResolution: true,
|
||||
});
|
||||
|
||||
const srcPath = resolve(__dirname, '..', 'src');
|
||||
const plugins = globSync([
|
||||
'src/plugins/*/index.{js,ts}',
|
||||
@ -35,35 +35,39 @@ export const pluginVirtualModuleGenerator = (
|
||||
return { name, path };
|
||||
});
|
||||
|
||||
const src = project.createSourceFile('vm:pluginIndexes', (writer) => {
|
||||
// prettier-ignore
|
||||
for (const { name, path } of plugins) {
|
||||
const src = globalProject.createSourceFile(
|
||||
'vm:pluginIndexes',
|
||||
(writer) => {
|
||||
// prettier-ignore
|
||||
for (const { name, path } of plugins) {
|
||||
const relativePath = relative(resolve(srcPath, '..'), path).replace(/\\/g, '/');
|
||||
writer.writeLine(`import ${snakeToCamel(name)}Plugin, { pluginStub as ${snakeToCamel(name)}PluginStub } from "./${relativePath}";`);
|
||||
}
|
||||
|
||||
writer.blankLine();
|
||||
writer.blankLine();
|
||||
|
||||
// Context-specific exports
|
||||
writer.writeLine(`export const ${mode}Plugins = {`);
|
||||
for (const { name } of plugins) {
|
||||
const checkMode = mode === 'main' ? 'backend' : mode;
|
||||
// HACK: To avoid situation like importing renderer plugins in main
|
||||
writer.writeLine(
|
||||
` ...(${snakeToCamel(name)}Plugin['${checkMode}'] ? { "${name}": ${snakeToCamel(name)}Plugin } : {}),`,
|
||||
);
|
||||
}
|
||||
writer.writeLine('};');
|
||||
writer.blankLine();
|
||||
// Context-specific exports
|
||||
writer.writeLine(`export const ${mode}Plugins = {`);
|
||||
for (const { name } of plugins) {
|
||||
const checkMode = mode === 'main' ? 'backend' : mode;
|
||||
// HACK: To avoid situation like importing renderer plugins in main
|
||||
writer.writeLine(
|
||||
` ...(${snakeToCamel(name)}Plugin['${checkMode}'] ? { "${name}": ${snakeToCamel(name)}Plugin } : {}),`,
|
||||
);
|
||||
}
|
||||
writer.writeLine('};');
|
||||
writer.blankLine();
|
||||
|
||||
// All plugins export (stub only) // Omit<Plugin, 'backend' | 'preload' | 'renderer'>
|
||||
writer.writeLine('export const allPlugins = {');
|
||||
for (const { name } of plugins) {
|
||||
writer.writeLine(` "${name}": ${snakeToCamel(name)}PluginStub,`);
|
||||
}
|
||||
writer.writeLine('};');
|
||||
writer.blankLine();
|
||||
});
|
||||
// All plugins export (stub only) // Omit<Plugin, 'backend' | 'preload' | 'renderer'>
|
||||
writer.writeLine('export const allPlugins = {');
|
||||
for (const { name } of plugins) {
|
||||
writer.writeLine(` "${name}": ${snakeToCamel(name)}PluginStub,`);
|
||||
}
|
||||
writer.writeLine('};');
|
||||
writer.blankLine();
|
||||
},
|
||||
{ overwrite: true },
|
||||
);
|
||||
|
||||
return src.getText();
|
||||
};
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { readFile } from 'node:fs/promises';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { resolve, basename, dirname } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
@ -8,10 +8,34 @@ import {
|
||||
ts,
|
||||
ObjectLiteralExpression,
|
||||
VariableDeclarationKind,
|
||||
Node,
|
||||
type ObjectLiteralElementLike,
|
||||
} from 'ts-morph';
|
||||
|
||||
import type { PluginOption } from 'vite';
|
||||
|
||||
// Initialize a global project instance to reuse across load calls
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const globalProject = new Project({
|
||||
tsConfigFilePath: resolve(__dirname, '..', 'tsconfig.json'),
|
||||
skipAddingFilesFromTsConfig: true,
|
||||
skipLoadingLibFiles: true,
|
||||
skipFileDependencyResolution: true,
|
||||
});
|
||||
|
||||
// Helper to extract a property’s name from its node
|
||||
const getPropertyName = (prop: Node): string | null => {
|
||||
const kind = prop.getKind();
|
||||
if (
|
||||
kind === ts.SyntaxKind.PropertyAssignment ||
|
||||
kind === ts.SyntaxKind.ShorthandPropertyAssignment ||
|
||||
kind === ts.SyntaxKind.MethodDeclaration
|
||||
) {
|
||||
return prop.getFirstChildByKindOrThrow(ts.SyntaxKind.Identifier).getText();
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
export default function (
|
||||
mode: 'backend' | 'preload' | 'renderer' | 'none',
|
||||
): PluginOption {
|
||||
@ -22,131 +46,96 @@ export default function (
|
||||
|
||||
return {
|
||||
name: 'ytm-plugin-loader',
|
||||
async load(id) {
|
||||
load(id) {
|
||||
if (!pluginFilter(id)) return null;
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
const project = new Project({
|
||||
tsConfigFilePath: resolve(__dirname, '..', 'tsconfig.json'),
|
||||
skipAddingFilesFromTsConfig: true,
|
||||
skipLoadingLibFiles: true,
|
||||
skipFileDependencyResolution: true,
|
||||
});
|
||||
|
||||
const src = project.createSourceFile(
|
||||
// Read file asynchronously
|
||||
const fileContent = readFileSync(id, 'utf8');
|
||||
// Create or update source file in the global project instance
|
||||
const src = globalProject.createSourceFile(
|
||||
'_pf' + basename(id),
|
||||
await readFile(id, 'utf8'),
|
||||
fileContent,
|
||||
{ overwrite: true },
|
||||
);
|
||||
|
||||
const exports = src.getExportedDeclarations();
|
||||
let objExpr: ObjectLiteralExpression | undefined = undefined;
|
||||
let objExpr: ObjectLiteralExpression | undefined;
|
||||
|
||||
for (const [name, [expr]] of exports) {
|
||||
if (name !== 'default') continue;
|
||||
|
||||
switch (expr.getKind()) {
|
||||
case ts.SyntaxKind.ObjectLiteralExpression: {
|
||||
objExpr = expr.asKindOrThrow(ts.SyntaxKind.ObjectLiteralExpression);
|
||||
break;
|
||||
}
|
||||
case ts.SyntaxKind.CallExpression: {
|
||||
const callExpr = expr.asKindOrThrow(ts.SyntaxKind.CallExpression);
|
||||
if (callExpr.getArguments().length !== 1) continue;
|
||||
|
||||
const name = callExpr.getExpression().getText();
|
||||
if (name !== 'createPlugin') continue;
|
||||
// Identify the default export as an object literal, or via a 'createPlugin' call
|
||||
for (const [exportName, declarations] of exports) {
|
||||
if (exportName !== 'default') continue;
|
||||
const expr = declarations[0];
|
||||
|
||||
const exprKind = expr.getKind();
|
||||
if (exprKind === ts.SyntaxKind.ObjectLiteralExpression) {
|
||||
objExpr = expr.asKindOrThrow(ts.SyntaxKind.ObjectLiteralExpression);
|
||||
break;
|
||||
} else if (exprKind === ts.SyntaxKind.CallExpression) {
|
||||
const callExpr = expr.asKindOrThrow(ts.SyntaxKind.CallExpression);
|
||||
if (
|
||||
callExpr.getArguments().length === 1 &&
|
||||
callExpr.getExpression().getText() === 'createPlugin'
|
||||
) {
|
||||
const arg = callExpr.getArguments()[0];
|
||||
if (arg.getKind() !== ts.SyntaxKind.ObjectLiteralExpression)
|
||||
continue;
|
||||
|
||||
objExpr = arg.asKindOrThrow(ts.SyntaxKind.ObjectLiteralExpression);
|
||||
break;
|
||||
if (arg.getKind() === ts.SyntaxKind.ObjectLiteralExpression) {
|
||||
objExpr = arg.asKindOrThrow(
|
||||
ts.SyntaxKind.ObjectLiteralExpression,
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!objExpr) return null;
|
||||
|
||||
const properties = objExpr.getProperties();
|
||||
const propertyNames = properties.map((prop) => {
|
||||
switch (prop.getKind()) {
|
||||
case ts.SyntaxKind.PropertyAssignment:
|
||||
return prop
|
||||
.asKindOrThrow(ts.SyntaxKind.PropertyAssignment)
|
||||
.getName();
|
||||
case ts.SyntaxKind.ShorthandPropertyAssignment:
|
||||
return prop
|
||||
.asKindOrThrow(ts.SyntaxKind.ShorthandPropertyAssignment)
|
||||
.getName();
|
||||
case ts.SyntaxKind.MethodDeclaration:
|
||||
return prop
|
||||
.asKindOrThrow(ts.SyntaxKind.MethodDeclaration)
|
||||
.getName();
|
||||
default:
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
});
|
||||
// Build a map of property names to their AST nodes for fast lookup
|
||||
const propMap = new Map<string, ObjectLiteralElementLike>();
|
||||
for (const prop of objExpr.getProperties()) {
|
||||
const name = getPropertyName(prop);
|
||||
if (name) propMap.set(name, prop);
|
||||
}
|
||||
|
||||
const contexts = ['backend', 'preload', 'renderer', 'menu'];
|
||||
for (const ctx of contexts) {
|
||||
if (mode === 'none') {
|
||||
const index = propertyNames.indexOf(ctx);
|
||||
if (index === -1) continue;
|
||||
|
||||
objExpr.getProperty(propertyNames[index])?.remove();
|
||||
if (mode === 'none' && propMap.has(ctx)) {
|
||||
propMap.get(ctx)?.remove();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ctx === mode) continue;
|
||||
if (ctx === 'menu' && mode === 'backend') continue;
|
||||
|
||||
const index = propertyNames.indexOf(ctx);
|
||||
if (index === -1) continue;
|
||||
|
||||
objExpr.getProperty(propertyNames[index])?.remove();
|
||||
if (ctx === mode || (ctx === 'menu' && mode === 'backend')) continue;
|
||||
if (propMap.has(ctx)) propMap.get(ctx)?.remove();
|
||||
}
|
||||
|
||||
const stubObjExpr = src
|
||||
.addVariableStatement({
|
||||
isExported: true,
|
||||
declarationKind: VariableDeclarationKind.Const,
|
||||
declarations: [
|
||||
{
|
||||
name: 'pluginStub',
|
||||
initializer: (writer) => writer.write(objExpr.getText()),
|
||||
},
|
||||
],
|
||||
})
|
||||
.getDeclarations()[0]
|
||||
.getInitializer() as ObjectLiteralExpression;
|
||||
|
||||
const stubProperties = stubObjExpr.getProperties();
|
||||
const stubPropertyNames = stubProperties.map((prop) => {
|
||||
switch (prop.getKind()) {
|
||||
case ts.SyntaxKind.PropertyAssignment:
|
||||
return prop
|
||||
.asKindOrThrow(ts.SyntaxKind.PropertyAssignment)
|
||||
.getName();
|
||||
case ts.SyntaxKind.ShorthandPropertyAssignment:
|
||||
return prop
|
||||
.asKindOrThrow(ts.SyntaxKind.ShorthandPropertyAssignment)
|
||||
.getName();
|
||||
case ts.SyntaxKind.MethodDeclaration:
|
||||
return prop
|
||||
.asKindOrThrow(ts.SyntaxKind.MethodDeclaration)
|
||||
.getName();
|
||||
default:
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
// Add an exported variable 'pluginStub' with the modified object literal's text
|
||||
const varStmt = src.addVariableStatement({
|
||||
isExported: true,
|
||||
declarationKind: VariableDeclarationKind.Const,
|
||||
declarations: [
|
||||
{
|
||||
name: 'pluginStub',
|
||||
initializer: (writer) => writer.write(objExpr.getText()),
|
||||
},
|
||||
],
|
||||
});
|
||||
const stubObjExpr = varStmt
|
||||
.getDeclarations()[0]
|
||||
.getInitializerIfKindOrThrow(ts.SyntaxKind.ObjectLiteralExpression);
|
||||
|
||||
if (mode === 'backend') contexts.pop();
|
||||
for (const ctx of contexts) {
|
||||
const index = stubPropertyNames.indexOf(ctx);
|
||||
if (index === -1) continue;
|
||||
// Similarly build a map for the stub properties
|
||||
const stubMap = new Map<string, ObjectLiteralElementLike>();
|
||||
for (const prop of stubObjExpr.getProperties()) {
|
||||
const name = getPropertyName(prop);
|
||||
if (name) stubMap.set(name, prop);
|
||||
}
|
||||
|
||||
stubObjExpr.getProperty(stubPropertyNames[index])?.remove();
|
||||
const stubContexts =
|
||||
mode === 'backend'
|
||||
? contexts.filter((ctx) => ctx !== 'backend')
|
||||
: contexts;
|
||||
for (const ctx of stubContexts) {
|
||||
if (stubMap.has(ctx)) {
|
||||
stubMap.get(ctx)?.remove();
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user