Compare commits

...

33 Commits

Author SHA1 Message Date
Lei Nelissen
914ca6a44f fix: incorporate jellyfin and emby as mentions 2024-07-25 23:29:50 +02:00
Lei Nelissen
a3b281ca72 fix: remove jellyfin mentions 2024-07-25 23:28:23 +02:00
Lei Nelissen
7079a7c653 fix: only enable playlists on jellyfin 2024-07-25 23:21:50 +02:00
Lei Nelissen
5bf68b6c42 chore: properly carry over old settings 2024-07-25 23:16:21 +02:00
Lei Nelissen
0592d64253 fix: correctly map platform names in auth header 2024-07-25 23:16:21 +02:00
Lei Nelissen
e149aa796c fix: weird issue when changing libraries 2024-07-25 23:16:21 +02:00
Lei Nelissen
0bf8b7a6e0 fix: rename credentials and save credentials type 2024-07-25 23:16:20 +02:00
Lei Nelissen
98c64e30eb fix: minor compatibility with emby for retrieving albums 2024-07-25 23:16:20 +02:00
Lei Nelissen
b7436afabe fix: support credential extraction from Emby 2024-07-25 23:16:20 +02:00
LibreTranslate
f2a8c4c9b2 Translated using Weblate (Czech)
Currently translated at 87.1% (68 of 78 strings)

Translation: Fintunes/App
Translate-URL: https://hosted.weblate.org/projects/fintunes/app/cs/
2024-07-25 23:14:27 +02:00
Weblate Translation Memory
f1232ba177 Translated using Weblate (Czech)
Currently translated at 87.1% (68 of 78 strings)

Translation: Fintunes/App
Translate-URL: https://hosted.weblate.org/projects/fintunes/app/cs/
2024-07-25 23:14:27 +02:00
Weblate Translation Memory
e19bc64dd2 Translated using Weblate (Bulgarian)
Currently translated at 97.4% (76 of 78 strings)

Translation: Fintunes/App
Translate-URL: https://hosted.weblate.org/projects/fintunes/app/bg/
2024-07-25 23:14:26 +02:00
Weblate Translation Memory
e8413dd099 Translated using Weblate (Danish)
Currently translated at 58.9% (46 of 78 strings)

Translation: Fintunes/App
Translate-URL: https://hosted.weblate.org/projects/fintunes/app/da/
2024-07-25 23:14:26 +02:00
Weblate Translation Memory
ef391f49d1 Translated using Weblate (Slovenian)
Currently translated at 96.1% (75 of 78 strings)

Translation: Fintunes/App
Translate-URL: https://hosted.weblate.org/projects/fintunes/app/sl/
2024-07-25 23:14:26 +02:00
Weblate Translation Memory
5803cc199e Translated using Weblate (Portuguese (Brazil))
Currently translated at 97.4% (76 of 78 strings)

Translation: Fintunes/App
Translate-URL: https://hosted.weblate.org/projects/fintunes/app/pt_BR/
2024-07-25 23:14:26 +02:00
Weblate Translation Memory
096182e753 Translated using Weblate (Catalan)
Currently translated at 64.1% (50 of 78 strings)

Translation: Fintunes/App
Translate-URL: https://hosted.weblate.org/projects/fintunes/app/ca/
2024-07-25 23:14:26 +02:00
LibreTranslate
4fc1431479 Translated using Weblate (Russian)
Currently translated at 94.8% (74 of 78 strings)

Translation: Fintunes/App
Translate-URL: https://hosted.weblate.org/projects/fintunes/app/ru/
2024-07-25 23:14:26 +02:00
Weblate Translation Memory
01c0c0280f Translated using Weblate (Russian)
Currently translated at 94.8% (74 of 78 strings)

Translation: Fintunes/App
Translate-URL: https://hosted.weblate.org/projects/fintunes/app/ru/
2024-07-25 23:14:26 +02:00
LibreTranslate
e8d674d369 Translated using Weblate (German)
Currently translated at 94.8% (74 of 78 strings)

Translation: Fintunes/App
Translate-URL: https://hosted.weblate.org/projects/fintunes/app/de/
2024-07-25 23:14:26 +02:00
Weblate Translation Memory
cbd1882d36 Translated using Weblate (German)
Currently translated at 94.8% (74 of 78 strings)

Translation: Fintunes/App
Translate-URL: https://hosted.weblate.org/projects/fintunes/app/de/
2024-07-25 23:14:25 +02:00
Weblate Translation Memory
a45f20a57d Translated using Weblate (Swedish)
Currently translated at 98.7% (77 of 78 strings)

Translation: Fintunes/App
Translate-URL: https://hosted.weblate.org/projects/fintunes/app/sv/
2024-07-25 23:14:25 +02:00
LibreTranslate
712b086fb3 Translated using Weblate (Italian)
Currently translated at 96.1% (75 of 78 strings)

Translation: Fintunes/App
Translate-URL: https://hosted.weblate.org/projects/fintunes/app/it/
2024-07-25 23:14:25 +02:00
Weblate Translation Memory
7f70b93faa Translated using Weblate (Italian)
Currently translated at 96.1% (75 of 78 strings)

Translation: Fintunes/App
Translate-URL: https://hosted.weblate.org/projects/fintunes/app/it/
2024-07-25 23:14:25 +02:00
Weblate Translation Memory
3a45895b22 Translated using Weblate (Polish)
Currently translated at 98.7% (77 of 78 strings)

Translation: Fintunes/App
Translate-URL: https://hosted.weblate.org/projects/fintunes/app/pl/
2024-07-25 23:14:25 +02:00
Weblate Translation Memory
d9f9226d62 Translated using Weblate (Ukrainian)
Currently translated at 98.7% (77 of 78 strings)

Translation: Fintunes/App
Translate-URL: https://hosted.weblate.org/projects/fintunes/app/uk/
2024-07-25 23:14:25 +02:00
Weblate Translation Memory
4f04f68e76 Translated using Weblate (Norwegian Bokmål)
Currently translated at 71.7% (56 of 78 strings)

Translation: Fintunes/App
Translate-URL: https://hosted.weblate.org/projects/fintunes/app/nb_NO/
2024-07-25 23:14:25 +02:00
Weblate Translation Memory
84388bc28f Translated using Weblate (Chinese (Simplified))
Currently translated at 98.7% (77 of 78 strings)

Translation: Fintunes/App
Translate-URL: https://hosted.weblate.org/projects/fintunes/app/zh_Hans/
2024-07-25 23:14:25 +02:00
Weblate Translation Memory
3a90be2372 Translated using Weblate (Dutch)
Currently translated at 98.7% (77 of 78 strings)

Translation: Fintunes/App
Translate-URL: https://hosted.weblate.org/projects/fintunes/app/nl/
2024-07-25 23:14:24 +02:00
LibreTranslate
a0c32fd5a8 Translated using Weblate (Japanese)
Currently translated at 96.1% (75 of 78 strings)

Translation: Fintunes/App
Translate-URL: https://hosted.weblate.org/projects/fintunes/app/ja/
2024-07-25 23:14:14 +02:00
Weblate Translation Memory
887aa3ab75 Translated using Weblate (Japanese)
Currently translated at 96.1% (75 of 78 strings)

Translation: Fintunes/App
Translate-URL: https://hosted.weblate.org/projects/fintunes/app/ja/
2024-07-25 23:14:14 +02:00
Weblate Translation Memory
a64bcec4b4 Translated using Weblate (French)
Currently translated at 98.7% (77 of 78 strings)

Translation: Fintunes/App
Translate-URL: https://hosted.weblate.org/projects/fintunes/app/fr/
2024-07-25 23:14:14 +02:00
Weblate Translation Memory
75a333a17b Translated using Weblate (Spanish)
Currently translated at 98.7% (77 of 78 strings)

