Compare commits

...

42 Commits

Author SHA1 Message Date
0747c43128 fix(deps): update dependency @ffmpeg.wasm/core-mt to v0.13.2 2025-12-20 09:51:28 +00:00
99be7c2629 chore(deps): update dependency typescript to v5.9.3 (#4171)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-20 18:48:33 +09:00
200da8dfaa chore(deps): update dependency electron-vite to v4.0.1 (#4170)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-20 18:48:27 +09:00
9a218a6516 fix(deps): update dependency @hono/zod-openapi to v1.2.0 (#4177)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-20 18:48:20 +09:00
933ee0ef75 chore(i18n): Translated using Weblate (Georgian)
Currently translated at 32.8% (150 of 457 strings)

Translation: pear-devs/pear-desktop/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/ka/
2025-12-20 04:00:34 +00:00
8dd7bcdf97 chore(i18n): Translated using Weblate (Catalan)
Currently translated at 100.0% (457 of 457 strings)

Translation: pear-devs/pear-desktop/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/ca/
2025-12-20 04:00:33 +00:00
d5c7e0475b chore: extend startingPages with Mixed for you entry (#3994) 2025-12-19 16:50:07 +09:00
a5af233683 chore(deps): update dependency vite to v7.3.0 (#3869)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-19 16:38:40 +09:00
27e3796622 "feat(api-server): add /api/v1/queue/next endpoint (#4117)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-12-19 16:17:30 +09:00
5843e85c4d fix(deps): update dependency happy-dom to v20.0.2 [security] (#4168)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-19 16:14:13 +09:00
92a943c755 Fixes 2 sync and UI bugs in music-together plugin (#4071)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-12-19 16:13:56 +09:00
58a19cdaa2 feat(api-server): Add HTTPS support and custom certificate configuration (#3874) 2025-12-19 16:10:12 +09:00
b1d2112bfc fix(deps): update dependency zod to v4.2.1 (#3870)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-19 16:09:37 +09:00
d229bc7f00 fix(deps): update dependency @hono/node-server to v1.19.7 (#3862)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-19 16:09:31 +09:00
033a4d3122 chore(deps): update dependency ts-morph to v27.0.2 (#4167)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-19 16:09:24 +09:00
8d252b6375 chore(deps): update dependency discord-api-types to v0.38.37 (#3866)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-19 16:06:49 +09:00
9453c0ca8f chore(deps): update dependency @electron/universal to v3.0.2 (#4166)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-19 16:06:41 +09:00
ce073b30d9 fix(deps): update dependency happy-dom to v20 [security] (#4021)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-19 16:05:43 +09:00
8f63e5e3a3 chore(deps): update dependency playwright to v1.55.1 [security] (#4026)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-19 16:00:12 +09:00
23853b66c6 chore(deps-dev): bump glob from 11.0.3 to 11.1.0 (#4092)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-19 15:59:56 +09:00
62a322f10d fix(deps): update dependency virtua to v0.48.2 (#3871)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-19 15:59:47 +09:00
9627dd2202 chore(deps): update dependency electron to v38.7.2 (#3867)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-19 15:59:39 +09:00
3383926faa chore(deps): update dependency glob to v11.1.0 [security] (#4091)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-19 15:59:30 +09:00
8179664064 fix(deps): update dependency hono to v4.10.3 [security] (#3880)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-19 15:58:25 +09:00
1a5e417f4f chore(i18n): Translated using Weblate (Swedish)
Currently translated at 100.0% (457 of 457 strings)

Translation: pear-devs/pear-desktop/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/sv/
2025-12-19 02:00:21 +00:00
ceb6da9bc9 chore(i18n): Translated using Weblate (Quechua)
Currently translated at 1.5% (7 of 457 strings)

Translation: pear-devs/pear-desktop/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/qu/
2025-12-19 02:00:20 +00:00
fdafb2dd07 chore(i18n): Translated using Weblate (Nepali)
Currently translated at 93.4% (427 of 457 strings)

Translation: pear-devs/pear-desktop/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/ne/
2025-12-19 02:00:19 +00:00
c3700e0e59 chore(i18n): Added translation using Weblate (Quechua) 2025-12-18 02:42:35 +01:00
3f1c26f82d chore(i18n): Translated using Weblate (German)
Currently translated at 99.5% (455 of 457 strings)

Translation: pear-devs/pear-desktop/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/de/
2025-12-17 21:00:21 +01:00
bb7816815c chore(i18n): Translated using Weblate (Catalan)
Currently translated at 100.0% (457 of 457 strings)

Translation: pear-devs/pear-desktop/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/ca/
2025-12-17 21:00:19 +01:00
a1773fd992 chore(i18n): Translated using Weblate (Turkish)
Currently translated at 100.0% (457 of 457 strings)

Translation: pear-devs/pear-desktop/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/tr/
2025-12-13 21:00:19 +00:00
1671bea942 chore(i18n): Added translation using Weblate (Kurdish (Northern)) 2025-12-11 15:44:04 +01:00
Adi
141ae03208 chore(i18n): Translated using Weblate (French)
Currently translated at 100.0% (457 of 457 strings)

Translation: pear-devs/pear-desktop/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/fr/
2025-12-09 14:02:30 +01:00
55c1012cda chore(i18n): Translated using Weblate (French)
Currently translated at 100.0% (457 of 457 strings)

Translation: pear-devs/pear-desktop/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/fr/
2025-12-09 14:02:30 +01:00
612c5c89c9 chore(i18n): Translated using Weblate (Portuguese)
Currently translated at 100.0% (457 of 457 strings)

Translation: pear-devs/pear-desktop/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/pt/
2025-12-09 14:02:29 +01:00
01bbf7e3f7 chore(i18n): Translated using Weblate (Slovak)
Currently translated at 88.4% (404 of 457 strings)

Translation: pear-devs/pear-desktop/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/sk/
2025-12-09 14:02:29 +01:00
6a7b7d88de chore(i18n): Translated using Weblate (Georgian)
Currently translated at 32.8% (150 of 457 strings)

Translation: pear-devs/pear-desktop/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/ka/
2025-12-09 14:02:29 +01:00
d06896450c chore(i18n): Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (457 of 457 strings)

Translation: pear-devs/pear-desktop/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/zh_Hant/
2025-12-08 06:00:18 +01:00
4a59afc505 chore(i18n): Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (457 of 457 strings)

Translation: pear-devs/pear-desktop/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/pt_BR/
2025-12-08 06:00:18 +01:00
7b0d63b6cf chore(i18n): Translated using Weblate (Georgian)
Currently translated at 31.0% (142 of 457 strings)

Translation: pear-devs/pear-desktop/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/ka/
2025-12-06 19:00:19 +01:00
c734ffe70f chore(i18n): Translated using Weblate (Spanish)
Currently translated at 100.0% (457 of 457 strings)

Translation: pear-devs/pear-desktop/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/es/
2025-12-04 23:00:20 +01:00
3d0ad69ddb chore(i18n): Translated using Weblate (Albanian)
Currently translated at 5.0% (23 of 457 strings)

Translation: pear-devs/pear-desktop/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/sq/
2025-12-04 23:00:18 +01:00
27 changed files with 881 additions and 551 deletions

View File

@ -45,11 +45,11 @@
},
"pnpm": {
"overrides": {
"vite": "npm:rolldown-vite@7.1.8",
"vite": "npm:rolldown-vite@7.3.0",
"node-gyp": "11.4.2",
"xml2js": "0.6.2",
"node-fetch": "3.3.2",
"@electron/universal": "3.0.1",
"@electron/universal": "3.0.2",
"@babel/runtime": "7.28.4"
},
"patchedDependencies": {
@ -66,16 +66,16 @@
"@dehoist/romanize-thai": "1.0.0",
"@electron-toolkit/tsconfig": "1.0.1",
"@electron/remote": "2.1.3",
"@ffmpeg.wasm/core-mt": "0.12.0",
"@ffmpeg.wasm/core-mt": "0.13.2",
"@ffmpeg.wasm/main": "0.12.0",
"@floating-ui/dom": "1.7.4",
"@foobar404/wave": "2.0.5",
"@ghostery/adblocker-electron": "2.11.6",
"@ghostery/adblocker-electron-preload": "2.11.6",
"@hono/node-server": "1.19.1",
"@hono/node-server": "1.19.7",
"@hono/node-ws": "1.2.0",
"@hono/swagger-ui": "0.5.2",
"@hono/zod-openapi": "1.1.0",
"@hono/zod-openapi": "1.2.0",
"@hono/zod-validator": "0.7.2",
"@jellybrick/dbus-next": "0.10.3",
"@jellybrick/electron-better-web-request": "1.0.4",
@ -105,8 +105,8 @@
"fflate": "0.8.2",
"filenamify": "6.0.0",
"hanja": "1.1.5",
"happy-dom": "18.0.1",
"hono": "4.9.6",
"happy-dom": "20.0.2",
"hono": "4.10.3",
"howler": "2.2.4",
"html-to-text": "9.0.5",
"i18next": "25.5.2",
@ -131,11 +131,11 @@
"solid-transition-group": "0.3.0",
"tiny-pinyin": "1.3.2",
"tinyld": "1.3.4",
"virtua": "0.42.3",
"virtua": "0.48.2",
"vudio": "2.1.1",
"x11": "2.3.0",
"youtubei.js": "^16.0.1",
"zod": "4.1.5"
"zod": "4.2.1"
},
"devDependencies": {
"@electron-toolkit/tsconfig": "1.0.1",
@ -153,12 +153,12 @@
"builtin-modules": "5.0.0",
"cross-env": "10.0.0",
"del-cli": "6.0.0",
"discord-api-types": "0.38.23",
"electron": "38.2.0",
"discord-api-types": "0.38.37",
"electron": "38.7.2",
"electron-builder": "26.0.12",
"electron-builder-squirrel-windows": "26.0.12",
"electron-devtools-installer": "4.0.0",
"electron-vite": "4.0.0",
"electron-vite": "4.0.1",
"eslint": "9.35.0",
"eslint-config-prettier": "10.1.8",
"eslint-import-resolver-exports": "1.0.0-beta.5",
@ -166,14 +166,14 @@
"eslint-plugin-import": "2.32.0",
"eslint-plugin-prettier": "5.5.4",
"eslint-plugin-solid": "0.14.5",
"glob": "11.0.3",
"glob": "11.1.0",
"node-gyp": "11.4.2",
"playwright": "1.55.0",
"ts-morph": "27.0.0",
"typescript": "5.9.2",
"playwright": "1.55.1",
"ts-morph": "27.0.2",
"typescript": "5.9.3",
"typescript-eslint": "8.43.0",
"utf-8-validate": "6.0.5",
"vite": "npm:rolldown-vite@7.1.8",
"vite": "npm:rolldown-vite@7.3.0",
"vite-plugin-inspect": "11.3.3",
"vite-plugin-resolve": "2.5.2",
"vite-plugin-solid": "2.11.8",

868
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -2,7 +2,7 @@
"common": {
"console": {
"plugins": {
"execute-failed": "Ha fallat l'execució de l'extensió {{pluginName}}::{{contextName}}",
"execute-failed": "Error en l'execució de l'extensió {{pluginName}}::{{contextName}}",
"executed-at-ms": "L'extensió {{pluginName}}::{{contextName}} s'ha executat als {{ms}}ms",
"initialize-failed": "Ha fallat la inicialització de l'extensió \"{{pluginName}}\"",
"load-all": "Carregant totes les extensions",
@ -237,7 +237,8 @@
"submenu": {
"percent": "{{ratio}}%"
}
}
},
"enable-seekbar": "Activar el tema en la barra de progrés"
},
"name": "Tema de color de l'àlbum"
},
@ -462,8 +463,8 @@
"label": "Text d'estat",
"submenu": {
"artist": "Escoltant {artist}",
"title": "Escoltant {song title}",
"pear-desktop": "Escoltant Pear Desktop"
"pear-desktop": "Escoltant Pear Desktop",
"title": "Escoltant {song title}"
}
}
},

View File

@ -237,7 +237,8 @@
"submenu": {
"percent": "{{ratio}}%"
}
}
},
"enable-seekbar": "Suchleisten-Design aktivieren"
},
"name": "Thema aus Albumfarbe"
},
@ -606,7 +607,7 @@
"permission": {
"all": "Gästen erlauben, Wiederhabeliste und Player zu bedienen",
"host-only": "Nur der Host kann die Playlist und den Player kontrollieren",
"playlist": "Gäste das Kontrollieren der Playlist erlauben"
"playlist": "Gästen das Kontrollieren der Playlist erlauben"
},
"set-permission": "Kontrollberechtigung ändern",
"status": {

View File

@ -323,6 +323,22 @@
},
"port": {
"label": "Port"
},
"https": {
"label": "HTTPS & Certificates",
"submenu": {
"enable-https": {
"label": "Enable HTTPS"
},
"cert": {
"label": "Certificate file (.crt/.pem)",
"dialogTitle": "Select HTTPS certificate file"
},
"key": {
"label": "Private key file (.key/.pem)",
"dialogTitle": "Select HTTPS private key file"
}
}
}
},
"name": "API Server [Beta]",

View File

@ -3,11 +3,11 @@
"console": {
"plugins": {
"execute-failed": "Error al ejecutar el complemento {{pluginName}}::{{contextName}}",
"executed-at-ms": "Complemento {{pluginName}}::{{contextName}} Ejecutó en {{ms}}ms",
"executed-at-ms": "Complemento {{pluginName}}::{{contextName}} se ejecutó en {{ms}}ms",
"initialize-failed": "Error al inicializar el complemento \"{{pluginName}}\"",
"load-all": "Cargando todos los complementos",
"load-failed": "Error al cargar el complemento \"{{pluginName}}\"",
"loaded": "Complementos \"{{pluginName}}\" cargado",
"loaded": "Complementos \"{{pluginName}}\" cargados",
"unload-failed": "No se ha podido descargar el complemento \"{{pluginName}}\"",
"unloaded": "Complemento \"{{pluginName}}\" descargado"
}
@ -237,7 +237,8 @@
"submenu": {
"percent": "{{ratio}}%"
}
}
},
"enable-seekbar": "Habilitar temas a la barra de búsqueda"
},
"name": "Tema de color del álbum"
},
@ -462,8 +463,8 @@
"label": "Texto de estado",
"submenu": {
"artist": "Escuchando a {artist}",
"title": "Escuchando {song title}",
"pear-desktop": "Escuchando Pear Desktop"
"pear-desktop": "Escuchando Pear Desktop",
"title": "Escuchando {song title}"
}
}
},

View File

@ -153,7 +153,7 @@
"custom-window-title": {
"label": "Titre de fenêtre personnalisé",
"prompt": {
"label": "Entrés un titre de fenêtre : (Laissé vide pour déactiver)",
"label": "Entrer un titre de fenêtre : (Laissé vide pour désactiver)",
"placeholder": "Exemple : Pear Desktop"
}
},
@ -237,7 +237,8 @@
"submenu": {
"percent": "{{ratio}}%"
}
}
},
"enable-seekbar": "Activer le thème sur la barre de progression"
},
"name": "Thème de couleur d'album"
},
@ -290,7 +291,7 @@
"description": "Ajout de la prise en charge de Pear Desktop pour le widget Amuse now playing de 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."
"query": "Le serveur API d'Amuse est en cours d'exécution. GET /query pour obtenir des informations sur les chansons."
}
},
"api-server": {
@ -302,7 +303,7 @@
"deny": "Interdire"
},
"message": "Autoriser {{ID}} ({{origin}}) à accéder à l'API?",
"title": "Requête d'autorisation d'API"
"title": "Demande d'autorisation à l'API"
}
},
"menu": {
@ -310,7 +311,7 @@
"label": "Plan d'autorisation",
"submenu": {
"auth-at-first": {
"label": "Autoriser à la première requête"
"label": "Autoriser lors de la première requête"
},
"none": {
"label": "Pas d'autorisation"
@ -331,7 +332,7 @@
"title": "Nom d'hôte"
},
"port": {
"label": "Entrez le port du serveur de l'API :",
"label": "Entrez le port du serveur API :",
"title": "Port"
}
}
@ -392,7 +393,7 @@
"toast": {
"caption-changed": "Sous-titres changés en {{language}}",
"caption-disabled": "Sous-titres désactivés",
"no-captions": "Aucun sous-titre disponible pour cette chanson"
"no-captions": "Aucun sous-titres disponibles pour cette chanson"
}
},
"compact-sidebar": {
@ -462,8 +463,8 @@
"label": "Texte d'état",
"submenu": {
"artist": "Écoute {artiste}",
"title": "Écoute {titre de la chanson}",
"pear-desktop": "Écoute Pear Desktop"
"pear-desktop": "Écoute Pear Desktop",
"title": "Écoute {titre de la chanson}"
}
}
},
@ -636,7 +637,7 @@
"name": "Navigation",
"templates": {
"back": {
"title": "Revenir à la page précédente"
"title": "Aller à la page précédente"
},
"forward": {
"title": "Aller à la page suivante"
@ -909,7 +910,7 @@
"name": "Tuna OBS"
},
"unobtrusive-player": {
"description": "Empêche le lecteur de s'afficher quand un chanson est en lecture",
"description": "Empêche le lecteur de s'afficher quand une chanson est en cours de lecture",
"name": "Lecteur Non-Intrusif"
},
"video-toggle": {

View File

@ -44,25 +44,38 @@
},
"dialog": {
"hide-menu-enabled": {
"detail": "მენიუ დამალულია, გამოიყენეთ 'Alt', რათა გამოაჩინოთ ის (ან 'Escape' თუ იყენებთ აპლიკაციის შიგნითა მენიუს)"
"detail": "მენიუ დამალულია, გამოიყენეთ 'Alt', რათა გამოაჩინოთ ის (ან 'Escape' თუ იყენებთ აპლიკაციის შიგნითა მენიუს)",
"message": "მენიუს დამალვა ჩართულია",
"title": "მენიუს დამალვა ჩართულია"
},
"need-to-restart": {
"buttons": {
"later": "მოგვიანებით"
}
"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": {
@ -70,19 +83,63 @@
"navigation": {
"label": "ნავიგაცია",
"submenu": {
"quit": "გასვლა"
"copy-current-url": "მიმდინარე URL-ის დაკოპირება",
"go-back": "უკან დაბრუნება",
"go-forward": "წინ გადასვლა",
"quit": "გასვლა",
"restart": "აპლიკაციის გადატვირთვა"
}
},
"options": {
"label": "მორგება",
"submenu": {
"language": {
"label": "ენა"
"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": "DevTools-ის გადართვა"
}
},
"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": "გამორთულია"
}
@ -162,11 +219,15 @@
"submenu": {
"percent": "{{size}}%"
}
},
"use-fullscreen": {
"label": "სრული ეკრანის გამოყენება"
}
}
},
"name": "გარემოს რეჟიმი"
},
"amuse": {
"name": "Amuse"
"name": "გაკვირვება"
},
"api-server": {
"dialog": {
@ -267,6 +328,13 @@
"status": {
"disconnected": "გათიშული"
}
},
"toast": {
"closed": "Music Together-ის ორგანიზატორი დაიხურა",
"disconnected": "Music Together-ის კავშირი გათიშულია",
"host-failed": "Music Together-ის გამოცხადება ვერ მოხერხდა",
"id-copied": "გამოსაცხადებელი ID დაკოპირებულია ბუფერში",
"id-copy-failed": "გამოსაცხადებელი ID-ის ვერ დაკოპირდა ბუფერში"
}
},
"navigation": {
@ -309,6 +377,30 @@
}
}
}
},
"video-toggle": {
"menu": {
"mode": {
"label": "რეჟიმი",
"submenu": {
"custom": "მორგებული გადამრთველი",
"disabled": "გამორთულია",
"native": "ადგილობრივი გადართვა"
}
}
},
"name": "ვიდეოს გადართვა",
"templates": {
"button-song": "სიმღერა",
"button-video": "ვიდეო"
}
},
"visualizer": {
"description": "პლეიერს ვიზუალიზატორს უმატებს",
"menu": {
"visualizer-type": "ვიზუალიზატორის ტიპი"
},
"name": "ვიზუალიზატორი"
}
}
}

