diff --git a/composeApp/build.gradle.kts b/composeApp/build.gradle.kts index 5136bd9c..73a97208 100644 --- a/composeApp/build.gradle.kts +++ b/composeApp/build.gradle.kts @@ -272,7 +272,7 @@ kotlin { afterEvaluate { dependencies { - add("fullImplementation", libs.quickjs.kt) + add("fullImplementation", files("libs/quickjs-kt-android-1.0.5-nuvio.aar")) add("fullImplementation", libs.ksoup) } } diff --git a/composeApp/libs/lib-decoder-iamf-release.aar b/composeApp/libs/lib-decoder-iamf-release.aar deleted file mode 100644 index 741d9e3f..00000000 Binary files a/composeApp/libs/lib-decoder-iamf-release.aar and /dev/null differ diff --git a/composeApp/libs/quickjs-kt-android-1.0.5-nuvio.aar b/composeApp/libs/quickjs-kt-android-1.0.5-nuvio.aar new file mode 100644 index 00000000..ef0ce528 Binary files /dev/null and b/composeApp/libs/quickjs-kt-android-1.0.5-nuvio.aar differ diff --git a/composeApp/src/androidMain/kotlin/com/nuvio/app/features/settings/ThemeSettingsStorage.android.kt b/composeApp/src/androidMain/kotlin/com/nuvio/app/features/settings/ThemeSettingsStorage.android.kt index 32e659c1..8b1506f0 100644 --- a/composeApp/src/androidMain/kotlin/com/nuvio/app/features/settings/ThemeSettingsStorage.android.kt +++ b/composeApp/src/androidMain/kotlin/com/nuvio/app/features/settings/ThemeSettingsStorage.android.kt @@ -18,7 +18,8 @@ actual object ThemeSettingsStorage { private const val selectedThemeKey = "selected_theme" private const val amoledEnabledKey = "amoled_enabled" private const val selectedAppLanguageKey = "selected_app_language" - private val syncKeys = listOf(selectedThemeKey, amoledEnabledKey, selectedAppLanguageKey) + private val profileScopedSyncKeys = listOf(selectedThemeKey, amoledEnabledKey) + private val globalSyncKeys = listOf(selectedAppLanguageKey) private var preferences: SharedPreferences? = null @@ -50,13 +51,18 @@ actual object ThemeSettingsStorage { ?.apply() } - actual fun loadSelectedAppLanguage(): String? = - preferences?.getString(ProfileScopedKey.of(selectedAppLanguageKey), null) + actual fun loadSelectedAppLanguage(): String? { + val value = preferences?.getString(selectedAppLanguageKey, null) + if (value != null) return value + val legacy = preferences?.getString(ProfileScopedKey.of(selectedAppLanguageKey), null) + if (legacy != null) saveSelectedAppLanguage(legacy) + return legacy + } actual fun saveSelectedAppLanguage(languageCode: String) { preferences ?.edit() - ?.putString(ProfileScopedKey.of(selectedAppLanguageKey), languageCode) + ?.putString(selectedAppLanguageKey, languageCode) ?.apply() } @@ -74,7 +80,8 @@ actual object ThemeSettingsStorage { actual fun replaceFromSyncPayload(payload: JsonObject) { preferences?.edit()?.apply { - syncKeys.forEach { remove(ProfileScopedKey.of(it)) } + profileScopedSyncKeys.forEach { remove(ProfileScopedKey.of(it)) } + globalSyncKeys.forEach { remove(it) } }?.apply() payload.decodeSyncString(selectedThemeKey)?.let(::saveSelectedTheme) diff --git a/composeApp/src/androidMain/res/xml/locale_config.xml b/composeApp/src/androidMain/res/xml/locale_config.xml index 7e330475..930f8bda 100644 --- a/composeApp/src/androidMain/res/xml/locale_config.xml +++ b/composeApp/src/androidMain/res/xml/locale_config.xml @@ -1,7 +1,9 @@ + + diff --git a/composeApp/src/commonMain/composeResources/values-el/strings.xml b/composeApp/src/commonMain/composeResources/values-el/strings.xml index a3afc52d..841a751c 100644 --- a/composeApp/src/commonMain/composeResources/values-el/strings.xml +++ b/composeApp/src/commonMain/composeResources/values-el/strings.xml @@ -569,7 +569,7 @@ Ποσοστό Ποσοστό κατωφλίου Εμφάνιση κάρτας επόμενου επεισοδίου όταν η αναπαραγωγή φτάσει σε αυτό το ποσοστό. - %1$d%% + %1$d% Άμεσα %1$ds Απεριόριστο @@ -774,7 +774,7 @@ Σήμανση ως μη παρακολουθηθέν Σήμανση ως παρακολουθηθέν Επόμενο - %1$d%% παρακολουθήθηκε + %1$s παρακολουθήθηκε Εγκαταστήστε και επικυρώστε τουλάχιστον ένα πρόσθετο πριν φορτώσετε γραμμές καταλόγου στην Αρχική. Τα εγκατεστημένα πρόσθετα δεν παρέχουν αυτή τη στιγμή καταλόγους συμβατούς με πίνακα χωρίς απαιτούμενα extras. Δεν υπάρχουν διαθέσιμες γραμμές αρχικής @@ -866,7 +866,7 @@ Δεν υπάρχει διαθέσιμος άμεσος σύνδεσμος ροής Δεν υπάρχουν διαθέσιμα μεταδεδομένα Ανανέωση ροών - Συνέχεια από %1$d%% + Συνέχεια από %1$d% Συνέχεια από %1$s ΜΕΓΕΘΟΣ %1$s Κλείσιμο τρέιλερ @@ -876,7 +876,7 @@ %1$s • %2$s Ο έλεγχος ενημερώσεων απέτυχε Η λήψη απέτυχε - Λήψη %1$d%% + Λήψη %1$d% Αδυναμία εκκίνησης εγκατάστασης Χρησιμοποιείτε την τελευταία έκδοση. Ενεργοποιήστε τις εγκαταστάσεις εφαρμογών για το Nuvio και επιστρέψτε για να συνεχίσετε. diff --git a/composeApp/src/commonMain/composeResources/values-es/strings.xml b/composeApp/src/commonMain/composeResources/values-es/strings.xml index 514e6e3a..6a07039f 100644 --- a/composeApp/src/commonMain/composeResources/values-es/strings.xml +++ b/composeApp/src/commonMain/composeResources/values-es/strings.xml @@ -219,7 +219,7 @@ Colección de películas de TMDB Producción Cadena - Discover de TMDB + Descubrir de TMDB Crea una para organizar tus catálogos. Aún no hay colecciones %1$d carpeta(s) @@ -298,7 +298,7 @@ Avanzar 10 segundos Fuentes Estilo - Subs + Subtítulos Subtítulos Brillo %1$s Volumen %1$s @@ -424,12 +424,12 @@ Nombre visible Instala un complemento con catálogos compatibles con tableros para configurar las filas de la pantalla de inicio. No hay catálogos de inicio - Fuente del Hero + Fuente de Destacado Oculto Mantener Inicio enfocado %1$s • Límite alcanzado (máx. %2$d) - No hay fuentes Hero seleccionadas - No está en Hero + No hay fuentes de Destacado seleccionadas + No está en Destacado Quita fijar arriba de la colección para moverla Fijado Fijado arriba @@ -437,12 +437,12 @@ CATÁLOGOS CATÁLOGOS Y COLECCIONES COLECCIONES - HERO - FUENTES DEL HERO + DESTACADO + FUENTES DE DESTACADO %1$d de %2$d seleccionados - Mostrar Hero - Mostrar un carrusel Hero destacado en la parte superior del inicio. Elige hasta 2 catálogos fuente abajo. - %1$d de %2$d catálogos visibles • %3$d fuentes Hero seleccionadas + Mostrar Destacado + Mostrar un carrusel destacado en la parte superior del inicio. Elige hasta 2 catálogos de origen abajo. + %1$d de %2$d catálogos visibles • %3$d fuentes de Destacado seleccionadas Abre un catálogo solo cuando necesites cambiarle el nombre o reordenarlo. Visible Reproductor, subtítulos y reproducción automática @@ -488,7 +488,7 @@ Instala, elimina, actualiza y ordena tus fuentes de contenido. Instala repositorios de scrapers en JavaScript y prueba proveedores internamente. Controla qué catálogos aparecen en Inicio y en qué orden. - Desactiva secciones de detalles y reordena todo debajo del Hero. + Desactiva secciones de detalles y reordena todo debajo del Destacado. Crea agrupaciones de catálogos personalizadas con carpetas mostradas en Inicio. INTEGRACIONES Mejora las páginas de detalles con arte, créditos, metadatos de episodios y más desde TMDB. @@ -641,7 +641,7 @@ 720p / más pequeño Fuentes WEB Tipo de renderizado - Estándar (Cues) + Estándar (marcas) Canvas con efectos OpenGL con efectos Canvas superpuesto @@ -687,7 +687,7 @@ Porcentaje Porcentaje de umbral Mostrar la tarjeta del siguiente episodio cuando la reproducción alcance este porcentaje. - %1$d%% + %1$d% Instantáneo %1$ds Ilimitado @@ -877,7 +877,7 @@ El catálogo seleccionado no devolvió elementos de descubrimiento. No se pudo cargar Descubrir Los addons instalados no exponen catálogos compatibles con el tablero para Descubrir. - No hay catálogos de descubrir + No hay catálogos de Descubrir El catálogo y los filtros seleccionados no devolvieron ningún elemento. No se encontraron títulos Instala y valida al menos un addon antes de explorar catálogos en Descubrir. @@ -892,7 +892,7 @@ Marcar como no visto Marcar como visto Siguiente - %1$d%% visto + %1$s visto Instala y valida al menos un addon antes de cargar filas de catálogo en Inicio. Los addons instalados no exponen actualmente catálogos compatibles con el tablero sin extras requeridos. No hay filas de inicio disponibles @@ -948,8 +948,8 @@ Gestionar perfiles Nombre del perfil Perfil nuevo - Addons principales desactivados - Addons principales activados + Complementos principales desactivados + Complementos principales activados Quitar PIN para %1$s Quitar bloqueo PIN Guardando... @@ -984,7 +984,7 @@ No hay enlace directo del stream disponible No hay metadatos disponibles Actualizar streams - Reanudar desde %1$d%% + Reanudar desde %1$d% Reanudar desde %1$s TAMAÑO %1$s Cerrar tráiler @@ -994,7 +994,7 @@ %1$s • %2$s Falló la comprobación de actualizaciones La descarga falló - Descargando %1$d%% + Descargando %1$d% No se pudo iniciar la instalación Estás usando la versión más reciente. Activa la instalación de apps para Nuvio y luego vuelve para continuar. @@ -1021,7 +1021,7 @@ logotipo de %1$s No se pudieron cargar los comentarios No se pudieron cargar los detalles desde ningún complemento. - Redes + Cadenas Ningún complemento proporciona metadatos para este contenido. Descarga fallida Muestra el progreso en vivo y los controles de descarga. @@ -1039,7 +1039,7 @@ Vista previa de la alerta de estreno de episodio. No se pudo enviar una notificación de prueba. Notificación de prueba enviada para %1$s. - No se puede reproducir esta transmisión. + No se puede reproducir este stream. El PIN de este perfil cambió. Conéctate una vez para actualizar el bloqueo en este dispositivo. No se pudo quitar el bloqueo por PIN. Inténtalo de nuevo. Conéctate a internet para quitar el bloqueo por PIN. @@ -1107,7 +1107,7 @@ Popular Reciente %1$s • %2$s - Mejor valorado + Mejor valorados Clasificación Detalles de la película Idioma original diff --git a/composeApp/src/commonMain/composeResources/values-fr/strings.xml b/composeApp/src/commonMain/composeResources/values-fr/strings.xml new file mode 100644 index 00000000..58880bec --- /dev/null +++ b/composeApp/src/commonMain/composeResources/values-fr/strings.xml @@ -0,0 +1,1162 @@ + + + Reconnaissance ouverte et crédits du projet + Retour + Annuler + Fermer + Supprimer + Terminé + Modifier + Importer + Suivant + OK + Lire + Précédent + Supprimer + Réorganiser + Réinitialiser + Reprendre + Réessayer + Enregistrer + Installation en cours + Addons + Actif + %1$d catalogues + Configurable + Actualisation + %1$d ressources + Indisponible + Configurer l\'addon + Supprimer l\'addon + Ajoutez une URL de manifeste pour commencer à charger des catalogues, métadonnées, streams ou sous-titres dans Nuvio. + Aucune addons installée. + Veuillez saisir 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 + Échec de l\'installation + %1$s a été validé et ajouté avec succès. + Addon installée + Déplacer l\'addon vers le bas + Déplacer l\'addon vers le haut + Actif + Addons + Catalogues + Actualiser l\'addon + Ajouter une addon + Addons installées + Aperçu + %1$d règles d\'ID + Version %1$s + Sélectionné + Copier le JSON + %1$d collection(s), %2$d dossier(s) + Supprimer "%1$s" ? Cette action est irréversible. + Supprimer la collection + Ajouter un catalogue + Ajouter un dossier + Tous les genres + Ajoutez des catalogues depuis vos extensions installées pour définir ce qu\'affiche ce dossier. + Aucune source de catalogue + Choisir + Emoji + URL de l\'image + Aucune + Couverture + Créer une collection + Terminé + Modifier la collection + Modifier le dossier + Configurez l\'identité, la présentation et les sources de catalogue du dossier avec la même structure que l\'éditeur principal de collections. + Ajoutez-en un pour commencer. + Aucun dossier + Dossiers + Filtre de genre + Afficher uniquement l\'image de couverture + Masquer le titre + Nouveau dossier + Affiche cette collection au-dessus de tous les catalogues normaux de l\'accueil. Plusieurs collections épinglées suivent l\'ordre de création. + Épingler au-dessus des catalogues + URL de l\'image de fond (facultatif) + Nom du dossier + URL du GIF animé (se lit uniquement au focus) + Nom de la collection + Enregistrer les modifications + Enregistrer + Apparence + Informations de base + Sources de catalogue + Choisissez les catalogues d\'extension que ce dossier doit regrouper. + Sélectionner des catalogues + Sélectionner un genre + %1$d sélectionné(s) + %1$d catalogues + %1$d sélectionné(s) + Affiche + Carré + Large + Combiner tous les catalogues en un seul onglet + Afficher l\'onglet « Tout » + Lire le GIF configuré à la place de la couverture statique lorsqu\'il est disponible. + Afficher le GIF si configuré + %1$d source(s) · %2$s + Forme de la tuile + Lignes + Onglets + Mode d\'affichage + Sources TMDB + Liste publique + Production + Chaîne + Collection + Personnalisé + Choisissez une source prédéfinie. Vous pouvez la modifier ou la supprimer après l\'avoir ajoutée. + Collez une URL de liste publique TMDB ou uniquement le numéro de l\'URL. + Recherchez par nom de studio, ou collez un ID/URL de société TMDB et ajoutez-le directement. + Saisissez un ID de chaîne. Les chaînes courantes sont disponibles dans les préréglages et les filtres rapides. + Recherchez le nom d\'une collection de films ou collez l\'ID de collection TMDB. + Créez une ligne TMDB dynamique avec des filtres optionnels. Laissez les champs vides si vous n\'avez pas besoin de ce filtre. + Liste publique TMDB + ID de chaîne + ID de collection + Nom, ID ou URL de société de production + ID ou URL TMDB + https://www.themoviedb.org/list/8504994 ou 8504994 + 213 pour Netflix, 49 pour HBO, 2739 pour Disney+ + 10 pour Star Wars Collection + Marvel Studios, 420 ou URL de société + Exemples : Marvel Studios, 420 ou https://www.themoviedb.org/company/420. + Exemple : Star Wars Collection, Harry Potter Collection ou une URL de collection. + Exemples d\'ID : Netflix 213, HBO 49, Disney+ 2739. + Exemple : https://www.themoviedb.org/list/8504994 ou 8504994. + Titre affiché + Affiché comme nom de ligne/onglet. Si vide, Nuvio en génère un depuis la source. + Films Marvel, Originaux Netflix, Pixar + Meilleurs films d\'action, drames coréens, animation 2024 + Résultats de recherche + Collection TMDB + Société TMDB %1$d + Collection TMDB %1$d + Type + Films + Séries + Les deux + Tri + Filtres + Laissez les champs vides si vous n\'avez pas besoin de ce filtre. + Genres rapides + Langues rapides + Pays rapides + Mots-clés rapides + Studios rapides + Chaînes rapides + ID de genre + Utilisez des numéros de genre TMDB. Séparez plusieurs valeurs par des virgules pour ET, ou des barres verticales pour OU. + Date de sortie ou de diffusion depuis + Date de sortie ou de diffusion jusqu\'au + Utilisez le format AAAA-MM-JJ, ex. 2024-01-01. + Note minimale + Note maximale + Note TMDB de 0 à 10. Exemple : 7.0. + Votes minimum + Utilisez ceci pour éviter les titres peu connus avec peu de votes. Exemple : 100. + Langue originale + Utilisez des codes de langue à deux lettres, ex. en, ko, ja, hi. + Pays d\'origine + Utilisez des codes de pays à deux lettres, ex. US, KR, JP, IN. + ID de mots-clés + Utilisez des numéros de mots-clés TMDB. Les puces rapides remplissent des exemples courants. + 9715 pour super-héros + ID de société + Utilisez des ID de studio/société. Les puces rapides remplissent des exemples courants. + 420 pour Marvel Studios + ID de chaîne + Pour les séries uniquement. Utilisez des ID de chaîne comme Netflix 213 ou HBO 49. + 213 pour Netflix + Année + Utilisez une année à quatre chiffres, ex. 2024. + Préréglages + Rechercher + Ajouter une source + Action + Aventure + Animation + Comédie + Horreur + Science-fiction + Drame + Crime + Téléréalité + Anglais + Coréen + Japonais + Hindi + Espagnol + États-Unis + Corée + Japon + Inde + Royaume-Uni + Super-héros + Adapté d\'un roman + Voyage dans le temps + Espace + Marvel + Disney + Pixar + Lucasfilm + Warner Bros. + Netflix + HBO + Disney+ + Prime Video + Hulu + Populaire + Mieux notés + Récent + Liste TMDB + Collection de films TMDB + Production + Chaîne + Découverte TMDB + Créez-en une pour organiser vos catalogues. + Aucune collection + %1$d dossier(s) + Aucun élément trouvé + Dossier introuvable + Collections + Importer des collections + JSON + Collez le JSON de vos collections ci-dessous. + Importer + Nouvelle collection + Épinglé + Tout + Vos collections + Fait avec ❤️ par Tapframe et ses amis + Version %1$s (%2$s) + Désactivé + Activé + Pause + Recharger + Vous avez déjà un compte ? + Continuer sans compte + Créer un compte + Pas encore de compte ? + Adresse e-mail + ou + Mot de passe + Connectez-vous pour accéder à votre bibliothèque et votre progression + Se connecter + Inscrivez-vous pour synchroniser vos données entre appareils + S\'inscrire + Vos données seront uniquement stockées localement + Regardez tout, partout + Bon retour + Bibliothèque + Bibliothèque Trakt + Accueil + Bibliothèque + Profil + Rechercher + Pistes audio + Audio + Intégré + Décalage inférieur + Fermer le lecteur + Couleur + En cours de lecture + E%1$d + S%1$dE%2$d + S%1$dE%2$d • %3$s + Épisodes + Taille de police + %1$dsp + Verrouiller les contrôles du lecteur + Aucune piste audio disponible + Aucun épisode disponible + Aucun stream trouvé + Aucun + Contour + Épisodes + Sources + Streams + Erreur de lecture + Lecture en cours + Appuyez pour chercher des sous-titres + Retour + Rétablir les valeurs par défaut + Remplir + Ajuster + Zoom + Reculer de 10 secondes + -%1$ds + +%1$ds + -%1$ds + +%1$ds + Avancer de 10 secondes + Sources + Style + Sous-titres + Sous-titres + Luminosité %1$s + Volume %1$s + Muet + Téléchargé + Diffusé + À confirmer + Appuyez pour déverrouiller + Piste %1$d + Déverrouiller les contrôles du lecteur + Vous regardez + Ajouter un profil + Effacer la recherche + Découvrir + Les extensions installées n\'ont retourné aucun résultat de recherche valide. + La recherche a échoué + Installez et validez au moins une extension avant de rechercher. + Aucune extension active + Les catalogues installés n\'ont retourné aucun résultat pour cette requête. + Aucun résultat trouvé + Vos extensions installées n\'exposent pas de catalogue de recherche. + Aucun catalogue de recherche + Rechercher des films, séries… + Recherches récentes + Supprimer la recherche récente + À propos + Général + Compte + Extensions + Apparence + Contenu et découverte + Continuer à regarder + Écran d\'accueil + Intégrations + Notes MDBList + Écran méta + Notifications + Lecture + Plugins + Personnalisation des affiches + Paramètres + Supporters et contributeurs + Enrichissement TMDB + Trakt + À PROPOS + Gérez votre compte, déconnectez-vous ou supprimez-le. + COMPTE + Ajustez 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érez les extensions et sources de découverte. + Gérez vos films et épisodes téléchargés. + Téléchargements + GÉNÉRAL + Connectez les services TMDB et MDBList. + Gérez les alertes de sortie d\'épisodes et envoyez une notification de test. + Basculer vers un profil différent. + Changer de profil + Connectez Trakt, synchronisez des listes et enregistrez des titres directement dans Trakt. + Chargement de vos listes Trakt… + Choisissez où enregistrer ce titre dans Trakt + Faire un don + Voir les détails + Supprimer + Recommencer depuis le début + Lire + %1$d/10 + Avis + Spoiler + Aucun avis Trakt pour l\'instant. + %1$d j\'aime + Ce commentaire contient des spoilers. + Ce commentaire contient des spoilers et a été masqué. + Commentaires + Bande-annonce + %1$s (%2$d) + Bandes-annonces + Aucun épisode terminé + Aucun téléchargement + %1$d épisode(s) téléchargé(s) + Actifs + Films + Séries + Afficher les téléchargements + Terminé • %1$s + Téléchargement • %1$s + Échoué + En pause • %1$s + Vu + Saison %1$d + Spéciaux + Reprendre où vous en étiez + Ajouter à la bibliothèque + Marquer comme non vu + Marquer comme vu + Retirer de la bibliothèque + Tout voir + Lire manuellement + Logo de %1$s + Compte + Supprimer le compte + Cela supprimera définitivement votre compte et toutes les données associées. + Cette action est irréversible. Toutes vos données, profils et historique de synchronisation seront définitivement supprimés. + Supprimer le compte ? + Adresse e-mail + Non connecté + Se déconnecter + Vous serez redirigé vers l\'écran de connexion. + Se déconnecter ? + Statut + Anonyme + Connecté + Noir AMOLED + Utilise des fonds noirs purs pour les écrans OLED. + Langue de l\'application + Choisir la langue + Afficher, masquer et ajuster le bandeau Continuer à regarder. + Ajustez la largeur partagée des cartes d\'affiches et les rayons des coins. + AFFICHAGE + ACCUEIL + THÈME + Collection • %1$s + Nom affiché + Installez une extension 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 + %1$s • Limite atteinte (max. %2$d) + Aucune source Hero sélectionnée + Absent du Hero + Retirez l\'épingle de la collection pour la déplacer + Épinglé + Épinglé en haut + Réorganiser + CATALOGUES + CATALOGUES ET COLLECTIONS + COLLECTIONS + HERO + SOURCES HERO + %1$d sur %2$d sélectionnés + Afficher le Hero + Afficher un carrousel Hero en vedette en haut de l\'accueil. Choisissez jusqu\'à 2 catalogues sources ci-dessous. + %1$d sur %2$d catalogues visibles • %3$d sources Hero sélectionnées + Ouvrez un catalogue uniquement si vous avez besoin de le renommer ou de le réorganiser. + Visible + Lecteur, sous-titres et lecture automatique + Rayon de carte + STYLE DE CARTE D\'AFFICHE + Largeur de carte + Personnalisé + Personnalisez la largeur de carte et le rayon des coins pour les cartes d\'affiches partagées dans toute l\'application. + Masquer les étiquettes + Mode paysage pour les affiches dans les rayons + Aperçu en direct + %1$s (%2$s) + Rayon de coin : %1$ddp + Hauteur : %1$ddp + Largeur : %1$ddp + Classique + Pilule + Arrondi + Marqué + Subtil + Équilibré + Confortable + Compact + Dense + Grand + Standard + Afficher une invite pour reprendre là où vous en étiez à l\'ouverture de l\'application après avoir quitté le lecteur. + Invite de reprise au démarrage + STYLE DE CARTE + AU DÉMARRAGE + COMPORTEMENT DE LA SUITE + VISIBILITÉ + Afficher le bandeau Continuer à regarder sur l\'écran d\'accueil. + Afficher Continuer à regarder + Affiche + Carte d\'affiche centrée sur la couverture + Large + Carte horizontale riche en informations + 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 vous revoyez des épisodes précédents. + La suite depuis l\'épisode le plus avancé + ACCUEIL + SOURCES + Installez, supprimez, mettez à jour et ordonnez vos sources de contenu. + Installez des dépôts de scrapers JavaScript et testez des fournisseurs en interne. + Contrôlez quels catalogues apparaissent à l\'accueil et dans quel ordre. + Désactivez des sections de détails et réorganisez tout sous le Hero. + Créez des regroupements de catalogues personnalisés avec des dossiers affichés à l\'accueil. + INTÉGRATIONS + Enrichissez les pages de détails avec de l\'art, des crédits, des métadonnées d\'épisodes et plus depuis TMDB. + Ajoutez des notes externes d\'IMDb, Rotten Tomatoes, Metacritic et d\'autres aux pages de détails. + Ajoutez votre clé API MDBList ci-dessous avant d\'activer les notes. + Obtenez une clé sur https://mdblist.com/preferences et collez-la ici. + Clé API + Clé API MDBList + Activer les notes MDBList + Afficher les notes externes de MDBList sur les pages de métadonnées lorsqu\'un ID IMDb est disponible. + CLÉ API + FOURNISSEURS DE NOTES + MDBLIST + Actions + Contrôles de lecture et de sauvegarde. + Casting + Liste principale du casting. + Fond cinématographique + Fond flou derrière le contenu, similaire à l\'écran de streams. + Collection + Rayon de collection ou de franchise associée. + Commentaires + Section de commentaires Trakt. + Détails + Durée, statut, sortie, langue et informations associées. + Cartes d\'épisodes + Choisissez 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. + Groupe %1$d + Plus comme ceci + Rayon de recommandations. + Aucun + Résumé + Synopsis, notes, genres et crédits principaux. + Production + Studios et chaînes. + APPARENCE + SECTIONS + Groupe d\'onglets %1$d + Disposition des onglets + Regroupez les sections en onglets comme dans l\'application TV. Assignez jusqu\'à 3 sections par groupe d\'onglets. + Bandes-annonces + Rayon de bandes-annonces et raccourcis de lecture. + Les notifications sont actuellement désactivées dans Nuvio. + 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. Activez-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 + TEST + Envoyer une notification de test + Envoi de la notification de test… + Envoyer une notification locale de test pour %1$s. + Sauvegardez d\'abord une série dans votre bibliothèque pour tester les notifications. + Notification de test + Communauté + Découvrez les personnes qui construisent et soutiennent Nuvio sur Mobile, TV et Web. + L\'API des supporters n\'est pas configurée. Ajoutez DONATIONS_BASE_URL dans local.properties. + Contributeurs + Supporters + Ouvrir GitHub + Profil GitHub indisponible + Aucun message joint. + Chargement des contributeurs… + Chargement des supporters… + Impossible de charger les contributeurs + Impossible de charger les supporters + Aucun contributeur trouvé. + Aucun supporter trouvé. + Impossible de charger les contributeurs. + Impossible de charger les supporters. + Impossible de charger les contributeurs pour le moment. + Impossible de charger les supporters pour le moment. + %1$d commits au total + Jan + Fév + Mar + Avr + Mai + Jun + Jul + Aoû + Sep + Oct + Nov + Déc + %1$s %2$s %3$s + Toutes les extensions + Tous les plugins + Extensions autorisées + Plugins autorisés + Anime Skip + ID client AnimeSkip + Saisissez votre ID client API AnimeSkip. Obtenez-en un sur anime-skip.com. + 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. + Appareil uniquement + Préférer l\'application (FFmpeg) + Préférer l\'appareil + Priorité du décodeur + Appuyez en dehors pour fermer + Appuyez en dehors pour enregistrer et fermer + %1$d jour + %1$d jours + %1$d heure + %1$d heures + Activer libass + Utiliser libass pour afficher les sous-titres ASS/SSA à la place du moteur par défaut. + Vitesse au maintien + Maintenir pour accélérer + Maintenez appuyé n\'importe où sur la surface du lecteur pour augmenter temporairement la vitesse. + Modèle regex invalide + Durée du cache du dernier lien + Mapper DV7 vers HEVC + Utiliser Dolby Vision Profil 7 vers HEVC comme alternative pour les appareils non compatibles. + Minutes avant la fin + Afficher la carte de l\'épisode suivant ce nombre de minutes avant la fin. + %1$d min + Aucun élément disponible + Non défini + Par défaut + Langue de l\'appareil + Forcé + 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. + Langue audio préférée + Langue des sous-titres préférée + Préréglages + Correspond au nom du stream, à l\'étiquette, à la description, à l\'extension et à l\'URL. + Modèle regex + 4K|2160p|Remux + N\'importe quel 1080p+ + AVC / x264 + Qualité BluRay + Dolby Atmos / DTS + Anglais + HDR / Dolby Vision + HEVC / x265 + Sans CAM/TS + Sans REMUX/HDR + 1080p standard + 4K / Remux + 720p / plus petit + Sources WEB + Type de rendu + Standard (Cues) + Canvas avec effets + OpenGL avec effets + Canvas superposé + OpenGL superposé + Réutiliser le dernier lien + Lire automatiquement votre dernier stream fonctionnel pour ce même film/épisode lorsque le cache est encore valide. + Langue audio secondaire + Langue des sous-titres secondaire + DÉCODEUR + ÉPISODE SUIVANT + LECTEUR + PASSER LES SEGMENTS + LECTURE AUTOMATIQUE DES STREAMS + SÉLECTION DU STREAM + SOUS-TITRES ET AUDIO + RENDU DES SOUS-TITRES + %1$d sélectionné(s) + Afficher la superposition de chargement + Afficher la superposition de chargement initiale pendant le démarrage d\'un stream. + Passer l\'intro/outro/récap + Afficher un bouton de saut lors des segments d\'intro, d\'outro et de récapitulatif détectés. + Périmètre des sources + Toutes les extensions + Considérer les streams de toutes les extensions installées. + Toutes les sources + Considérer les streams des extensions et des plugins. + Plugins activés uniquement + Considérer uniquement les streams des plugins activés. + Extensions installées uniquement + Considérer uniquement les streams des extensions installées. + Mode de sélection du stream + Premier stream disponible + Lire automatiquement le premier stream trouvé. + Manuel + Sélectionner les streams manuellement à chaque fois. + Correspondance regex + Sélectionner automatiquement un stream correspondant à un modèle regex. + Délai d\'expiration du stream + Combien de temps attendre les streams avant la sélection automatique. + Minutes avant la fin + Mode de seuil + Minutes avant la fin + Pourcentage + Pourcentage de seuil + Afficher la carte de l\'épisode suivant lorsque la lecture atteint ce pourcentage. + %1$d% + Instantané + %1$ds + Illimité + Lecture tunnelisée + Active la lecture tunnelisée pour une latence réduite dans la synchronisation audio/vidéo. + Ajoutez votre propre clé API TMDB ci-dessous avant d\'activer l\'enrichissement. + Clé API TMDB + Activer l\'enrichissement TMDB + Utiliser votre clé API TMDB pour enrichir les métadonnées de l\'extension sur l\'écran de détails lorsqu\'un ID TMDB ou IMDb est disponible. + Saisissez votre clé API v3 TMDB. + Code de langue + Visuels + Remplacer le fond, l\'affiche et le logo par les visuels TMDB. + Informations de base + Utiliser le titre, le synopsis, les genres et la note de TMDB. + Collections + Afficher des rayons de franchise et de collection pour les films lorsque TMDB les fournit. + Crédits + Utiliser les créateurs, réalisateurs, scénaristes et photos du casting de TMDB. + Détails + 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. + Plus comme ceci + Afficher les recommandations TMDB en bas des pages de détails. + Chaînes + Utiliser les métadonnées des chaînes TMDB pour les titres TV. + Sociétés de production + Utiliser les métadonnées des sociétés de production TMDB sur l\'écran de détails. + Affiches de saison + Utiliser les affiches de saison TMDB dans le sélecteur de saisons de l\'écran de métadonnées pour les séries. + Bandes-annonces + Récupérer et afficher la section des bandes-annonces TMDB sur les pages de détails. + Clé API personnelle + Langue préférée + Configurez le code de langue TMDB utilisé pour les métadonnées localisées, ex. `en`, `en-US` ou `pt-BR`. + IDENTIFIANTS + LOCALISATION + MODULES + TMDB + Après approbation, vous serez redirigé automatiquement. + AUTHENTIFICATION + Commentaires + Afficher les commentaires Trakt dans les détails des films et séries + Connecter Trakt + Connecté en tant que %1$s + Utilisateur Trakt + Déconnecter + Impossible d\'ouvrir le navigateur + FONCTIONNALITÉS + Terminez la connexion Trakt dans votre navigateur + Suivez ce que vous regardez, enregistrez dans votre liste ou vos listes personnalisées et gardez votre bibliothèque synchronisée avec Trakt. + Identifiants Trakt manquants dans local.properties (TRAKT_CLIENT_ID / TRAKT_CLIENT_SECRET). + Ouvrir la connexion Trakt + Vos actions d\'enregistrement peuvent maintenant cibler la watchlist Trakt et vos listes personnelles. + Connectez-vous avec Trakt pour activer la sauvegarde basée sur les listes et le mode bibliothèque Trakt. + Score du public + IMDb + Letterboxd + Metacritic + Rotten Tomatoes + TMDB + Trakt + Inconnu + Ambre + Cramoisi + Émeraude + Océan + Rose + Violet + Blanc + Épisode suivant + Recherche de la source… + Lecture via %1$s dans %2$d… + Miniature de l\'épisode suivant + Non diffusé + Passer + Passer l\'intro + Passer l\'outro + Passer le récap + Aucun sous-titre trouvé + Afrikaans + Albanais + Amharique + Arabe + Arménien + Azerbaïdjanais + Basque + Biélorusse + Bengali + Bosnien + Bulgare + Birman + Catalan + Chinois + Chinois (simplifié) + Chinois (traditionnel) + Croate + Tchèque + Danois + Néerlandais + Anglais + Estonien + Filipino + Finnois + Français + Galicien + Géorgien + Allemand + Grec + Gujarati + Hébreu + Hindi + Hongrois + Islandais + Indonésien + Irlandais + Italien + Japonais + Kannada + Kazakh + Khmer + Coréen + Laotien + Letton + Lituanien + Macédonien + Malais + Malayalam + Maltais + Marathi + Mongol + Népalais + Norvégien + Persan + Polonais + Portugais (Portugal) + Portugais (Brésil) + Pendjabi + Roumain + Russe + Serbe + Cingalais + Slovaque + Slovène + Espagnol + Espagnol (Amérique latine) + Swahili + Suédois + Tamoul + Telugu + Thaï + Turc + Ukrainien + Ourdou + Ouzbek + Vietnamien + Gallois + Zoulou + Effacer + Continuer + Ignorer + Installer + Plus tard + Non + Mettre à jour + Oui + Voulez-vous quitter l\'application ? + Quitter l\'application + Ce catalogue n\'a retourné aucun élément. + Aucun titre trouvé + Vérifiez votre connexion Wi‑Fi ou données mobiles et réessayez. + Réalisateur + Échec du chargement + Plus comme ceci + Saisons + Cette extension a retourné des vidéos pour la série, mais aucune n\'incluait de numéros de saison ou d\'épisode. + Cette extension n\'a fourni aucune métadonnée d\'épisode pour cette série. + Cette extension n\'a pas encore publié d\'épisodes. + Votre appareil est en ligne, mais Nuvio n\'a pas pu se connecter aux serveurs nécessaires. + Afficher moins + Afficher plus ▾ + Scénariste + Tous les genres + Catalogue + %1$s • %2$s + Le catalogue sélectionné n\'a retourné aucun élément de découverte. + Impossible de charger Découvrir + Les extensions installées n\'exposent pas de catalogues compatibles avec le tableau pour Découvrir. + Aucun catalogue de découverte + Le catalogue et les filtres sélectionnés n\'ont retourné aucun élément. + Aucun titre trouvé + Installez et validez au moins une extension avant d\'explorer les catalogues dans Découvrir. + Sélectionner un catalogue + Sélectionner un genre + Sélectionner un type + Type + Marquer les précédents comme non vus + Marquer les précédents comme vus + Marquer %1$s comme non vue + Marquer %1$s comme vue + Marquer comme non vu + Marquer comme vu + Suivant + %1$s vu + Installez et validez au moins une extension avant de charger des lignes de catalogue à l\'accueil. + Les extensions installées 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. + Actions + Liste principale du casting. + Rayon de collection ou de franchise associée. + Collection + Section de commentaires Trakt. + Durée, statut, date de sortie, langue et informations associées. + Détails + Saisons et liste d\'épisodes pour les séries. + Rayon de recommandations. + Plus comme ceci + Synopsis, notes, genres et crédits principaux. + Résumé + Studios et chaînes. + Production + Rayon de bandes-annonces et raccourcis de lecture. + De nouveau en ligne + Impossible d\'atteindre les serveurs + Pas de connexion Internet + (âge %1$d) + Né(e) le %1$s%2$s + Décédé(e) le %1$s + Connu(e) pour : %1$s + Récent + Impossible de charger les détails de %1$s + Populaire + Une erreur est survenue + À venir + Effacer + Annuler + Saisir le code PIN + Saisir le code PIN pour %1$s + Code PIN oublié ? + Code PIN incorrect + Bloqué. Réessayez dans %1$ds + Les options d\'avatar apparaîtront ici une fois le catalogue chargé. + Avatar : %1$s + Choisir un avatar + Choisissez un avatar ci-dessous. + Créer un profil + Toutes les données de "%1$s" seront définitivement supprimées. + Supprimer le profil + Ajouter un profil + Modifier le profil + Saisir le code PIN actuel + Saisir un nouveau code PIN + Profil %1$d + Chargement des avatars… + Gérer les profils + Nom du profil + Nouveau profil + Extensions principales désactivées + Extensions principales activées + Supprimer le code PIN pour %1$s + Supprimer le verrouillage PIN + Enregistrement… + Sécurité + Ajoutez un code PIN si vous souhaitez que ce profil soit verrouillé avant d\'y accéder. + Ce profil est protégé par un code PIN. + Sélectionnez un avatar pour ce profil. + Configurer le verrouillage PIN + Profil sans nom + Utiliser les extensions principales + Partager la configuration des extensions du profil principal plutôt que de gérer une liste séparée. + Qui regarde ? + Téléchargé + Reprendre + Scrapers actifs + Vérification d\'autres extensions… + Copier le lien du stream + Télécharger le fichier + Les extensions de streams installées n\'ont pas retourné de réponse valide. + Impossible de charger les streams + Installez d\'abord une extension pour charger les streams de ce titre. + Vos extensions installées ne fournissent pas de streams pour ce type de titre. + Aucune extension de streams disponible + Aucune de vos extensions installées n\'a retourné de streams pour ce titre. + S%1$d E%2$d + Épisode + S%1$dE%2$d - %3$s + Récupération… + Recherche de la source… + Recherche des streams… + Lien du stream copié + Aucun lien direct du stream disponible + Aucune métadonnée disponible + Actualiser les streams + Reprendre depuis %1$d% + Reprendre depuis %1$s + TAILLE %1$s + Fermer la bande-annonce + Impossible de lire la bande-annonce + Impossible de charger les listes Trakt + Impossible de mettre à jour les listes Trakt + %1$s • %2$s + Échec de la vérification des mises à jour + Échec du téléchargement + Téléchargement %1$d% + Impossible de démarrer l\'installation + Vous utilisez la version la plus récente. + Activez l\'installation d\'applications pour Nuvio puis revenez pour continuer. + Téléchargement de la mise à jour… + Aucune mise à jour trouvée. + Une nouvelle version est prête à être installée. + Les mises à jour intégrées ne sont pas disponibles dans cette version. + Préparation du téléchargement + Notes de version + Autoriser les installations pour continuer + Mise à jour disponible + Statut de la mise à jour + Cette extension est déjà installée. + Veuillez saisir une URL d\'extension valide + Impossible de charger le manifeste + Nuvio + Impossible de supprimer le compte + Échec de la connexion + Échec de la déconnexion + Échec de l\'inscription + Impossible de charger les éléments du catalogue. + À suivre + À suivre • S%1$dE%2$d + logo de %1$s + Impossible de charger les commentaires + Impossible de charger les détails depuis aucune extension. + Réseaux + Aucune extension ne fournit de métadonnées pour ce contenu. + Téléchargement échoué + 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é + En pause %1$s + Supprimer + Supprimer %1$s de votre bibliothèque ? + Retirer de la bibliothèque ? + Film + Alertes lorsqu\'un nouvel épisode d\'une série sauvegardée est disponible. + Aperçu de l\'alerte de sortie d\'épisode. + Impossible d\'envoyer une notification de test. + Notification de test envoyée pour %1$s. + Impossible de lire ce stream. + Le code PIN de ce profil a changé. Connectez-vous une fois pour mettre à jour le verrouillage sur cet appareil. + Impossible de supprimer le verrouillage PIN. Veuillez réessayer. + Connectez-vous à Internet pour supprimer le verrouillage PIN. + Ce code PIN ne peut pas encore être vérifié hors ligne sur cet appareil. Connectez-vous une fois et déverrouillez-le en ligne d\'abord. + Impossible de définir le code PIN. Veuillez réessayer. + Connectez-vous à Internet pour définir un code PIN. + Ce profil utilise les extensions principales. + Impossible de charger %1$s + Source + Intégré + Autorisation refusée + Terminez la connexion Trakt dans votre navigateur + Callback Trakt invalide + État du callback Trakt invalide + Réponse de jeton Trakt invalide + Impossible de charger la bibliothèque Trakt + Liste %1$d + Trakt n\'a pas retourné de code d\'autorisation + Identifiants Trakt manquants + Impossible de charger la progression Trakt + Impossible de terminer la connexion Trakt + Utilisateur Trakt + Liste de suivi + Bande-annonce + Inconnu + Extension + Enregistré + Lire %1$s + Reprendre %1$s + Le JSON est vide. + La collection %1$d a un ID vide. + La collection \'%1$s\' a un titre vide. + Le dossier %1$d dans \'%2$s\' a un ID vide. + Le dossier \'%1$s\' dans \'%2$s\' a un titre vide. + La source %1$d dans le dossier \'%2$s\' a des champs vides. + JSON invalide : %1$s + Extension introuvable : %1$s + Janvier + Février + Mars + Avril + Mai + Juin + Juillet + Août + Septembre + Octobre + Novembre + Décembre + Jan + Fév + Mar + Avr + Mai + Jun + Jul + Aoû + Sep + Oct + Nov + Déc + Société de production + Chaîne + Impossible de charger %1$s + Populaire + Récent + %1$s • %2$s + Mieux noté + Classification + Détails du film + Langue originale + Pays d\'origine + Informations de sortie + Durée + Affiches + Texte + Détails de la série + Statut + Vidéos + FICHIER + Aucun lien direct du stream disponible + Le téléchargement précédent a été remplacé + Téléchargement démarré + Format de stream non pris en charge pour les téléchargements + Corps de réponse vide + La requête a échoué avec HTTP %1$d + Le système de téléchargement n\'est pas initialisé + La requête de téléchargement a échoué + %1$s - %2$s + Les titres enregistrés apparaîtront ici après avoir appuyé sur Enregistrer dans un écran de détails. + Votre bibliothèque est vide + Impossible de charger la bibliothèque + Autre + Bibliothèque + Connectez Trakt et enregistrez des titres dans votre liste de suivi ou vos listes personnelles. + Votre bibliothèque Trakt est vide + Impossible de charger la bibliothèque Trakt + Bibliothèque Trakt + Anime + Chaînes + Films + Séries + TV + %1$s est maintenant disponible + %1$s • %2$s est maintenant disponible + Un nouvel épisode est maintenant disponible + %1$s est maintenant disponible + Sorties d\'épisodes + Créateur + Réalisateur + Scénariste + Score du public + Aucun stream de bande-annonce lisible trouvé. + Saison %1$d - %2$s + o + Ko + Mo + Go + diff --git a/composeApp/src/commonMain/composeResources/values-it/strings.xml b/composeApp/src/commonMain/composeResources/values-it/strings.xml index a2284ea0..1e6ce43e 100644 --- a/composeApp/src/commonMain/composeResources/values-it/strings.xml +++ b/composeApp/src/commonMain/composeResources/values-it/strings.xml @@ -29,11 +29,11 @@ Cancella addon Aggiungi un manifest URL per iniziare a caricare cataloghi , metadata, flussi o sottotitoli dentro Nuvio. Nessun addon installato ancora. - Inserisci l\'URL dell\'addon. + Inserisci l'URL dell'addon. URL Addon Installa Addon Caricamento dettagli manifest... - Validazione dell\'URL del manifest e caricamento dei dettagli dell\'addon prima dell\'installazione. + Validazione dell'URL del manifest e caricamento dei dettagli dell'addon prima dell'installazione. Verifica Addon Installazione Fallita %1$s è stato validato e aggiunto con successo. @@ -52,7 +52,7 @@ Selezionato Copia JSON %1$d collezioni, %2$d cartelle - Eliminare "%1$s"? L\'azione è irreversibile. + Eliminare "%1$s"? L'azione è irreversibile. Elimina Collezione Aggiungi Catalogo Aggiungi Cartella @@ -68,15 +68,15 @@ Fatto Modifica Collezione Modifica Cartella - Imposta l\'identità della cartella, la presentazione e le sorgenti del catalogo con la stessa struttura dell\'editor principale delle collezioni. + Imposta l'identità della cartella, la presentazione e le sorgenti del catalogo con la stessa struttura dell'editor principale delle collezioni. Aggiungine una per iniziare. Ancora nessuna cartella Cartelle Filtro Genere - Mostra solo l\'immagine di copertina + Mostra solo l'immagine di copertina Nascondi Titolo Nuova Cartella - Mostra questa collezione sopra tutti i normali cataloghi della home. In presenza di multiple collezioni fissate si seguirà l\'ordine di creazione. + Mostra questa collezione sopra tutti i normali cataloghi della home. In presenza di multiple collezioni fissate si seguirà l'ordine di creazione. Fissa sopra i cataloghi URL backdrop (opzionale) Nome cartella @@ -229,14 +229,14 @@ Gestisci il tuo account, disconnettiti o eliminalo. ACCOUNT Regola la presentazione della home e le preferenze visive. - Controlla se ci sono nuove versioni dell\'app. + Controlla se ci sono nuove versioni dell'app. Verifica aggiornamenti Gestisci gli addon e le sorgenti di scoperta. Gestisci i film e gli episodi scaricati. Download GENERALI Collega i servizi TMDB e MDBList. - Gestisci gli avvisi per l\'uscita di nuovi episodi e invia una notifica di test. + Gestisci gli avvisi per l'uscita di nuovi episodi e invia una notifica di test. Passa a un profilo diverso. Cambia profilo Collega Trakt, sincronizza la lista dei desideri e salva i titoli direttamente su Trakt. @@ -245,7 +245,7 @@ Dona Vai ai dettagli Rimuovi - Riproduci dall\'inizio + Riproduci dall'inizio Riproduci %1$d/10 Recensione @@ -284,7 +284,7 @@ Elimina account Questo eliminerà permanentemente il tuo account e tutti i dati associati. Questa azione non può essere annullata. Tutti i tuoi dati, profili e la cronologia di sincronizzazione saranno rimossi per sempre. - Eliminare l\'account? + Eliminare l'account? Email Accesso non effettuato Disconnetti @@ -332,7 +332,7 @@ STILE LOCANDINA Larghezza locandina Personalizzato - Personalizza la larghezza e il raggio degli angoli delle locandine in tutta l\'app. + Personalizza la larghezza e il raggio degli angoli delle locandine in tutta l'app. Nascondi etichette Modalità orizzontale per le locandine della riga Anteprima in tempo reale @@ -351,10 +351,10 @@ Denso Grande Standard - Mostra un popup per riprendere la visione all\'apertura dell\'app se eri uscito dal player. - Richiesta ripresa all\'avvio + Mostra un popup per riprendere la visione all'apertura dell'app se eri uscito dal player. + Richiesta ripresa all'avvio STILE SCHEDA - ALL\'AVVIO + ALL'AVVIO COMPORTAMENTO \"PROSSIMO EPISODIO\" VISIBILITÀ Mostra la riga \"Continua a guardare\" nella schermata Home. @@ -363,14 +363,14 @@ Scheda focalizzata sulla locandina Orizzontale Scheda orizzontale ricca di informazioni - Se abilitato, \"Prossimo episodio\" continua sempre dall\'ultimo episodio visto. Se disabilitato, segue l\'episodio visto più di recente. Utile se riguardi spesso episodi precedenti. - Prossimo episodio dall\'ultimo visto + Se abilitato, \"Prossimo episodio\" continua sempre dall'ultimo episodio visto. Se disabilitato, segue l'episodio visto più di recente. Utile se riguardi spesso episodi precedenti. + Prossimo episodio dall'ultimo visto HOME SORGENTI Installa, rimuovi, aggiorna e ordina le tue sorgenti di contenuto. Installa repository di scraper JavaScript e testa i provider internamente. Controlla quali cataloghi appaiono in Home e in quale ordine. - Disabilita le sezioni dei dettagli e riordina tutto ciò che sta sotto l\'elemento Hero. + Disabilita le sezioni dei dettagli e riordina tutto ciò che sta sotto l'elemento Hero. Crea raggruppamenti di cataloghi personalizzati con cartelle mostrate in Home. INTEGRAZIONI Migliora le pagine dei dettagli con immagini, crediti, metadati degli episodi di TMDB e altro ancora. @@ -416,7 +416,7 @@ SEZIONI Gruppo schede %1$d Layout a schede - Raggruppa le sezioni in schede (tab) come nell\'app TV. Assegna fino a 3 sezioni per ogni gruppo. + Raggruppa le sezioni in schede (tab) come nell'app TV. Assegna fino a 3 sezioni per ogni gruppo. Trailer Riga dei trailer e scorciatoie di riproduzione. Le notifiche sono attualmente disabilitate in Nuvio. @@ -433,7 +433,7 @@ Notifica di test Community Scopri le persone che sviluppano e supportano Nuvio su Mobile, TV e Web. - L\'API dei sostenitori non è configurata. Aggiungi DONATIONS_BASE_URL a local.properties. + L'API dei sostenitori non è configurata. Aggiungi DONATIONS_BASE_URL a local.properties. Collaboratori Sostenitori Apri GitHub @@ -472,7 +472,7 @@ Inserisci il tuo ID client API AnimeSkip. Ottienine uno su anime-skip.com. Cerca anche su AnimeSkip i timestamp per saltare le sigle (richiede ID client). Riproduzione automatica prossimo episodio - Trova e riproduce automaticamente l\'episodio successivo al raggiungimento della soglia. + Trova e riproduce automaticamente l'episodio successivo al raggiungimento della soglia. Solo dispositivo Preferisci App (FFmpeg) Preferisci dispositivo @@ -493,7 +493,7 @@ Mappa DV7 su HEVC Fallback da Dolby Vision Profile 7 a HEVC per i dispositivi non supportati. Minuti prima della fine - Mostra la scheda dell\'episodio successivo questo numero di minuti prima della fine. + Mostra la scheda dell'episodio successivo questo numero di minuti prima della fine. %1$d min Nessun elemento disponibile Non impostato @@ -528,8 +528,8 @@ Effetti OpenGL Overlay Canvas Overlay OpenGL - Riusa l\'ultimo link - Riproduci automaticamente l\'ultimo flusso funzionante per lo stesso film/episodio se la cache è ancora valida. + Riusa l'ultimo link + Riproduci automaticamente l'ultimo flusso funzionante per lo stesso film/episodio se la cache è ancora valida. Lingua audio secondaria Lingua sottotitoli secondaria DECODER @@ -542,7 +542,7 @@ RENDERING SOTTOTITOLI %1$d selezionati Mostra overlay di caricamento - Mostra una schermata di caricamento all\'avvio della riproduzione di un flusso. + Mostra una schermata di caricamento all'avvio della riproduzione di un flusso. Salta Intro/Outro/Recap Mostra il pulsante \"salta\" durante i segmenti rilevati di introduzione, chiusura e riassunto. Ambito sorgente @@ -568,14 +568,14 @@ Minuti prima della fine Percentuale Percentuale di soglia - Mostra la scheda dell\'episodio successivo quando la riproduzione raggiunge questa percentuale. - %1$d%% + Mostra la scheda dell'episodio successivo quando la riproduzione raggiunge questa percentuale. + %1$d% Istantaneo %1$ds Illimitato Riproduzione Tunneled Abilita la riproduzione tunneled per una minore latenza nella sincronizzazione audio/video. - Aggiungi la tua chiave API TMDB qui sotto prima di attivare l\'arricchimento. + Aggiungi la tua chiave API TMDB qui sotto prima di attivare l'arricchimento. Chiave API TMDB Abilita arricchimento TMDB Usa la tua chiave API TMDB per arricchire i metadati degli addon nella schermata dei dettagli quando è disponibile un ID TMDB o IMDb. @@ -590,7 +590,7 @@ Crediti Usa creatori, registi, sceneggiatori e foto del cast di TMDB. Dettagli - Usa info su rilascio, durata, classificazione d\'età, stato, paese e lingua di TMDB. + Usa info su rilascio, durata, classificazione d'età, stato, paese e lingua di TMDB. Episodi Usa titoli, miniature, descrizioni e durate degli episodi di TMDB per le serie. Altri titoli simili @@ -610,7 +610,7 @@ LOCALIZZAZIONE MODULI TMDB - Dopo l\'approvazione, verrai reindirizzato automaticamente. + Dopo l'approvazione, verrai reindirizzato automaticamente. AUTENTICAZIONE Commenti Mostra i commenti di Trakt nei dettagli di film e serie TV. @@ -620,7 +620,7 @@ Disconnetti Impossibile aprire il browser FUNZIONALITÀ - Completa l\'accesso a Trakt nel tuo browser + Completa l'accesso a Trakt nel tuo browser Tieni traccia di ciò che guardi, salva contenuti nella watchlist o in liste personalizzate e mantieni la tua libreria sincronizzata con Trakt. Credenziali Trakt mancanti in local.properties (TRAKT_CLIENT_ID / TRAKT_CLIENT_SECRET). Apri Login Trakt @@ -737,8 +737,8 @@ No Aggiorna - Vuoi uscire dall\'app? - Esci dall\'app + Vuoi uscire dall'app? + Esci dall'app Questo catalogo non ha restituito alcun elemento. Nessun titolo trovato Controlla la tua connessione Wi-Fi o dati mobili e riprova. @@ -746,8 +746,8 @@ Caricamento fallito Altri titoli simili Stagioni - L\'addon ha restituito i video per questa serie, ma nessuno include numeri di stagione o episodio. - L\'addon non ha fornito metadati sugli episodi per questa serie. + L'addon ha restituito i video per questa serie, ma nessuno include numeri di stagione o episodio. + L'addon non ha fornito metadati sugli episodi per questa serie. Gli episodi non sono ancora stati pubblicati da questo addon. Il dispositivo è online, ma Nuvio non è riuscito a raggiungere i server richiesti. Mostra meno @@ -774,7 +774,7 @@ Segna come non visto Segna come visto Prossimo episodio - %1$d%% visto + %1$s visto Installa e convalida almeno un addon prima di caricare le righe del catalogo in Home. Gli addon installati non espongono attualmente cataloghi compatibili con la bacheca senza gli extra richiesti. Nessuna riga disponibile in Home @@ -866,7 +866,7 @@ Nessun link diretto disponibile Nessun metadato disponibile Aggiorna flussi - Riprendi dal %1$d%% + Riprendi dal %1$d% Riprendi da %1$s DIMENSIONE %1$s Chiudi trailer @@ -876,13 +876,13 @@ %1$s • %2$s Controllo aggiornamenti fallito Download fallito - Download in corso: %1$d%% - Impossibile avviare l\'installazione - Stai utilizzando l\'ultima versione. - Abilita l\'installazione di app per Nuvio, quindi torna qui e continua. + Download in corso: %1$d% + Impossibile avviare l'installazione + Stai utilizzando l'ultima versione. + Abilita l'installazione di app per Nuvio, quindi torna qui e continua. Download aggiornamento in corso... Nessun aggiornamento trovato. - Una nuova versione è pronta per l\'installazione. + Una nuova versione è pronta per l'installazione. Gli aggiornamenti in-app non sono disponibili in questa versione. Preparazione del download Note di rilascio @@ -918,7 +918,7 @@ Rimuovere dalla libreria? Film Avvisi quando viene rilasciato un nuovo episodio di una serie salvata. - Anteprima dell\'avviso di uscita episodio. + Anteprima dell'avviso di uscita episodio. Impossibile inviare la notifica di test. Notifica di test inviata per %1$s. Impossibile riprodurre questo flusso. @@ -933,7 +933,7 @@ Flusso Incorporato (Embedded) Autorizzazione negata - Completa l\'accesso a Trakt nel tuo browser + Completa l'accesso a Trakt nel tuo browser Callback Trakt non valido Stato callback Trakt non valido Risposta token Trakt non valida @@ -942,7 +942,7 @@ Trakt non ha restituito un codice di autorizzazione Credenziali Trakt mancanti Impossibile caricare i progressi di Trakt - Impossibile completare l\'accesso a Trakt + Impossibile completare l'accesso a Trakt Utente Trakt Watchlist Trailer @@ -953,10 +953,10 @@ Riprendi %1$s Il file JSON è vuoto. La collezione %1$d ha un ID vuoto. - La collezione \'%1$s\' ha un titolo vuoto. - La cartella %1$d in \'%2$s\' ha un ID vuoto. - La cartella \'%1$s\' in \'%2$s\' ha un titolo vuoto. - La sorgente %1$d nella cartella \'%2$s\' presenta campi vuoti. + La collezione '%1$s' ha un titolo vuoto. + La cartella %1$d in '%2$s' ha un ID vuoto. + La cartella '%1$s' in '%2$s' ha un titolo vuoto. + La sorgente %1$d nella cartella '%2$s' presenta campi vuoti. JSON non valido: %1$s Addon non trovato: %1$s Gennaio @@ -993,7 +993,7 @@ Certificazione Dettagli film Lingua originale - Paese d\'origine + Paese d'origine Info rilascio Durata Locandine diff --git a/composeApp/src/commonMain/composeResources/values-pl/strings.xml b/composeApp/src/commonMain/composeResources/values-pl/strings.xml index 81aa7ac8..7200b49f 100644 --- a/composeApp/src/commonMain/composeResources/values-pl/strings.xml +++ b/composeApp/src/commonMain/composeResources/values-pl/strings.xml @@ -687,7 +687,7 @@ Procent Próg procentowy Pokaż kartę następnego odcinka, gdy odtwarzanie osiągnie ten procent. - %1$d%% + %1$d% Natychmiast %1$ds Bez limitu @@ -892,7 +892,7 @@ Oznacz jako nieobejrzane Oznacz jako obejrzane Następny - %1$d%% obejrzane + %1$s obejrzane Zainstaluj i sprawdź co najmniej jeden dodatek przed ładowaniem wierszy katalogów na ekranie głównym. Zainstalowane dodatki nie udostępniają obecnie katalogów kompatybilnych z tablicą bez wymaganych dodatków. Brak dostępnych wierszy ekranu głównego @@ -984,7 +984,7 @@ Brak bezpośredniego linku strumienia Brak dostępnych metadanych Odśwież strumienie - Wznów od %1$d%% + Wznów od %1$d% Wznów od %1$s ROZMIAR %1$s Zamknij zwiastun @@ -994,7 +994,7 @@ %1$s • %2$s Sprawdzanie aktualizacji nie powiodło się Pobieranie nie powiodło się - Pobieranie %1$d%% + Pobieranie %1$d% Nie można rozpocząć instalacji Używasz najnowszej wersji. Zezwól na instalację aplikacji dla Nuvio, a następnie wróć i kontynuuj. @@ -1158,4 +1158,4 @@ KB MB GB - \ No newline at end of file + diff --git a/composeApp/src/commonMain/composeResources/values-pt-rPT/strings.xml b/composeApp/src/commonMain/composeResources/values-pt-rPT/strings.xml new file mode 100644 index 00000000..9cf50f3c --- /dev/null +++ b/composeApp/src/commonMain/composeResources/values-pt-rPT/strings.xml @@ -0,0 +1,1161 @@ + + Reconhecimento aberto e créditos do projeto + Retroceder + Cancelar + Fechar + Eliminar + Concluído + Editar + Importar + Seguinte + OK + Reproduzir + Anterior + Remover + Reordenar + Repor + Retomar + Tentar novamente + Guardar + A instalar + Addons + Ativo + %1$d catálogos + Configurável + A atualizar + %1$d recursos + Indisponível + Configurar addon + Eliminar addon + Adiciona um URL de manifesto para começares a carregar catálogos, metadados, transmissões ou legendas no Nuvio. + Ainda não existem addons instalados. + Introduz um URL de addon. + URL do Addon + Instalar Addon + A carregar detalhes do manifesto... + A validar o URL do manifesto e a carregar os detalhes do addon antes da instalação. + A verificar Addon + Falha na Instalação + %1$s foi validado e adicionado com sucesso. + Addon Instalado + Mover addon para baixo + Mover addon para cima + Ativos + Addons + Catálogos + Atualizar addon + Adicionar Addon + Addons Instalados + Visão Geral + %1$d regras de ID + Versão %1$s + Selecionado + Copiar JSON + %1$d coleção(ões), %2$d pasta(s) + Eliminar \"%1$s\"? Esta ação não pode ser desfeita. + Eliminar Coleção + Adicionar Catálogo + Adicionar Pasta + Todos os géneros + Adiciona catálogos dos teus addons instalados para definires o que esta pasta mostra. + Ainda não existem fontes de catálogo + Escolher + Emoji + URL da Imagem + Nenhum + Capa + Criar Coleção + Concluído + Editar Coleção + Editar Pasta + Define a identidade da pasta, a apresentação e as fontes de catálogo com a mesma estrutura do editor de coleções principal. + Adiciona uma para começar. + Ainda não existem pastas + Pastas + Filtro de Género + Mostrar apenas a imagem de capa + Ocultar Título + Nova Pasta + Mostrar esta coleção acima de todos os catálogos normais do ecrã inicial. Múltiplas coleções afixadas seguem a ordem de criação. + Afixar Acima dos Catálogos + URL da imagem de fundo (opcional) + Nome da pasta + URL do GIF animado (reproduz apenas quando focado) + Nome da coleção + Guardar Alterações + Guardar + Aparência + Básico + Fontes de Catálogo + Escolhe os catálogos de addons que esta pasta deve agregar. + Selecionar Catálogos + Selecionar género + %1$d selecionados + %1$d catálogos + %1$d selecionados + Poster + Quadrado + Panorâmico + Combinar todos os catálogos num único separador + Mostrar Separador \"Tudo\" + Reproduzir o GIF configurado em vez da capa estática, quando disponível. + Mostrar GIF quando Configurado + %1$d fonte(s) · %2$s + Forma do Cartão + Linhas + Separadores + Modo de Visualização + Fontes TMDB + Lista Pública + Produção + Rede/Canal + Coleção + Personalizado + Escolhe uma fonte predefinida. Podes editar ou remover após adicionar. + Cola o URL de uma lista pública do TMDB ou apenas o número presente no URL. + Pesquisa pelo nome do estúdio ou cola o ID/URL de uma empresa no TMDB para adicionar diretamente. + Introduz um ID de rede. As redes comuns estão disponíveis nas Predefinições e filtros rápidos. + Pesquisa pelo nome de uma coleção de filmes ou cola o ID da coleção do TMDB. + Cria uma linha TMDB dinâmica usando filtros opcionais. Deixa os campos vazios quando não precisares de um filtro. + Lista pública TMDB + ID da Rede + ID da Coleção + Nome da produtora, ID ou URL + ID ou URL do TMDB + https://www.themoviedb.org/list/8504994 ou 8504994 + 213 para Netflix, 49 para HBO, 2739 para Disney+ + 10 para Coleção Star Wars + Marvel Studios, 420 ou URL da empresa + Exemplos: Marvel Studios, 420 ou https://www.themoviedb.org/company/420. + Exemplo: Coleção Star Wars, Coleção Harry Potter ou um URL de coleção. + Exemplos de IDs: Netflix 213, HBO 49, Disney+ 2739. + Exemplo: https://www.themoviedb.org/list/8504994 ou 8504994. + Título a exibir + Exibido como o nome da linha/separador. Se estiver em branco, o Nuvio cria um a partir da fonte. + Filmes Marvel, Originais Netflix, Pixar + Melhores Filmes de Ação, Dramas Coreanos, Animação 2024 + Resultados da Pesquisa + Coleção TMDB + Empresa TMDB %1$d + Coleção TMDB %1$d + Tipo + Filmes + Séries + Ambos + Ordenar + Filtros + Deixa os campos vazios quando não precisares de um filtro. + Géneros rápidos + Idiomas rápidos + Países rápidos + Palavras-chave rápidas + Estúdios rápidos + Redes rápidas + IDs de Género + Usa números de género TMDB. Separa múltiplos com vírgulas para AND, ou barras verticais para OR. + Data de lançamento desde + Data de lançamento até + Usa AAAA-MM-DD, por exemplo 2024-01-01. + Avaliação mínima + Avaliação máxima + Avaliação TMDB de 0 a 10. Exemplo: 7.0. + Mínimo de votos + Usa isto para evitar títulos obscuros com poucos votos. Exemplo: 100. + Idioma original + Usa códigos de idioma de duas letras, por exemplo en, ko, ja, hi. + País de origem + Usa códigos de país de duas letras, por exemplo US, KR, JP, IN. + IDs de Palavras-chave + Usa números de palavras-chave TMDB. Os botões rápidos preenchem exemplos comuns. + 9715 para super-herói + IDs de Empresas + Usa IDs de estúdio/empresa. Os botões rápidos preenchem exemplos comuns. + 420 para Marvel Studios + IDs de Redes + Apenas para séries. Usa IDs de rede como Netflix 213 ou HBO 49. + 213 para Netflix + Ano + Usa um ano com quatro dígitos, por exemplo 2024. + Predefinições + Pesquisar + Adicionar Fonte + Ação + Aventura + Animação + Comédia + Terror + Ficção Científica + Drama + Crime + Reality TV + Inglês + Coreano + Japonês + Hindi + Espanhol + Estados Unidos + Coreia + Japão + Índia + Reino Unido + Super-herói + Baseado em Romance/Livro + Viagem no Tempo + Espaço + Marvel + Disney + Pixar + Lucasfilm + Warner Bros. + Netflix + HBO + Disney+ + Prime Video + Hulu + Popular + Melhor Classificados + Recentes + Lista TMDB + Coleção de Filmes TMDB + Produção + Rede/Canal + Descobrir TMDB + Cria uma para organizares os teus catálogos. + Ainda não existem coleções + %1$d pasta(s) + Nenhum item encontrado + Pasta não encontrada + Coleções + Importar Coleções + JSON + Cola o JSON das tuas coleções abaixo. + Importar + Nova Coleção + Afixado + Tudo + As Tuas Coleções + Feito com ❤️ pela Tapframe e amigos + Versão %1$s (%2$s) + Desligado + Ligado + Pausa + Recarregar + Já tens uma conta? + Continuar Sem Conta + Criar Conta + Não tens uma conta? + E-mail +   ou  + Palavra-passe + Inicia sessão para acederes à tua biblioteca e progresso + Iniciar Sessão + Regista-te para sincronizares os teus dados entre dispositivos + Registar + Os teus dados serão guardados apenas localmente + Transmite tudo, em qualquer lugar + Bem-vindo de volta + Biblioteca + Biblioteca Trakt + Início + Biblioteca + Perfil + Pesquisa + Faixas de Áudio + Áudio + Integrado + Ajuste Inferior + Fechar reprodutor + Cor + A reproduzir + E%1$d + S%1$dE%2$d + S%1$dE%2$d • %3$s + Episódios + Tamanho da Letra + %1$dsp + Bloquear controlos + Nenhuma faixa de áudio disponível + Nenhum episódio disponível + Nenhuma transmissão encontrada + Nenhum + Contorno + Episódios + Fontes + Transmissões + Erro de reprodução + A reproduzir + Toca para obter legendas + Voltar + Repor Predefinições + Preencher + Ajustar + Zoom + Recuar 10 segundos + -%1$ds + +%1$ds + -%1$ds + +%1$ds + Avançar 10 segundos + Fontes + Estilo + Leg. + Legendas + Brilho %1$s + Volume %1$s + Sem som + Transferido + Emitido em + A anunciar + Toca para desbloquear + Faixa %1$d + Desbloquear controlos + Estás a ver + Adicionar Perfil + Limpar pesquisa + Descobrir + Os addons instalados falharam ao devolver resultados de pesquisa válidos. + Falha na pesquisa + Instala e valida pelo menos um addon antes de pesquisares. + Sem addons ativos + Os catálogos pesquisáveis instalados não encontraram correspondências para esta consulta. + Nenhum resultado encontrado + Os teus addons instalados não disponibilizam pesquisa de catálogo. + Sem catálogos pesquisáveis + Pesquisa filmes, séries... + Pesquisas Recentes + Remover pesquisa recente + Sobre + Geral + Conta + Addons + Aparência + Conteúdo e Descoberta + Continuar a Ver + Ecrã Inicial + Integrações + Classificações MDBList + Ecrã de Metadados + Notificações + Reprodução + Plugins + Personalização de Posters + Definições + Apoiantes e Colaboradores + Enriquecimento TMDB + Trakt + SOBRE + Gere a tua conta, termina sessão ou elimina-a. + CONTA + Ajusta a apresentação inicial e preferências visuais. + Verifica se existem novas versões da aplicação. + Procurar atualizações + Gere addons e fontes de descoberta. + Gere os teus filmes e episódios transferidos. + Transferências + GERAL + Liga os serviços TMDB e MDBList. + Gere alertas de lançamento de episódios e envia uma notificação de teste. + Mudar para um perfil diferente. + Mudar de Perfil + Liga o Trakt, sincroniza listas e guarda títulos diretamente no Trakt. + A carregar as tuas listas do Trakt… + Escolhe onde guardar este título no Trakt + Doar + Ver detalhes + Remover + Começar do início + Reproduzir + %1$d/10 + Crítica + Spoiler + Ainda não existem críticas do Trakt disponíveis. + %1$d gostos + Este comentário contém spoilers. + Este comentário contém spoilers e foi ocultado. + Comentários + Trailer + %1$s (%2$d) + Trailers + Sem episódios concluídos + Ainda não existem transferências + %1$d episódio(s) transferido(s) + Ativas + Filmes + Séries + Ver Transferências + Concluída • %1$s + A transferir • %1$s + Falhou + Pausada • %1$s + Visto + Temporada %1$d + Especiais + Continua de onde ficaste + Adicionar à biblioteca + Marcar como não visto + Marcar como visto + Remover da biblioteca + Ver Tudo + Reproduzir manualmente + Logótipo %1$s + Conta + Eliminar Conta + Isto eliminará permanentemente a tua conta e todos os dados associados. + Esta ação não pode ser desfeita. Todos os teus dados, perfis e histórico de sincronização serão removidos permanentemente. + Eliminar Conta? + E-mail + Sessão não iniciada + Terminar Sessão + Serás redirecionado para o ecrã de início de sessão. + Terminar Sessão? + Estado + Anónimo + Sessão Iniciada + Preto AMOLED + Utiliza fundos pretos puros para ecrãs OLED. + Idioma da Aplicação + Escolher Idioma + Mostra, oculta e personaliza o aspeto da secção \"Continuar a Ver\". + Ajusta a largura e o arredondamento dos cantos dos posters. + ECRÃ + INÍCIO + TEMA + Coleção • %1$s + Nome de Exibição + Instala um addon com catálogos compatíveis para configurares as linhas do Ecrã Inicial. + Sem catálogos iniciais + Fonte do destaque (Hero) + Oculto + Manter Início focado + %1$s • Limite atingido (máx. %2$d) + Nenhuma fonte de destaque selecionada + Fora do destaque + Remove a afixação no topo da coleção para a moveres + Afixado + Afixado no topo + Reordenar + CATÁLOGOS + CATÁLOGOS E COLEÇÕES + COLEÇÕES + DESTAQUE (HERO) + FONTES DE DESTAQUE + %1$d de %2$d selecionados + Mostrar Destaque + Exibe um carrossel de destaque no topo do Início. Escolhe até 2 catálogos fonte abaixo. + %1$d de %2$d catálogos visíveis • %3$d fontes de destaque selecionadas + Abre um catálogo apenas quando precisares de o renomear ou reordenar. + Visível + Reprodutor, legendas e reprodução automática + Arredondamento do Cartão + ESTILO DO CARTÃO DO POSTER + Largura do Cartão + Personalizado + Personaliza a largura e o arredondamento dos cantos para os cartões de poster em toda a aplicação. + Ocultar etiquetas + Modo panorâmico para posters em prateleira + Pré-visualização em Tempo Real + %1$s (%2$s) + Raio dos cantos: %1$ddp + Altura: %1$ddp + Largura: %1$ddp + Clássico + Pílula + Arredondado + Afiado + Subtil + Equilibrado + Conforto + Compacto + Denso + Grande + Padrão + Mostra um aviso para continuares de onde ficaste ao abrir a aplicação após saíres do reprodutor. + Aviso de retoma ao iniciar + ESTILO DO CARTÃO + AO INICIAR + COMPORTAMENTO DO SEGUINTE + VISIBILIDADE + Exibe a secção \"Continuar a Ver\" no ecrã Inicial. + Mostrar Continuar a Ver + Poster + Cartão focado na imagem + Panorâmico + Cartão horizontal denso em informação + Quando ativado, o \"Seguinte\" continua sempre a partir do último episódio visto. Quando desativado, segue a partir do episódio visto mais recentemente. Útil se costumas rever episódios anteriores. + Seguinte a partir do último episódio + INÍCIO + FONTES + Instala, remove, atualiza e ordena as tuas fontes de conteúdo. + Instala repositórios de scrapers JavaScript e testa fornecedores internamente. + Controla quais os catálogos que aparecem no Início e por que ordem. + Desativa secções de detalhes e reordena tudo abaixo do Destaque. + Cria agrupamentos de catálogos personalizados com pastas exibidas no Início. + INTEGRAÇÕES + Melhora as páginas de detalhes com imagens TMDB, créditos, metadados de episódios e mais. + Adiciona classificações externas do IMDb, Rotten Tomatoes, Metacritic e outros. + Adiciona a tua chave API do MDBList abaixo antes de ativares as classificações. + Obtém uma chave em https://mdblist.com/preferences e cola-a aqui. + Chave API + Chave API MDBList + Ativar classificações MDBList + Mostra classificações externas do MDBList nas páginas de metadados quando o ID do IMDb estiver disponível. + CHAVE API + FORNECEDORES DE CLASSIFICAÇÃO + MDBLIST + Ações + Controlos de reprodução e gravação. + Elenco + Lista do elenco principal. + Fundo Cinemático + Fundo desfocado atrás do conteúdo, semelhante ao ecrã de transmissão. + Coleção + Linha de coleções ou franchises relacionados. + Comentários + Secção de comentários do Trakt. + Detalhes + Duração, estado, lançamento, idioma e informações relacionadas. + Cartões de Episódio + Escolhe como os episódios são apresentados no ecrã de metadados. + Horizontal + Cartões em linha tipo imagem de fundo + Lista + Cartões empilhados focados nos detalhes + Episódios + Lista de temporadas e episódios para séries. + Grupo %1$d + Mais como este + Linha de recomendações. + Nenhum + Resumo + Sinopse, classificações, géneros e créditos principais. + Produção + Estúdios e redes/canais. + APARÊNCIA + SECÇÕES + Grupo de Separadores %1$d + Esquema em Separadores + Agrupa secções em separadores. Atribui até 3 secções por grupo de separadores. + Trailers + Linha de trailers e atalhos de reprodução. + As notificações estão atualmente desativadas no Nuvio. + Alertas de lançamento de episódios + Agenda notificações locais quando um novo episódio de uma série guardada ficar disponível. + As notificações do sistema estão desativadas para o Nuvio. Ativa-as para receberes alertas. + %1$d alertas de lançamento estão agendados neste dispositivo. + ALERTAS + TESTE + Enviar Notificação de Teste + A enviar notificação de teste... + Enviar uma notificação de teste local para %1$s. + Guarda primeiro uma série na tua biblioteca para testares as notificações. + Notificação de teste + Comunidade + Conhece as pessoas que constroem e apoiam o Nuvio em Mobile, TV e Web. + API de Apoiantes não configurada. + Colaboradores + Apoiantes + Abrir GitHub + Perfil de GitHub indisponível + Sem mensagem anexada. + A carregar colaboradores... + A carregar apoiantes... + Não foi possível carregar os colaboradores + Não foi possível carregar os apoiantes + Nenhum colaborador encontrado. + Nenhum apoiante encontrado. + Incapaz de carregar colaboradores. + Incapaz de carregar apoiantes. + Não foi possível carregar os colaboradores agora. + Não foi possível carregar os apoiantes agora. + %1$d commits no total + Jan + Fev + Mar + Abr + Mai + Jun + Jul + Ago + Set + Out + Nov + Dez + %1$s %2$s, %3$s + Todos os Addons + Todos os Plugins + Addons Permitidos + Plugins Permitidos + Anime Skip + ID de Cliente AnimeSkip + Introduz o teu ID de cliente API do AnimeSkip. + Pesquisar também no AnimeSkip por marcas de tempo para saltar (requer ID de cliente). + Reproduzir Episódio Seguinte Automaticamente + Encontra e reproduz automaticamente o episódio seguinte quando o limite é atingido. + Apenas Dispositivo + Preferir Aplicação (FFmpeg) + Preferir Dispositivo + Prioridade do Descodificador + Toca fora para fechar + Toca fora para guardar e fechar + %1$d dia + %1$d dias + %1$d hora + %1$d horas + Ativar libass + Usa o libass para renderização de legendas ASS/SSA em vez do renderizador padrão. + Velocidade ao Premir + Premir para Acelerar + Prime longamente em qualquer parte do reprodutor para aumentar temporariamente a velocidade. + Padrão regex inválido + Duração do Cache do Último Link + Mapear DV7 para HEVC + Alternativa de Dolby Vision Profile 7 para HEVC em dispositivos não suportados. + Minutos Antes do Fim + Mostra o cartão do próximo episódio estes minutos antes do fim. + %1$d min + Nenhum item disponível + Não definido + Predefinição + Idioma do Dispositivo + Forçadas + Nenhum + Preferir Grupo de Maratona + Ao reproduzir automaticamente, prefere uma transmissão do mesmo grupo que a atual. + Idioma de Áudio Preferido + Idioma de Legendas Preferido + Predefinições + Compara com nome da stream, etiqueta, descrição, addon e URL. + Padrão Regex + 4K|2160p|Remux + Qualquer 1080p+ + AVC / x264 + Qualidade BluRay + Dolby Atmos / DTS + Inglês + HDR / Dolby Vision + HEVC / x265 + Sem CAM/TS + Sem REMUX/HDR + 1080p Padrão + 4K / Remux + 720p / Menor + Fontes WEB + Tipo de Renderização + Padrão (Cues) + Canvas de Efeitos + OpenGL de Efeitos + Canvas de Sobreposição + OpenGL de Sobreposição + Reutilizar Último Link + Reproduz automaticamente a última transmissão funcional para este filme/episódio enquanto o cache for válido. + Idioma de Áudio Secundário + Idioma de Legendas Secundário + DESCODIFICADOR + EPISÓDIO SEGUINTE + REPRODUTOR + SALTAR SEGMENTOS + REPRODUÇÃO AUTOMÁTICA + SELEÇÃO DE TRANSMISSÃO + LEGENDAS E ÁUDIO + RENDERIZAÇÃO DE LEGENDAS + %1$d selecionados + Mostrar Sobreposição de Carga + Mostra a animação de carregamento inicial enquanto uma transmissão começa. + Saltar Introdução/Créditos/Resumo + Mostra o botão de saltar durante segmentos detetados de introdução, créditos e resumo. + Âmbito da Fonte + Todos os Addons + Considerar transmissões de todos os addons instalados. + Todas as Fontes + Considerar transmissões tanto de addons como de plugins. + Apenas Plugins Ativados + Considerar apenas transmissões de plugins ativados. + Apenas Addons Instalados + Considerar apenas transmissões de addons instalados. + Modo de Seleção de Transmissão + Primeira Disponível + Reproduz automaticamente a primeira transmissão encontrada. + Manual + Selecionar transmissões manualmente de cada vez. + Correspondência Regex + Auto-seleciona uma stream que corresponda a um padrão regex. + Tempo Limite da Transmissão + Quanto tempo esperar por transmissões antes da auto-seleção. + Minutos Antes do Fim + Modo de Limite + Minutos Antes do Fim + Percentagem + Percentagem de Limite + Mostra o cartão do próximo episódio quando a reprodução atinge esta percentagem. + %1$d% + Instantâneo + %1$ds + Ilimitado + Reprodução Tunelada + Ativa a reprodução tunelada para menor latência na sincronização áudio/vídeo. + Adiciona a tua própria chave API do TMDB abaixo antes de ativares o enriquecimento. + Chave API TMDB + Ativar enriquecimento TMDB + Usa a tua chave API do TMDB para enriquecer metadados no ecrã de detalhes quando um ID TMDB ou IMDb está disponível. + Introduz a tua chave API v3 do TMDB. + Código de idioma + Arte Visual + Substitui fundo, poster e logótipo por arte do TMDB. + Informação básica + Usa título, sinopse, géneros e classificação do TMDB. + Coleções + Mostra linhas de franchises e coleções para filmes quando disponíveis. + Créditos + Usa criadores, realizadores, argumentistas e fotos do elenco do TMDB. + Detalhes + Usa info de lançamento, duração, classificação etária, estado, país e idioma do TMDB. + Episódios + Usa títulos, miniaturas, descrições e durações de episódios do TMDB para séries. + Mais como este + Mostra recomendações do TMDB no fundo das páginas de detalhes. + Redes/Canais + Usa metadados de redes do TMDB para títulos de TV. + Produtoras + Usa metadados de produtoras do TMDB no ecrã de detalhes. + Posters de temporadas + Usa posters de temporadas do TMDB no seletor de temporadas para séries. + Trailers + Procura e exibe a secção de trailers do TMDB nas páginas de detalhes. + Chave API pessoal + Idioma preferido + Define o código de idioma TMDB para metadados localizados (ex: `pt-PT`, `en-US`). + CREDENCIAIS + LOCALIZAÇÃO + MÓDULOS + TMDB + Após aprovação, serás redirecionado de volta automaticamente. + AUTENTICAÇÃO + Comentários + Mostrar comentários do Trakt nos detalhes de filmes e séries + Ligar ao Trakt + Ligado como %1$s + Utilizador Trakt + Desligar + Falha ao abrir o navegador + FUNCIONALIDADES + Conclui o início de sessão do Trakt no teu navegador + Monitoriza o que vês, guarda na lista de interesse ou listas personalizadas e mantém a biblioteca sincronizada. + Faltam credenciais do Trakt em local.properties. + Abrir Login do Trakt + As tuas ações de \"Guardar\" podem agora visar a lista de interesse e listas pessoais do Trakt. + Inicia sessão com o Trakt para ativar gravação baseada em listas e modo de biblioteca Trakt. + Pontuação do Público + IMDb + Letterboxd + Metacritic + Rotten Tomatoes + TMDB + Trakt + Desconhecido + Âmbar + Carmesim + Esmeralda + Oceano + Rosa + Violeta + Branco + Próximo Episódio + A procurar fonte… + A reproduzir via %1$s em %2$d… + Miniatura do próximo episódio + Não emitido + Saltar + Saltar Introdução + Saltar Créditos + Saltar Resumo + Nenhuma legenda encontrada + Africâner + Albanês + Amárico + Árabe + Arménio + Azerbaijano + Basco + Bielorrusso + Bengali + Bósnio + Búlgaro + Birmanês + Catalão + Chinês + Chinês (Simplificado) + Chinês (Tradicional) + Croata + Checo + Dinamarquês + Holandês + Inglês + Estónio + Filipino + Finlandês + Francês + Galego + Georgiano + Alemão + Grego + Gujarati + Hebraico + Hindi + Húngaro + Islandês + Indonésio + Irlandês + Italiano + Japonês + Canarim + Cazaque + Khmer + Coreano + Lao + Letão + Lituano + Macedónio + Malaio + Malaiala + Maltês + Marata + Mongol + Nepalês + Norueguês + Persa + Polaco + Português (Portugal) + Português (Brasil) + Panjabi + Romeno + Russo + Sérvio + Cingalês + Eslovaco + Esloveno + Espanhol + Espanhol (América Latina) + Suaíli + Sueco + Tâmil + Telugo + Tailandês + Turco + Ucraniano + Urdu + Usbeque + Vietnamita + Galês + Zulu + Limpar + Continuar + Ignorar + Instalar + Mais tarde + Não + Atualizar + Sim + Queres sair da aplicação? + Sair da aplicação + Este catálogo não devolveu nenhum item. + Nenhum título encontrado + Verifica a tua ligação Wi-Fi ou dados móveis e tenta novamente. + Realizador + Falha ao carregar + Mais Como Este + Temporadas + Este addon devolveu vídeos, mas nenhum inclui números de temporada ou episódio. + Este addon não forneceu metadados de episódios para esta série. + Os episódios ainda não foram publicados por este addon. + O teu dispositivo está online, mas o Nuvio não conseguiu contactar os servidores necessários. + Mostrar Menos + Mostrar Mais ▾ + Argumentista + Todos os Géneros + Catálogo + %1$s • %2$s + O catálogo selecionado falhou ao devolver itens de descoberta. + Não foi possível carregar descoberta + Os addons instalados não expõem catálogos compatíveis para descoberta. + Sem catálogos de descoberta + O catálogo e filtros selecionados não devolveram nenhuns itens. + Nenhum título encontrado + Instala e valida pelo menos um addon antes de navegar nos catálogos. + Selecionar Catálogo + Selecionar Género + Selecionar Tipo + Tipo + Marcar anteriores como não vistos + Marcar anteriores como vistos + Marcar %1$s como não vista + Marcar %1$s como vista + Marcar como não visto + Marcar como visto + Próximo + %1$s visto + Instala e valida pelo menos um addon antes de carregar o Início. + Os addons instalados não expõem catálogos compatíveis de momento. + Nenhuma linha disponível no Início + Ver Detalhes + Controlos de reprodução e gravação. + Ações + Lista do elenco principal. + Linha de coleção ou franchise relacionada. + Coleção + Secção de comentários do Trakt. + Duração, estado, lançamento, idioma e informações relacionadas. + Detalhes + Lista de temporadas e episódios para séries. + Linha de recomendações. + Mais Como Este + Sinopse, classificações, géneros e créditos principais. + Visão Geral + Estúdios e redes/canais. + Produção + Linha de trailers e atalhos de reprodução. + Novamente online + Não é possível contactar os servidores + Sem ligação à internet + (%1$d anos) + Nascimento: %1$s%2$s + Falecimento: %1$s + Conhecido por: %1$s + Mais recente + Não foi possível carregar os detalhes de %1$s + Popular + Algo correu mal + Próximos lançamentos + Apagar + Cancelar + Introduzir PIN + Introduz o PIN para %1$s + Esqueceste-te do PIN? + PIN incorreto + Bloqueado. Tenta novamente em %1$ds + As opções de avatar aparecerão aqui quando o catálogo carregar. + Avatar: %1$s + Escolhe um avatar + Escolhe um avatar abaixo. + Criar Perfil + Todos os dados de \"%1$s\" serão eliminados permanentemente. + Eliminar Perfil + Adicionar Perfil + Editar Perfil + Introduz o PIN atual + Introduz o novo PIN + Perfil %1$d + A carregar avatares... + Gerir Perfis + Nome do perfil + Novo perfil + Addons primários desativados + Addons primários ativados + Remover PIN de %1$s + Remover Bloqueio por PIN + A guardar... + Segurança + Adiciona um PIN se quiseres bloquear este perfil antes de alternares para ele. + Este perfil está protegido com um PIN. + Seleciona um avatar para este perfil. + Definir Bloqueio por PIN + Perfil sem nome + Usar Addons Primários + Partilha a configuração de addons do perfil principal em vez de gerir uma lista separada. + Quem está a ver? + Descarregado + Retomar + Scrapers ativos + A verificar mais addons… + Copiar link da stream + Descarregar ficheiro + Os addons de transmissão instalados falharam ao devolver uma resposta válida. + Não foi possível carregar as transmissões + Instala um addon primeiro para carregar transmissões para este título. + Os teus addons instalados não fornecem transmissões para este tipo de título. + Nenhum addon de transmissão disponível + Nenhum dos teus addons instalados devolveu transmissões para este título. + T%1$d E%2$d + Episódio + T%1$dE%2$d - %3$s + A obter… + A procurar fonte… + A procurar transmissões… + Link da stream copiado + Nenhum link direto disponível + Nenhuns metadados disponíveis + Atualizar transmissões + Retomar de %1$d% + Retomar de %1$s + TAMANHO %1$s + Fechar trailer + Não é possível reproduzir o trailer + Falha ao carregar listas do Trakt + Falha ao atualizar listas do Trakt + %1$s • %2$s + Falha ao verificar atualizações + Falha no download + A descarregar %1$d% + Não foi possível iniciar a instalação + Estás a usar a versão mais recente. + Ativa a instalação de aplicações para o Nuvio, depois volta e continua. + A descarregar atualização... + Nenhuma atualização encontrada. + Uma nova versão está pronta para instalar. + As atualizações na aplicação não estão disponíveis nesta versão. + A preparar download + Notas de lançamento + Permitir que a instalação continue + Atualização disponível + Estado da atualização + Esse addon já está instalado. + Introduz um URL de addon válido + Não foi possível carregar o manifesto + Nuvio + Falha ao eliminar conta + Falha ao iniciar sessão + Falha ao terminar sessão + Falha ao criar conta + Não foi possível carregar os itens do catálogo. + A Seguir + A Seguir • T%1$dE%2$d + Logótipo de %1$s + Falha ao carregar comentários + Não foi possível carregar detalhes de nenhum addon. + Redes/Canais + Nenhum addon fornece metadados para este conteúdo. + Falha no download + Mostra o progresso e controlos de downloads em direto. + Downloads + Download concluído + A descarregar %1$s • %2$s + A descarregar %1$s • %2$s / %3$s + Falha no download + Em pausa %1$s + Remover + Remover %1$s da tua biblioteca? + Remover da Biblioteca? + Filme + Alertas para quando um novo episódio de uma série guardada é lançado. + Pré-visualização do alerta de lançamento de episódio. + Falha ao enviar uma notificação de teste. + Notificação de teste enviada para %1$s. + Não é possível reproduzir esta transmissão. + O PIN deste perfil mudou. Liga-te uma vez para atualizar o bloqueio neste dispositivo. + Não foi possível remover o bloqueio por PIN. Tenta novamente. + Liga-te à internet para remover o bloqueio por PIN. + Este PIN ainda não pode ser verificado offline neste dispositivo. Liga-te e desbloqueia-o online primeiro. + Não foi possível definir o PIN. Tenta novamente. + Liga-te à internet para definir um PIN. + Este perfil utiliza addons primários. + Falha ao carregar %1$s + Transmissão + Incorporado + Autorização negada + Conclui o início de sessão do Trakt no teu navegador + Callback do Trakt inválido + Estado de callback do Trakt inválido + Resposta de token do Trakt inválida + Falha ao carregar biblioteca do Trakt + Lista %1$d + O Trakt não devolveu um código de autorização + Faltam credenciais do Trakt + Falha ao carregar progresso do Trakt + Falha ao concluir o início de sessão no Trakt + Utilizador Trakt + Lista de interesse + Trailer + Desconhecido + Addon + Guardado + Reproduzir %1$s + Retomar %1$s + O JSON está vazio. + A coleção %1$d tem um ID em branco. + A coleção \'%1$s\' tem um título em branco. + A pasta %1$d em \'%2$s\' tem um ID em branco. + A pasta \'%1$s\' em \'%2$s\' tem um título em branco. + A fonte %1$d na pasta \'%2$s\' tem campos em branco. + JSON inválido: %1$s + Addon não encontrado: %1$s + Janeiro + Fevereiro + Março + Abril + Maio + Junho + Julho + Agosto + Setembro + Outubro + Novembro + Dezembro + Jan + Fev + Mar + Abr + Mai + Jun + Jul + Ago + Set + Out + Nov + Dez + Produtora + Rede/Canal + Não foi possível carregar %1$s + Popular + Recente + %1$s • %2$s + Melhor Classificados + Certificação + Detalhes do Filme + Idioma Original + País de Origem + Info de Lançamento + Duração + Posters + Texto + Detalhes da Série + Estado + Vídeos + Ficheiro + Nenhum link direto disponível + Download anterior substituído + Download iniciado + Formato de transmissão não suportado para downloads + Corpo da resposta vazio + O pedido falhou com HTTP %1$d + O sistema de downloads não foi inicializado + Pedido de download falhou + %1$s - %2$s + Os títulos guardados aparecerão aqui após tocares em \"Guardar\" no ecrã de detalhes. + A tua biblioteca está vazia + Não foi possível carregar a biblioteca + Outro + Biblioteca + Liga o Trakt e guarda títulos na tua lista de interesse ou listas pessoais. + A tua biblioteca Trakt está vazia + Não foi possível carregar a biblioteca Trakt + Biblioteca Trakt + Anime + Canais + Filmes + Séries + TV + %1$s já está disponível + %1$s • %2$s já está disponível + Um novo episódio já está disponível + %1$s já está disponível + Lançamentos de Episódios + Criador + Realizador + Argumentista + Pontuação do Público + Nenhuma transmissão de trailer reproduzível encontrada. + Temporada %1$d - %2$s + B + KB + MB + GB + diff --git a/composeApp/src/commonMain/composeResources/values-tr/strings.xml b/composeApp/src/commonMain/composeResources/values-tr/strings.xml index 9684bfc6..e3d66b12 100644 --- a/composeApp/src/commonMain/composeResources/values-tr/strings.xml +++ b/composeApp/src/commonMain/composeResources/values-tr/strings.xml @@ -569,7 +569,7 @@ Yüzde Eşik yüzdesi Oynatma bu yüzdeye ulaşınca sonraki bölüm kartını göster. - %1$d%% + %1$d% Hemen %1$dsn Sınırsız @@ -774,7 +774,7 @@ İzlenmedi olarak işaretle İzlendi olarak işaretle Sıradaki - %%%1$d izlendi + %1$s izlendi Ana sayfada katalog satırlarını yüklemeden önce en az bir eklenti kurup doğrula. Kurulu eklentiler şu anda gerekli ek bilgiler olmadan ana sayfaya uyumlu katalog sunmuyor. Ana sayfa satırı yok @@ -866,7 +866,7 @@ Doğrudan yayın bağlantısı yok Meta veri yok Yayınları yenile - %%%1$d konumundan devam et + %1$d% konumundan devam et %1$s konumundan devam et BOYUT %1$s Fragmanı kapat @@ -876,7 +876,7 @@ %1$s • %2$s Güncelleme kontrolü olmadı İndirme olmadı - İndiriliyor %%%1$d + İndiriliyor %1$d% Kurulum başlatılamadı En güncel sürümü kullanıyorsun. Nuvio için uygulama kurulumlarına izin ver, sonra geri gelip devam et. diff --git a/composeApp/src/commonMain/composeResources/values/strings.xml b/composeApp/src/commonMain/composeResources/values/strings.xml index 2afb0532..4a014ff0 100644 --- a/composeApp/src/commonMain/composeResources/values/strings.xml +++ b/composeApp/src/commonMain/composeResources/values/strings.xml @@ -110,29 +110,38 @@ Production Network Collection + Person + Director Custom Pick a ready-made source. You can edit or remove it after adding. Paste a public TMDB list URL or only the number from the URL. Search by studio name, or paste a TMDB company ID/URL and add it directly. Enter a network ID. Common networks are available in Presets and quick filters. Search a movie collection name or paste the collection ID from TMDB. + Enter a TMDB person ID or URL to build a row from cast credits. + Enter a TMDB person ID or URL to build a row from director credits. Build a live TMDB row using optional filters. Leave fields empty when you do not need that filter. Public TMDB list Network ID Collection ID + Person ID Production company name, ID, or URL TMDB ID or URL https://www.themoviedb.org/list/8504994 or 8504994 213 for Netflix, 49 for HBO, 2739 for Disney+ 10 for Star Wars Collection Marvel Studios, 420, or company URL + 31 for Tom Hanks, or person URL Examples: Marvel Studios, 420, or https://www.themoviedb.org/company/420. Example: Star Wars Collection, Harry Potter Collection, or a collection URL. Example IDs: Netflix 213, HBO 49, Disney+ 2739. Example: https://www.themoviedb.org/list/8504994 or 8504994. + Example: https://www.themoviedb.org/person/31-tom-hanks or 31. Display title Shown as the row/tab name. If blank, Nuvio creates one from the source. Marvel Movies, Netflix Originals, Pixar + Tom Hanks Movies, Favorite Actors + Christopher Nolan Movies, Favorite Directors Best Action Movies, Korean Dramas, 2024 Animation Search Results TMDB Collection @@ -212,6 +221,7 @@ Disney+ Prime Video Hulu + Original Popular Top Rated Recent @@ -219,6 +229,8 @@ TMDB Movie Collection Production Network + Person + Director TMDB Discover Create one to organize your catalogs. No collections yet @@ -687,7 +699,7 @@ Percentage Threshold Percentage Show next episode card when playback reaches this percentage. - %1$d%% + %1$d% Instant %1$ds Unlimited @@ -892,7 +904,7 @@ Mark as unwatched Mark as watched Up next - %1$d%% watched + %1$s watched Install and validate at least one addon before loading catalog rows on Home. Installed addons do not currently expose board-compatible catalogs without required extras. No home rows available @@ -984,7 +996,7 @@ No direct stream link available No metadata available Refresh streams - Resume from %1$d%% + Resume from %1$d% Resume from %1$s SIZE %1$s Close trailer @@ -994,7 +1006,7 @@ %1$s • %2$s Update check failed Download failed - Downloading %1$d%% + Downloading %1$d% Unable to start installation You're using the latest version. Enable app installs for Nuvio, then come back and continue. diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/collection/CollectionCatalogResolver.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/collection/CollectionCatalogResolver.kt new file mode 100644 index 00000000..cad93b34 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/collection/CollectionCatalogResolver.kt @@ -0,0 +1,43 @@ +package com.nuvio.app.features.collection + +import com.nuvio.app.features.addons.AddonCatalog +import com.nuvio.app.features.addons.ManagedAddon + +internal data class ResolvedCollectionCatalog( + val addon: ManagedAddon, + val catalog: AddonCatalog, +) + +internal fun List.findCollectionCatalog( + source: CollectionCatalogSource, +): ResolvedCollectionCatalog? { + val declaredAddon = firstOrNull { it.manifest?.id == source.addonId } + val declaredCatalog = declaredAddon?.manifest?.catalogs?.findSourceCatalog(source) + if (declaredAddon != null && declaredCatalog != null) { + return ResolvedCollectionCatalog(addon = declaredAddon, catalog = declaredCatalog) + } + + return firstNotNullOfOrNull { addon -> + val catalog = addon.manifest?.catalogs?.find { + it.id == source.catalogId && it.type == source.type + } ?: return@firstNotNullOfOrNull null + ResolvedCollectionCatalog(addon = addon, catalog = catalog) + } +} + +internal fun List.findAvailableCatalog( + source: CollectionCatalogSource, +): AvailableCatalog? { + val declaredCatalogs = filter { it.addonId == source.addonId } + return declaredCatalogs.findSourceCatalog(source) + ?: firstOrNull { it.catalogId == source.catalogId && it.type == source.type } +} + +private fun List.findSourceCatalog(source: CollectionCatalogSource): AddonCatalog? = + find { it.id == source.catalogId && it.type == source.type } + ?: find { it.id == source.catalogId.substringBefore(",") && it.type == source.type } + +private fun List.findSourceCatalog(source: CollectionCatalogSource): AvailableCatalog? = + find { it.catalogId == source.catalogId && it.type == source.type } + ?: find { it.catalogId == source.catalogId.substringBefore(",") && it.type == source.type } + 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 cbb476c8..f7597072 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 @@ -46,6 +46,8 @@ enum class TmdbBuilderMode { PRODUCTION, NETWORK, COLLECTION, + PERSON, + DIRECTOR, DISCOVER, } @@ -340,9 +342,15 @@ object CollectionEditorRepository { } else { _uiState.value.tmdbMediaType } + val sortBy = when (mode) { + TmdbBuilderMode.LIST, + TmdbBuilderMode.COLLECTION -> TmdbCollectionSort.ORIGINAL.value + else -> TmdbCollectionSort.POPULAR_DESC.value + } _uiState.value = _uiState.value.copy( tmdbBuilderMode = mode, tmdbMediaType = mediaType, + tmdbSortBy = sortBy, tmdbMediaBoth = if ( mode == TmdbBuilderMode.NETWORK || mode == TmdbBuilderMode.LIST || @@ -459,6 +467,8 @@ object CollectionEditorRepository { TmdbBuilderMode.COLLECTION -> TmdbCollectionSourceType.COLLECTION TmdbBuilderMode.PRODUCTION -> TmdbCollectionSourceType.COMPANY TmdbBuilderMode.NETWORK -> TmdbCollectionSourceType.NETWORK + TmdbBuilderMode.PERSON -> TmdbCollectionSourceType.PERSON + TmdbBuilderMode.DIRECTOR -> TmdbCollectionSourceType.DIRECTOR TmdbBuilderMode.DISCOVER -> TmdbCollectionSourceType.DISCOVER } val id = TmdbCollectionSourceResolver.parseTmdbId(state.tmdbInput) @@ -473,6 +483,8 @@ object CollectionEditorRepository { 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 -> "TMDB Discover" } } @@ -561,6 +573,8 @@ private val coverMetadataSourceTypes = setOf( TmdbCollectionSourceType.COLLECTION, TmdbCollectionSourceType.COMPANY, TmdbCollectionSourceType.NETWORK, + TmdbCollectionSourceType.PERSON, + TmdbCollectionSourceType.DIRECTOR, ) private fun CollectionCatalogSource.toCollectionSource(): CollectionSource = @@ -591,6 +605,8 @@ private fun selectedMediaTypes( ): List = when (sourceType) { TmdbCollectionSourceType.COMPANY, + TmdbCollectionSourceType.PERSON, + TmdbCollectionSourceType.DIRECTOR, TmdbCollectionSourceType.DISCOVER -> if (state.tmdbMediaBoth) { listOf(TmdbCollectionMediaType.MOVIE, TmdbCollectionMediaType.TV) } else { 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 41ee6532..a47e36ab 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 @@ -111,9 +111,7 @@ fun CollectionEditorScreen( val genrePickerSource = genrePickerIndex?.let { editingFolder.resolvedSources.getOrNull(it) } val genrePickerCatalogSource = genrePickerSource?.addonCatalogSource() val genrePickerCatalog = genrePickerCatalogSource?.let { source -> - state.availableCatalogs.find { - it.addonId == source.addonId && it.type == source.type && it.catalogId == source.catalogId - } + state.availableCatalogs.findAvailableCatalog(source) } FolderEditorPage( @@ -757,11 +755,7 @@ private fun FolderEditorPage( } else if (addonSource != null) { FolderCatalogSourceCard( source = addonSource, - matchingCatalog = state.availableCatalogs.find { - it.addonId == addonSource.addonId && - it.type == addonSource.type && - it.catalogId == addonSource.catalogId - }, + matchingCatalog = state.availableCatalogs.findAvailableCatalog(addonSource), onRemove = { CollectionEditorRepository.removeCatalogSource(index) }, onOpenGenrePicker = { CollectionEditorRepository.showGenrePicker(index) }, ) @@ -897,13 +891,19 @@ private fun TmdbSourcePickerScreen( TmdbBuilderMode.COLLECTION -> TmdbCollectionSourceType.COLLECTION TmdbBuilderMode.PRODUCTION -> TmdbCollectionSourceType.COMPANY TmdbBuilderMode.NETWORK -> TmdbCollectionSourceType.NETWORK + TmdbBuilderMode.PERSON -> TmdbCollectionSourceType.PERSON + TmdbBuilderMode.DIRECTOR -> TmdbCollectionSourceType.DIRECTOR TmdbBuilderMode.DISCOVER -> TmdbCollectionSourceType.DISCOVER } val requiresId = sourceType != TmdbCollectionSourceType.DISCOVER val showMediaControls = state.tmdbBuilderMode == TmdbBuilderMode.PRODUCTION || + state.tmdbBuilderMode == TmdbBuilderMode.PERSON || + state.tmdbBuilderMode == TmdbBuilderMode.DIRECTOR || state.tmdbBuilderMode == TmdbBuilderMode.DISCOVER val showSortControls = state.tmdbBuilderMode == TmdbBuilderMode.PRODUCTION || state.tmdbBuilderMode == TmdbBuilderMode.NETWORK || + state.tmdbBuilderMode == TmdbBuilderMode.PERSON || + state.tmdbBuilderMode == TmdbBuilderMode.DIRECTOR || state.tmdbBuilderMode == TmdbBuilderMode.DISCOVER val showFilterControls = state.tmdbBuilderMode == TmdbBuilderMode.DISCOVER @@ -1892,6 +1892,8 @@ private fun tmdbBuilderModeLabel(mode: TmdbBuilderMode): String = TmdbBuilderMode.PRODUCTION -> stringResource(Res.string.collections_editor_tmdb_production_mode) TmdbBuilderMode.NETWORK -> stringResource(Res.string.collections_editor_tmdb_network_mode) TmdbBuilderMode.COLLECTION -> stringResource(Res.string.collections_editor_tmdb_collection_mode) + TmdbBuilderMode.PERSON -> stringResource(Res.string.collections_editor_tmdb_person_mode) + TmdbBuilderMode.DIRECTOR -> stringResource(Res.string.collections_editor_tmdb_director_mode) TmdbBuilderMode.DISCOVER -> stringResource(Res.string.collections_editor_tmdb_custom_mode) } @@ -1903,6 +1905,8 @@ private fun tmdbModeHelpText(mode: TmdbBuilderMode): String = TmdbBuilderMode.PRODUCTION -> stringResource(Res.string.collections_editor_tmdb_help_production) TmdbBuilderMode.NETWORK -> stringResource(Res.string.collections_editor_tmdb_help_network) TmdbBuilderMode.COLLECTION -> stringResource(Res.string.collections_editor_tmdb_help_collection) + TmdbBuilderMode.PERSON -> stringResource(Res.string.collections_editor_tmdb_help_person) + TmdbBuilderMode.DIRECTOR -> stringResource(Res.string.collections_editor_tmdb_help_director) TmdbBuilderMode.DISCOVER -> stringResource(Res.string.collections_editor_tmdb_help_discover) } @@ -1913,6 +1917,8 @@ private fun tmdbInputLabel(mode: TmdbBuilderMode): String = TmdbBuilderMode.NETWORK -> stringResource(Res.string.collections_editor_tmdb_network_id) TmdbBuilderMode.COLLECTION -> stringResource(Res.string.collections_editor_tmdb_collection_id) TmdbBuilderMode.PRODUCTION -> stringResource(Res.string.collections_editor_tmdb_company_search) + TmdbBuilderMode.PERSON, + TmdbBuilderMode.DIRECTOR -> stringResource(Res.string.collections_editor_tmdb_person_id) else -> stringResource(Res.string.collections_editor_tmdb_id_or_url) } @@ -1923,6 +1929,8 @@ private fun tmdbInputPlaceholder(mode: TmdbBuilderMode): String = TmdbBuilderMode.NETWORK -> stringResource(Res.string.collections_editor_tmdb_network_placeholder) TmdbBuilderMode.COLLECTION -> stringResource(Res.string.collections_editor_tmdb_collection_placeholder) TmdbBuilderMode.PRODUCTION -> stringResource(Res.string.collections_editor_tmdb_company_placeholder) + TmdbBuilderMode.PERSON, + TmdbBuilderMode.DIRECTOR -> stringResource(Res.string.collections_editor_tmdb_person_placeholder) else -> stringResource(Res.string.collections_editor_tmdb_id_or_url) } @@ -1933,6 +1941,8 @@ private fun tmdbInputHelper(mode: TmdbBuilderMode): String = TmdbBuilderMode.COLLECTION -> stringResource(Res.string.collections_editor_tmdb_collection_helper) TmdbBuilderMode.NETWORK -> stringResource(Res.string.collections_editor_tmdb_network_helper) TmdbBuilderMode.LIST -> stringResource(Res.string.collections_editor_tmdb_list_helper) + TmdbBuilderMode.PERSON, + TmdbBuilderMode.DIRECTOR -> stringResource(Res.string.collections_editor_tmdb_person_helper) else -> "" } @@ -1940,12 +1950,15 @@ private fun tmdbInputHelper(mode: TmdbBuilderMode): String = private fun tmdbTitlePlaceholder(mode: TmdbBuilderMode): String = when (mode) { TmdbBuilderMode.DISCOVER -> stringResource(Res.string.collections_editor_tmdb_discover_title_placeholder) + TmdbBuilderMode.PERSON -> stringResource(Res.string.collections_editor_tmdb_person_title_placeholder) + TmdbBuilderMode.DIRECTOR -> stringResource(Res.string.collections_editor_tmdb_director_title_placeholder) else -> stringResource(Res.string.collections_editor_tmdb_title_placeholder) } @Composable private fun tmdbSortLabel(sort: TmdbCollectionSort): String = when (sort) { + TmdbCollectionSort.ORIGINAL -> stringResource(Res.string.collections_editor_tmdb_sort_original) TmdbCollectionSort.POPULAR_DESC -> stringResource(Res.string.collections_editor_tmdb_sort_popular) TmdbCollectionSort.VOTE_AVERAGE_DESC -> stringResource(Res.string.collections_editor_tmdb_sort_top_rated) TmdbCollectionSort.RELEASE_DATE_DESC -> stringResource(Res.string.collections_editor_tmdb_sort_recent) @@ -1979,6 +1992,16 @@ private fun tmdbSourceSubtitle(source: CollectionSource): String { stringResource(Res.string.collections_editor_tmdb_series), sort, ).joinToString(" • ") + TmdbCollectionSourceType.PERSON -> listOf( + stringResource(Res.string.collections_editor_tmdb_subtitle_person), + media, + sort, + ).joinToString(" • ") + TmdbCollectionSourceType.DIRECTOR -> listOf( + stringResource(Res.string.collections_editor_tmdb_subtitle_director), + media, + sort, + ).joinToString(" • ") TmdbCollectionSourceType.DISCOVER -> listOf( stringResource(Res.string.collections_editor_tmdb_subtitle_discover), media, diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/collection/CollectionModels.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/collection/CollectionModels.kt index 5d17161b..f0780ad2 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/collection/CollectionModels.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/collection/CollectionModels.kt @@ -69,6 +69,8 @@ enum class TmdbCollectionSourceType { COMPANY, NETWORK, DISCOVER, + PERSON, + DIRECTOR, } @Serializable @@ -86,6 +88,7 @@ enum class TmdbCollectionMediaType(val value: String) { } enum class TmdbCollectionSort(val value: String) { + ORIGINAL("original"), POPULAR_DESC("popularity.desc"), VOTE_AVERAGE_DESC("vote_average.desc"), RELEASE_DATE_DESC("primary_release_date.desc"), @@ -133,6 +136,7 @@ data class CollectionFolder( val sources: List = emptyList(), val catalogSources: List = emptyList(), val heroBackdropUrl: String? = null, + val heroVideoUrl: String? = null, val titleLogoUrl: String? = null, ) { val posterShape: PosterShape diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/collection/CollectionRepository.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/collection/CollectionRepository.kt index 9d57d011..0e9553ae 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/collection/CollectionRepository.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/collection/CollectionRepository.kt @@ -4,7 +4,10 @@ import co.touchlab.kermit.Logger import com.nuvio.app.features.addons.AddonRepository import com.nuvio.app.features.addons.ManagedAddon import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.runBlocking import kotlinx.serialization.decodeFromString @@ -33,6 +36,8 @@ object CollectionRepository { private val _collections = MutableStateFlow>(emptyList()) val collections: StateFlow> = _collections.asStateFlow() + private val _localChangeEvents = MutableSharedFlow(extraBufferCapacity = 1) + internal val localChangeEvents: SharedFlow = _localChangeEvents.asSharedFlow() private var rawCollectionsJson: JsonElement = JsonArray(emptyList()) private var hasLoaded = false @@ -244,16 +249,19 @@ object CollectionRepository { internal fun applyFromRemote(collections: List, rawJson: JsonElement) { rawCollectionsJson = rawJson _collections.value = collections - persist() + persist(sync = false) } private fun ensureLoaded() { if (!hasLoaded) initialize() } - private fun persist() { + private fun persist(sync: Boolean = true) { runCatching { CollectionStorage.savePayload(mergedCollectionsJson().toString()) + if (sync) { + _localChangeEvents.tryEmit(Unit) + } }.onFailure { e -> log.e(e) { "Failed to persist collections" } } diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/collection/CollectionSyncService.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/collection/CollectionSyncService.kt index 1ec14547..de0931ec 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/collection/CollectionSyncService.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/collection/CollectionSyncService.kt @@ -15,8 +15,6 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.delay import kotlinx.coroutines.flow.debounce -import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.drop import kotlinx.coroutines.launch import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonArray @@ -125,9 +123,7 @@ object CollectionSyncService { @OptIn(FlowPreview::class) private fun observeLocalChangesAndPush() { observeJob = scope.launch { - CollectionRepository.collections - .drop(1) - .distinctUntilChanged() + CollectionRepository.localChangeEvents .debounce(PUSH_DEBOUNCE_MS) .collect { if (isSyncingFromRemote) return@collect diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/collection/FolderDetailRepository.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/collection/FolderDetailRepository.kt index e853eeba..36698b25 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/collection/FolderDetailRepository.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/collection/FolderDetailRepository.kt @@ -140,16 +140,19 @@ object FolderDetailRepository { source = source, type = type, catalogId = tmdbCatalogId(source), - supportsPagination = source.tmdbSourceType != TmdbCollectionSourceType.COLLECTION.name, + supportsPagination = source.tmdbSourceType !in setOf( + TmdbCollectionSourceType.COLLECTION.name, + TmdbCollectionSourceType.PERSON.name, + TmdbCollectionSourceType.DIRECTOR.name, + ), isLoading = true, ), ) } else { val catalogSource = source.addonCatalogSource() ?: return@forEach - val addon = addons.find { it.manifest?.id == catalogSource.addonId } - val catalog = addon?.manifest?.catalogs?.find { - it.id == catalogSource.catalogId && it.type == catalogSource.type - } + val resolvedCatalog = addons.findCollectionCatalog(catalogSource) + val addon = resolvedCatalog?.addon + val catalog = resolvedCatalog?.catalog val label = catalog?.name ?: catalogSource.catalogId val typeLabel = localizedMediaTypeLabel(catalogSource.type) val genreSuffix = if (catalogSource.genre != null) " · ${catalogSource.genre}" else "" @@ -184,8 +187,8 @@ object FolderDetailRepository { sources.forEachIndexed { sourceIndex, source -> val tabIndex = if (showAll) sourceIndex + 1 else sourceIndex val catalogSource = source.addonCatalogSource() - val addon = catalogSource?.let { value -> addons.find { it.manifest?.id == value.addonId } } - if (!source.isTmdb && addon == null) { + val resolvedCatalog = catalogSource?.let { addons.findCollectionCatalog(it) } + if (!source.isTmdb && resolvedCatalog == null) { updateTab(tabIndex) { it.copy( isLoading = false, 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 ee25fa48..3f37d3d8 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 @@ -29,6 +29,8 @@ object TmdbCollectionSourceResolver { when (sourceType) { TmdbCollectionSourceType.LIST -> resolveList(source, apiKey, language, page) TmdbCollectionSourceType.COLLECTION -> resolveCollection(source, apiKey, language) + TmdbCollectionSourceType.PERSON, + TmdbCollectionSourceType.DIRECTOR -> resolvePersonCredits(source, apiKey, language) TmdbCollectionSourceType.COMPANY, TmdbCollectionSourceType.NETWORK, TmdbCollectionSourceType.DISCOVER -> resolveDiscover(source, apiKey, language, page) @@ -85,6 +87,19 @@ object TmdbCollectionSourceResolver { ) } + TmdbCollectionSourceType.PERSON, + TmdbCollectionSourceType.DIRECTOR -> { + val body = fetch( + endpoint = "person/$id", + apiKey = apiKey, + query = mapOf("language" to language), + ) ?: error("TMDB person not found") + TmdbSourceImportMetadata( + title = body.name?.takeIf { it.isNotBlank() }, + coverImageUrl = imageUrl(body.profilePath, "w500"), + ) + } + TmdbCollectionSourceType.DISCOVER -> TmdbSourceImportMetadata(title = "TMDB Discover") } } @@ -153,7 +168,7 @@ object TmdbCollectionSourceResolver { fun parseTmdbId(input: String): Int? { val trimmed = input.trim() trimmed.toIntOrNull()?.let { return it } - return Regex("""(?:list|collection|company|network)/(\d+)""") + return Regex("""(?:list|collection|company|network|person)/(\d+)""") .find(trimmed) ?.groupValues ?.getOrNull(1) @@ -193,6 +208,7 @@ object TmdbCollectionSourceResolver { ) ?: error("TMDB list not found") val items = body.items.orEmpty() .mapNotNull { it.toPreview() } + .sortedFor(source.sortBy) .distinctBy { "${it.type}:${it.id}" } return CatalogPage( items = items, @@ -213,12 +229,35 @@ object TmdbCollectionSourceResolver { query = mapOf("language" to language), ) ?: error("TMDB collection not found") val items = body.parts.orEmpty() - .sortedBy { it.releaseDate ?: "9999" } .mapNotNull { it.toPreview(TmdbCollectionMediaType.MOVIE) } + .sortedFor(source.sortBy) .distinctBy { it.id } return CatalogPage(items = items, rawItemCount = items.size, nextSkip = null) } + private suspend fun resolvePersonCredits( + source: CollectionSource, + apiKey: String, + language: String, + ): CatalogPage { + val id = source.tmdbId ?: error("Missing TMDB person ID") + val mediaType = source.tmdbMediaType() + val body = fetch( + endpoint = "person/$id/combined_credits", + apiKey = apiKey, + query = mapOf("language" to language), + ) ?: error("TMDB person credits not found") + val items = when (source.tmdbType()) { + TmdbCollectionSourceType.DIRECTOR -> body.crew.orEmpty() + .filter { it.job.equals("Director", ignoreCase = true) } + .mapNotNull { it.toPreview(mediaType) } + else -> body.cast.orEmpty().mapNotNull { it.toPreview(mediaType) } + } + .distinctBy { "${it.type}:${it.id}" } + .sortedFor(source.sortBy) + return CatalogPage(items = items, rawItemCount = items.size, nextSkip = null) + } + private suspend fun resolveDiscover( source: CollectionSource, apiKey: String, @@ -312,6 +351,21 @@ object TmdbCollectionSourceResolver { }.getOrNull() } + private fun List.sortedFor(sortBy: String?): List = + when (sortBy) { + TmdbCollectionSort.ORIGINAL.value -> this + TmdbCollectionSort.VOTE_AVERAGE_DESC.value -> sortedWith( + compareByDescending { it.imdbRating?.toDoubleOrNull() ?: -1.0 } + .thenByDescending { it.rawReleaseDate ?: it.releaseInfo.orEmpty() }, + ) + TmdbCollectionSort.RELEASE_DATE_DESC.value, + TmdbCollectionSort.FIRST_AIR_DATE_DESC.value -> sortedByDescending { it.rawReleaseDate ?: it.releaseInfo.orEmpty() } + TmdbCollectionSort.POPULAR_DESC.value, + null, + "" -> this + else -> this + } + private fun TmdbListItem.toPreview(): MetaPreview? { val media = mediaType?.lowercase() val contentType = if (media == "tv") TmdbCollectionMediaType.TV else TmdbCollectionMediaType.MOVIE @@ -362,6 +416,62 @@ object TmdbCollectionSourceResolver { ) } + private fun TmdbPersonCreditCast.toPreview(mediaType: TmdbCollectionMediaType): MetaPreview? { + if (!matchesMediaType(mediaType, this.mediaType)) return null + val title = title?.takeIf { it.isNotBlank() } + ?: name?.takeIf { it.isNotBlank() } + ?: originalTitle?.takeIf { it.isNotBlank() } + ?: originalName?.takeIf { it.isNotBlank() } + ?: return null + return MetaPreview( + id = "tmdb:$id", + type = if (mediaType == TmdbCollectionMediaType.TV) "series" else "movie", + name = title, + poster = imageUrl(posterPath, "w500") ?: imageUrl(backdropPath, "w780"), + banner = imageUrl(backdropPath, "w1280"), + posterShape = PosterShape.Poster, + description = overview?.takeIf { it.isNotBlank() }, + releaseInfo = when (mediaType) { + TmdbCollectionMediaType.MOVIE -> releaseDate?.take(4) + TmdbCollectionMediaType.TV -> firstAirDate?.take(4) + }, + rawReleaseDate = when (mediaType) { + TmdbCollectionMediaType.MOVIE -> releaseDate + TmdbCollectionMediaType.TV -> firstAirDate + }, + popularity = popularity, + imdbRating = voteAverage?.let { ((it * 10).roundToInt() / 10.0).toString() }, + ) + } + + private fun TmdbPersonCreditCrew.toPreview(mediaType: TmdbCollectionMediaType): MetaPreview? { + if (!matchesMediaType(mediaType, this.mediaType)) return null + val title = title?.takeIf { it.isNotBlank() } + ?: name?.takeIf { it.isNotBlank() } + ?: originalTitle?.takeIf { it.isNotBlank() } + ?: originalName?.takeIf { it.isNotBlank() } + ?: return null + return MetaPreview( + id = "tmdb:$id", + type = if (mediaType == TmdbCollectionMediaType.TV) "series" else "movie", + name = title, + poster = imageUrl(posterPath, "w500") ?: imageUrl(backdropPath, "w780"), + banner = imageUrl(backdropPath, "w1280"), + posterShape = PosterShape.Poster, + description = overview?.takeIf { it.isNotBlank() }, + releaseInfo = when (mediaType) { + TmdbCollectionMediaType.MOVIE -> releaseDate?.take(4) + TmdbCollectionMediaType.TV -> firstAirDate?.take(4) + }, + rawReleaseDate = when (mediaType) { + TmdbCollectionMediaType.MOVIE -> releaseDate + TmdbCollectionMediaType.TV -> firstAirDate + }, + popularity = popularity, + imdbRating = voteAverage?.let { ((it * 10).roundToInt() / 10.0).toString() }, + ) + } + private fun CollectionSource.tmdbType(): TmdbCollectionSourceType = tmdbSourceType ?.let { raw -> runCatching { TmdbCollectionSourceType.valueOf(raw.uppercase()) }.getOrNull() } @@ -370,6 +480,12 @@ object TmdbCollectionSourceResolver { private fun CollectionSource.tmdbMediaType(): TmdbCollectionMediaType = TmdbCollectionMediaType.fromString(mediaType) + private fun matchesMediaType(expected: TmdbCollectionMediaType, actual: String?): Boolean = + when (expected) { + TmdbCollectionMediaType.MOVIE -> actual == "movie" + TmdbCollectionMediaType.TV -> actual == "tv" + } + private fun company(title: String, id: Int) = CollectionSource( provider = "tmdb", tmdbSourceType = TmdbCollectionSourceType.COMPANY.name, @@ -391,6 +507,7 @@ object TmdbCollectionSourceResolver { private fun movieSort(sortBy: String?): String = when (sortBy) { TmdbCollectionSort.FIRST_AIR_DATE_DESC.value -> TmdbCollectionSort.RELEASE_DATE_DESC.value + TmdbCollectionSort.ORIGINAL.value -> TmdbCollectionSort.POPULAR_DESC.value null, "" -> TmdbCollectionSort.POPULAR_DESC.value else -> sortBy } @@ -398,6 +515,7 @@ object TmdbCollectionSourceResolver { private fun tvSort(sortBy: String?): String = when (sortBy) { TmdbCollectionSort.RELEASE_DATE_DESC.value -> TmdbCollectionSort.FIRST_AIR_DATE_DESC.value + TmdbCollectionSort.ORIGINAL.value -> TmdbCollectionSort.POPULAR_DESC.value null, "" -> TmdbCollectionSort.POPULAR_DESC.value else -> sortBy } @@ -449,6 +567,12 @@ private data class TmdbNetworkResponse( @SerialName("logo_path") val logoPath: String? = null, ) +@Serializable +private data class TmdbPersonResponse( + val name: String? = null, + @SerialName("profile_path") val profilePath: String? = null, +) + @Serializable data class TmdbCompanySearchResult( val id: Int, @@ -496,6 +620,47 @@ private data class TmdbGenreItem( val name: String, ) +@Serializable +private data class TmdbPersonCreditsResponse( + val cast: List? = null, + val crew: List? = null, +) + +@Serializable +private data class TmdbPersonCreditCast( + val id: Int, + @SerialName("media_type") val mediaType: String? = null, + val title: String? = null, + val name: String? = null, + @SerialName("original_title") val originalTitle: String? = null, + @SerialName("original_name") val originalName: String? = null, + val overview: String? = null, + @SerialName("poster_path") val posterPath: String? = null, + @SerialName("backdrop_path") val backdropPath: String? = null, + @SerialName("release_date") val releaseDate: String? = null, + @SerialName("first_air_date") val firstAirDate: String? = null, + @SerialName("vote_average") val voteAverage: Double? = null, + val popularity: Double? = null, +) + +@Serializable +private data class TmdbPersonCreditCrew( + val id: Int, + @SerialName("media_type") val mediaType: String? = null, + val title: String? = null, + val name: String? = null, + @SerialName("original_title") val originalTitle: String? = null, + @SerialName("original_name") val originalName: String? = null, + val overview: String? = null, + @SerialName("poster_path") val posterPath: String? = null, + @SerialName("backdrop_path") val backdropPath: String? = null, + @SerialName("release_date") val releaseDate: String? = null, + @SerialName("first_air_date") val firstAirDate: String? = null, + val job: String? = null, + @SerialName("vote_average") val voteAverage: Double? = null, + val popularity: Double? = null, +) + @Serializable private data class TmdbListItem( val id: Int, diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/home/components/HomeContinueWatchingSection.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/home/components/HomeContinueWatchingSection.kt index 1fd74b47..b84db29a 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/home/components/HomeContinueWatchingSection.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/home/components/HomeContinueWatchingSection.kt @@ -365,7 +365,7 @@ private fun ContinueWatchingWideCard( Text( text = stringResource( Res.string.home_continue_watching_watched, - continueWatchingProgressPercent(item.progressFraction), + "${continueWatchingProgressPercent(item.progressFraction)}%", ), style = MaterialTheme.typography.labelSmall.copy( fontSize = layout.progressLabelSize, diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerScreen.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerScreen.kt index e862fc29..c3c5dd75 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerScreen.kt @@ -657,7 +657,6 @@ fun PlayerScreen( } } playerController?.seekTo(targetPositionMs) - controlsVisible = true showSeekFeedback(direction, nextState.amountMs) accumulatedSeekResetJob?.cancel() diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/profiles/ProfileRepository.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/profiles/ProfileRepository.kt index b6637490..07a9d9c6 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/profiles/ProfileRepository.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/profiles/ProfileRepository.kt @@ -70,6 +70,7 @@ object ProfileRepository { val stored = decodeStoredPayload() ?: return false loadedCacheForUserId = stored.userId applyStoredPayload(stored) + ThemeSettingsRepository.onProfileChanged() return _state.value.profiles.isNotEmpty() } diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/AppLanguage.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/AppLanguage.kt index a8e8c904..a5062582 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/AppLanguage.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/AppLanguage.kt @@ -2,7 +2,9 @@ package com.nuvio.app.features.settings import nuvio.composeapp.generated.resources.Res import nuvio.composeapp.generated.resources.lang_english +import nuvio.composeapp.generated.resources.lang_french import nuvio.composeapp.generated.resources.lang_spanish +import nuvio.composeapp.generated.resources.lang_portuguese_portugal import nuvio.composeapp.generated.resources.lang_turkish import nuvio.composeapp.generated.resources.lang_italian import nuvio.composeapp.generated.resources.lang_greek @@ -14,6 +16,7 @@ enum class AppLanguage( val labelRes: StringResource, ) { ENGLISH("en", Res.string.lang_english), + FRENCH("fr", Res.string.lang_french), SPANISH("es", Res.string.lang_spanish), TURKISH("tr", Res.string.lang_turkish), ITALIAN("it", Res.string.lang_italian), diff --git a/composeApp/src/iosMain/kotlin/com/nuvio/app/features/settings/ThemeSettingsStorage.ios.kt b/composeApp/src/iosMain/kotlin/com/nuvio/app/features/settings/ThemeSettingsStorage.ios.kt index cf91655b..f71eaaea 100644 --- a/composeApp/src/iosMain/kotlin/com/nuvio/app/features/settings/ThemeSettingsStorage.ios.kt +++ b/composeApp/src/iosMain/kotlin/com/nuvio/app/features/settings/ThemeSettingsStorage.ios.kt @@ -14,7 +14,8 @@ actual object ThemeSettingsStorage { private const val selectedThemeKey = "selected_theme" private const val amoledEnabledKey = "amoled_enabled" private const val selectedAppLanguageKey = "selected_app_language" - private val syncKeys = listOf(selectedThemeKey, amoledEnabledKey, selectedAppLanguageKey) + private val profileScopedSyncKeys = listOf(selectedThemeKey, amoledEnabledKey) + private val globalSyncKeys = listOf(selectedAppLanguageKey) actual fun loadSelectedTheme(): String? = NSUserDefaults.standardUserDefaults.stringForKey(ProfileScopedKey.of(selectedThemeKey)) @@ -37,11 +38,16 @@ actual object ThemeSettingsStorage { NSUserDefaults.standardUserDefaults.setBool(enabled, forKey = ProfileScopedKey.of(amoledEnabledKey)) } - actual fun loadSelectedAppLanguage(): String? = - NSUserDefaults.standardUserDefaults.stringForKey(ProfileScopedKey.of(selectedAppLanguageKey)) + actual fun loadSelectedAppLanguage(): String? { + val value = NSUserDefaults.standardUserDefaults.stringForKey(selectedAppLanguageKey) + if (value != null) return value + val legacy = NSUserDefaults.standardUserDefaults.stringForKey(ProfileScopedKey.of(selectedAppLanguageKey)) + if (legacy != null) saveSelectedAppLanguage(legacy) + return legacy + } actual fun saveSelectedAppLanguage(languageCode: String) { - NSUserDefaults.standardUserDefaults.setObject(languageCode, forKey = ProfileScopedKey.of(selectedAppLanguageKey)) + NSUserDefaults.standardUserDefaults.setObject(languageCode, forKey = selectedAppLanguageKey) } actual fun applySelectedAppLanguage(languageCode: String) = Unit @@ -53,9 +59,12 @@ actual object ThemeSettingsStorage { } actual fun replaceFromSyncPayload(payload: JsonObject) { - syncKeys.forEach { key -> + profileScopedSyncKeys.forEach { key -> NSUserDefaults.standardUserDefaults.removeObjectForKey(ProfileScopedKey.of(key)) } + globalSyncKeys.forEach { key -> + NSUserDefaults.standardUserDefaults.removeObjectForKey(key) + } payload.decodeSyncString(selectedThemeKey)?.let(::saveSelectedTheme) payload.decodeSyncBoolean(amoledEnabledKey)?.let(::saveAmoledEnabled) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d5929dc6..ae480b9f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -22,7 +22,7 @@ ktor = "3.4.1" material3 = "1.11.0-alpha07" androidx-media3 = "1.8.0" supabase = "3.4.1" -quickjsKt = "1.0.1" +quickjsKt = "1.0.5" ksoup = "0.2.6" reorderable = "3.0.0" desugarJdkLibs = "2.1.5" diff --git a/iosApp/Configuration/Version.xcconfig b/iosApp/Configuration/Version.xcconfig index 9f8d477b..d2dd1f20 100644 --- a/iosApp/Configuration/Version.xcconfig +++ b/iosApp/Configuration/Version.xcconfig @@ -1,3 +1,3 @@ -CURRENT_PROJECT_VERSION=40 -MARKETING_VERSION=0.1.9 +CURRENT_PROJECT_VERSION=42 +MARKETING_VERSION=0.1.10 diff --git a/mediamp b/mediamp deleted file mode 160000 index df33966d..00000000 --- a/mediamp +++ /dev/null @@ -1 +0,0 @@ -Subproject commit df33966d7fbc6eb14e43fb1892e062417d76e7f5 diff --git a/vendor/mpv-kt-upstream b/vendor/mpv-kt-upstream deleted file mode 160000 index 8a8ddddf..00000000 --- a/vendor/mpv-kt-upstream +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 8a8ddddf430555878273da13006fc57e182b0c0c