Translation: Fintunes/App
Translate-URL: https://hosted.weblate.org/projects/fintunes/app/es/
2024-07-25 23:14:14 +02:00
Lei Nelissen
0d09c6f0b8 feat: Media stream info in now playing modal (#233)
* feat: add base codec info to player

* fix: redundant console.log

* chore: translation

* fix: only overflow direct play
2024-07-25 17:16:30 +02:00
43 changed files with 485 additions and 204 deletions

View File

@@ -0,0 +1,8 @@
<svg width="56" height="56" viewBox="0 0 56 56" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M50.5303 36.5379C51.7368 36.5379 52.7016 35.573 52.7016 34.3935V21.6065C52.7016 20.427 51.7368 19.4352 50.5303 19.4352C49.2972 19.4352 48.3859 20.427 48.3859 21.6065V34.3935C48.3859 35.573 49.2972 36.5379 50.5303 36.5379Z" />
<path d="M41.5233 50.0488C42.7295 50.0488 43.6677 49.0839 43.6677 47.9043V8.09575C43.6677 6.91623 42.7295 5.92436 41.5233 5.92436C40.2633 5.92436 39.352 6.91623 39.352 8.09575V47.9043C39.352 49.0839 40.2633 50.0488 41.5233 50.0488Z" />
<path d="M32.4894 41.9261C33.7224 41.9261 34.6607 40.9879 34.6607 39.7817V16.2183C34.6607 15.012 33.7224 14.0469 32.4894 14.0469C31.256 14.0469 30.3447 15.012 30.3447 16.2183V39.7817C30.3447 40.9879 31.256 41.9261 32.4894 41.9261Z" />
<path d="M23.4553 56C24.6884 56 25.6535 55.0348 25.6535 53.8287V2.17137C25.6535 0.965053 24.6884 0 23.4553 0C22.249 0 21.3376 0.965053 21.3376 2.17137V53.8287C21.3376 55.0348 22.249 56 23.4553 56Z" />
<path d="M14.4481 45.1966C15.6812 45.1966 16.6195 44.2317 16.6195 43.0253V12.9746C16.6195 11.7683 15.6812 10.7764 14.4481 10.7764C13.2418 10.7764 12.3035 11.7683 12.3035 12.9746V43.0253C12.3035 44.2317 13.2418 45.1966 14.4481 45.1966Z" />
<path d="M5.41411 34.2326C6.67405 34.2326 7.61231 33.2675 7.61231 32.0613V23.9387C7.61231 22.7324 6.67405 21.7405 5.41411 21.7405C4.2078 21.7405 3.29636 22.7324 3.29636 23.9387V32.0613C3.29636 33.2675 4.2078 34.2326 5.41411 34.2326Z" />
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -1,12 +1,12 @@
{
"play-album": "Пусни албум",
"track": "Песен",
"jellyfin-user-id": "Jellyfin потребител",
"user-id": "Jellyfin потребител",
"play-playlist": "Пусни списък",
"color-scheme-system": "Система",
"streaming": "Предава се поточно",
"download-album": "Изтегли албум",
"jellyfin-access-token": "Jellyfin Жетон за достъп (access token)",
"access-token": "Jellyfin Жетон за достъп (access token)",
"delete-playlist": "Изтрий списък",
"add-to-queue": "Добави към опашката",
"playback-reporting": "Докладване на изпълненията",
@@ -16,7 +16,7 @@
"no-results": "Няма резултати…",
"total-duration": "Обща продължителност",
"error-reporting": "Доклаване на грешки",
"jellyfin-server-url": "Адрес на Jellyfin сървър",
"server-url": "Адрес на Jellyfin сървър",
"downloads": "Изтеглени",
"onboarding-cta": "За да започнете Ви трябва Jellyfin сървър. Натиснете бутона отдолу, за да въведете адресът на вашия Jellyfin сървър и се впишете в него.",
"shuffle-album": "Разбъркай албум",
@@ -26,7 +26,7 @@
"artists": "Изпълнители",
"queue": "Опашка",
"error-reporting-description": "Докато използвате приложението, може да срещнете грешки. Докладването на тези грешки помага на програмата да стане по-сигурна и стабилна.",
"set-jellyfin-server": "Настройте Jellyfin сървър",
"set-server": "Настройте Jellyfin сървър",
"similar-albums": "Подобни албуми",
"albums": "Албуми",
"why-use-tracking-description": "Проследяването помага да се ускори процесът по разработка на приложението, като се докладват странни гранични случаи и недоглеждания. Това помага на приложението да стане по стабилно и приятно за използване от всички.",
@@ -52,7 +52,7 @@
"disable": "Изключи",
"search": "Търси",
"download-playlist": "Изтегли списък",
"jellyfin-library": "Jellyfin Библиотека",
"library": "Jellyfin Библиотека",
"enable-error-reporting": "Желаете ли да пуснете докладването на грешки?",
"color-scheme-description": "По подразбиране Fintunes ще следва цветовата схмена на вашата операционна система. Вие обаче можете да изберете Fintunes да се показва винаги в тъмен или светъл режим.",
"color-scheme": "Цветова схема",
@@ -66,7 +66,7 @@
"setting-cache": "Кеш",
"local-playback": "Локално изпълняване",
"clear-queue": "Изчисти опашката",
"set-jellyfin-server-instruction": "Моля въведете адресът на вашия Jellyfin сървър. Трябва да включва протоколът и порта",
"set-server-instruction": "Моля въведете адресът на вашия Jellyfin сървър. Трябва да включва протоколът и порта",
"delete-all-tracks": "Изтрий всички песни",
"where-is-data-stored": "Къде се пазят данните?",
"color-scheme-light": "Светъл режим",
@@ -75,5 +75,6 @@
"confirm-delete-all-tracks": "Сигурни ли сте, че искате да изтриете всички текущо изтеглени песни?",
"cancel": "Отказ",
"delete": "Изтрий",
"disc": "диск"
"disc": "диск",
"lyrics": "Текст на песен"
}

View File

@@ -52,13 +52,13 @@
"onboarding-welcome": "Benvinguts!",
"onboarding-intro": "Fintunes us permetrà reproduir la vostra biblioteca de música des de qualsevol lloc, amb total suport per a l'àudio en segon pla i l'enviament del so a altres dispositius.",
"onboarding-cta": "Per començar, necessiteu un servidor de Jellyfin. Si us plau, introdueix dins de la caixa de text els caràcters que veu a la imatge de sota. Això és requerit per evitar enviaments automàtics.",
"set-jellyfin-server": "Configura el servidor Jellyfin",
"set-jellyfin-server-instruction": "Introduïu l'URL del vostre servidor Jellyfin. Assegureu-vos d'incloure el protocol i el port",
"set-server": "Configura el servidor Jellyfin",
"set-server-instruction": "Introduïu l'URL del vostre servidor Jellyfin. Assegureu-vos d'incloure el protocol i el port",
"settings": "Configuració",
"jellyfin-library": "Biblioteca Jellyfin",
"jellyfin-server-url": "Preguntes Freqüents - FAQ",
"jellyfin-access-token": "Jellyfin Access Token",
"jellyfin-user-id": "Jellyfin User ID",
"library": "Biblioteca Jellyfin",
"server-url": "Preguntes Freqüents - FAQ",
"access-token": "Jellyfin Access Token",
"user-id": "Jellyfin User ID",
"setting-cache": "Caché",
"setting-cache-description": "Si heu actualitzat la vostra biblioteca de Jellyfin, però laplicació està subjectant a actius amagats, podeu esborrar la memòria cau amb aquest botó. Això obligarà laplicació a fer una cerca de la biblioteca des de zero.",
"recent-albums": "Afegit Recentment",
@@ -74,6 +74,7 @@
"sleep-timer": "Temporitzador son",
"confirm-delete-all-tracks": "Estàs segur que vols eliminar totes les pistes descarregades actualment?",
"delete": "Esborrar",
"cancel": "Cancel·lar",
"disc": "Disc"
"cancel": "Cancel·la",
"disc": "Disc",
"lyrics": "Lletres"
}

View File

@@ -1,12 +1,12 @@
{
"play-album": "Přehrát Album",
"track": "Skladba",
"jellyfin-user-id": "ID uživatele Jellyfin",
"user-id": "ID uživatele Jellyfin",
"play-playlist": "Přehrát seznam skladeb",
"color-scheme-system": "Systém",
"streaming": "Streamování",
"download-album": "Stáhnout Album",
"jellyfin-access-token": "Přístupový token Jellyfin",
"access-token": "Přístupový token Jellyfin",
"delete-playlist": "Smazat seznam skladeb",
"add-to-queue": "Přidat do Fronty",
"playback-reporting": "Hlášení o přehrávání",
@@ -16,7 +16,7 @@
"no-results": "Žádné výsledky…",
"total-duration": "Celková doba trvání",
"error-reporting": "Hlášení chyb",
"jellyfin-server-url": "Adresa URL serveru Jellyfin",
"server-url": "Adresa URL serveru Jellyfin",
"downloads": "Ke stažení",
"onboarding-cta": "Abyste mohli začít, potřebujete server Jellyfin. Kliknutím na tlačítko níže zadejte adresu svého serveru Jellyfin a přihlaste se k němu.",
"shuffle-album": "Zamíchat album",
@@ -26,10 +26,10 @@
"artists": "Umělci",
"queue": "Fronta",
"error-reporting-description": "Během používání této aplikace můžete narazit na chyby. Nahlášení těchto chyb pomáhá vytvářet bezpečnější a stabilnější prostředí aplikace.",
"set-jellyfin-server": "Nastavit Jellyfin Server",
"set-server": "Nastavit Jellyfin Server",
"similar-albums": "Podobná alba",
"albums": "Alba",
"why-use-tracking-description": "Sledování pomáhá urychlit vývoj to aplikace hlášením podivných okrajových chyb a přehlédnutí. To pomáhá, aby byla aplikace stabilnější a robustnější, a tím se zlepšuje zážitek z aplikace pro každého.",
"why-use-tracking-description": "Sledování pomáhá urychlit vývoj pro tuto aplikaci tím, že nahlásí divné případy hran a dohledy. To pomáhá, aby aplikace stabilnější a robustní, čímž zlepšuje zážitek z aplikace pro každého.",
"onboarding-welcome": "Vítejte!",
"reset-cache": "Resetovat mezipaměť",
"album": "Album",
@@ -52,7 +52,7 @@
"disable": "Zakázat",
"search": "Vyhledat",
"download-playlist": "Stáhnnout seznam skladeb",
"jellyfin-library": "Knihovna Jellyfin",
"library": "Knihovna Jellyfin",
"enable-error-reporting": "Chcete povolit hlášení chyb?",
"color-scheme-description": "Ve výchozím nastavení bude Fintunes sledovat barevné schéma vašeho operačního systému. Můžete se však rozhodnout toto nastavení změnit, abyste zajistili, že Fintunes bude vždy v tmavém nebo světlém režimu.",
"color-scheme": "Barevné Schéma",
@@ -66,7 +66,7 @@
"setting-cache": "Mezipaměť",
"local-playback": "Místní přehrávání",
"clear-queue": "Vymazat Frontu",
"set-jellyfin-server-instruction": "Zadejte adresu URL serveru Jellyfin. Nezapomeňte uvést protokol a port",
"set-server-instruction": "Zadejte adresu URL serveru Jellyfin. Nezapomeňte uvést protokol a port",
"delete-all-tracks": "Smazat Všechny Skladby",
"where-is-data-stored": "Kde jsou data uložena?",
"color-scheme-light": "Světlý Režim",
@@ -75,5 +75,6 @@
"confirm-delete-all-tracks": "Přejete si skutečně odstranit všechny stažené skladby?",
"delete": "Čeština",
"cancel": "Hledat",
"disc": "Čeština"
"disc": "Čeština",
"lyrics": "Text skladby"
}

View File

@@ -3,7 +3,7 @@
"error-reporting": "Fejlrapportering",
"music": "Musik",
"album": "Album",
"jellyfin-server-url": "Jellyfin server URL",
"server-url": "Jellyfin server URL",
"setting-cache": "Cache",
"reset-cache": "Nulstil cache",
"enable": "Aktivér",
@@ -31,12 +31,12 @@
"play-album": "Afspil album",
"queue": "Kø",
"add-to-queue": "Tilføj til kø",
"jellyfin-library": "Jellyfin bibliotek",
"jellyfin-user-id": "Jellyfin bruger ID",
"library": "Jellyfin bibliotek",
"user-id": "Jellyfin bruger ID",
"recent-albums": "Seneste albummer",
"clear-queue": "Ryd kø",
"no-results": "Ingen resultater…",
"set-jellyfin-server": "Sæt Jellyfin Server",
"set-server": "Sæt Jellyfin Server",
"why-use-tracking": "Hvorfor bruge sporing?",
"what-data-is-gathered": "Hvilke data indsamles?",
"where-is-data-stored": "Hvor gemmes data?",
@@ -53,8 +53,8 @@
"sleep-timer": "Søvn timer",
"onboarding-intro": "Fintunes giver dig mulighed for at streame dit musikbibliotek overalt, med fuld støtte til baggrundslyd og støbning.",
"onboarding-cta": "For at komme i gang skal du bruge en Jellyfin server. Klik på knappen nedenfor for at indtaste din Jellyfin-serveradresse og log ind på den.",
"set-jellyfin-server-instruction": "Indtast venligst din Jellyfin server URL. Sørg for at inkludere protokollen og havnen",
"jellyfin-access-token": "Jellyfin Access Token",
"set-server-instruction": "Indtast venligst din Jellyfin server URL. Sørg for at inkludere protokollen og havnen",
"access-token": "Jellyfin Access Token",
"setting-cache-description": "Hvis du har opdateret dit Jellyfin-bibliotek, men appen holder på cachelagrede aktiver, kan du tvinges til at rydde cachen ved hjælp af denne knap. Dette vil tvinge app til at hente biblioteket fra bunden.",
"error-reporting-description": "Under brug af denne app, kan du støde fejl. Rapportering af disse fejl hjælper med at skabe en mere sikker og stabil appoplevelse.",
"error-reporting-rationale": "Når du aktiverer fejlrapportering, hver gang en fejl opstår, oprettes en rapport automatisk og sendes til en server, sammen med nyttige fejlfindingsoplysninger såsom enheder, versioner og den specifikke fejl.",
@@ -75,5 +75,6 @@
"delete": "Slet",
"cancel": "Annuller",
"confirm-delete-all-tracks": "Er du sikker på, at du vil slette alle aktuelt downloadede spor?",
"disc": "Disk"
"disc": "Disk",
"lyrics": "Sangtekster"
}