View File

@ -0,0 +1 @@
{}

View File

@ -150,6 +150,13 @@
"visual-tweaks": {
"label": "भिजुअल ट्वीक्स",
"submenu": {
"custom-window-title": {
"label": "अनुकूलन विन्डो शीर्षक",
"prompt": {
"label": "अनुकूलन विन्डो शीर्षक प्रविष्ट गर्नुहोस्: (असक्षम पार्न खाली छोड्नुहोस्)",
"placeholder": "उदाहरण: पियर डेस्कटप"
}
},
"like-buttons": {
"default": "पूर्वनिर्धारित",
"force-show": "देखाउनुहोस",
@ -179,7 +186,7 @@
"plugins": {
"enabled": "सक्षम गरियो",
"label": "प्लगइनहरू",
"new": "NEW"
"new": "नयाँ"
},
"view": {
"label": "हेर्नुहोस्",

View File

@ -237,7 +237,8 @@
"submenu": {
"percent": "{{ratio}}%"
}
}
},
"enable-seekbar": "Ativar personalização da barra de progresso"
},
"name": "Tema da cor do álbum"
},

View File

@ -237,7 +237,8 @@
"submenu": {
"percent": "{{ratio}}%"
}
}
},
"enable-seekbar": "Ativar temas na barra de reprodução"
},
"name": "Tema de cores do álbum"
},
@ -462,8 +463,8 @@
"label": "Texto de estado",
"submenu": {
"artist": "A ouvir {artist}",
"title": "A ouvir {song title}",
"pear-desktop": "A reproduzir Pear Desktop"
"pear-desktop": "A reproduzir Pear Desktop",
"title": "A ouvir {song title}"
}
}
},

