mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-08-09 00:17:59 +00:00
Merge branch 'cmp-rewrite' of https://github.com/khanh71/NuvioMobile into cmp-rewrite
This commit is contained in:
commit
fbc8e9534c
40 changed files with 1162 additions and 363 deletions
|
|
@ -48,6 +48,7 @@ import com.nuvio.app.features.trakt.TraktLibraryStorage
|
|||
import com.nuvio.app.features.trakt.TraktSettingsStorage
|
||||
import com.nuvio.app.features.tmdb.TmdbSettingsStorage
|
||||
import com.nuvio.app.features.updater.AndroidAppUpdaterPlatform
|
||||
import com.nuvio.app.core.ui.CardDepthStyleStorage
|
||||
import com.nuvio.app.core.ui.PosterCardStyleStorage
|
||||
import com.nuvio.app.features.watched.WatchedStorage
|
||||
import com.nuvio.app.features.streams.StreamLinkCacheStorage
|
||||
|
|
@ -90,6 +91,7 @@ class MainActivity : AppCompatActivity() {
|
|||
SearchHistoryStorage.initialize(applicationContext)
|
||||
SeasonViewModeStorage.initialize(applicationContext)
|
||||
PosterCardStyleStorage.initialize(applicationContext)
|
||||
CardDepthStyleStorage.initialize(applicationContext)
|
||||
DebridSettingsStorage.initialize(applicationContext)
|
||||
TmdbSettingsStorage.initialize(applicationContext)
|
||||
MdbListSettingsStorage.initialize(applicationContext)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
package com.nuvio.app.core.ui
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import com.nuvio.app.core.storage.ProfileScopedKey
|
||||
|
||||
internal actual object CardDepthStyleStorage {
|
||||
private const val preferencesName = "nuvio_card_depth_style"
|
||||
private const val payloadKey = "card_depth_style_payload"
|
||||
|
||||
private var preferences: SharedPreferences? = null
|
||||
|
||||
fun initialize(context: Context) {
|
||||
preferences = context.getSharedPreferences(preferencesName, Context.MODE_PRIVATE)
|
||||
}
|
||||
|
||||
actual fun loadPayload(): String? =
|
||||
preferences?.getString(ProfileScopedKey.of(payloadKey), null)
|
||||
|
||||
actual fun savePayload(payload: String) {
|
||||
preferences
|
||||
?.edit()
|
||||
?.putString(ProfileScopedKey.of(payloadKey), payload)
|
||||
?.apply()
|
||||
}
|
||||
}
|
||||
|
|
@ -433,7 +433,9 @@
|
|||
<string name="compose_settings_root_check_updates_description">Изтегли последна версия</string>
|
||||
<string name="compose_settings_root_check_updates_title">Провери за актуализации</string>
|
||||
<string name="compose_settings_root_content_discovery_description">Управлявай добавки и източници за discovery.</string>
|
||||
<string name="compose_settings_root_content_discovery_description_appstore">Управлявайте добавките и изберете какво да се показва в Nuvio.</string>
|
||||
<string name="compose_settings_root_downloads_description">Управлявай изтеглените филми и епизоди.</string>
|
||||
<string name="compose_settings_root_downloads_description_appstore">Управлявайте запазеното на това устройство.</string>
|
||||
<string name="compose_settings_root_downloads_title">Изтегляния</string>
|
||||
<string name="compose_settings_root_general_section">ОБЩИ</string>
|
||||
<string name="compose_settings_root_integrations_description">Управлявай наличните интеграции</string>
|
||||
|
|
@ -1845,11 +1847,13 @@
|
|||
<string name="player_error_torrent">Грешка в торента: %1$s</string>
|
||||
<!-- Current cmp-rewrite string additions -->
|
||||
<string name="action_switch">Превключи</string>
|
||||
<string name="addons_appstore_add_description">Свържете собствен медиен сървър, за да разглеждате и възпроизвеждате от личната си библиотека.</string>
|
||||
<string name="addons_appstore_empty_subtitle">Добавете URL на сървър по-горе, когато искате Nuvio да показва вашата лична библиотека.</string>
|
||||
<string name="addons_appstore_empty_title">Няма свързани лични библиотеки.</string>
|
||||
<string name="addons_appstore_input_placeholder">URL на сървър</string>
|
||||
<string name="addons_appstore_install_button">Добави</string>
|
||||
<string name="addons_appstore_add_description">Пренесете библиотеката си в Nuvio с добавките, които използвате.</string>
|
||||
<string name="addons_appstore_empty_subtitle">Добавете добавка, за да започнете да изграждате библиотеката си.</string>
|
||||
<string name="addons_appstore_empty_title">Настройте Nuvio по свой начин.</string>
|
||||
<string name="addons_appstore_input_placeholder">URL на добавка</string>
|
||||
<string name="addons_appstore_install_button">Добавяне на добавка</string>
|
||||
<string name="store_empty_unavailable_title">Все още няма нищо тук</string>
|
||||
<string name="store_empty_unavailable_message">Текущите ви добавки все още нямат какво да покажат тук.</string>
|
||||
<string name="community_donation_progress_title">Сървър и поддръжка за този месец</string>
|
||||
<string name="community_donation_progress_complete">Покрито. Допълнителната подкрепа вече отива за разработка.</string>
|
||||
<string name="community_donation_progress_remaining">След 100% допълнителната подкрепа отива за разработка.</string>
|
||||
|
|
@ -1861,7 +1865,7 @@
|
|||
<string name="details_actions_menu_label">Още действия</string>
|
||||
<string name="p2p_error_unknown">Неизвестна грешка в торента</string>
|
||||
<string name="settings_appearance_app_language_device">Език на устройството</string>
|
||||
<string name="settings_content_discovery_addons_description_appstore">Свържете лични медийни източници и управлявайте достъпа до собствената си библиотека.</string>
|
||||
<string name="settings_content_discovery_addons_description_appstore">Добавяйте и управлявайте добавките, които използвате с Nuvio.</string>
|
||||
<string name="settings_playback_external_player_send_skip_segments">Изпращане на времеви маркери за интро и аутро</string>
|
||||
<string name="settings_playback_external_player_send_skip_segments_description">Предайте откритите времеви маркери за интро и аутро към външния плейър за автоматично прескачане. Работи само в плейъри, които го поддържат; останалите плейъри го игнорират.</string>
|
||||
<string name="settings_playback_touch_gestures">Жестове с докосване</string>
|
||||
|
|
|
|||
|
|
@ -32,11 +32,13 @@
|
|||
<string name="addons_badge_unavailable">Nedostupné</string>
|
||||
<string name="addons_configure">Konfigurovat doplněk</string>
|
||||
<string name="addons_delete">Smazat doplněk</string>
|
||||
<string name="addons_appstore_add_description">Připojte svůj vlastní mediální server a procházejte i přehrávejte ze své osobní knihovny.</string>
|
||||
<string name="addons_appstore_empty_subtitle">Přidejte výše adresu URL serveru, pokud chcete, aby Nuvio zobrazovalo vaši soukromou knihovnu.</string>
|
||||
<string name="addons_appstore_empty_title">Nejsou připojeny žádné osobní knihovny.</string>
|
||||
<string name="addons_appstore_input_placeholder">URL serveru</string>
|
||||
<string name="addons_appstore_install_button">Přidat</string>
|
||||
<string name="addons_appstore_add_description">Přeneste svou knihovnu do Nuvia pomocí doplňků, které používáte.</string>
|
||||
<string name="addons_appstore_empty_subtitle">Přidejte doplněk a začněte vytvářet svou knihovnu.</string>
|
||||
<string name="addons_appstore_empty_title">Přizpůsobte si Nuvio.</string>
|
||||
<string name="addons_appstore_input_placeholder">URL doplňku</string>
|
||||
<string name="addons_appstore_install_button">Přidat doplněk</string>
|
||||
<string name="store_empty_unavailable_title">Zatím tu nic není</string>
|
||||
<string name="store_empty_unavailable_message">Vaše současné doplňky tu zatím nemají co zobrazit.</string>
|
||||
<string name="addons_empty_subtitle">Přidejte URL manifestu, abyste mohli do Nuvio začít načítat katalogy, metadata, streamy nebo titulky.</string>
|
||||
<string name="addons_empty_title">Zatím nejsou nainstalovány žádné doplňky.</string>
|
||||
<string name="addons_error_enter_url">Zadejte URL doplňku.</string>
|
||||
|
|
@ -442,7 +444,9 @@
|
|||
<string name="compose_settings_root_check_updates_description">Stáhnout nejnovější verzi</string>
|
||||
<string name="compose_settings_root_check_updates_title">Zkontrolovat aktualizace</string>
|
||||
<string name="compose_settings_root_content_discovery_description">Správa doplňků a zdrojů objevování.</string>
|
||||
<string name="compose_settings_root_content_discovery_description_appstore">Spravujte doplňky a vyberte, co se má v Nuviu zobrazovat.</string>
|
||||
<string name="compose_settings_root_downloads_description">Spravujte své stažené filmy a epizody.</string>
|
||||
<string name="compose_settings_root_downloads_description_appstore">Spravujte obsah uložený v tomto zařízení.</string>
|
||||
<string name="compose_settings_root_downloads_title">Stažené položky</string>
|
||||
<string name="compose_settings_root_general_section">OBECNÉ</string>
|
||||
<string name="compose_settings_root_integrations_description">Spravovat dostupné integrace</string>
|
||||
|
|
@ -638,7 +642,7 @@
|
|||
<string name="settings_content_discovery_section_home">DOMŮ</string>
|
||||
<string name="settings_content_discovery_section_sources">ZDROJE</string>
|
||||
<string name="settings_content_discovery_addons_description">Instalovat, odstraňovat, obnovovat a třídit vaše zdroje obsahu.</string>
|
||||
<string name="settings_content_discovery_addons_description_appstore">Připojit osobní mediální zdroje a spravovat přístup k vlastní knihovně.</string>
|
||||
<string name="settings_content_discovery_addons_description_appstore">Přidávejte a spravujte doplňky, které používáte s Nuviem.</string>
|
||||
<string name="settings_content_discovery_plugins_description">Instalovat repozitáře pro JavaScriptové scarpování a testovat poskytovatele interně.</string>
|
||||
<string name="settings_content_discovery_homescreen_description">Upravit rozvržení domovské obrazovky, viditelnost obsahu a chování plakátů.</string>
|
||||
<string name="settings_content_discovery_meta_screen_description">Nastavení pro obrazovku detailu a epizody.</string>
|
||||
|
|
|
|||
|
|
@ -27,6 +27,13 @@
|
|||
<string name="addons_badge_unavailable">Nicht verfügbar</string>
|
||||
<string name="addons_configure">Addon konfigurieren</string>
|
||||
<string name="addons_delete">Addon löschen</string>
|
||||
<string name="addons_appstore_add_description">Bring deine Mediathek mit den Addons, die du nutzt, zu Nuvio.</string>
|
||||
<string name="addons_appstore_empty_subtitle">Füge ein Addon hinzu, um deine Mediathek aufzubauen.</string>
|
||||
<string name="addons_appstore_empty_title">Richte Nuvio für dich ein.</string>
|
||||
<string name="addons_appstore_input_placeholder">Addon-URL</string>
|
||||
<string name="addons_appstore_install_button">Addon hinzufügen</string>
|
||||
<string name="store_empty_unavailable_title">Hier gibt es noch nichts</string>
|
||||
<string name="store_empty_unavailable_message">Deine aktuellen Addons zeigen hier noch nichts an.</string>
|
||||
<string name="addons_empty_subtitle">Füge eine Manifest-URL hinzu, um Kataloge, Metadaten, Streams oder Untertitel in Nuvio zu laden.</string>
|
||||
<string name="addons_empty_title">Noch keine Addons installiert.</string>
|
||||
<string name="addons_error_enter_url">Gib eine Addon-URL ein.</string>
|
||||
|
|
@ -383,7 +390,9 @@
|
|||
<string name="compose_settings_root_check_updates_description">Suche nach neuen Versionen der App.</string>
|
||||
<string name="compose_settings_root_check_updates_title">Nach Updates suchen</string>
|
||||
<string name="compose_settings_root_content_discovery_description">Verwalte Addons und Entdeckungsquellen.</string>
|
||||
<string name="compose_settings_root_content_discovery_description_appstore">Verwalte Addons und wähle aus, was in Nuvio angezeigt wird.</string>
|
||||
<string name="compose_settings_root_downloads_description">Verwalte deine heruntergeladenen Filme und Episoden.</string>
|
||||
<string name="compose_settings_root_downloads_description_appstore">Verwalte, was du auf diesem Gerät gespeichert hast.</string>
|
||||
<string name="compose_settings_root_downloads_title">Downloads</string>
|
||||
<string name="compose_settings_root_general_section">ALLGEMEIN</string>
|
||||
<string name="compose_settings_root_integrations_description">TMDB- und MDBList-Dienste verbinden.</string>
|
||||
|
|
@ -519,6 +528,7 @@
|
|||
<string name="settings_content_discovery_section_home">START</string>
|
||||
<string name="settings_content_discovery_section_sources">QUELLEN</string>
|
||||
<string name="settings_content_discovery_addons_description">Installiere, entferne, aktualisiere und sortiere deine Inhaltsquellen.</string>
|
||||
<string name="settings_content_discovery_addons_description_appstore">Füge die Addons hinzu, die du mit Nuvio nutzt, und verwalte sie.</string>
|
||||
<string name="settings_content_discovery_plugins_description">Installiere JavaScript-Scraper-Repositories und teste Anbieter intern.</string>
|
||||
<string name="settings_content_discovery_homescreen_description">Lege fest, welche Kataloge auf der Startseite und in welcher Reihenfolge erscheinen.</string>
|
||||
<string name="settings_content_discovery_meta_screen_description">Detail-Abschnitte deaktivieren und alles unterhalb des Hero neu anordnen.</string>
|
||||
|
|
|
|||
|
|
@ -233,7 +233,9 @@
|
|||
<string name="compose_settings_root_check_updates_description">Ελέγξτε για νέες εκδόσεις της εφαρμογής.</string>
|
||||
<string name="compose_settings_root_check_updates_title">Έλεγχος για ενημερώσεις</string>
|
||||
<string name="compose_settings_root_content_discovery_description">Διαχείριση πρόσθετων και πηγών ανακάλυψης.</string>
|
||||
<string name="compose_settings_root_content_discovery_description_appstore">Διαχειριστείτε τα πρόσθετα και επιλέξτε τι εμφανίζεται στο Nuvio.</string>
|
||||
<string name="compose_settings_root_downloads_description">Διαχειριστείτε τις κατεβασμένες ταινίες και επεισόδιά σας.</string>
|
||||
<string name="compose_settings_root_downloads_description_appstore">Διαχειριστείτε όσα έχετε αποθηκεύσει σε αυτή τη συσκευή.</string>
|
||||
<string name="compose_settings_root_downloads_title">Λήψεις</string>
|
||||
<string name="compose_settings_root_general_section">ΓΕΝΙΚΑ</string>
|
||||
<string name="compose_settings_root_integrations_description">Σύνδεση υπηρεσιών TMDB και MDBList.</string>
|
||||
|
|
@ -1061,11 +1063,13 @@
|
|||
<string name="action_saving">Αποθήκευση…</string>
|
||||
<string name="action_switch">Εναλλαγή</string>
|
||||
<string name="action_validate">Επικύρωση</string>
|
||||
<string name="addons_appstore_add_description">Συνδέστε τον δικό σας διακομιστή μέσων για να περιηγηθείτε και να αναπαραγάγετε από τη δική σας προσωπική βιβλιοθήκη.</string>
|
||||
<string name="addons_appstore_empty_subtitle">Προσθέστε μια διεύθυνση URL διακομιστή παραπάνω όταν θέλετε το Nuvio να εμφανίζει τη δική σας ιδιωτική βιβλιοθήκη.</string>
|
||||
<string name="addons_appstore_empty_title">Δεν υπάρχουν προσωπικές βιβλιοθήκες συνδεδεμένες.</string>
|
||||
<string name="addons_appstore_input_placeholder">Διεύθυνση URL διακομιστή</string>
|
||||
<string name="addons_appstore_install_button">Προσθήκη</string>
|
||||
<string name="addons_appstore_add_description">Φέρτε τη βιβλιοθήκη σας στο Nuvio με τα πρόσθετα που χρησιμοποιείτε.</string>
|
||||
<string name="addons_appstore_empty_subtitle">Προσθέστε ένα πρόσθετο για να αρχίσετε να δημιουργείτε τη βιβλιοθήκη σας.</string>
|
||||
<string name="addons_appstore_empty_title">Κάντε το Nuvio δικό σας.</string>
|
||||
<string name="addons_appstore_input_placeholder">URL πρόσθετου</string>
|
||||
<string name="addons_appstore_install_button">Προσθήκη πρόσθετου</string>
|
||||
<string name="store_empty_unavailable_title">Δεν υπάρχει τίποτα εδώ ακόμη</string>
|
||||
<string name="store_empty_unavailable_message">Τα πρόσθετα που χρησιμοποιείτε δεν έχουν ακόμη τίποτα να εμφανίσουν εδώ.</string>
|
||||
<string name="addons_badge_disabled">Απενεργοποιημένο</string>
|
||||
<string name="addons_manifest_missing_field">Το manifest είναι κενό "%1$s"</string>
|
||||
<string name="cloud_library_connect_action">Σύνδεση λογαριασμού</string>
|
||||
|
|
@ -1520,7 +1524,7 @@
|
|||
<string name="settings_appearance_app_language_device">Device Γλώσσα</string>
|
||||
<string name="settings_appearance_liquid_glass">Liquid Glass</string>
|
||||
<string name="settings_appearance_liquid_glass_description">Use the native iPhone tab bar on iOS 26 and later.</string>
|
||||
<string name="settings_content_discovery_addons_description_appstore">Connect personal media sources and manage access to your own Βιβλιοθήκη.</string>
|
||||
<string name="settings_content_discovery_addons_description_appstore">Προσθέστε και διαχειριστείτε τα πρόσθετα που χρησιμοποιείτε με το Nuvio.</string>
|
||||
<string name="settings_continue_watching_blur_next_up_description">Blur next episode thumbnails in Συνέχεια Watching to avoid spoilers.</string>
|
||||
<string name="settings_continue_watching_blur_next_up_title">Blur Unwatched in Συνέχεια Watching</string>
|
||||
<string name="settings_continue_watching_section_sort_order">Ταξινόμηση ORDER</string>
|
||||
|
|
|
|||
|
|
@ -32,11 +32,13 @@
|
|||
<string name="addons_badge_unavailable">No disponible</string>
|
||||
<string name="addons_configure">Configurar complemento</string>
|
||||
<string name="addons_delete">Eliminar complemento</string>
|
||||
<string name="addons_appstore_add_description">Conecte su propio servidor multimedia para navegar y reproducir desde su biblioteca personal.</string>
|
||||
<string name="addons_appstore_empty_subtitle">Agregue una URL de servidor arriba cuando desee que Nuvio muestre su biblioteca privada.</string>
|
||||
<string name="addons_appstore_empty_title">No hay bibliotecas personales conectadas.</string>
|
||||
<string name="addons_appstore_input_placeholder">URL del servidor</string>
|
||||
<string name="addons_appstore_install_button">Agregar</string>
|
||||
<string name="addons_appstore_add_description">Lleva tu biblioteca a Nuvio con los complementos que utilizas.</string>
|
||||
<string name="addons_appstore_empty_subtitle">Agrega un complemento para empezar a crear tu biblioteca.</string>
|
||||
<string name="addons_appstore_empty_title">Haz tuyo Nuvio.</string>
|
||||
<string name="addons_appstore_input_placeholder">URL del complemento</string>
|
||||
<string name="addons_appstore_install_button">Agregar complemento</string>
|
||||
<string name="store_empty_unavailable_title">Aún no hay nada aquí</string>
|
||||
<string name="store_empty_unavailable_message">Tus complementos actuales aún no tienen nada que mostrar aquí.</string>
|
||||
<string name="addons_empty_subtitle">Agrega una URL de manifiesto para empezar a cargar catálogos, metadatos, streams o subtítulos en Nuvio.</string>
|
||||
<string name="addons_empty_title">Aún no hay complementos instalados.</string>
|
||||
<string name="addons_error_enter_url">Introduce una URL de complemento.</string>
|
||||
|
|
@ -442,7 +444,9 @@
|
|||
<string name="compose_settings_root_check_updates_description">Buscar nuevas versiones de la app.</string>
|
||||
<string name="compose_settings_root_check_updates_title">Buscar actualizaciones</string>
|
||||
<string name="compose_settings_root_content_discovery_description">Administra complementos y fuentes de descubrimiento.</string>
|
||||
<string name="compose_settings_root_content_discovery_description_appstore">Administra tus complementos y elige qué aparece en Nuvio.</string>
|
||||
<string name="compose_settings_root_downloads_description">Administra tus películas y episodios descargados.</string>
|
||||
<string name="compose_settings_root_downloads_description_appstore">Administra lo que has guardado en este dispositivo.</string>
|
||||
<string name="compose_settings_root_downloads_title">Descargas</string>
|
||||
<string name="compose_settings_root_general_section">GENERAL</string>
|
||||
<string name="compose_settings_root_integrations_description">Conecta los servicios TMDB y MDBList.</string>
|
||||
|
|
@ -638,7 +642,7 @@
|
|||
<string name="settings_content_discovery_section_home">INICIO</string>
|
||||
<string name="settings_content_discovery_section_sources">FUENTES</string>
|
||||
<string name="settings_content_discovery_addons_description">Instala, elimina, actualiza y ordena tus fuentes de contenido.</string>
|
||||
<string name="settings_content_discovery_addons_description_appstore">Conecte fuentes de medios personales y administre el acceso a su propia biblioteca.</string>
|
||||
<string name="settings_content_discovery_addons_description_appstore">Agrega y administra los complementos que utilizas con Nuvio.</string>
|
||||
<string name="settings_content_discovery_plugins_description">Instala repositorios de scrapers en JavaScript y prueba proveedores internamente.</string>
|
||||
<string name="settings_content_discovery_homescreen_description">Controla qué catálogos aparecen en Inicio y en qué orden.</string>
|
||||
<string name="settings_content_discovery_meta_screen_description">Desactiva secciones de detalles y reordena todo debajo del Destacado.</string>
|
||||
|
|
|
|||
|
|
@ -33,11 +33,13 @@
|
|||
<string name="addons_badge_unavailable">Indisponible</string>
|
||||
<string name="addons_configure">Configurer l’addon</string>
|
||||
<string name="addons_delete">Supprimer l’addon</string>
|
||||
<string name="addons_appstore_add_description">Connectez votre propre serveur multimédia pour parcourir et lire depuis votre bibliothèque personnelle.</string>
|
||||
<string name="addons_appstore_empty_subtitle">Ajoutez une URL de serveur ci-dessus pour que Nuvio affiche votre bibliothèque privée.</string>
|
||||
<string name="addons_appstore_empty_title">Aucune bibliothèque personnelle connectée.</string>
|
||||
<string name="addons_appstore_input_placeholder">URL du serveur</string>
|
||||
<string name="addons_appstore_install_button">Ajouter</string>
|
||||
<string name="addons_appstore_add_description">Retrouvez votre bibliothèque dans Nuvio avec les addons que vous utilisez.</string>
|
||||
<string name="addons_appstore_empty_subtitle">Ajoutez un addon pour commencer à créer votre bibliothèque.</string>
|
||||
<string name="addons_appstore_empty_title">Personnalisez Nuvio.</string>
|
||||
<string name="addons_appstore_input_placeholder">URL de l’addon</string>
|
||||
<string name="addons_appstore_install_button">Ajouter un addon</string>
|
||||
<string name="store_empty_unavailable_title">Rien à afficher pour le moment</string>
|
||||
<string name="store_empty_unavailable_message">Vos addons actuels n’ont encore rien à afficher ici.</string>
|
||||
<string name="addons_empty_subtitle">Ajoutez une URL de manifeste pour commencer à charger des catalogues, métadonnées, streams ou sous-titres dans Nuvio.</string>
|
||||
<string name="addons_empty_title">Aucun addon installé.</string>
|
||||
<string name="addons_error_enter_url">Saisissez une URL d’addon.</string>
|
||||
|
|
@ -434,7 +436,9 @@
|
|||
<string name="compose_settings_root_check_updates_description">Rechercher de nouvelles versions de l’application.</string>
|
||||
<string name="compose_settings_root_check_updates_title">Vérifier les mises à jour</string>
|
||||
<string name="compose_settings_root_content_discovery_description">Gérez les addons et sources de découverte.</string>
|
||||
<string name="compose_settings_root_content_discovery_description_appstore">Gérez vos addons et choisissez ce qui apparaît dans Nuvio.</string>
|
||||
<string name="compose_settings_root_downloads_description">Gérez vos films et épisodes téléchargés.</string>
|
||||
<string name="compose_settings_root_downloads_description_appstore">Gérez ce que vous avez enregistré sur cet appareil.</string>
|
||||
<string name="compose_settings_root_downloads_title">Téléchargements</string>
|
||||
<string name="compose_settings_root_general_section">GÉNÉRAL</string>
|
||||
<string name="compose_settings_root_integrations_description">Connectez les services TMDB et MDBList.</string>
|
||||
|
|
@ -648,7 +652,7 @@
|
|||
<string name="settings_content_discovery_section_home">ACCUEIL</string>
|
||||
<string name="settings_content_discovery_section_sources">SOURCES</string>
|
||||
<string name="settings_content_discovery_addons_description">Installez, supprimez, mettez à jour et ordonnez vos sources de contenu.</string>
|
||||
<string name="settings_content_discovery_addons_description_appstore">Connectez des sources multimédias personnelles et gérez l’accès à votre propre bibliothèque.</string>
|
||||
<string name="settings_content_discovery_addons_description_appstore">Ajoutez et gérez les addons que vous utilisez avec Nuvio.</string>
|
||||
<string name="settings_content_discovery_plugins_description">Installez des dépôts de scrapers JavaScript et testez des fournisseurs en interne.</string>
|
||||
<string name="settings_content_discovery_homescreen_description">Contrôle quels catalogues apparaissent à l’accueil et dans quel ordre.</string>
|
||||
<string name="settings_content_discovery_meta_screen_description">Désactivez des sections de détails et réorganisez tout sous le Hero.</string>
|
||||
|
|
|
|||
|
|
@ -401,7 +401,9 @@
|
|||
<string name="compose_settings_root_check_updates_description">A legújabb verzió letöltése</string>
|
||||
<string name="compose_settings_root_check_updates_title">Frissítések keresése</string>
|
||||
<string name="compose_settings_root_content_discovery_description">Kiegészítők és tartalomforrások kezelése</string>
|
||||
<string name="compose_settings_root_content_discovery_description_appstore">Kezeld a kiegészítőket, és válaszd ki, mi jelenjen meg a Nuvióban.</string>
|
||||
<string name="compose_settings_root_downloads_description">A letöltött filmek és epizódok kezelése</string>
|
||||
<string name="compose_settings_root_downloads_description_appstore">Kezeld az eszközre mentett tartalmakat.</string>
|
||||
<string name="compose_settings_root_downloads_title">Letöltések</string>
|
||||
<string name="compose_settings_root_general_section">ÁLTALÁNOS</string>
|
||||
<string name="compose_settings_root_integrations_description">Elérhető integrációk kezelése</string>
|
||||
|
|
@ -1339,11 +1341,13 @@
|
|||
<string name="unit_bytes_gb">GB</string>
|
||||
<string name="action_switch">Váltás</string>
|
||||
<string name="addons_badge_disabled">Kikapcsolva</string>
|
||||
<string name="addons_appstore_add_description">Csatlakoztasd saját médiaszerveredet a személyes könyvtárad böngészéséhez és lejátszásához.</string>
|
||||
<string name="addons_appstore_empty_subtitle">Adj meg egy szerver URL-t fent, ha szeretnéd, hogy a Nuvio megjelenítse a privát könyvtáradat.</string>
|
||||
<string name="addons_appstore_empty_title">Nincsenek csatlakoztatott személyes könyvtárak.</string>
|
||||
<string name="addons_appstore_input_placeholder">Szerver URL</string>
|
||||
<string name="addons_appstore_install_button">Hozzáadás</string>
|
||||
<string name="addons_appstore_add_description">Hozd el a könyvtáradat a Nuvióba a használt kiegészítőkkel.</string>
|
||||
<string name="addons_appstore_empty_subtitle">Adj hozzá egy kiegészítőt a könyvtárad felépítéséhez.</string>
|
||||
<string name="addons_appstore_empty_title">Szabd személyre a Nuviót.</string>
|
||||
<string name="addons_appstore_input_placeholder">Kiegészítő URL-je</string>
|
||||
<string name="addons_appstore_install_button">Kiegészítő hozzáadása</string>
|
||||
<string name="store_empty_unavailable_title">Itt még nincs semmi</string>
|
||||
<string name="store_empty_unavailable_message">A jelenlegi kiegészítőid még nem jelenítenek meg itt semmit.</string>
|
||||
<string name="collections_editor_tmdb_genres_movie_placeholder">28,12</string>
|
||||
<string name="collections_editor_tmdb_genres_series_placeholder">18,35</string>
|
||||
<string name="collections_editor_tmdb_date_from_placeholder">2020-01-01</string>
|
||||
|
|
@ -1397,7 +1401,7 @@
|
|||
<string name="settings_show_secret">Érték megjelenítése</string>
|
||||
<string name="settings_continue_watching_style_card">Kártya</string>
|
||||
<string name="settings_continue_watching_style_card_description">TV-stílusú fekvő kártya</string>
|
||||
<string name="settings_content_discovery_addons_description_appstore">Személyes médiaforrások csatlakoztatása és a saját könyvtáradhoz való hozzáférés kezelése.</string>
|
||||
<string name="settings_content_discovery_addons_description_appstore">Add hozzá és kezeld a Nuvióval használt kiegészítőket.</string>
|
||||
<string name="settings_debrid_cloud_library">Felhő könyvtár</string>
|
||||
<string name="settings_debrid_cloud_library_description">A csatlakoztatott fiókjaidban már meglévő fájlok böngészése és lejátszása.</string>
|
||||
<string name="settings_debrid_resolve_with">Feloldás ezzel</string>
|
||||
|
|
|
|||
|
|
@ -31,6 +31,13 @@
|
|||
<string name="addons_badge_unavailable">Tidak Tersedia</string>
|
||||
<string name="addons_configure">Konfigurasi addon</string>
|
||||
<string name="addons_delete">Hapus addon</string>
|
||||
<string name="addons_appstore_add_description">Bawa pustaka Anda ke Nuvio dengan addon yang Anda gunakan.</string>
|
||||
<string name="addons_appstore_empty_subtitle">Tambahkan addon untuk mulai membangun pustaka Anda.</string>
|
||||
<string name="addons_appstore_empty_title">Jadikan Nuvio milik Anda.</string>
|
||||
<string name="addons_appstore_input_placeholder">URL Addon</string>
|
||||
<string name="addons_appstore_install_button">Tambah Addon</string>
|
||||
<string name="store_empty_unavailable_title">Belum ada apa pun di sini</string>
|
||||
<string name="store_empty_unavailable_message">Addon yang Anda gunakan belum memiliki apa pun untuk ditampilkan di sini.</string>
|
||||
<string name="addons_empty_subtitle">Tambahkan URL manifes untuk mulai memuat katalog, metadata, streaming, atau subtitle ke Nuvio.</string>
|
||||
<string name="addons_empty_title">Belum ada addon yang terpasang.</string>
|
||||
<string name="addons_error_enter_url">Masukkan URL addon.</string>
|
||||
|
|
@ -412,7 +419,9 @@
|
|||
<string name="compose_settings_root_check_updates_description">Unduh versi terbaru</string>
|
||||
<string name="compose_settings_root_check_updates_title">Periksa pembaruan</string>
|
||||
<string name="compose_settings_root_content_discovery_description">Kelola addon dan sumber penemuan.</string>
|
||||
<string name="compose_settings_root_content_discovery_description_appstore">Kelola addon dan pilih apa yang tampil di Nuvio.</string>
|
||||
<string name="compose_settings_root_downloads_description">Kelola film dan episode yang telah diunduh.</string>
|
||||
<string name="compose_settings_root_downloads_description_appstore">Kelola yang Anda simpan di perangkat ini.</string>
|
||||
<string name="compose_settings_root_downloads_title">Unduhan</string>
|
||||
<string name="compose_settings_root_general_section">UMUM</string>
|
||||
<string name="compose_settings_root_integrations_description">Kelola integrasi yang tersedia</string>
|
||||
|
|
@ -597,6 +606,7 @@
|
|||
<string name="settings_content_discovery_section_home">BERANDA</string>
|
||||
<string name="settings_content_discovery_section_sources">SUMBER</string>
|
||||
<string name="settings_content_discovery_addons_description">Pasang, hapus, perbarui, dan urutkan sumber konten Anda.</string>
|
||||
<string name="settings_content_discovery_addons_description_appstore">Tambahkan dan kelola addon yang Anda gunakan dengan Nuvio.</string>
|
||||
<string name="settings_content_discovery_plugins_description">Pasang repositori scraper JavaScript dan uji penyedia secara internal.</string>
|
||||
<string name="settings_content_discovery_homescreen_description">Sesuaikan tata letak beranda, visibilitas konten, dan poster</string>
|
||||
<string name="settings_content_discovery_meta_screen_description">Pengaturan untuk layar detail dan episode.</string>
|
||||
|
|
|
|||
|
|
@ -31,6 +31,13 @@
|
|||
<string name="addons_badge_unavailable">Tidak Tersedia</string>
|
||||
<string name="addons_configure">Konfigurasi addon</string>
|
||||
<string name="addons_delete">Hapus addon</string>
|
||||
<string name="addons_appstore_add_description">Bawa pustaka Anda ke Nuvio dengan addon yang Anda gunakan.</string>
|
||||
<string name="addons_appstore_empty_subtitle">Tambahkan addon untuk mulai membangun pustaka Anda.</string>
|
||||
<string name="addons_appstore_empty_title">Jadikan Nuvio milik Anda.</string>
|
||||
<string name="addons_appstore_input_placeholder">URL Addon</string>
|
||||
<string name="addons_appstore_install_button">Tambah Addon</string>
|
||||
<string name="store_empty_unavailable_title">Belum ada apa pun di sini</string>
|
||||
<string name="store_empty_unavailable_message">Addon yang Anda gunakan belum memiliki apa pun untuk ditampilkan di sini.</string>
|
||||
<string name="addons_empty_subtitle">Tambahkan URL manifes untuk mulai memuat katalog, metadata, streaming, atau subtitle ke Nuvio.</string>
|
||||
<string name="addons_empty_title">Belum ada addon yang terpasang.</string>
|
||||
<string name="addons_error_enter_url">Masukkan URL addon.</string>
|
||||
|
|
@ -412,7 +419,9 @@
|
|||
<string name="compose_settings_root_check_updates_description">Unduh versi terbaru</string>
|
||||
<string name="compose_settings_root_check_updates_title">Periksa pembaruan</string>
|
||||
<string name="compose_settings_root_content_discovery_description">Kelola addon dan sumber penemuan.</string>
|
||||
<string name="compose_settings_root_content_discovery_description_appstore">Kelola addon dan pilih apa yang tampil di Nuvio.</string>
|
||||
<string name="compose_settings_root_downloads_description">Kelola film dan episode yang telah diunduh.</string>
|
||||
<string name="compose_settings_root_downloads_description_appstore">Kelola yang Anda simpan di perangkat ini.</string>
|
||||
<string name="compose_settings_root_downloads_title">Unduhan</string>
|
||||
<string name="compose_settings_root_general_section">UMUM</string>
|
||||
<string name="compose_settings_root_integrations_description">Kelola integrasi yang tersedia</string>
|
||||
|
|
@ -597,6 +606,7 @@
|
|||
<string name="settings_content_discovery_section_home">BERANDA</string>
|
||||
<string name="settings_content_discovery_section_sources">SUMBER</string>
|
||||
<string name="settings_content_discovery_addons_description">Pasang, hapus, perbarui, dan urutkan sumber konten Anda.</string>
|
||||
<string name="settings_content_discovery_addons_description_appstore">Tambahkan dan kelola addon yang Anda gunakan dengan Nuvio.</string>
|
||||
<string name="settings_content_discovery_plugins_description">Pasang repositori scraper JavaScript dan uji penyedia secara internal.</string>
|
||||
<string name="settings_content_discovery_homescreen_description">Sesuaikan tata letak beranda, visibilitas konten, dan poster</string>
|
||||
<string name="settings_content_discovery_meta_screen_description">Pengaturan untuk layar detail dan episode.</string>
|
||||
|
|
|
|||
|
|
@ -27,6 +27,13 @@
|
|||
<string name="addons_badge_unavailable">Non Disponibile</string>
|
||||
<string name="addons_configure">Configura addon</string>
|
||||
<string name="addons_delete">Cancella addon</string>
|
||||
<string name="addons_appstore_add_description">Porta la tua libreria su Nuvio con gli addon che usi.</string>
|
||||
<string name="addons_appstore_empty_subtitle">Aggiungi un addon per iniziare a creare la tua libreria.</string>
|
||||
<string name="addons_appstore_empty_title">Fai tuo Nuvio.</string>
|
||||
<string name="addons_appstore_input_placeholder">URL addon</string>
|
||||
<string name="addons_appstore_install_button">Aggiungi addon</string>
|
||||
<string name="store_empty_unavailable_title">Non c’è ancora niente qui</string>
|
||||
<string name="store_empty_unavailable_message">Gli addon che usi non hanno ancora nulla da mostrare qui.</string>
|
||||
<string name="addons_empty_subtitle">Aggiungi un manifest URL per iniziare a caricare cataloghi , metadata, flussi o sottotitoli dentro Nuvio.</string>
|
||||
<string name="addons_empty_title">Nessun addon installato ancora.</string>
|
||||
<string name="addons_error_enter_url">Inserisci l'URL dell'addon.</string>
|
||||
|
|
@ -245,7 +252,9 @@
|
|||
<string name="compose_settings_root_check_updates_description">Controlla se ci sono nuove versioni dell'app.</string>
|
||||
<string name="compose_settings_root_check_updates_title">Verifica aggiornamenti</string>
|
||||
<string name="compose_settings_root_content_discovery_description">Gestisci gli addon e le sorgenti di scoperta.</string>
|
||||
<string name="compose_settings_root_content_discovery_description_appstore">Gestisci gli addon e scegli cosa mostrare in Nuvio.</string>
|
||||
<string name="compose_settings_root_downloads_description">Gestisci i film e gli episodi scaricati.</string>
|
||||
<string name="compose_settings_root_downloads_description_appstore">Gestisci ciò che hai salvato su questo dispositivo.</string>
|
||||
<string name="compose_settings_root_downloads_title">Download</string>
|
||||
<string name="compose_settings_root_general_section">GENERALI</string>
|
||||
<string name="compose_settings_root_integrations_description">Collega i servizi TMDB e MDBList.</string>
|
||||
|
|
@ -382,6 +391,7 @@
|
|||
<string name="settings_content_discovery_section_home">HOME</string>
|
||||
<string name="settings_content_discovery_section_sources">SORGENTI</string>
|
||||
<string name="settings_content_discovery_addons_description">Installa, rimuovi, aggiorna e ordina le tue sorgenti di contenuto.</string>
|
||||
<string name="settings_content_discovery_addons_description_appstore">Aggiungi e gestisci gli addon che usi con Nuvio.</string>
|
||||
<string name="settings_content_discovery_plugins_description">Installa repository di scraper JavaScript e testa i provider internamente.</string>
|
||||
<string name="settings_content_discovery_homescreen_description">Controlla quali cataloghi appaiono in Home e in quale ordine.</string>
|
||||
<string name="settings_content_discovery_meta_screen_description">Disabilita le sezioni dei dettagli e riordina tutto ciò che sta sotto l'elemento Hero.</string>
|
||||
|
|
|
|||
|
|
@ -32,11 +32,13 @@
|
|||
<string name="addons_badge_unavailable">利用不可</string>
|
||||
<string name="addons_configure">アドオンを設定</string>
|
||||
<string name="addons_delete">アドオンを削除</string>
|
||||
<string name="addons_appstore_add_description">独自のメディアサーバーを接続して、個人ライブラリを参照・再生します。</string>
|
||||
<string name="addons_appstore_empty_subtitle">Nuvioに個人ライブラリを表示させたい場合は、上にサーバーURLを追加してください。</string>
|
||||
<string name="addons_appstore_empty_title">接続された個人ライブラリはありません。</string>
|
||||
<string name="addons_appstore_input_placeholder">サーバーURL</string>
|
||||
<string name="addons_appstore_install_button">追加</string>
|
||||
<string name="addons_appstore_add_description">いつものアドオンで、ライブラリをNuvioにまとめましょう。</string>
|
||||
<string name="addons_appstore_empty_subtitle">アドオンを追加して、ライブラリを作り始めましょう。</string>
|
||||
<string name="addons_appstore_empty_title">Nuvioを自分好みに。</string>
|
||||
<string name="addons_appstore_input_placeholder">アドオンURL</string>
|
||||
<string name="addons_appstore_install_button">アドオンを追加</string>
|
||||
<string name="store_empty_unavailable_title">まだ何もありません</string>
|
||||
<string name="store_empty_unavailable_message">現在のアドオンには、ここに表示できるものがまだありません。</string>
|
||||
<string name="addons_empty_subtitle">マニフェストURLを追加して、カタログ・メタデータ・ストリーム・字幕をNuvioに読み込みましょう。</string>
|
||||
<string name="addons_empty_title">アドオンがありません</string>
|
||||
<string name="addons_error_enter_url">アドオンのURLを入力してください。</string>
|
||||
|
|
@ -442,7 +444,9 @@
|
|||
<string name="compose_settings_root_check_updates_description">最新リリースをダウンロード</string>
|
||||
<string name="compose_settings_root_check_updates_title">アップデートを確認</string>
|
||||
<string name="compose_settings_root_content_discovery_description">アドオンと探索ソースを管理</string>
|
||||
<string name="compose_settings_root_content_discovery_description_appstore">アドオンを管理して、Nuvioに表示する内容を選べます。</string>
|
||||
<string name="compose_settings_root_downloads_description">ダウンロードした映画とエピソードを管理</string>
|
||||
<string name="compose_settings_root_downloads_description_appstore">このデバイスに保存した項目を管理します。</string>
|
||||
<string name="compose_settings_root_downloads_title">ダウンロード</string>
|
||||
<string name="compose_settings_root_general_section">一般</string>
|
||||
<string name="compose_settings_root_integrations_description">利用可能な連携を管理</string>
|
||||
|
|
@ -638,7 +642,7 @@
|
|||
<string name="settings_content_discovery_section_home">ホーム</string>
|
||||
<string name="settings_content_discovery_section_sources">ソース</string>
|
||||
<string name="settings_content_discovery_addons_description">コンテンツソースのインストール・削除・更新・並べ替え</string>
|
||||
<string name="settings_content_discovery_addons_description_appstore">個人メディアソースを接続し、自分専用ライブラリへのアクセスを管理します。</string>
|
||||
<string name="settings_content_discovery_addons_description_appstore">Nuvioで使うアドオンを追加・管理します。</string>
|
||||
<string name="settings_content_discovery_plugins_description">JavaScriptスクレイパーリポジトリのインストールと内部テスト</string>
|
||||
<string name="settings_content_discovery_homescreen_description">ホームレイアウト・コンテンツ表示・ポスターの動作を調整</string>
|
||||
<string name="settings_content_discovery_meta_screen_description">詳細画面とエピソード画面の設定</string>
|
||||
|
|
|
|||
|
|
@ -29,6 +29,13 @@
|
|||
<string name="addons_badge_unavailable">Utilgjengelig</string>
|
||||
<string name="addons_configure">Konfigurer tillegg</string>
|
||||
<string name="addons_delete">Slett tillegg</string>
|
||||
<string name="addons_appstore_add_description">Ta med biblioteket ditt til Nuvio med tilleggene du bruker.</string>
|
||||
<string name="addons_appstore_empty_subtitle">Legg til et tillegg for å begynne å bygge biblioteket ditt.</string>
|
||||
<string name="addons_appstore_empty_title">Gjør Nuvio til ditt.</string>
|
||||
<string name="addons_appstore_input_placeholder">URL til tillegg</string>
|
||||
<string name="addons_appstore_install_button">Legg til tillegg</string>
|
||||
<string name="store_empty_unavailable_title">Det er ingenting her ennå</string>
|
||||
<string name="store_empty_unavailable_message">Tilleggene du bruker, har ingenting å vise her ennå.</string>
|
||||
<string name="addons_empty_subtitle">Legg til en manifest-URL for å starte lasting av kataloger, metadata, strømmer eller undertekster i Nuvio.</string>
|
||||
<string name="addons_empty_title">Ingen tillegg installert ennå.</string>
|
||||
<string name="addons_error_enter_url">Skriv inn en tilleggs-URL.</string>
|
||||
|
|
@ -400,7 +407,9 @@
|
|||
<string name="compose_settings_root_check_updates_description">Last ned nyeste versjon</string>
|
||||
<string name="compose_settings_root_check_updates_title">Se etter oppdateringer</string>
|
||||
<string name="compose_settings_root_content_discovery_description">Administrer tillegg og oppdagelseskilder.</string>
|
||||
<string name="compose_settings_root_content_discovery_description_appstore">Administrer tillegg og velg hva som vises i Nuvio.</string>
|
||||
<string name="compose_settings_root_downloads_description">Administrer nedlastede filmer og episoder.</string>
|
||||
<string name="compose_settings_root_downloads_description_appstore">Administrer det du har lagret på denne enheten.</string>
|
||||
<string name="compose_settings_root_downloads_title">Nedlastinger</string>
|
||||
<string name="compose_settings_root_general_section">GENERELT</string>
|
||||
<string name="compose_settings_root_integrations_description">Administrer tilgjengelige integrasjoner</string>
|
||||
|
|
@ -581,6 +590,7 @@
|
|||
<string name="settings_content_discovery_section_home">HJEM</string>
|
||||
<string name="settings_content_discovery_section_sources">KILDER</string>
|
||||
<string name="settings_content_discovery_addons_description">Installer, fjern, oppdater og sorter innholdskilder.</string>
|
||||
<string name="settings_content_discovery_addons_description_appstore">Legg til og administrer tilleggene du bruker med Nuvio.</string>
|
||||
<string name="settings_content_discovery_plugins_description">Installer JavaScript-scraper-repositories og test providere internt.</string>
|
||||
<string name="settings_content_discovery_homescreen_description">Juster hjemmeoppsett, synlighet og plakat-oppførsel.</string>
|
||||
<string name="settings_content_discovery_meta_screen_description">Innstillinger for detalj- og episodeskjermer.</string>
|
||||
|
|
|
|||
|
|
@ -32,11 +32,13 @@
|
|||
<string name="addons_badge_unavailable">Niet beschikbaar</string>
|
||||
<string name="addons_configure">Addon configureren</string>
|
||||
<string name="addons_delete">Addon verwijderen</string>
|
||||
<string name="addons_appstore_add_description">Verbind je eigen mediaserver om je persoonlijke bibliotheek te bekijken en af te spelen.</string>
|
||||
<string name="addons_appstore_empty_subtitle">Voeg hierboven een server-URL toe als je wilt dat Nuvio je privébibliotheek toont.</string>
|
||||
<string name="addons_appstore_empty_title">Geen persoonlijke bibliotheken verbonden.</string>
|
||||
<string name="addons_appstore_input_placeholder">Server-URL</string>
|
||||
<string name="addons_appstore_install_button">Toevoegen</string>
|
||||
<string name="addons_appstore_add_description">Breng je bibliotheek naar Nuvio met de add-ons die je gebruikt.</string>
|
||||
<string name="addons_appstore_empty_subtitle">Voeg een add-on toe om je bibliotheek op te bouwen.</string>
|
||||
<string name="addons_appstore_empty_title">Maak Nuvio helemaal van jou.</string>
|
||||
<string name="addons_appstore_input_placeholder">Add-on-URL</string>
|
||||
<string name="addons_appstore_install_button">Add-on toevoegen</string>
|
||||
<string name="store_empty_unavailable_title">Hier is nog niets</string>
|
||||
<string name="store_empty_unavailable_message">Je huidige add-ons hebben hier nog niets om te tonen.</string>
|
||||
<string name="addons_empty_subtitle">Voeg een manifest-URL toe om catalogi, metadata, streams of ondertitels in Nuvio te laden.</string>
|
||||
<string name="addons_empty_title">Nog geen addons geïnstalleerd.</string>
|
||||
<string name="addons_error_enter_url">Voer een addon-URL in.</string>
|
||||
|
|
@ -442,7 +444,9 @@
|
|||
<string name="compose_settings_root_check_updates_description">Download de laatste versie</string>
|
||||
<string name="compose_settings_root_check_updates_title">Controleren op updates</string>
|
||||
<string name="compose_settings_root_content_discovery_description">Beheer addons en ontdekkingsbronnen.</string>
|
||||
<string name="compose_settings_root_content_discovery_description_appstore">Beheer add-ons en kies wat er in Nuvio verschijnt.</string>
|
||||
<string name="compose_settings_root_downloads_description">Beheer je gedownloade films en afleveringen.</string>
|
||||
<string name="compose_settings_root_downloads_description_appstore">Beheer wat je op dit apparaat hebt opgeslagen.</string>
|
||||
<string name="compose_settings_root_downloads_title">Downloads</string>
|
||||
<string name="compose_settings_root_general_section">ALGEMEEN</string>
|
||||
<string name="compose_settings_root_integrations_description">Beheer beschikbare integraties</string>
|
||||
|
|
@ -656,7 +660,7 @@
|
|||
<string name="settings_content_discovery_section_home">HOME</string>
|
||||
<string name="settings_content_discovery_section_sources">BRONNEN</string>
|
||||
<string name="settings_content_discovery_addons_description">Installeer, verwijder, vernieuw en sorteer je contentbronnen.</string>
|
||||
<string name="settings_content_discovery_addons_description_appstore">Verbind persoonlijke mediabronnen en beheer toegang tot je eigen bibliotheek.</string>
|
||||
<string name="settings_content_discovery_addons_description_appstore">Voeg de add-ons toe die je met Nuvio gebruikt en beheer ze.</string>
|
||||
<string name="settings_content_discovery_plugins_description">Installeer JavaScript-scraperrepository's en test providers intern.</string>
|
||||
<string name="settings_content_discovery_homescreen_description">Pas startschermindeling, zichtbaarheid van inhoud en postergedrag aan</string>
|
||||
<string name="settings_content_discovery_meta_screen_description">Instellingen voor de detail- en afleveringsschermen.</string>
|
||||
|
|
|
|||
|
|
@ -31,6 +31,13 @@
|
|||
<string name="addons_badge_unavailable">Niedostępny</string>
|
||||
<string name="addons_configure">Konfiguruj dodatek</string>
|
||||
<string name="addons_delete">Usuń dodatek</string>
|
||||
<string name="addons_appstore_add_description">Przenieś swoją bibliotekę do Nuvio za pomocą używanych dodatków.</string>
|
||||
<string name="addons_appstore_empty_subtitle">Dodaj dodatek, aby zacząć tworzyć swoją bibliotekę.</string>
|
||||
<string name="addons_appstore_empty_title">Dostosuj Nuvio do siebie.</string>
|
||||
<string name="addons_appstore_input_placeholder">URL dodatku</string>
|
||||
<string name="addons_appstore_install_button">Dodaj dodatek</string>
|
||||
<string name="store_empty_unavailable_title">Na razie nic tu nie ma</string>
|
||||
<string name="store_empty_unavailable_message">Używane dodatki nie mają jeszcze nic do wyświetlenia.</string>
|
||||
<string name="addons_empty_subtitle">Dodaj URL manifestu, aby zacząć ładować katalogi, metadane, strumienie lub napisy do Nuvio.</string>
|
||||
<string name="addons_empty_title">Brak zainstalowanych dodatków.</string>
|
||||
<string name="addons_error_enter_url">Wprowadź URL dodatku.</string>
|
||||
|
|
@ -412,7 +419,9 @@
|
|||
<string name="compose_settings_root_check_updates_description">Sprawdź dostępność nowych wersji aplikacji.</string>
|
||||
<string name="compose_settings_root_check_updates_title">Sprawdź aktualizacje</string>
|
||||
<string name="compose_settings_root_content_discovery_description">Zarządzaj dodatkami i źródłami odkrywania.</string>
|
||||
<string name="compose_settings_root_content_discovery_description_appstore">Zarządzaj dodatkami i wybierz, co ma być widoczne w Nuvio.</string>
|
||||
<string name="compose_settings_root_downloads_description">Zarządzaj pobranymi filmami i odcinkami.</string>
|
||||
<string name="compose_settings_root_downloads_description_appstore">Zarządzaj zawartością zapisaną na tym urządzeniu.</string>
|
||||
<string name="compose_settings_root_downloads_title">Pobrane</string>
|
||||
<string name="compose_settings_root_general_section">OGÓLNE</string>
|
||||
<string name="compose_settings_root_integrations_description">Połącz usługi TMDB i MDBList.</string>
|
||||
|
|
@ -597,6 +606,7 @@
|
|||
<string name="settings_content_discovery_section_home">EKRAN GŁÓWNY</string>
|
||||
<string name="settings_content_discovery_section_sources">ŹRÓDŁA</string>
|
||||
<string name="settings_content_discovery_addons_description">Instaluj, usuwaj, odświeżaj i sortuj źródła treści.</string>
|
||||
<string name="settings_content_discovery_addons_description_appstore">Dodawaj używane w Nuvio dodatki i zarządzaj nimi.</string>
|
||||
<string name="settings_content_discovery_plugins_description">Instaluj repozytoria scraperów JavaScript i testuj dostawców wewnętrznie.</string>
|
||||
<string name="settings_content_discovery_homescreen_description">Kontroluj, które katalogi pojawiają się na ekranie głównym i w jakiej kolejności.</string>
|
||||
<string name="settings_content_discovery_meta_screen_description">Wyłącz sekcje szczegółów i zmień kolejność wszystkiego poniżej Hero.</string>
|
||||
|
|
|
|||
|
|
@ -30,6 +30,13 @@
|
|||
<string name="addons_badge_unavailable">Indisponível</string>
|
||||
<string name="addons_configure">Configurar addon</string>
|
||||
<string name="addons_delete">Excluir addon</string>
|
||||
<string name="addons_appstore_add_description">Leve sua biblioteca para o Nuvio com os addons que você usa.</string>
|
||||
<string name="addons_appstore_empty_subtitle">Adicione um addon para começar a montar sua biblioteca.</string>
|
||||
<string name="addons_appstore_empty_title">Deixe o Nuvio do seu jeito.</string>
|
||||
<string name="addons_appstore_input_placeholder">URL do addon</string>
|
||||
<string name="addons_appstore_install_button">Adicionar addon</string>
|
||||
<string name="store_empty_unavailable_title">Ainda não há nada aqui</string>
|
||||
<string name="store_empty_unavailable_message">Os addons que você usa ainda não têm nada para mostrar aqui.</string>
|
||||
<string name="addons_empty_subtitle">Adicione uma URL de manifesto para começar a carregar catálogos, metadados, streams ou legendas no Nuvio.</string>
|
||||
<string name="addons_empty_title">Nenhum addon instalado ainda.</string>
|
||||
<string name="addons_error_enter_url">Insira uma URL de addon.</string>
|
||||
|
|
@ -401,7 +408,9 @@
|
|||
<string name="compose_settings_root_check_updates_description">Baixar a versão mais recente</string>
|
||||
<string name="compose_settings_root_check_updates_title">Verificar atualizações</string>
|
||||
<string name="compose_settings_root_content_discovery_description">Gerenciar addons e fontes de descoberta.</string>
|
||||
<string name="compose_settings_root_content_discovery_description_appstore">Gerencie addons e escolha o que aparece no Nuvio.</string>
|
||||
<string name="compose_settings_root_downloads_description">Gerencie seus filmes e episódios baixados.</string>
|
||||
<string name="compose_settings_root_downloads_description_appstore">Gerencie o que você salvou neste dispositivo.</string>
|
||||
<string name="compose_settings_root_downloads_title">Downloads</string>
|
||||
<string name="compose_settings_root_general_section">GERAL</string>
|
||||
<string name="compose_settings_root_integrations_description">Gerenciar integrações disponíveis</string>
|
||||
|
|
@ -582,6 +591,7 @@
|
|||
<string name="settings_content_discovery_section_home">PÁGINA INICIAL</string>
|
||||
<string name="settings_content_discovery_section_sources">FONTES</string>
|
||||
<string name="settings_content_discovery_addons_description">Instale, remova, atualize e ordene suas fontes de conteúdo.</string>
|
||||
<string name="settings_content_discovery_addons_description_appstore">Adicione e gerencie os addons que você usa com o Nuvio.</string>
|
||||
<string name="settings_content_discovery_plugins_description">Instale repositórios de scraper JavaScript e teste provedores internamente.</string>
|
||||
<string name="settings_content_discovery_homescreen_description">Ajuste layout da página inicial, visibilidade de conteúdo e comportamento de pôsteres</string>
|
||||
<string name="settings_content_discovery_meta_screen_description">Configurações para as telas de detalhes e episódios.</string>
|
||||
|
|
|
|||
|
|
@ -31,6 +31,13 @@
|
|||
<string name="addons_badge_unavailable">Indisponível</string>
|
||||
<string name="addons_configure">Configurar addon</string>
|
||||
<string name="addons_delete">Eliminar addon</string>
|
||||
<string name="addons_appstore_add_description">Leva a tua biblioteca para o Nuvio com os addons que usas.</string>
|
||||
<string name="addons_appstore_empty_subtitle">Adiciona um addon para começares a criar a tua biblioteca.</string>
|
||||
<string name="addons_appstore_empty_title">Deixa o Nuvio à tua maneira.</string>
|
||||
<string name="addons_appstore_input_placeholder">URL do addon</string>
|
||||
<string name="addons_appstore_install_button">Adicionar addon</string>
|
||||
<string name="store_empty_unavailable_title">Ainda não há nada aqui</string>
|
||||
<string name="store_empty_unavailable_message">Os addons que usas ainda não têm nada para mostrar aqui.</string>
|
||||
<string name="addons_empty_subtitle">Adiciona um URL de manifesto para começares a carregar catálogos, metadados, streams ou legendas no Nuvio.</string>
|
||||
<string name="addons_empty_title">Ainda não tens addons instalados.</string>
|
||||
<string name="addons_error_enter_url">Introduz o URL de um addon.</string>
|
||||
|
|
@ -432,7 +439,9 @@
|
|||
<string name="compose_settings_root_check_updates_description">Transferir a versão mais recente</string>
|
||||
<string name="compose_settings_root_check_updates_title">Procurar atualizações</string>
|
||||
<string name="compose_settings_root_content_discovery_description">Gere addons e fontes de descoberta.</string>
|
||||
<string name="compose_settings_root_content_discovery_description_appstore">Gere os addons e escolhe o que aparece no Nuvio.</string>
|
||||
<string name="compose_settings_root_downloads_description">Gere os teus filmes e episódios transferidos.</string>
|
||||
<string name="compose_settings_root_downloads_description_appstore">Gere o que guardaste neste dispositivo.</string>
|
||||
<string name="compose_settings_root_downloads_title">Transferências</string>
|
||||
<string name="compose_settings_root_general_section">GERAL</string>
|
||||
<string name="compose_settings_root_integrations_description">Gere as integrações disponíveis</string>
|
||||
|
|
@ -627,6 +636,7 @@
|
|||
<string name="settings_content_discovery_section_home">PÁGINA INICIAL</string>
|
||||
<string name="settings_content_discovery_section_sources">FONTES</string>
|
||||
<string name="settings_content_discovery_addons_description">Instala, remove, atualiza e ordena as tuas fontes de conteúdo.</string>
|
||||
<string name="settings_content_discovery_addons_description_appstore">Adiciona e gere os addons que usas com o Nuvio.</string>
|
||||
<string name="settings_content_discovery_plugins_description">Instala repositórios de scrapers JavaScript e testa fornecedores internamente.</string>
|
||||
<string name="settings_content_discovery_homescreen_description">Ajusta o esquema da página inicial, a visibilidade do conteúdo e o comportamento dos cartazes.</string>
|
||||
<string name="settings_content_discovery_meta_screen_description">Definições para as páginas de detalhes e de episódios.</string>
|
||||
|
|
|
|||
|
|
@ -32,11 +32,13 @@
|
|||
<string name="addons_badge_unavailable">Indisponibil</string>
|
||||
<string name="addons_configure">Configurează extensia</string>
|
||||
<string name="addons_delete">Șterge extensia</string>
|
||||
<string name="addons_appstore_add_description">Conectează-ți propriul server media pentru a naviga și a reda din biblioteca personală.</string>
|
||||
<string name="addons_appstore_empty_subtitle">Adaugă un URL de server mai sus când vrei ca Nuvio să îți afișeze biblioteca privată.</string>
|
||||
<string name="addons_appstore_empty_title">Nicio bibliotecă personală conectată.</string>
|
||||
<string name="addons_appstore_input_placeholder">URL Server</string>
|
||||
<string name="addons_appstore_install_button">Adaugă</string>
|
||||
<string name="addons_appstore_add_description">Folosește extensiile preferate pentru a-ți aduce biblioteca în Nuvio.</string>
|
||||
<string name="addons_appstore_empty_subtitle">Adaugă o extensie pentru a începe să-ți construiești biblioteca.</string>
|
||||
<string name="addons_appstore_empty_title">Personalizează Nuvio.</string>
|
||||
<string name="addons_appstore_input_placeholder">URL extensie</string>
|
||||
<string name="addons_appstore_install_button">Adaugă extensie</string>
|
||||
<string name="store_empty_unavailable_title">Nu este nimic aici încă</string>
|
||||
<string name="store_empty_unavailable_message">Extensiile pe care le folosești nu au încă nimic de afișat aici.</string>
|
||||
<string name="addons_empty_subtitle">Adaugă un URL de manifest pentru a începe încărcarea de cataloage, metadate, fluxuri sau subtitrări în Nuvio.</string>
|
||||
<string name="addons_empty_title">Nu există extensii instalate încă.</string>
|
||||
<string name="addons_error_enter_url">Introdu un URL pentru extensie.</string>
|
||||
|
|
@ -442,7 +444,9 @@
|
|||
<string name="compose_settings_root_check_updates_description">Descarcă cea mai recentă versiune</string>
|
||||
<string name="compose_settings_root_check_updates_title">Caută actualizări</string>
|
||||
<string name="compose_settings_root_content_discovery_description">Gestionează extensiile și sursele de descoperire.</string>
|
||||
<string name="compose_settings_root_content_discovery_description_appstore">Gestionează extensiile și alege ce apare în Nuvio.</string>
|
||||
<string name="compose_settings_root_downloads_description">Gestionează filmele și episoadele descărcate.</string>
|
||||
<string name="compose_settings_root_downloads_description_appstore">Gestionează ce ai salvat pe acest dispozitiv.</string>
|
||||
<string name="compose_settings_root_downloads_title">Descărcări</string>
|
||||
<string name="compose_settings_root_general_section">GENERAL</string>
|
||||
<string name="compose_settings_root_integrations_description">Gestionează integrările disponibile</string>
|
||||
|
|
@ -656,7 +660,7 @@
|
|||
<string name="settings_content_discovery_section_home">ECRAN PRINCIPAL</string>
|
||||
<string name="settings_content_discovery_section_sources">SURSE</string>
|
||||
<string name="settings_content_discovery_addons_description">Instalează, elimină, reîmprospătează și sortează sursele de conținut.</string>
|
||||
<string name="settings_content_discovery_addons_description_appstore">Conectează surse media personale și gestionează accesul la propria bibliotecă.</string>
|
||||
<string name="settings_content_discovery_addons_description_appstore">Adaugă și gestionează extensiile pe care le folosești cu Nuvio.</string>
|
||||
<string name="settings_content_discovery_plugins_description">Instalează depozite de scraper JavaScript și testează furnizorii intern.</string>
|
||||
<string name="settings_content_discovery_homescreen_description">Ajustează aspectul ecranului principal, vizibilitatea conținutului și comportamentul posterului</string>
|
||||
<string name="settings_content_discovery_meta_screen_description">Setări pentru ecranele de detalii și de episoade.</string>
|
||||
|
|
@ -1932,4 +1936,4 @@
|
|||
<item quantity="one">%d titlu</item>
|
||||
<item quantity="other">%d titluri</item>
|
||||
</plurals>
|
||||
</resources>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -434,7 +434,9 @@
|
|||
<string name="compose_settings_root_check_updates_description">Stiahnuť najnovšie vydanie</string>
|
||||
<string name="compose_settings_root_check_updates_title">Skontrolovať aktualizácie</string>
|
||||
<string name="compose_settings_root_content_discovery_description">Spravovať doplnky a zdroje objavovania.</string>
|
||||
<string name="compose_settings_root_content_discovery_description_appstore">Spravujte doplnky a vyberte, čo sa má zobrazovať v Nuviu.</string>
|
||||
<string name="compose_settings_root_downloads_description">Spravovať stiahnuté filmy a epizódy.</string>
|
||||
<string name="compose_settings_root_downloads_description_appstore">Spravujte obsah uložený v tomto zariadení.</string>
|
||||
<string name="compose_settings_root_downloads_title">Stiahnuté</string>
|
||||
<string name="compose_settings_root_general_section">VŠEOBECNÉ</string>
|
||||
<string name="compose_settings_root_integrations_description">Spravovať dostupné integrácie</string>
|
||||
|
|
@ -1898,15 +1900,17 @@
|
|||
<string name="player_error_torrent">Chyba torrentu: %1$s</string>
|
||||
<!-- Current cmp-rewrite string additions -->
|
||||
<string name="action_switch">Prepnúť</string>
|
||||
<string name="addons_appstore_add_description">Pripojte vlastný mediálny server na prehliadanie a prehrávanie z vašej osobnej knižnice.</string>
|
||||
<string name="addons_appstore_empty_subtitle">Pridajte URL servera vyššie, ak chcete, aby Nuvio zobrazovalo vašu súkromnú knižnicu.</string>
|
||||
<string name="addons_appstore_empty_title">Nie sú pripojené žiadne osobné knižnice.</string>
|
||||
<string name="addons_appstore_input_placeholder">URL servera</string>
|
||||
<string name="addons_appstore_install_button">Pridať</string>
|
||||
<string name="addons_appstore_add_description">Preneste svoju knižnicu do Nuvia pomocou doplnkov, ktoré používate.</string>
|
||||
<string name="addons_appstore_empty_subtitle">Pridajte doplnok a začnite si vytvárať knižnicu.</string>
|
||||
<string name="addons_appstore_empty_title">Prispôsobte si Nuvio.</string>
|
||||
<string name="addons_appstore_input_placeholder">URL doplnku</string>
|
||||
<string name="addons_appstore_install_button">Pridať doplnok</string>
|
||||
<string name="store_empty_unavailable_title">Zatiaľ tu nič nie je</string>
|
||||
<string name="store_empty_unavailable_message">Doplnky, ktoré používate, tu zatiaľ nemajú čo zobraziť.</string>
|
||||
<string name="compose_auth_terms_prefix">Registráciou súhlasím s</string>
|
||||
<string name="compose_auth_terms_link">Podmienkami</string>
|
||||
<string name="settings_appearance_app_language_device">Jazyk zariadenia</string>
|
||||
<string name="settings_content_discovery_addons_description_appstore">Pripojte osobné mediálne zdroje a spravujte prístup k vlastnej knižnici.</string>
|
||||
<string name="settings_content_discovery_addons_description_appstore">Pridávajte a spravujte doplnky, ktoré používate s Nuviom.</string>
|
||||
<string name="settings_playback_external_player_send_skip_segments">Odosielať časové značky intra a outra</string>
|
||||
<string name="settings_playback_external_player_send_skip_segments_description">Odovzdať vyriešené časové značky intra a outra externému prehrávaču na automatické preskakovanie. Funguje iba v prehrávačoch, ktoré to podporujú; ostatné prehrávače to ignorujú.</string>
|
||||
<string name="settings_playback_option_original">Pôvodný jazyk</string>
|
||||
|
|
|
|||
|
|
@ -31,6 +31,13 @@
|
|||
<string name="addons_badge_unavailable">Kullanılamıyor</string>
|
||||
<string name="addons_configure">Eklentiyi ayarla</string>
|
||||
<string name="addons_delete">Eklentiyi sil</string>
|
||||
<string name="addons_appstore_add_description">Kullandığın eklentilerle kitaplığını Nuvio'ya taşı.</string>
|
||||
<string name="addons_appstore_empty_subtitle">Kitaplığını oluşturmaya başlamak için bir eklenti ekle.</string>
|
||||
<string name="addons_appstore_empty_title">Nuvio'yu kendine göre ayarla.</string>
|
||||
<string name="addons_appstore_input_placeholder">Eklenti URL'si</string>
|
||||
<string name="addons_appstore_install_button">Eklenti ekle</string>
|
||||
<string name="store_empty_unavailable_title">Burada henüz bir şey yok</string>
|
||||
<string name="store_empty_unavailable_message">Kullandığın eklentilerin burada gösterecek bir şeyi yok.</string>
|
||||
<string name="addons_empty_subtitle">Nuvio'ya katalog, meta veri, yayın veya altyazı yüklemeye başlamak için bir manifest URL'si ekle.</string>
|
||||
<string name="addons_empty_title">Henüz eklenti kurulmamış.</string>
|
||||
<string name="addons_error_enter_url">Bir eklenti URL'si gir.</string>
|
||||
|
|
@ -402,7 +409,9 @@
|
|||
<string name="compose_settings_root_check_updates_description">Uygulamanın yeni sürümü var mı kontrol et.</string>
|
||||
<string name="compose_settings_root_check_updates_title">Güncellemeleri kontrol et</string>
|
||||
<string name="compose_settings_root_content_discovery_description">Eklentileri ve keşif kaynaklarını yönet.</string>
|
||||
<string name="compose_settings_root_content_discovery_description_appstore">Eklentileri yönet ve Nuvio’da nelerin görüneceğini seç.</string>
|
||||
<string name="compose_settings_root_downloads_description">İndirdiğin film ve bölümleri yönet.</string>
|
||||
<string name="compose_settings_root_downloads_description_appstore">Bu cihaza kaydettiklerini yönet.</string>
|
||||
<string name="compose_settings_root_downloads_title">İndirilenler</string>
|
||||
<string name="compose_settings_root_general_section">GENEL</string>
|
||||
<string name="compose_settings_root_integrations_description">TMDB ve MDBList servislerini bağla.</string>
|
||||
|
|
@ -587,6 +596,7 @@
|
|||
<string name="settings_content_discovery_section_home">ANA SAYFA</string>
|
||||
<string name="settings_content_discovery_section_sources">KAYNAKLAR</string>
|
||||
<string name="settings_content_discovery_addons_description">İçerik kaynaklarını kur, kaldır, yenile ve sırala.</string>
|
||||
<string name="settings_content_discovery_addons_description_appstore">Nuvio ile kullandığın eklentileri ekle ve yönet.</string>
|
||||
<string name="settings_content_discovery_plugins_description">JavaScript scraper depoları kur ve sağlayıcıları içeriden test et.</string>
|
||||
<string name="settings_content_discovery_homescreen_description">Ana sayfada hangi katalogların hangi sırayla görüneceğini kontrol et.</string>
|
||||
<string name="settings_content_discovery_meta_screen_description">Detay bölümlerini kapat ve öne çıkanların altındaki her şeyi yeniden sırala.</string>
|
||||
|
|
|
|||
|
|
@ -32,11 +32,13 @@
|
|||
<string name="addons_badge_unavailable">Unavailable</string>
|
||||
<string name="addons_configure">Configure addon</string>
|
||||
<string name="addons_delete">Delete addon</string>
|
||||
<string name="addons_appstore_add_description">Connect your own media server to browse and play from your personal library.</string>
|
||||
<string name="addons_appstore_empty_subtitle">Add a server URL above when you want Nuvio to show your private library.</string>
|
||||
<string name="addons_appstore_empty_title">No personal libraries connected.</string>
|
||||
<string name="addons_appstore_input_placeholder">Server URL</string>
|
||||
<string name="addons_appstore_install_button">Add</string>
|
||||
<string name="addons_appstore_add_description">Bring your library into Nuvio with the addons you use.</string>
|
||||
<string name="addons_appstore_empty_subtitle">Add an addon to start building your library.</string>
|
||||
<string name="addons_appstore_empty_title">Make Nuvio yours.</string>
|
||||
<string name="addons_appstore_input_placeholder">Addon URL</string>
|
||||
<string name="addons_appstore_install_button">Add Addon</string>
|
||||
<string name="store_empty_unavailable_title">Nothing here yet</string>
|
||||
<string name="store_empty_unavailable_message">Your current addons don’t have anything to show here.</string>
|
||||
<string name="addons_empty_subtitle">Add a manifest URL to start loading catalogs, metadata, streams or subtitles into Nuvio.</string>
|
||||
<string name="addons_empty_title">No addons installed yet.</string>
|
||||
<string name="addons_error_enter_url">Enter an addon URL.</string>
|
||||
|
|
@ -442,7 +444,9 @@
|
|||
<string name="compose_settings_root_check_updates_description">Download latest release</string>
|
||||
<string name="compose_settings_root_check_updates_title">Check for updates</string>
|
||||
<string name="compose_settings_root_content_discovery_description">Manage addons and discovery sources.</string>
|
||||
<string name="compose_settings_root_content_discovery_description_appstore">Manage addons and choose what appears in Nuvio.</string>
|
||||
<string name="compose_settings_root_downloads_description">Manage your downloaded movies and episodes.</string>
|
||||
<string name="compose_settings_root_downloads_description_appstore">Manage what you’ve saved on this device.</string>
|
||||
<string name="compose_settings_root_downloads_title">Downloads</string>
|
||||
<string name="compose_settings_root_general_section">GENERAL</string>
|
||||
<string name="compose_settings_root_integrations_description">Manage available integrations</string>
|
||||
|
|
@ -601,6 +605,37 @@
|
|||
<string name="settings_homescreen_visible">Visible</string>
|
||||
<string name="settings_hide_secret">Hide value</string>
|
||||
<string name="settings_playback_subtitle">Player, subtitles, and auto-play</string>
|
||||
<string name="settings_card_depth_title">Card Depth Effect</string>
|
||||
<string name="settings_card_depth_description">Adds a lit top edge and a soft sheen to image cards for a subtle sense of depth.</string>
|
||||
<string name="settings_card_depth_enabled">Enable depth effect</string>
|
||||
<string name="settings_card_depth_edge">Edge glow</string>
|
||||
<string name="settings_card_depth_edge_subtle">Subtle</string>
|
||||
<string name="settings_card_depth_edge_balanced">Balanced</string>
|
||||
<string name="settings_card_depth_edge_bold">Bold</string>
|
||||
<string name="settings_card_depth_sheen">Top sheen</string>
|
||||
<string name="settings_card_depth_sheen_off">Off</string>
|
||||
<string name="settings_card_depth_sheen_soft">Soft</string>
|
||||
<string name="settings_card_depth_sheen_bright">Bright</string>
|
||||
<string name="settings_card_depth_apply_to">Apply to</string>
|
||||
<string name="settings_card_depth_fine_tune">Fine-tune</string>
|
||||
<string name="settings_card_depth_fine_tune_title">Fine-tune Depth</string>
|
||||
<string name="settings_card_depth_fine_tune_hint">Drag the dot right for a brighter edge, up for more sheen. Use the slider to extend the glow around the full outline.</string>
|
||||
<string name="settings_card_depth_pad_edge_axis">Edge glow →</string>
|
||||
<string name="settings_card_depth_pad_sheen_axis">↑ Sheen</string>
|
||||
<string name="settings_card_depth_edge_value">Edge glow</string>
|
||||
<string name="settings_card_depth_sheen_value">Sheen</string>
|
||||
<string name="settings_card_depth_edge_coverage">Edge coverage</string>
|
||||
<string name="settings_card_depth_coverage_top">Top only</string>
|
||||
<string name="settings_card_depth_coverage_half">Half</string>
|
||||
<string name="settings_card_depth_coverage_full">Full outline</string>
|
||||
<string name="settings_card_depth_coverage_value">Edge coverage</string>
|
||||
<string name="settings_card_depth_preview_title">Episode Title</string>
|
||||
<string name="settings_card_depth_preview_meta">S1 · E1 · 45 min</string>
|
||||
<string name="settings_card_depth_surface_posters">Posters</string>
|
||||
<string name="settings_card_depth_surface_continue_watching">Continue Watching</string>
|
||||
<string name="settings_card_depth_surface_episodes">Episode cards</string>
|
||||
<string name="settings_card_depth_surface_cast">Cast</string>
|
||||
<string name="settings_card_depth_surface_trailers">Trailers</string>
|
||||
<string name="settings_poster_card_radius">Corner Radius</string>
|
||||
<string name="settings_poster_card_style">Poster Card Style</string>
|
||||
<string name="settings_poster_card_width">Width</string>
|
||||
|
|
@ -656,7 +691,7 @@
|
|||
<string name="settings_content_discovery_section_home">HOME</string>
|
||||
<string name="settings_content_discovery_section_sources">SOURCES</string>
|
||||
<string name="settings_content_discovery_addons_description">Install, remove, refresh, and sort your content sources.</string>
|
||||
<string name="settings_content_discovery_addons_description_appstore">Connect personal media sources and manage access to your own library.</string>
|
||||
<string name="settings_content_discovery_addons_description_appstore">Add and manage the addons you use with Nuvio.</string>
|
||||
<string name="settings_content_discovery_plugins_description">Install JavaScript scraper repositories and test providers internally.</string>
|
||||
<string name="settings_content_discovery_homescreen_description">Adjust home layout, content visibility, and poster behavior</string>
|
||||
<string name="settings_content_discovery_meta_screen_description">Settings for the detail and episode screens.</string>
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import com.nuvio.app.features.streams.StreamLaunchStore
|
|||
import com.nuvio.app.features.streams.StreamsRepository
|
||||
import com.nuvio.app.features.trakt.TraktAuthRepository
|
||||
import com.nuvio.app.features.trakt.TraktSettingsRepository
|
||||
import com.nuvio.app.core.ui.CardDepthStyleRepository
|
||||
import com.nuvio.app.core.ui.PosterCardStyleRepository
|
||||
import com.nuvio.app.features.watchprogress.ContinueWatchingPreferencesRepository
|
||||
import com.nuvio.app.features.watchprogress.ContinueWatchingEnrichmentCache
|
||||
|
|
@ -61,6 +62,7 @@ internal object LocalAccountDataCleaner {
|
|||
CollectionRepository.clearLocalState()
|
||||
ThemeSettingsRepository.clearLocalState()
|
||||
PosterCardStyleRepository.clearLocalState()
|
||||
CardDepthStyleRepository.clearLocalState()
|
||||
TraktAuthRepository.clearLocalState()
|
||||
TraktSettingsRepository.clearLocalState()
|
||||
PlayerSettingsRepository.clearLocalState()
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ import com.nuvio.app.features.notifications.EpisodeReleaseNotificationsRepositor
|
|||
import com.nuvio.app.features.player.PlayerSettingsStorage
|
||||
import com.nuvio.app.features.player.PlayerSettingsRepository
|
||||
import com.nuvio.app.features.profiles.ProfileRepository
|
||||
import com.nuvio.app.core.ui.CardDepthStyleRepository
|
||||
import com.nuvio.app.core.ui.CardDepthStyleStorage
|
||||
import com.nuvio.app.core.ui.PosterCardStyleRepository
|
||||
import com.nuvio.app.core.ui.PosterCardStyleStorage
|
||||
import com.nuvio.app.features.settings.ThemeSettingsStorage
|
||||
|
|
@ -27,7 +29,6 @@ import com.nuvio.app.features.tmdb.TmdbSettingsStorage
|
|||
import com.nuvio.app.features.tmdb.TmdbSettingsRepository
|
||||
import com.nuvio.app.features.trakt.TraktCommentsStorage
|
||||
import com.nuvio.app.features.trakt.TraktCommentsSettings
|
||||
import com.nuvio.app.features.trakt.ProfileSettingsWatchSourceOutbox
|
||||
import com.nuvio.app.features.trakt.TraktSettingsStorage
|
||||
import com.nuvio.app.features.trakt.TraktSettingsRepository
|
||||
import com.nuvio.app.features.watchprogress.ContinueWatchingPreferencesStorage
|
||||
|
|
@ -35,21 +36,16 @@ import com.nuvio.app.features.watchprogress.ContinueWatchingPreferencesRepositor
|
|||
import io.github.jan.supabase.postgrest.postgrest
|
||||
import io.github.jan.supabase.postgrest.rpc
|
||||
import kotlin.concurrent.Volatile
|
||||
import kotlinx.atomicfu.locks.SynchronizedObject
|
||||
import kotlinx.atomicfu.locks.synchronized
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.debounce
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.drop
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
|
|
@ -65,22 +61,10 @@ import kotlinx.serialization.json.put
|
|||
|
||||
private const val PUSH_DEBOUNCE_MS = 1500L
|
||||
|
||||
private data class ObservedProfileSettingsChange(
|
||||
val signature: String,
|
||||
val accountId: String?,
|
||||
)
|
||||
|
||||
private data class SkippedProfileSettingsPush(
|
||||
val signature: String,
|
||||
val accountId: String?,
|
||||
val profileId: Int,
|
||||
)
|
||||
|
||||
object ProfileSettingsSync {
|
||||
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
|
||||
private val log = Logger.withTag("ProfileSettingsSync")
|
||||
private val syncMutex = Mutex()
|
||||
private val observeLock = SynchronizedObject()
|
||||
private val json = Json {
|
||||
ignoreUnknownKeys = true
|
||||
encodeDefaults = true
|
||||
|
|
@ -93,62 +77,33 @@ object ProfileSettingsSync {
|
|||
private var isServerSyncInFlight: Boolean = false
|
||||
|
||||
@Volatile
|
||||
private var skipNextPush: SkippedProfileSettingsPush? = null
|
||||
|
||||
@Volatile
|
||||
private var pushEnabledAccountId: String? = null
|
||||
|
||||
@Volatile
|
||||
private var pendingLocalPush: SkippedProfileSettingsPush? = null
|
||||
private var skipNextPushSignature: String? = null
|
||||
|
||||
private var observeJob: Job? = null
|
||||
private var pendingPushRetryJob: Job? = null
|
||||
|
||||
fun startObserving() = synchronized(observeLock) {
|
||||
if (observeJob?.isActive == true) return@synchronized
|
||||
fun startObserving() {
|
||||
if (observeJob?.isActive == true) return
|
||||
ensureRepositoriesLoaded()
|
||||
observeLocalChangesAndPush()
|
||||
}
|
||||
|
||||
fun clearAccountState() {
|
||||
synchronized(observeLock) {
|
||||
observeJob?.cancel()
|
||||
observeJob = null
|
||||
}
|
||||
skipNextPush = null
|
||||
pushEnabledAccountId = null
|
||||
pendingLocalPush = null
|
||||
pendingPushRetryJob?.cancel()
|
||||
pendingPushRetryJob = null
|
||||
observeJob?.cancel()
|
||||
observeJob = null
|
||||
skipNextPushSignature = null
|
||||
}
|
||||
|
||||
suspend fun pull(profileId: Int): Boolean {
|
||||
startObserving()
|
||||
val accountId = currentCloudAccountId() ?: return false
|
||||
ensureRepositoriesLoaded()
|
||||
return syncMutex.withLock {
|
||||
if (!isCurrentSyncTarget(profileId = profileId, accountId = accountId)) {
|
||||
if (ProfileRepository.activeProfileId != profileId) {
|
||||
log.d { "pull(profileId=$profileId) — skipped because profile is no longer active" }
|
||||
return@withLock false
|
||||
}
|
||||
isServerSyncInFlight = true
|
||||
try {
|
||||
pushEnabledAccountId = accountId
|
||||
hydrateDurableWatchSourcePush(profileId = profileId, accountId = accountId)
|
||||
if (
|
||||
!pushCurrentStateLocked(
|
||||
profileId = profileId,
|
||||
accountId = accountId,
|
||||
forceCurrentState = false,
|
||||
)
|
||||
) {
|
||||
schedulePendingPushRetry()
|
||||
return@withLock false
|
||||
}
|
||||
val observedSignatureAtStart = currentObservedStateSignature()
|
||||
val localBlob = exportSettingsBlob()
|
||||
if (!isCurrentSyncTarget(profileId = profileId, accountId = accountId)) {
|
||||
throw CancellationException("Profile settings pull target changed")
|
||||
}
|
||||
if (ProfileRepository.activeProfileId != profileId) return@withLock false
|
||||
val localSignature = buildSignature(localBlob)
|
||||
|
||||
val params = buildJsonObject {
|
||||
|
|
@ -156,105 +111,41 @@ object ProfileSettingsSync {
|
|||
put("p_platform", MOBILE_SYNC_PLATFORM)
|
||||
}
|
||||
val result = SupabaseProvider.client.postgrest.rpc("sync_pull_profile_settings_blob", params)
|
||||
if (!isCurrentSyncTarget(profileId = profileId, accountId = accountId)) {
|
||||
throw CancellationException("Profile settings pull target changed")
|
||||
}
|
||||
if (ProfileRepository.activeProfileId != profileId) return@withLock false
|
||||
val response = result.decodeList<SettingsBlobResponse>().firstOrNull()
|
||||
val remoteJson = response?.settingsJson
|
||||
|
||||
val pendingDuringPull = pendingLocalPush?.let { pending ->
|
||||
pending.accountId == accountId && pending.profileId == profileId
|
||||
} == true
|
||||
val durableSourceChangeDuringPull =
|
||||
ProfileSettingsWatchSourceOutbox.pendingFor(accountId, profileId) != null
|
||||
if (
|
||||
pendingDuringPull ||
|
||||
durableSourceChangeDuringPull ||
|
||||
currentObservedStateSignature() != observedSignatureAtStart
|
||||
) {
|
||||
if (
|
||||
!pushCurrentStateLocked(
|
||||
profileId = profileId,
|
||||
accountId = accountId,
|
||||
forceCurrentState = true,
|
||||
)
|
||||
) {
|
||||
schedulePendingPushRetry()
|
||||
}
|
||||
return@withLock false
|
||||
}
|
||||
|
||||
if (remoteJson == null) {
|
||||
log.i { "pull(profileId=$profileId) — no remote settings blob found" }
|
||||
if (localSignature != defaultSignature()) {
|
||||
pushToRemoteLocked(profileId, localBlob, accountId)
|
||||
}
|
||||
pushEnabledAccountId = accountId
|
||||
return@withLock false
|
||||
}
|
||||
|
||||
val remoteBlob = try {
|
||||
json.decodeFromJsonElement(MobileProfileSettingsBlob.serializer(), remoteJson)
|
||||
} catch (error: Throwable) {
|
||||
log.e(error) { "pull(profileId=$profileId) — failed to decode remote settings blob" }
|
||||
throw error
|
||||
}
|
||||
|
||||
var restoredPendingSourceAfterRemoteApply = false
|
||||
isApplyingRemoteBlob = true
|
||||
try {
|
||||
val remoteBlob = runCatching {
|
||||
json.decodeFromJsonElement(MobileProfileSettingsBlob.serializer(), remoteJson)
|
||||
}.getOrElse { error ->
|
||||
log.e(error) { "pull(profileId=$profileId) — failed to decode remote settings blob" }
|
||||
return@withLock false
|
||||
}
|
||||
val remoteSignature = buildSignature(remoteBlob)
|
||||
if (remoteSignature == localSignature) {
|
||||
log.d { "pull(profileId=$profileId) — remote matches local" }
|
||||
pushEnabledAccountId = accountId
|
||||
return@withLock false
|
||||
}
|
||||
|
||||
if (!isCurrentSyncTarget(profileId = profileId, accountId = accountId)) {
|
||||
throw CancellationException("Profile settings pull target changed")
|
||||
}
|
||||
if (ProfileRepository.activeProfileId != profileId) return@withLock false
|
||||
applyRemoteBlob(remoteBlob)
|
||||
ProfileSettingsWatchSourceOutbox.pendingFor(accountId, profileId)?.let { pendingSource ->
|
||||
if (TraktSettingsRepository.uiState.value.watchProgressSource != pendingSource.source) {
|
||||
TraktSettingsRepository.setWatchProgressSource(pendingSource.source, profileId)
|
||||
}
|
||||
restoredPendingSourceAfterRemoteApply = true
|
||||
}
|
||||
skipNextPush = SkippedProfileSettingsPush(
|
||||
signature = currentObservedStateSignature(),
|
||||
accountId = currentCloudAccountId(),
|
||||
profileId = profileId,
|
||||
)
|
||||
skipNextPushSignature = currentObservedStateSignature()
|
||||
} finally {
|
||||
isApplyingRemoteBlob = false
|
||||
}
|
||||
|
||||
if (restoredPendingSourceAfterRemoteApply) {
|
||||
pendingLocalPush = SkippedProfileSettingsPush(
|
||||
signature = currentObservedStateSignature(),
|
||||
accountId = accountId,
|
||||
profileId = profileId,
|
||||
)
|
||||
if (
|
||||
!pushCurrentStateLocked(
|
||||
profileId = profileId,
|
||||
accountId = accountId,
|
||||
forceCurrentState = true,
|
||||
)
|
||||
) {
|
||||
schedulePendingPushRetry()
|
||||
}
|
||||
return@withLock false
|
||||
}
|
||||
|
||||
log.i { "pull(profileId=$profileId) — applied remote settings blob" }
|
||||
pushEnabledAccountId = accountId
|
||||
true
|
||||
} catch (error: CancellationException) {
|
||||
throw error
|
||||
} catch (error: Exception) {
|
||||
log.e(error) { "pull(profileId=$profileId) — FAILED" }
|
||||
throw error
|
||||
false
|
||||
} finally {
|
||||
isServerSyncInFlight = false
|
||||
}
|
||||
|
|
@ -263,22 +154,16 @@ object ProfileSettingsSync {
|
|||
|
||||
suspend fun pushCurrentProfileToRemote(): Boolean {
|
||||
ensureRepositoriesLoaded()
|
||||
val accountId = currentCloudAccountId() ?: return false
|
||||
return syncMutex.withLock {
|
||||
try {
|
||||
runCatching {
|
||||
val profileId = ProfileRepository.activeProfileId
|
||||
if (!isCurrentSyncTarget(profileId = profileId, accountId = accountId)) return@withLock false
|
||||
pushCurrentStateLocked(
|
||||
profileId = profileId,
|
||||
accountId = accountId,
|
||||
forceCurrentState = true,
|
||||
)
|
||||
} catch (error: CancellationException) {
|
||||
throw error
|
||||
} catch (error: Throwable) {
|
||||
val blob = exportSettingsBlob()
|
||||
if (ProfileRepository.activeProfileId != profileId) return@runCatching false
|
||||
pushToRemoteLocked(profileId, blob)
|
||||
true
|
||||
}.onFailure { error ->
|
||||
log.e(error) { "pushCurrentProfileToRemote() — FAILED" }
|
||||
false
|
||||
}
|
||||
}.getOrDefault(false)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -289,6 +174,7 @@ object ProfileSettingsSync {
|
|||
ThemeSettingsRepository.amoledEnabled.map { "amoled" },
|
||||
ThemeSettingsRepository.liquidGlassNativeTabBarEnabled.map { "liquid_glass_tab_bar" },
|
||||
PosterCardStyleRepository.uiState.map { "poster_card_style" },
|
||||
CardDepthStyleRepository.uiState.map { "card_depth_style" },
|
||||
PlayerSettingsRepository.uiState.map { "player" },
|
||||
StreamBadgeSettingsRepository.uiState.map { "stream_badges" },
|
||||
DebridSettingsRepository.uiState.map { "debrid" },
|
||||
|
|
@ -303,84 +189,24 @@ object ProfileSettingsSync {
|
|||
)
|
||||
|
||||
observeJob = scope.launch {
|
||||
combine(signatureFlows) {
|
||||
ObservedProfileSettingsChange(
|
||||
signature = currentObservedStateSignature(),
|
||||
accountId = currentCloudAccountId(),
|
||||
)
|
||||
}
|
||||
combine(signatureFlows) { currentObservedStateSignature() }
|
||||
.drop(1)
|
||||
.distinctUntilChanged()
|
||||
.onEach { change ->
|
||||
val authState = AuthRepository.state.value
|
||||
if (authState !is AuthState.Authenticated || authState.isAnonymous) return@onEach
|
||||
if (change.accountId == null || change.accountId != authState.userId) return@onEach
|
||||
val observedChange = SkippedProfileSettingsPush(
|
||||
signature = change.signature,
|
||||
accountId = change.accountId,
|
||||
profileId = ProfileRepository.activeProfileId,
|
||||
)
|
||||
if (skipNextPush != observedChange) {
|
||||
pendingLocalPush = observedChange
|
||||
schedulePendingPushRetry()
|
||||
}
|
||||
}
|
||||
.debounce(PUSH_DEBOUNCE_MS)
|
||||
.collect { change ->
|
||||
.collect { signature ->
|
||||
val authState = AuthRepository.state.value
|
||||
if (authState !is AuthState.Authenticated || authState.isAnonymous) return@collect
|
||||
if (change.accountId == null || change.accountId != authState.userId) return@collect
|
||||
val profileId = ProfileRepository.activeProfileId
|
||||
val observedChange = SkippedProfileSettingsPush(
|
||||
signature = change.signature,
|
||||
accountId = change.accountId,
|
||||
profileId = profileId,
|
||||
)
|
||||
if (skipNextPush == observedChange) {
|
||||
skipNextPush = null
|
||||
if (pendingLocalPush == observedChange) {
|
||||
pendingLocalPush = null
|
||||
}
|
||||
if (isApplyingRemoteBlob || isServerSyncInFlight) return@collect
|
||||
if (signature == skipNextPushSignature) {
|
||||
skipNextPushSignature = null
|
||||
return@collect
|
||||
}
|
||||
pendingLocalPush = observedChange
|
||||
if (pushEnabledAccountId != change.accountId) return@collect
|
||||
if (isApplyingRemoteBlob || isServerSyncInFlight) return@collect
|
||||
pushCurrentProfileToRemote()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun schedulePendingPushRetry() {
|
||||
if (pendingPushRetryJob?.isActive == true) return
|
||||
pendingPushRetryJob = scope.launch {
|
||||
var retryDelayMs = 5_000L
|
||||
while (pendingLocalPush != null) {
|
||||
delay(retryDelayMs)
|
||||
val pending = pendingLocalPush ?: break
|
||||
if (
|
||||
pending.accountId == currentCloudAccountId() &&
|
||||
pending.profileId == ProfileRepository.activeProfileId &&
|
||||
pushEnabledAccountId == pending.accountId &&
|
||||
!isApplyingRemoteBlob &&
|
||||
!isServerSyncInFlight &&
|
||||
pushCurrentProfileToRemote()
|
||||
) {
|
||||
break
|
||||
}
|
||||
retryDelayMs = (retryDelayMs * 2L).coerceAtMost(60_000L)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun pushToRemoteLocked(
|
||||
profileId: Int,
|
||||
blob: MobileProfileSettingsBlob,
|
||||
accountId: String,
|
||||
) {
|
||||
if (!isCurrentSyncTarget(profileId = profileId, accountId = accountId)) {
|
||||
throw CancellationException("Profile settings push target changed")
|
||||
}
|
||||
private suspend fun pushToRemoteLocked(profileId: Int, blob: MobileProfileSettingsBlob) {
|
||||
val params = buildJsonObject {
|
||||
put("p_profile_id", profileId)
|
||||
put("p_platform", MOBILE_SYNC_PLATFORM)
|
||||
|
|
@ -388,80 +214,16 @@ object ProfileSettingsSync {
|
|||
putSyncOriginClientId()
|
||||
}
|
||||
SupabaseProvider.client.postgrest.rpc("sync_push_profile_settings_blob", params)
|
||||
if (!isCurrentSyncTarget(profileId = profileId, accountId = accountId)) {
|
||||
throw CancellationException("Profile settings push target changed")
|
||||
}
|
||||
log.d { "pushToRemoteLocked(profileId=$profileId) — success" }
|
||||
}
|
||||
|
||||
private fun hydrateDurableWatchSourcePush(profileId: Int, accountId: String) {
|
||||
val durableChange = ProfileSettingsWatchSourceOutbox.pendingFor(accountId, profileId) ?: return
|
||||
if (TraktSettingsRepository.uiState.value.watchProgressSource != durableChange.source) {
|
||||
TraktSettingsRepository.setWatchProgressSource(durableChange.source, profileId)
|
||||
}
|
||||
pendingLocalPush = SkippedProfileSettingsPush(
|
||||
signature = currentObservedStateSignature(),
|
||||
accountId = accountId,
|
||||
profileId = profileId,
|
||||
)
|
||||
schedulePendingPushRetry()
|
||||
}
|
||||
|
||||
private suspend fun pushCurrentStateLocked(
|
||||
profileId: Int,
|
||||
accountId: String,
|
||||
forceCurrentState: Boolean,
|
||||
): Boolean {
|
||||
val durableChange = ProfileSettingsWatchSourceOutbox.pendingFor(accountId, profileId)
|
||||
val inMemoryChange = pendingLocalPush?.takeIf { pending ->
|
||||
pending.accountId == accountId && pending.profileId == profileId
|
||||
}
|
||||
if (!forceCurrentState && durableChange == null && inMemoryChange == null) return true
|
||||
|
||||
val signature = currentObservedStateSignature()
|
||||
pushToRemoteLocked(profileId, exportSettingsBlob(), accountId)
|
||||
if (currentObservedStateSignature() != signature) {
|
||||
pendingLocalPush = SkippedProfileSettingsPush(
|
||||
signature = currentObservedStateSignature(),
|
||||
accountId = accountId,
|
||||
profileId = profileId,
|
||||
)
|
||||
schedulePendingPushRetry()
|
||||
return false
|
||||
}
|
||||
|
||||
val pushedChange = SkippedProfileSettingsPush(
|
||||
signature = signature,
|
||||
accountId = accountId,
|
||||
profileId = profileId,
|
||||
)
|
||||
if (pendingLocalPush == pushedChange) {
|
||||
pendingLocalPush = null
|
||||
}
|
||||
if (
|
||||
durableChange != null &&
|
||||
TraktSettingsRepository.uiState.value.watchProgressSource == durableChange.source
|
||||
) {
|
||||
ProfileSettingsWatchSourceOutbox.clearIfMatches(durableChange)
|
||||
}
|
||||
|
||||
val durablePushRemains = ProfileSettingsWatchSourceOutbox.pendingFor(accountId, profileId) != null
|
||||
val memoryPushRemains = pendingLocalPush?.let { pending ->
|
||||
pending.accountId == accountId && pending.profileId == profileId
|
||||
} == true
|
||||
if (durablePushRemains || memoryPushRemains) {
|
||||
schedulePendingPushRetry()
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private fun exportSettingsBlob(): MobileProfileSettingsBlob {
|
||||
ensureRepositoriesLoaded()
|
||||
return MobileProfileSettingsBlob(
|
||||
features = MobileProfileSettingsFeatures(
|
||||
themeSettings = ThemeSettingsStorage.exportToSyncPayload(),
|
||||
posterCardStyleSettingsPayload = PosterCardStyleStorage.loadPayload().orEmpty().trim(),
|
||||
cardDepthStyleSettingsPayload = CardDepthStyleStorage.loadPayload().orEmpty().trim(),
|
||||
playerSettings = PlayerSettingsStorage.exportToSyncPayload(),
|
||||
streamBadgeSettings = StreamBadgeSettingsStorage.exportToSyncPayload(),
|
||||
debridSettings = DebridSettingsStorage.exportToSyncPayload(),
|
||||
|
|
@ -486,6 +248,9 @@ object ProfileSettingsSync {
|
|||
PosterCardStyleStorage.savePayload(blob.features.posterCardStyleSettingsPayload)
|
||||
PosterCardStyleRepository.onProfileChanged()
|
||||
|
||||
CardDepthStyleStorage.savePayload(blob.features.cardDepthStyleSettingsPayload)
|
||||
CardDepthStyleRepository.onProfileChanged()
|
||||
|
||||
PlayerSettingsStorage.replaceFromSyncPayload(blob.features.playerSettings)
|
||||
PlayerSettingsRepository.onProfileChanged()
|
||||
|
||||
|
|
@ -523,6 +288,7 @@ object ProfileSettingsSync {
|
|||
private fun ensureRepositoriesLoaded() {
|
||||
ThemeSettingsRepository.ensureLoaded()
|
||||
PosterCardStyleRepository.ensureLoaded()
|
||||
CardDepthStyleRepository.ensureLoaded()
|
||||
PlayerSettingsRepository.ensureLoaded()
|
||||
StreamBadgeSettingsRepository.ensureLoaded()
|
||||
DebridSettingsRepository.ensureLoaded()
|
||||
|
|
@ -539,14 +305,12 @@ object ProfileSettingsSync {
|
|||
private fun buildSignature(blob: MobileProfileSettingsBlob): String =
|
||||
json.encodeToString(MobileProfileSettingsBlob.serializer(), blob)
|
||||
|
||||
private fun defaultSignature(): String =
|
||||
buildSignature(MobileProfileSettingsBlob())
|
||||
|
||||
private fun currentObservedStateSignature(): String = listOf(
|
||||
"theme=${ThemeSettingsRepository.selectedTheme.value.name}",
|
||||
"amoled=${ThemeSettingsRepository.amoledEnabled.value}",
|
||||
"liquid_glass_tab_bar=${ThemeSettingsRepository.liquidGlassNativeTabBarEnabled.value}",
|
||||
"poster_card_style=${PosterCardStyleRepository.uiState.value}",
|
||||
"card_depth_style=${CardDepthStyleRepository.uiState.value}",
|
||||
"player=${PlayerSettingsRepository.uiState.value}",
|
||||
"stream_badges=${StreamBadgeSettingsRepository.uiState.value}",
|
||||
"debrid=${DebridSettingsRepository.uiState.value}",
|
||||
|
|
@ -560,13 +324,6 @@ object ProfileSettingsSync {
|
|||
"episode_release_alerts=${EpisodeReleaseNotificationsRepository.uiState.value.isEnabled}",
|
||||
).joinToString(separator = "||")
|
||||
|
||||
private fun currentCloudAccountId(): String? =
|
||||
(AuthRepository.state.value as? AuthState.Authenticated)
|
||||
?.takeUnless { it.isAnonymous }
|
||||
?.userId
|
||||
|
||||
private fun isCurrentSyncTarget(profileId: Int, accountId: String): Boolean =
|
||||
ProfileRepository.activeProfileId == profileId && currentCloudAccountId() == accountId
|
||||
}
|
||||
|
||||
@Serializable
|
||||
|
|
@ -579,6 +336,7 @@ private data class MobileProfileSettingsBlob(
|
|||
private data class MobileProfileSettingsFeatures(
|
||||
@SerialName("theme_settings") val themeSettings: JsonObject = JsonObject(emptyMap()),
|
||||
@SerialName("poster_card_style_settings_payload") val posterCardStyleSettingsPayload: String = "",
|
||||
@SerialName("card_depth_style_settings_payload") val cardDepthStyleSettingsPayload: String = "",
|
||||
@SerialName("player_settings") val playerSettings: JsonObject = JsonObject(emptyMap()),
|
||||
@SerialName("stream_badge_settings") val streamBadgeSettings: JsonObject = JsonObject(emptyMap()),
|
||||
@SerialName("debrid_settings") val debridSettings: JsonObject = JsonObject(emptyMap()),
|
||||
|
|
|
|||
|
|
@ -0,0 +1,96 @@
|
|||
package com.nuvio.app.core.ui
|
||||
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.drawWithContent
|
||||
import androidx.compose.ui.geometry.Size
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
@Composable
|
||||
fun rememberCardDepthStyleUiState(): CardDepthStyleUiState {
|
||||
CardDepthStyleRepository.ensureLoaded()
|
||||
val uiState by CardDepthStyleRepository.uiState.collectAsState()
|
||||
return uiState
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Modifier.nuvioCardDepth(
|
||||
shape: Shape,
|
||||
surface: NuvioCardDepthSurface,
|
||||
fallbackBorderAlpha: Float = 0f,
|
||||
): Modifier {
|
||||
val state = rememberCardDepthStyleUiState()
|
||||
if (!state.isEnabledFor(surface)) {
|
||||
return if (fallbackBorderAlpha > 0f) {
|
||||
border(
|
||||
width = 1.dp,
|
||||
color = Color.White.copy(alpha = fallbackBorderAlpha),
|
||||
shape = shape,
|
||||
)
|
||||
} else {
|
||||
this
|
||||
}
|
||||
}
|
||||
|
||||
return cardDepthVisual(
|
||||
shape = shape,
|
||||
edgeStrength = state.edgeStrength.toFloat(),
|
||||
sheenStrength = state.sheenStrength.toFloat(),
|
||||
edgeCoverage = state.edgeCoverage.toFloat(),
|
||||
)
|
||||
}
|
||||
|
||||
fun Modifier.cardDepthVisual(
|
||||
shape: Shape,
|
||||
edgeStrength: Float,
|
||||
sheenStrength: Float,
|
||||
edgeCoverage: Float = DefaultCardDepthEdgeCoverage.toFloat(),
|
||||
): Modifier {
|
||||
val edgeTop = edgeStrength.coerceIn(0f, 100f) / 100f
|
||||
val sheen = sheenStrength.coerceIn(0f, 100f) / 100f
|
||||
val coverage = edgeCoverage.coerceIn(0f, 100f) / 100f
|
||||
|
||||
val withEdge = if (edgeTop > 0f) {
|
||||
border(
|
||||
width = 1.dp,
|
||||
brush = Brush.verticalGradient(
|
||||
colors = listOf(
|
||||
Color.White.copy(alpha = edgeTop),
|
||||
Color.White.copy(alpha = edgeTop * (0.33f + 0.67f * coverage)),
|
||||
Color.White.copy(alpha = edgeTop * coverage),
|
||||
),
|
||||
),
|
||||
shape = shape,
|
||||
)
|
||||
} else {
|
||||
this
|
||||
}
|
||||
|
||||
return if (sheen > 0f) {
|
||||
withEdge.drawWithContent {
|
||||
drawContent()
|
||||
val sheenHeight = size.height * 0.22f
|
||||
if (sheenHeight > 0f) {
|
||||
drawRect(
|
||||
brush = Brush.verticalGradient(
|
||||
colors = listOf(
|
||||
Color.White.copy(alpha = sheen),
|
||||
Color.Transparent,
|
||||
),
|
||||
startY = 0f,
|
||||
endY = sheenHeight,
|
||||
),
|
||||
size = Size(size.width, sheenHeight),
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
withEdge
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,175 @@
|
|||
package com.nuvio.app.core.ui
|
||||
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
internal const val DefaultCardDepthEdgeStrength = 28
|
||||
internal const val DefaultCardDepthSheenStrength = 10
|
||||
internal const val DefaultCardDepthEdgeCoverage = 0
|
||||
|
||||
enum class NuvioCardDepthSurface {
|
||||
Posters,
|
||||
ContinueWatching,
|
||||
EpisodeCards,
|
||||
Cast,
|
||||
Trailers,
|
||||
}
|
||||
|
||||
@Serializable
|
||||
private data class StoredCardDepthStylePreferences(
|
||||
val enabled: Boolean = false,
|
||||
val edgeStrength: Int = DefaultCardDepthEdgeStrength,
|
||||
val sheenStrength: Int = DefaultCardDepthSheenStrength,
|
||||
val edgeCoverage: Int = DefaultCardDepthEdgeCoverage,
|
||||
val postersEnabled: Boolean = true,
|
||||
val continueWatchingEnabled: Boolean = true,
|
||||
val episodeCardsEnabled: Boolean = true,
|
||||
val castEnabled: Boolean = true,
|
||||
val trailersEnabled: Boolean = true,
|
||||
)
|
||||
|
||||
data class CardDepthStyleUiState(
|
||||
val enabled: Boolean = false,
|
||||
val edgeStrength: Int = DefaultCardDepthEdgeStrength,
|
||||
val sheenStrength: Int = DefaultCardDepthSheenStrength,
|
||||
val edgeCoverage: Int = DefaultCardDepthEdgeCoverage,
|
||||
val postersEnabled: Boolean = true,
|
||||
val continueWatchingEnabled: Boolean = true,
|
||||
val episodeCardsEnabled: Boolean = true,
|
||||
val castEnabled: Boolean = true,
|
||||
val trailersEnabled: Boolean = true,
|
||||
) {
|
||||
fun isEnabledFor(surface: NuvioCardDepthSurface): Boolean =
|
||||
enabled && isSurfaceEnabled(surface)
|
||||
|
||||
fun isSurfaceEnabled(surface: NuvioCardDepthSurface): Boolean =
|
||||
when (surface) {
|
||||
NuvioCardDepthSurface.Posters -> postersEnabled
|
||||
NuvioCardDepthSurface.ContinueWatching -> continueWatchingEnabled
|
||||
NuvioCardDepthSurface.EpisodeCards -> episodeCardsEnabled
|
||||
NuvioCardDepthSurface.Cast -> castEnabled
|
||||
NuvioCardDepthSurface.Trailers -> trailersEnabled
|
||||
}
|
||||
}
|
||||
|
||||
object CardDepthStyleRepository {
|
||||
private val json = Json {
|
||||
ignoreUnknownKeys = true
|
||||
encodeDefaults = true
|
||||
}
|
||||
|
||||
private val _uiState = MutableStateFlow(CardDepthStyleUiState())
|
||||
val uiState: StateFlow<CardDepthStyleUiState> = _uiState.asStateFlow()
|
||||
|
||||
private var hasLoaded = false
|
||||
|
||||
fun ensureLoaded() {
|
||||
if (hasLoaded) return
|
||||
loadFromDisk()
|
||||
}
|
||||
|
||||
fun onProfileChanged() {
|
||||
loadFromDisk()
|
||||
}
|
||||
|
||||
fun clearLocalState() {
|
||||
hasLoaded = false
|
||||
_uiState.value = CardDepthStyleUiState()
|
||||
}
|
||||
|
||||
fun setEnabled(enabled: Boolean) {
|
||||
update { it.copy(enabled = enabled) }
|
||||
}
|
||||
|
||||
fun setEdgeStrength(strength: Int) {
|
||||
update { it.copy(edgeStrength = strength.coerceIn(0, 100)) }
|
||||
}
|
||||
|
||||
fun setSheenStrength(strength: Int) {
|
||||
update { it.copy(sheenStrength = strength.coerceIn(0, 100)) }
|
||||
}
|
||||
|
||||
fun setEdgeCoverage(coverage: Int) {
|
||||
update { it.copy(edgeCoverage = coverage.coerceIn(0, 100)) }
|
||||
}
|
||||
|
||||
fun setSurfaceEnabled(surface: NuvioCardDepthSurface, enabled: Boolean) {
|
||||
update {
|
||||
when (surface) {
|
||||
NuvioCardDepthSurface.Posters -> it.copy(postersEnabled = enabled)
|
||||
NuvioCardDepthSurface.ContinueWatching -> it.copy(continueWatchingEnabled = enabled)
|
||||
NuvioCardDepthSurface.EpisodeCards -> it.copy(episodeCardsEnabled = enabled)
|
||||
NuvioCardDepthSurface.Cast -> it.copy(castEnabled = enabled)
|
||||
NuvioCardDepthSurface.Trailers -> it.copy(trailersEnabled = enabled)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun resetToDefaults() {
|
||||
ensureLoaded()
|
||||
if (_uiState.value == CardDepthStyleUiState()) return
|
||||
_uiState.value = CardDepthStyleUiState()
|
||||
persist()
|
||||
}
|
||||
|
||||
private fun update(transform: (CardDepthStyleUiState) -> CardDepthStyleUiState) {
|
||||
ensureLoaded()
|
||||
val next = transform(_uiState.value)
|
||||
if (_uiState.value == next) return
|
||||
_uiState.value = next
|
||||
persist()
|
||||
}
|
||||
|
||||
private fun loadFromDisk() {
|
||||
hasLoaded = true
|
||||
|
||||
val payload = CardDepthStyleStorage.loadPayload().orEmpty().trim()
|
||||
if (payload.isEmpty()) {
|
||||
_uiState.value = CardDepthStyleUiState()
|
||||
return
|
||||
}
|
||||
|
||||
val stored = runCatching {
|
||||
json.decodeFromString<StoredCardDepthStylePreferences>(payload)
|
||||
}.getOrNull()
|
||||
|
||||
_uiState.value = if (stored != null) {
|
||||
CardDepthStyleUiState(
|
||||
enabled = stored.enabled,
|
||||
edgeStrength = stored.edgeStrength.coerceIn(0, 100),
|
||||
sheenStrength = stored.sheenStrength.coerceIn(0, 100),
|
||||
edgeCoverage = stored.edgeCoverage.coerceIn(0, 100),
|
||||
postersEnabled = stored.postersEnabled,
|
||||
continueWatchingEnabled = stored.continueWatchingEnabled,
|
||||
episodeCardsEnabled = stored.episodeCardsEnabled,
|
||||
castEnabled = stored.castEnabled,
|
||||
trailersEnabled = stored.trailersEnabled,
|
||||
)
|
||||
} else {
|
||||
CardDepthStyleUiState()
|
||||
}
|
||||
}
|
||||
|
||||
private fun persist() {
|
||||
CardDepthStyleStorage.savePayload(
|
||||
json.encodeToString(
|
||||
StoredCardDepthStylePreferences(
|
||||
enabled = _uiState.value.enabled,
|
||||
edgeStrength = _uiState.value.edgeStrength,
|
||||
sheenStrength = _uiState.value.sheenStrength,
|
||||
edgeCoverage = _uiState.value.edgeCoverage,
|
||||
postersEnabled = _uiState.value.postersEnabled,
|
||||
continueWatchingEnabled = _uiState.value.continueWatchingEnabled,
|
||||
episodeCardsEnabled = _uiState.value.episodeCardsEnabled,
|
||||
castEnabled = _uiState.value.castEnabled,
|
||||
trailersEnabled = _uiState.value.trailersEnabled,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
package com.nuvio.app.core.ui
|
||||
|
||||
internal expect object CardDepthStyleStorage {
|
||||
fun loadPayload(): String?
|
||||
fun savePayload(payload: String)
|
||||
}
|
||||
|
|
@ -148,6 +148,10 @@ fun NuvioPosterCard(
|
|||
.aspectRatio(shape.aspectRatio)
|
||||
.clip(cardShape)
|
||||
.background(tokens.colors.surface)
|
||||
.nuvioCardDepth(
|
||||
shape = cardShape,
|
||||
surface = NuvioCardDepthSurface.Posters,
|
||||
)
|
||||
.posterCardClickable(
|
||||
onClick = onClick,
|
||||
onLongClick = onLongClick,
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@ import androidx.compose.ui.unit.sp
|
|||
import coil3.compose.AsyncImage
|
||||
import coil3.compose.LocalPlatformContext
|
||||
import coil3.request.ImageRequest
|
||||
import com.nuvio.app.core.ui.NuvioCardDepthSurface
|
||||
import com.nuvio.app.core.ui.nuvioCardDepth
|
||||
import com.nuvio.app.features.details.MetaPerson
|
||||
import com.nuvio.app.features.details.castAvatarSharedTransitionKey
|
||||
import nuvio.composeapp.generated.resources.*
|
||||
|
|
@ -143,6 +145,10 @@ private fun CastItem(
|
|||
.background(
|
||||
color = MaterialTheme.colorScheme.surfaceVariant,
|
||||
shape = CircleShape,
|
||||
)
|
||||
.nuvioCardDepth(
|
||||
shape = CircleShape,
|
||||
surface = NuvioCardDepthSurface.Cast,
|
||||
),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -64,7 +64,9 @@ import com.nuvio.app.core.build.AppFeaturePolicy
|
|||
import com.nuvio.app.core.format.formatReleaseDateForDisplay
|
||||
import com.nuvio.app.core.i18n.localizedSeasonEpisodeCode
|
||||
import com.nuvio.app.core.ui.NuvioAnimatedWatchedBadge
|
||||
import com.nuvio.app.core.ui.NuvioCardDepthSurface
|
||||
import com.nuvio.app.core.ui.NuvioProgressBar
|
||||
import com.nuvio.app.core.ui.nuvioCardDepth
|
||||
import com.nuvio.app.core.ui.posterCardClickable
|
||||
import com.nuvio.app.features.details.MetaDetails
|
||||
import com.nuvio.app.features.details.MetaEpisodeCardStyle
|
||||
|
|
@ -670,10 +672,10 @@ private fun EpisodeHorizontalCard(
|
|||
.height(metrics.cardHeight)
|
||||
.clip(cardShape)
|
||||
.background(MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.45f))
|
||||
.border(
|
||||
width = 1.dp,
|
||||
color = Color.White.copy(alpha = 0.12f),
|
||||
.nuvioCardDepth(
|
||||
shape = cardShape,
|
||||
surface = NuvioCardDepthSurface.EpisodeCards,
|
||||
fallbackBorderAlpha = 0.12f,
|
||||
)
|
||||
.posterCardClickable(
|
||||
onClick = onClick,
|
||||
|
|
@ -699,12 +701,12 @@ private fun EpisodeHorizontalCard(
|
|||
.fillMaxSize()
|
||||
.background(
|
||||
Brush.verticalGradient(
|
||||
colors = listOf(
|
||||
Color.Transparent,
|
||||
Color.Black.copy(alpha = 0.10f),
|
||||
Color.Black.copy(alpha = 0.42f),
|
||||
Color.Black.copy(alpha = 0.78f),
|
||||
),
|
||||
0f to Color.Transparent,
|
||||
0.42f to Color.Transparent,
|
||||
0.56f to Color.Black.copy(alpha = 0.20f),
|
||||
0.70f to Color.Black.copy(alpha = 0.45f),
|
||||
0.84f to Color.Black.copy(alpha = 0.68f),
|
||||
1f to Color.Black.copy(alpha = 0.92f),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -37,6 +37,8 @@ import androidx.compose.ui.text.style.TextOverflow
|
|||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import coil3.compose.AsyncImage
|
||||
import com.nuvio.app.core.ui.NuvioCardDepthSurface
|
||||
import com.nuvio.app.core.ui.nuvioCardDepth
|
||||
import com.nuvio.app.features.details.MetaTrailer
|
||||
import nuvio.composeapp.generated.resources.*
|
||||
import nuvio.composeapp.generated.resources.detail_tab_trailer
|
||||
|
|
@ -193,6 +195,10 @@ private fun TrailerCard(
|
|||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(cornerRadius))
|
||||
.nuvioCardDepth(
|
||||
shape = RoundedCornerShape(cornerRadius),
|
||||
surface = NuvioCardDepthSurface.Trailers,
|
||||
)
|
||||
.clickable(onClick = onClick),
|
||||
) {
|
||||
AsyncImage(
|
||||
|
|
|
|||
|
|
@ -48,7 +48,9 @@ import androidx.compose.ui.unit.sp
|
|||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import coil3.compose.AsyncImage
|
||||
import com.nuvio.app.core.ui.DisintegratingContainer
|
||||
import com.nuvio.app.core.ui.NuvioCardDepthSurface
|
||||
import com.nuvio.app.core.ui.NuvioProgressBar
|
||||
import com.nuvio.app.core.ui.nuvioCardDepth
|
||||
import com.nuvio.app.core.ui.NuvioShelfSection
|
||||
import com.nuvio.app.core.ui.NuvioTokens
|
||||
import com.nuvio.app.core.ui.PosterLandscapeAspectRatio
|
||||
|
|
@ -703,6 +705,10 @@ private fun ContinueWatchingCard(
|
|||
.aspectRatio(PosterLandscapeAspectRatio)
|
||||
.clip(RoundedCornerShape(cardMetrics.cornerRadius))
|
||||
.background(MaterialTheme.colorScheme.surfaceVariant)
|
||||
.nuvioCardDepth(
|
||||
shape = RoundedCornerShape(cardMetrics.cornerRadius),
|
||||
surface = NuvioCardDepthSurface.ContinueWatching,
|
||||
)
|
||||
.posterCardClickable(
|
||||
onClick = onClick,
|
||||
onLongClick = onLongClick,
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import kotlinx.serialization.SerialName
|
|||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
private const val PARENTAL_GUIDE_BASE_URL = "https://api.imdbapi.dev"
|
||||
private const val PARENTAL_GUIDE_BASE_URL = "https://api.tiffara.com"
|
||||
private val imdbIdPattern = Regex("tt\\d+")
|
||||
|
||||
data class ParentalGuideResult(
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import com.nuvio.app.features.downloads.DownloadsRepository
|
|||
import com.nuvio.app.features.details.MetaScreenSettingsRepository
|
||||
import com.nuvio.app.features.home.HomeCatalogSettingsRepository
|
||||
import com.nuvio.app.features.home.HomeRepository
|
||||
import com.nuvio.app.core.ui.CardDepthStyleRepository
|
||||
import com.nuvio.app.core.ui.PosterCardStyleRepository
|
||||
import com.nuvio.app.features.library.LibraryRepository
|
||||
import com.nuvio.app.features.mdblist.MdbListSettingsRepository
|
||||
|
|
@ -162,6 +163,7 @@ object ProfileRepository {
|
|||
}
|
||||
ThemeSettingsRepository.onProfileChanged()
|
||||
PosterCardStyleRepository.onProfileChanged()
|
||||
CardDepthStyleRepository.onProfileChanged()
|
||||
PlayerSettingsRepository.onProfileChanged()
|
||||
StreamBadgeSettingsRepository.onProfileChanged()
|
||||
P2pSettingsRepository.onProfileChanged()
|
||||
|
|
|
|||
|
|
@ -2,8 +2,12 @@ package com.nuvio.app.features.settings
|
|||
|
||||
import androidx.compose.animation.core.animateDpAsState
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.Canvas
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.gestures.detectDragGestures
|
||||
import androidx.compose.foundation.gestures.detectTapGestures
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
|
|
@ -11,6 +15,8 @@ import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
|||
import androidx.compose.foundation.layout.FlowRow
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.aspectRatio
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
|
|
@ -18,23 +24,81 @@ import androidx.compose.foundation.layout.size
|
|||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.lazy.LazyListScope
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.Tune
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.FilterChip
|
||||
import androidx.compose.material3.FilterChipDefaults
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Slider
|
||||
import androidx.compose.material3.SliderDefaults
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.SwitchDefaults
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.rememberModalBottomSheetState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableFloatStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberUpdatedState
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.input.pointer.pointerInput
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import kotlin.math.roundToInt
|
||||
import com.nuvio.app.core.ui.CardDepthStyleRepository
|
||||
import com.nuvio.app.core.ui.CardDepthStyleUiState
|
||||
import com.nuvio.app.core.ui.DefaultCardDepthEdgeCoverage
|
||||
import com.nuvio.app.core.ui.DefaultCardDepthEdgeStrength
|
||||
import com.nuvio.app.core.ui.DefaultCardDepthSheenStrength
|
||||
import com.nuvio.app.core.ui.NuvioActionLabel
|
||||
import com.nuvio.app.core.ui.NuvioCardDepthSurface
|
||||
import com.nuvio.app.core.ui.NuvioModalBottomSheet
|
||||
import com.nuvio.app.core.ui.PosterCardStyleRepository
|
||||
import com.nuvio.app.core.ui.PosterCardStyleUiState
|
||||
import com.nuvio.app.core.ui.cardDepthVisual
|
||||
import nuvio.composeapp.generated.resources.Res
|
||||
import nuvio.composeapp.generated.resources.action_reset
|
||||
import nuvio.composeapp.generated.resources.settings_card_depth_apply_to
|
||||
import nuvio.composeapp.generated.resources.settings_card_depth_description
|
||||
import nuvio.composeapp.generated.resources.settings_card_depth_edge
|
||||
import nuvio.composeapp.generated.resources.settings_card_depth_edge_balanced
|
||||
import nuvio.composeapp.generated.resources.settings_card_depth_edge_bold
|
||||
import nuvio.composeapp.generated.resources.settings_card_depth_edge_subtle
|
||||
import nuvio.composeapp.generated.resources.settings_card_depth_edge_value
|
||||
import nuvio.composeapp.generated.resources.settings_card_depth_edge_coverage
|
||||
import nuvio.composeapp.generated.resources.settings_card_depth_coverage_full
|
||||
import nuvio.composeapp.generated.resources.settings_card_depth_coverage_half
|
||||
import nuvio.composeapp.generated.resources.settings_card_depth_coverage_top
|
||||
import nuvio.composeapp.generated.resources.settings_card_depth_coverage_value
|
||||
import nuvio.composeapp.generated.resources.settings_card_depth_enabled
|
||||
import nuvio.composeapp.generated.resources.settings_card_depth_fine_tune
|
||||
import nuvio.composeapp.generated.resources.settings_card_depth_fine_tune_hint
|
||||
import nuvio.composeapp.generated.resources.settings_card_depth_fine_tune_title
|
||||
import nuvio.composeapp.generated.resources.settings_card_depth_pad_edge_axis
|
||||
import nuvio.composeapp.generated.resources.settings_card_depth_pad_sheen_axis
|
||||
import nuvio.composeapp.generated.resources.settings_card_depth_preview_meta
|
||||
import nuvio.composeapp.generated.resources.settings_card_depth_preview_title
|
||||
import nuvio.composeapp.generated.resources.settings_card_depth_sheen
|
||||
import nuvio.composeapp.generated.resources.settings_card_depth_sheen_value
|
||||
import nuvio.composeapp.generated.resources.settings_card_depth_sheen_bright
|
||||
import nuvio.composeapp.generated.resources.settings_card_depth_sheen_off
|
||||
import nuvio.composeapp.generated.resources.settings_card_depth_sheen_soft
|
||||
import nuvio.composeapp.generated.resources.settings_card_depth_surface_cast
|
||||
import nuvio.composeapp.generated.resources.settings_card_depth_surface_continue_watching
|
||||
import nuvio.composeapp.generated.resources.settings_card_depth_surface_episodes
|
||||
import nuvio.composeapp.generated.resources.settings_card_depth_surface_posters
|
||||
import nuvio.composeapp.generated.resources.settings_card_depth_surface_trailers
|
||||
import nuvio.composeapp.generated.resources.settings_card_depth_title
|
||||
import nuvio.composeapp.generated.resources.settings_poster_card_radius
|
||||
import nuvio.composeapp.generated.resources.settings_poster_card_style
|
||||
import nuvio.composeapp.generated.resources.settings_poster_card_width
|
||||
|
|
@ -90,6 +154,451 @@ internal fun LazyListScope.posterCustomizationSettingsContent(
|
|||
}
|
||||
}
|
||||
}
|
||||
item {
|
||||
CardDepthStyleRepository.ensureLoaded()
|
||||
val cardDepthState by CardDepthStyleRepository.uiState.collectAsState()
|
||||
SettingsSection(
|
||||
title = stringResource(Res.string.settings_card_depth_title),
|
||||
isTablet = isTablet,
|
||||
actions = {
|
||||
NuvioActionLabel(
|
||||
text = stringResource(Res.string.action_reset),
|
||||
onClick = CardDepthStyleRepository::resetToDefaults,
|
||||
)
|
||||
},
|
||||
) {
|
||||
SettingsGroup(isTablet = isTablet) {
|
||||
CardDepthStyleControls(
|
||||
isTablet = isTablet,
|
||||
uiState = cardDepthState,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun CardDepthStyleControls(
|
||||
isTablet: Boolean,
|
||||
uiState: CardDepthStyleUiState,
|
||||
) {
|
||||
var showFineTune by remember { mutableStateOf(false) }
|
||||
val edgeOptions = listOf(
|
||||
PresetOption(stringResource(Res.string.settings_card_depth_edge_subtle), 28),
|
||||
PresetOption(stringResource(Res.string.settings_card_depth_edge_balanced), 42),
|
||||
PresetOption(stringResource(Res.string.settings_card_depth_edge_bold), 56),
|
||||
)
|
||||
val sheenOptions = listOf(
|
||||
PresetOption(stringResource(Res.string.settings_card_depth_sheen_off), 0),
|
||||
PresetOption(stringResource(Res.string.settings_card_depth_sheen_soft), 10),
|
||||
PresetOption(stringResource(Res.string.settings_card_depth_sheen_bright), 16),
|
||||
)
|
||||
val coverageOptions = listOf(
|
||||
PresetOption(stringResource(Res.string.settings_card_depth_coverage_top), 0),
|
||||
PresetOption(stringResource(Res.string.settings_card_depth_coverage_half), 50),
|
||||
PresetOption(stringResource(Res.string.settings_card_depth_coverage_full), 100),
|
||||
)
|
||||
val surfaceRows = listOf(
|
||||
stringResource(Res.string.settings_card_depth_surface_posters) to NuvioCardDepthSurface.Posters,
|
||||
stringResource(Res.string.settings_card_depth_surface_continue_watching) to NuvioCardDepthSurface.ContinueWatching,
|
||||
stringResource(Res.string.settings_card_depth_surface_episodes) to NuvioCardDepthSurface.EpisodeCards,
|
||||
stringResource(Res.string.settings_card_depth_surface_cast) to NuvioCardDepthSurface.Cast,
|
||||
stringResource(Res.string.settings_card_depth_surface_trailers) to NuvioCardDepthSurface.Trailers,
|
||||
)
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = if (isTablet) 20.dp else 16.dp, vertical = if (isTablet) 18.dp else 16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(14.dp),
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(Res.string.settings_card_depth_description),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
PosterToggleRow(
|
||||
title = stringResource(Res.string.settings_card_depth_enabled),
|
||||
checked = uiState.enabled,
|
||||
onCheckedChange = CardDepthStyleRepository::setEnabled,
|
||||
)
|
||||
if (uiState.enabled) {
|
||||
PosterStyleOptionRow(
|
||||
title = stringResource(Res.string.settings_card_depth_edge),
|
||||
selectedValue = uiState.edgeStrength,
|
||||
options = edgeOptions,
|
||||
onSelected = CardDepthStyleRepository::setEdgeStrength,
|
||||
)
|
||||
PosterStyleOptionRow(
|
||||
title = stringResource(Res.string.settings_card_depth_sheen),
|
||||
selectedValue = uiState.sheenStrength,
|
||||
options = sheenOptions,
|
||||
onSelected = CardDepthStyleRepository::setSheenStrength,
|
||||
)
|
||||
PosterStyleOptionRow(
|
||||
title = stringResource(Res.string.settings_card_depth_edge_coverage),
|
||||
selectedValue = uiState.edgeCoverage,
|
||||
options = coverageOptions,
|
||||
onSelected = CardDepthStyleRepository::setEdgeCoverage,
|
||||
)
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(10.dp))
|
||||
.clickable { showFineTune = true },
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(Res.string.settings_card_depth_fine_tune),
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
fontWeight = FontWeight.Medium,
|
||||
)
|
||||
Icon(
|
||||
imageVector = Icons.Rounded.Tune,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
)
|
||||
}
|
||||
Text(
|
||||
text = stringResource(Res.string.settings_card_depth_apply_to),
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
)
|
||||
surfaceRows.forEach { (title, surface) ->
|
||||
PosterToggleRow(
|
||||
title = title,
|
||||
checked = uiState.isSurfaceEnabled(surface),
|
||||
onCheckedChange = { enabled ->
|
||||
CardDepthStyleRepository.setSurfaceEnabled(surface, enabled)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (showFineTune) {
|
||||
CardDepthFineTuneSheet(
|
||||
initialEdgeStrength = uiState.edgeStrength,
|
||||
initialSheenStrength = uiState.sheenStrength,
|
||||
initialEdgeCoverage = uiState.edgeCoverage,
|
||||
onDismiss = { showFineTune = false },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun CardDepthFineTuneSheet(
|
||||
initialEdgeStrength: Int,
|
||||
initialSheenStrength: Int,
|
||||
initialEdgeCoverage: Int,
|
||||
onDismiss: () -> Unit,
|
||||
) {
|
||||
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
|
||||
var draftEdge by remember { mutableFloatStateOf(initialEdgeStrength.toFloat()) }
|
||||
var draftSheen by remember { mutableFloatStateOf(initialSheenStrength.toFloat()) }
|
||||
var draftCoverage by remember { mutableFloatStateOf(initialEdgeCoverage.toFloat()) }
|
||||
|
||||
fun commitDraft() {
|
||||
CardDepthStyleRepository.setEdgeStrength(draftEdge.roundToInt())
|
||||
CardDepthStyleRepository.setSheenStrength(draftSheen.roundToInt())
|
||||
CardDepthStyleRepository.setEdgeCoverage(draftCoverage.roundToInt())
|
||||
}
|
||||
|
||||
NuvioModalBottomSheet(
|
||||
onDismissRequest = {
|
||||
commitDraft()
|
||||
onDismiss()
|
||||
},
|
||||
sheetState = sheetState,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(start = 20.dp, end = 20.dp, bottom = 32.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(Res.string.settings_card_depth_fine_tune_title),
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
)
|
||||
NuvioActionLabel(
|
||||
text = stringResource(Res.string.action_reset),
|
||||
onClick = {
|
||||
draftEdge = DefaultCardDepthEdgeStrength.toFloat()
|
||||
draftSheen = DefaultCardDepthSheenStrength.toFloat()
|
||||
draftCoverage = DefaultCardDepthEdgeCoverage.toFloat()
|
||||
commitDraft()
|
||||
},
|
||||
)
|
||||
}
|
||||
Text(
|
||||
text = stringResource(Res.string.settings_card_depth_fine_tune_hint),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
CardDepthPreviewCard(
|
||||
edgeStrength = draftEdge,
|
||||
sheenStrength = draftSheen,
|
||||
edgeCoverage = draftCoverage,
|
||||
)
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
Text(
|
||||
text = "${stringResource(Res.string.settings_card_depth_edge_value)}: ${formatDepthPercentage(draftEdge)}",
|
||||
style = MaterialTheme.typography.labelLarge,
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
)
|
||||
Text(
|
||||
text = "${stringResource(Res.string.settings_card_depth_sheen_value)}: ${formatDepthPercentage(draftSheen)}",
|
||||
style = MaterialTheme.typography.labelLarge,
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
)
|
||||
}
|
||||
CardDepthTuningPad(
|
||||
edgeStrength = draftEdge,
|
||||
sheenStrength = draftSheen,
|
||||
onChange = { edge, sheen ->
|
||||
draftEdge = edge
|
||||
draftSheen = sheen
|
||||
},
|
||||
onCommit = { edge, sheen ->
|
||||
CardDepthStyleRepository.setEdgeStrength(edge.roundToInt())
|
||||
CardDepthStyleRepository.setSheenStrength(sheen.roundToInt())
|
||||
},
|
||||
)
|
||||
Text(
|
||||
text = "${stringResource(Res.string.settings_card_depth_coverage_value)}: ${formatDepthPercentage(draftCoverage)}",
|
||||
style = MaterialTheme.typography.labelLarge,
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
)
|
||||
Slider(
|
||||
value = draftCoverage,
|
||||
onValueChange = { draftCoverage = it },
|
||||
onValueChangeFinished = {
|
||||
CardDepthStyleRepository.setEdgeCoverage(draftCoverage.roundToInt())
|
||||
},
|
||||
valueRange = 0f..100f,
|
||||
colors = SliderDefaults.colors(
|
||||
thumbColor = MaterialTheme.colorScheme.primary,
|
||||
activeTrackColor = MaterialTheme.colorScheme.primary,
|
||||
),
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun formatDepthPercentage(value: Float): String {
|
||||
val tenths = (value.coerceIn(0f, 100f) * 10f).roundToInt()
|
||||
return "${tenths / 10}.${tenths % 10}%"
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun CardDepthPreviewCard(
|
||||
edgeStrength: Float,
|
||||
sheenStrength: Float,
|
||||
edgeCoverage: Float,
|
||||
) {
|
||||
val shape = RoundedCornerShape(14.dp)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.aspectRatio(16f / 9f)
|
||||
.clip(shape)
|
||||
.background(
|
||||
Brush.linearGradient(
|
||||
colors = listOf(
|
||||
Color(0xFF33415C),
|
||||
Color(0xFF232D42),
|
||||
Color(0xFF141A28),
|
||||
),
|
||||
),
|
||||
)
|
||||
.cardDepthVisual(
|
||||
shape = shape,
|
||||
edgeStrength = edgeStrength,
|
||||
sheenStrength = sheenStrength,
|
||||
edgeCoverage = edgeCoverage,
|
||||
),
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(
|
||||
Brush.verticalGradient(
|
||||
0f to Color.Transparent,
|
||||
0.42f to Color.Transparent,
|
||||
0.56f to Color.Black.copy(alpha = 0.20f),
|
||||
0.70f to Color.Black.copy(alpha = 0.45f),
|
||||
0.84f to Color.Black.copy(alpha = 0.68f),
|
||||
1f to Color.Black.copy(alpha = 0.92f),
|
||||
),
|
||||
),
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomStart)
|
||||
.padding(14.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(Res.string.settings_card_depth_preview_meta),
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
color = Color.White.copy(alpha = 0.78f),
|
||||
)
|
||||
Text(
|
||||
text = stringResource(Res.string.settings_card_depth_preview_title),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
color = Color.White,
|
||||
fontWeight = FontWeight.ExtraBold,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun CardDepthTuningPad(
|
||||
edgeStrength: Float,
|
||||
sheenStrength: Float,
|
||||
onChange: (Float, Float) -> Unit,
|
||||
onCommit: (Float, Float) -> Unit,
|
||||
) {
|
||||
val maxEdge = 70f
|
||||
val maxSheen = 25f
|
||||
val shape = RoundedCornerShape(16.dp)
|
||||
val thumbColor = MaterialTheme.colorScheme.primary
|
||||
val gridColor = MaterialTheme.colorScheme.outlineVariant.copy(alpha = 0.4f)
|
||||
val currentEdge by rememberUpdatedState(edgeStrength)
|
||||
val currentSheen by rememberUpdatedState(sheenStrength)
|
||||
val currentOnChange by rememberUpdatedState(onChange)
|
||||
val currentOnCommit by rememberUpdatedState(onCommit)
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(200.dp)
|
||||
.clip(shape)
|
||||
.background(MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.35f))
|
||||
.border(
|
||||
width = 1.dp,
|
||||
color = MaterialTheme.colorScheme.outlineVariant.copy(alpha = 0.5f),
|
||||
shape = shape,
|
||||
)
|
||||
.pointerInput(Unit) {
|
||||
fun valuesAt(position: Offset): Pair<Float, Float> {
|
||||
val x = (position.x / size.width).coerceIn(0f, 1f)
|
||||
val y = 1f - (position.y / size.height).coerceIn(0f, 1f)
|
||||
return (x * maxEdge) to (y * maxSheen)
|
||||
}
|
||||
detectDragGestures(
|
||||
onDragStart = { position ->
|
||||
val (edge, sheen) = valuesAt(position)
|
||||
currentOnChange(edge, sheen)
|
||||
},
|
||||
onDrag = { change, _ ->
|
||||
change.consume()
|
||||
val (edge, sheen) = valuesAt(change.position)
|
||||
currentOnChange(edge, sheen)
|
||||
},
|
||||
onDragEnd = {
|
||||
currentOnCommit(currentEdge, currentSheen)
|
||||
},
|
||||
onDragCancel = {
|
||||
currentOnCommit(currentEdge, currentSheen)
|
||||
},
|
||||
)
|
||||
}
|
||||
.pointerInput(Unit) {
|
||||
fun valuesAt(position: Offset): Pair<Float, Float> {
|
||||
val x = (position.x / size.width).coerceIn(0f, 1f)
|
||||
val y = 1f - (position.y / size.height).coerceIn(0f, 1f)
|
||||
return (x * maxEdge) to (y * maxSheen)
|
||||
}
|
||||
detectTapGestures { position ->
|
||||
val (edge, sheen) = valuesAt(position)
|
||||
currentOnChange(edge, sheen)
|
||||
currentOnCommit(edge, sheen)
|
||||
}
|
||||
},
|
||||
) {
|
||||
Canvas(modifier = Modifier.fillMaxSize()) {
|
||||
for (step in 1..3) {
|
||||
val x = size.width * step / 4f
|
||||
val y = size.height * step / 4f
|
||||
drawLine(
|
||||
color = gridColor,
|
||||
start = Offset(x, 0f),
|
||||
end = Offset(x, size.height),
|
||||
strokeWidth = 1.dp.toPx(),
|
||||
)
|
||||
drawLine(
|
||||
color = gridColor,
|
||||
start = Offset(0f, y),
|
||||
end = Offset(size.width, y),
|
||||
strokeWidth = 1.dp.toPx(),
|
||||
)
|
||||
}
|
||||
val thumbX = size.width * (edgeStrength / maxEdge).coerceIn(0f, 1f)
|
||||
val thumbY = size.height * (1f - (sheenStrength / maxSheen).coerceIn(0f, 1f))
|
||||
drawLine(
|
||||
color = thumbColor.copy(alpha = 0.35f),
|
||||
start = Offset(thumbX, 0f),
|
||||
end = Offset(thumbX, size.height),
|
||||
strokeWidth = 1.dp.toPx(),
|
||||
)
|
||||
drawLine(
|
||||
color = thumbColor.copy(alpha = 0.35f),
|
||||
start = Offset(0f, thumbY),
|
||||
end = Offset(size.width, thumbY),
|
||||
strokeWidth = 1.dp.toPx(),
|
||||
)
|
||||
drawCircle(
|
||||
color = thumbColor.copy(alpha = 0.25f),
|
||||
radius = 16.dp.toPx(),
|
||||
center = Offset(thumbX, thumbY),
|
||||
)
|
||||
drawCircle(
|
||||
color = thumbColor,
|
||||
radius = 9.dp.toPx(),
|
||||
center = Offset(thumbX, thumbY),
|
||||
)
|
||||
drawCircle(
|
||||
color = Color.White,
|
||||
radius = 4.dp.toPx(),
|
||||
center = Offset(thumbX, thumbY),
|
||||
)
|
||||
}
|
||||
Text(
|
||||
text = stringResource(Res.string.settings_card_depth_pad_sheen_axis),
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
modifier = Modifier
|
||||
.align(Alignment.TopStart)
|
||||
.padding(10.dp),
|
||||
)
|
||||
Text(
|
||||
text = stringResource(Res.string.settings_card_depth_pad_edge_axis),
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomEnd)
|
||||
.padding(10.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalLayoutApi::class)
|
||||
|
|
|
|||
|
|
@ -710,6 +710,23 @@ internal fun settingsSearchEntries(
|
|||
)
|
||||
}
|
||||
|
||||
val cardDepthSection = stringResource(Res.string.settings_card_depth_title)
|
||||
listOf(
|
||||
PlaybackSearchRow("card-depth-effect", cardDepthSection, stringResource(Res.string.settings_card_depth_description)),
|
||||
PlaybackSearchRow("card-depth-edge", stringResource(Res.string.settings_card_depth_edge)),
|
||||
PlaybackSearchRow("card-depth-sheen", stringResource(Res.string.settings_card_depth_sheen)),
|
||||
).forEach { row ->
|
||||
addRow(
|
||||
page = SettingsPage.PosterCustomization,
|
||||
key = "poster-${row.key}",
|
||||
title = row.title,
|
||||
description = row.description,
|
||||
pageLabel = posterStylePage,
|
||||
section = cardDepthSection,
|
||||
icon = Icons.Rounded.Tune,
|
||||
)
|
||||
}
|
||||
|
||||
val homeLayoutSection = stringResource(Res.string.settings_homescreen_section_hero)
|
||||
listOf(
|
||||
PlaybackSearchRow("home-hero", stringResource(Res.string.settings_homescreen_show_hero), stringResource(Res.string.settings_homescreen_show_hero_description)),
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
package com.nuvio.app.core.ui
|
||||
|
||||
import com.nuvio.app.core.storage.ProfileScopedKey
|
||||
import platform.Foundation.NSUserDefaults
|
||||
|
||||
internal actual object CardDepthStyleStorage {
|
||||
private const val payloadKey = "card_depth_style_payload"
|
||||
|
||||
actual fun loadPayload(): String? =
|
||||
NSUserDefaults.standardUserDefaults.stringForKey(ProfileScopedKey.of(payloadKey))
|
||||
|
||||
actual fun savePayload(payload: String) {
|
||||
NSUserDefaults.standardUserDefaults.setObject(payload, forKey = ProfileScopedKey.of(payloadKey))
|
||||
}
|
||||
}
|
||||
1
enginefs
Submodule
1
enginefs
Submodule
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 9f11922ae092e4a7120a7e1bdabb0698df1eecb8
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
CURRENT_PROJECT_VERSION=94
|
||||
MARKETING_VERSION=0.2.22
|
||||
CURRENT_PROJECT_VERSION=95
|
||||
MARKETING_VERSION=0.2.23
|
||||
|
||||
|
|
|
|||
1
stremiotorrernt/public/enginefs
Submodule
1
stremiotorrernt/public/enginefs
Submodule
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 9f11922ae092e4a7120a7e1bdabb0698df1eecb8
|
||||
Loading…
Reference in a new issue