View File

@@ -1,7 +1,7 @@
{
"onboarding-welcome": "Willkommen!",
"settings": "Einstellungen",
"jellyfin-library": "Jellyfin-Bibliothek",
"library": "Jellyfin-Bibliothek",
"more-info": "Mehr Informationen",
"track": "Titel",
"playlists": "Wiedergabelisten",
@@ -28,11 +28,11 @@
"now-playing": "Läuft gerade",
"onboarding-intro": "Mit Fintunes können Sie Ihre Musikbibliothek von überall aus streamen, mit voller Unterstützung für Hintergrundwiedergabe und Casting.",
"onboarding-cta": "Um loslegen zu können benötigen Sie einen Jellyfin Server. Klicken Sie auf den Button und geben Sie ihre Jellyfin Serveradresse und Logindaten ein.",
"set-jellyfin-server": "Legen Sie den Jellyfin-Server fest",
"set-jellyfin-server-instruction": "Bitte geben Sie die URL Ihres Jellyfin-Servers ein. Stellen Sie sicher, dass Sie das Protokoll und den Port angeben",
"jellyfin-server-url": "Jellyfin-Server-URL",
"jellyfin-access-token": "Jellyfin-Zugriffstoken",
"jellyfin-user-id": "Jellyfin-Benutzer-ID",
"set-server": "Legen Sie den Jellyfin-Server fest",
"set-server-instruction": "Bitte geben Sie die URL Ihres Jellyfin-Servers ein. Stellen Sie sicher, dass Sie das Protokoll und den Port angeben",
"server-url": "Jellyfin-Server-URL",
"access-token": "Jellyfin-Zugriffstoken",
"user-id": "Jellyfin-Benutzer-ID",
"setting-cache": "Zwischenspeicher",
"setting-cache-description": "Wenn Sie Ihre Jellyfin-Bibliothek aktualisiert haben, aber weiterhin zwischengespeicherte Elemente angezeigt werden kann man mit diesen Button erzwingen, den Zwischenspeicher vollständig zu leeren. Dies zwingt die App Ihre Bibliothek vollständig neu abzurufen.",
"reset-cache": "Zwischenspeicher zurücksetzen",
@@ -41,7 +41,7 @@
"error-reporting-description": "Bei der Nutzung dieser App können Fehler auftreten. Das Melden dieser Fehler trägt dazu bei, ein sichereres und stabileres App-Erlebnis zu schaffen.",
"error-reporting-rationale": "Wenn Sie die Fehlerberichterstattung aktivieren, wird jedes Mal, wenn ein Fehler auftritt, automatisch ein Bericht erstellt und an einen Server gesendet, zusammen mit hilfreichen Debugging-Informationen wie Geräte, Versionen und der spezifische Fehler.",
"why-use-tracking": "Warum werden Tracker verwendet?",
"why-use-tracking-description": "Tracker helfen, die Entwicklung dieser App zu beschleunigen, indem merkwürdige Sonderfälle oder übersehende Fehler direkt an uns gemeldet werden können. Dies hilft uns die App stabiler, robuster und nutzerfreundlicher für alle zu machen.",
"why-use-tracking-description": "Die Verfolgung hilft, die Entwicklung für diese App zu beschleunigen, indem sie seltsame Randfälle und Aufsichten meldet. Dies hilft, die App stabiler und robuster zu machen und so das App-Erlebnis für jeden zu verbessern.",
"what-data-is-gathered": "Welche Daten werden gesammelt?",
"what-data-is-gathered-description": "Wir protokollieren den Fehler, den Gerätetyp, die Betriebssystemversion, die App-Version und die Geräte-ID. In den Fehlerberichten wird kein Anwendungsstatus gesendet. Die Geräte-ID ist ein eindeutiger Hash, der in Ihren Geräteeinstellungen zurückgesetzt werden kann, und wir können aus dieser Kennung keine persönlichen Informationen ableiten.",
"where-is-data-stored": "Wo werden die Daten gespeichert?",
@@ -75,5 +75,6 @@
"confirm-delete-all-tracks": "Sind Sie sicher, dass Sie alle aktuell heruntergeladenen Titel löschen möchten?",
"delete": "Löschen",
"cancel": "Abbrechen",
"disc": "Scheiben"
"disc": "Scheiben",
"lyrics": "Sangtekster"
}

View File

@@ -13,16 +13,16 @@
"now-playing": "Now Playing",
"onboarding-welcome": "Welcome!",
"onboarding-intro": "Fintunes will allow you to stream your music library from anywhere, with full support for background audio and casting.",
"onboarding-cta": "In order to get started, you need a Jellyfin server. Click the button below to enter your Jellyfin server address and login to it.",
"set-jellyfin-server": "Set Jellyfin Server",
"set-jellyfin-server-instruction": "Please enter your Jellyfin server URL. Make sure to include the protocol and port",
"onboarding-cta": "In order to get started, you need a Jellyfin (or Emby) server. Click the button below to enter your server address and login to it.",
"set-server": "Set Server",
"set-server-instruction": "Please enter your server URL. Make sure to include the protocol and port",
"settings": "Settings",
"jellyfin-library": "Jellyfin Library",
"jellyfin-server-url": "Jellyfin Server URL",
"jellyfin-access-token": "Jellyfin Access Token",
"jellyfin-user-id": "Jellyfin User ID",
"library": "Library",
"server-url": "Server URL",
"access-token": "Access Token",
"user-id": "User ID",
"setting-cache": "Cache",
"setting-cache-description": "If you have updated your Jellyfin library, but the app is holding on to cached assets, you can forcefully clear the cache using this button. This will force the app to fetch the library from scratch.",
"setting-cache-description": "If you have updated your library, but the app is holding on to cached assets, you can forcefully clear the cache using this button. This will force the app to fetch the library from scratch.",
"reset-cache": "Reset Cache",
"recent-albums": "Recent Albums",
"error-reporting": "Error Reporting",
@@ -64,7 +64,7 @@
"total-duration": "Total duration",
"similar-albums": "Similar albums",
"playback-reporting": "Playback Reporting",
"playback-reporting-description": "With Playback Reporting, all your playback events are relayed back to Jellyfin. This allows you to track your most listened songs, particularly with Jellyfin plugins such as ListenBrainz.",
"playback-reporting-description": "With Playback Reporting, all your playback events are relayed back to your server. This allows you to track your most listened songs, particularly with plugins such as ListenBrainz or LastFM.",
"color-scheme": "Color Scheme",
"color-scheme-description": "By default, Fintunes will follow your operating system's color scheme. You can however choose to override this to make sure Fintunes is always in dark mode or light mode.",
"color-scheme-system": "System",
@@ -76,5 +76,9 @@
"delete": "Delete",
"cancel": "Cancel",
"disc": "Disc",
"lyrics": "Lyrics"
"lyrics": "Lyrics",
"direct-play": "Direct play",
"transcoded": "Transcoded",
"khz": "kHz",
"kbps": "kbps"
}

View File

@@ -14,13 +14,13 @@
"onboarding-welcome": "Bienvenido!",
"onboarding-intro": "Fintunes te permitirá reproducir tu biblioteca musical desde cualquier sitio, con suporte completo para audio en segundo plano y casteo en otros dispositivos.",
"onboarding-cta": "Para empezar necesitas un servidor de Jellyfin. Pulsa el botón de abajo para introducir la dirección del servidor y autentifícate con tus credenciales.",
"set-jellyfin-server": "Introduce servidor de Jellyfin",
"set-jellyfin-server-instruction": "Por favor introduce la URL de tu servidor de Jellyfin. Acuérdate de incluir protocolo y puerto",
"set-server": "Introduce servidor de Jellyfin",
"set-server-instruction": "Por favor introduce la URL de tu servidor de Jellyfin. Acuérdate de incluir protocolo y puerto",
"settings": "Configuración",
"jellyfin-library": "Biblioteca Jellyfin",
"jellyfin-server-url": "Url del servidor Jellyfin",
"jellyfin-access-token": "Token de acceso Jellyfin",
"jellyfin-user-id": "ID de usuario Jellyfin",
"library": "Biblioteca Jellyfin",
"server-url": "Url del servidor Jellyfin",
"access-token": "Token de acceso Jellyfin",
"user-id": "ID de usuario Jellyfin",
"setting-cache": "Caché",
"setting-cache-description": "Si actualizó la biblioteca Jellyfin, pero el software aún muestra el contenido anterior, puede hacer clic en el botón a continuación para forzar una actualización. Esto obligará al software a recuperar la información del servidor.",
"reset-cache": "Resetear Caché",
@@ -75,5 +75,6 @@
"confirm-delete-all-tracks": "¿Estás seguro de que quieres borrar todas las pistas descargadas actualmente?",
"delete": "Borrar",
"cancel": "Cancelar",
"disc": "Disco"
"disc": "Disco",
"lyrics": "Letras"
}

View File