View File

@ -0,0 +1,15 @@
{
"common": {
"console": {
"plugins": {
"execute-failed": "Pluginta mana ruwayta atirqanchu {{pluginName}}::{{contextName}}",
"executed-at-ms": "Plugin nisqa {{pluginName}}::{{contextName}} ejecutado en {{ms}}ms",
"initialize-failed": "Plugin qallariyta mana atirqanchu \"{{pluginName}}\"",
"load-all": "Llapanta cargaspa",
"load-failed": "Pluginta mana kargayta atirqanchu \"{{pluginName}}\"",
"loaded": "Plugin nisqa \"{{pluginName}}\" cargado",
"unload-failed": "Pluginta mana uraykachiyta atirqanchu \"{{pluginName}}\""
}
}
}
}

View File

@ -237,7 +237,8 @@
"submenu": {
"percent": "{{ratio}}%"
}
}
},
"enable-seekbar": "Povoliť farebnú tému aj v seekbare"
},
"name": "Farebná téma albumu"
},

View File

@ -3,7 +3,56 @@
"console": {
"plugins": {
"execute-failed": "Dështoi në ekzekutimin e plugin-it {{pluginName}}::{{contextName}}",
"executed-at-ms": "Shtojca {{pluginName}}::{{contextName}} u ekzekutua në {{ms}}ms"
"executed-at-ms": "Shtojca {{pluginName}}::{{contextName}} u ekzekutua në {{ms}}ms",
"initialize-failed": "Dështoi ekzekutimi i plugin-it \"{{pluginName}}\"",
"load-all": "Duke ngarkuar të gjithe plugins",
"load-failed": "Dështoi në ngarkimin e plugin-it \"{{pluginName}}\"",
"loaded": "Plugin \"{{pluginName}}\" u ngarkua",
"unload-failed": "Shkarkimi i plugin-it \"{{pluginName}}\" dështoi",
"unloaded": "Plugin \"{{pluginName}}\" u shkarkua"
}
}
},
"language": {
"name": "Emri i gjuhës në anglisht. p.sh. japonisht, koreanisht, anglisht, rusisht"
},
"main": {
"console": {
"did-finish-load": {
"dev-tools": "Ngarkimi përfundoi. DevTools u hap."
},
"i18n": {
"loaded": "i18n i ngarkuar"
},
"second-instance": {
"receive-command": "U mor komanda mbi protokollin: \"{{command}}\""
},
"theme": {
"css-file-not-found": "Skedari CSS \"{{cssFile}}\" nuk ekziston, duke u injoruar"
},
"unresponsive": {
"details": "Gabim i Papërgjigjes!\n{{error}}"
},
"when-ready": {
"clearing-cache-after-20s": "Duke pastruar memorien e përkohshme të aplikacionit"
},
"window": {
"tried-to-render-offscreen": "Dritarja u përpoq të renderohej jashtë ekranit, windowSize={{windowSize}}, displaySize={{displaySize}}, position={{position}}"
}
},
"dialog": {
"hide-menu-enabled": {
"detail": "Menuja është e fshehur, përdorni 'Alt' për ta shfaqur (ose 'Escape' nëse përdorni Menunë brenda aplikacionit)",
"message": "Fshehja e menusë është aktivizuar",
"title": "Fshih Menunë Aktivizuar"
},
"need-to-restart": {
"buttons": {
"later": "Më vonë",
"restart-now": "Rinisni Tani"
},
"detail": "\"{{pluginName}}\" kërkon një restart që të hyjë në fuqi",
"message": "\"{{pluginName}}\" kerkon restart"
}
}
}

