diff --git a/composeApp/src/commonMain/composeResources/values-fr/strings.xml b/composeApp/src/commonMain/composeResources/values-fr/strings.xml
index c6316cdd7..c524aaacf 100644
--- a/composeApp/src/commonMain/composeResources/values-fr/strings.xml
+++ b/composeApp/src/commonMain/composeResources/values-fr/strings.xml
@@ -1,5 +1,6 @@
+ Sources de données, remerciements et licences de plateforme
Reconnaissance et crédits du projet
Retour
Annuler
@@ -18,21 +19,24 @@
Reprendre
Réessayer
Enregistrer
+ Enregistrement…
+ Valider
Installation en cours
Addons
Actif
%1$d catalogues
Configurable
+ Désactivé
Actualisation
%1$d ressources
Indisponible
- Configurer l’addon
- Supprimer l’addon
+ Configurer l'addon
+ Supprimer l'addon
Ajoute une URL de manifeste pour commencer à charger des catalogues, métadonnées, streams ou sous-titres dans Nuvio.
Aucun addon installé.
- Saisis une URL d’addon.
- URL de l’addon
- Installer l’addon
+ Saisis une URL d'addon.
+ URL de l'addon
+ Installer l'addon
Chargement des détails du manifeste…
Validation de l’URL du manifeste et chargement des détails de l’addon avant installation.
Vérification de l’addon
@@ -58,7 +62,7 @@
Ajouter un catalogue
Ajouter un dossier
Tous les genres
- Ajoute des catalogues depuis tes addons installés pour définir ce qu’affiche ce dossier.
+ Ajoute des catalogues depuis tes addons installés pour définir ce qu'affiche ce dossier.
Aucune source de catalogue
Choisir
Emoji
@@ -69,8 +73,8 @@
Terminé
Modifier la collection
Modifier le dossier
- Configure l’identité, la présentation et les sources de catalogue du dossier avec la même structure que l’éditeur principal de collections.
- Ajoute-en un pour commencer.
+ Configure l'identité, la présentation et les sources de catalogue du dossier avec la même structure que l'éditeur principal de collections.
+ Ajoutes-en un pour commencer.
Aucun dossier
Dossiers
Filtre de genre
@@ -88,7 +92,7 @@
Apparence
Informations de base
Sources de catalogue
- Choisis les catalogues d’addon que ce dossier doit regrouper.
+ Choisis les catalogues d'addon que ce dossier doit regrouper.
Sélectionner des catalogues
Sélectionner un genre
%1$d sélectionné(s)
@@ -114,14 +118,14 @@
Personne
Réalisateur
Personnalisé
- Choisis une source prédéfinie. Tu peux la modifier ou la supprimer après l’avoir ajoutée.
- Colle une URL de liste publique TMDB ou uniquement le numéro de l’URL.
- Recherche par nom de studio, ou colle un ID/URL de société TMDB et ajoute-le directement.
+ Choisis une source prédéfinie. Tu peux la modifier ou la supprimer après l'avoir ajoutée.
+ Colle une URL de liste publique TMDB ou uniquement le numéro de l'URL.
+ Cherche par nom de studio, ou colle un ID/URL de société TMDB et ajoute-le directement.
Saisis un ID de chaîne. Les chaînes courantes sont disponibles dans les préréglages et les filtres rapides.
- Recherche le nom d’une collection de films ou colle l’ID de collection TMDB.
- Saisis un ID ou une URL de personne TMDB pour créer une ligne à partir de ses crédits d’acteur.
- Saisis un ID ou une URL de personne TMDB pour créer une ligne à partir de ses crédits de réalisation.
- Crée une ligne TMDB dynamique avec des filtres optionnels. Laisse les champs vides si tu n’as pas besoin de ce filtre.
+ Cherche le nom d'une collection de films ou colle l'ID de collection TMDB.
+ Saisis un ID ou une URL de personne TMDB pour créer une ligne à partir des crédits de casting.
+ Saisis un ID ou une URL de personne TMDB pour créer une ligne à partir des crédits de réalisation.
+ Crée une ligne TMDB dynamique avec des filtres optionnels. Laisse les champs vides si tu n'as pas besoin de ce filtre.
Liste publique TMDB
ID de chaîne
ID de collection
@@ -154,7 +158,7 @@
Les deux
Tri
Filtres
- Laisse les champs vides si tu n’as pas besoin de ce filtre.
+ Laisse les champs vides si tu n'as pas besoin de ce filtre.
Genres rapides
Langues rapides
Pays rapides
@@ -163,27 +167,20 @@
Chaînes rapides
ID de genre
Utilise des numéros de genre TMDB. Sépare plusieurs valeurs par des virgules pour ET, ou des barres verticales pour OU.
- 28,12
- 18,35
Date de sortie ou de diffusion depuis
- Date de sortie ou de diffusion jusqu’au
+ Date de sortie ou de diffusion jusqu'au
Utilise le format AAAA-MM-JJ, ex. 2024-01-01.
- 2020-01-01
- 2024-12-31
Note minimale
Note maximale
Note TMDB de 0 à 10. Exemple : 7.0.
7.0
10
Votes minimum
- Utilise ceci pour éviter les titres peu connus avec peu de votes. Exemple : 100.
- 100
+ Utilise ceci pour éviter les titres peu connus avec peu de votes. Exemple : 100.
Langue originale
Utilise des codes de langue à deux lettres, ex. en, ko, ja, hi.
- en, ko, ja, hi
- Pays d’origine
+ Pays d'origine
Utilise des codes de pays à deux lettres, ex. US, KR, JP, IN.
- US, KR, JP, IN
ID de mots-clés
Utilise des numéros de mots-clés TMDB. Les puces rapides remplissent des exemples courants.
9715 pour super-héros
@@ -195,7 +192,6 @@
213 pour Netflix
Année
Utilise une année à quatre chiffres, ex. 2024.
- 2024
Préréglages
Rechercher
Ajouter une source
@@ -203,9 +199,9 @@
Modifier la liste Trakt
Listes Trakt
Liste Trakt
- Titre, URL Trakt ou ID de liste
- Utilise une URL de liste Trakt publique, un ID numérique de liste, ou recherche par nom.
- Films du week-end, Films primés
+ Rechercher un titre, URL Trakt ou ID de liste
+ Utilise une URL publique de liste Trakt ou un ID numérique de liste, ou cherche par nom.
+ Programme du week-end, Lauréats
Résultats de recherche
Listes en vogue
Listes populaires
@@ -262,15 +258,27 @@
Populaire
Mieux notés
Récent
- Original
+ Les plus votés
+ Région de visionnage
+ Code pays ISO 3166-1 où le titre est disponible. Exemple : US, GB.
+ Régions rapides
+ ID des fournisseurs de visionnage
+ Utilise les ID de fournisseurs de visionnage TMDB. Sépare-les par des virgules pour ET, ou par des pipes pour OU.
+ 8|337|350
+ Fournisseurs rapides
+ Netflix
+ Prime Video
+ Disney+
+ Apple TV+
+ Hulu
Liste TMDB
Collection de films TMDB
Production
Chaîne
Personne
Réalisateur
- Découverte TMDB
- Crée-en une pour organiser tes catalogues.
+ TMDB Discover
+ Crées-en une pour organiser tes catalogues.
Aucune collection
%1$d dossier(s)
Aucun élément trouvé
@@ -290,7 +298,7 @@
Activé
Pause
Recharger
- Tu as déjà un compte ?
+ Tu as déjà un compte ?
Continuer sans compte
Créer un compte
Pas encore de compte ?
@@ -300,7 +308,7 @@
Connecte-toi pour accéder à ta bibliothèque et ta progression
Se connecter
Inscris-toi pour synchroniser tes données entre appareils
- S’inscrire
+ S'inscrire
Tes données seront uniquement stockées localement
Regarde tout, partout
Bon retour
@@ -369,7 +377,7 @@
Aucun addon actif
Les catalogues installés n’ont retourné aucun résultat pour cette requête.
Aucun résultat trouvé
- Tes addons installés n’exposent pas de catalogue de recherche.
+ Tes addons installés n'exposent pas de catalogue de recherche.
Aucun catalogue de recherche
Rechercher des films, séries…
Recherches récentes
@@ -381,8 +389,10 @@
Apparence
Contenu et découverte
Continuer à regarder
- Écran d’accueil
+ Services connectés
+ Écran d'accueil
Intégrations
+ Licences et attributions
Notes MDBList
Écran méta
Notifications
@@ -396,18 +406,47 @@
À PROPOS
Gère ton compte, déconnecte-toi ou supprime-le.
COMPTE
- Ajuste la présentation de l’accueil et les préférences visuelles.
- Rechercher de nouvelles versions de l’application.
+ Ajuste la présentation de l'accueil et les préférences visuelles.
+ Rechercher de nouvelles versions de l'application.
Vérifier les mises à jour
Gère les addons et sources de découverte.
Gère tes films et épisodes téléchargés.
Téléchargements
GÉNÉRAL
Connecte les services TMDB et MDBList.
- Gère les alertes de sortie d’épisodes et envoie une notification de test.
+ Gère les alertes de sortie d'épisodes et envoie une notification de test.
Basculer vers un profil différent.
Changer de profil
Connecte Trakt, synchronise des listes et enregistre des titres directement dans Trakt.
+ Aucun paramètre trouvé.
+ Rechercher dans les paramètres…
+ RÉSULTATS
+ LICENCE DE L’APP
+ DONNÉES ET SERVICES
+ LICENCE DE LECTURE
+ Nuvio Mobile
+ Le code source et les termes de la licence sont disponibles dans le dépôt du projet.
+ Distribué sous la licence GNU General Public License v3.0.
+ The Movie Database (TMDB)
+ Nuvio utilise l’API TMDB pour les métadonnées de films et séries, les visuels, les bandes-annonces, le casting, les détails de production, les collections et les recommandations. Ce produit utilise l’API TMDB mais n’est ni approuvé ni certifié par TMDB.
+ Jeux de données non commerciaux IMDb
+ Nuvio utilise les jeux de données non commerciaux IMDb, y compris title.ratings.tsv.gz, pour les notes et le nombre de votes IMDb. Informations fournies par IMDb (https://www.imdb.com). Utilisé avec autorisation. Les données IMDb sont destinées à un usage personnel et non commercial selon les conditions d'IMDb.
+ Trakt
+ Nuvio se connecte à Trakt pour l’authentification du compte, l’historique de visionnage, la synchronisation de progression, les données de bibliothèque, les notes, les listes et les commentaires. Nuvio n’est ni affilié ni approuvé par Trakt.
+ Premiumize
+ Nuvio se connecte à Premiumize pour l’authentification du compte, l’accès à la cloud library, les vérifications de cache et les fonctionnalités de lecture cloud. Nuvio n’est ni affilié ni approuvé par Premiumize.
+ TorBox
+ Nuvio se connecte à TorBox pour l’authentification du compte, l’accès à la cloud library, les vérifications de cache et les fonctionnalités de lecture cloud. Nuvio n’est ni affilié ni approuvé par TorBox.
+ MDBList
+ Nuvio utilise MDBList pour les notes et les données de scores externes. Nuvio n’est ni affilié ni approuvé par MDBList.
+ IntroDB
+ Nuvio utilise l’API IntroDB pour les timestamps d’intro, de récap, de générique et d’aperçu fournis par la communauté et utilisés par les contrôles de saut. Nuvio n’est ni affilié ni approuvé par IntroDB.
+ MPVKit
+ Utilisé pour la lecture sur les builds iOS.
+ Le code source MPVKit seul est distribué sous LGPL v3.0. Les bundles MPVKit, y compris les bibliothèques libmpv et FFmpeg, sont également distribués sous LGPL v3.0.
+ AndroidX Media3 ExoPlayer 1.8.0
+ Utilisé pour la lecture sur les builds Android.
+ Distribué sous la licence Apache, version 2.0.
Chargement de tes listes Trakt…
Choisis où enregistrer ce titre dans Trakt
Faire un don
@@ -452,12 +491,12 @@
Supprimer le compte
Cela supprimera définitivement ton compte et toutes les données associées.
Cette action est irréversible. Toutes tes données, profils et historique de synchronisation seront définitivement supprimés.
- Supprimer le compte ?
+ Supprimer le compte ?
Adresse e-mail
Non connecté
Se déconnecter
- Tu seras redirigé vers l’écran de connexion.
- Se déconnecter ?
+ Tu seras redirigé vers l'écran de connexion.
+ Se déconnecter ?
Statut
Anonyme
Connecté
@@ -466,14 +505,16 @@
Langue de l’application
Choisir la langue
Afficher, masquer et ajuster le bandeau Continuer à regarder.
- Ajuste la largeur partagée des cartes d’affiches et les rayons des coins.
+ Liquid Glass
+ Utilise la barre d’onglets native iPhone sur iOS 26 et versions ultérieures. Le changement instantané de profil depuis la barre d’onglets n’est pas disponible quand cette option est activée.
+ Ajuste la largeur des cartes et le rayon des coins.
AFFICHAGE
ACCUEIL
THÈME
Collection • %1$s
Nom affiché
- Installe un addon avec des catalogues compatibles avec les tableaux pour configurer les lignes de l’écran d’accueil.
- Aucun catalogue d’accueil
+ Installe un addon avec des catalogues compatibles avec les tableaux pour configurer les lignes de l'écran d'accueil.
+ Aucun catalogue d'accueil
Source Hero
Masqué
Garder l’accueil en focus
@@ -491,16 +532,21 @@
SOURCES HERO
%1$d sur %2$d sélectionnés
Afficher le Hero
- Afficher un carrousel Hero en vedette en haut de l’accueil. Choisis jusqu’à 2 catalogues sources ci-dessous.
+ Afficher un carrousel Hero en vedette en haut de l'accueil. Choisis jusqu'à 2 catalogues sources ci-dessous.
+ Masquer le contenu non sorti
+ Masque les films et séries qui ne sont pas encore sortis.
+ Masquer le soulignement des catalogues
+ Supprime la ligne d’accent sous les titres de catalogues et de collections dans toute l’app.
%1$d sur %2$d catalogues visibles • %3$d sources Hero sélectionnées
Ouvre un catalogue uniquement si tu as besoin de le renommer ou de le réorganiser.
Visible
+ Masquer la valeur
Lecteur, sous-titres et lecture automatique
Rayon de carte
STYLE DE CARTE D’AFFICHE
Largeur de carte
Personnalisé
- Personnalise la largeur de carte et le rayon des coins pour les cartes d’affiches partagées dans toute l’application.
+ Ajuste la largeur des cartes et le rayon des coins.
Masquer les étiquettes
Mode paysage pour les affiches dans les rayons
Aperçu en direct
@@ -519,9 +565,20 @@
Dense
Grand
Standard
- Afficher une invite pour reprendre là où tu en étais à l’ouverture de l’application après avoir quitté le lecteur.
+ Afficher la valeur
+ Afficher une invite pour reprendre là où tu en étais à l'ouverture de l'application après avoir quitté le lecteur.
Invite de reprise au démarrage
- STYLE DE CARTE
+ Floute les vignettes du prochain épisode dans Continuer à regarder pour éviter les spoilers.
+ Flouter le contenu non vu dans Continuer à regarder
+ Inclut les épisodes à venir dans Continuer à regarder avant leur diffusion.
+ Afficher les épisodes à venir non diffusés
+ ORDRE DE TRI
+ Ordre de tri
+ Par défaut
+ Trier tous les éléments par récence
+ Style streaming
+ Éléments sortis d’abord, à venir à la fin
+ STYLE DE CARTE D'AFFICHE
AU DÉMARRAGE
COMPORTEMENT DE LA SUITE
VISIBILITÉ
@@ -531,19 +588,67 @@
Carte d’affiche centrée sur la couverture
Large
Carte horizontale riche en informations
- Activé : « La suite » reprend toujours depuis l’épisode le plus avancé vu. Désactivé : suit l’épisode le plus récemment visionné. Utile si tu revois des épisodes précédents.
- La suite depuis l’épisode le plus avancé
+ Quand activé, La suite reprend toujours depuis l'épisode le plus avancé vu. Quand désactivé, suit l'épisode le plus récemment visionné. Utile si tu revois des épisodes précédents.
+ La suite depuis l'épisode le plus avancé
+ Préfère les vignettes d’épisode quand elles sont disponibles.
+ Préférer les vignettes d’épisode dans Continuer à regarder
ACCUEIL
SOURCES
Installe, supprime, mets à jour et ordonne tes sources de contenu.
Installe des dépôts de scrapers JavaScript et teste des fournisseurs en interne.
- Contrôle quels catalogues apparaissent à l’accueil et dans quel ordre.
+ Contrôlez quels catalogues apparaissent à l'accueil et dans quel ordre.
Désactive des sections de détails et réorganise tout sous le Hero.
- Crée des regroupements de catalogues personnalisés avec des dossiers affichés à l’accueil.
+ Crée des regroupements de catalogues personnalisés avec des dossiers affichés à l'accueil.
INTÉGRATIONS
- Enrichis les pages de détails avec de l’art, des crédits, des métadonnées d’épisodes et plus depuis TMDB.
- Ajoute des notes externes d’IMDb, Rotten Tomatoes, Metacritic et d’autres aux pages de détails.
- Ajoute ta clé API MDBList ci-dessous avant d’activer les notes.
+ Enrichissez les pages de détails avec de l'art, des crédits, des métadonnées d'épisodes et plus depuis TMDB.
+ Ajoute des notes externes d'IMDb, Rotten Tomatoes, Metacritic et d'autres aux pages de détails.
+ Connecte des comptes pour les liens et l’accès à la bibliothèque
+ Services connectés
+ Ces intégrations sont expérimentales et peuvent être conservées, modifiées ou supprimées plus tard.
+ Cloud library
+ Parcoure et lis les fichiers déjà présents dans tes comptes connectés.
+ Résoudre les liens lisibles
+ Demande à un service connecté des liens lisibles quand un résultat le nécessite. Cela peut ajouter l’élément à ce service.
+ Résoudre avec
+ Choisis quel compte connecté gère les liens lisibles.
+ Connecte d’abord un compte.
+ Comptes
+ Connecte ton compte %1$s.
+ Lie ton compte %1$s dans le navigateur.
+ Clé API %1$s
+ Saisis ta clé API %1$s.
+ Saisir la clé API %1$s
+ Non défini
+ Connecté
+ Connecter %1$s
+ Déconnecter %1$s
+ Déconnecter
+ %1$s est connecté sur cet appareil.
+ Démarrage de la connexion sécurisée…
+ Ouvre le lien et saisis ce code pour approuver Nuvio.
+ Code copié.
+ Ouvrir le lien
+ En attente de l’approbation…
+ Impossible de démarrer la connexion.
+ Cette méthode de connexion n’est pas configurée dans ce build.
+ Ce code a expiré. Réessaie.
+ Préparation des liens
+ Préparer les liens
+ Résoudre les liens lisibles avant le démarrage de la lecture.
+ Liens à préparer
+ Utilise un nombre plus faible si possible. Les services connectés peuvent limiter le nombre de liens résolus sur une période donnée. Ouvrir un film ou un épisode peut compter dans ces limites même si tu n’appuies pas sur Regarder, car les liens sont préparés à l’avance.
+ 1 lien
+ %1$d liens
+ Mise en forme
+ Modèle de nom
+ Contrôle la manière dont les noms de résultats apparaissent.
+ Modèle de description
+ Contrôle les métadonnées affichées sous chaque résultat.
+ Réinitialiser la mise en forme
+ Restaurer la mise en forme par défaut des résultats.
+ Clé API validée.
+ Impossible de valider cette clé API.
+ Ajoute ta clé API MDBList ci-dessous avant d'activer les notes.
Obtiens une clé sur https://mdblist.com/preferences et colle-la ici.
Clé API
Clé API MDBList
@@ -553,7 +658,7 @@
FOURNISSEURS DE NOTES
MDBLIST
Actions
- Contrôles de lecture et de sauvegarde.
+ Contrôles de lecture et d'enregistrement.
Casting
Liste principale du casting.
Fond cinématographique
@@ -563,20 +668,22 @@
Commentaires
Section de commentaires Trakt.
Détails
- Durée, statut, sortie, langue et informations associées.
- Cartes d’épisodes
- Choisis comment les épisodes sont affichés sur l’écran de métadonnées.
+ Durée, statut, date de sortie, langue et informations associées.
+ Cartes d'épisodes
+ Choisis comment les épisodes sont affichés sur l'écran de métadonnées.
Horizontal
Cartes en ligne style fond
Liste
Cartes empilées centrées sur les détails
Épisodes
- Saisons et liste d’épisodes pour les séries.
+ Saisons et liste d'épisodes pour les séries.
+ Flouter les épisodes non vus
+ Floute les vignettes d’épisodes jusqu’à leur visionnage pour éviter les spoilers.
Groupe %1$d
- À voir aussi
- Rayon de recommandations.
+ Plus comme ceci
+ Visuels de recommandations TMDB sur la page de détail.
Aucun
- Résumé
+ Aperçu
Synopsis, notes, genres et crédits principaux.
Production
Studios et chaînes.
@@ -588,8 +695,8 @@
Bandes-annonces
Rayon de bandes-annonces et raccourcis de lecture.
Les notifications sont actuellement désactivées dans Nuvio.
- Alertes de sortie d’épisodes
- Programme des notifications locales lorsqu’un nouvel épisode d’une série enregistrée est disponible.
+ Alertes de sortie d'épisodes
+ Programmez des notifications locales lorsqu'un nouvel épisode d'une série sauvegardée est disponible.
Les notifications système sont désactivées pour Nuvio. Active-les pour recevoir des alertes et des notifications de test.
Il y a actuellement %1$d alertes de sortie programmées sur cet appareil.
ALERTES
@@ -597,11 +704,11 @@
Envoyer une notification de test
Envoi de la notification de test…
Envoyer une notification locale de test pour %1$s.
- Sauvegarde d’abord une série dans ta bibliothèque pour tester les notifications.
+ Sauvegarde d'abord une série dans ta bibliothèque pour tester les notifications.
Notification de test
Communauté
Découvre les personnes qui construisent et soutiennent Nuvio sur Mobile, TV et Web.
- L’API des supporters n’est pas configurée. Ajoute DONATIONS_BASE_URL dans local.properties.
+ L'API des supporters n'est pas configurée. Ajoute DONATIONS_BASE_URL dans local.properties.
Contributeurs
Supporters
Ouvrir GitHub
@@ -639,9 +746,9 @@
ID client AnimeSkip
Saisis ton ID client API AnimeSkip. Obtiens-en un sur anime-skip.com.
Activer la soumission d’intro
- Affiche un bouton pour soumettre les marqueurs d’intro/outro à la base communautaire.
+ Affiche un bouton pour soumettre des timestamps d’intro/outro à la base de données communautaire.
Clé API IntroDB
- Saisis ta clé API IntroDB pour soumettre des marqueurs. Requis pour la soumission.
+ Saisis ta clé API IntroDB pour soumettre des timestamps. Requise pour la soumission.
Rechercher également des marqueurs de saut sur AnimeSkip (nécessite un ID client).
Lecture automatique de l’épisode suivant
Rechercher et lire automatiquement l’épisode suivant lorsque le seuil est atteint.
@@ -657,6 +764,11 @@
%1$d heures
Activer libass
Utiliser libass pour afficher les sous-titres ASS/SSA à la place du moteur par défaut.
+ Lecteur externe
+ App de lecteur externe
+ Ouvre les nouvelles lectures avec l’app vidéo par défaut d’Android ou le sélecteur système.
+ Ouvre les nouvelles lectures avec le lecteur installé sélectionné.
+ Aucun lecteur externe compatible n’est installé
Vitesse au maintien
Maintenir pour accélérer
Garde le doigt appuyé n’importe où sur le lecteur pour accélérer temporairement.
@@ -675,6 +787,8 @@
Aucun
Préférer le groupe binge
Lors de la lecture automatique, préférer un stream du même groupe binge que le stream actuel.
+ Réutiliser le groupe de binge
+ Mémorise et réutilise le dernier groupe de binge entre les sessions (Continuer à regarder, Détails, etc.).
Langue audio préférée
Langue des sous-titres préférée
Préréglages
@@ -747,10 +861,10 @@
Illimité
Lecture tunnelisée
Active la lecture tunnelisée pour une latence réduite dans la synchronisation audio/vidéo.
- Ajoute ta propre clé API TMDB ci-dessous avant d’activer l’enrichissement.
+ Ajoute ta propre clé API TMDB ci-dessous avant d'activer l'enrichissement.
Clé API TMDB
- Activer l’enrichissement TMDB
- Utiliser ta clé API TMDB pour enrichir les métadonnées de l’addon sur l’écran de détails lorsqu’un ID TMDB ou IMDb est disponible.
+ Activer l'enrichissement TMDB
+ Utiliser ta clé API TMDB pour enrichir les métadonnées de l'addon sur l'écran de détails lorsqu'un ID TMDB ou IMDb est disponible.
Saisis ta clé API v3 TMDB.
Code de langue
Visuels
@@ -765,8 +879,8 @@
Utiliser les informations de sortie, durée, classification, statut, pays et langue de TMDB.
Épisodes
Utiliser les titres, miniatures, descriptions et durées des épisodes de TMDB pour les séries.
- À voir aussi
- Afficher les recommandations TMDB en bas des pages de détails.
+ Plus comme ceci
+ Visuels de recommandations TMDB sur la page de détail.
Chaînes
Utiliser les métadonnées des chaînes TMDB pour les titres TV.
Sociétés de production
@@ -796,8 +910,30 @@
Suis ce que tu regardes, enregistre dans ta liste ou tes listes personnalisées et garde ta bibliothèque synchronisée avec Trakt.
Identifiants Trakt manquants dans local.properties (TRAKT_CLIENT_ID / TRAKT_CLIENT_SECRET).
Ouvrir la connexion Trakt
- Tu peux maintenant enregistrer dans ta liste de suivi Trakt et tes listes personnelles.
+ Tes actions d'enregistrement peuvent maintenant cibler la watchlist Trakt et tes listes personnelles.
Connecte-toi avec Trakt pour activer la sauvegarde basée sur les listes et le mode bibliothèque Trakt.
+ Source de la bibliothèque
+ Choisis quelle bibliothèque utiliser pour enregistrer et consulter ta collection
+ Source de la bibliothèque
+ Choisis où enregistrer et gérer les éléments de ta bibliothèque
+ Trakt
+ Bibliothèque Nuvio
+ Bibliothèque Trakt sélectionnée
+ Bibliothèque Nuvio sélectionnée
+ Progression de visionnage
+ Choisis quelle source de progression alimente la reprise et Continuer à regarder
+ Progression de visionnage
+ Choisis si la reprise et Continuer à regarder doivent utiliser Trakt ou Nuvio Sync pendant que le scrobbling Trakt reste actif.
+ Trakt
+ Nuvio Sync
+ Source de progression réglée sur Trakt
+ Source de progression réglée sur Nuvio Sync
+ Fenêtre de Continuer à regarder
+ Historique Trakt pris en compte pour Continuer à regarder
+ Fenêtre de Continuer à regarder
+ Choisis quelle quantité d’activité Trakt apparaît dans Continuer à regarder.
+ Tout l’historique
+ %1$d jours
Score du public
IMDb
Letterboxd
@@ -909,19 +1045,19 @@
Non
Mettre à jour
Oui
- Veux-tu quitter l’application ?
- Quitter l’application
- Ce catalogue n’a retourné aucun élément.
+ Veux-tu quitter l'application ?
+ Quitter l'application
+ Ce catalogue n'a renvoyé aucun élément.
Aucun titre trouvé
Vérifie ta connexion Wi‑Fi ou données mobiles et réessaie.
Réalisateur
Échec du chargement
À voir aussi
Saisons
- Cet addon a retourné des vidéos pour la série, mais aucune n’incluait de numéros de saison ou d’épisode.
- Cet addon n’a fourni aucune métadonnée d’épisode pour cette série.
- Cet addon n’a pas encore publié d’épisodes.
- Ton appareil est en ligne, mais Nuvio n’a pas pu se connecter aux serveurs nécessaires.
+ Cet addon a renvoyé des vidéos pour la série, mais aucune n'incluait de numéros de saison ou d'épisode.
+ Cet addon n'a fourni aucune métadonnée d'épisode pour cette série.
+ Cet addon n'a pas encore publié d'épisodes.
+ Ton appareil est en ligne, mais Nuvio n'a pas pu se connecter aux serveurs nécessaires.
Afficher moins
Afficher plus ▾
Scénariste
@@ -934,7 +1070,7 @@
Aucun catalogue de découverte
Le catalogue et les filtres sélectionnés n’ont retourné aucun élément.
Aucun titre trouvé
- Installe et valide au moins un addon avant d’explorer les catalogues dans Découvrir.
+ Installe et valide au moins un addon avant d'explorer les catalogues dans Découvrir.
Sélectionner un catalogue
Sélectionner un genre
Sélectionner un type
@@ -943,15 +1079,16 @@
Marquer les précédents comme vus
Marquer %1$s comme non vue
Marquer %1$s comme vue
+ Marquer les saisons précédentes comme vues
Marquer comme non vu
Marquer comme vu
- Suivant
+ À suivre
%1$s vu
- Installe et valide au moins un addon avant de charger des lignes de catalogue à l’accueil.
- Les addons installés n’exposent actuellement aucun catalogue compatible avec le tableau sans extras requis.
- Aucune ligne d’accueil disponible
+ Installe et valide au moins un addon avant de charger des lignes de catalogue à l'accueil.
+ Les addons installés n'exposent actuellement aucun catalogue compatible avec le tableau sans extras requis.
+ Aucune ligne d'accueil disponible
Voir les détails
- Contrôles pour lire et enregistrer.
+ Contrôles de lecture et d'enregistrement.
Actions
Liste principale du casting.
Rayon de collection ou de franchise associée.
@@ -963,7 +1100,7 @@
Rayon de recommandations.
À voir aussi
Synopsis, notes, genres et crédits principaux.
- Résumé
+ Aperçu
Studios et chaînes.
Production
Rayon de bandes-annonces et raccourcis de lecture.
@@ -988,12 +1125,17 @@
Code PIN oublié ?
Code PIN incorrect
Bloqué. Réessaie dans %1$ds
- Les options d’avatar apparaîtront ici une fois le catalogue chargé.
- Avatar : %1$s
+ Les options d'avatar apparaîtront ici une fois le catalogue chargé.
+ Avatar : %1$s
+ Saisis une URL d’image http:// ou https:// valide.
Choisir un avatar
Choisis un avatar ci-dessous.
Créer un profil
- Toutes les données de « %1$s » seront définitivement supprimées.
+ URL d’avatar personnalisée sélectionnée.
+ URL d’avatar personnalisée
+ Colle un lien d’image, ou laisse vide pour utiliser le catalogue d’avatars intégré.
+ https://example.com/avatar.png
+ Toutes les données de "%1$s" seront définitivement supprimées.
Supprimer le profil
Ajouter un profil
Modifier le profil
@@ -1010,7 +1152,7 @@
Supprimer le verrouillage PIN
Enregistrement…
Sécurité
- Ajoute un code PIN si tu souhaites que ce profil soit verrouillé avant d’y accéder.
+ Ajoute un code PIN si tu souhaites que ce profil soit verrouillé avant d'y accéder.
Ce profil est protégé par un code PIN.
Sélectionne un avatar pour ce profil.
Configurer le verrouillage PIN
@@ -1024,12 +1166,14 @@
Vérification d’autres addons…
Copier le lien du stream
Télécharger le fichier
- Les addons de streams installés n’ont pas retourné de réponse valide.
+ Ouvrir dans le lecteur externe
+ Ouvrir dans le lecteur interne
+ Les addons de streams installés n'ont pas renvoyé de réponse valide.
Impossible de charger les streams
- Installe d’abord un addon pour charger les streams de ce titre.
+ Installe d'abord un addon pour charger les streams de ce titre.
Tes addons installés ne fournissent pas de streams pour ce type de titre.
Aucun addon de streams disponible
- Aucun de tes addons installés n’a retourné de streams pour ce titre.
+ Aucun de tes addons installés n'a renvoyé de stream pour ce titre.
S%1$d E%2$d
Épisode
S%1$dE%2$d - %3$s
@@ -1043,6 +1187,14 @@
Reprendre depuis %1$d%
Reprendre depuis %1$s
TAILLE %1$s
+ Ce type de stream n’est pas pris en charge
+ Connecte un compte dans les paramètres.
+ Non mis en cache sur Torbox.
+ Ce lien a expiré. Actualisation des résultats.
+ Impossible d’ouvrir ce lien.
+ Impossible d’ouvrir le lecteur externe
+ Choisis d’abord un lecteur externe dans les paramètres
+ Aucun lecteur externe disponible
Fermer la bande-annonce
Impossible de lire la bande-annonce
Impossible de charger les listes Trakt
@@ -1050,12 +1202,10 @@
%1$s • %2$s
Échec de la vérification des mises à jour
Échec du téléchargement
- Corps du téléchargement vide
- Le fichier de mise à jour téléchargé est introuvable.
- Téléchargement %1$d%%
- Impossible de démarrer l’installation
+ Téléchargement %1$d %
+ Impossible de démarrer l'installation
Tu utilises la version la plus récente.
- Active l’installation d’applications pour Nuvio puis reviens pour continuer.
+ Active l'installation d'applications pour Nuvio puis reviens pour continuer.
Téléchargement de la mise à jour…
Aucune mise à jour trouvée.
Une nouvelle version est prête à être installée.
@@ -1066,7 +1216,7 @@
Mise à jour disponible
Statut de la mise à jour
Cet addon est déjà installé.
- Saisis une URL d’addon valide
+ Saisis une URL d'addon valide
Impossible de charger le manifeste
Nuvio
Impossible de supprimer le compte
@@ -1078,20 +1228,21 @@
À suivre • S%1$dE%2$d
Logo de %1$s
Impossible de charger les commentaires
- Aucun addon n’a pu fournir les détails.
+ Impossible de charger les détails depuis aucun addon.
Chaînes
Aucun addon ne fournit de métadonnées pour ce contenu.
- Téléchargement échoué
+ Échec du téléchargement
Affiche la progression en direct et les contrôles de téléchargement.
Téléchargements
Téléchargement terminé
Téléchargement de %1$s • %2$s
Téléchargement de %1$s • %2$s / %3$s
- Téléchargement échoué
+ Échec du téléchargement
En pause %1$s
- Retirer
- Supprimer %1$s de ta bibliothèque ?
- Retirer de la bibliothèque ?
+ Supprimer
+ Retirer %1$s de %2$s ?
+ Supprimer %1$s de ta bibliothèque ?
+ Retirer de la bibliothèque ?
Film
Alertes lorsqu’un nouvel épisode d’une série enregistrée est disponible.
Aperçu de l’alerte de sortie d’épisode.
@@ -1102,7 +1253,7 @@
Le code PIN de ce profil a changé. Connecte-toi une fois pour mettre à jour le verrouillage sur cet appareil.
Impossible de supprimer le verrouillage PIN. Réessaie.
Connecte-toi à Internet pour supprimer le verrouillage PIN.
- Ce code PIN ne peut pas encore être vérifié hors ligne sur cet appareil. Connecte-toi une fois et déverrouille-le en ligne d’abord.
+ Ce code PIN ne peut pas encore être vérifié hors ligne sur cet appareil. Connecte-toi une fois et déverrouille-le en ligne d'abord.
Impossible de définir le code PIN. Réessaie.
Connecte-toi à Internet pour définir un code PIN.
Ce profil utilise les addons principaux.
@@ -1111,8 +1262,6 @@
Intégré
Autorisation refusée
Termine la connexion Trakt dans ton navigateur
- Connecté à Trakt
- Déconnecté de Trakt
Callback Trakt invalide
État du callback Trakt invalide
Réponse de jeton Trakt invalide
@@ -1201,7 +1350,7 @@
Populaire
Récent
%1$s • %2$s
- Mieux noté
+ Mieux notés
Classification
Détails du film
Langue originale
@@ -1227,15 +1376,46 @@
La requête de téléchargement a échoué
Impossible d’écrire dans le fichier de téléchargement partiel
%1$s - %2$s
- Appuie sur Enregistrer depuis un écran de détails pour ajouter des titres ici.
+ Les titres enregistrés apparaîtront ici après avoir appuyé sur Enregistrer dans un écran de détails.
Ta bibliothèque est vide
Impossible de charger la bibliothèque
Autre
+ Cloud
+ Enregistrés
Bibliothèque
Connecte Trakt et enregistre des titres dans ta liste de suivi ou tes listes personnelles.
Ta bibliothèque Trakt est vide
Impossible de charger la bibliothèque Trakt
Bibliothèque Trakt
+ Connecter un compte
+ Connecte un compte dans les paramètres Services connectés pour parcourir les fichiers lisibles de ta cloud library.
+ Aucun compte cloud connecté
+ Ouvrir Services connectés
+ Active la cloud library dans les paramètres Services connectés pour parcourir les fichiers des comptes connectés.
+ Cloud library désactivée
+ Aucun fichier cloud lisible ne correspond aux filtres actuels.
+ Rien ici pour l’instant
+ Choisis un fichier à lire
+ Impossible de charger la cloud library %1$s
+ Cet élément n’expose aucun fichier vidéo lisible.
+ Aucun fichier lisible
+ Aucun fichier lisible
+ Cloud library désactivée.
+ Impossible de lire ce fichier cloud.
+ Lire le fichier
+ Aucun service cloud connecté.
+ %1$s n’est pas connecté.
+ %1$d fichiers lisibles
+ Tous
+ Actualiser la cloud library
+ Sélectionner le fournisseur
+ Sélectionner un type
+ Prêt à lire
+ Tous
+ Torrents
+ Usenet
+ Web
+ Fichiers
Anime
Chaînes
Films
@@ -1245,7 +1425,15 @@
%1$s • %2$s est maintenant disponible
Un nouvel épisode est maintenant disponible
%1$s est maintenant disponible
- Sorties d’épisodes
+ Sorties d'épisodes
+ Alcool/Drogues
+ Effrayant
+ Nudité
+ Grossièretés
+ Léger
+ Modéré
+ Sévère
+ Violence
Créateur
Réalisateur
Scénariste
@@ -1256,288 +1444,165 @@
Ko
Mo
Go
- APERÇU
- %1$d dépôts
- %1$d fournisseurs
- Plugins activés
- Plugins désactivés
- Clé API TMDB renseignée
- Clé API TMDB manquante
- Les fournisseurs de plugins nécessitent une clé API TMDB. Configure-la dans l’écran TMDB, sinon les fournisseurs ne fonctionneront pas correctement.
- Activer les fournisseurs de plugins
- Utilise les fournisseurs de plugins pendant la recherche de streams.
- Regrouper les fournisseurs par dépôt
- Dans Streams, affiche un fournisseur par dépôt au lieu d’un par source.
- AJOUTER UN DÉPÔT
- URL du manifeste du plugin
- Installation…
- Installer le dépôt de plugin
- Saisis une URL de dépôt de plugin.
- Saisis une URL de plugin valide.
- Ce dépôt de plugin est déjà installé.
- Impossible d’installer le dépôt de plugin
- Impossible d’actualiser le dépôt
- Fournisseur introuvable
- Les plugins ne sont pas disponibles dans cette version.
- Nom du manifeste manquant.
- Version du manifeste manquante.
- Le manifeste ne contient aucun fournisseur.
- %1$s installé.
- DÉPÔTS INSTALLÉS
- Aucun dépôt de plugin installé pour l’instant.
- Ajoute une URL de dépôt pour installer des plugins fournisseurs.
- Version %1$s
- Actualiser le dépôt de plugin
- Supprimer le dépôt de plugin
- Actualisation
- FOURNISSEURS
- Aucun fournisseur disponible pour l’instant.
- Aucune description
- v%1$s
- Désactivé par le dépôt
- Test en cours…
- Tester le fournisseur
- Erreur
- Échec du test du fournisseur
- Résultats du test (%1$d)
- Dépôt de plugin
- Soumettre l’intro
- Soumettre les horodatages
- TYPE DE SEGMENT
+
+ Soumettre l’intro
+ Paramètres vidéo
+
+ La cloud library n’est pas disponible pour %1$s.
+
+ Vidéo
+ Réinitialiser le réglage
+ Préréglage de sortie
+ Détection du pic HDR
+ Estime le pic de luminosité HDR quand les métadonnées sont absentes ou erronées.
+ Tone mapping
+ Deband
+ Réduit les artefacts de banding au prix d’une légère perte de performances.
+ Interpolation d’images
+ Lisse le mouvement quand mpv peut utiliser la synchro d’affichage proprement.
+ Luminosité
+ Contraste
+ Saturation
+ Gamma
+
+ Native EDR
+ Optimal pour les iPhones et iPads compatibles HDR.
+ SDR tone mapped
+ Blancs et noirs plus prévisibles sur une sortie de type SDR.
+ Compatibilité
+ Au plus proche de l’ancien comportement MPV iOS.
+ Personnalisé
+ Utilise tes valeurs avancées ci-dessous.
+ Désactivé
+
+ Sortie vidéo iOS
+ Décodeur matériel
+ Plage dynamique étendue
+ Mode de sortie Metal par défaut pour les nouvelles sessions de lecture.
+ Indice de couleur d’affichage
+ Permettre à mpv de cibler par défaut l’espace colorimétrique de l’écran actif.
+ Primaires cibles
+ Transfert cible
+
+ Gestion des résultats
+ Nombre max de résultats
+ Limite le nombre de résultats affichés.
+ Tri des résultats
+ Choisis l’ordre dans lequel les résultats apparaissent.
+ Limite par résolution
+ Plafonne les résultats répétés en 2160p, 1080p, 720p après le tri.
+ Limite par qualité
+ Plafonne les résultats répétés en BluRay, WEB-DL, REMUX après le tri.
+ Plage de taille
+ Filtre les résultats par taille de fichier.
+
+ En savoir plus
+ Format par défaut
+ Saisis un groupe par ligne.
+
+ Par défaut
+ Plus gros d’abord
+ Plus petit d’abord
+ Meilleur audio d’abord
+ Langue d’abord
+
+ Tous
+ %1$d sélectionné(s)
+
+ Tous les résultats
+ %1$d résultats
+
+ Jusqu’à %1$d Go
+ %1$d Go et plus
+ %1$d-%2$d Go
+
+ Résolutions préférées
+ Trie d’abord les résolutions sélectionnées, dans l’ordre par défaut.
+ Résolutions requises
+ Affiche uniquement les résolutions sélectionnées.
+ Résolutions exclues
+ Masque les résolutions sélectionnées.
+
+ Qualités préférées
+ Trie d’abord les qualités sélectionnées, dans l’ordre par défaut.
+ Qualités requises
+ Affiche uniquement les qualités sélectionnées.
+ Qualités exclues
+ Masque les qualités sélectionnées.
+
+ Tags visuels préférés
+ Trie d’abord les tags DV, HDR, 10bit, IMAX et similaires.
+ Tags visuels requis
+ Exige les tags DV, HDR, 10bit, IMAX, SDR et similaires.
+ Tags visuels exclus
+ Masque les tags DV, HDR, 10bit, 3D et similaires.
+
+ Tags audio préférés
+ Trie d’abord les tags Atmos, TrueHD, DTS, AAC et similaires.
+ Tags audio requis
+ Exige les tags Atmos, TrueHD, DTS, AAC et similaires.
+ Tags audio exclus
+ Masque les tags audio sélectionnés.
+
+ Canaux préférés
+ Trie d’abord les configurations de canaux préférées.
+ Canaux requis
+ Affiche uniquement les configurations de canaux sélectionnées.
+ Canaux exclus
+ Masque les configurations de canaux sélectionnées.
+
+ Encodages préférés
+ Trie d’abord les encodages AV1, HEVC, AVC et similaires.
+ Encodages requis
+ Exige les encodages AV1, HEVC, AVC et similaires.
+ Encodages exclus
+ Masque les encodages sélectionnés.
+
+ Langues préférées
+ Trie d’abord les langues audio préférées.
+ Langues requises
+ Affiche uniquement les résultats avec les langues sélectionnées.
+ Langues exclues
+ Masque les résultats où toutes les langues sont exclues.
+
+ Release groups requis
+ Affiche uniquement les release groups sélectionnés.
+ Release groups exclus
+ Masque les release groups sélectionnés.
+
+ Soumettre les timestamps
+ TYPE DE SEGMENT
Intro
Récap
Outro
- DÉBUT (MM:SS)
- FIN (MM:SS)
- Soumettre
- Capturer
- Clé API invalide ou échec de connexion
- Masquer les contenus non sortis
- Masque les films et séries qui ne sont pas encore sortis.
- Liquid Glass
- Utilise la barre d’onglets native d’iPhone sur iOS 26 et plus. Le changement de profil instantané depuis la barre d’onglets est indisponible quand cette option est active.
- Floute les miniatures du prochain épisode dans « Continuer à regarder » pour éviter les spoilers.
- Flouter les épisodes non vus dans « Continuer à regarder »
- Inclure les épisodes à venir dans « Continuer à regarder » avant leur diffusion.
- Afficher les prochains épisodes non diffusés
- Préférer les miniatures d’épisodes lorsqu’elles sont disponibles.
- Préférer les miniatures d’épisodes dans « Continuer à regarder »
- Masquer la valeur
- Flouter les épisodes non vus
- Flouter les miniatures d’épisodes jusqu’à ce qu’ils soient vus pour éviter les spoilers.
- Afficher la valeur
- Les streams torrent ne sont pas pris en charge
- Tout l’historique
- Historique Trakt pris en compte pour « Continuer à regarder »
- Fenêtre de reprise
- Choisis quelle quantité d’activité Trakt doit apparaître dans « Continuer à regarder ».
- Fenêtre de reprise
- %1$d jours
- Choisis où enregistrer et gérer les éléments de ta bibliothèque
- Source de la bibliothèque
- Bibliothèque Nuvio
- Bibliothèque Nuvio sélectionnée
- Choisis la bibliothèque à utiliser pour enregistrer et consulter ta collection
- Source de la bibliothèque
- Trakt
- Bibliothèque Trakt sélectionnée
- Choisis si la reprise et « Continuer à regarder » doivent utiliser Trakt ou Nuvio Sync, sans interrompre le scrobbling Trakt.
- Progression de visionnage
- Source de progression définie sur Nuvio Sync
- Nuvio Sync
- Trakt
- Choisis quelle source de progression alimente la reprise et « Continuer à regarder »
- Progression de visionnage
- Source de progression définie sur Trakt
- Saisis une URL d’image valide commençant par http:// ou https://.
- URL d’avatar personnalisée sélectionnée.
- URL d’avatar personnalisée
- Colle un lien d’image, ou laisse vide pour utiliser le catalogue d’avatars intégré.
- https://exemple.com/avatar.png
- Sources de données, remerciements et licences de la plateforme
- Licences et attributions
- Impossible d’ouvrir le lecteur externe
- Choisis d’abord un lecteur externe dans les paramètres
- Aucun lecteur externe disponible
- Retirer %1$s de %2$s ?
- ORDRE DE TRI
- Par défaut
- Trie tous les éléments par récence
- Style streaming
- Éléments sortis d’abord, à venir à la fin
- Ordre de tri
- Masquer le soulignement des catalogues
- Retire le trait d’accent sous les titres de catalogues et de collections dans toute l’application.
- Utilisé pour la lecture sur les builds Android.
- Sous licence Apache License, version 2.0.
- AndroidX Media3 ExoPlayer 1.8.0
- Nuvio utilise les IMDb Non-Commercial Datasets, dont title.ratings.tsv.gz, pour les notes et le nombre de votes IMDb. Informations fournies par IMDb (https://www.imdb.com). Utilisé avec autorisation. Les données IMDb sont destinées à un usage personnel et non commercial selon les conditions d’IMDb.
- IMDb Non-Commercial Datasets
- Nuvio utilise l’API IntroDB pour les horodatages d’intro, de récap, de générique et d’aperçu fournis par la communauté, utilisés par les contrôles de saut. Nuvio n’est ni affilié ni approuvé par IntroDB.
- IntroDB
- Nuvio utilise MDBList pour les notes et les données de fournisseurs de scores externes. Nuvio n’est ni affilié ni approuvé par MDBList.
- MDBList
- Utilisé pour la lecture sur les builds iOS.
- Le code source de MPVKit seul est sous licence LGPL v3.0. Les bundles MPVKit, dont libmpv et les bibliothèques FFmpeg, sont également sous licence LGPL v3.0.
- MPVKit
- Le code source et les termes de licence sont disponibles dans le dépôt du projet.
- Sous licence GNU General Public License v3.0.
- Nuvio Mobile
- LICENCE DE L’APPLICATION
- DONNÉES ET SERVICES
- LICENCE DE LECTURE
- Nuvio utilise l’API TMDB pour les métadonnées de films et de séries, les artworks, les bandes-annonces, le casting, les détails de production, les collections et les recommandations. Ce produit utilise l’API TMDB mais n’est ni approuvé ni certifié par TMDB.
- The Movie Database (TMDB)
- Nuvio se connecte à Trakt pour l’authentification de compte, l’historique de visionnage, la synchronisation de progression, les données de bibliothèque, les notes, les listes et les commentaires. Nuvio n’est ni affilié ni approuvé par Trakt.
- Trakt
- Lecteur externe
- Application de lecteur externe
- Ouvre la nouvelle lecture avec l’application vidéo par défaut d’Android ou le sélecteur système.
- Ouvre la nouvelle lecture avec le lecteur installé sélectionné.
- Aucun lecteur externe pris en charge n’est installé
- Aucun paramètre trouvé.
- Rechercher dans les paramètres…
- RÉSULTATS
- Ouvrir dans un lecteur externe
- Ouvrir dans le lecteur interne
- Problème de connexion
- Vérifie ta connexion et réessaie.
- Bibliothèque Nuvio
- Dépôt de plugin
- Valider
- Plateformes rapides
- Régions rapides
- Les plus votés
- Apple TV+
- Disney+
- Hulu
- Netflix
- Prime Video
- ID de plateforme
- Utilise des ID de plateforme TMDB. Sépare plusieurs valeurs par des virgules pour ET, ou des barres verticales pour OU.
- 8|337|350
- Région de visionnage
- Code pays ISO 3166-1 où le titre est disponible. Exemple : US, GB.
- Debrid
- Ajoute une clé API Debrid dans les Paramètres.
- Impossible de résoudre ce stream Debrid.
- Ce résultat Debrid a expiré. Actualisation des streams.
- Alcool/Drogues
- Effrayant
- Nudité
- Grossièretés
- Léger
- Modéré
- Sévère
- Violence
- Ajoute d’abord une clé API.
- Modèle de description
- Contrôle les métadonnées affichées sous chaque source.
- Activer les sources
- Afficher les résultats lisibles des comptes connectés.
- La prise en charge de Debrid est expérimentale et peut être conservée, modifiée ou retirée plus tard.
- Impossible de valider cette clé API.
- Clé API validée.
- Modèle de nom
- Contrôle l’affichage des noms de source.
- %1$d sources
- 1 source
- Préparer les liens
- Résoudre les premières sources avant le début de la lecture.
- Sources à préparer
- Connecte ton compte Torbox.
- Mise en forme
- Lecture instantanée
- Compte
- Debrid
- Sources de comptes cloud expérimentales
- Enregistrement…
- Saisis la clé API Torbox
- Saisis ta clé API Torbox.
- Clé API Torbox
- Restaure la mise en forme des sources par défaut.
- Réinitialiser la mise en forme
- Non défini
- Privilégie une valeur basse quand c’est possible. Les services Debrid peuvent limiter le nombre de liens résolus dans une période donnée. Ouvrir un film ou un épisode peut compter dans ces limites même sans appuyer sur Lecture, car les liens sont préparés à l’avance.
- To
- 28,12
- 18,35
- 2020-01-01
- 2024-12-31
- 7.0
- 10
- 100
- en, ko, ja, hi
- US, KR, JP, IN
- 2024
- ID %1$s
- %1$s (%2$s)
- Connecté à Trakt
- Déconnecté de Trakt
- Saisis un nom, une URL ou un ID de liste Trakt
- Saisis un ID ou une URL de liste Trakt
- Impossible de charger la liste Trakt
- Liste Trakt %1$d
- Liste Trakt résolue
+ DÉBUT (MM:SS)
+ FIN (MM:SS)
+ Soumettre
+ Capturer
+
+ Décodeur matériel
+ Primaires cibles
+ Transfert cible
+
+ Liste Trakt résolue
+ TMDB Discover
+ US, KR, JP, IN
+
+ Saisis un nom de liste Trakt, une URL ou un ID
Aucune liste Trakt trouvée
- Saisis un ID ou une URL TMDB valide.
- Liste TMDB %1$s
- Collection TMDB %1$s
- Production TMDB %1$s
- Chaîne TMDB %1$s
- Personne TMDB %1$s
- Réalisateur TMDB %1$s
- Découverte TMDB
- Impossible de charger la source TMDB
- Liste Trakt de séries
- Liste Trakt de films
- Ajoute une clé API TMDB dans les Paramètres pour utiliser les sources TMDB.
- Liste TMDB introuvable
- Collection TMDB introuvable
- Société TMDB introuvable
- Chaîne TMDB introuvable
- Personne TMDB introuvable
- ID de liste TMDB manquant
- ID de collection TMDB manquant
- ID de personne TMDB manquant
- Crédits TMDB de la personne introuvables
- Découverte TMDB n’a retourné aucune donnée
- ID de liste Trakt manquant
- Saisis un ID ou une URL de liste Trakt valide
- La liste Trakt ne contient pas d’ID numérique
- Échec de la requête Trakt
- Identifiants Trakt manquants.
- %1$d éléments
- %1$d j’aime
- Liste Trakt publique
- Liste Trakt introuvable ou non publique
- Limite de requêtes Trakt atteinte
- %1$s (%2$d)
- Échec du chargement des commentaires Trakt (%1$d)
- Aucune mise à jour n’a encore été publiée.
- Erreur API releases GitHub : %1$d
- La release n’a pas de tag ni de nom
- Aucun fichier APK trouvé dans la release
- Échec du téléchargement (HTTP %1$d)
- Corps de téléchargement vide
- Le fichier de mise à jour téléchargé est introuvable.
- Échec de la requête (HTTP %1$d)
- Corps de réponse vide
- Impossible de finaliser le fichier de téléchargement
- Impossible d’ouvrir le fichier de téléchargement partiel
- Le fichier de téléchargement partiel n’est pas ouvert
- Impossible d’écrire dans le fichier de téléchargement partiel
- %1$d h %2$d min
- %1$d h
- %1$d min
- Aucun résultat de recherche pour %1$s.
- Ce dépôt de plugin est déjà installé.
- Impossible d’installer le dépôt de plugin
- Impossible d’actualiser le dépôt
- Le nom du manifeste est manquant.
- La version du manifeste est manquante.
- Le manifeste ne contient aucun fournisseur.
- Champ « %1$s » manquant dans le manifeste
- Moteur de lecture MPV indisponible. Reconstruis l’app.
- Impossible de lire ce flux.
+ Saisis un ID ou une URL TMDB valide.
+ Impossible de charger la source TMDB
+ Saisis un ID ou une URL de liste Trakt
+ Impossible de charger la liste Trakt
+
+ Canada
+ Australie
+ Allemagne
+
+ Films
+ Séries
+
+ La cloud library est désactivée.
+
+ Clé API invalide ou échec de connexion
diff --git a/composeApp/src/commonMain/composeResources/values/strings.xml b/composeApp/src/commonMain/composeResources/values/strings.xml
index 596c0ee64..ddc5b09e5 100644
--- a/composeApp/src/commonMain/composeResources/values/strings.xml
+++ b/composeApp/src/commonMain/composeResources/values/strings.xml
@@ -1456,145 +1456,165 @@
KB
MB
GB
- OVERVIEW
- %1$d repos
- %1$d providers
- Plugins enabled
- Plugins disabled
- TMDB API key set
- TMDB API key missing
- Plugin providers require a TMDB API key. Set it on the TMDB screen or plugin providers will not work correctly.
- Enable plugin providers globally
- Use plugin providers during stream discovery.
- Group plugin providers by repository
- In Streams, show one provider per repository instead of one per source.
- ADD REPOSITORY
- Plugin manifest URL
- Installing…
- Install Plugin Repository
- Enter a plugin repository URL.
- Enter a valid plugin URL.
- That plugin repository is already installed.
- Unable to install plugin repository
- Unable to refresh repository
- Provider not found
- Plugins are not available in this build.
- Manifest name is missing.
- Manifest version is missing.
- Manifest has no providers.
- Installed %1$s.
- INSTALLED REPOSITORIES
- No plugin repositories installed yet.
- Add a repository URL to install provider plugins for stream discovery.
- Version %1$s
- Refresh plugin repository
- Delete plugin repository
- Refreshing
- PROVIDERS
- No providers available yet.
- No description
- v%1$s
- Disabled by repo
- Testing…
- Test Provider
- Error
- Provider test failed
- Test results (%1$d)
- Plugin repository
- Submit Intro
+
+ Submit Intro
+ Video settings
+
+ Cloud library is not available for %1$s.
+
+ Video
+ Reset tuning
+ Output preset
+ HDR peak detection
+ Estimate HDR peak brightness when metadata is bad or missing.
+ Tone mapping
+ Deband
+ Reduce color banding at a small performance cost.
+ Frame interpolation
+ Smooth motion when mpv can use display sync cleanly.
+ Brightness
+ Contrast
+ Saturation
+ Gamma
+
+ Native EDR
+ Best for HDR-capable iPhones and iPads.
+ SDR tone mapped
+ More predictable whites and blacks on SDR-style output.
+ Compatibility
+ Closest to the older iOS MPV behavior.
+ Custom
+ Use your advanced values below.
+ Off
+
+ iOS video output
+ Hardware decoder
+ Extended dynamic range
+ Default Metal output mode for new playback sessions.
+ Display color hint
+ Let mpv target the active display color space by default.
+ Target primaries
+ Target transfer
+
+ Result Management
+ Max results
+ Limit how many results appear.
+ Sort results
+ Choose how results are ordered.
+ Per resolution limit
+ Cap repeated 2160p, 1080p, 720p results after sorting.
+ Per quality limit
+ Cap repeated BluRay, WEB-DL, REMUX results after sorting.
+ Size range
+ Filter results by file size.
+
+ Learn more
+ Default format
+ Enter one group per line.
+
+ Default
+ Largest first
+ Smallest first
+ Best audio first
+ Language first
+
+ Any
+ %1$d selected
+
+ All results
+ %1$d results
+
+ Up to %1$dGB
+ %1$dGB+
+ %1$d-%2$dGB
+
+ Preferred resolutions
+ Sort selected resolutions first, in default order.
+ Required resolutions
+ Only show selected resolutions.
+ Excluded resolutions
+ Hide selected resolutions.
+
+ Preferred qualities
+ Sort selected qualities first, in default order.
+ Required qualities
+ Only show selected qualities.
+ Excluded qualities
+ Hide selected qualities.
+
+ Preferred visual tags
+ Sort DV, HDR, 10bit, IMAX and similar tags.
+ Required visual tags
+ Require DV, HDR, 10bit, IMAX, SDR and similar tags.
+ Excluded visual tags
+ Hide DV, HDR, 10bit, 3D and similar tags.
+
+ Preferred audio tags
+ Sort Atmos, TrueHD, DTS, AAC and similar tags.
+ Required audio tags
+ Require Atmos, TrueHD, DTS, AAC and similar tags.
+ Excluded audio tags
+ Hide selected audio tags.
+
+ Preferred channels
+ Sort preferred channel layouts first.
+ Required channels
+ Only show selected channel layouts.
+ Excluded channels
+ Hide selected channel layouts.
+
+ Preferred encodes
+ Sort AV1, HEVC, AVC and similar encodes.
+ Required encodes
+ Require AV1, HEVC, AVC and similar encodes.
+ Excluded encodes
+ Hide selected encodes.
+
+ Preferred languages
+ Sort preferred audio languages first.
+ Required languages
+ Only show results with selected languages.
+ Excluded languages
+ Hide results where every language is excluded.
+
+ Required release groups
+ Only show selected release groups.
+ Excluded release groups
+ Hide selected release groups.
+
Submit Timestamps
- SEGMENT TYPE
+ SEGMENT TYPE
Intro
Recap
Outro
- START TIME (MM:SS)
- END TIME (MM:SS)
- Submit
- Capture
- Invalid API Key or connection failed
- Connection issue
- Please check your connection and try again.
- Nuvio Library
- Plugin repository
- TB
- 28,12
- 18,35
- 2020-01-01
- 2024-12-31
- 7.0
- 10
- 100
- en, ko, ja, hi
- US, KR, JP, IN
- 2024
- ID %1$s
- %1$s (%2$s)
- Connected to Trakt
- Disconnected from Trakt
- Enter a Trakt list name, URL, or ID
- Enter a Trakt list ID or URL
- Could not load Trakt list
- Trakt List %1$d
- Resolved Trakt list
+ START TIME (MM:SS)
+ END TIME (MM:SS)
+ Submit
+ Capture
+
+ Hardware decoder
+ Target primaries
+ Target transfer
+
+ Resolved Trakt list
+ TMDB Discover
+ US, KR, JP, IN
+
+ Enter a Trakt list name, URL, or ID
No Trakt lists found
- Enter a valid TMDB ID or URL.
- TMDB List %1$s
- TMDB Collection %1$s
- TMDB Production %1$s
- TMDB Network %1$s
- TMDB Person %1$s
- TMDB Director %1$s
- TMDB Discover
- Could not load TMDB source
- Trakt Series List
- Trakt Movie List
- Add a TMDB API key in Settings to use TMDB sources.
- TMDB list not found
- TMDB collection not found
- TMDB company not found
- TMDB network not found
- TMDB person not found
- Missing TMDB list ID
- Missing TMDB collection ID
- Missing TMDB person ID
- TMDB person credits not found
- TMDB discover returned no data
- Missing Trakt list ID
- Enter a valid Trakt list ID or URL
- Trakt list did not include a numeric ID
- Trakt request failed
- Missing Trakt credentials.
- %1$d items
- %1$d likes
- Trakt public list
- Trakt list not found or not public
- Trakt rate limit reached
- %1$s (%2$d)
- Failed to load Trakt comments (%1$d)
- No update has been published yet.
- GitHub releases API error: %1$d
- Release has no tag or name
- No APK asset found in the release
- Download failed with HTTP %1$d
- Empty download body
- Downloaded update file is missing.
- Request failed with HTTP %1$d
- Empty response body
- Failed to finalize download file
- Failed to open partial download file
- Partial download file is not open
- Failed to write partial download file
- %1$dh %2$dm
- %1$dh
- %1$dm
- No search results returned for %1$s.
- That plugin repository is already installed.
- Unable to install plugin repository
- Unable to refresh repository
- Manifest name is missing.
- Manifest version is missing.
- Manifest has no providers.
- Manifest missing \"%1$s\"
- MPV player engine not available. Please rebuild the app.
- Unable to play this stream.
+ Enter a valid TMDB ID or URL.
+ Could not load TMDB source
+ Enter a Trakt list ID or URL
+ Could not load Trakt list
+
+ Canada
+ Australia
+ Germany
+
+ Movies
+ Series
+
+ Cloud library is disabled.
+
+ Invalid API Key or connection failed
diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/cloud/CloudLibraryRepository.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/cloud/CloudLibraryRepository.kt
index 492b70a3f..72c7e97a9 100644
--- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/cloud/CloudLibraryRepository.kt
+++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/cloud/CloudLibraryRepository.kt
@@ -12,6 +12,10 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
+import nuvio.composeapp.generated.resources.Res
+import nuvio.composeapp.generated.resources.cloud_library_playback_disabled
+import nuvio.composeapp.generated.resources.cloud_library_provider_unavailable
+import org.jetbrains.compose.resources.getString
internal class CloudLibraryStore(
private val credentialsProvider: suspend () -> List,
@@ -26,7 +30,10 @@ internal class CloudLibraryStore(
if (api == null) {
return@map CloudLibraryProviderState(
provider = credential.provider,
- errorMessage = "Cloud library is not available for ${credential.provider.displayName}.",
+ errorMessage = getString(
+ Res.string.cloud_library_provider_unavailable,
+ credential.provider.displayName,
+ ),
)
}
@@ -191,7 +198,7 @@ object CloudLibraryRepository {
): CloudLibraryPlaybackResult {
DebridSettingsRepository.ensureLoaded()
if (!DebridSettingsRepository.snapshot().cloudLibraryEnabled) {
- return CloudLibraryPlaybackResult.Failed("Cloud library is disabled.")
+ return CloudLibraryPlaybackResult.Failed(getString(Res.string.cloud_library_playback_disabled))
}
val result = store.resolvePlayback(item, file)
if (result is CloudLibraryPlaybackResult.Success) {
diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/collection/CollectionEditorRepository.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/collection/CollectionEditorRepository.kt
index 5f4b41e73..a0c819189 100644
--- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/collection/CollectionEditorRepository.kt
+++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/collection/CollectionEditorRepository.kt
@@ -11,25 +11,17 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
-import kotlinx.coroutines.runBlocking
import nuvio.composeapp.generated.resources.Res
-import nuvio.composeapp.generated.resources.collections_editor_tmdb_collection_title_format
-import nuvio.composeapp.generated.resources.collections_editor_tmdb_director_title_format
-import nuvio.composeapp.generated.resources.collections_editor_tmdb_discover_title
-import nuvio.composeapp.generated.resources.collections_editor_tmdb_invalid_id_or_url
-import nuvio.composeapp.generated.resources.collections_editor_tmdb_list_title_format
-import nuvio.composeapp.generated.resources.collections_editor_tmdb_network_title_format
-import nuvio.composeapp.generated.resources.collections_editor_tmdb_person_title_format
-import nuvio.composeapp.generated.resources.collections_editor_tmdb_production_title_format
-import nuvio.composeapp.generated.resources.collections_editor_tmdb_source_load_failed
-import nuvio.composeapp.generated.resources.collections_editor_trakt_enter_id_or_url
-import nuvio.composeapp.generated.resources.collections_editor_trakt_enter_name_url_or_id
-import nuvio.composeapp.generated.resources.collections_editor_trakt_fallback_title
-import nuvio.composeapp.generated.resources.collections_editor_trakt_load_failed
+import nuvio.composeapp.generated.resources.collections_editor_media_movies_suffix
+import nuvio.composeapp.generated.resources.collections_editor_media_series_suffix
+import nuvio.composeapp.generated.resources.collections_editor_resolved_trakt_list
+import nuvio.composeapp.generated.resources.collections_editor_tmdb_discover
+import nuvio.composeapp.generated.resources.collections_editor_tmdb_invalid_id_error
+import nuvio.composeapp.generated.resources.collections_editor_tmdb_load_error
+import nuvio.composeapp.generated.resources.collections_editor_trakt_id_url_required
+import nuvio.composeapp.generated.resources.collections_editor_trakt_input_required
+import nuvio.composeapp.generated.resources.collections_editor_trakt_load_error
import nuvio.composeapp.generated.resources.collections_editor_trakt_no_lists_found
-import nuvio.composeapp.generated.resources.collections_editor_trakt_resolved_subtitle
-import nuvio.composeapp.generated.resources.media_movies
-import nuvio.composeapp.generated.resources.media_series
import org.jetbrains.compose.resources.getString
import kotlin.uuid.ExperimentalUuidApi
import kotlin.uuid.Uuid
@@ -414,23 +406,25 @@ object CollectionEditorRepository {
val state = _uiState.value
val query = state.traktInput.trim()
if (query.isBlank()) {
- _uiState.value = state.copy(
- traktSearchError = runBlocking { getString(Res.string.collections_editor_trakt_enter_name_url_or_id) },
- )
+ scope.launch {
+ _uiState.value = _uiState.value.copy(
+ traktSearchError = getString(Res.string.collections_editor_trakt_input_required),
+ )
+ }
return
}
scope.launch {
+ val loadErrorMessage = getString(Res.string.collections_editor_trakt_load_error)
val results = if (query.isTraktListIdentifierInput()) {
runCatching {
val metadata = TraktPublicListSourceResolver.listImportMetadata(query)
- val id = metadata.traktListId
- ?: error(getString(Res.string.collections_editor_trakt_load_failed))
+ val id = metadata.traktListId ?: error(loadErrorMessage)
listOf(
TraktPublicListSearchResult(
traktListId = id,
- title = metadata.title ?: getString(Res.string.collections_editor_trakt_fallback_title, id),
- subtitle = getString(Res.string.collections_editor_trakt_resolved_subtitle),
+ title = metadata.title ?: "Trakt List $id",
+ subtitle = getString(Res.string.collections_editor_resolved_trakt_list),
coverImageUrl = metadata.coverImageUrl,
),
)
@@ -647,45 +641,46 @@ object CollectionEditorRepository {
}
val id = TmdbCollectionSourceResolver.parseTmdbId(state.tmdbInput)
if (sourceType != TmdbCollectionSourceType.DISCOVER && id == null) {
- _uiState.value = state.copy(
- tmdbSearchError = runBlocking { getString(Res.string.collections_editor_tmdb_invalid_id_or_url) },
- )
+ scope.launch {
+ _uiState.value = _uiState.value.copy(
+ tmdbSearchError = getString(Res.string.collections_editor_tmdb_invalid_id_error),
+ )
+ }
return
}
val mediaTypes = selectedMediaTypes(state, sourceType)
- val baseTitle = state.tmdbTitleInput.ifBlank {
- runBlocking {
- val idArg = id?.toString().orEmpty()
+ scope.launch {
+ val moviesSuffix = getString(Res.string.collections_editor_media_movies_suffix)
+ val seriesSuffix = getString(Res.string.collections_editor_media_series_suffix)
+ val baseTitle = state.tmdbTitleInput.ifBlank {
when (sourceType) {
- TmdbCollectionSourceType.LIST -> getString(Res.string.collections_editor_tmdb_list_title_format, idArg).trim()
- TmdbCollectionSourceType.COLLECTION -> getString(Res.string.collections_editor_tmdb_collection_title_format, idArg).trim()
- TmdbCollectionSourceType.COMPANY -> getString(Res.string.collections_editor_tmdb_production_title_format, idArg).trim()
- TmdbCollectionSourceType.NETWORK -> getString(Res.string.collections_editor_tmdb_network_title_format, idArg).trim()
- TmdbCollectionSourceType.PERSON -> getString(Res.string.collections_editor_tmdb_person_title_format, idArg).trim()
- TmdbCollectionSourceType.DIRECTOR -> getString(Res.string.collections_editor_tmdb_director_title_format, idArg).trim()
- TmdbCollectionSourceType.DISCOVER -> getString(Res.string.collections_editor_tmdb_discover_title)
+ TmdbCollectionSourceType.LIST -> "TMDB List ${id ?: ""}".trim()
+ TmdbCollectionSourceType.COLLECTION -> "TMDB Collection ${id ?: ""}".trim()
+ TmdbCollectionSourceType.COMPANY -> "TMDB Production ${id ?: ""}".trim()
+ TmdbCollectionSourceType.NETWORK -> "TMDB Network ${id ?: ""}".trim()
+ TmdbCollectionSourceType.PERSON -> "TMDB Person ${id ?: ""}".trim()
+ TmdbCollectionSourceType.DIRECTOR -> "TMDB Director ${id ?: ""}".trim()
+ TmdbCollectionSourceType.DISCOVER -> getString(Res.string.collections_editor_tmdb_discover)
}
}
- }
- val sources = mediaTypes.map { mediaType ->
- CollectionSource(
- provider = "tmdb",
- tmdbSourceType = sourceType.name,
- title = titleForMedia(baseTitle, mediaType, mediaTypes.size > 1),
- tmdbId = id,
- mediaType = mediaType.name,
- sortBy = state.tmdbSortBy,
- filters = state.tmdbFilters,
- )
- }
- if (sourceType == TmdbCollectionSourceType.LIST || sourceType == TmdbCollectionSourceType.COLLECTION) {
- scope.launch {
+ val sources = mediaTypes.map { mediaType ->
+ CollectionSource(
+ provider = "tmdb",
+ tmdbSourceType = sourceType.name,
+ title = titleForMedia(baseTitle, mediaType, mediaTypes.size > 1, moviesSuffix, seriesSuffix),
+ tmdbId = id,
+ mediaType = mediaType.name,
+ sortBy = state.tmdbSortBy,
+ filters = state.tmdbFilters,
+ )
+ }
+ if (sourceType == TmdbCollectionSourceType.LIST || sourceType == TmdbCollectionSourceType.COLLECTION) {
val metadata = runCatching { TmdbCollectionSourceResolver.importMetadata(sourceType, id!!) }
val resolved = metadata.getOrNull()
if (metadata.isFailure) {
_uiState.value = _uiState.value.copy(
tmdbSearchError = metadata.exceptionOrNull()?.message
- ?: getString(Res.string.collections_editor_tmdb_source_load_failed),
+ ?: getString(Res.string.collections_editor_tmdb_load_error),
)
return@launch
}
@@ -695,10 +690,10 @@ object CollectionEditorRepository {
},
coverImageUrl = resolved?.coverImageUrl,
)
+ } else {
+ addTmdbSourcesFromPicker(sources)
}
- return
}
- addTmdbSourcesFromPicker(sources)
}
private fun addTmdbSources(sources: List, coverImageUrl: String? = null) {
@@ -730,20 +725,24 @@ object CollectionEditorRepository {
val state = _uiState.value
val input = state.traktInput.trim()
if (input.isBlank()) {
- _uiState.value = state.copy(
- traktSearchError = runBlocking { getString(Res.string.collections_editor_trakt_enter_id_or_url) },
- )
+ scope.launch {
+ _uiState.value = _uiState.value.copy(
+ traktSearchError = getString(Res.string.collections_editor_trakt_id_url_required),
+ )
+ }
return
}
scope.launch {
+ val moviesSuffix = getString(Res.string.collections_editor_media_movies_suffix)
+ val seriesSuffix = getString(Res.string.collections_editor_media_series_suffix)
val metadata = runCatching { TraktPublicListSourceResolver.listImportMetadata(input) }
val resolved = metadata.getOrNull()
val listId = resolved?.traktListId
if (metadata.isFailure || listId == null) {
_uiState.value = _uiState.value.copy(
traktSearchError = metadata.exceptionOrNull()?.message
- ?: getString(Res.string.collections_editor_trakt_load_failed),
+ ?: getString(Res.string.collections_editor_trakt_load_error),
)
return@launch
}
@@ -755,7 +754,7 @@ object CollectionEditorRepository {
sources = selectedTraktMediaTypes(state).map { mediaType ->
CollectionSource(
provider = "trakt",
- title = titleForMedia(title, mediaType, state.traktMediaBoth),
+ title = titleForMedia(title, mediaType, state.traktMediaBoth, moviesSuffix, seriesSuffix),
traktListId = listId,
mediaType = mediaType.name,
sortBy = TraktListSort.normalize(state.traktSortBy),
@@ -770,19 +769,23 @@ object CollectionEditorRepository {
fun addTraktSourceFromResult(result: TraktPublicListSearchResult) {
val state = _uiState.value
val title = state.traktTitleInput.ifBlank { result.title }
- addTraktSourcesToFolder(
- sources = selectedTraktMediaTypes(state).map { mediaType ->
- CollectionSource(
- provider = "trakt",
- title = titleForMedia(title, mediaType, state.traktMediaBoth),
- traktListId = result.traktListId,
- mediaType = mediaType.name,
- sortBy = TraktListSort.normalize(state.traktSortBy),
- sortHow = TraktSortHow.normalize(state.traktSortHow),
- )
- },
- coverImageUrl = result.coverImageUrl,
- )
+ scope.launch {
+ val moviesSuffix = getString(Res.string.collections_editor_media_movies_suffix)
+ val seriesSuffix = getString(Res.string.collections_editor_media_series_suffix)
+ addTraktSourcesToFolder(
+ sources = selectedTraktMediaTypes(state).map { mediaType ->
+ CollectionSource(
+ provider = "trakt",
+ title = titleForMedia(title, mediaType, state.traktMediaBoth, moviesSuffix, seriesSuffix),
+ traktListId = result.traktListId,
+ mediaType = mediaType.name,
+ sortBy = TraktListSort.normalize(state.traktSortBy),
+ sortHow = TraktSortHow.normalize(state.traktSortHow),
+ )
+ },
+ coverImageUrl = result.coverImageUrl,
+ )
+ }
}
private fun addTraktSourcesToFolder(sources: List, coverImageUrl: String? = null) {
@@ -913,15 +916,13 @@ private fun titleForMedia(
title: String,
mediaType: TmdbCollectionMediaType,
addSuffix: Boolean,
+ moviesSuffix: String,
+ seriesSuffix: String,
): String {
if (!addSuffix) return title
- val suffix = runBlocking {
- getString(
- when (mediaType) {
- TmdbCollectionMediaType.MOVIE -> Res.string.media_movies
- TmdbCollectionMediaType.TV -> Res.string.media_series
- },
- )
+ val suffix = when (mediaType) {
+ TmdbCollectionMediaType.MOVIE -> moviesSuffix
+ TmdbCollectionMediaType.TV -> seriesSuffix
}
return "$title $suffix"
}
diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/collection/CollectionEditorScreen.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/collection/CollectionEditorScreen.kt
index cd77ca225..fa6b8444c 100644
--- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/collection/CollectionEditorScreen.kt
+++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/collection/CollectionEditorScreen.kt
@@ -1269,7 +1269,7 @@ private fun TmdbSourcePickerScreen(
label = stringResource(Res.string.collections_editor_tmdb_country),
helper = stringResource(Res.string.collections_editor_tmdb_country_helper),
value = state.tmdbFilters.withOriginCountry.orEmpty(),
- placeholder = stringResource(Res.string.collections_editor_tmdb_country_placeholder),
+ placeholder = stringResource(Res.string.collections_editor_watch_region_placeholder),
onValueChange = { value ->
CollectionEditorRepository.updateTmdbFilters {
it.copy(withOriginCountry = value.ifBlank { null })
@@ -1387,9 +1387,9 @@ private fun TmdbSourcePickerScreen(
chips = listOf(
stringResource(Res.string.collections_editor_tmdb_country_us) to "US",
stringResource(Res.string.collections_editor_tmdb_country_uk) to "GB",
- "Canada" to "CA",
- "Australia" to "AU",
- "Germany" to "DE",
+ stringResource(Res.string.collections_editor_tmdb_country_ca) to "CA",
+ stringResource(Res.string.collections_editor_tmdb_country_au) to "AU",
+ stringResource(Res.string.collections_editor_tmdb_country_de) to "DE",
),
onSelect = { value ->
CollectionEditorRepository.updateTmdbFilters { it.copy(watchRegion = value) }
diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/collection/TmdbCollectionSourceResolver.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/collection/TmdbCollectionSourceResolver.kt
index d8d9176cc..9ace8d967 100644
--- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/collection/TmdbCollectionSourceResolver.kt
+++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/collection/TmdbCollectionSourceResolver.kt
@@ -14,17 +14,7 @@ import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import nuvio.composeapp.generated.resources.Res
-import nuvio.composeapp.generated.resources.collections_tmdb_api_key_required
-import nuvio.composeapp.generated.resources.collections_tmdb_collection_not_found
-import nuvio.composeapp.generated.resources.collections_tmdb_company_not_found
-import nuvio.composeapp.generated.resources.collections_tmdb_discover_no_data
-import nuvio.composeapp.generated.resources.collections_tmdb_list_not_found
-import nuvio.composeapp.generated.resources.collections_tmdb_missing_collection_id
-import nuvio.composeapp.generated.resources.collections_tmdb_missing_list_id
-import nuvio.composeapp.generated.resources.collections_tmdb_missing_person_id
-import nuvio.composeapp.generated.resources.collections_tmdb_network_not_found
-import nuvio.composeapp.generated.resources.collections_tmdb_person_credits_not_found
-import nuvio.composeapp.generated.resources.collections_tmdb_person_not_found
+import nuvio.composeapp.generated.resources.collections_editor_tmdb_discover
import org.jetbrains.compose.resources.getString
import kotlin.math.roundToInt
@@ -113,7 +103,7 @@ object TmdbCollectionSourceResolver {
)
}
- TmdbCollectionSourceType.DISCOVER -> TmdbSourceImportMetadata(title = "TMDB Discover")
+ TmdbCollectionSourceType.DISCOVER -> TmdbSourceImportMetadata(title = getString(Res.string.collections_editor_tmdb_discover))
}
}
diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/IosVideoSettingsModal.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/IosVideoSettingsModal.kt
index adb17d137..7b3f6f25c 100644
--- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/IosVideoSettingsModal.kt
+++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/IosVideoSettingsModal.kt
@@ -41,6 +41,22 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
+import nuvio.composeapp.generated.resources.Res
+import nuvio.composeapp.generated.resources.player_video_settings_brightness
+import nuvio.composeapp.generated.resources.player_video_settings_contrast
+import nuvio.composeapp.generated.resources.player_video_settings_deband
+import nuvio.composeapp.generated.resources.player_video_settings_deband_desc
+import nuvio.composeapp.generated.resources.player_video_settings_gamma
+import nuvio.composeapp.generated.resources.player_video_settings_hdr_peak_detection
+import nuvio.composeapp.generated.resources.player_video_settings_hdr_peak_detection_desc
+import nuvio.composeapp.generated.resources.player_video_settings_interpolation
+import nuvio.composeapp.generated.resources.player_video_settings_interpolation_desc
+import nuvio.composeapp.generated.resources.player_video_settings_output_preset
+import nuvio.composeapp.generated.resources.player_video_settings_reset_tuning
+import nuvio.composeapp.generated.resources.player_video_settings_saturation
+import nuvio.composeapp.generated.resources.player_video_settings_title
+import nuvio.composeapp.generated.resources.player_video_settings_tone_mapping
+import org.jetbrains.compose.resources.stringResource
import kotlin.math.roundToInt
@Composable
@@ -96,7 +112,7 @@ internal fun IosVideoSettingsModal(
verticalAlignment = Alignment.CenterVertically,
) {
Text(
- text = "Video",
+ text = stringResource(Res.string.player_video_settings_title),
color = colorScheme.onSurface,
fontSize = 18.sp,
fontWeight = FontWeight.Bold,
@@ -106,7 +122,7 @@ internal fun IosVideoSettingsModal(
PlayerSettingsRepository.resetIosVideoOutputTuning()
onSettingsChanged()
}) {
- Text("Reset tuning")
+ Text(stringResource(Res.string.player_video_settings_reset_tuning))
}
}
@@ -118,11 +134,11 @@ internal fun IosVideoSettingsModal(
verticalArrangement = Arrangement.spacedBy(14.dp),
) {
OptionGroup(
- title = "Output preset",
+ title = stringResource(Res.string.player_video_settings_output_preset),
options = IosVideoOutputPreset.entries,
selected = settings.iosVideoOutputPreset,
- label = { it.label },
- description = { it.description },
+ label = { it.localizedLabel() },
+ description = { it.localizedDescription() },
onSelect = {
PlayerSettingsRepository.setIosVideoOutputPreset(it)
onSettingsChanged()
@@ -130,8 +146,8 @@ internal fun IosVideoSettingsModal(
)
ToggleRow(
- title = "HDR peak detection",
- description = "Estimate HDR peak brightness when metadata is bad or missing.",
+ title = stringResource(Res.string.player_video_settings_hdr_peak_detection),
+ description = stringResource(Res.string.player_video_settings_hdr_peak_detection_desc),
checked = settings.iosHdrComputePeakEnabled,
onCheckedChange = {
PlayerSettingsRepository.setIosHdrComputePeakEnabled(it)
@@ -140,7 +156,7 @@ internal fun IosVideoSettingsModal(
)
OptionGroup(
- title = "Tone mapping",
+ title = stringResource(Res.string.player_video_settings_tone_mapping),
options = IosToneMappingMode.entries,
selected = settings.iosToneMappingMode,
label = { it.label },
@@ -151,8 +167,8 @@ internal fun IosVideoSettingsModal(
)
ToggleRow(
- title = "Deband",
- description = "Reduce color banding at a small performance cost.",
+ title = stringResource(Res.string.player_video_settings_deband),
+ description = stringResource(Res.string.player_video_settings_deband_desc),
checked = settings.iosDebandEnabled,
onCheckedChange = {
PlayerSettingsRepository.setIosDebandEnabled(it)
@@ -160,8 +176,8 @@ internal fun IosVideoSettingsModal(
},
)
ToggleRow(
- title = "Frame interpolation",
- description = "Smooth motion when mpv can use display sync cleanly.",
+ title = stringResource(Res.string.player_video_settings_interpolation),
+ description = stringResource(Res.string.player_video_settings_interpolation_desc),
checked = settings.iosInterpolationEnabled,
onCheckedChange = {
PlayerSettingsRepository.setIosInterpolationEnabled(it)
@@ -170,7 +186,7 @@ internal fun IosVideoSettingsModal(
)
PictureSlider(
- title = "Brightness",
+ title = stringResource(Res.string.player_video_settings_brightness),
value = settings.iosBrightness,
onValueChanged = {
PlayerSettingsRepository.setIosBrightness(it)
@@ -178,7 +194,7 @@ internal fun IosVideoSettingsModal(
},
)
PictureSlider(
- title = "Contrast",
+ title = stringResource(Res.string.player_video_settings_contrast),
value = settings.iosContrast,
onValueChanged = {
PlayerSettingsRepository.setIosContrast(it)
@@ -186,7 +202,7 @@ internal fun IosVideoSettingsModal(
},
)
PictureSlider(
- title = "Saturation",
+ title = stringResource(Res.string.player_video_settings_saturation),
value = settings.iosSaturation,
onValueChanged = {
PlayerSettingsRepository.setIosSaturation(it)
@@ -194,7 +210,7 @@ internal fun IosVideoSettingsModal(
},
)
PictureSlider(
- title = "Gamma",
+ title = stringResource(Res.string.player_video_settings_gamma),
value = settings.iosGamma,
onValueChanged = {
PlayerSettingsRepository.setIosGamma(it)
@@ -260,8 +276,8 @@ private fun OptionGroup(
title: String,
options: List,
selected: T,
- label: (T) -> String,
- description: ((T) -> String)? = null,
+ label: @Composable (T) -> String,
+ description: @Composable ((T) -> String)? = null,
onSelect: (T) -> Unit,
) {
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerControls.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerControls.kt
index 7105e43ce..9241e9a12 100644
--- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerControls.kt
+++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerControls.kt
@@ -308,7 +308,7 @@ private fun PlayerHeader(
if (onSubmitIntroClick != null) {
PlayerHeaderIconButton(
icon = Icons.Rounded.Flag,
- contentDescription = stringResource(Res.string.submit_intro_action),
+ contentDescription = stringResource(Res.string.player_action_submit_intro),
buttonSize = metrics.headerIconSize + 16.dp,
iconSize = metrics.headerIconSize,
onClick = onSubmitIntroClick,
@@ -328,7 +328,7 @@ private fun PlayerHeader(
if (onVideoSettingsClick != null) {
PlayerHeaderIconButton(
icon = Icons.Rounded.Build,
- contentDescription = "Video settings",
+ contentDescription = stringResource(Res.string.player_action_video_settings),
buttonSize = metrics.headerIconSize + 16.dp,
iconSize = metrics.headerIconSize,
onClick = onVideoSettingsClick,
diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerModels.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerModels.kt
index 773a276dc..a7b97866d 100644
--- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerModels.kt
+++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerModels.kt
@@ -1,6 +1,18 @@
package com.nuvio.app.features.player
+import androidx.compose.runtime.Composable
import kotlinx.serialization.Serializable
+import nuvio.composeapp.generated.resources.Res
+import nuvio.composeapp.generated.resources.player_ios_hardware_decoder_off
+import nuvio.composeapp.generated.resources.player_ios_preset_compatibility_desc
+import nuvio.composeapp.generated.resources.player_ios_preset_compatibility_label
+import nuvio.composeapp.generated.resources.player_ios_preset_custom_desc
+import nuvio.composeapp.generated.resources.player_ios_preset_custom_label
+import nuvio.composeapp.generated.resources.player_ios_preset_native_edr_desc
+import nuvio.composeapp.generated.resources.player_ios_preset_native_edr_label
+import nuvio.composeapp.generated.resources.player_ios_preset_sdr_tone_mapped_desc
+import nuvio.composeapp.generated.resources.player_ios_preset_sdr_tone_mapped_label
+import org.jetbrains.compose.resources.stringResource
@Serializable
data class PlayerRoute(
@@ -129,6 +141,28 @@ enum class IosHardwareDecoderMode(
Off("no", "Off"),
}
+@Composable
+fun IosVideoOutputPreset.localizedLabel(): String = when (this) {
+ IosVideoOutputPreset.NativeEdr -> stringResource(Res.string.player_ios_preset_native_edr_label)
+ IosVideoOutputPreset.SdrToneMapped -> stringResource(Res.string.player_ios_preset_sdr_tone_mapped_label)
+ IosVideoOutputPreset.Compatibility -> stringResource(Res.string.player_ios_preset_compatibility_label)
+ IosVideoOutputPreset.Custom -> stringResource(Res.string.player_ios_preset_custom_label)
+}
+
+@Composable
+fun IosVideoOutputPreset.localizedDescription(): String = when (this) {
+ IosVideoOutputPreset.NativeEdr -> stringResource(Res.string.player_ios_preset_native_edr_desc)
+ IosVideoOutputPreset.SdrToneMapped -> stringResource(Res.string.player_ios_preset_sdr_tone_mapped_desc)
+ IosVideoOutputPreset.Compatibility -> stringResource(Res.string.player_ios_preset_compatibility_desc)
+ IosVideoOutputPreset.Custom -> stringResource(Res.string.player_ios_preset_custom_desc)
+}
+
+@Composable
+fun IosHardwareDecoderMode.localizedLabel(): String = when (this) {
+ IosHardwareDecoderMode.Off -> stringResource(Res.string.player_ios_hardware_decoder_off)
+ else -> label
+}
+
data class PlayerPlaybackSnapshot(
val isLoading: Boolean = true,
val isPlaying: Boolean = false,
diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/skip/SubmitIntroDialog.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/skip/SubmitIntroDialog.kt
index 6f3e80691..a7d290497 100644
--- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/skip/SubmitIntroDialog.kt
+++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/skip/SubmitIntroDialog.kt
@@ -52,14 +52,14 @@ import kotlinx.coroutines.launch
import nuvio.composeapp.generated.resources.Res
import nuvio.composeapp.generated.resources.action_cancel
import nuvio.composeapp.generated.resources.action_close
-import nuvio.composeapp.generated.resources.submit_intro_button_submit
-import nuvio.composeapp.generated.resources.submit_intro_capture_button
-import nuvio.composeapp.generated.resources.submit_intro_end_time_label
+import nuvio.composeapp.generated.resources.submit_intro_capture
+import nuvio.composeapp.generated.resources.submit_intro_end_time
import nuvio.composeapp.generated.resources.submit_intro_segment_intro
import nuvio.composeapp.generated.resources.submit_intro_segment_outro
import nuvio.composeapp.generated.resources.submit_intro_segment_recap
-import nuvio.composeapp.generated.resources.submit_intro_segment_type_label
-import nuvio.composeapp.generated.resources.submit_intro_start_time_label
+import nuvio.composeapp.generated.resources.submit_intro_segment_type
+import nuvio.composeapp.generated.resources.submit_intro_start_time
+import nuvio.composeapp.generated.resources.submit_intro_submit
import nuvio.composeapp.generated.resources.submit_intro_title
import org.jetbrains.compose.resources.stringResource
import kotlin.math.floor
@@ -110,18 +110,14 @@ fun SubmitIntroDialog(
fontWeight = FontWeight.Bold,
)
IconButton(onClick = onDismiss) {
- Icon(
- Icons.Rounded.Close,
- contentDescription = stringResource(Res.string.action_close),
- tint = MaterialTheme.colorScheme.onSurfaceVariant,
- )
+ Icon(Icons.Rounded.Close, contentDescription = stringResource(Res.string.action_close), tint = MaterialTheme.colorScheme.onSurfaceVariant)
}
}
// Segment Type
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
Text(
- text = stringResource(Res.string.submit_intro_segment_type_label),
+ text = stringResource(Res.string.submit_intro_segment_type),
style = MaterialTheme.typography.labelSmall,
color = MaterialTheme.colorScheme.onSurfaceVariant,
fontWeight = FontWeight.SemiBold,
@@ -156,7 +152,7 @@ fun SubmitIntroDialog(
// Start Time
TimeInputRow(
- label = stringResource(Res.string.submit_intro_start_time_label),
+ label = stringResource(Res.string.submit_intro_start_time),
value = startTimeStr,
onValueChange = onStartTimeChange,
onCapture = { onStartTimeChange(formatSecondsToMMSS(currentTimeSec)) }
@@ -164,7 +160,7 @@ fun SubmitIntroDialog(
// End Time
TimeInputRow(
- label = stringResource(Res.string.submit_intro_end_time_label),
+ label = stringResource(Res.string.submit_intro_end_time),
value = endTimeStr,
onValueChange = onEndTimeChange,
onCapture = { onEndTimeChange(formatSecondsToMMSS(currentTimeSec)) }
@@ -234,7 +230,7 @@ fun SubmitIntroDialog(
) {
Icon(Icons.Rounded.Send, contentDescription = null, tint = MaterialTheme.colorScheme.onPrimary, modifier = Modifier.size(18.dp))
Text(
- text = stringResource(Res.string.submit_intro_button_submit),
+ text = stringResource(Res.string.submit_intro_submit),
color = MaterialTheme.colorScheme.onPrimary,
fontWeight = FontWeight.Bold
)
@@ -345,7 +341,7 @@ private fun TimeInputRow(
modifier = Modifier.size(18.dp)
)
Text(
- text = stringResource(Res.string.submit_intro_capture_button),
+ text = stringResource(Res.string.submit_intro_capture),
color = MaterialTheme.colorScheme.onSurfaceVariant,
style = MaterialTheme.typography.bodySmall,
fontWeight = FontWeight.SemiBold
diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/DebridSettingsPage.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/DebridSettingsPage.kt
index 80eb22b15..76d742154 100644
--- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/DebridSettingsPage.kt
+++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/DebridSettingsPage.kt
@@ -120,8 +120,82 @@ import nuvio.composeapp.generated.resources.settings_debrid_resolve_with_descrip
import nuvio.composeapp.generated.resources.settings_debrid_section_instant_playback
import nuvio.composeapp.generated.resources.settings_debrid_section_formatting
import nuvio.composeapp.generated.resources.settings_debrid_section_providers
+import nuvio.composeapp.generated.resources.settings_debrid_section_result_management
import nuvio.composeapp.generated.resources.settings_debrid_section_title
+import nuvio.composeapp.generated.resources.settings_debrid_max_results
+import nuvio.composeapp.generated.resources.settings_debrid_max_results_desc
+import nuvio.composeapp.generated.resources.settings_debrid_sort_results
+import nuvio.composeapp.generated.resources.settings_debrid_sort_results_desc
+import nuvio.composeapp.generated.resources.settings_debrid_per_resolution_limit
+import nuvio.composeapp.generated.resources.settings_debrid_per_resolution_limit_desc
+import nuvio.composeapp.generated.resources.settings_debrid_per_quality_limit
+import nuvio.composeapp.generated.resources.settings_debrid_per_quality_limit_desc
+import nuvio.composeapp.generated.resources.settings_debrid_size_range
+import nuvio.composeapp.generated.resources.settings_debrid_size_range_desc
+import nuvio.composeapp.generated.resources.settings_debrid_learn_more
+import nuvio.composeapp.generated.resources.settings_debrid_template_default_format
+import nuvio.composeapp.generated.resources.settings_debrid_release_groups_hint
+import nuvio.composeapp.generated.resources.settings_debrid_sort_default
+import nuvio.composeapp.generated.resources.settings_debrid_sort_largest
+import nuvio.composeapp.generated.resources.settings_debrid_sort_smallest
+import nuvio.composeapp.generated.resources.settings_debrid_sort_best_audio
+import nuvio.composeapp.generated.resources.settings_debrid_sort_language
+import nuvio.composeapp.generated.resources.settings_debrid_selection_any
+import nuvio.composeapp.generated.resources.settings_debrid_selection_count
+import nuvio.composeapp.generated.resources.settings_debrid_results_all
+import nuvio.composeapp.generated.resources.settings_debrid_results_count
+import nuvio.composeapp.generated.resources.settings_debrid_size_up_to
+import nuvio.composeapp.generated.resources.settings_debrid_size_min
+import nuvio.composeapp.generated.resources.settings_debrid_size_range_value
+import nuvio.composeapp.generated.resources.settings_debrid_rule_preferred_resolutions
+import nuvio.composeapp.generated.resources.settings_debrid_rule_preferred_resolutions_desc
+import nuvio.composeapp.generated.resources.settings_debrid_rule_required_resolutions
+import nuvio.composeapp.generated.resources.settings_debrid_rule_required_resolutions_desc
+import nuvio.composeapp.generated.resources.settings_debrid_rule_excluded_resolutions
+import nuvio.composeapp.generated.resources.settings_debrid_rule_excluded_resolutions_desc
+import nuvio.composeapp.generated.resources.settings_debrid_rule_preferred_qualities
+import nuvio.composeapp.generated.resources.settings_debrid_rule_preferred_qualities_desc
+import nuvio.composeapp.generated.resources.settings_debrid_rule_required_qualities
+import nuvio.composeapp.generated.resources.settings_debrid_rule_required_qualities_desc
+import nuvio.composeapp.generated.resources.settings_debrid_rule_excluded_qualities
+import nuvio.composeapp.generated.resources.settings_debrid_rule_excluded_qualities_desc
+import nuvio.composeapp.generated.resources.settings_debrid_rule_preferred_visual_tags
+import nuvio.composeapp.generated.resources.settings_debrid_rule_preferred_visual_tags_desc
+import nuvio.composeapp.generated.resources.settings_debrid_rule_required_visual_tags
+import nuvio.composeapp.generated.resources.settings_debrid_rule_required_visual_tags_desc
+import nuvio.composeapp.generated.resources.settings_debrid_rule_excluded_visual_tags
+import nuvio.composeapp.generated.resources.settings_debrid_rule_excluded_visual_tags_desc
+import nuvio.composeapp.generated.resources.settings_debrid_rule_preferred_audio_tags
+import nuvio.composeapp.generated.resources.settings_debrid_rule_preferred_audio_tags_desc
+import nuvio.composeapp.generated.resources.settings_debrid_rule_required_audio_tags
+import nuvio.composeapp.generated.resources.settings_debrid_rule_required_audio_tags_desc
+import nuvio.composeapp.generated.resources.settings_debrid_rule_excluded_audio_tags
+import nuvio.composeapp.generated.resources.settings_debrid_rule_excluded_audio_tags_desc
+import nuvio.composeapp.generated.resources.settings_debrid_rule_preferred_channels
+import nuvio.composeapp.generated.resources.settings_debrid_rule_preferred_channels_desc
+import nuvio.composeapp.generated.resources.settings_debrid_rule_required_channels
+import nuvio.composeapp.generated.resources.settings_debrid_rule_required_channels_desc
+import nuvio.composeapp.generated.resources.settings_debrid_rule_excluded_channels
+import nuvio.composeapp.generated.resources.settings_debrid_rule_excluded_channels_desc
+import nuvio.composeapp.generated.resources.settings_debrid_rule_preferred_encodes
+import nuvio.composeapp.generated.resources.settings_debrid_rule_preferred_encodes_desc
+import nuvio.composeapp.generated.resources.settings_debrid_rule_required_encodes
+import nuvio.composeapp.generated.resources.settings_debrid_rule_required_encodes_desc
+import nuvio.composeapp.generated.resources.settings_debrid_rule_excluded_encodes
+import nuvio.composeapp.generated.resources.settings_debrid_rule_excluded_encodes_desc
+import nuvio.composeapp.generated.resources.settings_debrid_rule_preferred_languages
+import nuvio.composeapp.generated.resources.settings_debrid_rule_preferred_languages_desc
+import nuvio.composeapp.generated.resources.settings_debrid_rule_required_languages
+import nuvio.composeapp.generated.resources.settings_debrid_rule_required_languages_desc
+import nuvio.composeapp.generated.resources.settings_debrid_rule_excluded_languages
+import nuvio.composeapp.generated.resources.settings_debrid_rule_excluded_languages_desc
+import nuvio.composeapp.generated.resources.settings_debrid_rule_required_release_groups
+import nuvio.composeapp.generated.resources.settings_debrid_rule_required_release_groups_desc
+import nuvio.composeapp.generated.resources.settings_debrid_rule_excluded_release_groups
+import nuvio.composeapp.generated.resources.settings_debrid_rule_excluded_release_groups_desc
+import org.jetbrains.compose.resources.getString
import org.jetbrains.compose.resources.stringResource
+import kotlinx.coroutines.runBlocking
private const val CLOUD_SERVICES_FAQ_URL = "https://nuvioapp.space/faq#common-cloud-library-and-cloud-services"
@@ -319,14 +393,14 @@ internal fun LazyListScope.debridSettingsContent(
val rows = debridRuleRows(preferences)
SettingsSection(
- title = "Result Management",
+ title = stringResource(Res.string.settings_debrid_section_result_management),
isTablet = isTablet,
) {
SettingsGroup(isTablet = isTablet) {
DebridPreferenceRow(
isTablet = isTablet,
- title = "Max results",
- description = "Limit how many results appear.",
+ title = stringResource(Res.string.settings_debrid_max_results),
+ description = stringResource(Res.string.settings_debrid_max_results_desc),
value = streamMaxResultsLabel(preferences.maxResults),
enabled = settings.canResolvePlayableLinks,
onClick = { activeStreamPicker = DebridStreamPicker.MAX_RESULTS },
@@ -334,8 +408,8 @@ internal fun LazyListScope.debridSettingsContent(
SettingsGroupDivider(isTablet = isTablet)
DebridPreferenceRow(
isTablet = isTablet,
- title = "Sort results",
- description = "Choose how results are ordered.",
+ title = stringResource(Res.string.settings_debrid_sort_results),
+ description = stringResource(Res.string.settings_debrid_sort_results_desc),
value = sortProfileLabel(preferences.sortCriteria),
enabled = settings.canResolvePlayableLinks,
onClick = { activeStreamPicker = DebridStreamPicker.SORT_MODE },
@@ -343,8 +417,8 @@ internal fun LazyListScope.debridSettingsContent(
SettingsGroupDivider(isTablet = isTablet)
DebridPreferenceRow(
isTablet = isTablet,
- title = "Per resolution limit",
- description = "Cap repeated 2160p, 1080p, 720p results after sorting.",
+ title = stringResource(Res.string.settings_debrid_per_resolution_limit),
+ description = stringResource(Res.string.settings_debrid_per_resolution_limit_desc),
value = streamMaxResultsLabel(preferences.maxPerResolution),
enabled = settings.canResolvePlayableLinks,
onClick = { activeStreamPicker = DebridStreamPicker.MAX_PER_RESOLUTION },
@@ -352,8 +426,8 @@ internal fun LazyListScope.debridSettingsContent(
SettingsGroupDivider(isTablet = isTablet)
DebridPreferenceRow(
isTablet = isTablet,
- title = "Per quality limit",
- description = "Cap repeated BluRay, WEB-DL, REMUX results after sorting.",
+ title = stringResource(Res.string.settings_debrid_per_quality_limit),
+ description = stringResource(Res.string.settings_debrid_per_quality_limit_desc),
value = streamMaxResultsLabel(preferences.maxPerQuality),
enabled = settings.canResolvePlayableLinks,
onClick = { activeStreamPicker = DebridStreamPicker.MAX_PER_QUALITY },
@@ -361,8 +435,8 @@ internal fun LazyListScope.debridSettingsContent(
SettingsGroupDivider(isTablet = isTablet)
DebridPreferenceRow(
isTablet = isTablet,
- title = "Size range",
- description = "Filter results by file size.",
+ title = stringResource(Res.string.settings_debrid_size_range),
+ description = stringResource(Res.string.settings_debrid_size_range_desc),
value = sizeRangeLabel(preferences),
enabled = settings.canResolvePlayableLinks,
onClick = { activeStreamPicker = DebridStreamPicker.SIZE_RANGE },
@@ -480,7 +554,7 @@ private fun DebridLearnMoreFooter(
contentAlignment = Alignment.Center,
) {
TextButton(onClick = onClick) {
- Text("Learn more")
+ Text(stringResource(Res.string.settings_debrid_learn_more))
}
}
}
@@ -491,12 +565,13 @@ private enum class DebridTemplateField {
}
private fun templatePreview(value: String, defaultValue: String): String {
- if (value.trim().isBlank() || value.trim() == defaultValue.trim()) return "Default format"
+ val defaultFormat = runBlocking { getString(Res.string.settings_debrid_template_default_format) }
+ if (value.trim().isBlank() || value.trim() == defaultValue.trim()) return defaultFormat
val firstLine = value
.lineSequence()
.map { it.trim() }
.firstOrNull { it.isNotBlank() }
- ?: return "Default format"
+ ?: return defaultFormat
return if (firstLine.length <= 28) firstLine else "${firstLine.take(28)}..."
}
@@ -716,7 +791,7 @@ private fun DebridStreamPreferenceDialog(
) {
when (picker) {
DebridStreamPicker.MAX_RESULTS -> DebridIntChoiceDialog(
- title = "Max results",
+ title = stringResource(Res.string.settings_debrid_max_results),
selectedValue = preferences.maxResults,
options = listOf(0, 5, 10, 20, 50),
label = { streamMaxResultsLabel(it) },
@@ -724,7 +799,7 @@ private fun DebridStreamPreferenceDialog(
onDismiss = onDismiss,
)
DebridStreamPicker.MAX_PER_RESOLUTION -> DebridIntChoiceDialog(
- title = "Max results",
+ title = stringResource(Res.string.settings_debrid_max_results),
selectedValue = preferences.maxPerResolution,
options = listOf(0, 1, 2, 3, 5),
label = { streamMaxResultsLabel(it) },
@@ -732,7 +807,7 @@ private fun DebridStreamPreferenceDialog(
onDismiss = onDismiss,
)
DebridStreamPicker.MAX_PER_QUALITY -> DebridIntChoiceDialog(
- title = "Max results",
+ title = stringResource(Res.string.settings_debrid_max_results),
selectedValue = preferences.maxPerQuality,
options = listOf(0, 1, 2, 3, 5),
label = { streamMaxResultsLabel(it) },
@@ -740,7 +815,7 @@ private fun DebridStreamPreferenceDialog(
onDismiss = onDismiss,
)
DebridStreamPicker.SORT_MODE -> DebridSingleChoiceDialog(
- title = "Sort results",
+ title = stringResource(Res.string.settings_debrid_sort_results),
selectedValue = sortProfileFor(preferences.sortCriteria),
options = listOf(
DebridSortProfile.DEFAULT,
@@ -754,7 +829,7 @@ private fun DebridStreamPreferenceDialog(
onDismiss = onDismiss,
)
DebridStreamPicker.SIZE_RANGE -> DebridSingleChoiceDialog(
- title = "Size range",
+ title = stringResource(Res.string.settings_debrid_size_range),
selectedValue = preferences.sizeMinGb to preferences.sizeMaxGb,
options = listOf(0 to 0, 0 to 5, 0 to 10, 5 to 20, 10 to 50, 20 to 100),
label = { sizeRangeLabel(it.first, it.second) },
@@ -762,7 +837,7 @@ private fun DebridStreamPreferenceDialog(
onDismiss = onDismiss,
)
DebridStreamPicker.PREFERRED_RESOLUTIONS -> DebridMultiChoiceDialog(
- title = "Preferred resolutions",
+ title = stringResource(Res.string.settings_debrid_rule_preferred_resolutions),
selectedValues = preferences.preferredResolutions,
values = DebridStreamResolution.defaultOrder,
label = { it.label },
@@ -770,7 +845,7 @@ private fun DebridStreamPreferenceDialog(
onDismiss = onDismiss,
)
DebridStreamPicker.REQUIRED_RESOLUTIONS -> DebridMultiChoiceDialog(
- title = "Required resolutions",
+ title = stringResource(Res.string.settings_debrid_rule_required_resolutions),
selectedValues = preferences.requiredResolutions,
values = DebridStreamResolution.defaultOrder,
label = { it.label },
@@ -778,7 +853,7 @@ private fun DebridStreamPreferenceDialog(
onDismiss = onDismiss,
)
DebridStreamPicker.EXCLUDED_RESOLUTIONS -> DebridMultiChoiceDialog(
- title = "Excluded resolutions",
+ title = stringResource(Res.string.settings_debrid_rule_excluded_resolutions),
selectedValues = preferences.excludedResolutions,
values = DebridStreamResolution.defaultOrder,
label = { it.label },
@@ -786,7 +861,7 @@ private fun DebridStreamPreferenceDialog(
onDismiss = onDismiss,
)
DebridStreamPicker.PREFERRED_QUALITIES -> DebridMultiChoiceDialog(
- title = "Preferred qualities",
+ title = stringResource(Res.string.settings_debrid_rule_preferred_qualities),
selectedValues = preferences.preferredQualities,
values = DebridStreamQuality.defaultOrder,
label = { it.label },
@@ -794,7 +869,7 @@ private fun DebridStreamPreferenceDialog(
onDismiss = onDismiss,
)
DebridStreamPicker.REQUIRED_QUALITIES -> DebridMultiChoiceDialog(
- title = "Required qualities",
+ title = stringResource(Res.string.settings_debrid_rule_required_qualities),
selectedValues = preferences.requiredQualities,
values = DebridStreamQuality.defaultOrder,
label = { it.label },
@@ -802,7 +877,7 @@ private fun DebridStreamPreferenceDialog(
onDismiss = onDismiss,
)
DebridStreamPicker.EXCLUDED_QUALITIES -> DebridMultiChoiceDialog(
- title = "Excluded qualities",
+ title = stringResource(Res.string.settings_debrid_rule_excluded_qualities),
selectedValues = preferences.excludedQualities,
values = DebridStreamQuality.defaultOrder,
label = { it.label },
@@ -810,7 +885,7 @@ private fun DebridStreamPreferenceDialog(
onDismiss = onDismiss,
)
DebridStreamPicker.PREFERRED_VISUAL_TAGS -> DebridMultiChoiceDialog(
- title = "Preferred visual tags",
+ title = stringResource(Res.string.settings_debrid_rule_preferred_visual_tags),
selectedValues = preferences.preferredVisualTags,
values = DebridStreamVisualTag.defaultOrder,
label = { it.label },
@@ -818,7 +893,7 @@ private fun DebridStreamPreferenceDialog(
onDismiss = onDismiss,
)
DebridStreamPicker.REQUIRED_VISUAL_TAGS -> DebridMultiChoiceDialog(
- title = "Required visual tags",
+ title = stringResource(Res.string.settings_debrid_rule_required_visual_tags),
selectedValues = preferences.requiredVisualTags,
values = DebridStreamVisualTag.defaultOrder,
label = { it.label },
@@ -826,7 +901,7 @@ private fun DebridStreamPreferenceDialog(
onDismiss = onDismiss,
)
DebridStreamPicker.EXCLUDED_VISUAL_TAGS -> DebridMultiChoiceDialog(
- title = "Excluded visual tags",
+ title = stringResource(Res.string.settings_debrid_rule_excluded_visual_tags),
selectedValues = preferences.excludedVisualTags,
values = DebridStreamVisualTag.defaultOrder,
label = { it.label },
@@ -834,7 +909,7 @@ private fun DebridStreamPreferenceDialog(
onDismiss = onDismiss,
)
DebridStreamPicker.PREFERRED_AUDIO_TAGS -> DebridMultiChoiceDialog(
- title = "Preferred audio tags",
+ title = stringResource(Res.string.settings_debrid_rule_preferred_audio_tags),
selectedValues = preferences.preferredAudioTags,
values = DebridStreamAudioTag.defaultOrder,
label = { it.label },
@@ -842,7 +917,7 @@ private fun DebridStreamPreferenceDialog(
onDismiss = onDismiss,
)
DebridStreamPicker.REQUIRED_AUDIO_TAGS -> DebridMultiChoiceDialog(
- title = "Required audio tags",
+ title = stringResource(Res.string.settings_debrid_rule_required_audio_tags),
selectedValues = preferences.requiredAudioTags,
values = DebridStreamAudioTag.defaultOrder,
label = { it.label },
@@ -850,7 +925,7 @@ private fun DebridStreamPreferenceDialog(
onDismiss = onDismiss,
)
DebridStreamPicker.EXCLUDED_AUDIO_TAGS -> DebridMultiChoiceDialog(
- title = "Excluded audio tags",
+ title = stringResource(Res.string.settings_debrid_rule_excluded_audio_tags),
selectedValues = preferences.excludedAudioTags,
values = DebridStreamAudioTag.defaultOrder,
label = { it.label },
@@ -858,7 +933,7 @@ private fun DebridStreamPreferenceDialog(
onDismiss = onDismiss,
)
DebridStreamPicker.PREFERRED_AUDIO_CHANNELS -> DebridMultiChoiceDialog(
- title = "Preferred channels",
+ title = stringResource(Res.string.settings_debrid_rule_preferred_channels),
selectedValues = preferences.preferredAudioChannels,
values = DebridStreamAudioChannel.defaultOrder,
label = { it.label },
@@ -866,7 +941,7 @@ private fun DebridStreamPreferenceDialog(
onDismiss = onDismiss,
)
DebridStreamPicker.REQUIRED_AUDIO_CHANNELS -> DebridMultiChoiceDialog(
- title = "Required channels",
+ title = stringResource(Res.string.settings_debrid_rule_required_channels),
selectedValues = preferences.requiredAudioChannels,
values = DebridStreamAudioChannel.defaultOrder,
label = { it.label },
@@ -874,7 +949,7 @@ private fun DebridStreamPreferenceDialog(
onDismiss = onDismiss,
)
DebridStreamPicker.EXCLUDED_AUDIO_CHANNELS -> DebridMultiChoiceDialog(
- title = "Excluded channels",
+ title = stringResource(Res.string.settings_debrid_rule_excluded_channels),
selectedValues = preferences.excludedAudioChannels,
values = DebridStreamAudioChannel.defaultOrder,
label = { it.label },
@@ -882,7 +957,7 @@ private fun DebridStreamPreferenceDialog(
onDismiss = onDismiss,
)
DebridStreamPicker.PREFERRED_ENCODES -> DebridMultiChoiceDialog(
- title = "Preferred encodes",
+ title = stringResource(Res.string.settings_debrid_rule_preferred_encodes),
selectedValues = preferences.preferredEncodes,
values = DebridStreamEncode.defaultOrder,
label = { it.label },
@@ -890,7 +965,7 @@ private fun DebridStreamPreferenceDialog(
onDismiss = onDismiss,
)
DebridStreamPicker.REQUIRED_ENCODES -> DebridMultiChoiceDialog(
- title = "Required encodes",
+ title = stringResource(Res.string.settings_debrid_rule_required_encodes),
selectedValues = preferences.requiredEncodes,
values = DebridStreamEncode.defaultOrder,
label = { it.label },
@@ -898,7 +973,7 @@ private fun DebridStreamPreferenceDialog(
onDismiss = onDismiss,
)
DebridStreamPicker.EXCLUDED_ENCODES -> DebridMultiChoiceDialog(
- title = "Excluded encodes",
+ title = stringResource(Res.string.settings_debrid_rule_excluded_encodes),
selectedValues = preferences.excludedEncodes,
values = DebridStreamEncode.defaultOrder,
label = { it.label },
@@ -906,7 +981,7 @@ private fun DebridStreamPreferenceDialog(
onDismiss = onDismiss,
)
DebridStreamPicker.PREFERRED_LANGUAGES -> DebridMultiChoiceDialog(
- title = "Preferred languages",
+ title = stringResource(Res.string.settings_debrid_rule_preferred_languages),
selectedValues = preferences.preferredLanguages,
values = DebridStreamLanguage.entries,
label = { it.label },
@@ -914,7 +989,7 @@ private fun DebridStreamPreferenceDialog(
onDismiss = onDismiss,
)
DebridStreamPicker.REQUIRED_LANGUAGES -> DebridMultiChoiceDialog(
- title = "Required languages",
+ title = stringResource(Res.string.settings_debrid_rule_required_languages),
selectedValues = preferences.requiredLanguages,
values = DebridStreamLanguage.entries,
label = { it.label },
@@ -922,7 +997,7 @@ private fun DebridStreamPreferenceDialog(
onDismiss = onDismiss,
)
DebridStreamPicker.EXCLUDED_LANGUAGES -> DebridMultiChoiceDialog(
- title = "Excluded languages",
+ title = stringResource(Res.string.settings_debrid_rule_excluded_languages),
selectedValues = preferences.excludedLanguages,
values = DebridStreamLanguage.entries,
label = { it.label },
@@ -930,13 +1005,13 @@ private fun DebridStreamPreferenceDialog(
onDismiss = onDismiss,
)
DebridStreamPicker.REQUIRED_RELEASE_GROUPS -> DebridTextListDialog(
- title = "Required release groups",
+ title = stringResource(Res.string.settings_debrid_rule_required_release_groups),
selectedValues = preferences.requiredReleaseGroups,
onSelected = { value -> onPreferencesChanged(preferences.copy(requiredReleaseGroups = value)) },
onDismiss = onDismiss,
)
DebridStreamPicker.EXCLUDED_RELEASE_GROUPS -> DebridTextListDialog(
- title = "Excluded release groups",
+ title = stringResource(Res.string.settings_debrid_rule_excluded_release_groups),
selectedValues = preferences.excludedReleaseGroups,
onSelected = { value -> onPreferencesChanged(preferences.copy(excludedReleaseGroups = value)) },
onDismiss = onDismiss,
@@ -1063,7 +1138,7 @@ private fun DebridTextListDialog(
BasicAlertDialog(onDismissRequest = onDismiss) {
DebridDialogSurface(title = title) {
Text(
- text = "Enter one group per line.",
+ text = stringResource(Res.string.settings_debrid_release_groups_hint),
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant,
)
@@ -1184,56 +1259,73 @@ private fun DebridDialogOptionRow(
@Composable
private fun streamMaxResultsLabel(value: Int): String =
- if (value <= 0) "All results" else "$value results"
-
-private fun sortProfileLabel(value: DebridSortProfile): String =
- when (value) {
- DebridSortProfile.DEFAULT -> "Default"
- DebridSortProfile.LARGEST -> "Largest first"
- DebridSortProfile.SMALLEST -> "Smallest first"
- DebridSortProfile.AUDIO -> "Best audio first"
- DebridSortProfile.LANGUAGE -> "Language first"
+ if (value <= 0) {
+ stringResource(Res.string.settings_debrid_results_all)
+ } else {
+ stringResource(Res.string.settings_debrid_results_count, value)
}
+@Composable
+private fun sortProfileLabel(value: DebridSortProfile): String =
+ when (value) {
+ DebridSortProfile.DEFAULT -> stringResource(Res.string.settings_debrid_sort_default)
+ DebridSortProfile.LARGEST -> stringResource(Res.string.settings_debrid_sort_largest)
+ DebridSortProfile.SMALLEST -> stringResource(Res.string.settings_debrid_sort_smallest)
+ DebridSortProfile.AUDIO -> stringResource(Res.string.settings_debrid_sort_best_audio)
+ DebridSortProfile.LANGUAGE -> stringResource(Res.string.settings_debrid_sort_language)
+ }
+
+@Composable
+private fun sortProfileLabel(criteria: List): String =
+ sortProfileLabel(sortProfileFor(criteria))
+
+@Composable
private fun debridRuleRows(preferences: DebridStreamPreferences): List =
listOf(
- DebridRuleRow(DebridStreamPicker.PREFERRED_RESOLUTIONS, "Preferred resolutions", "Sort selected resolutions first, in default order.", selectionCountLabel(preferences.preferredResolutions)),
- DebridRuleRow(DebridStreamPicker.REQUIRED_RESOLUTIONS, "Required resolutions", "Only show selected resolutions.", selectionCountLabel(preferences.requiredResolutions)),
- DebridRuleRow(DebridStreamPicker.EXCLUDED_RESOLUTIONS, "Excluded resolutions", "Hide selected resolutions.", selectionCountLabel(preferences.excludedResolutions)),
- DebridRuleRow(DebridStreamPicker.PREFERRED_QUALITIES, "Preferred qualities", "Sort selected qualities first, in default order.", selectionCountLabel(preferences.preferredQualities)),
- DebridRuleRow(DebridStreamPicker.REQUIRED_QUALITIES, "Required qualities", "Only show selected qualities.", selectionCountLabel(preferences.requiredQualities)),
- DebridRuleRow(DebridStreamPicker.EXCLUDED_QUALITIES, "Excluded qualities", "Hide selected qualities.", selectionCountLabel(preferences.excludedQualities)),
- DebridRuleRow(DebridStreamPicker.PREFERRED_VISUAL_TAGS, "Preferred visual tags", "Sort DV, HDR, 10bit, IMAX and similar tags.", selectionCountLabel(preferences.preferredVisualTags)),
- DebridRuleRow(DebridStreamPicker.REQUIRED_VISUAL_TAGS, "Required visual tags", "Require DV, HDR, 10bit, IMAX, SDR and similar tags.", selectionCountLabel(preferences.requiredVisualTags)),
- DebridRuleRow(DebridStreamPicker.EXCLUDED_VISUAL_TAGS, "Excluded visual tags", "Hide DV, HDR, 10bit, 3D and similar tags.", selectionCountLabel(preferences.excludedVisualTags)),
- DebridRuleRow(DebridStreamPicker.PREFERRED_AUDIO_TAGS, "Preferred audio tags", "Sort Atmos, TrueHD, DTS, AAC and similar tags.", selectionCountLabel(preferences.preferredAudioTags)),
- DebridRuleRow(DebridStreamPicker.REQUIRED_AUDIO_TAGS, "Required audio tags", "Require Atmos, TrueHD, DTS, AAC and similar tags.", selectionCountLabel(preferences.requiredAudioTags)),
- DebridRuleRow(DebridStreamPicker.EXCLUDED_AUDIO_TAGS, "Excluded audio tags", "Hide selected audio tags.", selectionCountLabel(preferences.excludedAudioTags)),
- DebridRuleRow(DebridStreamPicker.PREFERRED_AUDIO_CHANNELS, "Preferred channels", "Sort preferred channel layouts first.", selectionCountLabel(preferences.preferredAudioChannels)),
- DebridRuleRow(DebridStreamPicker.REQUIRED_AUDIO_CHANNELS, "Required channels", "Only show selected channel layouts.", selectionCountLabel(preferences.requiredAudioChannels)),
- DebridRuleRow(DebridStreamPicker.EXCLUDED_AUDIO_CHANNELS, "Excluded channels", "Hide selected channel layouts.", selectionCountLabel(preferences.excludedAudioChannels)),
- DebridRuleRow(DebridStreamPicker.PREFERRED_ENCODES, "Preferred encodes", "Sort AV1, HEVC, AVC and similar encodes.", selectionCountLabel(preferences.preferredEncodes)),
- DebridRuleRow(DebridStreamPicker.REQUIRED_ENCODES, "Required encodes", "Require AV1, HEVC, AVC and similar encodes.", selectionCountLabel(preferences.requiredEncodes)),
- DebridRuleRow(DebridStreamPicker.EXCLUDED_ENCODES, "Excluded encodes", "Hide selected encodes.", selectionCountLabel(preferences.excludedEncodes)),
- DebridRuleRow(DebridStreamPicker.PREFERRED_LANGUAGES, "Preferred languages", "Sort preferred audio languages first.", selectionCountLabel(preferences.preferredLanguages)),
- DebridRuleRow(DebridStreamPicker.REQUIRED_LANGUAGES, "Required languages", "Only show results with selected languages.", selectionCountLabel(preferences.requiredLanguages)),
- DebridRuleRow(DebridStreamPicker.EXCLUDED_LANGUAGES, "Excluded languages", "Hide results where every language is excluded.", selectionCountLabel(preferences.excludedLanguages)),
- DebridRuleRow(DebridStreamPicker.REQUIRED_RELEASE_GROUPS, "Required release groups", "Only show selected release groups.", selectionCountLabel(preferences.requiredReleaseGroups)),
- DebridRuleRow(DebridStreamPicker.EXCLUDED_RELEASE_GROUPS, "Excluded release groups", "Hide selected release groups.", selectionCountLabel(preferences.excludedReleaseGroups)),
+ DebridRuleRow(DebridStreamPicker.PREFERRED_RESOLUTIONS, stringResource(Res.string.settings_debrid_rule_preferred_resolutions), stringResource(Res.string.settings_debrid_rule_preferred_resolutions_desc), selectionCountLabel(preferences.preferredResolutions)),
+ DebridRuleRow(DebridStreamPicker.REQUIRED_RESOLUTIONS, stringResource(Res.string.settings_debrid_rule_required_resolutions), stringResource(Res.string.settings_debrid_rule_required_resolutions_desc), selectionCountLabel(preferences.requiredResolutions)),
+ DebridRuleRow(DebridStreamPicker.EXCLUDED_RESOLUTIONS, stringResource(Res.string.settings_debrid_rule_excluded_resolutions), stringResource(Res.string.settings_debrid_rule_excluded_resolutions_desc), selectionCountLabel(preferences.excludedResolutions)),
+ DebridRuleRow(DebridStreamPicker.PREFERRED_QUALITIES, stringResource(Res.string.settings_debrid_rule_preferred_qualities), stringResource(Res.string.settings_debrid_rule_preferred_qualities_desc), selectionCountLabel(preferences.preferredQualities)),
+ DebridRuleRow(DebridStreamPicker.REQUIRED_QUALITIES, stringResource(Res.string.settings_debrid_rule_required_qualities), stringResource(Res.string.settings_debrid_rule_required_qualities_desc), selectionCountLabel(preferences.requiredQualities)),
+ DebridRuleRow(DebridStreamPicker.EXCLUDED_QUALITIES, stringResource(Res.string.settings_debrid_rule_excluded_qualities), stringResource(Res.string.settings_debrid_rule_excluded_qualities_desc), selectionCountLabel(preferences.excludedQualities)),
+ DebridRuleRow(DebridStreamPicker.PREFERRED_VISUAL_TAGS, stringResource(Res.string.settings_debrid_rule_preferred_visual_tags), stringResource(Res.string.settings_debrid_rule_preferred_visual_tags_desc), selectionCountLabel(preferences.preferredVisualTags)),
+ DebridRuleRow(DebridStreamPicker.REQUIRED_VISUAL_TAGS, stringResource(Res.string.settings_debrid_rule_required_visual_tags), stringResource(Res.string.settings_debrid_rule_required_visual_tags_desc), selectionCountLabel(preferences.requiredVisualTags)),
+ DebridRuleRow(DebridStreamPicker.EXCLUDED_VISUAL_TAGS, stringResource(Res.string.settings_debrid_rule_excluded_visual_tags), stringResource(Res.string.settings_debrid_rule_excluded_visual_tags_desc), selectionCountLabel(preferences.excludedVisualTags)),
+ DebridRuleRow(DebridStreamPicker.PREFERRED_AUDIO_TAGS, stringResource(Res.string.settings_debrid_rule_preferred_audio_tags), stringResource(Res.string.settings_debrid_rule_preferred_audio_tags_desc), selectionCountLabel(preferences.preferredAudioTags)),
+ DebridRuleRow(DebridStreamPicker.REQUIRED_AUDIO_TAGS, stringResource(Res.string.settings_debrid_rule_required_audio_tags), stringResource(Res.string.settings_debrid_rule_required_audio_tags_desc), selectionCountLabel(preferences.requiredAudioTags)),
+ DebridRuleRow(DebridStreamPicker.EXCLUDED_AUDIO_TAGS, stringResource(Res.string.settings_debrid_rule_excluded_audio_tags), stringResource(Res.string.settings_debrid_rule_excluded_audio_tags_desc), selectionCountLabel(preferences.excludedAudioTags)),
+ DebridRuleRow(DebridStreamPicker.PREFERRED_AUDIO_CHANNELS, stringResource(Res.string.settings_debrid_rule_preferred_channels), stringResource(Res.string.settings_debrid_rule_preferred_channels_desc), selectionCountLabel(preferences.preferredAudioChannels)),
+ DebridRuleRow(DebridStreamPicker.REQUIRED_AUDIO_CHANNELS, stringResource(Res.string.settings_debrid_rule_required_channels), stringResource(Res.string.settings_debrid_rule_required_channels_desc), selectionCountLabel(preferences.requiredAudioChannels)),
+ DebridRuleRow(DebridStreamPicker.EXCLUDED_AUDIO_CHANNELS, stringResource(Res.string.settings_debrid_rule_excluded_channels), stringResource(Res.string.settings_debrid_rule_excluded_channels_desc), selectionCountLabel(preferences.excludedAudioChannels)),
+ DebridRuleRow(DebridStreamPicker.PREFERRED_ENCODES, stringResource(Res.string.settings_debrid_rule_preferred_encodes), stringResource(Res.string.settings_debrid_rule_preferred_encodes_desc), selectionCountLabel(preferences.preferredEncodes)),
+ DebridRuleRow(DebridStreamPicker.REQUIRED_ENCODES, stringResource(Res.string.settings_debrid_rule_required_encodes), stringResource(Res.string.settings_debrid_rule_required_encodes_desc), selectionCountLabel(preferences.requiredEncodes)),
+ DebridRuleRow(DebridStreamPicker.EXCLUDED_ENCODES, stringResource(Res.string.settings_debrid_rule_excluded_encodes), stringResource(Res.string.settings_debrid_rule_excluded_encodes_desc), selectionCountLabel(preferences.excludedEncodes)),
+ DebridRuleRow(DebridStreamPicker.PREFERRED_LANGUAGES, stringResource(Res.string.settings_debrid_rule_preferred_languages), stringResource(Res.string.settings_debrid_rule_preferred_languages_desc), selectionCountLabel(preferences.preferredLanguages)),
+ DebridRuleRow(DebridStreamPicker.REQUIRED_LANGUAGES, stringResource(Res.string.settings_debrid_rule_required_languages), stringResource(Res.string.settings_debrid_rule_required_languages_desc), selectionCountLabel(preferences.requiredLanguages)),
+ DebridRuleRow(DebridStreamPicker.EXCLUDED_LANGUAGES, stringResource(Res.string.settings_debrid_rule_excluded_languages), stringResource(Res.string.settings_debrid_rule_excluded_languages_desc), selectionCountLabel(preferences.excludedLanguages)),
+ DebridRuleRow(DebridStreamPicker.REQUIRED_RELEASE_GROUPS, stringResource(Res.string.settings_debrid_rule_required_release_groups), stringResource(Res.string.settings_debrid_rule_required_release_groups_desc), selectionCountLabel(preferences.requiredReleaseGroups)),
+ DebridRuleRow(DebridStreamPicker.EXCLUDED_RELEASE_GROUPS, stringResource(Res.string.settings_debrid_rule_excluded_release_groups), stringResource(Res.string.settings_debrid_rule_excluded_release_groups_desc), selectionCountLabel(preferences.excludedReleaseGroups)),
)
+@Composable
private fun selectionCountLabel(values: List<*>): String =
- if (values.isEmpty()) "Any" else "${values.size} selected"
+ if (values.isEmpty()) {
+ stringResource(Res.string.settings_debrid_selection_any)
+ } else {
+ stringResource(Res.string.settings_debrid_selection_count, values.size)
+ }
+@Composable
private fun sizeRangeLabel(preferences: DebridStreamPreferences): String =
sizeRangeLabel(preferences.sizeMinGb, preferences.sizeMaxGb)
+@Composable
private fun sizeRangeLabel(minGb: Int, maxGb: Int): String =
when {
- minGb <= 0 && maxGb <= 0 -> "Any"
- minGb <= 0 -> "Up to ${maxGb}GB"
- maxGb <= 0 -> "${minGb}GB+"
- else -> "${minGb}-${maxGb}GB"
+ minGb <= 0 && maxGb <= 0 -> stringResource(Res.string.settings_debrid_selection_any)
+ minGb <= 0 -> stringResource(Res.string.settings_debrid_size_up_to, maxGb)
+ maxGb <= 0 -> stringResource(Res.string.settings_debrid_size_min, minGb)
+ else -> stringResource(Res.string.settings_debrid_size_range_value, minGb, maxGb)
}
private fun sortProfileFor(criteria: List): DebridSortProfile {
@@ -1250,9 +1342,6 @@ private fun sortProfileFor(criteria: List): DebridSor
}
}
-private fun sortProfileLabel(criteria: List): String =
- sortProfileLabel(sortProfileFor(criteria))
-
private fun sortCriteriaForProfile(profile: DebridSortProfile): List =
when (profile) {
DebridSortProfile.DEFAULT -> DebridStreamSortCriterion.defaultOrder
diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/PlaybackSettingsPage.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/PlaybackSettingsPage.kt
index aa1af828f..b29ae546a 100644
--- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/PlaybackSettingsPage.kt
+++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/PlaybackSettingsPage.kt
@@ -57,6 +57,7 @@ import com.nuvio.app.features.player.AvailableLanguageOptions
import com.nuvio.app.features.player.ExternalPlayerApp
import com.nuvio.app.features.player.ExternalPlayerPlatform
import com.nuvio.app.features.player.IosHardwareDecoderMode
+import com.nuvio.app.features.player.localizedLabel
import com.nuvio.app.features.player.IosTargetPrimaries
import com.nuvio.app.features.player.IosTargetTransfer
import com.nuvio.app.features.player.PlayerSettingsRepository
@@ -72,6 +73,7 @@ import com.nuvio.app.isIos
import kotlinx.coroutines.launch
import nuvio.composeapp.generated.resources.*
import org.jetbrains.compose.resources.StringResource
+import org.jetbrains.compose.resources.getString
import org.jetbrains.compose.resources.stringResource
import kotlin.math.roundToInt
@@ -501,42 +503,42 @@ private fun PlaybackSettingsSection(
if (isIos) {
SettingsSection(
- title = "iOS video output",
+ title = stringResource(Res.string.settings_playback_ios_video_output),
isTablet = isTablet,
) {
SettingsGroup(isTablet = isTablet) {
SettingsNavigationRow(
- title = "Hardware decoder",
- description = autoPlayPlayerSettings.iosHardwareDecoderMode.label,
+ title = stringResource(Res.string.settings_playback_ios_hardware_decoder),
+ description = autoPlayPlayerSettings.iosHardwareDecoderMode.localizedLabel(),
isTablet = isTablet,
onClick = { showIosHardwareDecoderDialog = true },
)
SettingsGroupDivider(isTablet = isTablet)
SettingsSwitchRow(
- title = "Extended dynamic range",
- description = "Default Metal output mode for new playback sessions.",
+ title = stringResource(Res.string.settings_playback_ios_extended_dynamic_range),
+ description = stringResource(Res.string.settings_playback_ios_extended_dynamic_range_desc),
checked = autoPlayPlayerSettings.iosExtendedDynamicRangeEnabled,
isTablet = isTablet,
onCheckedChange = PlayerSettingsRepository::setIosExtendedDynamicRangeEnabled,
)
SettingsGroupDivider(isTablet = isTablet)
SettingsSwitchRow(
- title = "Display color hint",
- description = "Let mpv target the active display color space by default.",
+ title = stringResource(Res.string.settings_playback_ios_display_color_hint),
+ description = stringResource(Res.string.settings_playback_ios_display_color_hint_desc),
checked = autoPlayPlayerSettings.iosTargetColorspaceHintEnabled,
isTablet = isTablet,
onCheckedChange = PlayerSettingsRepository::setIosTargetColorspaceHintEnabled,
)
SettingsGroupDivider(isTablet = isTablet)
SettingsNavigationRow(
- title = "Target primaries",
+ title = stringResource(Res.string.settings_playback_ios_target_primaries),
description = autoPlayPlayerSettings.iosTargetPrimaries.label,
isTablet = isTablet,
onClick = { showIosTargetPrimariesDialog = true },
)
SettingsGroupDivider(isTablet = isTablet)
SettingsNavigationRow(
- title = "Target transfer",
+ title = stringResource(Res.string.settings_playback_ios_target_transfer),
description = autoPlayPlayerSettings.iosTargetTransfer.label,
isTablet = isTablet,
onClick = { showIosTargetTransferDialog = true },
@@ -924,7 +926,7 @@ private fun PlaybackSettingsSection(
if (showIosHardwareDecoderDialog) {
IosEnumSelectionDialog(
- title = "Hardware decoder",
+ title = stringResource(Res.string.settings_playback_ios_hw_decoder_dialog),
options = IosHardwareDecoderMode.entries,
selected = autoPlayPlayerSettings.iosHardwareDecoderMode,
label = { it.label },
@@ -938,7 +940,7 @@ private fun PlaybackSettingsSection(
if (showIosTargetPrimariesDialog) {
IosEnumSelectionDialog(
- title = "Target primaries",
+ title = stringResource(Res.string.settings_playback_ios_target_primaries_dialog),
options = IosTargetPrimaries.entries,
selected = autoPlayPlayerSettings.iosTargetPrimaries,
label = { it.label },
@@ -952,7 +954,7 @@ private fun PlaybackSettingsSection(
if (showIosTargetTransferDialog) {
IosEnumSelectionDialog(
- title = "Target transfer",
+ title = stringResource(Res.string.settings_playback_ios_target_transfer_dialog),
options = IosTargetTransfer.entries,
selected = autoPlayPlayerSettings.iosTargetTransfer,
label = { it.label },
@@ -2379,7 +2381,7 @@ private fun IntroDbApiKeyDialog(
if (isValid) {
onSave(trimmed)
} else {
- errorMessage = invalidKeyMessage
+ errorMessage = getString(Res.string.settings_playback_introdb_invalid_api_key)
}
}
},