@@ -14,13 +14,13 @@
"onboarding-welcome": "Bienvenue !",
"onboarding-intro": "Fintunes vous permettra de diffuser votre bibliothèque musicale de n'importe où, avec un support de la lecture en arrière plan et la diffusion à distance.",
"onboarding-cta": "Pour utiliser Fintunes, vous avez besoin d'un serveur Jellyfin. Cliquez sur le bouton ci-dessous pour entrer l'adresse de votre serveur Jellyfin et vous y connecter.",
"set-jellyfin-server": "Configurer le serveur Jellyfin",
"set-jellyfin-server-instruction": "Veuillez entrer l'URL de votre serveur Jellyfin. Assurez-vous d'inclure le protocole et le port",
"set-server": "Configurer le serveur Jellyfin",
"set-server-instruction": "Veuillez entrer l'URL de votre serveur Jellyfin. Assurez-vous d'inclure le protocole et le port",
"settings": "Réglages",
"jellyfin-library": "Bibliothèque Jellyfin",
"jellyfin-server-url": "URL du serveur Jellyfin",
"jellyfin-access-token": "Jeton d'accès à la Jellyfin",
"jellyfin-user-id": "ID utilisateur Jellyfin",
"library": "Bibliothèque Jellyfin",
"server-url": "URL du serveur Jellyfin",
"access-token": "Jeton d'accès à la Jellyfin",
"user-id": "ID utilisateur Jellyfin",
"setting-cache": "Cache",
"setting-cache-description": "Si vous avez mis à jour votre bibliothèque Jellyfin mais que l'application conserve toujours des ressources en cache, vous pouvez vider le cache en utilisant ce bouton. Cela forcera l'application à récupérer lintégralité de bibliothèque.",
"reset-cache": "Réinitialiser le cache",
@@ -75,5 +75,6 @@
"confirm-delete-all-tracks": "Êtes-vous sûr de vouloir supprimer toutes les pistes actuellement téléchargées?",
"delete": "Supprimer",
"cancel": "Annuler",
"disc": "Disc"
"disc": "Disc",
"lyrics": "Paroles"
}

View File

@@ -1,5 +1,5 @@
{
"jellyfin-server-url": "URL del Server Jellyfin",
"server-url": "URL del Server Jellyfin",
"settings": "Impostazioni",
"why-use-tracking": "Perché utilizzare il Monitoraggio?",
"streaming": "Streaming",
@@ -23,11 +23,11 @@
"onboarding-welcome": "Benvenuto!",
"onboarding-intro": "Fintunes ti consentirà di riprodurre in streaming la tua libreria musicale da dove vuoi, con pieno supporto per l'audio di sottofondo e la trasmissione.",
"onboarding-cta": "Per iniziare, hai bisogno di un server Jellyfin. Fai clic sul pulsante in basso per inserire l'indirizzo del tuo server Jellyfin e accedere.",
"set-jellyfin-server": "Imposta il Server Jellyfin",
"set-jellyfin-server-instruction": "Inserisci l'URL del tuo server Jellyfin. Assicurati di includere il protocollo e la porta",
"jellyfin-library": "Libreria Jellyfin",
"jellyfin-access-token": "Token di Accesso Jellyfin",
"jellyfin-user-id": "ID Utente Jellyfin",
"set-server": "Imposta il Server Jellyfin",
"set-server-instruction": "Inserisci l'URL del tuo server Jellyfin. Assicurati di includere il protocollo e la porta",
"library": "Libreria Jellyfin",
"access-token": "Token di Accesso Jellyfin",
"user-id": "ID Utente Jellyfin",
"setting-cache": "Cache",
"setting-cache-description": "Se hai aggiornato la tua libreria Jellyfin, ma l'app trattiene le risorse memorizzate nella cache, puoi forzare la cancellazione della cache utilizzando questo pulsante. Ciò costringerà l'app a recuperare la libreria da zero.",
"reset-cache": "Reimposta Cache",
@@ -35,7 +35,7 @@
"error-reporting": "Segnalazione Errori",
"error-reporting-description": "Durante l'utilizzo di questa app, potresti riscontrare errori. La segnalazione di questi errori ci aiuta a creare un'esperienza dell'app più sicura e stabile.",
"error-reporting-rationale": "Quando abiliti la segnalazione degli errori, ogni volta che si verifica un errore, viene creato automaticamente un rapporto e inviato a un server, insieme a utili informazioni di debug come dispositivi, versioni e l'errore specifico.",
"why-use-tracking-description": "Il monitoraggio aiuta ad accelerare lo sviluppo di questa app segnalando casi strani e sviste. Ciò ci aiuta a rendere l'app più stabile e robusta, aumentando così l'esperienza dell'app per tutti.",
"why-use-tracking-description": "Tracing aiuta a velocizzare lo sviluppo per questa applicazione segnalando strani casi di bordo e sovrastudini. Questo aiuta a rendere l'applicazione più stabile e robusto, migliorando così l'esperienza app per tutti.",
"what-data-is-gathered": "Quali dati vengono raccolti?",
"what-data-is-gathered-description": "Registriamo l'errore, il tipo di dispositivo, la versione del sistema operativo, la versione dell'app e l'ID del dispositivo. Nessuno stato dell'applicazione viene inviato in nessuna segnalazione di errore. L'ID del dispositivo è un hash unico che può essere ripristinato nelle impostazioni del dispositivo e non possiamo dedurre alcuna informazione personale da questo identificatore.",
"where-is-data-stored": "Dove vengono archiviati i dati?",
@@ -75,5 +75,6 @@
"delete": "Cancella",
"cancel": "Annulla",
"confirm-delete-all-tracks": "Sei sicuro di voler eliminare tutte le tracce attualmente scaricate?",
"disc": "Disc"
"disc": "Disc",
"lyrics": "Testi"
}

View File

@@ -14,13 +14,13 @@
"onboarding-welcome": "ようこそ。",
"onboarding-intro": "Fintunes は、どこからでも音楽ライブラリをストリーミングすることができます。バックグラウンド・オーディオやキャスティングを完全にサポートします。",
"onboarding-cta": "始めるためには、Jellyfin サーバーが必要です。下のボタンをクリックして、Jellyfin サーバーのアドレスを入力し、ログインしてください。",
"set-jellyfin-server": "Jellyfin サーバ を設定",
"set-jellyfin-server-instruction": "Jellyfin サーバの URL を入力してください。プロトコルとポートを必ず含めてください。",
"set-server": "Jellyfin サーバ を設定",
"set-server-instruction": "Jellyfin サーバの URL を入力してください。プロトコルとポートを必ず含めてください。",
"settings": "設定",
"jellyfin-library": "Jellyfin ライブライ",
"jellyfin-server-url": "Jellyfin サーバの URL",
"jellyfin-access-token": "Jellyfin アクセストークン",
"jellyfin-user-id": "Jellyfin ユーザ ID",
"library": "Jellyfin ライブライ",
"server-url": "Jellyfin サーバの URL",
"access-token": "Jellyfin アクセストークン",
"user-id": "Jellyfin ユーザ ID",
"setting-cache": "キャッシュ",
"setting-cache-description": "Jellyfinライブラリをアップデートしたにもかかわらず、アプリがキャッシュされたアセットを保持している場合、このボタンを使って強制的にキャッシュをクリアすることができます。これにより、アプリはライブラリを最初から取得するようになります。",
"reset-cache": "キャッシュをリセット",
@@ -29,7 +29,7 @@
"error-reporting-description": "このアプリを使用中に、エラーの可能性があります。これらのエラーを報告することで、よりセキュアで安定したアプリ体験を実現することができます。",
"error-reporting-rationale": "エラーレポートを有効にすると、エラーが発生するたびにレポートが自動的に作成され、デバイス、バージョン、特定のエラーなど、デバッグに役立つ情報とともにサーバーに送信されます。",
"why-use-tracking": "なぜトラッキングを使うか?",
"why-use-tracking-description": "トラッキングは、奇妙なエッジケースや見落としを報告することで、このアプリの開発を促進します。これにより、アプリの安定性と堅牢性が向上し、すべての人のアプリ体験が向上します。",
"why-use-tracking-description": "リリック",
"what-data-is-gathered": "どのデータが集まりますか?",
"what-data-is-gathered-description": "エラー、デバイスタイプ、OSバージョン、アプリバージョン、デバイスIDが記録されます。いかなるエラー報告においても、アプリケーションの状態は送信されません。デバイスIDは、デバイスの設定でリセット可能な一意のハッシュであり、この識別子から個人情報を推測することはできません。",
"where-is-data-stored": "データはどこに保存されていますか?",
@@ -75,5 +75,6 @@
"confirm-delete-all-tracks": "ダウンロードしたすべてのトラックを削除したいですか?",
"delete": "削除",
"cancel": "キャンセル",
"disc": "ディスク"
"disc": "ディスク",
"lyrics": "歌詞"
}

View File

@@ -26,13 +26,13 @@
"onboarding-welcome": "Velkommen.",
"onboarding-intro": "Fintunes lar deg strømme musikkbiblioteket ditt fra hvor som helst, med full støtte for bakgrunnslyd og casting.",
"onboarding-cta": "For å begynne trenger du en Jellyfin-tjener. Klikk på knappen nedenfor for å angi Jellyfin-tjeneradressen din og logge på den.",
"set-jellyfin-server": "Sett Jellyfin-tjener",
"set-jellyfin-server-instruction": "Skriv inn Jellyfin-tjenerens nettadresse. Sørg for å inkludere protokollen og porten.",
"set-server": "Sett Jellyfin-tjener",
"set-server-instruction": "Skriv inn Jellyfin-tjenerens nettadresse. Sørg for å inkludere protokollen og porten.",
"settings": "Innstillinger",
"jellyfin-library": "Jellyfin-bibliotek",
"jellyfin-server-url": "Jellyfin-tjenernettadresse",
"jellyfin-access-token": "Jellyfin-tilgangssymbol",
"jellyfin-user-id": "Jellyfin-bruker-ID",
"library": "Jellyfin-bibliotek",
"server-url": "Jellyfin-tjenernettadresse",
"access-token": "Jellyfin-tilgangssymbol",
"user-id": "Jellyfin-bruker-ID",
"setting-cache": "Mellomlager",
"setting-cache-description": "Du tømme hurtiglageret med denne knappen hvis du har oppdatert Jellyfin-biblioteket ditt, men programmet holder på hurtiglagrede eiendeler. Dette vil tvinge programmet til å hente biblioteket fra bunnen av.",
"reset-cache": "Tilbakestill hurtiglager",
@@ -74,5 +74,7 @@
"sleep-timer": "Sleep timer",
"delete": "Slett",
"cancel": "Avbryt",
"confirm-delete-all-tracks": "Er du sikker på at du vil slette alle nedlastede spor?"
"confirm-delete-all-tracks": "Er du sikker på at du vil slette alle nedlastede spor?",
"disc": "Plate",
"lyrics": "Sangtekster"
}

View File