View File

@ -86,7 +86,7 @@
"copy-current-url": "Kopiera nuvarande länk",
"go-back": "Gå tillbaka",
"go-forward": "Gå framåt",
"quit": "Avsluta",
"quit": "Stäng",
"restart": "Starta om appen"
}
},
@ -237,7 +237,8 @@
"submenu": {
"percent": "{{ratio}}%"
}
}
},
"enable-seekbar": "Aktivera temaanpassning av uppspelningsreglaget"
},
"name": "Albumfärgtema"
},
@ -462,8 +463,8 @@
"label": "Statusmeddelande",
"submenu": {
"artist": "Lyssnar på {artist}",
"title": "Lyssnar på {song title}",
"pear-desktop": "Lyssnar på Pear Desktop"
"pear-desktop": "Lyssnar på Pear Desktop",
"title": "Lyssnar på {song title}"
}
}
},

View File

@ -237,7 +237,8 @@
"submenu": {
"percent": "{{ratio}}%"
}
}
},
"enable-seekbar": "Arama çubuğu temalarını etkinleştir"
},
"name": "Albüm Renk Teması"
},
@ -462,8 +463,8 @@
"label": "Durum metni",
"submenu": {
"artist": "{artist} Dinleniyor",
"title": "{song title} Dinleniyor",
"pear-desktop": "Pear Desktop Dinleniyor"
"pear-desktop": "Pear Desktop Dinleniyor",
"title": "{song title} Dinleniyor"
}
}
},

