mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-07-26 22:42:17 +00:00
i18n(fr): extract hardcoded strings, harmonize tutoiement, audit cohérence
- Extract 13 hardcoded user-facing strings from cloud, collection, player, debrid, IntroDB to res/values/strings.xml (FR translations included) - Refactor titleForMedia() to accept localized suffixes as parameters, wrap addTmdbSourceFromInput/addTraktSourceFromResult in scope.launch - Pre-load error message for runCatching error() to keep i18n-aware - Harmonize entire values-fr/strings.xml to tutoiement (100 occurrences converted from vouvoiement: Cliquez→Clique, Vous→Tu, Votre→Ton, etc.) - Fix terminology inconsistencies: Résumé→Aperçu, Réseaux→Chaînes (TMDB), Suivant→À suivre (up next), Téléchargement échoué→Échec du téléchargement, Découverte TMDB→TMDB Discover, etc. - Add 134 EN strings, +444 FR (134 new + 310 backfill/rewrites) - Parity preserved: 1532 ↔ 1532 strings, 0 placeholder divergence
This commit is contained in:
parent
c8c3095517
commit
f0736813c4
12 changed files with 981 additions and 761 deletions
File diff suppressed because it is too large
Load diff
|
|
@ -1456,145 +1456,165 @@
|
|||
<string name="unit_bytes_kb">KB</string>
|
||||
<string name="unit_bytes_mb">MB</string>
|
||||
<string name="unit_bytes_gb">GB</string>
|
||||
<string name="plugins_section_overview">OVERVIEW</string>
|
||||
<string name="plugins_badge_repos">%1$d repos</string>
|
||||
<string name="plugins_badge_providers">%1$d providers</string>
|
||||
<string name="plugins_badge_enabled">Plugins enabled</string>
|
||||
<string name="plugins_badge_disabled">Plugins disabled</string>
|
||||
<string name="plugins_badge_tmdb_key_set">TMDB API key set</string>
|
||||
<string name="plugins_badge_tmdb_key_missing">TMDB API key missing</string>
|
||||
<string name="plugins_tmdb_required_message">Plugin providers require a TMDB API key. Set it on the TMDB screen or plugin providers will not work correctly.</string>
|
||||
<string name="plugins_enable_globally_title">Enable plugin providers globally</string>
|
||||
<string name="plugins_enable_globally_desc">Use plugin providers during stream discovery.</string>
|
||||
<string name="plugins_group_by_repo_title">Group plugin providers by repository</string>
|
||||
<string name="plugins_group_by_repo_desc">In Streams, show one provider per repository instead of one per source.</string>
|
||||
<string name="plugins_section_add_repo">ADD REPOSITORY</string>
|
||||
<string name="plugins_input_manifest_placeholder">Plugin manifest URL</string>
|
||||
<string name="plugins_button_installing">Installing…</string>
|
||||
<string name="plugins_button_install_repo">Install Plugin Repository</string>
|
||||
<string name="plugins_error_enter_repo_url">Enter a plugin repository URL.</string>
|
||||
<string name="plugins_error_enter_valid_url">Enter a valid plugin URL.</string>
|
||||
<string name="plugins_error_already_installed">That plugin repository is already installed.</string>
|
||||
<string name="plugins_error_install_failed">Unable to install plugin repository</string>
|
||||
<string name="plugins_error_refresh_failed">Unable to refresh repository</string>
|
||||
<string name="plugins_error_provider_not_found">Provider not found</string>
|
||||
<string name="plugins_error_unavailable_build">Plugins are not available in this build.</string>
|
||||
<string name="plugins_manifest_error_name_missing">Manifest name is missing.</string>
|
||||
<string name="plugins_manifest_error_version_missing">Manifest version is missing.</string>
|
||||
<string name="plugins_manifest_error_no_providers">Manifest has no providers.</string>
|
||||
<string name="plugins_message_installed">Installed %1$s.</string>
|
||||
<string name="plugins_section_installed_repos">INSTALLED REPOSITORIES</string>
|
||||
<string name="plugins_empty_repos_title">No plugin repositories installed yet.</string>
|
||||
<string name="plugins_empty_repos_subtitle">Add a repository URL to install provider plugins for stream discovery.</string>
|
||||
<string name="plugins_repo_version">Version %1$s</string>
|
||||
<string name="plugins_cd_refresh_repo">Refresh plugin repository</string>
|
||||
<string name="plugins_cd_delete_repo">Delete plugin repository</string>
|
||||
<string name="plugins_badge_refreshing">Refreshing</string>
|
||||
<string name="plugins_section_providers">PROVIDERS</string>
|
||||
<string name="plugins_empty_providers">No providers available yet.</string>
|
||||
<string name="plugins_provider_no_description">No description</string>
|
||||
<string name="plugins_provider_version">v%1$s</string>
|
||||
<string name="plugins_provider_disabled_by_repo">Disabled by repo</string>
|
||||
<string name="plugins_button_testing">Testing…</string>
|
||||
<string name="plugins_button_test_provider">Test Provider</string>
|
||||
<string name="plugins_test_error_title">Error</string>
|
||||
<string name="plugins_test_failed">Provider test failed</string>
|
||||
<string name="plugins_test_results_count">Test results (%1$d)</string>
|
||||
<string name="plugins_repo_fallback_label">Plugin repository</string>
|
||||
<string name="submit_intro_action">Submit Intro</string>
|
||||
<!-- PlayerControls a11y -->
|
||||
<string name="player_action_submit_intro">Submit Intro</string>
|
||||
<string name="player_action_video_settings">Video settings</string>
|
||||
<!-- CloudLibrary -->
|
||||
<string name="cloud_library_provider_unavailable">Cloud library is not available for %1$s.</string>
|
||||
<!-- Player iOS video settings modal -->
|
||||
<string name="player_video_settings_title">Video</string>
|
||||
<string name="player_video_settings_reset_tuning">Reset tuning</string>
|
||||
<string name="player_video_settings_output_preset">Output preset</string>
|
||||
<string name="player_video_settings_hdr_peak_detection">HDR peak detection</string>
|
||||
<string name="player_video_settings_hdr_peak_detection_desc">Estimate HDR peak brightness when metadata is bad or missing.</string>
|
||||
<string name="player_video_settings_tone_mapping">Tone mapping</string>
|
||||
<string name="player_video_settings_deband">Deband</string>
|
||||
<string name="player_video_settings_deband_desc">Reduce color banding at a small performance cost.</string>
|
||||
<string name="player_video_settings_interpolation">Frame interpolation</string>
|
||||
<string name="player_video_settings_interpolation_desc">Smooth motion when mpv can use display sync cleanly.</string>
|
||||
<string name="player_video_settings_brightness">Brightness</string>
|
||||
<string name="player_video_settings_contrast">Contrast</string>
|
||||
<string name="player_video_settings_saturation">Saturation</string>
|
||||
<string name="player_video_settings_gamma">Gamma</string>
|
||||
<!-- iOS video output presets -->
|
||||
<string name="player_ios_preset_native_edr_label">Native EDR</string>
|
||||
<string name="player_ios_preset_native_edr_desc">Best for HDR-capable iPhones and iPads.</string>
|
||||
<string name="player_ios_preset_sdr_tone_mapped_label">SDR tone mapped</string>
|
||||
<string name="player_ios_preset_sdr_tone_mapped_desc">More predictable whites and blacks on SDR-style output.</string>
|
||||
<string name="player_ios_preset_compatibility_label">Compatibility</string>
|
||||
<string name="player_ios_preset_compatibility_desc">Closest to the older iOS MPV behavior.</string>
|
||||
<string name="player_ios_preset_custom_label">Custom</string>
|
||||
<string name="player_ios_preset_custom_desc">Use your advanced values below.</string>
|
||||
<string name="player_ios_hardware_decoder_off">Off</string>
|
||||
<!-- Playback settings iOS video output -->
|
||||
<string name="settings_playback_ios_video_output">iOS video output</string>
|
||||
<string name="settings_playback_ios_hardware_decoder">Hardware decoder</string>
|
||||
<string name="settings_playback_ios_extended_dynamic_range">Extended dynamic range</string>
|
||||
<string name="settings_playback_ios_extended_dynamic_range_desc">Default Metal output mode for new playback sessions.</string>
|
||||
<string name="settings_playback_ios_display_color_hint">Display color hint</string>
|
||||
<string name="settings_playback_ios_display_color_hint_desc">Let mpv target the active display color space by default.</string>
|
||||
<string name="settings_playback_ios_target_primaries">Target primaries</string>
|
||||
<string name="settings_playback_ios_target_transfer">Target transfer</string>
|
||||
<!-- Debrid Result Management section -->
|
||||
<string name="settings_debrid_section_result_management">Result Management</string>
|
||||
<string name="settings_debrid_max_results">Max results</string>
|
||||
<string name="settings_debrid_max_results_desc">Limit how many results appear.</string>
|
||||
<string name="settings_debrid_sort_results">Sort results</string>
|
||||
<string name="settings_debrid_sort_results_desc">Choose how results are ordered.</string>
|
||||
<string name="settings_debrid_per_resolution_limit">Per resolution limit</string>
|
||||
<string name="settings_debrid_per_resolution_limit_desc">Cap repeated 2160p, 1080p, 720p results after sorting.</string>
|
||||
<string name="settings_debrid_per_quality_limit">Per quality limit</string>
|
||||
<string name="settings_debrid_per_quality_limit_desc">Cap repeated BluRay, WEB-DL, REMUX results after sorting.</string>
|
||||
<string name="settings_debrid_size_range">Size range</string>
|
||||
<string name="settings_debrid_size_range_desc">Filter results by file size.</string>
|
||||
<!-- Debrid misc -->
|
||||
<string name="settings_debrid_learn_more">Learn more</string>
|
||||
<string name="settings_debrid_template_default_format">Default format</string>
|
||||
<string name="settings_debrid_release_groups_hint">Enter one group per line.</string>
|
||||
<!-- Debrid sort profiles -->
|
||||
<string name="settings_debrid_sort_default">Default</string>
|
||||
<string name="settings_debrid_sort_largest">Largest first</string>
|
||||
<string name="settings_debrid_sort_smallest">Smallest first</string>
|
||||
<string name="settings_debrid_sort_best_audio">Best audio first</string>
|
||||
<string name="settings_debrid_sort_language">Language first</string>
|
||||
<!-- Debrid selection labels -->
|
||||
<string name="settings_debrid_selection_any">Any</string>
|
||||
<string name="settings_debrid_selection_count">%1$d selected</string>
|
||||
<!-- Debrid result count labels -->
|
||||
<string name="settings_debrid_results_all">All results</string>
|
||||
<string name="settings_debrid_results_count">%1$d results</string>
|
||||
<!-- Debrid size range labels -->
|
||||
<string name="settings_debrid_size_up_to">Up to %1$dGB</string>
|
||||
<string name="settings_debrid_size_min">%1$dGB+</string>
|
||||
<string name="settings_debrid_size_range_value">%1$d-%2$dGB</string>
|
||||
<!-- Debrid rule rows: resolutions -->
|
||||
<string name="settings_debrid_rule_preferred_resolutions">Preferred resolutions</string>
|
||||
<string name="settings_debrid_rule_preferred_resolutions_desc">Sort selected resolutions first, in default order.</string>
|
||||
<string name="settings_debrid_rule_required_resolutions">Required resolutions</string>
|
||||
<string name="settings_debrid_rule_required_resolutions_desc">Only show selected resolutions.</string>
|
||||
<string name="settings_debrid_rule_excluded_resolutions">Excluded resolutions</string>
|
||||
<string name="settings_debrid_rule_excluded_resolutions_desc">Hide selected resolutions.</string>
|
||||
<!-- Debrid rule rows: qualities -->
|
||||
<string name="settings_debrid_rule_preferred_qualities">Preferred qualities</string>
|
||||
<string name="settings_debrid_rule_preferred_qualities_desc">Sort selected qualities first, in default order.</string>
|
||||
<string name="settings_debrid_rule_required_qualities">Required qualities</string>
|
||||
<string name="settings_debrid_rule_required_qualities_desc">Only show selected qualities.</string>
|
||||
<string name="settings_debrid_rule_excluded_qualities">Excluded qualities</string>
|
||||
<string name="settings_debrid_rule_excluded_qualities_desc">Hide selected qualities.</string>
|
||||
<!-- Debrid rule rows: visual tags -->
|
||||
<string name="settings_debrid_rule_preferred_visual_tags">Preferred visual tags</string>
|
||||
<string name="settings_debrid_rule_preferred_visual_tags_desc">Sort DV, HDR, 10bit, IMAX and similar tags.</string>
|
||||
<string name="settings_debrid_rule_required_visual_tags">Required visual tags</string>
|
||||
<string name="settings_debrid_rule_required_visual_tags_desc">Require DV, HDR, 10bit, IMAX, SDR and similar tags.</string>
|
||||
<string name="settings_debrid_rule_excluded_visual_tags">Excluded visual tags</string>
|
||||
<string name="settings_debrid_rule_excluded_visual_tags_desc">Hide DV, HDR, 10bit, 3D and similar tags.</string>
|
||||
<!-- Debrid rule rows: audio tags -->
|
||||
<string name="settings_debrid_rule_preferred_audio_tags">Preferred audio tags</string>
|
||||
<string name="settings_debrid_rule_preferred_audio_tags_desc">Sort Atmos, TrueHD, DTS, AAC and similar tags.</string>
|
||||
<string name="settings_debrid_rule_required_audio_tags">Required audio tags</string>
|
||||
<string name="settings_debrid_rule_required_audio_tags_desc">Require Atmos, TrueHD, DTS, AAC and similar tags.</string>
|
||||
<string name="settings_debrid_rule_excluded_audio_tags">Excluded audio tags</string>
|
||||
<string name="settings_debrid_rule_excluded_audio_tags_desc">Hide selected audio tags.</string>
|
||||
<!-- Debrid rule rows: channels -->
|
||||
<string name="settings_debrid_rule_preferred_channels">Preferred channels</string>
|
||||
<string name="settings_debrid_rule_preferred_channels_desc">Sort preferred channel layouts first.</string>
|
||||
<string name="settings_debrid_rule_required_channels">Required channels</string>
|
||||
<string name="settings_debrid_rule_required_channels_desc">Only show selected channel layouts.</string>
|
||||
<string name="settings_debrid_rule_excluded_channels">Excluded channels</string>
|
||||
<string name="settings_debrid_rule_excluded_channels_desc">Hide selected channel layouts.</string>
|
||||
<!-- Debrid rule rows: encodes -->
|
||||
<string name="settings_debrid_rule_preferred_encodes">Preferred encodes</string>
|
||||
<string name="settings_debrid_rule_preferred_encodes_desc">Sort AV1, HEVC, AVC and similar encodes.</string>
|
||||
<string name="settings_debrid_rule_required_encodes">Required encodes</string>
|
||||
<string name="settings_debrid_rule_required_encodes_desc">Require AV1, HEVC, AVC and similar encodes.</string>
|
||||
<string name="settings_debrid_rule_excluded_encodes">Excluded encodes</string>
|
||||
<string name="settings_debrid_rule_excluded_encodes_desc">Hide selected encodes.</string>
|
||||
<!-- Debrid rule rows: languages -->
|
||||
<string name="settings_debrid_rule_preferred_languages">Preferred languages</string>
|
||||
<string name="settings_debrid_rule_preferred_languages_desc">Sort preferred audio languages first.</string>
|
||||
<string name="settings_debrid_rule_required_languages">Required languages</string>
|
||||
<string name="settings_debrid_rule_required_languages_desc">Only show results with selected languages.</string>
|
||||
<string name="settings_debrid_rule_excluded_languages">Excluded languages</string>
|
||||
<string name="settings_debrid_rule_excluded_languages_desc">Hide results where every language is excluded.</string>
|
||||
<!-- Debrid rule rows: release groups -->
|
||||
<string name="settings_debrid_rule_required_release_groups">Required release groups</string>
|
||||
<string name="settings_debrid_rule_required_release_groups_desc">Only show selected release groups.</string>
|
||||
<string name="settings_debrid_rule_excluded_release_groups">Excluded release groups</string>
|
||||
<string name="settings_debrid_rule_excluded_release_groups_desc">Hide selected release groups.</string>
|
||||
<!-- Submit intro/recap/outro dialog -->
|
||||
<string name="submit_intro_title">Submit Timestamps</string>
|
||||
<string name="submit_intro_segment_type_label">SEGMENT TYPE</string>
|
||||
<string name="submit_intro_segment_type">SEGMENT TYPE</string>
|
||||
<string name="submit_intro_segment_intro">Intro</string>
|
||||
<string name="submit_intro_segment_recap">Recap</string>
|
||||
<string name="submit_intro_segment_outro">Outro</string>
|
||||
<string name="submit_intro_start_time_label">START TIME (MM:SS)</string>
|
||||
<string name="submit_intro_end_time_label">END TIME (MM:SS)</string>
|
||||
<string name="submit_intro_button_submit">Submit</string>
|
||||
<string name="submit_intro_capture_button">Capture</string>
|
||||
<string name="settings_playback_introdb_invalid_key">Invalid API Key or connection failed</string>
|
||||
<string name="network_connection_issue">Connection issue</string>
|
||||
<string name="network_please_check_connection">Please check your connection and try again.</string>
|
||||
<string name="library_local_tab_title">Nuvio Library</string>
|
||||
<string name="streams_plugin_repository_fallback">Plugin repository</string>
|
||||
<string name="unit_bytes_tb">TB</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>
|
||||
<string name="collections_editor_tmdb_date_to_placeholder">2024-12-31</string>
|
||||
<string name="collections_editor_tmdb_rating_min_placeholder">7.0</string>
|
||||
<string name="collections_editor_tmdb_rating_max_placeholder">10</string>
|
||||
<string name="collections_editor_tmdb_votes_min_placeholder">100</string>
|
||||
<string name="collections_editor_tmdb_language_placeholder">en, ko, ja, hi</string>
|
||||
<string name="collections_editor_tmdb_country_placeholder">US, KR, JP, IN</string>
|
||||
<string name="collections_editor_tmdb_year_placeholder">2024</string>
|
||||
<string name="collections_editor_trakt_list_id_format">ID %1$s</string>
|
||||
<string name="player_addon_subtitle_display_format">%1$s (%2$s)</string>
|
||||
<string name="trakt_connected_status">Connected to Trakt</string>
|
||||
<string name="trakt_disconnected_status">Disconnected from Trakt</string>
|
||||
<string name="collections_editor_trakt_enter_name_url_or_id">Enter a Trakt list name, URL, or ID</string>
|
||||
<string name="collections_editor_trakt_enter_id_or_url">Enter a Trakt list ID or URL</string>
|
||||
<string name="collections_editor_trakt_load_failed">Could not load Trakt list</string>
|
||||
<string name="collections_editor_trakt_fallback_title">Trakt List %1$d</string>
|
||||
<string name="collections_editor_trakt_resolved_subtitle">Resolved Trakt list</string>
|
||||
<string name="submit_intro_start_time">START TIME (MM:SS)</string>
|
||||
<string name="submit_intro_end_time">END TIME (MM:SS)</string>
|
||||
<string name="submit_intro_submit">Submit</string>
|
||||
<string name="submit_intro_capture">Capture</string>
|
||||
<!-- iOS advanced playback -->
|
||||
<string name="settings_playback_ios_hw_decoder_dialog">Hardware decoder</string>
|
||||
<string name="settings_playback_ios_target_primaries_dialog">Target primaries</string>
|
||||
<string name="settings_playback_ios_target_transfer_dialog">Target transfer</string>
|
||||
<!-- Collection editor -->
|
||||
<string name="collections_editor_resolved_trakt_list">Resolved Trakt list</string>
|
||||
<string name="collections_editor_tmdb_discover">TMDB Discover</string>
|
||||
<string name="collections_editor_watch_region_placeholder">US, KR, JP, IN</string>
|
||||
<!-- Collection editor error messages -->
|
||||
<string name="collections_editor_trakt_input_required">Enter a Trakt list name, URL, or ID</string>
|
||||
<string name="collections_editor_trakt_no_lists_found">No Trakt lists found</string>
|
||||
<string name="collections_editor_tmdb_invalid_id_or_url">Enter a valid TMDB ID or URL.</string>
|
||||
<string name="collections_editor_tmdb_list_title_format">TMDB List %1$s</string>
|
||||
<string name="collections_editor_tmdb_collection_title_format">TMDB Collection %1$s</string>
|
||||
<string name="collections_editor_tmdb_production_title_format">TMDB Production %1$s</string>
|
||||
<string name="collections_editor_tmdb_network_title_format">TMDB Network %1$s</string>
|
||||
<string name="collections_editor_tmdb_person_title_format">TMDB Person %1$s</string>
|
||||
<string name="collections_editor_tmdb_director_title_format">TMDB Director %1$s</string>
|
||||
<string name="collections_editor_tmdb_discover_title">TMDB Discover</string>
|
||||
<string name="collections_editor_tmdb_source_load_failed">Could not load TMDB source</string>
|
||||
<string name="collections_folder_trakt_series_list">Trakt Series List</string>
|
||||
<string name="collections_folder_trakt_movie_list">Trakt Movie List</string>
|
||||
<string name="collections_tmdb_api_key_required">Add a TMDB API key in Settings to use TMDB sources.</string>
|
||||
<string name="collections_tmdb_list_not_found">TMDB list not found</string>
|
||||
<string name="collections_tmdb_collection_not_found">TMDB collection not found</string>
|
||||
<string name="collections_tmdb_company_not_found">TMDB company not found</string>
|
||||
<string name="collections_tmdb_network_not_found">TMDB network not found</string>
|
||||
<string name="collections_tmdb_person_not_found">TMDB person not found</string>
|
||||
<string name="collections_tmdb_missing_list_id">Missing TMDB list ID</string>
|
||||
<string name="collections_tmdb_missing_collection_id">Missing TMDB collection ID</string>
|
||||
<string name="collections_tmdb_missing_person_id">Missing TMDB person ID</string>
|
||||
<string name="collections_tmdb_person_credits_not_found">TMDB person credits not found</string>
|
||||
<string name="collections_tmdb_discover_no_data">TMDB discover returned no data</string>
|
||||
<string name="collections_trakt_missing_list_id">Missing Trakt list ID</string>
|
||||
<string name="collections_trakt_invalid_list_id_or_url">Enter a valid Trakt list ID or URL</string>
|
||||
<string name="collections_trakt_missing_numeric_id">Trakt list did not include a numeric ID</string>
|
||||
<string name="collections_trakt_request_failed">Trakt request failed</string>
|
||||
<string name="collections_trakt_credentials_missing">Missing Trakt credentials.</string>
|
||||
<string name="collections_trakt_list_items_count">%1$d items</string>
|
||||
<string name="collections_trakt_list_likes_count">%1$d likes</string>
|
||||
<string name="collections_trakt_public_list">Trakt public list</string>
|
||||
<string name="collections_trakt_list_not_found_or_private">Trakt list not found or not public</string>
|
||||
<string name="collections_trakt_rate_limit_reached">Trakt rate limit reached</string>
|
||||
<string name="collections_trakt_error_with_code">%1$s (%2$d)</string>
|
||||
<string name="details_comments_trakt_load_failed_with_code">Failed to load Trakt comments (%1$d)</string>
|
||||
<string name="updates_no_channel_release">No update has been published yet.</string>
|
||||
<string name="updates_github_api_error">GitHub releases API error: %1$d</string>
|
||||
<string name="updates_release_missing_title">Release has no tag or name</string>
|
||||
<string name="updates_apk_asset_missing">No APK asset found in the release</string>
|
||||
<string name="updates_download_failed_http">Download failed with HTTP %1$d</string>
|
||||
<string name="updates_empty_download_body">Empty download body</string>
|
||||
<string name="updates_downloaded_file_missing">Downloaded update file is missing.</string>
|
||||
<string name="network_request_failed_http">Request failed with HTTP %1$d</string>
|
||||
<string name="network_empty_response_body">Empty response body</string>
|
||||
<string name="downloads_error_finalize_file_failed">Failed to finalize download file</string>
|
||||
<string name="downloads_error_open_partial_file_failed">Failed to open partial download file</string>
|
||||
<string name="downloads_error_partial_file_not_open">Partial download file is not open</string>
|
||||
<string name="downloads_error_write_partial_file_failed">Failed to write partial download file</string>
|
||||
<string name="details_runtime_hours_minutes">%1$dh %2$dm</string>
|
||||
<string name="details_runtime_hours_only">%1$dh</string>
|
||||
<string name="details_runtime_minutes_only">%1$dm</string>
|
||||
<string name="search_error_no_results_for_catalog">No search results returned for %1$s.</string>
|
||||
<string name="plugins_repository_already_installed">That plugin repository is already installed.</string>
|
||||
<string name="plugins_repository_install_failed">Unable to install plugin repository</string>
|
||||
<string name="plugins_repository_refresh_failed">Unable to refresh repository</string>
|
||||
<string name="plugins_manifest_name_missing">Manifest name is missing.</string>
|
||||
<string name="plugins_manifest_version_missing">Manifest version is missing.</string>
|
||||
<string name="plugins_manifest_no_providers">Manifest has no providers.</string>
|
||||
<string name="addons_manifest_missing_field">Manifest missing \"%1$s\"</string>
|
||||
<string name="player_error_mpv_unavailable">MPV player engine not available. Please rebuild the app.</string>
|
||||
<string name="player_error_unable_to_play_stream">Unable to play this stream.</string>
|
||||
<string name="collections_editor_tmdb_invalid_id_error">Enter a valid TMDB ID or URL.</string>
|
||||
<string name="collections_editor_tmdb_load_error">Could not load TMDB source</string>
|
||||
<string name="collections_editor_trakt_id_url_required">Enter a Trakt list ID or URL</string>
|
||||
<string name="collections_editor_trakt_load_error">Could not load Trakt list</string>
|
||||
<!-- Collection editor TMDB country chips -->
|
||||
<string name="collections_editor_tmdb_country_ca">Canada</string>
|
||||
<string name="collections_editor_tmdb_country_au">Australia</string>
|
||||
<string name="collections_editor_tmdb_country_de">Germany</string>
|
||||
<!-- Collection editor media type suffixes (appended to source titles) -->
|
||||
<string name="collections_editor_media_movies_suffix">Movies</string>
|
||||
<string name="collections_editor_media_series_suffix">Series</string>
|
||||
<!-- Cloud library -->
|
||||
<string name="cloud_library_playback_disabled">Cloud library is disabled.</string>
|
||||
<!-- IntroDB settings -->
|
||||
<string name="settings_playback_introdb_invalid_api_key">Invalid API Key or connection failed</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -12,6 +12,10 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
|||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import nuvio.composeapp.generated.resources.Res
|
||||
import nuvio.composeapp.generated.resources.cloud_library_playback_disabled
|
||||
import nuvio.composeapp.generated.resources.cloud_library_provider_unavailable
|
||||
import org.jetbrains.compose.resources.getString
|
||||
|
||||
internal class CloudLibraryStore(
|
||||
private val credentialsProvider: suspend () -> List<DebridServiceCredential>,
|
||||
|
|
@ -26,7 +30,10 @@ internal class CloudLibraryStore(
|
|||
if (api == null) {
|
||||
return@map CloudLibraryProviderState(
|
||||
provider = credential.provider,
|
||||
errorMessage = "Cloud library is not available for ${credential.provider.displayName}.",
|
||||
errorMessage = getString(
|
||||
Res.string.cloud_library_provider_unavailable,
|
||||
credential.provider.displayName,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -191,7 +198,7 @@ object CloudLibraryRepository {
|
|||
): CloudLibraryPlaybackResult {
|
||||
DebridSettingsRepository.ensureLoaded()
|
||||
if (!DebridSettingsRepository.snapshot().cloudLibraryEnabled) {
|
||||
return CloudLibraryPlaybackResult.Failed("Cloud library is disabled.")
|
||||
return CloudLibraryPlaybackResult.Failed(getString(Res.string.cloud_library_playback_disabled))
|
||||
}
|
||||
val result = store.resolvePlayback(item, file)
|
||||
if (result is CloudLibraryPlaybackResult.Success) {
|
||||
|
|
|
|||
|
|
@ -11,25 +11,17 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
|||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import nuvio.composeapp.generated.resources.Res
|
||||
import nuvio.composeapp.generated.resources.collections_editor_tmdb_collection_title_format
|
||||
import nuvio.composeapp.generated.resources.collections_editor_tmdb_director_title_format
|
||||
import nuvio.composeapp.generated.resources.collections_editor_tmdb_discover_title
|
||||
import nuvio.composeapp.generated.resources.collections_editor_tmdb_invalid_id_or_url
|
||||
import nuvio.composeapp.generated.resources.collections_editor_tmdb_list_title_format
|
||||
import nuvio.composeapp.generated.resources.collections_editor_tmdb_network_title_format
|
||||
import nuvio.composeapp.generated.resources.collections_editor_tmdb_person_title_format
|
||||
import nuvio.composeapp.generated.resources.collections_editor_tmdb_production_title_format
|
||||
import nuvio.composeapp.generated.resources.collections_editor_tmdb_source_load_failed
|
||||
import nuvio.composeapp.generated.resources.collections_editor_trakt_enter_id_or_url
|
||||
import nuvio.composeapp.generated.resources.collections_editor_trakt_enter_name_url_or_id
|
||||
import nuvio.composeapp.generated.resources.collections_editor_trakt_fallback_title
|
||||
import nuvio.composeapp.generated.resources.collections_editor_trakt_load_failed
|
||||
import nuvio.composeapp.generated.resources.collections_editor_media_movies_suffix
|
||||
import nuvio.composeapp.generated.resources.collections_editor_media_series_suffix
|
||||
import nuvio.composeapp.generated.resources.collections_editor_resolved_trakt_list
|
||||
import nuvio.composeapp.generated.resources.collections_editor_tmdb_discover
|
||||
import nuvio.composeapp.generated.resources.collections_editor_tmdb_invalid_id_error
|
||||
import nuvio.composeapp.generated.resources.collections_editor_tmdb_load_error
|
||||
import nuvio.composeapp.generated.resources.collections_editor_trakt_id_url_required
|
||||
import nuvio.composeapp.generated.resources.collections_editor_trakt_input_required
|
||||
import nuvio.composeapp.generated.resources.collections_editor_trakt_load_error
|
||||
import nuvio.composeapp.generated.resources.collections_editor_trakt_no_lists_found
|
||||
import nuvio.composeapp.generated.resources.collections_editor_trakt_resolved_subtitle
|
||||
import nuvio.composeapp.generated.resources.media_movies
|
||||
import nuvio.composeapp.generated.resources.media_series
|
||||
import org.jetbrains.compose.resources.getString
|
||||
import kotlin.uuid.ExperimentalUuidApi
|
||||
import kotlin.uuid.Uuid
|
||||
|
|
@ -414,23 +406,25 @@ object CollectionEditorRepository {
|
|||
val state = _uiState.value
|
||||
val query = state.traktInput.trim()
|
||||
if (query.isBlank()) {
|
||||
_uiState.value = state.copy(
|
||||
traktSearchError = runBlocking { getString(Res.string.collections_editor_trakt_enter_name_url_or_id) },
|
||||
)
|
||||
scope.launch {
|
||||
_uiState.value = _uiState.value.copy(
|
||||
traktSearchError = getString(Res.string.collections_editor_trakt_input_required),
|
||||
)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
scope.launch {
|
||||
val loadErrorMessage = getString(Res.string.collections_editor_trakt_load_error)
|
||||
val results = if (query.isTraktListIdentifierInput()) {
|
||||
runCatching {
|
||||
val metadata = TraktPublicListSourceResolver.listImportMetadata(query)
|
||||
val id = metadata.traktListId
|
||||
?: error(getString(Res.string.collections_editor_trakt_load_failed))
|
||||
val id = metadata.traktListId ?: error(loadErrorMessage)
|
||||
listOf(
|
||||
TraktPublicListSearchResult(
|
||||
traktListId = id,
|
||||
title = metadata.title ?: getString(Res.string.collections_editor_trakt_fallback_title, id),
|
||||
subtitle = getString(Res.string.collections_editor_trakt_resolved_subtitle),
|
||||
title = metadata.title ?: "Trakt List $id",
|
||||
subtitle = getString(Res.string.collections_editor_resolved_trakt_list),
|
||||
coverImageUrl = metadata.coverImageUrl,
|
||||
),
|
||||
)
|
||||
|
|
@ -647,45 +641,46 @@ object CollectionEditorRepository {
|
|||
}
|
||||
val id = TmdbCollectionSourceResolver.parseTmdbId(state.tmdbInput)
|
||||
if (sourceType != TmdbCollectionSourceType.DISCOVER && id == null) {
|
||||
_uiState.value = state.copy(
|
||||
tmdbSearchError = runBlocking { getString(Res.string.collections_editor_tmdb_invalid_id_or_url) },
|
||||
)
|
||||
scope.launch {
|
||||
_uiState.value = _uiState.value.copy(
|
||||
tmdbSearchError = getString(Res.string.collections_editor_tmdb_invalid_id_error),
|
||||
)
|
||||
}
|
||||
return
|
||||
}
|
||||
val mediaTypes = selectedMediaTypes(state, sourceType)
|
||||
val baseTitle = state.tmdbTitleInput.ifBlank {
|
||||
runBlocking {
|
||||
val idArg = id?.toString().orEmpty()
|
||||
scope.launch {
|
||||
val moviesSuffix = getString(Res.string.collections_editor_media_movies_suffix)
|
||||
val seriesSuffix = getString(Res.string.collections_editor_media_series_suffix)
|
||||
val baseTitle = state.tmdbTitleInput.ifBlank {
|
||||
when (sourceType) {
|
||||
TmdbCollectionSourceType.LIST -> getString(Res.string.collections_editor_tmdb_list_title_format, idArg).trim()
|
||||
TmdbCollectionSourceType.COLLECTION -> getString(Res.string.collections_editor_tmdb_collection_title_format, idArg).trim()
|
||||
TmdbCollectionSourceType.COMPANY -> getString(Res.string.collections_editor_tmdb_production_title_format, idArg).trim()
|
||||
TmdbCollectionSourceType.NETWORK -> getString(Res.string.collections_editor_tmdb_network_title_format, idArg).trim()
|
||||
TmdbCollectionSourceType.PERSON -> getString(Res.string.collections_editor_tmdb_person_title_format, idArg).trim()
|
||||
TmdbCollectionSourceType.DIRECTOR -> getString(Res.string.collections_editor_tmdb_director_title_format, idArg).trim()
|
||||
TmdbCollectionSourceType.DISCOVER -> getString(Res.string.collections_editor_tmdb_discover_title)
|
||||
TmdbCollectionSourceType.LIST -> "TMDB List ${id ?: ""}".trim()
|
||||
TmdbCollectionSourceType.COLLECTION -> "TMDB Collection ${id ?: ""}".trim()
|
||||
TmdbCollectionSourceType.COMPANY -> "TMDB Production ${id ?: ""}".trim()
|
||||
TmdbCollectionSourceType.NETWORK -> "TMDB Network ${id ?: ""}".trim()
|
||||
TmdbCollectionSourceType.PERSON -> "TMDB Person ${id ?: ""}".trim()
|
||||
TmdbCollectionSourceType.DIRECTOR -> "TMDB Director ${id ?: ""}".trim()
|
||||
TmdbCollectionSourceType.DISCOVER -> getString(Res.string.collections_editor_tmdb_discover)
|
||||
}
|
||||
}
|
||||
}
|
||||
val sources = mediaTypes.map { mediaType ->
|
||||
CollectionSource(
|
||||
provider = "tmdb",
|
||||
tmdbSourceType = sourceType.name,
|
||||
title = titleForMedia(baseTitle, mediaType, mediaTypes.size > 1),
|
||||
tmdbId = id,
|
||||
mediaType = mediaType.name,
|
||||
sortBy = state.tmdbSortBy,
|
||||
filters = state.tmdbFilters,
|
||||
)
|
||||
}
|
||||
if (sourceType == TmdbCollectionSourceType.LIST || sourceType == TmdbCollectionSourceType.COLLECTION) {
|
||||
scope.launch {
|
||||
val sources = mediaTypes.map { mediaType ->
|
||||
CollectionSource(
|
||||
provider = "tmdb",
|
||||
tmdbSourceType = sourceType.name,
|
||||
title = titleForMedia(baseTitle, mediaType, mediaTypes.size > 1, moviesSuffix, seriesSuffix),
|
||||
tmdbId = id,
|
||||
mediaType = mediaType.name,
|
||||
sortBy = state.tmdbSortBy,
|
||||
filters = state.tmdbFilters,
|
||||
)
|
||||
}
|
||||
if (sourceType == TmdbCollectionSourceType.LIST || sourceType == TmdbCollectionSourceType.COLLECTION) {
|
||||
val metadata = runCatching { TmdbCollectionSourceResolver.importMetadata(sourceType, id!!) }
|
||||
val resolved = metadata.getOrNull()
|
||||
if (metadata.isFailure) {
|
||||
_uiState.value = _uiState.value.copy(
|
||||
tmdbSearchError = metadata.exceptionOrNull()?.message
|
||||
?: getString(Res.string.collections_editor_tmdb_source_load_failed),
|
||||
?: getString(Res.string.collections_editor_tmdb_load_error),
|
||||
)
|
||||
return@launch
|
||||
}
|
||||
|
|
@ -695,10 +690,10 @@ object CollectionEditorRepository {
|
|||
},
|
||||
coverImageUrl = resolved?.coverImageUrl,
|
||||
)
|
||||
} else {
|
||||
addTmdbSourcesFromPicker(sources)
|
||||
}
|
||||
return
|
||||
}
|
||||
addTmdbSourcesFromPicker(sources)
|
||||
}
|
||||
|
||||
private fun addTmdbSources(sources: List<CollectionSource>, coverImageUrl: String? = null) {
|
||||
|
|
@ -730,20 +725,24 @@ object CollectionEditorRepository {
|
|||
val state = _uiState.value
|
||||
val input = state.traktInput.trim()
|
||||
if (input.isBlank()) {
|
||||
_uiState.value = state.copy(
|
||||
traktSearchError = runBlocking { getString(Res.string.collections_editor_trakt_enter_id_or_url) },
|
||||
)
|
||||
scope.launch {
|
||||
_uiState.value = _uiState.value.copy(
|
||||
traktSearchError = getString(Res.string.collections_editor_trakt_id_url_required),
|
||||
)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
scope.launch {
|
||||
val moviesSuffix = getString(Res.string.collections_editor_media_movies_suffix)
|
||||
val seriesSuffix = getString(Res.string.collections_editor_media_series_suffix)
|
||||
val metadata = runCatching { TraktPublicListSourceResolver.listImportMetadata(input) }
|
||||
val resolved = metadata.getOrNull()
|
||||
val listId = resolved?.traktListId
|
||||
if (metadata.isFailure || listId == null) {
|
||||
_uiState.value = _uiState.value.copy(
|
||||
traktSearchError = metadata.exceptionOrNull()?.message
|
||||
?: getString(Res.string.collections_editor_trakt_load_failed),
|
||||
?: getString(Res.string.collections_editor_trakt_load_error),
|
||||
)
|
||||
return@launch
|
||||
}
|
||||
|
|
@ -755,7 +754,7 @@ object CollectionEditorRepository {
|
|||
sources = selectedTraktMediaTypes(state).map { mediaType ->
|
||||
CollectionSource(
|
||||
provider = "trakt",
|
||||
title = titleForMedia(title, mediaType, state.traktMediaBoth),
|
||||
title = titleForMedia(title, mediaType, state.traktMediaBoth, moviesSuffix, seriesSuffix),
|
||||
traktListId = listId,
|
||||
mediaType = mediaType.name,
|
||||
sortBy = TraktListSort.normalize(state.traktSortBy),
|
||||
|
|
@ -770,19 +769,23 @@ object CollectionEditorRepository {
|
|||
fun addTraktSourceFromResult(result: TraktPublicListSearchResult) {
|
||||
val state = _uiState.value
|
||||
val title = state.traktTitleInput.ifBlank { result.title }
|
||||
addTraktSourcesToFolder(
|
||||
sources = selectedTraktMediaTypes(state).map { mediaType ->
|
||||
CollectionSource(
|
||||
provider = "trakt",
|
||||
title = titleForMedia(title, mediaType, state.traktMediaBoth),
|
||||
traktListId = result.traktListId,
|
||||
mediaType = mediaType.name,
|
||||
sortBy = TraktListSort.normalize(state.traktSortBy),
|
||||
sortHow = TraktSortHow.normalize(state.traktSortHow),
|
||||
)
|
||||
},
|
||||
coverImageUrl = result.coverImageUrl,
|
||||
)
|
||||
scope.launch {
|
||||
val moviesSuffix = getString(Res.string.collections_editor_media_movies_suffix)
|
||||
val seriesSuffix = getString(Res.string.collections_editor_media_series_suffix)
|
||||
addTraktSourcesToFolder(
|
||||
sources = selectedTraktMediaTypes(state).map { mediaType ->
|
||||
CollectionSource(
|
||||
provider = "trakt",
|
||||
title = titleForMedia(title, mediaType, state.traktMediaBoth, moviesSuffix, seriesSuffix),
|
||||
traktListId = result.traktListId,
|
||||
mediaType = mediaType.name,
|
||||
sortBy = TraktListSort.normalize(state.traktSortBy),
|
||||
sortHow = TraktSortHow.normalize(state.traktSortHow),
|
||||
)
|
||||
},
|
||||
coverImageUrl = result.coverImageUrl,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun addTraktSourcesToFolder(sources: List<CollectionSource>, coverImageUrl: String? = null) {
|
||||
|
|
@ -913,15 +916,13 @@ private fun titleForMedia(
|
|||
title: String,
|
||||
mediaType: TmdbCollectionMediaType,
|
||||
addSuffix: Boolean,
|
||||
moviesSuffix: String,
|
||||
seriesSuffix: String,
|
||||
): String {
|
||||
if (!addSuffix) return title
|
||||
val suffix = runBlocking {
|
||||
getString(
|
||||
when (mediaType) {
|
||||
TmdbCollectionMediaType.MOVIE -> Res.string.media_movies
|
||||
TmdbCollectionMediaType.TV -> Res.string.media_series
|
||||
},
|
||||
)
|
||||
val suffix = when (mediaType) {
|
||||
TmdbCollectionMediaType.MOVIE -> moviesSuffix
|
||||
TmdbCollectionMediaType.TV -> seriesSuffix
|
||||
}
|
||||
return "$title $suffix"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1269,7 +1269,7 @@ private fun TmdbSourcePickerScreen(
|
|||
label = stringResource(Res.string.collections_editor_tmdb_country),
|
||||
helper = stringResource(Res.string.collections_editor_tmdb_country_helper),
|
||||
value = state.tmdbFilters.withOriginCountry.orEmpty(),
|
||||
placeholder = stringResource(Res.string.collections_editor_tmdb_country_placeholder),
|
||||
placeholder = stringResource(Res.string.collections_editor_watch_region_placeholder),
|
||||
onValueChange = { value ->
|
||||
CollectionEditorRepository.updateTmdbFilters {
|
||||
it.copy(withOriginCountry = value.ifBlank { null })
|
||||
|
|
@ -1387,9 +1387,9 @@ private fun TmdbSourcePickerScreen(
|
|||
chips = listOf(
|
||||
stringResource(Res.string.collections_editor_tmdb_country_us) to "US",
|
||||
stringResource(Res.string.collections_editor_tmdb_country_uk) to "GB",
|
||||
"Canada" to "CA",
|
||||
"Australia" to "AU",
|
||||
"Germany" to "DE",
|
||||
stringResource(Res.string.collections_editor_tmdb_country_ca) to "CA",
|
||||
stringResource(Res.string.collections_editor_tmdb_country_au) to "AU",
|
||||
stringResource(Res.string.collections_editor_tmdb_country_de) to "DE",
|
||||
),
|
||||
onSelect = { value ->
|
||||
CollectionEditorRepository.updateTmdbFilters { it.copy(watchRegion = value) }
|
||||
|
|
|
|||
|
|
@ -14,17 +14,7 @@ import kotlinx.serialization.SerialName
|
|||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.Json
|
||||
import nuvio.composeapp.generated.resources.Res
|
||||
import nuvio.composeapp.generated.resources.collections_tmdb_api_key_required
|
||||
import nuvio.composeapp.generated.resources.collections_tmdb_collection_not_found
|
||||
import nuvio.composeapp.generated.resources.collections_tmdb_company_not_found
|
||||
import nuvio.composeapp.generated.resources.collections_tmdb_discover_no_data
|
||||
import nuvio.composeapp.generated.resources.collections_tmdb_list_not_found
|
||||
import nuvio.composeapp.generated.resources.collections_tmdb_missing_collection_id
|
||||
import nuvio.composeapp.generated.resources.collections_tmdb_missing_list_id
|
||||
import nuvio.composeapp.generated.resources.collections_tmdb_missing_person_id
|
||||
import nuvio.composeapp.generated.resources.collections_tmdb_network_not_found
|
||||
import nuvio.composeapp.generated.resources.collections_tmdb_person_credits_not_found
|
||||
import nuvio.composeapp.generated.resources.collections_tmdb_person_not_found
|
||||
import nuvio.composeapp.generated.resources.collections_editor_tmdb_discover
|
||||
import org.jetbrains.compose.resources.getString
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
|
|
@ -113,7 +103,7 @@ object TmdbCollectionSourceResolver {
|
|||
)
|
||||
}
|
||||
|
||||
TmdbCollectionSourceType.DISCOVER -> TmdbSourceImportMetadata(title = "TMDB Discover")
|
||||
TmdbCollectionSourceType.DISCOVER -> TmdbSourceImportMetadata(title = getString(Res.string.collections_editor_tmdb_discover))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -41,6 +41,22 @@ import androidx.compose.ui.draw.clip
|
|||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import nuvio.composeapp.generated.resources.Res
|
||||
import nuvio.composeapp.generated.resources.player_video_settings_brightness
|
||||
import nuvio.composeapp.generated.resources.player_video_settings_contrast
|
||||
import nuvio.composeapp.generated.resources.player_video_settings_deband
|
||||
import nuvio.composeapp.generated.resources.player_video_settings_deband_desc
|
||||
import nuvio.composeapp.generated.resources.player_video_settings_gamma
|
||||
import nuvio.composeapp.generated.resources.player_video_settings_hdr_peak_detection
|
||||
import nuvio.composeapp.generated.resources.player_video_settings_hdr_peak_detection_desc
|
||||
import nuvio.composeapp.generated.resources.player_video_settings_interpolation
|
||||
import nuvio.composeapp.generated.resources.player_video_settings_interpolation_desc
|
||||
import nuvio.composeapp.generated.resources.player_video_settings_output_preset
|
||||
import nuvio.composeapp.generated.resources.player_video_settings_reset_tuning
|
||||
import nuvio.composeapp.generated.resources.player_video_settings_saturation
|
||||
import nuvio.composeapp.generated.resources.player_video_settings_title
|
||||
import nuvio.composeapp.generated.resources.player_video_settings_tone_mapping
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
@Composable
|
||||
|
|
@ -96,7 +112,7 @@ internal fun IosVideoSettingsModal(
|
|||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
text = "Video",
|
||||
text = stringResource(Res.string.player_video_settings_title),
|
||||
color = colorScheme.onSurface,
|
||||
fontSize = 18.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
|
|
@ -106,7 +122,7 @@ internal fun IosVideoSettingsModal(
|
|||
PlayerSettingsRepository.resetIosVideoOutputTuning()
|
||||
onSettingsChanged()
|
||||
}) {
|
||||
Text("Reset tuning")
|
||||
Text(stringResource(Res.string.player_video_settings_reset_tuning))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -118,11 +134,11 @@ internal fun IosVideoSettingsModal(
|
|||
verticalArrangement = Arrangement.spacedBy(14.dp),
|
||||
) {
|
||||
OptionGroup(
|
||||
title = "Output preset",
|
||||
title = stringResource(Res.string.player_video_settings_output_preset),
|
||||
options = IosVideoOutputPreset.entries,
|
||||
selected = settings.iosVideoOutputPreset,
|
||||
label = { it.label },
|
||||
description = { it.description },
|
||||
label = { it.localizedLabel() },
|
||||
description = { it.localizedDescription() },
|
||||
onSelect = {
|
||||
PlayerSettingsRepository.setIosVideoOutputPreset(it)
|
||||
onSettingsChanged()
|
||||
|
|
@ -130,8 +146,8 @@ internal fun IosVideoSettingsModal(
|
|||
)
|
||||
|
||||
ToggleRow(
|
||||
title = "HDR peak detection",
|
||||
description = "Estimate HDR peak brightness when metadata is bad or missing.",
|
||||
title = stringResource(Res.string.player_video_settings_hdr_peak_detection),
|
||||
description = stringResource(Res.string.player_video_settings_hdr_peak_detection_desc),
|
||||
checked = settings.iosHdrComputePeakEnabled,
|
||||
onCheckedChange = {
|
||||
PlayerSettingsRepository.setIosHdrComputePeakEnabled(it)
|
||||
|
|
@ -140,7 +156,7 @@ internal fun IosVideoSettingsModal(
|
|||
)
|
||||
|
||||
OptionGroup(
|
||||
title = "Tone mapping",
|
||||
title = stringResource(Res.string.player_video_settings_tone_mapping),
|
||||
options = IosToneMappingMode.entries,
|
||||
selected = settings.iosToneMappingMode,
|
||||
label = { it.label },
|
||||
|
|
@ -151,8 +167,8 @@ internal fun IosVideoSettingsModal(
|
|||
)
|
||||
|
||||
ToggleRow(
|
||||
title = "Deband",
|
||||
description = "Reduce color banding at a small performance cost.",
|
||||
title = stringResource(Res.string.player_video_settings_deband),
|
||||
description = stringResource(Res.string.player_video_settings_deband_desc),
|
||||
checked = settings.iosDebandEnabled,
|
||||
onCheckedChange = {
|
||||
PlayerSettingsRepository.setIosDebandEnabled(it)
|
||||
|
|
@ -160,8 +176,8 @@ internal fun IosVideoSettingsModal(
|
|||
},
|
||||
)
|
||||
ToggleRow(
|
||||
title = "Frame interpolation",
|
||||
description = "Smooth motion when mpv can use display sync cleanly.",
|
||||
title = stringResource(Res.string.player_video_settings_interpolation),
|
||||
description = stringResource(Res.string.player_video_settings_interpolation_desc),
|
||||
checked = settings.iosInterpolationEnabled,
|
||||
onCheckedChange = {
|
||||
PlayerSettingsRepository.setIosInterpolationEnabled(it)
|
||||
|
|
@ -170,7 +186,7 @@ internal fun IosVideoSettingsModal(
|
|||
)
|
||||
|
||||
PictureSlider(
|
||||
title = "Brightness",
|
||||
title = stringResource(Res.string.player_video_settings_brightness),
|
||||
value = settings.iosBrightness,
|
||||
onValueChanged = {
|
||||
PlayerSettingsRepository.setIosBrightness(it)
|
||||
|
|
@ -178,7 +194,7 @@ internal fun IosVideoSettingsModal(
|
|||
},
|
||||
)
|
||||
PictureSlider(
|
||||
title = "Contrast",
|
||||
title = stringResource(Res.string.player_video_settings_contrast),
|
||||
value = settings.iosContrast,
|
||||
onValueChanged = {
|
||||
PlayerSettingsRepository.setIosContrast(it)
|
||||
|
|
@ -186,7 +202,7 @@ internal fun IosVideoSettingsModal(
|
|||
},
|
||||
)
|
||||
PictureSlider(
|
||||
title = "Saturation",
|
||||
title = stringResource(Res.string.player_video_settings_saturation),
|
||||
value = settings.iosSaturation,
|
||||
onValueChanged = {
|
||||
PlayerSettingsRepository.setIosSaturation(it)
|
||||
|
|
@ -194,7 +210,7 @@ internal fun IosVideoSettingsModal(
|
|||
},
|
||||
)
|
||||
PictureSlider(
|
||||
title = "Gamma",
|
||||
title = stringResource(Res.string.player_video_settings_gamma),
|
||||
value = settings.iosGamma,
|
||||
onValueChanged = {
|
||||
PlayerSettingsRepository.setIosGamma(it)
|
||||
|
|
@ -260,8 +276,8 @@ private fun <T> OptionGroup(
|
|||
title: String,
|
||||
options: List<T>,
|
||||
selected: T,
|
||||
label: (T) -> String,
|
||||
description: ((T) -> String)? = null,
|
||||
label: @Composable (T) -> String,
|
||||
description: @Composable ((T) -> String)? = null,
|
||||
onSelect: (T) -> Unit,
|
||||
) {
|
||||
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
|
|
|
|||
|
|
@ -308,7 +308,7 @@ private fun PlayerHeader(
|
|||
if (onSubmitIntroClick != null) {
|
||||
PlayerHeaderIconButton(
|
||||
icon = Icons.Rounded.Flag,
|
||||
contentDescription = stringResource(Res.string.submit_intro_action),
|
||||
contentDescription = stringResource(Res.string.player_action_submit_intro),
|
||||
buttonSize = metrics.headerIconSize + 16.dp,
|
||||
iconSize = metrics.headerIconSize,
|
||||
onClick = onSubmitIntroClick,
|
||||
|
|
@ -328,7 +328,7 @@ private fun PlayerHeader(
|
|||
if (onVideoSettingsClick != null) {
|
||||
PlayerHeaderIconButton(
|
||||
icon = Icons.Rounded.Build,
|
||||
contentDescription = "Video settings",
|
||||
contentDescription = stringResource(Res.string.player_action_video_settings),
|
||||
buttonSize = metrics.headerIconSize + 16.dp,
|
||||
iconSize = metrics.headerIconSize,
|
||||
onClick = onVideoSettingsClick,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,18 @@
|
|||
package com.nuvio.app.features.player
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import kotlinx.serialization.Serializable
|
||||
import nuvio.composeapp.generated.resources.Res
|
||||
import nuvio.composeapp.generated.resources.player_ios_hardware_decoder_off
|
||||
import nuvio.composeapp.generated.resources.player_ios_preset_compatibility_desc
|
||||
import nuvio.composeapp.generated.resources.player_ios_preset_compatibility_label
|
||||
import nuvio.composeapp.generated.resources.player_ios_preset_custom_desc
|
||||
import nuvio.composeapp.generated.resources.player_ios_preset_custom_label
|
||||
import nuvio.composeapp.generated.resources.player_ios_preset_native_edr_desc
|
||||
import nuvio.composeapp.generated.resources.player_ios_preset_native_edr_label
|
||||
import nuvio.composeapp.generated.resources.player_ios_preset_sdr_tone_mapped_desc
|
||||
import nuvio.composeapp.generated.resources.player_ios_preset_sdr_tone_mapped_label
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
|
||||
@Serializable
|
||||
data class PlayerRoute(
|
||||
|
|
@ -129,6 +141,28 @@ enum class IosHardwareDecoderMode(
|
|||
Off("no", "Off"),
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun IosVideoOutputPreset.localizedLabel(): String = when (this) {
|
||||
IosVideoOutputPreset.NativeEdr -> stringResource(Res.string.player_ios_preset_native_edr_label)
|
||||
IosVideoOutputPreset.SdrToneMapped -> stringResource(Res.string.player_ios_preset_sdr_tone_mapped_label)
|
||||
IosVideoOutputPreset.Compatibility -> stringResource(Res.string.player_ios_preset_compatibility_label)
|
||||
IosVideoOutputPreset.Custom -> stringResource(Res.string.player_ios_preset_custom_label)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun IosVideoOutputPreset.localizedDescription(): String = when (this) {
|
||||
IosVideoOutputPreset.NativeEdr -> stringResource(Res.string.player_ios_preset_native_edr_desc)
|
||||
IosVideoOutputPreset.SdrToneMapped -> stringResource(Res.string.player_ios_preset_sdr_tone_mapped_desc)
|
||||
IosVideoOutputPreset.Compatibility -> stringResource(Res.string.player_ios_preset_compatibility_desc)
|
||||
IosVideoOutputPreset.Custom -> stringResource(Res.string.player_ios_preset_custom_desc)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun IosHardwareDecoderMode.localizedLabel(): String = when (this) {
|
||||
IosHardwareDecoderMode.Off -> stringResource(Res.string.player_ios_hardware_decoder_off)
|
||||
else -> label
|
||||
}
|
||||
|
||||
data class PlayerPlaybackSnapshot(
|
||||
val isLoading: Boolean = true,
|
||||
val isPlaying: Boolean = false,
|
||||
|
|
|
|||
|
|
@ -52,14 +52,14 @@ import kotlinx.coroutines.launch
|
|||
import nuvio.composeapp.generated.resources.Res
|
||||
import nuvio.composeapp.generated.resources.action_cancel
|
||||
import nuvio.composeapp.generated.resources.action_close
|
||||
import nuvio.composeapp.generated.resources.submit_intro_button_submit
|
||||
import nuvio.composeapp.generated.resources.submit_intro_capture_button
|
||||
import nuvio.composeapp.generated.resources.submit_intro_end_time_label
|
||||
import nuvio.composeapp.generated.resources.submit_intro_capture
|
||||
import nuvio.composeapp.generated.resources.submit_intro_end_time
|
||||
import nuvio.composeapp.generated.resources.submit_intro_segment_intro
|
||||
import nuvio.composeapp.generated.resources.submit_intro_segment_outro
|
||||
import nuvio.composeapp.generated.resources.submit_intro_segment_recap
|
||||
import nuvio.composeapp.generated.resources.submit_intro_segment_type_label
|
||||
import nuvio.composeapp.generated.resources.submit_intro_start_time_label
|
||||
import nuvio.composeapp.generated.resources.submit_intro_segment_type
|
||||
import nuvio.composeapp.generated.resources.submit_intro_start_time
|
||||
import nuvio.composeapp.generated.resources.submit_intro_submit
|
||||
import nuvio.composeapp.generated.resources.submit_intro_title
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
import kotlin.math.floor
|
||||
|
|
@ -110,18 +110,14 @@ fun SubmitIntroDialog(
|
|||
fontWeight = FontWeight.Bold,
|
||||
)
|
||||
IconButton(onClick = onDismiss) {
|
||||
Icon(
|
||||
Icons.Rounded.Close,
|
||||
contentDescription = stringResource(Res.string.action_close),
|
||||
tint = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
Icon(Icons.Rounded.Close, contentDescription = stringResource(Res.string.action_close), tint = MaterialTheme.colorScheme.onSurfaceVariant)
|
||||
}
|
||||
}
|
||||
|
||||
// Segment Type
|
||||
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
Text(
|
||||
text = stringResource(Res.string.submit_intro_segment_type_label),
|
||||
text = stringResource(Res.string.submit_intro_segment_type),
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
|
|
@ -156,7 +152,7 @@ fun SubmitIntroDialog(
|
|||
|
||||
// Start Time
|
||||
TimeInputRow(
|
||||
label = stringResource(Res.string.submit_intro_start_time_label),
|
||||
label = stringResource(Res.string.submit_intro_start_time),
|
||||
value = startTimeStr,
|
||||
onValueChange = onStartTimeChange,
|
||||
onCapture = { onStartTimeChange(formatSecondsToMMSS(currentTimeSec)) }
|
||||
|
|
@ -164,7 +160,7 @@ fun SubmitIntroDialog(
|
|||
|
||||
// End Time
|
||||
TimeInputRow(
|
||||
label = stringResource(Res.string.submit_intro_end_time_label),
|
||||
label = stringResource(Res.string.submit_intro_end_time),
|
||||
value = endTimeStr,
|
||||
onValueChange = onEndTimeChange,
|
||||
onCapture = { onEndTimeChange(formatSecondsToMMSS(currentTimeSec)) }
|
||||
|
|
@ -234,7 +230,7 @@ fun SubmitIntroDialog(
|
|||
) {
|
||||
Icon(Icons.Rounded.Send, contentDescription = null, tint = MaterialTheme.colorScheme.onPrimary, modifier = Modifier.size(18.dp))
|
||||
Text(
|
||||
text = stringResource(Res.string.submit_intro_button_submit),
|
||||
text = stringResource(Res.string.submit_intro_submit),
|
||||
color = MaterialTheme.colorScheme.onPrimary,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
|
|
@ -345,7 +341,7 @@ private fun TimeInputRow(
|
|||
modifier = Modifier.size(18.dp)
|
||||
)
|
||||
Text(
|
||||
text = stringResource(Res.string.submit_intro_capture_button),
|
||||
text = stringResource(Res.string.submit_intro_capture),
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
fontWeight = FontWeight.SemiBold
|
||||
|
|
|
|||
|
|
@ -120,8 +120,82 @@ import nuvio.composeapp.generated.resources.settings_debrid_resolve_with_descrip
|
|||
import nuvio.composeapp.generated.resources.settings_debrid_section_instant_playback
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_section_formatting
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_section_providers
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_section_result_management
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_section_title
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_max_results
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_max_results_desc
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_sort_results
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_sort_results_desc
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_per_resolution_limit
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_per_resolution_limit_desc
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_per_quality_limit
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_per_quality_limit_desc
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_size_range
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_size_range_desc
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_learn_more
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_template_default_format
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_release_groups_hint
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_sort_default
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_sort_largest
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_sort_smallest
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_sort_best_audio
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_sort_language
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_selection_any
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_selection_count
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_results_all
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_results_count
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_size_up_to
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_size_min
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_size_range_value
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_rule_preferred_resolutions
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_rule_preferred_resolutions_desc
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_rule_required_resolutions
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_rule_required_resolutions_desc
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_rule_excluded_resolutions
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_rule_excluded_resolutions_desc
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_rule_preferred_qualities
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_rule_preferred_qualities_desc
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_rule_required_qualities
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_rule_required_qualities_desc
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_rule_excluded_qualities
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_rule_excluded_qualities_desc
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_rule_preferred_visual_tags
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_rule_preferred_visual_tags_desc
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_rule_required_visual_tags
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_rule_required_visual_tags_desc
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_rule_excluded_visual_tags
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_rule_excluded_visual_tags_desc
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_rule_preferred_audio_tags
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_rule_preferred_audio_tags_desc
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_rule_required_audio_tags
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_rule_required_audio_tags_desc
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_rule_excluded_audio_tags
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_rule_excluded_audio_tags_desc
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_rule_preferred_channels
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_rule_preferred_channels_desc
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_rule_required_channels
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_rule_required_channels_desc
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_rule_excluded_channels
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_rule_excluded_channels_desc
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_rule_preferred_encodes
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_rule_preferred_encodes_desc
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_rule_required_encodes
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_rule_required_encodes_desc
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_rule_excluded_encodes
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_rule_excluded_encodes_desc
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_rule_preferred_languages
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_rule_preferred_languages_desc
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_rule_required_languages
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_rule_required_languages_desc
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_rule_excluded_languages
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_rule_excluded_languages_desc
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_rule_required_release_groups
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_rule_required_release_groups_desc
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_rule_excluded_release_groups
|
||||
import nuvio.composeapp.generated.resources.settings_debrid_rule_excluded_release_groups_desc
|
||||
import org.jetbrains.compose.resources.getString
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
||||
private const val CLOUD_SERVICES_FAQ_URL = "https://nuvioapp.space/faq#common-cloud-library-and-cloud-services"
|
||||
|
||||
|
|
@ -319,14 +393,14 @@ internal fun LazyListScope.debridSettingsContent(
|
|||
val rows = debridRuleRows(preferences)
|
||||
|
||||
SettingsSection(
|
||||
title = "Result Management",
|
||||
title = stringResource(Res.string.settings_debrid_section_result_management),
|
||||
isTablet = isTablet,
|
||||
) {
|
||||
SettingsGroup(isTablet = isTablet) {
|
||||
DebridPreferenceRow(
|
||||
isTablet = isTablet,
|
||||
title = "Max results",
|
||||
description = "Limit how many results appear.",
|
||||
title = stringResource(Res.string.settings_debrid_max_results),
|
||||
description = stringResource(Res.string.settings_debrid_max_results_desc),
|
||||
value = streamMaxResultsLabel(preferences.maxResults),
|
||||
enabled = settings.canResolvePlayableLinks,
|
||||
onClick = { activeStreamPicker = DebridStreamPicker.MAX_RESULTS },
|
||||
|
|
@ -334,8 +408,8 @@ internal fun LazyListScope.debridSettingsContent(
|
|||
SettingsGroupDivider(isTablet = isTablet)
|
||||
DebridPreferenceRow(
|
||||
isTablet = isTablet,
|
||||
title = "Sort results",
|
||||
description = "Choose how results are ordered.",
|
||||
title = stringResource(Res.string.settings_debrid_sort_results),
|
||||
description = stringResource(Res.string.settings_debrid_sort_results_desc),
|
||||
value = sortProfileLabel(preferences.sortCriteria),
|
||||
enabled = settings.canResolvePlayableLinks,
|
||||
onClick = { activeStreamPicker = DebridStreamPicker.SORT_MODE },
|
||||
|
|
@ -343,8 +417,8 @@ internal fun LazyListScope.debridSettingsContent(
|
|||
SettingsGroupDivider(isTablet = isTablet)
|
||||
DebridPreferenceRow(
|
||||
isTablet = isTablet,
|
||||
title = "Per resolution limit",
|
||||
description = "Cap repeated 2160p, 1080p, 720p results after sorting.",
|
||||
title = stringResource(Res.string.settings_debrid_per_resolution_limit),
|
||||
description = stringResource(Res.string.settings_debrid_per_resolution_limit_desc),
|
||||
value = streamMaxResultsLabel(preferences.maxPerResolution),
|
||||
enabled = settings.canResolvePlayableLinks,
|
||||
onClick = { activeStreamPicker = DebridStreamPicker.MAX_PER_RESOLUTION },
|
||||
|
|
@ -352,8 +426,8 @@ internal fun LazyListScope.debridSettingsContent(
|
|||
SettingsGroupDivider(isTablet = isTablet)
|
||||
DebridPreferenceRow(
|
||||
isTablet = isTablet,
|
||||
title = "Per quality limit",
|
||||
description = "Cap repeated BluRay, WEB-DL, REMUX results after sorting.",
|
||||
title = stringResource(Res.string.settings_debrid_per_quality_limit),
|
||||
description = stringResource(Res.string.settings_debrid_per_quality_limit_desc),
|
||||
value = streamMaxResultsLabel(preferences.maxPerQuality),
|
||||
enabled = settings.canResolvePlayableLinks,
|
||||
onClick = { activeStreamPicker = DebridStreamPicker.MAX_PER_QUALITY },
|
||||
|
|
@ -361,8 +435,8 @@ internal fun LazyListScope.debridSettingsContent(
|
|||
SettingsGroupDivider(isTablet = isTablet)
|
||||
DebridPreferenceRow(
|
||||
isTablet = isTablet,
|
||||
title = "Size range",
|
||||
description = "Filter results by file size.",
|
||||
title = stringResource(Res.string.settings_debrid_size_range),
|
||||
description = stringResource(Res.string.settings_debrid_size_range_desc),
|
||||
value = sizeRangeLabel(preferences),
|
||||
enabled = settings.canResolvePlayableLinks,
|
||||
onClick = { activeStreamPicker = DebridStreamPicker.SIZE_RANGE },
|
||||
|
|
@ -480,7 +554,7 @@ private fun DebridLearnMoreFooter(
|
|||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
TextButton(onClick = onClick) {
|
||||
Text("Learn more")
|
||||
Text(stringResource(Res.string.settings_debrid_learn_more))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -491,12 +565,13 @@ private enum class DebridTemplateField {
|
|||
}
|
||||
|
||||
private fun templatePreview(value: String, defaultValue: String): String {
|
||||
if (value.trim().isBlank() || value.trim() == defaultValue.trim()) return "Default format"
|
||||
val defaultFormat = runBlocking { getString(Res.string.settings_debrid_template_default_format) }
|
||||
if (value.trim().isBlank() || value.trim() == defaultValue.trim()) return defaultFormat
|
||||
val firstLine = value
|
||||
.lineSequence()
|
||||
.map { it.trim() }
|
||||
.firstOrNull { it.isNotBlank() }
|
||||
?: return "Default format"
|
||||
?: return defaultFormat
|
||||
return if (firstLine.length <= 28) firstLine else "${firstLine.take(28)}..."
|
||||
}
|
||||
|
||||
|
|
@ -716,7 +791,7 @@ private fun DebridStreamPreferenceDialog(
|
|||
) {
|
||||
when (picker) {
|
||||
DebridStreamPicker.MAX_RESULTS -> DebridIntChoiceDialog(
|
||||
title = "Max results",
|
||||
title = stringResource(Res.string.settings_debrid_max_results),
|
||||
selectedValue = preferences.maxResults,
|
||||
options = listOf(0, 5, 10, 20, 50),
|
||||
label = { streamMaxResultsLabel(it) },
|
||||
|
|
@ -724,7 +799,7 @@ private fun DebridStreamPreferenceDialog(
|
|||
onDismiss = onDismiss,
|
||||
)
|
||||
DebridStreamPicker.MAX_PER_RESOLUTION -> DebridIntChoiceDialog(
|
||||
title = "Max results",
|
||||
title = stringResource(Res.string.settings_debrid_max_results),
|
||||
selectedValue = preferences.maxPerResolution,
|
||||
options = listOf(0, 1, 2, 3, 5),
|
||||
label = { streamMaxResultsLabel(it) },
|
||||
|
|
@ -732,7 +807,7 @@ private fun DebridStreamPreferenceDialog(
|
|||
onDismiss = onDismiss,
|
||||
)
|
||||
DebridStreamPicker.MAX_PER_QUALITY -> DebridIntChoiceDialog(
|
||||
title = "Max results",
|
||||
title = stringResource(Res.string.settings_debrid_max_results),
|
||||
selectedValue = preferences.maxPerQuality,
|
||||
options = listOf(0, 1, 2, 3, 5),
|
||||
label = { streamMaxResultsLabel(it) },
|
||||
|
|
@ -740,7 +815,7 @@ private fun DebridStreamPreferenceDialog(
|
|||
onDismiss = onDismiss,
|
||||
)
|
||||
DebridStreamPicker.SORT_MODE -> DebridSingleChoiceDialog(
|
||||
title = "Sort results",
|
||||
title = stringResource(Res.string.settings_debrid_sort_results),
|
||||
selectedValue = sortProfileFor(preferences.sortCriteria),
|
||||
options = listOf(
|
||||
DebridSortProfile.DEFAULT,
|
||||
|
|
@ -754,7 +829,7 @@ private fun DebridStreamPreferenceDialog(
|
|||
onDismiss = onDismiss,
|
||||
)
|
||||
DebridStreamPicker.SIZE_RANGE -> DebridSingleChoiceDialog(
|
||||
title = "Size range",
|
||||
title = stringResource(Res.string.settings_debrid_size_range),
|
||||
selectedValue = preferences.sizeMinGb to preferences.sizeMaxGb,
|
||||
options = listOf(0 to 0, 0 to 5, 0 to 10, 5 to 20, 10 to 50, 20 to 100),
|
||||
label = { sizeRangeLabel(it.first, it.second) },
|
||||
|
|
@ -762,7 +837,7 @@ private fun DebridStreamPreferenceDialog(
|
|||
onDismiss = onDismiss,
|
||||
)
|
||||
DebridStreamPicker.PREFERRED_RESOLUTIONS -> DebridMultiChoiceDialog(
|
||||
title = "Preferred resolutions",
|
||||
title = stringResource(Res.string.settings_debrid_rule_preferred_resolutions),
|
||||
selectedValues = preferences.preferredResolutions,
|
||||
values = DebridStreamResolution.defaultOrder,
|
||||
label = { it.label },
|
||||
|
|
@ -770,7 +845,7 @@ private fun DebridStreamPreferenceDialog(
|
|||
onDismiss = onDismiss,
|
||||
)
|
||||
DebridStreamPicker.REQUIRED_RESOLUTIONS -> DebridMultiChoiceDialog(
|
||||
title = "Required resolutions",
|
||||
title = stringResource(Res.string.settings_debrid_rule_required_resolutions),
|
||||
selectedValues = preferences.requiredResolutions,
|
||||
values = DebridStreamResolution.defaultOrder,
|
||||
label = { it.label },
|
||||
|
|
@ -778,7 +853,7 @@ private fun DebridStreamPreferenceDialog(
|
|||
onDismiss = onDismiss,
|
||||
)
|
||||
DebridStreamPicker.EXCLUDED_RESOLUTIONS -> DebridMultiChoiceDialog(
|
||||
title = "Excluded resolutions",
|
||||
title = stringResource(Res.string.settings_debrid_rule_excluded_resolutions),
|
||||
selectedValues = preferences.excludedResolutions,
|
||||
values = DebridStreamResolution.defaultOrder,
|
||||
label = { it.label },
|
||||
|
|
@ -786,7 +861,7 @@ private fun DebridStreamPreferenceDialog(
|
|||
onDismiss = onDismiss,
|
||||
)
|
||||
DebridStreamPicker.PREFERRED_QUALITIES -> DebridMultiChoiceDialog(
|
||||
title = "Preferred qualities",
|
||||
title = stringResource(Res.string.settings_debrid_rule_preferred_qualities),
|
||||
selectedValues = preferences.preferredQualities,
|
||||
values = DebridStreamQuality.defaultOrder,
|
||||
label = { it.label },
|
||||
|
|
@ -794,7 +869,7 @@ private fun DebridStreamPreferenceDialog(
|
|||
onDismiss = onDismiss,
|
||||
)
|
||||
DebridStreamPicker.REQUIRED_QUALITIES -> DebridMultiChoiceDialog(
|
||||
title = "Required qualities",
|
||||
title = stringResource(Res.string.settings_debrid_rule_required_qualities),
|
||||
selectedValues = preferences.requiredQualities,
|
||||
values = DebridStreamQuality.defaultOrder,
|
||||
label = { it.label },
|
||||
|
|
@ -802,7 +877,7 @@ private fun DebridStreamPreferenceDialog(
|
|||
onDismiss = onDismiss,
|
||||
)
|
||||
DebridStreamPicker.EXCLUDED_QUALITIES -> DebridMultiChoiceDialog(
|
||||
title = "Excluded qualities",
|
||||
title = stringResource(Res.string.settings_debrid_rule_excluded_qualities),
|
||||
selectedValues = preferences.excludedQualities,
|
||||
values = DebridStreamQuality.defaultOrder,
|
||||
label = { it.label },
|
||||
|
|
@ -810,7 +885,7 @@ private fun DebridStreamPreferenceDialog(
|
|||
onDismiss = onDismiss,
|
||||
)
|
||||
DebridStreamPicker.PREFERRED_VISUAL_TAGS -> DebridMultiChoiceDialog(
|
||||
title = "Preferred visual tags",
|
||||
title = stringResource(Res.string.settings_debrid_rule_preferred_visual_tags),
|
||||
selectedValues = preferences.preferredVisualTags,
|
||||
values = DebridStreamVisualTag.defaultOrder,
|
||||
label = { it.label },
|
||||
|
|
@ -818,7 +893,7 @@ private fun DebridStreamPreferenceDialog(
|
|||
onDismiss = onDismiss,
|
||||
)
|
||||
DebridStreamPicker.REQUIRED_VISUAL_TAGS -> DebridMultiChoiceDialog(
|
||||
title = "Required visual tags",
|
||||
title = stringResource(Res.string.settings_debrid_rule_required_visual_tags),
|
||||
selectedValues = preferences.requiredVisualTags,
|
||||
values = DebridStreamVisualTag.defaultOrder,
|
||||
label = { it.label },
|
||||
|
|
@ -826,7 +901,7 @@ private fun DebridStreamPreferenceDialog(
|
|||
onDismiss = onDismiss,
|
||||
)
|
||||
DebridStreamPicker.EXCLUDED_VISUAL_TAGS -> DebridMultiChoiceDialog(
|
||||
title = "Excluded visual tags",
|
||||
title = stringResource(Res.string.settings_debrid_rule_excluded_visual_tags),
|
||||
selectedValues = preferences.excludedVisualTags,
|
||||
values = DebridStreamVisualTag.defaultOrder,
|
||||
label = { it.label },
|
||||
|
|
@ -834,7 +909,7 @@ private fun DebridStreamPreferenceDialog(
|
|||
onDismiss = onDismiss,
|
||||
)
|
||||
DebridStreamPicker.PREFERRED_AUDIO_TAGS -> DebridMultiChoiceDialog(
|
||||
title = "Preferred audio tags",
|
||||
title = stringResource(Res.string.settings_debrid_rule_preferred_audio_tags),
|
||||
selectedValues = preferences.preferredAudioTags,
|
||||
values = DebridStreamAudioTag.defaultOrder,
|
||||
label = { it.label },
|
||||
|
|
@ -842,7 +917,7 @@ private fun DebridStreamPreferenceDialog(
|
|||
onDismiss = onDismiss,
|
||||
)
|
||||
DebridStreamPicker.REQUIRED_AUDIO_TAGS -> DebridMultiChoiceDialog(
|
||||
title = "Required audio tags",
|
||||
title = stringResource(Res.string.settings_debrid_rule_required_audio_tags),
|
||||
selectedValues = preferences.requiredAudioTags,
|
||||
values = DebridStreamAudioTag.defaultOrder,
|
||||
label = { it.label },
|
||||
|
|
@ -850,7 +925,7 @@ private fun DebridStreamPreferenceDialog(
|
|||
onDismiss = onDismiss,
|
||||
)
|
||||
DebridStreamPicker.EXCLUDED_AUDIO_TAGS -> DebridMultiChoiceDialog(
|
||||
title = "Excluded audio tags",
|
||||
title = stringResource(Res.string.settings_debrid_rule_excluded_audio_tags),
|
||||
selectedValues = preferences.excludedAudioTags,
|
||||
values = DebridStreamAudioTag.defaultOrder,
|
||||
label = { it.label },
|
||||
|
|
@ -858,7 +933,7 @@ private fun DebridStreamPreferenceDialog(
|
|||
onDismiss = onDismiss,
|
||||
)
|
||||
DebridStreamPicker.PREFERRED_AUDIO_CHANNELS -> DebridMultiChoiceDialog(
|
||||
title = "Preferred channels",
|
||||
title = stringResource(Res.string.settings_debrid_rule_preferred_channels),
|
||||
selectedValues = preferences.preferredAudioChannels,
|
||||
values = DebridStreamAudioChannel.defaultOrder,
|
||||
label = { it.label },
|
||||
|
|
@ -866,7 +941,7 @@ private fun DebridStreamPreferenceDialog(
|
|||
onDismiss = onDismiss,
|
||||
)
|
||||
DebridStreamPicker.REQUIRED_AUDIO_CHANNELS -> DebridMultiChoiceDialog(
|
||||
title = "Required channels",
|
||||
title = stringResource(Res.string.settings_debrid_rule_required_channels),
|
||||
selectedValues = preferences.requiredAudioChannels,
|
||||
values = DebridStreamAudioChannel.defaultOrder,
|
||||
label = { it.label },
|
||||
|
|
@ -874,7 +949,7 @@ private fun DebridStreamPreferenceDialog(
|
|||
onDismiss = onDismiss,
|
||||
)
|
||||
DebridStreamPicker.EXCLUDED_AUDIO_CHANNELS -> DebridMultiChoiceDialog(
|
||||
title = "Excluded channels",
|
||||
title = stringResource(Res.string.settings_debrid_rule_excluded_channels),
|
||||
selectedValues = preferences.excludedAudioChannels,
|
||||
values = DebridStreamAudioChannel.defaultOrder,
|
||||
label = { it.label },
|
||||
|
|
@ -882,7 +957,7 @@ private fun DebridStreamPreferenceDialog(
|
|||
onDismiss = onDismiss,
|
||||
)
|
||||
DebridStreamPicker.PREFERRED_ENCODES -> DebridMultiChoiceDialog(
|
||||
title = "Preferred encodes",
|
||||
title = stringResource(Res.string.settings_debrid_rule_preferred_encodes),
|
||||
selectedValues = preferences.preferredEncodes,
|
||||
values = DebridStreamEncode.defaultOrder,
|
||||
label = { it.label },
|
||||
|
|
@ -890,7 +965,7 @@ private fun DebridStreamPreferenceDialog(
|
|||
onDismiss = onDismiss,
|
||||
)
|
||||
DebridStreamPicker.REQUIRED_ENCODES -> DebridMultiChoiceDialog(
|
||||
title = "Required encodes",
|
||||
title = stringResource(Res.string.settings_debrid_rule_required_encodes),
|
||||
selectedValues = preferences.requiredEncodes,
|
||||
values = DebridStreamEncode.defaultOrder,
|
||||
label = { it.label },
|
||||
|
|
@ -898,7 +973,7 @@ private fun DebridStreamPreferenceDialog(
|
|||
onDismiss = onDismiss,
|
||||
)
|
||||
DebridStreamPicker.EXCLUDED_ENCODES -> DebridMultiChoiceDialog(
|
||||
title = "Excluded encodes",
|
||||
title = stringResource(Res.string.settings_debrid_rule_excluded_encodes),
|
||||
selectedValues = preferences.excludedEncodes,
|
||||
values = DebridStreamEncode.defaultOrder,
|
||||
label = { it.label },
|
||||
|
|
@ -906,7 +981,7 @@ private fun DebridStreamPreferenceDialog(
|
|||
onDismiss = onDismiss,
|
||||
)
|
||||
DebridStreamPicker.PREFERRED_LANGUAGES -> DebridMultiChoiceDialog(
|
||||
title = "Preferred languages",
|
||||
title = stringResource(Res.string.settings_debrid_rule_preferred_languages),
|
||||
selectedValues = preferences.preferredLanguages,
|
||||
values = DebridStreamLanguage.entries,
|
||||
label = { it.label },
|
||||
|
|
@ -914,7 +989,7 @@ private fun DebridStreamPreferenceDialog(
|
|||
onDismiss = onDismiss,
|
||||
)
|
||||
DebridStreamPicker.REQUIRED_LANGUAGES -> DebridMultiChoiceDialog(
|
||||
title = "Required languages",
|
||||
title = stringResource(Res.string.settings_debrid_rule_required_languages),
|
||||
selectedValues = preferences.requiredLanguages,
|
||||
values = DebridStreamLanguage.entries,
|
||||
label = { it.label },
|
||||
|
|
@ -922,7 +997,7 @@ private fun DebridStreamPreferenceDialog(
|
|||
onDismiss = onDismiss,
|
||||
)
|
||||
DebridStreamPicker.EXCLUDED_LANGUAGES -> DebridMultiChoiceDialog(
|
||||
title = "Excluded languages",
|
||||
title = stringResource(Res.string.settings_debrid_rule_excluded_languages),
|
||||
selectedValues = preferences.excludedLanguages,
|
||||
values = DebridStreamLanguage.entries,
|
||||
label = { it.label },
|
||||
|
|
@ -930,13 +1005,13 @@ private fun DebridStreamPreferenceDialog(
|
|||
onDismiss = onDismiss,
|
||||
)
|
||||
DebridStreamPicker.REQUIRED_RELEASE_GROUPS -> DebridTextListDialog(
|
||||
title = "Required release groups",
|
||||
title = stringResource(Res.string.settings_debrid_rule_required_release_groups),
|
||||
selectedValues = preferences.requiredReleaseGroups,
|
||||
onSelected = { value -> onPreferencesChanged(preferences.copy(requiredReleaseGroups = value)) },
|
||||
onDismiss = onDismiss,
|
||||
)
|
||||
DebridStreamPicker.EXCLUDED_RELEASE_GROUPS -> DebridTextListDialog(
|
||||
title = "Excluded release groups",
|
||||
title = stringResource(Res.string.settings_debrid_rule_excluded_release_groups),
|
||||
selectedValues = preferences.excludedReleaseGroups,
|
||||
onSelected = { value -> onPreferencesChanged(preferences.copy(excludedReleaseGroups = value)) },
|
||||
onDismiss = onDismiss,
|
||||
|
|
@ -1063,7 +1138,7 @@ private fun DebridTextListDialog(
|
|||
BasicAlertDialog(onDismissRequest = onDismiss) {
|
||||
DebridDialogSurface(title = title) {
|
||||
Text(
|
||||
text = "Enter one group per line.",
|
||||
text = stringResource(Res.string.settings_debrid_release_groups_hint),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
|
|
@ -1184,56 +1259,73 @@ private fun DebridDialogOptionRow(
|
|||
|
||||
@Composable
|
||||
private fun streamMaxResultsLabel(value: Int): String =
|
||||
if (value <= 0) "All results" else "$value results"
|
||||
|
||||
private fun sortProfileLabel(value: DebridSortProfile): String =
|
||||
when (value) {
|
||||
DebridSortProfile.DEFAULT -> "Default"
|
||||
DebridSortProfile.LARGEST -> "Largest first"
|
||||
DebridSortProfile.SMALLEST -> "Smallest first"
|
||||
DebridSortProfile.AUDIO -> "Best audio first"
|
||||
DebridSortProfile.LANGUAGE -> "Language first"
|
||||
if (value <= 0) {
|
||||
stringResource(Res.string.settings_debrid_results_all)
|
||||
} else {
|
||||
stringResource(Res.string.settings_debrid_results_count, value)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun sortProfileLabel(value: DebridSortProfile): String =
|
||||
when (value) {
|
||||
DebridSortProfile.DEFAULT -> stringResource(Res.string.settings_debrid_sort_default)
|
||||
DebridSortProfile.LARGEST -> stringResource(Res.string.settings_debrid_sort_largest)
|
||||
DebridSortProfile.SMALLEST -> stringResource(Res.string.settings_debrid_sort_smallest)
|
||||
DebridSortProfile.AUDIO -> stringResource(Res.string.settings_debrid_sort_best_audio)
|
||||
DebridSortProfile.LANGUAGE -> stringResource(Res.string.settings_debrid_sort_language)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun sortProfileLabel(criteria: List<DebridStreamSortCriterion>): String =
|
||||
sortProfileLabel(sortProfileFor(criteria))
|
||||
|
||||
@Composable
|
||||
private fun debridRuleRows(preferences: DebridStreamPreferences): List<DebridRuleRow> =
|
||||
listOf(
|
||||
DebridRuleRow(DebridStreamPicker.PREFERRED_RESOLUTIONS, "Preferred resolutions", "Sort selected resolutions first, in default order.", selectionCountLabel(preferences.preferredResolutions)),
|
||||
DebridRuleRow(DebridStreamPicker.REQUIRED_RESOLUTIONS, "Required resolutions", "Only show selected resolutions.", selectionCountLabel(preferences.requiredResolutions)),
|
||||
DebridRuleRow(DebridStreamPicker.EXCLUDED_RESOLUTIONS, "Excluded resolutions", "Hide selected resolutions.", selectionCountLabel(preferences.excludedResolutions)),
|
||||
DebridRuleRow(DebridStreamPicker.PREFERRED_QUALITIES, "Preferred qualities", "Sort selected qualities first, in default order.", selectionCountLabel(preferences.preferredQualities)),
|
||||
DebridRuleRow(DebridStreamPicker.REQUIRED_QUALITIES, "Required qualities", "Only show selected qualities.", selectionCountLabel(preferences.requiredQualities)),
|
||||
DebridRuleRow(DebridStreamPicker.EXCLUDED_QUALITIES, "Excluded qualities", "Hide selected qualities.", selectionCountLabel(preferences.excludedQualities)),
|
||||
DebridRuleRow(DebridStreamPicker.PREFERRED_VISUAL_TAGS, "Preferred visual tags", "Sort DV, HDR, 10bit, IMAX and similar tags.", selectionCountLabel(preferences.preferredVisualTags)),
|
||||
DebridRuleRow(DebridStreamPicker.REQUIRED_VISUAL_TAGS, "Required visual tags", "Require DV, HDR, 10bit, IMAX, SDR and similar tags.", selectionCountLabel(preferences.requiredVisualTags)),
|
||||
DebridRuleRow(DebridStreamPicker.EXCLUDED_VISUAL_TAGS, "Excluded visual tags", "Hide DV, HDR, 10bit, 3D and similar tags.", selectionCountLabel(preferences.excludedVisualTags)),
|
||||
DebridRuleRow(DebridStreamPicker.PREFERRED_AUDIO_TAGS, "Preferred audio tags", "Sort Atmos, TrueHD, DTS, AAC and similar tags.", selectionCountLabel(preferences.preferredAudioTags)),
|
||||
DebridRuleRow(DebridStreamPicker.REQUIRED_AUDIO_TAGS, "Required audio tags", "Require Atmos, TrueHD, DTS, AAC and similar tags.", selectionCountLabel(preferences.requiredAudioTags)),
|
||||
DebridRuleRow(DebridStreamPicker.EXCLUDED_AUDIO_TAGS, "Excluded audio tags", "Hide selected audio tags.", selectionCountLabel(preferences.excludedAudioTags)),
|
||||
DebridRuleRow(DebridStreamPicker.PREFERRED_AUDIO_CHANNELS, "Preferred channels", "Sort preferred channel layouts first.", selectionCountLabel(preferences.preferredAudioChannels)),
|
||||
DebridRuleRow(DebridStreamPicker.REQUIRED_AUDIO_CHANNELS, "Required channels", "Only show selected channel layouts.", selectionCountLabel(preferences.requiredAudioChannels)),
|
||||
DebridRuleRow(DebridStreamPicker.EXCLUDED_AUDIO_CHANNELS, "Excluded channels", "Hide selected channel layouts.", selectionCountLabel(preferences.excludedAudioChannels)),
|
||||
DebridRuleRow(DebridStreamPicker.PREFERRED_ENCODES, "Preferred encodes", "Sort AV1, HEVC, AVC and similar encodes.", selectionCountLabel(preferences.preferredEncodes)),
|
||||
DebridRuleRow(DebridStreamPicker.REQUIRED_ENCODES, "Required encodes", "Require AV1, HEVC, AVC and similar encodes.", selectionCountLabel(preferences.requiredEncodes)),
|
||||
DebridRuleRow(DebridStreamPicker.EXCLUDED_ENCODES, "Excluded encodes", "Hide selected encodes.", selectionCountLabel(preferences.excludedEncodes)),
|
||||
DebridRuleRow(DebridStreamPicker.PREFERRED_LANGUAGES, "Preferred languages", "Sort preferred audio languages first.", selectionCountLabel(preferences.preferredLanguages)),
|
||||
DebridRuleRow(DebridStreamPicker.REQUIRED_LANGUAGES, "Required languages", "Only show results with selected languages.", selectionCountLabel(preferences.requiredLanguages)),
|
||||
DebridRuleRow(DebridStreamPicker.EXCLUDED_LANGUAGES, "Excluded languages", "Hide results where every language is excluded.", selectionCountLabel(preferences.excludedLanguages)),
|
||||
DebridRuleRow(DebridStreamPicker.REQUIRED_RELEASE_GROUPS, "Required release groups", "Only show selected release groups.", selectionCountLabel(preferences.requiredReleaseGroups)),
|
||||
DebridRuleRow(DebridStreamPicker.EXCLUDED_RELEASE_GROUPS, "Excluded release groups", "Hide selected release groups.", selectionCountLabel(preferences.excludedReleaseGroups)),
|
||||
DebridRuleRow(DebridStreamPicker.PREFERRED_RESOLUTIONS, stringResource(Res.string.settings_debrid_rule_preferred_resolutions), stringResource(Res.string.settings_debrid_rule_preferred_resolutions_desc), selectionCountLabel(preferences.preferredResolutions)),
|
||||
DebridRuleRow(DebridStreamPicker.REQUIRED_RESOLUTIONS, stringResource(Res.string.settings_debrid_rule_required_resolutions), stringResource(Res.string.settings_debrid_rule_required_resolutions_desc), selectionCountLabel(preferences.requiredResolutions)),
|
||||
DebridRuleRow(DebridStreamPicker.EXCLUDED_RESOLUTIONS, stringResource(Res.string.settings_debrid_rule_excluded_resolutions), stringResource(Res.string.settings_debrid_rule_excluded_resolutions_desc), selectionCountLabel(preferences.excludedResolutions)),
|
||||
DebridRuleRow(DebridStreamPicker.PREFERRED_QUALITIES, stringResource(Res.string.settings_debrid_rule_preferred_qualities), stringResource(Res.string.settings_debrid_rule_preferred_qualities_desc), selectionCountLabel(preferences.preferredQualities)),
|
||||
DebridRuleRow(DebridStreamPicker.REQUIRED_QUALITIES, stringResource(Res.string.settings_debrid_rule_required_qualities), stringResource(Res.string.settings_debrid_rule_required_qualities_desc), selectionCountLabel(preferences.requiredQualities)),
|
||||
DebridRuleRow(DebridStreamPicker.EXCLUDED_QUALITIES, stringResource(Res.string.settings_debrid_rule_excluded_qualities), stringResource(Res.string.settings_debrid_rule_excluded_qualities_desc), selectionCountLabel(preferences.excludedQualities)),
|
||||
DebridRuleRow(DebridStreamPicker.PREFERRED_VISUAL_TAGS, stringResource(Res.string.settings_debrid_rule_preferred_visual_tags), stringResource(Res.string.settings_debrid_rule_preferred_visual_tags_desc), selectionCountLabel(preferences.preferredVisualTags)),
|
||||
DebridRuleRow(DebridStreamPicker.REQUIRED_VISUAL_TAGS, stringResource(Res.string.settings_debrid_rule_required_visual_tags), stringResource(Res.string.settings_debrid_rule_required_visual_tags_desc), selectionCountLabel(preferences.requiredVisualTags)),
|
||||
DebridRuleRow(DebridStreamPicker.EXCLUDED_VISUAL_TAGS, stringResource(Res.string.settings_debrid_rule_excluded_visual_tags), stringResource(Res.string.settings_debrid_rule_excluded_visual_tags_desc), selectionCountLabel(preferences.excludedVisualTags)),
|
||||
DebridRuleRow(DebridStreamPicker.PREFERRED_AUDIO_TAGS, stringResource(Res.string.settings_debrid_rule_preferred_audio_tags), stringResource(Res.string.settings_debrid_rule_preferred_audio_tags_desc), selectionCountLabel(preferences.preferredAudioTags)),
|
||||
DebridRuleRow(DebridStreamPicker.REQUIRED_AUDIO_TAGS, stringResource(Res.string.settings_debrid_rule_required_audio_tags), stringResource(Res.string.settings_debrid_rule_required_audio_tags_desc), selectionCountLabel(preferences.requiredAudioTags)),
|
||||
DebridRuleRow(DebridStreamPicker.EXCLUDED_AUDIO_TAGS, stringResource(Res.string.settings_debrid_rule_excluded_audio_tags), stringResource(Res.string.settings_debrid_rule_excluded_audio_tags_desc), selectionCountLabel(preferences.excludedAudioTags)),
|
||||
DebridRuleRow(DebridStreamPicker.PREFERRED_AUDIO_CHANNELS, stringResource(Res.string.settings_debrid_rule_preferred_channels), stringResource(Res.string.settings_debrid_rule_preferred_channels_desc), selectionCountLabel(preferences.preferredAudioChannels)),
|
||||
DebridRuleRow(DebridStreamPicker.REQUIRED_AUDIO_CHANNELS, stringResource(Res.string.settings_debrid_rule_required_channels), stringResource(Res.string.settings_debrid_rule_required_channels_desc), selectionCountLabel(preferences.requiredAudioChannels)),
|
||||
DebridRuleRow(DebridStreamPicker.EXCLUDED_AUDIO_CHANNELS, stringResource(Res.string.settings_debrid_rule_excluded_channels), stringResource(Res.string.settings_debrid_rule_excluded_channels_desc), selectionCountLabel(preferences.excludedAudioChannels)),
|
||||
DebridRuleRow(DebridStreamPicker.PREFERRED_ENCODES, stringResource(Res.string.settings_debrid_rule_preferred_encodes), stringResource(Res.string.settings_debrid_rule_preferred_encodes_desc), selectionCountLabel(preferences.preferredEncodes)),
|
||||
DebridRuleRow(DebridStreamPicker.REQUIRED_ENCODES, stringResource(Res.string.settings_debrid_rule_required_encodes), stringResource(Res.string.settings_debrid_rule_required_encodes_desc), selectionCountLabel(preferences.requiredEncodes)),
|
||||
DebridRuleRow(DebridStreamPicker.EXCLUDED_ENCODES, stringResource(Res.string.settings_debrid_rule_excluded_encodes), stringResource(Res.string.settings_debrid_rule_excluded_encodes_desc), selectionCountLabel(preferences.excludedEncodes)),
|
||||
DebridRuleRow(DebridStreamPicker.PREFERRED_LANGUAGES, stringResource(Res.string.settings_debrid_rule_preferred_languages), stringResource(Res.string.settings_debrid_rule_preferred_languages_desc), selectionCountLabel(preferences.preferredLanguages)),
|
||||
DebridRuleRow(DebridStreamPicker.REQUIRED_LANGUAGES, stringResource(Res.string.settings_debrid_rule_required_languages), stringResource(Res.string.settings_debrid_rule_required_languages_desc), selectionCountLabel(preferences.requiredLanguages)),
|
||||
DebridRuleRow(DebridStreamPicker.EXCLUDED_LANGUAGES, stringResource(Res.string.settings_debrid_rule_excluded_languages), stringResource(Res.string.settings_debrid_rule_excluded_languages_desc), selectionCountLabel(preferences.excludedLanguages)),
|
||||
DebridRuleRow(DebridStreamPicker.REQUIRED_RELEASE_GROUPS, stringResource(Res.string.settings_debrid_rule_required_release_groups), stringResource(Res.string.settings_debrid_rule_required_release_groups_desc), selectionCountLabel(preferences.requiredReleaseGroups)),
|
||||
DebridRuleRow(DebridStreamPicker.EXCLUDED_RELEASE_GROUPS, stringResource(Res.string.settings_debrid_rule_excluded_release_groups), stringResource(Res.string.settings_debrid_rule_excluded_release_groups_desc), selectionCountLabel(preferences.excludedReleaseGroups)),
|
||||
)
|
||||
|
||||
@Composable
|
||||
private fun selectionCountLabel(values: List<*>): String =
|
||||
if (values.isEmpty()) "Any" else "${values.size} selected"
|
||||
if (values.isEmpty()) {
|
||||
stringResource(Res.string.settings_debrid_selection_any)
|
||||
} else {
|
||||
stringResource(Res.string.settings_debrid_selection_count, values.size)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun sizeRangeLabel(preferences: DebridStreamPreferences): String =
|
||||
sizeRangeLabel(preferences.sizeMinGb, preferences.sizeMaxGb)
|
||||
|
||||
@Composable
|
||||
private fun sizeRangeLabel(minGb: Int, maxGb: Int): String =
|
||||
when {
|
||||
minGb <= 0 && maxGb <= 0 -> "Any"
|
||||
minGb <= 0 -> "Up to ${maxGb}GB"
|
||||
maxGb <= 0 -> "${minGb}GB+"
|
||||
else -> "${minGb}-${maxGb}GB"
|
||||
minGb <= 0 && maxGb <= 0 -> stringResource(Res.string.settings_debrid_selection_any)
|
||||
minGb <= 0 -> stringResource(Res.string.settings_debrid_size_up_to, maxGb)
|
||||
maxGb <= 0 -> stringResource(Res.string.settings_debrid_size_min, minGb)
|
||||
else -> stringResource(Res.string.settings_debrid_size_range_value, minGb, maxGb)
|
||||
}
|
||||
|
||||
private fun sortProfileFor(criteria: List<DebridStreamSortCriterion>): DebridSortProfile {
|
||||
|
|
@ -1250,9 +1342,6 @@ private fun sortProfileFor(criteria: List<DebridStreamSortCriterion>): DebridSor
|
|||
}
|
||||
}
|
||||
|
||||
private fun sortProfileLabel(criteria: List<DebridStreamSortCriterion>): String =
|
||||
sortProfileLabel(sortProfileFor(criteria))
|
||||
|
||||
private fun sortCriteriaForProfile(profile: DebridSortProfile): List<DebridStreamSortCriterion> =
|
||||
when (profile) {
|
||||
DebridSortProfile.DEFAULT -> DebridStreamSortCriterion.defaultOrder
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ import com.nuvio.app.features.player.AvailableLanguageOptions
|
|||
import com.nuvio.app.features.player.ExternalPlayerApp
|
||||
import com.nuvio.app.features.player.ExternalPlayerPlatform
|
||||
import com.nuvio.app.features.player.IosHardwareDecoderMode
|
||||
import com.nuvio.app.features.player.localizedLabel
|
||||
import com.nuvio.app.features.player.IosTargetPrimaries
|
||||
import com.nuvio.app.features.player.IosTargetTransfer
|
||||
import com.nuvio.app.features.player.PlayerSettingsRepository
|
||||
|
|
@ -72,6 +73,7 @@ import com.nuvio.app.isIos
|
|||
import kotlinx.coroutines.launch
|
||||
import nuvio.composeapp.generated.resources.*
|
||||
import org.jetbrains.compose.resources.StringResource
|
||||
import org.jetbrains.compose.resources.getString
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
|
|
@ -501,42 +503,42 @@ private fun PlaybackSettingsSection(
|
|||
|
||||
if (isIos) {
|
||||
SettingsSection(
|
||||
title = "iOS video output",
|
||||
title = stringResource(Res.string.settings_playback_ios_video_output),
|
||||
isTablet = isTablet,
|
||||
) {
|
||||
SettingsGroup(isTablet = isTablet) {
|
||||
SettingsNavigationRow(
|
||||
title = "Hardware decoder",
|
||||
description = autoPlayPlayerSettings.iosHardwareDecoderMode.label,
|
||||
title = stringResource(Res.string.settings_playback_ios_hardware_decoder),
|
||||
description = autoPlayPlayerSettings.iosHardwareDecoderMode.localizedLabel(),
|
||||
isTablet = isTablet,
|
||||
onClick = { showIosHardwareDecoderDialog = true },
|
||||
)
|
||||
SettingsGroupDivider(isTablet = isTablet)
|
||||
SettingsSwitchRow(
|
||||
title = "Extended dynamic range",
|
||||
description = "Default Metal output mode for new playback sessions.",
|
||||
title = stringResource(Res.string.settings_playback_ios_extended_dynamic_range),
|
||||
description = stringResource(Res.string.settings_playback_ios_extended_dynamic_range_desc),
|
||||
checked = autoPlayPlayerSettings.iosExtendedDynamicRangeEnabled,
|
||||
isTablet = isTablet,
|
||||
onCheckedChange = PlayerSettingsRepository::setIosExtendedDynamicRangeEnabled,
|
||||
)
|
||||
SettingsGroupDivider(isTablet = isTablet)
|
||||
SettingsSwitchRow(
|
||||
title = "Display color hint",
|
||||
description = "Let mpv target the active display color space by default.",
|
||||
title = stringResource(Res.string.settings_playback_ios_display_color_hint),
|
||||
description = stringResource(Res.string.settings_playback_ios_display_color_hint_desc),
|
||||
checked = autoPlayPlayerSettings.iosTargetColorspaceHintEnabled,
|
||||
isTablet = isTablet,
|
||||
onCheckedChange = PlayerSettingsRepository::setIosTargetColorspaceHintEnabled,
|
||||
)
|
||||
SettingsGroupDivider(isTablet = isTablet)
|
||||
SettingsNavigationRow(
|
||||
title = "Target primaries",
|
||||
title = stringResource(Res.string.settings_playback_ios_target_primaries),
|
||||
description = autoPlayPlayerSettings.iosTargetPrimaries.label,
|
||||
isTablet = isTablet,
|
||||
onClick = { showIosTargetPrimariesDialog = true },
|
||||
)
|
||||
SettingsGroupDivider(isTablet = isTablet)
|
||||
SettingsNavigationRow(
|
||||
title = "Target transfer",
|
||||
title = stringResource(Res.string.settings_playback_ios_target_transfer),
|
||||
description = autoPlayPlayerSettings.iosTargetTransfer.label,
|
||||
isTablet = isTablet,
|
||||
onClick = { showIosTargetTransferDialog = true },
|
||||
|
|
@ -924,7 +926,7 @@ private fun PlaybackSettingsSection(
|
|||
|
||||
if (showIosHardwareDecoderDialog) {
|
||||
IosEnumSelectionDialog(
|
||||
title = "Hardware decoder",
|
||||
title = stringResource(Res.string.settings_playback_ios_hw_decoder_dialog),
|
||||
options = IosHardwareDecoderMode.entries,
|
||||
selected = autoPlayPlayerSettings.iosHardwareDecoderMode,
|
||||
label = { it.label },
|
||||
|
|
@ -938,7 +940,7 @@ private fun PlaybackSettingsSection(
|
|||
|
||||
if (showIosTargetPrimariesDialog) {
|
||||
IosEnumSelectionDialog(
|
||||
title = "Target primaries",
|
||||
title = stringResource(Res.string.settings_playback_ios_target_primaries_dialog),
|
||||
options = IosTargetPrimaries.entries,
|
||||
selected = autoPlayPlayerSettings.iosTargetPrimaries,
|
||||
label = { it.label },
|
||||
|
|
@ -952,7 +954,7 @@ private fun PlaybackSettingsSection(
|
|||
|
||||
if (showIosTargetTransferDialog) {
|
||||
IosEnumSelectionDialog(
|
||||
title = "Target transfer",
|
||||
title = stringResource(Res.string.settings_playback_ios_target_transfer_dialog),
|
||||
options = IosTargetTransfer.entries,
|
||||
selected = autoPlayPlayerSettings.iosTargetTransfer,
|
||||
label = { it.label },
|
||||
|
|
@ -2379,7 +2381,7 @@ private fun IntroDbApiKeyDialog(
|
|||
if (isValid) {
|
||||
onSave(trimmed)
|
||||
} else {
|
||||
errorMessage = invalidKeyMessage
|
||||
errorMessage = getString(Res.string.settings_playback_introdb_invalid_api_key)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
Loading…
Reference in a new issue