@@ -14,13 +14,13 @@
"onboarding-welcome": "Welkom!",
"onboarding-intro": "Fintunes maakt het mogelijk om van waar dan ook je muziek te streamen, met volledige support voor achtergroundaudio en casting.",
"onboarding-cta": "Om te starten moet je een Jellyfin server hebben. Klik de onderstaande knop om het adres van je Jellyfin server in te vullen en er in te loggen.",
"set-jellyfin-server": "Stel Jellyfin Server in",
"set-jellyfin-server-instruction": "Vul alsjeblieft je Jellyfin server URL in. Voeg ook het protocol en de poort toe",
"set-server": "Stel Jellyfin Server in",
"set-server-instruction": "Vul alsjeblieft je Jellyfin server URL in. Voeg ook het protocol en de poort toe",
"settings": "Instellingen",
"jellyfin-library": "Jellyfin Bibliotheek",
"jellyfin-server-url": "Jellyfin Server URL",
"jellyfin-access-token": "Jellyfin Toegangstoken",
"jellyfin-user-id": "Jellyfin Gebruiker ID",
"library": "Jellyfin Bibliotheek",
"server-url": "Jellyfin Server URL",
"access-token": "Jellyfin Toegangstoken",
"user-id": "Jellyfin Gebruiker ID",
"setting-cache": "Cache",
"setting-cache-description": "Als je Jellyfin bibliotheek geüpdatet hebt, maar de app nog aan elementen uit de cache vasthoudt, kun je de cache geforceerd leegmaken met deze knop. Dit forceert de app om de bibliotheek weer vanaf het begin op te bouwen.",
"reset-cache": "Leeg Cache",
@@ -75,5 +75,10 @@
"sleep-timer": "Slaaptimer",
"delete": "Verwijder",
"cancel": "Annuleer",
"disc": "Schijf"
"disc": "Schijf",
"direct-play": "Direct afgespeeld",
"transcoded": "Getranscodeerd",
"khz": "kHz",
"kbps": "kbps",
"lyrics": "Songtekst"
}

View File

@@ -1,7 +1,7 @@
{
"onboarding-welcome": "Witaj!",
"onboarding-intro": "Fintunes umożliwia strumieniowe przesyłanie biblioteki muzycznej z dowolnego miejsca, z pełną obsługą dźwięku w tle i przesyłania.",
"set-jellyfin-server": "Ustaw Serwer Jellyfin",
"set-server": "Ustaw Serwer Jellyfin",
"settings": "Ustawienia",
"reset-cache": "Zresetuj pamięć podręczną",
"color-scheme-light": "Jasny motyw",
@@ -19,11 +19,11 @@
"music": "Muzyka",
"now-playing": "Teraz gra",
"onboarding-cta": "Aby rozpocząć pracę, potrzebujesz serwera Jellyfin. Kliknij poniższy przycisk, aby wprowadzić adres serwera Jellyfin i zalogować się do niego.",
"set-jellyfin-server-instruction": "Wprowadź adres URL serwera Jellyfin. Upewnij się, że podałeś protokół i port",
"jellyfin-library": "Biblioteka Jellyfin",
"jellyfin-server-url": "URL do serwera Jellyfin",
"jellyfin-access-token": "Token dostępowy do Jellyfin",
"jellyfin-user-id": "ID użytkownika Jellyfin",
"set-server-instruction": "Wprowadź adres URL serwera Jellyfin. Upewnij się, że podałeś protokół i port",
"library": "Biblioteka Jellyfin",
"server-url": "URL do serwera Jellyfin",
"access-token": "Token dostępowy do Jellyfin",
"user-id": "ID użytkownika Jellyfin",
"setting-cache": "Pamięć podręczna",
"setting-cache-description": "Jeśli zaktualizowałeś bibliotekę Jellyfin, ale aplikacja przechowuje zasoby w pamięci podręcznej, możesz wymusić wyczyszczenie pamięci podręcznej za pomocą tego przycisku. Zmusi to aplikację do pobrania biblioteki od zera.",
"recent-albums": "Ostatnie Albumy",
@@ -75,5 +75,6 @@
"delete": "Usuń",
"cancel": "Anuluj",
"confirm-delete-all-tracks": "Czy na pewno chcesz usunąć wszystkie aktualnie pobrane utwory?",
"disc": "Disc disc"
"disc": "Disc disc",
"lyrics": "Tekst utworu"
}

View File

@@ -24,15 +24,15 @@
"onboarding-welcome": "Boas-vindas!",
"onboarding-intro": "O Fintunes permite a você fazer streaming de sua biblioteca de músicas de qualquer lugar, com suporte total para reprodução de áudio em segundo plano e transmissão de áudio.",
"onboarding-cta": "Para usar o Fintunes, você precisa de um servidor Jellyfin. Clique no botão abaixo para inserir o endereço do servidor Jellyfin e fazer login.",
"set-jellyfin-server": "Configurar o servidor Jellyfin",
"set-jellyfin-server-instruction": "Insira a URL do servidor Jellyfin. Certifique-se de incluir o protocolo e a porta",
"set-server": "Configurar o servidor Jellyfin",
"set-server-instruction": "Insira a URL do servidor Jellyfin. Certifique-se de incluir o protocolo e a porta",
"settings": "Configurações",
"error-reporting-description": "Durante o uso deste aplicativo, você pode encontrar erros. Relatar esses erros ajuda a criar uma experiência de aplicativo mais segura e estável.",
"error-reporting-rationale": "Quando você ativa o relatório de erros, toda vez que ocorre um erro, um relatório é criado automaticamente e enviado a um servidor, juntamente com informações úteis de depuração, como dispositivos, versões e o erro específico.",
"jellyfin-library": "Biblioteca do Jellyfin",
"jellyfin-server-url": "URL do servidor Jellyfin",
"jellyfin-access-token": "Token de acesso do Jellyfin",
"jellyfin-user-id": "ID de usuário do Jellyfin",
"library": "Biblioteca do Jellyfin",
"server-url": "URL do servidor Jellyfin",
"access-token": "Token de acesso do Jellyfin",
"user-id": "ID de usuário do Jellyfin",
"setting-cache": "Cache",
"setting-cache-description": "Se você atualizou sua biblioteca do Jellyfin, mas o aplicativo continua usando os recursos em cache, você pode forçar a limpeza do cache usando este botão. Isso forçará o aplicativo a recuperar a biblioteca do zero.",
"reset-cache": "Redefinir cache",
@@ -75,5 +75,6 @@
"confirm-delete-all-tracks": "Tem a certeza de que deseja excluir todas as faixas atualmente baixadas?",
"delete": "Excluir",
"cancel": "Cancelar",
"disc": "Disco"
"disc": "Disco",
"lyrics": "Letras"
}

View File

@@ -27,13 +27,13 @@
"onboarding-welcome": "Добро пожаловать!",
"onboarding-intro": "Fintunes позволит вам транслировать вашу музыкальную библиотеку из любого места с полной поддержкой фонового воспроизведения и трансляций.",
"onboarding-cta": "Для начала вам нужен сервер Jellyfin. Нажмите кнопку ниже, чтобы ввести адрес своего сервера Jellyfin и авторизоваться на нем.",
"set-jellyfin-server": "Установить сервер Jellyfin",
"set-jellyfin-server-instruction": "Пожалуйста, введите URL вашего сервера Jellyfin. Обязательно укажите протокол и порт",
"set-server": "Установить сервер Jellyfin",
"set-server-instruction": "Пожалуйста, введите URL вашего сервера Jellyfin. Обязательно укажите протокол и порт",
"settings": "Настройки",
"jellyfin-library": "Библиотека Jellyfin",
"jellyfin-server-url": "URL сервера Jellyfin",
"jellyfin-access-token": "Токен доступа Jellyfin",
"jellyfin-user-id": "Идентификатор пользователя Jellyfin",
"library": "Библиотека Jellyfin",
"server-url": "URL сервера Jellyfin",
"access-token": "Токен доступа Jellyfin",
"user-id": "Идентификатор пользователя Jellyfin",
"setting-cache": "Кэш",
"setting-cache-description": "Если вы обновили свою библиотеку Jellyfin, но приложение сохраняет кэшированные ресурсы, вы можете принудительно очистить кэш с помощью этой кнопки. Это заставит приложение обновить библиотеку.",
"reset-cache": "Сбросить кеш",
@@ -42,7 +42,7 @@
"error-reporting-description": "Во время использования этого приложения вы можете столкнуться с ошибками. Сообщение об этих ошибках помогает сделать приложение более безопасным и стабильным.",
"error-reporting-rationale": "Когда вы включаете создание отчетов об ошибках, при каждом возникновении ошибки автоматически создается отчет и отправляется на сервер вместе с полезной отладочной информацией, такой как устройства, версии и конкретная ошибка.",
"why-use-tracking": "Зачем использовать отслеживание?",
"why-use-tracking-description": "Отслеживание помогает ускорить разработку этого приложения, сообщая о странных случаях и упущениях. Это помогает сделать приложение более стабильным и надежным, тем самым повышая удобство работы с приложением для всех.",
"why-use-tracking-description": "Отслеживание помогает ускорить разработку этого приложения, сообщая о странных случаях и надзорах. Это помогает сделать приложение более стабильным и надежным, тем самым улучшая опыт приложений для всех.",
"what-data-is-gathered": "Какие данные собираются?",
"what-data-is-gathered-description": "Мы собираем ошибки, тип устройства, версию ОС, версию приложения и идентификатор устройства. Ни в одном отчете об ошибках не передается состояние приложения. Идентификатор устройства - это уникальный хэш, который можно сбросить в настройках вашего устройства, и мы не можем извлечь какую-либо личную информацию из этого идентификатора.",
"where-is-data-stored": "Где хранятся данные?",
@@ -75,5 +75,6 @@
"confirm-delete-all-tracks": "Вы уверены, что хотите удалить все загруженные в настоящее время треки?",
"delete": "Удалить",
"cancel": "Отмена",
"disc": "Disc"
"disc": "Disc",
"lyrics": "Текст песни"
}

View File

@@ -13,13 +13,13 @@
"search": "Iskanje",
"music": "Glasba",
"now-playing": "Zdaj igra",
"set-jellyfin-server": "Nastavite strežnik Jellyfin",
"set-jellyfin-server-instruction": "Vnesite URL svojega strežnika Jellyfin. Ne pozabite vključiti protokola in vrat",
"set-server": "Nastavite strežnik Jellyfin",
"set-server-instruction": "Vnesite URL svojega strežnika Jellyfin. Ne pozabite vključiti protokola in vrat",
"settings": "Nastavitve",
"jellyfin-library": "Knjižnica Jellyfin",
"jellyfin-server-url": "URL strežnika Jellyfin",
"jellyfin-access-token": "Žeton za dostop Jellyfin",
"jellyfin-user-id": "ID uporabnika Jellyfin",
"library": "Knjižnica Jellyfin",
"server-url": "URL strežnika Jellyfin",
"access-token": "Žeton za dostop Jellyfin",
"user-id": "ID uporabnika Jellyfin",
"setting-cache": "Predpomnilnik",
"setting-cache-description": "Če ste posodobili knjižnico Jellyfin, vendar aplikacija zadržuje predpomnjena sredstva, lahko s tem gumbom prisilno počistite predpomnilnik. To bo prisililo aplikacijo, da znova pridobi knjižnico.",
"reset-cache": "Ponastavi predpomnilnik",
@@ -74,5 +74,7 @@
"sleep-timer": "Časovnik za spanje",
"cancel": "Prekliči",
"confirm-delete-all-tracks": "Ali ste prepričani, da želite izbrisati vse trenutno prenesene skladbe?",
"delete": "Izbriši"
"delete": "Izbriši",
"disc": "Disk",
"lyrics": "Besedilo"
}