View File

@ -237,7 +237,8 @@
"submenu": {
"percent": "{{ratio}}%"
}
}
},
"enable-seekbar": "啟用進度條主題樣式"
},
"name": "隨歌曲色調變更主題"
},

View File

@ -1,3 +1,7 @@
import { createServer as createHttpServer } from 'node:http';
import { createServer as createHttpsServer } from 'node:https';
import { readFileSync } from 'node:fs';
import { jwt } from 'hono/jwt';
import { OpenAPIHono as Hono } from '@hono/zod-openapi';
import { cors } from 'hono/cors';
@ -48,22 +52,26 @@ export const backend = createBackend<BackendType, APIServerConfig>({
(newVolumeState: VolumeState) => (this.volumeState = newVolumeState),
);
this.run(config.hostname, config.port);
this.run(config);
},
stop() {
this.end();
},
onConfigChange(config) {
const old = this.oldConfig;
if (
this.oldConfig?.hostname === config.hostname &&
this.oldConfig?.port === config.port
old?.hostname === config.hostname &&
old?.port === config.port &&
old?.useHttps === config.useHttps &&
old?.certPath === config.certPath &&
old?.keyPath === config.keyPath
) {
this.oldConfig = config;
return;
}
this.end();
this.run(config.hostname, config.port);
this.run(config);
this.oldConfig = config;
},
@ -153,15 +161,30 @@ export const backend = createBackend<BackendType, APIServerConfig>({
this.injectWebSocket = ws.injectWebSocket.bind(this);
},
run(hostname, port) {
run(config) {
if (!this.app) return;
try {
this.server = serve({
fetch: this.app.fetch.bind(this.app),
port,
hostname,
});
const serveOptions =
config.useHttps && config.certPath && config.keyPath
? {
fetch: this.app.fetch.bind(this.app),
port: config.port,
hostname: config.hostname,
createServer: createHttpsServer,
serverOptions: {
key: readFileSync(config.keyPath),
cert: readFileSync(config.certPath),
},
}
: {
fetch: this.app.fetch.bind(this.app),
port: config.port,
hostname: config.hostname,
createServer: createHttpServer,
};
this.server = serve(serveOptions);
if (this.injectWebSocket && this.server) {
this.injectWebSocket(this.server);

View File

@ -411,6 +411,26 @@ const routes = {
},
},
}),
nextSongInfo: createRoute({
method: 'get',
path: `/api/${API_VERSION}/queue/next`,
summary: 'get next song info',
description:
'Get information about the next song in the queue (relative index +1)',
responses: {
200: {
description: 'Success',
content: {
'application/json': {
schema: SongInfoSchema,
},
},
},
204: {
description: 'No next song in queue',
},
},
}),
queueInfo: createRoute({
method: 'get',
path: `/api/${API_VERSION}/queue`,
@ -748,6 +768,63 @@ export const register = (
app.openapi(routes.oldQueueInfo, queueInfo);
app.openapi(routes.queueInfo, queueInfo);
app.openapi(routes.nextSongInfo, async (ctx) => {
const queueResponsePromise = new Promise<QueueResponse>((resolve) => {
ipcMain.once('peard:get-queue-response', (_, queue: QueueResponse) => {
return resolve(queue);
});
controller.requestQueueInformation();
});
const queue = await queueResponsePromise;
if (!queue?.items || queue.items.length === 0) {
ctx.status(204);
return ctx.body(null);
}
// Find the currently selected song
const currentIndex = queue.items.findIndex((item) => {
const renderer =
item.playlistPanelVideoRenderer ||
item.playlistPanelVideoWrapperRenderer?.primaryRenderer
?.playlistPanelVideoRenderer;
return renderer?.selected === true;
});
// Get the next song (currentIndex + 1)
const nextIndex = currentIndex + 1;
if (nextIndex >= queue.items.length) {
// No next song available
ctx.status(204);
return ctx.body(null);
}
const nextItem = queue.items[nextIndex];
const nextRenderer =
nextItem.playlistPanelVideoRenderer ||
nextItem.playlistPanelVideoWrapperRenderer?.primaryRenderer
?.playlistPanelVideoRenderer;
if (!nextRenderer) {
ctx.status(204);
return ctx.body(null);
}
// Extract relevant information similar to SongInfo format
const nextSongInfo = {
title: nextRenderer.title?.runs?.[0]?.text,
videoId: nextRenderer.videoId,
thumbnail: nextRenderer.thumbnail,
lengthText: nextRenderer.lengthText,
shortBylineText: nextRenderer.shortBylineText,
};
ctx.status(200);
return ctx.json(nextSongInfo);
});
app.openapi(routes.addSongToQueue, (ctx) => {
const { videoId, insertPosition } = ctx.req.valid('json');
controller.addSongToQueue(videoId, insertPosition);

View File

@ -17,6 +17,6 @@ export type BackendType = {
injectWebSocket?: (server: ReturnType<typeof serve>) => void;
init: (ctx: BackendContext<APIServerConfig>) => void;
run: (hostname: string, port: number) => void;
run: (config: APIServerConfig) => void;
end: () => void;
};

View File

@ -11,6 +11,9 @@ export interface APIServerConfig {
secret: string;
authorizedClients: string[];
useHttps: boolean;
certPath: string;
keyPath: string;
}
export const defaultAPIServerConfig: APIServerConfig = {
@ -21,4 +24,7 @@ export const defaultAPIServerConfig: APIServerConfig = {
secret: Date.now().toString(36),
authorizedClients: [],
useHttps: false,
certPath: '',
keyPath: '',
};

View File

@ -1,3 +1,4 @@
import { dialog } from 'electron';
import prompt from 'custom-electron-prompt';
import { t } from '@/i18n';
@ -93,5 +94,51 @@ export const onMenu = async ({
},
],
},
{
label: t('plugins.api-server.menu.https.label'),
type: 'submenu',
submenu: [
{
label: t('plugins.api-server.menu.https.submenu.enable-https.label'),
type: 'checkbox',
checked: config.useHttps,
click(menuItem) {
setConfig({ ...config, useHttps: menuItem.checked });
},
},
{
label: t('plugins.api-server.menu.https.submenu.cert.label'),
type: 'normal',
async click() {
const config = await getConfig();
const result = await dialog.showOpenDialog(window, {
title: t(
'plugins.api-server.menu.https.submenu.cert.dialogTitle',
),
filters: [{ name: 'Certificate', extensions: ['crt', 'pem'] }],
properties: ['openFile'],
});
if (!result.canceled && result.filePaths.length > 0) {
setConfig({ ...config, certPath: result.filePaths[0] });
}
},
},
{
label: t('plugins.api-server.menu.https.submenu.key.label'),
type: 'normal',
async click() {
const config = await getConfig();
const result = await dialog.showOpenDialog(window, {
title: t('plugins.api-server.menu.https.submenu.key.dialogTitle'),
filters: [{ name: 'Private Key', extensions: ['key', 'pem'] }],
properties: ['openFile'],
});
if (!result.canceled && result.filePaths.length > 0) {
setConfig({ ...config, keyPath: result.filePaths[0] });
}
},
},
],
},
];
};

View File

@ -9,9 +9,11 @@ import delay from 'delay';
import type { Permission, Profile, VideoData } from './types';
export type ConnectionEventMap = {
CLEAR_QUEUE: {};
ADD_SONGS: { videoList: VideoData[]; index?: number };
REMOVE_SONG: { index: number };
MOVE_SONG: { fromIndex: number; toIndex: number };
SET_INDEX: { index: number };
IDENTIFY: { profile: Profile } | undefined;
SYNC_PROFILE: { profiles: Record<string, Profile> } | undefined;
SYNC_QUEUE: { videoList: VideoData[] } | undefined;
@ -171,9 +173,10 @@ export class Connection {
public async broadcast<Event extends keyof ConnectionEventMap>(
type: Event,
payload: ConnectionEventMap[Event],
after?: ConnectionEventUnion[],
) {
await Promise.all(
this.getConnections().map((conn) => conn.send({ type, payload })),
this.getConnections().map((conn) => conn.send({ type, payload, after })),
);
}

View File

@ -215,6 +215,25 @@ export default createPlugin<
this.ignoreChange = true;
switch (event.type) {
case 'CLEAR_QUEUE': {
if (conn && this.permission === 'host-only') {
await this.connection?.broadcast('SYNC_QUEUE', {
videoList: this.queue?.videoList ?? [],
});
return;
}
this.queue?.clear();
await this.connection?.broadcast('CLEAR_QUEUE', {});
break;
}
case 'SET_INDEX': {
this.queue?.setIndex(event.payload.index);
await this.connection?.broadcast('SET_INDEX', {
index: event.payload.index,
});
break;
}
case 'ADD_SONGS': {
if (conn && this.permission === 'host-only') {
await this.connection?.broadcast('SYNC_QUEUE', {
@ -234,7 +253,15 @@ export default createPlugin<
await this.connection?.broadcast('ADD_SONGS', {
...event.payload,
videoList,
});
},
event.after,
);
const afterevent = event.after?.at(0);
if (afterevent?.type === 'SET_INDEX') {
this.queue?.setIndex(afterevent.payload.index);
}
break;
}
case 'REMOVE_SONG': {
@ -385,6 +412,16 @@ export default createPlugin<
const queueListener = async (event: ConnectionEventUnion) => {
this.ignoreChange = true;
switch (event.type) {
case 'CLEAR_QUEUE': {
await this.connection?.broadcast('CLEAR_QUEUE', {});
break;
}
case 'SET_INDEX': {
await this.connection?.broadcast('SET_INDEX', {
index: event.payload.index,
});
break;
}
case 'ADD_SONGS': {
await this.connection?.broadcast('ADD_SONGS', {
...event.payload,
@ -392,7 +429,9 @@ export default createPlugin<
...it,
ownerId: it.ownerId ?? this.connection!.id,
})),
});
},
event.after,
);
break;
}
case 'REMOVE_SONG': {
@ -420,6 +459,14 @@ export default createPlugin<
const listener = async (event: ConnectionEventUnion) => {
this.ignoreChange = true;
switch (event.type) {
case 'CLEAR_QUEUE': {
this.queue?.clear();
break;
}
case 'SET_INDEX': {
this.queue?.setIndex(event.payload.index);
break;
}
case 'ADD_SONGS': {
const videoList: VideoData[] = event.payload.videoList.map(
(it) => ({
@ -429,6 +476,13 @@ export default createPlugin<
);
await this.queue?.addVideos(videoList, event.payload.index);
const afterevent = event.after?.at(0);
if (afterevent?.type === 'SET_INDEX') {
this.queue?.setIndex(afterevent.payload.index);
}
break;
}
case 'REMOVE_SONG': {

View File

@ -314,6 +314,11 @@ export class Queue {
if (!this.internalDispatch) {
if (event.type === 'CLEAR') {
this.ignoreFlag = true;
this.broadcast({
type: 'CLEAR_QUEUE',
payload: {},
});
return;
}
if (event.type === 'ADD_ITEMS') {
if (this.ignoreFlag) {
@ -347,7 +352,7 @@ export class Queue {
},
after: [
{
type: 'SYNC_PROGRESS',
type: 'SET_INDEX',
payload: {
index,
},

View File

@ -16,4 +16,5 @@ export const startingPages: Record<string, string> = {
'Uploaded Songs': 'FEmusic_library_privately_owned_tracks',
'Uploaded Albums': 'FEmusic_library_privately_owned_releases',
'Uploaded Artists': 'FEmusic_library_privately_owned_artists',
'Mixed for you': 'FEmusic_mixed_for_you',
};