View File

@@ -18,13 +18,13 @@
"onboarding-welcome": "Välkommen!",
"onboarding-intro": "Fintunes låter dig strömma ditt musikbibliotek var som helst, med fullt stöd för bakgrundsljud och casting.",
"onboarding-cta": "För att komma igång behöver du en Jellyfin-server. Klicka på knappen nedan för att ange din Jellyfin-serveradress och logga in på den.",
"set-jellyfin-server": "Ställ in Jellyfin-server",
"set-jellyfin-server-instruction": "Ange din Jellyfin-server-URL. Se till att inkludera protokoll och port",
"set-server": "Ställ in Jellyfin-server",
"set-server-instruction": "Ange din Jellyfin-server-URL. Se till att inkludera protokoll och port",
"settings": "inställningar",
"jellyfin-library": "Jellyfin-bibliotek",
"jellyfin-server-url": "Jellyfin Server URL",
"jellyfin-access-token": "Jellyfin Access Token",
"jellyfin-user-id": "Jellyfin användar-ID",
"library": "Jellyfin-bibliotek",
"server-url": "Jellyfin Server URL",
"access-token": "Jellyfin Access Token",
"user-id": "Jellyfin användar-ID",
"setting-cache": "Cache",
"setting-cache-description": "Om du har uppdaterat ditt Jellyfin-bibliotek men appen håller kvar cachelagrade tillgångar kan du tvångsrensa cachen med den här knappen. Detta kommer att tvinga appen att hämta biblioteket från början.",
"reset-cache": "Töm cache",
@@ -75,5 +75,6 @@
"delete": "Delete",
"cancel": "Avbokning",
"confirm-delete-all-tracks": "Är du säker på att du vill ta bort alla för närvarande nedladdade spår?",
"disc": "Disc"
"disc": "Disc",
"lyrics": "Låttext"
}

View File

@@ -14,13 +14,13 @@
"onboarding-welcome": "Ласкаво просимо!",
"onboarding-intro": "Fintunes дозволить вам транслювати свою музичну бібліотеку з будь-якого місця з повною підтримкою фонового звуку та трансляції.",
"onboarding-cta": "Щоб почати, вам потрібен сервер Jellyfin. Натисніть кнопку нижче, щоб ввести адресу свого сервера Jellyfin і увійти на нього.",
"set-jellyfin-server": "Встановити сервер Jellyfin",
"set-jellyfin-server-instruction": "Будь ласка, введіть URL-адресу вашого сервера Jellyfin. Не забудьте вказати протокол і порт",
"set-server": "Встановити сервер Jellyfin",
"set-server-instruction": "Будь ласка, введіть URL-адресу вашого сервера Jellyfin. Не забудьте вказати протокол і порт",
"settings": "Налаштування",
"jellyfin-library": "Бібліотека Jellyfin",
"jellyfin-server-url": "URL-адреса сервера Jellyfin",
"jellyfin-access-token": "Токен доступу Jellyfin",
"jellyfin-user-id": "ID користувача Jellyfin",
"library": "Бібліотека Jellyfin",
"server-url": "URL-адреса сервера Jellyfin",
"access-token": "Токен доступу Jellyfin",
"user-id": "ID користувача Jellyfin",
"setting-cache": "Кеш",
"setting-cache-description": "Якщо ви оновили свою бібліотеку Jellyfin, але застосунок зберігає кешовані ресурси, ви можете примусово очистити кеш за допомогою цієї кнопки. Це змусить застосунок отримати бібліотеку з нуля.",
"reset-cache": "Скинути кеш",
@@ -75,5 +75,6 @@
"confirm-delete-all-tracks": "Ви впевнені, що ви хочете видалити всі завантажені треки?",
"delete": "Видалити",
"cancel": "Скасувати",
"disc": "Диски"
"disc": "Диски",
"lyrics": "Лірика"
}

View File

@@ -14,13 +14,13 @@
"onboarding-welcome": "欢迎",
"onboarding-intro": "Fintunes可以在任何地方播放Jellyfin库中的音乐。",
"onboarding-cta": "在开始前你需要一个Jellyfin服务器。点击下方的按钮连接到服务器并登录。",
"set-jellyfin-server": "设置Jellyfin服务器",
"set-jellyfin-server-instruction": "设置Jellyfin服务器的完整网址包括HTTP/HTTS和端口。",
"set-server": "设置Jellyfin服务器",
"set-server-instruction": "设置Jellyfin服务器的完整网址包括HTTP/HTTS和端口。",
"settings": "设置",
"jellyfin-library": "Jellyfin库",
"jellyfin-server-url": "Jellyfin服务器网址",
"jellyfin-access-token": "Jellyfin Access Token",
"jellyfin-user-id": "Jellyfin用户ID",
"library": "Jellyfin库",
"server-url": "Jellyfin服务器网址",
"access-token": "Jellyfin Access Token",
"user-id": "Jellyfin用户ID",
"setting-cache": "缓存",
"setting-cache-description": "如果你更新了Jellyfin库但软件仍显示之前的内容你可以点击下面的按钮强制刷新。这将强制软件重新从服务器获取信息。",
"reset-cache": "清除缓存",
@@ -75,5 +75,6 @@
"confirm-delete-all-tracks": "您确定要删除所有当前下载的曲目吗?",
"delete": "删除",
"cancel": "取消",
"disc": "歧视"
"disc": "歧视",
"lyrics": "歌词"
}

View File

@@ -13,13 +13,13 @@ export type LocaleKeys = 'play-next'
| 'onboarding-welcome'
| 'onboarding-intro'
| 'onboarding-cta'
| 'set-jellyfin-server'
| 'set-jellyfin-server-instruction'
| 'set-server'
| 'set-server-instruction'
| 'settings'
| 'jellyfin-library'
| 'jellyfin-server-url'
| 'jellyfin-access-token'
| 'jellyfin-user-id'
| 'library'
| 'server-url'
| 'access-token'
| 'user-id'
| 'setting-cache'
| 'setting-cache-description'
| 'reset-cache'
@@ -75,4 +75,8 @@ export type LocaleKeys = 'play-next'
| 'delete'
| 'cancel'
| 'disc'
| 'lyrics';
| 'lyrics'
| 'direct-play'
| 'transcoded'
| 'khz'
| 'kbps'

View File

@@ -32,6 +32,7 @@ const HeaderContainer = styled.View`
`;
const NavigationHeader: React.FC = () => {
const type = useTypedSelector((state) => state.settings.credentials?.type);
const navigation = useNavigation<NavigationProp>();
const handleAllAlbumsClick = useCallback(() => { navigation.navigate('Albums'); }, [navigation]);
const handlePlaylistsClick = useCallback(() => { navigation.navigate('Playlists'); }, [navigation]);
@@ -39,9 +40,17 @@ const NavigationHeader: React.FC = () => {
return (
<>
<ListButton onPress={handleAllAlbumsClick} testID="all-albums">{t('all-albums')}</ListButton>
<ListButton onPress={handleArtistsClick} testID="artists">{t('artists')}</ListButton>
<ListButton onPress={handlePlaylistsClick} testID="playlists">{t('playlists')}</ListButton>
<ListButton onPress={handleAllAlbumsClick} testID="all-albums">
{t('all-albums')}
</ListButton>
<ListButton onPress={handleArtistsClick} testID="artists">
{t('artists')}
</ListButton>
{type === 'jellyfin' && (
<ListButton onPress={handlePlaylistsClick} testID="playlists">
{t('playlists')}
</ListButton>
)}
<ListContainer>
<HeaderContainer>
<Header>{t('recent-albums')}</Header>

View File

@@ -114,7 +114,7 @@ const TrackListView: React.FC<TrackListViewProps> = ({
return [['0', trackIds]];
}
const groups = groupBy(trackIds, (id) => tracks[id].ParentIndexNumber);
const groups = groupBy(trackIds, (id) => tracks[id]?.ParentIndexNumber);
return Object.entries(groups);
}, [trackIds, tracks, listNumberingStyle]);

View File

@@ -38,7 +38,7 @@ const Logo = styled.Image`
function Onboarding() {
// Get account from Redux and dispatcher
const account = useTypedSelector(state => state.settings.jellyfin);
const account = useTypedSelector(state => state.settings.credentials);
const dispatch = useAppDispatch();
// Also retrieve the navigation handler so that we can open the modal in
@@ -71,7 +71,7 @@ function Onboarding() {
</Text>
<ButtonContainer>
<Button
title={t('set-jellyfin-server')}
title={t('set-server')}
onPress={handleClick}/>
</ButtonContainer>
</TextContainer>

View File

@@ -27,7 +27,7 @@ export function SettingsList() {
return (
<SafeScrollView>
<ListButton onPress={handleLibraryClick}>{t('jellyfin-library')}</ListButton>
<ListButton onPress={handleLibraryClick}>{t('library')}</ListButton>
<ListButton onPress={handleCacheClick}>{t('setting-cache')}</ListButton>
<ListButton onPress={handleSentryClick}>{t('error-reporting')}</ListButton>
<ListButton onPress={handlePlaybackReportingClick}>{t('playback-reporting')}</ListButton>
@@ -50,7 +50,7 @@ export default function Settings() {
headerBackground: () => <ColoredBlurView style={StyleSheet.absoluteFill} />,
}}>
<Stack.Screen name="SettingList" component={SettingsList} options={{ headerTitle: t('settings') }} />
<Stack.Screen name="Library" component={Library} options={{ headerTitle: t('jellyfin-library') }} />
<Stack.Screen name="Library" component={Library} options={{ headerTitle: t('library') }} />
<Stack.Screen name="Cache" component={Cache} options={{ headerTitle: t('setting-cache') }} />
<Stack.Screen name="Sentry" component={Sentry} options={{ headerTitle: t('error-reporting') }} />
<Stack.Screen name="Playback Reporting" component={PlaybackReporting} options={{ headerTitle: t('playback-reporting')}} />

View File

@@ -11,25 +11,25 @@ import { InputContainer, Input } from '../components/Input';
export default function LibrarySettings() {
const defaultStyles = useDefaultStyles();
const { jellyfin } = useTypedSelector(state => state.settings);
const { credentials } = useTypedSelector(state => state.settings);
const navigation = useNavigation<NavigationProp>();
const handleSetLibrary = useCallback(() => navigation.navigate('SetJellyfinServer'), [navigation]);
return (
<Container>
<InputContainer>
<Paragraph style={defaultStyles.text}>{t('jellyfin-server-url')}</Paragraph>
<Input placeholder="https://jellyfin.yourserver.com/" value={jellyfin?.uri} editable={false} style={defaultStyles.input} />
<Paragraph style={defaultStyles.text}>{t('server-url')}</Paragraph>
<Input placeholder="https://jellyfin.yourserver.com/" value={credentials?.uri} editable={false} style={defaultStyles.input} />
</InputContainer>
<InputContainer>
<Paragraph style={defaultStyles.text}>{t('jellyfin-access-token')}</Paragraph>
<Input placeholder="deadbeefdeadbeefdeadbeef" value={jellyfin?.access_token} editable={false} style={defaultStyles.input} />
<Paragraph style={defaultStyles.text}>{t('access-token')}</Paragraph>
<Input placeholder="deadbeefdeadbeefdeadbeef" value={credentials?.access_token} editable={false} style={defaultStyles.input} />
</InputContainer>
<InputContainer>
<Paragraph style={defaultStyles.text}>{t('jellyfin-user-id')}</Paragraph>
<Input placeholder="deadbeefdeadbeefdeadbeef" value={jellyfin?.user_id} editable={false} style={defaultStyles.input} />
<Paragraph style={defaultStyles.text}>{t('user-id')}</Paragraph>
<Input placeholder="deadbeefdeadbeefdeadbeef" value={credentials?.user_id} editable={false} style={defaultStyles.input} />
</InputContainer>
<Button title={t('set-jellyfin-server')} onPress={handleSetLibrary} />
<Button title={t('set-server')} onPress={handleSetLibrary} />
</Container>
);
}

View File

@@ -19,6 +19,7 @@ const previous = () => TrackPlayer.skipToPrevious();
const Container = styled.View`
align-items: center;
margin-top: 40px;
margin-bottom: 52px;
`;
const Buttons = styled.View`

View File

@@ -0,0 +1,79 @@
import { Text } from '@/components/Typography';
import useCurrentTrack from '@/utility/useCurrentTrack';
import React from 'react-native';
import WaveformIcon from '@/assets/icons/waveform.svg';
import useDefaultStyles from '@/components/Colors';
import styled, { css } from 'styled-components/native';
import { useMemo } from 'react';
import { t } from '@/localisation';
const Container = styled.View`
flex-direction: row;
gap: 8px;
margin-top: 12px;
margin-bottom: 16px;
`;
const Info = styled.View`
flex-direction: row;
justify-content: space-between;
gap: 8px;
flex-grow: 1;
flex-shrink: 1;
`;
const Label = styled(Text)<{ overflow?: boolean }>`
opacity: 0.5;
font-size: 13px;
${(props) => props?.overflow && css`
flex: 0 1 auto;
`}
`;
/**
* This component displays information about the media that is being played
* back, such as the bitrate, sample rate, codec and whether it's transcoded.
*/
export default function MediaInformation() {
const styles = useDefaultStyles();
const { track, albumTrack } = useCurrentTrack();
const mediaStream = useMemo(() => (
albumTrack?.MediaStreams?.find((d) => d.Type === 'Audio')
), [albumTrack]);
if (!albumTrack || !track) {
return null;
}
return (
<Container>
<WaveformIcon fill={styles.icon.color} height={16} width={16} />
<Info>
<Label numberOfLines={1} overflow>
{track.isDirectPlay ? t('direct-play') : t('transcoded')}
</Label>
<Label numberOfLines={1}>
{track.isDirectPlay
? mediaStream?.Codec.toUpperCase()
: track.contentType?.replace('audio/', '').toUpperCase()
}
</Label>
{mediaStream && (
<>
<Label numberOfLines={1}>
{((track.isDirectPlay ? mediaStream.BitRate : track.bitRate) / 1000)
.toFixed(0)}
{t('kbps')}
</Label>
<Label numberOfLines={1}>
{(mediaStream.SampleRate / 1000).toFixed(1)}
{t('khz')}
</Label>
</>
)}
</Info>
</Container>
);
}

View File

@@ -13,7 +13,6 @@ import { t } from '@/localisation';
const Container = styled.View`
align-self: flex-start;
align-items: flex-start;
margin-top: 52px;
padding: 8px;
margin-left: -8px;
flex: 0 1 auto;

View File

@@ -12,6 +12,7 @@ import Timer from './components/Timer';
import styled from 'styled-components/native';
import { ColoredBlurView } from '@/components/Colors.tsx';
import LyricsPreview from './components/LyricsPreview.tsx';
import MediaInformation from './components/MediaInformation';
const Group = styled.View`
flex-direction: row;
@@ -28,6 +29,7 @@ export default function Player() {
<NowPlaying />
<ConnectionNotice />
<StreamStatus />
<MediaInformation />
<ProgressBar />
<MediaControls />
<Group>

View File

@@ -5,9 +5,50 @@ import { AppState } from '@/store';
interface Props {
serverUrl: string;
onCredentialsRetrieved: (credentials: AppState['settings']['jellyfin']) => void;
onCredentialsRetrieved: (credentials: AppState['settings']['credentials']) => void;
}
type CredentialEventData = {
credentials: {
Servers: {
ManualAddress: string,
ManualAddressOnly: boolean,
IsLocalServer: boolean,
DateLastAccessed: number,
LastConnectionMode: number,
Type: string,
Name: string,
Id: string,
UserId: string | null,
AccessToken: string | null,
Users: {
UserId: string,
AccessToken: string,
}[]
LocalAddress: string,
RemoteAddress: string,
}[]
},
deviceId: string,
type: 'emby',
} | {
credentials: {
Servers: {
ManualAddress: string,
manualAddressOnly: boolean,
DateLastAccessed: number,
LastConnectionMode: number,
Name: string,
Id: string,
UserId: string | null,
AccessToken: string | null,
LocalAddress: string,
}[]
},
deviceId: string,
type: 'jellyfin',
} | undefined;
class CredentialGenerator extends Component<Props> {
ref = createRef<WebView>();
@@ -18,12 +59,18 @@ class CredentialGenerator extends Component<Props> {
checkIfCredentialsAreThere = debounce(() => {
// Inject some javascript to check if the credentials can be extracted
// from localstore
// from localstore. We simultaneously attempt to extract credentials for
// any back-end.
this.ref.current?.injectJavaScript(`
try {
let credentials = JSON.parse(window.localStorage.getItem('jellyfin_credentials'));
let deviceId = window.localStorage.getItem('_deviceId2');
window.ReactNativeWebView.postMessage(JSON.stringify({ credentials, deviceId }))
window.ReactNativeWebView.postMessage(JSON.stringify({ credentials, deviceId, type: 'jellyfin' }))
} catch(e) { }; true;
try {
let credentials = JSON.parse(window.localStorage.getItem('servercredentials3'));
let deviceId = window.localStorage.getItem('_deviceId2');
window.ReactNativeWebView.postMessage(JSON.stringify({ credentials, deviceId, type: 'emby' }))
} catch(e) { }; true;
`);
}, 500);
@@ -35,36 +82,73 @@ class CredentialGenerator extends Component<Props> {
}
// Parse the content
const data = JSON.parse(event.nativeEvent.data);
const data = JSON.parse(event.nativeEvent.data) as CredentialEventData;
if (__DEV__) {
console.log('Received credential event data: ', JSON.stringify(data));
}
if (!data.deviceId
|| !data.credentials?.Servers?.length
|| !data.credentials?.Servers[0]?.UserId
|| !data.credentials?.Servers[0]?.AccessToken) {
// Since Jellyfin and Emby are similar, we'll attempt to extract the
// credentials in a generic way.
let userId: string | undefined, accessToken: string | undefined;
// GUARD: Attempt to extract emby format credentials
if (data?.type === 'emby'
&& data.credentials?.Servers?.length
&& data.credentials?.Servers[0]?.Users?.length
) {
userId = data.credentials.Servers[0].Users[0].UserId;
accessToken = data.credentials.Servers[0].Users[0].AccessToken;
// GUARD: Attempt to extract jellyfin format credentials
} else if (data?.type === 'jellyfin'
&& data.credentials?.Servers?.length
) {
userId = data.credentials.Servers[0].UserId || undefined;
accessToken = data.credentials.Servers[0].AccessToken || undefined;
}
// We can extract the deviceId and server address in the same way for
// both Jellyfin and Emby.
const deviceId = data?.deviceId;
const address = data?.credentials?.Servers?.length
&& data?.credentials.Servers[0].ManualAddress;
// GUARD: log extract credentials in dev
if (__DEV__) {
console.log('Extracted the following credentials:', { userId, accessToken, deviceId, address });
}
// GUARD: Check that all the required credentials are available
if (!userId || !accessToken || !deviceId || !address) {
if (__DEV__) {
console.error('Failed to extract credentials from event');
}
return;
}
const { credentials: { Servers: [ credentials ] }, deviceId } = data;
// Attempt to perform a request using the credentials to see if they're
// good
const response = await fetch(`${credentials.ManualAddress}/Users/Me`, {
const response = await fetch(`${address}/Users/${userId}`, {
headers: {
'X-Emby-Authorization': `MediaBrowser Client="", Device="", DeviceId="", Version="", Token="${credentials.AccessToken}"`
'X-Emby-Authorization': `MediaBrowser Client="", Device="", DeviceId="", Version="", Token="${accessToken}"`
}
});
// GUARD: The request must succeed
if (response.status !== 200) {
if (__DEV__) {
const body = await response.text();
console.error('Failed to retrieve user object using credentials:', response.status, body);
}
return;
}
// If a message is received, the credentials should be there
this.props.onCredentialsRetrieved({
uri: credentials.ManualAddress,
user_id: credentials.UserId,
access_token: credentials.AccessToken,
uri: address,
user_id: userId,
access_token: accessToken,
device_id: deviceId,
type: data.type,
});
};

View File

@@ -22,7 +22,7 @@ export default function SetJellyfinServer() {
const navigation = useNavigation();
// Save creedentials to store and close the modal
const saveCredentials = useCallback((credentials: AppState['settings']['jellyfin']) => {
const saveCredentials = useCallback((credentials: AppState['settings']['credentials']) => {
if (credentials) {
dispatch(setJellyfinCredentials(credentials));
navigation.dispatch(StackActions.popToTop());
@@ -39,7 +39,7 @@ export default function SetJellyfinServer() {
) : (
<View style={{ padding: 20, flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>
{t('set-jellyfin-server-instruction')}
{t('set-server-instruction')}
</Text>
<Input
placeholder="https://jellyfin.yourserver.io/"
@@ -51,7 +51,7 @@ export default function SetJellyfinServer() {
style={[ defaultStyles.input, { width: '100%' } ]}
/>
<Button
title={t('set-jellyfin-server')}
title={t('set-server')}
onPress={() => setIsLogginIn(true)}
disabled={!serverUrl?.length}
color={defaultStyles.themeColor.color}

View File

@@ -64,6 +64,23 @@ const persistConfig: PersistConfig<Omit<AppState, '_persist'>> = {
}
};
},
// @ts-expect-error migrations are poorly typed
5: (state: AppState) => {
// @ts-expect-error
const credentials = state.settings.jellyfin && {
// @ts-expect-error
...(state.settings.jellyfin as AppState['settings']['credentials']),
type: 'jellyfin',
};
return {
...state,
settings: {
...state.settings,
credentials,
},
};
}
})
};

View File

@@ -8,6 +8,29 @@ export interface UserData {
Key: string;
}
export interface MediaStream {
Codec: string
TimeBase: string
VideoRange: string
VideoRangeType: string
AudioSpatialFormat: string
DisplayTitle: string
IsInterlaced: boolean
ChannelLayout: string
BitRate: number
Channels: number
SampleRate: number
IsDefault: boolean
IsForced: boolean
IsHearingImpaired: boolean
Type: string
Index: number
IsExternal: boolean
IsTextSubtitleStream: boolean
SupportsExternalStream: boolean
Level: number
}
export interface ArtistItem {
Name: string;
Id: string;
@@ -71,6 +94,7 @@ export interface AlbumTrack {
MediaType: string;
HasLyrics: boolean;
Lyrics: Lyrics | null;
MediaStreams: MediaStream[];
}
export interface State {

View File

@@ -1,7 +1,7 @@
import { createAction } from '@reduxjs/toolkit';
import { ColorScheme } from './types';
export const setJellyfinCredentials = createAction<{ access_token: string, user_id: string, uri: string, device_id: string; }>('SET_JELLYFIN_CREDENTIALS');
export const setJellyfinCredentials = createAction<{ access_token: string, user_id: string, uri: string, device_id: string; type: 'jellyfin' | 'emby' }>('SET_JELLYFIN_CREDENTIALS');
export const setBitrate = createAction<number>('SET_BITRATE');
export const setOnboardingStatus = createAction<boolean>('SET_ONBOARDING_STATUS');
export const setReceivedErrorReportingAlert = createAction<void>('SET_RECEIVED_ERROR_REPORTING_ALERT');

View File

@@ -3,11 +3,12 @@ import { setReceivedErrorReportingAlert, setBitrate, setJellyfinCredentials, set
import { ColorScheme } from './types';
interface State {
jellyfin?: {
credentials?: {
uri: string;
user_id: string;
access_token: string;
device_id: string;
type: 'jellyfin' | 'emby';
}
bitrate: number;
isOnboardingComplete: boolean;
@@ -27,7 +28,7 @@ const initialState: State = {
const settings = createReducer(initialState, builder => {
builder.addCase(setJellyfinCredentials, (state, action) => ({
...state,
jellyfin: action.payload,
credentials: action.payload,
}));
builder.addCase(setBitrate, (state, action) => ({
...state,

View File

@@ -36,7 +36,9 @@ export async function retrieveAlbum(id: string): Promise<Album> {
const latestAlbumsOptions = {
IncludeItemTypes: 'MusicAlbum',
Fields: 'DateCreated',
SortOrder: 'Ascending',
SortOrder: 'Descending',
SortBy: 'DateCreated',
Recursive: 'true',
};
/**
@@ -51,7 +53,8 @@ export async function retrieveRecentAlbums(numberOfAlbums = 24) {
const params = new URLSearchParams(options).toString();
// Retrieve albums
return fetchApi<Album[]>(({ user_id }) => `/Users/${user_id}/Items/Latest?${params}`);
return fetchApi<{ Items: Album[] }>(({ user_id }) => `/Users/${user_id}/Items?${params}`)
.then((d) => d.Items);
}
/**
@@ -61,6 +64,7 @@ export async function retrieveAlbumTracks(ItemId: string) {
const singleAlbumOptions = {
ParentId: ItemId,
SortBy: 'ParentIndexNumber,IndexNumber,SortName',
Fields: 'MediaStreams',
};
const singleAlbumParams = new URLSearchParams(singleAlbumOptions).toString();

View File

@@ -1,6 +1,17 @@
import type { AppState, Store } from '@/store';
import { Platform } from 'react-native';
import { version } from '../../../package.json';
type Credentials = AppState['settings']['jellyfin'];
type Credentials = AppState['settings']['credentials'];
/** Map the output of `Platform.OS`, so that Jellyfin can understand it. */
const deviceMap: Record<typeof Platform['OS'], string> = {
ios: 'iOS',
android: 'Android',
macos: 'macOS',
web: 'Web',
windows: 'Windows',
};
/**
* This is a convenience function that converts a set of Jellyfin credentials
@@ -10,11 +21,14 @@ type Credentials = AppState['settings']['jellyfin'];
function generateConfig(credentials: Credentials): RequestInit {
return {
headers: {
'X-Emby-Authorization': `MediaBrowser Client="", Device="", DeviceId="", Version="", Token="${credentials?.access_token}"`
'X-Emby-Authorization': `MediaBrowser Client="Fintunes", Device="${deviceMap[Platform.OS]}", DeviceId="${credentials?.device_id}", Version="${version}", Token="${credentials?.access_token}"`
}
};
}
/**
* Retrieve a copy of the store without getting caught in import cycles.
*/
export function asyncFetchStore() {
return require('@/store').default as Store;
}
@@ -33,7 +47,7 @@ export async function fetchApi<T>(
parseResponse = true
) {
// Retrieve the latest credentials from the Redux store
const credentials = asyncFetchStore().getState().settings.jellyfin;
const credentials = asyncFetchStore().getState().settings.credentials;
// GUARD: Check that the credentials are present
if (!credentials) {
@@ -74,7 +88,7 @@ export async function fetchApi<T>(
const data = await response.json();
throw data;
} catch {
throw response;
throw new Error('FailedRequest');
}
}
@@ -92,7 +106,7 @@ export async function fetchApi<T>(
* Retrieve an image URL for a given ItemId
*/
export function getImage(ItemId: string): string {
const credentials = asyncFetchStore().getState().settings.jellyfin;
const credentials = asyncFetchStore().getState().settings.credentials;
const uri = encodeURI(`${credentials?.uri}/Items/${ItemId}/Images/Primary?format=jpeg`);
return uri;
}

View File

@@ -48,6 +48,7 @@ export async function sendPlaybackEvent(
ItemId: activeTrack.backendId,
CanSeek: true,
PlaybackStartTimeTicks: null,
PlaySessionId: activeTrack?.backendId || 'fintunes',
};
// Generate a config from the credentials and dispatch the request

View File

@@ -27,7 +27,7 @@ export async function retrieveAllPlaylists() {
* Retrieve all albums that are available on the Jellyfin server
*/
export async function retrievePlaylistTracks(ItemId: string) {
const credentials = asyncFetchStore().getState().settings.jellyfin;
const credentials = asyncFetchStore().getState().settings.credentials;
const singlePlaylistOptions = {
SortBy: 'IndexNumber,SortName',
UserId: credentials?.user_id || '',

View File

@@ -22,14 +22,15 @@ const baseTrackOptions: Record<string, string> = {
TranscodingContainer: 'aac',
AudioCodec: 'aac',
Container: 'mp3,aac',
audioBitRate: '320000',
...trackOptionsOsOverrides[Platform.OS],
};
} as const;
/**
* Generate the track streaming url from the trackId
*/
export function generateTrackUrl(trackId: string) {
const credentials = store.getState().settings.jellyfin;
const credentials = store.getState().settings.credentials;
const trackOptions = {
...baseTrackOptions,
UserId: credentials?.user_id || '',
@@ -47,10 +48,12 @@ export function generateTrackUrl(trackId: string) {
* Generate a track object from a Jellyfin ItemId so that
* react-native-track-player can easily consume it.
*/
export function generateTrack(track: AlbumTrack): Track {
export async function generateTrack(track: AlbumTrack): Promise<Track> {
// Also construct the URL for the stream
const url = generateTrackUrl(track.Id);
const response = await fetch(url, { method: 'HEAD' });
return {
url,
backendId: track.Id,
@@ -63,6 +66,9 @@ export function generateTrack(track: AlbumTrack): Track {
: getImage(track.Id),
hasLyrics: track.HasLyrics,
lyrics: track.Lyrics,
contentType: response.headers.get('Content-Type') || undefined,
isDirectPlay: response.headers.has('Content-Length'),
bitRate: baseTrackOptions.audioBitRate,
};
}

View File

@@ -27,9 +27,9 @@ export default function useCurrentTrack(): CurrentTrackResponse {
// Retrieve the current track from the queue using the index
const retrieveCurrentTrack = useCallback(async () => {
const queue = await TrackPlayer.getQueue();
const currentTrackIndex = await TrackPlayer.getCurrentTrack();
if (currentTrackIndex !== null) {
setTrack(queue[currentTrackIndex] as Track);
const currentTrackIndex = await TrackPlayer.getActiveTrackIndex();
if (currentTrackIndex !== undefined) {
setTrack(queue[currentTrackIndex]);
setIndex(currentTrackIndex);
} else {
setTrack(undefined);

View File

@@ -49,7 +49,7 @@ export default function usePlayTracks() {
const queue = await TrackPlayer.getQueue();
// Convert all trackIds to the relevant format for react-native-track-player
const generatedTracks = trackIds.map((trackId) => {
const generatedTracks = (await Promise.all(trackIds.map(async (trackId) => {
const track = tracks[trackId];
// GUARD: Check that the track actually exists in Redux
@@ -58,7 +58,7 @@ export default function usePlayTracks() {
}
// Retrieve the generated track from Jellyfin
const generatedTrack = generateTrack(track);
const generatedTrack = await generateTrack(track);
// Check if a downloaded version exists, and if so rewrite the URL
const download = downloads[trackId];
@@ -67,7 +67,7 @@ export default function usePlayTracks() {
}
return generatedTrack;
}).filter((t): t is Track => typeof t !== 'undefined');
}))).filter((t): t is Track => typeof t !== 'undefined');
// Potentially shuffle all tracks
const newTracks = shuffle ? shuffleArray(generatedTracks) : generatedTracks;