diff --git a/lib/i18n/da.i18n.json b/lib/i18n/da.i18n.json index c770f7ec..3878c714 100644 --- a/lib/i18n/da.i18n.json +++ b/lib/i18n/da.i18n.json @@ -108,6 +108,8 @@ "useGlobalHubsDescription": "Vis startsidehubbe som den officielle Plex-klient. Når slået fra, vises anbefalinger per bibliotek.", "showServerNameOnHubs": "Vis servernavn på hubbe", "showServerNameOnHubsDescription": "Vis altid servernavnet i hubtitler. Når slået fra, vises kun ved duplikerede navne.", + "groupLibrariesByServer": "Grupper biblioteker efter server", + "groupLibrariesByServerDescription": "Vis en overskrift for hver Plex-server i sidepanelet, når du er forbundet til flere servere.", "alwaysKeepSidebarOpen": "Hold altid sidepanelet åbent", "alwaysKeepSidebarOpenDescription": "Sidepanelet forbliver udvidet, og indholdsområdet tilpasser sig", "showUnwatchedCount": "Vis antal usete", @@ -550,6 +552,8 @@ "analysisStarted": "Analyse startet for \"${title}\"", "failedToAnalyze": "Kunne ikke analysere bibliotek: ${error}", "noLibrariesFound": "Ingen biblioteker fundet", + "allLibrariesHidden": "Alle biblioteker er skjult", + "hiddenLibrariesCount": "Skjulte biblioteker (${count})", "thisLibraryIsEmpty": "Dette bibliotek er tomt", "all": "Alle", "clearAll": "Ryd alle", diff --git a/lib/i18n/de.i18n.json b/lib/i18n/de.i18n.json index ac942d8c..0b4ef368 100644 --- a/lib/i18n/de.i18n.json +++ b/lib/i18n/de.i18n.json @@ -108,6 +108,8 @@ "useGlobalHubsDescription": "Zeigt Startseiten-Hubs wie der offizielle Plex-Client. Wenn deaktiviert, werden stattdessen Empfehlungen pro Bibliothek angezeigt.", "showServerNameOnHubs": "Servername bei Hubs anzeigen", "showServerNameOnHubsDescription": "Zeigt immer den Servernamen in Hub-Titeln an. Wenn deaktiviert, nur bei doppelten Hub-Namen.", + "groupLibrariesByServer": "Mediatheken nach Server gruppieren", + "groupLibrariesByServerDescription": "Zeigt eine Überschrift für jeden Plex-Server in der Seitenleiste an, wenn du mit mehreren Servern verbunden bist.", "alwaysKeepSidebarOpen": "Seitenleiste immer geöffnet halten", "alwaysKeepSidebarOpenDescription": "Seitenleiste bleibt erweitert und Inhaltsbereich passt sich an", "showUnwatchedCount": "Anzahl nicht gesehener Folgen anzeigen", @@ -550,6 +552,8 @@ "analysisStarted": "Analyse gestartet für „${title}“", "failedToAnalyze": "Analyse der Mediathek fehlgeschlagen: ${error}", "noLibrariesFound": "Keine Mediatheken gefunden", + "allLibrariesHidden": "Alle Mediatheken sind ausgeblendet", + "hiddenLibrariesCount": "Ausgeblendete Mediatheken (${count})", "thisLibraryIsEmpty": "Diese Mediathek ist leer", "all": "Alle", "clearAll": "Alle löschen", diff --git a/lib/i18n/en.i18n.json b/lib/i18n/en.i18n.json index 98ef01eb..a86155c2 100644 --- a/lib/i18n/en.i18n.json +++ b/lib/i18n/en.i18n.json @@ -108,6 +108,8 @@ "useGlobalHubsDescription": "Show home page hubs like the official Plex client. When off, shows per-library recommendations instead.", "showServerNameOnHubs": "Show Server Name on Hubs", "showServerNameOnHubsDescription": "Always display the server name in hub titles. When off, only shows for duplicate hub names.", + "groupLibrariesByServer": "Group Libraries by Server", + "groupLibrariesByServerDescription": "Show a header for each Plex server in the sidebar when you're connected to multiple servers.", "alwaysKeepSidebarOpen": "Always Keep Sidebar Open", "alwaysKeepSidebarOpenDescription": "Sidebar stays expanded and content area adjusts to fit", "showUnwatchedCount": "Show Unwatched Count", @@ -550,6 +552,8 @@ "analysisStarted": "Analysis started for \"${title}\"", "failedToAnalyze": "Failed to analyze library: ${error}", "noLibrariesFound": "No libraries found", + "allLibrariesHidden": "All libraries are hidden", + "hiddenLibrariesCount": "Hidden libraries (${count})", "thisLibraryIsEmpty": "This library is empty", "all": "All", "clearAll": "Clear All", diff --git a/lib/i18n/es.i18n.json b/lib/i18n/es.i18n.json index ed94819a..5e48bcff 100644 --- a/lib/i18n/es.i18n.json +++ b/lib/i18n/es.i18n.json @@ -108,6 +108,8 @@ "useGlobalHubsDescription": "Mostrar los hubs de la página de inicio como el cliente oficial de Plex. Cuando está desactivado, muestra recomendaciones por biblioteca en su lugar.", "showServerNameOnHubs": "Mostrar Nombre del Servidor en los Hubs", "showServerNameOnHubsDescription": "Mostrar siempre el nombre del servidor en los títulos de los hubs. Cuando está desactivado, solo se muestra para nombres de hubs duplicados.", + "groupLibrariesByServer": "Agrupar bibliotecas por servidor", + "groupLibrariesByServerDescription": "Muestra un encabezado para cada servidor Plex en la barra lateral cuando estás conectado a varios servidores.", "alwaysKeepSidebarOpen": "Mantener siempre la barra lateral abierta", "alwaysKeepSidebarOpenDescription": "La barra lateral permanece expandida y el área de contenido se ajusta para adaptarse", "showUnwatchedCount": "Mostrar conteo de no vistos", @@ -550,6 +552,8 @@ "analysisStarted": "Análisis iniciado para \"${title}\"", "failedToAnalyze": "Error al analizar la biblioteca: ${error}", "noLibrariesFound": "No se encontraron bibliotecas", + "allLibrariesHidden": "Todas las bibliotecas están ocultas", + "hiddenLibrariesCount": "Bibliotecas ocultas (${count})", "thisLibraryIsEmpty": "Esta biblioteca está vacía", "all": "Todos", "clearAll": "Borrar Todo", diff --git a/lib/i18n/fr.i18n.json b/lib/i18n/fr.i18n.json index 2f8a2352..4264d7af 100644 --- a/lib/i18n/fr.i18n.json +++ b/lib/i18n/fr.i18n.json @@ -108,6 +108,8 @@ "useGlobalHubsDescription": "Afficher les hubs de la page d'accueil comme le client Plex officiel. Lorsque cette option est désactivée, affiche à la place les recommandations par bibliothèque.", "showServerNameOnHubs": "Afficher le nom du serveur sur les hubs", "showServerNameOnHubsDescription": "Toujours afficher le nom du serveur dans les titres des hubs. Lorsque cette option est désactivée, seuls les noms de hubs en double s'affichent.", + "groupLibrariesByServer": "Grouper les bibliothèques par serveur", + "groupLibrariesByServerDescription": "Affiche un en-tête pour chaque serveur Plex dans la barre latérale lorsque vous êtes connecté à plusieurs serveurs.", "alwaysKeepSidebarOpen": "Toujours garder la barre latérale ouverte", "alwaysKeepSidebarOpenDescription": "La barre latérale reste étendue et la zone de contenu s'adapte", "showUnwatchedCount": "Afficher le nombre non visionné", @@ -550,6 +552,8 @@ "analysisStarted": "L'analyse a commencé pour \"${title}\"", "failedToAnalyze": "Échec de l'analyse de la bibliothèque: ${error}", "noLibrariesFound": "Aucune bibliothèque trouvée", + "allLibrariesHidden": "Toutes les bibliothèques sont masquées", + "hiddenLibrariesCount": "Bibliothèques masquées (${count})", "thisLibraryIsEmpty": "Cette bibliothèque est vide", "all": "Tout", "clearAll": "Tout effacer", diff --git a/lib/i18n/it.i18n.json b/lib/i18n/it.i18n.json index 3448f4cf..e5421d12 100644 --- a/lib/i18n/it.i18n.json +++ b/lib/i18n/it.i18n.json @@ -108,6 +108,8 @@ "useGlobalHubsDescription": "Mostra gli hub della home page come il client Plex ufficiale. Se disattivato, mostra invece i suggerimenti per libreria.", "showServerNameOnHubs": "Mostra nome server sugli hub", "showServerNameOnHubsDescription": "Mostra sempre il nome del server nei titoli degli hub. Se disattivato, solo per nomi hub duplicati.", + "groupLibrariesByServer": "Raggruppa librerie per server", + "groupLibrariesByServerDescription": "Mostra un'intestazione per ogni server Plex nella barra laterale quando sei connesso a più server.", "alwaysKeepSidebarOpen": "Mantieni sempre aperta la barra laterale", "alwaysKeepSidebarOpenDescription": "La barra laterale rimane espansa e l'area del contenuto si adatta", "showUnwatchedCount": "Mostra conteggio non visti", @@ -550,6 +552,8 @@ "analysisStarted": "Analisi iniziata per \"${title}\"", "failedToAnalyze": "Impossibile analizzare libreria: ${error}", "noLibrariesFound": "Nessuna libreria trovata", + "allLibrariesHidden": "Tutte le librerie sono nascoste", + "hiddenLibrariesCount": "Librerie nascoste (${count})", "thisLibraryIsEmpty": "Questa libreria è vuota", "all": "Tutto", "clearAll": "Cancella tutto", diff --git a/lib/i18n/ja.i18n.json b/lib/i18n/ja.i18n.json index 5e138f85..0c862609 100644 --- a/lib/i18n/ja.i18n.json +++ b/lib/i18n/ja.i18n.json @@ -108,6 +108,8 @@ "useGlobalHubsDescription": "公式Plexクライアントのようにホームページのハブを表示。オフにすると、ライブラリごとのおすすめを表示。", "showServerNameOnHubs": "ハブにサーバー名を表示", "showServerNameOnHubsDescription": "ハブタイトルに常にサーバー名を表示。オフにすると、重複名のみ表示。", + "groupLibrariesByServer": "サーバーごとにライブラリをグループ化", + "groupLibrariesByServerDescription": "複数のサーバーに接続しているとき、サイドバーに各 Plex サーバーのヘッダーを表示します。", "alwaysKeepSidebarOpen": "サイドバーを常に開いておく", "alwaysKeepSidebarOpenDescription": "サイドバーを展開したまま、コンテンツ領域が調整される", "showUnwatchedCount": "未視聴数を表示", @@ -550,6 +552,8 @@ "analysisStarted": "\"${title}\"の解析を開始しました", "failedToAnalyze": "ライブラリの解析に失敗しました: ${error}", "noLibrariesFound": "ライブラリが見つかりません", + "allLibrariesHidden": "すべてのライブラリが非表示です", + "hiddenLibrariesCount": "非表示のライブラリ (${count})", "thisLibraryIsEmpty": "このライブラリは空です", "all": "すべて", "clearAll": "すべてクリア", diff --git a/lib/i18n/ko.i18n.json b/lib/i18n/ko.i18n.json index a4b95491..a06e8615 100644 --- a/lib/i18n/ko.i18n.json +++ b/lib/i18n/ko.i18n.json @@ -108,6 +108,8 @@ "useGlobalHubsDescription": "공식 Plex 클라이언트처럼 홈 페이지 허브를 표시합니다. 끄면 라이브러리별 추천이 대신 표시됩니다.", "showServerNameOnHubs": "허브에 서버 이름 표시", "showServerNameOnHubsDescription": "허브 제목에 항상 서버 이름을 표시합니다. 끄면 중복된 허브 이름에만 표시됩니다.", + "groupLibrariesByServer": "서버별로 라이브러리 그룹화", + "groupLibrariesByServerDescription": "여러 서버에 연결되어 있을 때 사이드바에 각 Plex 서버의 헤더를 표시합니다.", "alwaysKeepSidebarOpen": "사이드바 항상 열어두기", "alwaysKeepSidebarOpenDescription": "사이드바가 확장된 상태로 유지되고 콘텐츠 영역이 맞춰집니다", "showUnwatchedCount": "미시청 수 표시", @@ -550,6 +552,8 @@ "analysisStarted": "\"${title}\" 분석 시작됨", "failedToAnalyze": "미디어 라이브러리 분석 실패: ${error}", "noLibrariesFound": "미디어 라이브러리 없음", + "allLibrariesHidden": "모든 라이브러리가 숨겨졌습니다", + "hiddenLibrariesCount": "숨겨진 라이브러리 (${count})", "thisLibraryIsEmpty": "이 미디어 라이브러리는 비어 있습니다", "all": "전체", "clearAll": "모두 삭제", diff --git a/lib/i18n/nb.i18n.json b/lib/i18n/nb.i18n.json index f82ca017..4260d6b6 100644 --- a/lib/i18n/nb.i18n.json +++ b/lib/i18n/nb.i18n.json @@ -108,6 +108,8 @@ "useGlobalHubsDescription": "Vis hjemmeside-huber som den offisielle Plex-klienten. Når av, vises per-bibliotek-anbefalinger i stedet.", "showServerNameOnHubs": "Vis servernavn på huber", "showServerNameOnHubsDescription": "Vis alltid servernavnet i hubtitler. Når av, vises kun for dupliserte hubnavn.", + "groupLibrariesByServer": "Grupper biblioteker etter server", + "groupLibrariesByServerDescription": "Vis en overskrift for hver Plex-server i sidefeltet når du er koblet til flere servere.", "alwaysKeepSidebarOpen": "Hold sidefeltet alltid åpent", "alwaysKeepSidebarOpenDescription": "Sidefeltet forblir utvidet og innholdsområdet tilpasser seg", "showUnwatchedCount": "Vis antall usette", @@ -550,6 +552,8 @@ "analysisStarted": "Analyse startet for \"${title}\"", "failedToAnalyze": "Kunne ikke analysere bibliotek: ${error}", "noLibrariesFound": "Ingen biblioteker funnet", + "allLibrariesHidden": "Alle biblioteker er skjult", + "hiddenLibrariesCount": "Skjulte biblioteker (${count})", "thisLibraryIsEmpty": "Dette biblioteket er tomt", "all": "Alle", "clearAll": "Tøm alle", diff --git a/lib/i18n/nl.i18n.json b/lib/i18n/nl.i18n.json index b29867e8..89d25833 100644 --- a/lib/i18n/nl.i18n.json +++ b/lib/i18n/nl.i18n.json @@ -108,6 +108,8 @@ "useGlobalHubsDescription": "Toon startpagina-hubs zoals de officiële Plex-client. Indien uitgeschakeld, worden in plaats daarvan aanbevelingen per bibliotheek getoond.", "showServerNameOnHubs": "Servernaam tonen bij hubs", "showServerNameOnHubsDescription": "Toon altijd de servernaam in hub-titels. Indien uitgeschakeld, alleen bij dubbele hub-namen.", + "groupLibrariesByServer": "Bibliotheken groeperen per server", + "groupLibrariesByServerDescription": "Toont een kop voor elke Plex-server in de zijbalk wanneer je met meerdere servers verbonden bent.", "alwaysKeepSidebarOpen": "Zijbalk altijd open houden", "alwaysKeepSidebarOpenDescription": "Zijbalk blijft uitgevouwen en inhoudsgebied past zich aan", "showUnwatchedCount": "Aantal ongekeken tonen", @@ -550,6 +552,8 @@ "analysisStarted": "Analyse gestart voor \"${title}\"", "failedToAnalyze": "Kon bibliotheek niet analyseren: ${error}", "noLibrariesFound": "Geen bibliotheken gevonden", + "allLibrariesHidden": "Alle bibliotheken zijn verborgen", + "hiddenLibrariesCount": "Verborgen bibliotheken (${count})", "thisLibraryIsEmpty": "Deze bibliotheek is leeg", "all": "Alles", "clearAll": "Alles wissen", diff --git a/lib/i18n/pl.i18n.json b/lib/i18n/pl.i18n.json index e324e4e3..0860a6a1 100644 --- a/lib/i18n/pl.i18n.json +++ b/lib/i18n/pl.i18n.json @@ -108,6 +108,8 @@ "useGlobalHubsDescription": "Pokaż huby strony głównej jak w oficjalnym kliencie Plex. Gdy wyłączone, pokazuje rekomendacje per biblioteka.", "showServerNameOnHubs": "Pokaż nazwę serwera w hubach", "showServerNameOnHubsDescription": "Zawsze wyświetlaj nazwę serwera w tytułach hubów. Gdy wyłączone, pokazuje tylko dla zduplikowanych nazw.", + "groupLibrariesByServer": "Grupuj biblioteki według serwera", + "groupLibrariesByServerDescription": "Pokazuj nagłówek dla każdego serwera Plex na pasku bocznym, gdy jesteś połączony z wieloma serwerami.", "alwaysKeepSidebarOpen": "Zawsze utrzymuj panel boczny otwarty", "alwaysKeepSidebarOpenDescription": "Panel boczny jest rozwinięty, a obszar treści dostosowuje się", "showUnwatchedCount": "Pokaż liczbę nieobejrzanych", @@ -550,6 +552,8 @@ "analysisStarted": "Analiza rozpoczęta dla \"${title}\"", "failedToAnalyze": "Nie udało się przeanalizować biblioteki: ${error}", "noLibrariesFound": "Nie znaleziono bibliotek", + "allLibrariesHidden": "Wszystkie biblioteki są ukryte", + "hiddenLibrariesCount": "Ukryte biblioteki (${count})", "thisLibraryIsEmpty": "Ta biblioteka jest pusta", "all": "Wszystkie", "clearAll": "Wyczyść wszystko", diff --git a/lib/i18n/pt.i18n.json b/lib/i18n/pt.i18n.json index 0b2ac1c6..7ae6081b 100644 --- a/lib/i18n/pt.i18n.json +++ b/lib/i18n/pt.i18n.json @@ -108,6 +108,8 @@ "useGlobalHubsDescription": "Mostrar hubs da página inicial como o cliente oficial Plex. Quando desativado, mostra recomendações por biblioteca.", "showServerNameOnHubs": "Mostrar Nome do Servidor nos Hubs", "showServerNameOnHubsDescription": "Sempre exibir o nome do servidor nos títulos dos hubs. Quando desativado, mostra apenas para nomes duplicados.", + "groupLibrariesByServer": "Agrupar Bibliotecas por Servidor", + "groupLibrariesByServerDescription": "Mostra um cabeçalho para cada servidor Plex na barra lateral quando você está conectado a vários servidores.", "alwaysKeepSidebarOpen": "Manter Barra Lateral Sempre Aberta", "alwaysKeepSidebarOpenDescription": "A barra lateral fica expandida e a área de conteúdo se ajusta", "showUnwatchedCount": "Mostrar Contagem de Não Assistidos", @@ -550,6 +552,8 @@ "analysisStarted": "Análise iniciada para \"${title}\"", "failedToAnalyze": "Falha ao analisar biblioteca: ${error}", "noLibrariesFound": "Nenhuma biblioteca encontrada", + "allLibrariesHidden": "Todas as bibliotecas estão ocultas", + "hiddenLibrariesCount": "Bibliotecas ocultas (${count})", "thisLibraryIsEmpty": "Esta biblioteca está vazia", "all": "Todos", "clearAll": "Limpar Tudo", diff --git a/lib/i18n/ru.i18n.json b/lib/i18n/ru.i18n.json index 14803889..90e08e81 100644 --- a/lib/i18n/ru.i18n.json +++ b/lib/i18n/ru.i18n.json @@ -108,6 +108,8 @@ "useGlobalHubsDescription": "Показывать хабы главной страницы как в официальном клиенте Plex. При выключении показывает рекомендации по библиотекам.", "showServerNameOnHubs": "Показывать имя сервера в хабах", "showServerNameOnHubsDescription": "Всегда показывать имя сервера в заголовках хабов. При выключении показывает только для дублирующихся имён.", + "groupLibrariesByServer": "Группировать библиотеки по серверам", + "groupLibrariesByServerDescription": "Показывать заголовок для каждого сервера Plex на боковой панели при подключении к нескольким серверам.", "alwaysKeepSidebarOpen": "Всегда держать боковую панель открытой", "alwaysKeepSidebarOpenDescription": "Боковая панель остаётся развёрнутой, область контента подстраивается", "showUnwatchedCount": "Показывать количество непросмотренных", @@ -550,6 +552,8 @@ "analysisStarted": "Анализ начат для \"${title}\"", "failedToAnalyze": "Не удалось проанализировать библиотеку: ${error}", "noLibrariesFound": "Библиотеки не найдены", + "allLibrariesHidden": "Все библиотеки скрыты", + "hiddenLibrariesCount": "Скрытые библиотеки (${count})", "thisLibraryIsEmpty": "Эта библиотека пуста", "all": "Все", "clearAll": "Очистить все", diff --git a/lib/i18n/strings.g.dart b/lib/i18n/strings.g.dart index f7c446d4..197252ff 100644 --- a/lib/i18n/strings.g.dart +++ b/lib/i18n/strings.g.dart @@ -4,9 +4,9 @@ /// To regenerate, run: `dart run slang` /// /// Locales: 15 -/// Strings: 14370 (958 per locale) +/// Strings: 14430 (962 per locale) /// -/// Built on 2026-04-24 at 08:58 UTC +/// Built on 2026-04-24 at 16:44 UTC // coverage:ignore-file // ignore_for_file: type=lint, unused_import diff --git a/lib/i18n/strings_da.g.dart b/lib/i18n/strings_da.g.dart index 0bc3fe20..177c09ed 100644 --- a/lib/i18n/strings_da.g.dart +++ b/lib/i18n/strings_da.g.dart @@ -230,6 +230,8 @@ class _TranslationsSettingsDa implements TranslationsSettingsEn { @override String get useGlobalHubsDescription => 'Vis startsidehubbe som den officielle Plex-klient. Når slået fra, vises anbefalinger per bibliotek.'; @override String get showServerNameOnHubs => 'Vis servernavn på hubbe'; @override String get showServerNameOnHubsDescription => 'Vis altid servernavnet i hubtitler. Når slået fra, vises kun ved duplikerede navne.'; + @override String get groupLibrariesByServer => 'Grupper biblioteker efter server'; + @override String get groupLibrariesByServerDescription => 'Vis en overskrift for hver Plex-server i sidepanelet, når du er forbundet til flere servere.'; @override String get alwaysKeepSidebarOpen => 'Hold altid sidepanelet åbent'; @override String get alwaysKeepSidebarOpenDescription => 'Sidepanelet forbliver udvidet, og indholdsområdet tilpasser sig'; @override String get showUnwatchedCount => 'Vis antal usete'; @@ -747,6 +749,8 @@ class _TranslationsLibrariesDa implements TranslationsLibrariesEn { @override String analysisStarted({required Object title}) => 'Analyse startet for "${title}"'; @override String failedToAnalyze({required Object error}) => 'Kunne ikke analysere bibliotek: ${error}'; @override String get noLibrariesFound => 'Ingen biblioteker fundet'; + @override String get allLibrariesHidden => 'Alle biblioteker er skjult'; + @override String hiddenLibrariesCount({required Object count}) => 'Skjulte biblioteker (${count})'; @override String get thisLibraryIsEmpty => 'Dette bibliotek er tomt'; @override String get all => 'Alle'; @override String get clearAll => 'Ryd alle'; @@ -1623,6 +1627,8 @@ extension on TranslationsDa { 'settings.useGlobalHubsDescription' => 'Vis startsidehubbe som den officielle Plex-klient. Når slået fra, vises anbefalinger per bibliotek.', 'settings.showServerNameOnHubs' => 'Vis servernavn på hubbe', 'settings.showServerNameOnHubsDescription' => 'Vis altid servernavnet i hubtitler. Når slået fra, vises kun ved duplikerede navne.', + 'settings.groupLibrariesByServer' => 'Grupper biblioteker efter server', + 'settings.groupLibrariesByServerDescription' => 'Vis en overskrift for hver Plex-server i sidepanelet, når du er forbundet til flere servere.', 'settings.alwaysKeepSidebarOpen' => 'Hold altid sidepanelet åbent', 'settings.alwaysKeepSidebarOpenDescription' => 'Sidepanelet forbliver udvidet, og indholdsområdet tilpasser sig', 'settings.showUnwatchedCount' => 'Vis antal usete', @@ -2031,14 +2037,16 @@ extension on TranslationsDa { 'libraries.analysisStarted' => ({required Object title}) => 'Analyse startet for "${title}"', 'libraries.failedToAnalyze' => ({required Object error}) => 'Kunne ikke analysere bibliotek: ${error}', 'libraries.noLibrariesFound' => 'Ingen biblioteker fundet', + 'libraries.allLibrariesHidden' => 'Alle biblioteker er skjult', + 'libraries.hiddenLibrariesCount' => ({required Object count}) => 'Skjulte biblioteker (${count})', 'libraries.thisLibraryIsEmpty' => 'Dette bibliotek er tomt', 'libraries.all' => 'Alle', + _ => null, + } ?? switch (path) { 'libraries.clearAll' => 'Ryd alle', 'libraries.scanLibraryConfirm' => ({required Object title}) => 'Er du sikker på, at du vil scanne "${title}"?', 'libraries.analyzeLibraryConfirm' => ({required Object title}) => 'Er du sikker på, at du vil analysere "${title}"?', 'libraries.refreshMetadataConfirm' => ({required Object title}) => 'Er du sikker på, at du vil opdatere metadata for "${title}"?', - _ => null, - } ?? switch (path) { 'libraries.emptyTrashConfirm' => ({required Object title}) => 'Er du sikker på, at du vil tømme papirkurven for "${title}"?', 'libraries.manageLibraries' => 'Administrer biblioteker', 'libraries.sort' => 'Sortér', diff --git a/lib/i18n/strings_de.g.dart b/lib/i18n/strings_de.g.dart index bd66455a..3b47cac0 100644 --- a/lib/i18n/strings_de.g.dart +++ b/lib/i18n/strings_de.g.dart @@ -230,6 +230,8 @@ class _TranslationsSettingsDe implements TranslationsSettingsEn { @override String get useGlobalHubsDescription => 'Zeigt Startseiten-Hubs wie der offizielle Plex-Client. Wenn deaktiviert, werden stattdessen Empfehlungen pro Bibliothek angezeigt.'; @override String get showServerNameOnHubs => 'Servername bei Hubs anzeigen'; @override String get showServerNameOnHubsDescription => 'Zeigt immer den Servernamen in Hub-Titeln an. Wenn deaktiviert, nur bei doppelten Hub-Namen.'; + @override String get groupLibrariesByServer => 'Mediatheken nach Server gruppieren'; + @override String get groupLibrariesByServerDescription => 'Zeigt eine Überschrift für jeden Plex-Server in der Seitenleiste an, wenn du mit mehreren Servern verbunden bist.'; @override String get alwaysKeepSidebarOpen => 'Seitenleiste immer geöffnet halten'; @override String get alwaysKeepSidebarOpenDescription => 'Seitenleiste bleibt erweitert und Inhaltsbereich passt sich an'; @override String get showUnwatchedCount => 'Anzahl nicht gesehener Folgen anzeigen'; @@ -747,6 +749,8 @@ class _TranslationsLibrariesDe implements TranslationsLibrariesEn { @override String analysisStarted({required Object title}) => 'Analyse gestartet für „${title}“'; @override String failedToAnalyze({required Object error}) => 'Analyse der Mediathek fehlgeschlagen: ${error}'; @override String get noLibrariesFound => 'Keine Mediatheken gefunden'; + @override String get allLibrariesHidden => 'Alle Mediatheken sind ausgeblendet'; + @override String hiddenLibrariesCount({required Object count}) => 'Ausgeblendete Mediatheken (${count})'; @override String get thisLibraryIsEmpty => 'Diese Mediathek ist leer'; @override String get all => 'Alle'; @override String get clearAll => 'Alle löschen'; @@ -1623,6 +1627,8 @@ extension on TranslationsDe { 'settings.useGlobalHubsDescription' => 'Zeigt Startseiten-Hubs wie der offizielle Plex-Client. Wenn deaktiviert, werden stattdessen Empfehlungen pro Bibliothek angezeigt.', 'settings.showServerNameOnHubs' => 'Servername bei Hubs anzeigen', 'settings.showServerNameOnHubsDescription' => 'Zeigt immer den Servernamen in Hub-Titeln an. Wenn deaktiviert, nur bei doppelten Hub-Namen.', + 'settings.groupLibrariesByServer' => 'Mediatheken nach Server gruppieren', + 'settings.groupLibrariesByServerDescription' => 'Zeigt eine Überschrift für jeden Plex-Server in der Seitenleiste an, wenn du mit mehreren Servern verbunden bist.', 'settings.alwaysKeepSidebarOpen' => 'Seitenleiste immer geöffnet halten', 'settings.alwaysKeepSidebarOpenDescription' => 'Seitenleiste bleibt erweitert und Inhaltsbereich passt sich an', 'settings.showUnwatchedCount' => 'Anzahl nicht gesehener Folgen anzeigen', @@ -2031,14 +2037,16 @@ extension on TranslationsDe { 'libraries.analysisStarted' => ({required Object title}) => 'Analyse gestartet für „${title}“', 'libraries.failedToAnalyze' => ({required Object error}) => 'Analyse der Mediathek fehlgeschlagen: ${error}', 'libraries.noLibrariesFound' => 'Keine Mediatheken gefunden', + 'libraries.allLibrariesHidden' => 'Alle Mediatheken sind ausgeblendet', + 'libraries.hiddenLibrariesCount' => ({required Object count}) => 'Ausgeblendete Mediatheken (${count})', 'libraries.thisLibraryIsEmpty' => 'Diese Mediathek ist leer', 'libraries.all' => 'Alle', + _ => null, + } ?? switch (path) { 'libraries.clearAll' => 'Alle löschen', 'libraries.scanLibraryConfirm' => ({required Object title}) => '„${title}“ wirklich scannen?', 'libraries.analyzeLibraryConfirm' => ({required Object title}) => '„${title}“ wirklich analysieren?', 'libraries.refreshMetadataConfirm' => ({required Object title}) => 'Metadaten für „${title}“ wirklich aktualisieren?', - _ => null, - } ?? switch (path) { 'libraries.emptyTrashConfirm' => ({required Object title}) => 'Papierkorb für „${title}“ wirklich leeren?', 'libraries.manageLibraries' => 'Mediatheken verwalten', 'libraries.sort' => 'Sortieren', diff --git a/lib/i18n/strings_en.g.dart b/lib/i18n/strings_en.g.dart index 259352df..6740eaef 100644 --- a/lib/i18n/strings_en.g.dart +++ b/lib/i18n/strings_en.g.dart @@ -430,6 +430,12 @@ class TranslationsSettingsEn { /// en: 'Always display the server name in hub titles. When off, only shows for duplicate hub names.' String get showServerNameOnHubsDescription => 'Always display the server name in hub titles. When off, only shows for duplicate hub names.'; + /// en: 'Group Libraries by Server' + String get groupLibrariesByServer => 'Group Libraries by Server'; + + /// en: 'Show a header for each Plex server in the sidebar when you're connected to multiple servers.' + String get groupLibrariesByServerDescription => 'Show a header for each Plex server in the sidebar when you\'re connected to multiple servers.'; + /// en: 'Always Keep Sidebar Open' String get alwaysKeepSidebarOpen => 'Always Keep Sidebar Open'; @@ -1709,6 +1715,12 @@ class TranslationsLibrariesEn { /// en: 'No libraries found' String get noLibrariesFound => 'No libraries found'; + /// en: 'All libraries are hidden' + String get allLibrariesHidden => 'All libraries are hidden'; + + /// en: 'Hidden libraries (${count})' + String hiddenLibrariesCount({required Object count}) => 'Hidden libraries (${count})'; + /// en: 'This library is empty' String get thisLibraryIsEmpty => 'This library is empty'; @@ -3547,6 +3559,8 @@ extension on Translations { 'settings.useGlobalHubsDescription' => 'Show home page hubs like the official Plex client. When off, shows per-library recommendations instead.', 'settings.showServerNameOnHubs' => 'Show Server Name on Hubs', 'settings.showServerNameOnHubsDescription' => 'Always display the server name in hub titles. When off, only shows for duplicate hub names.', + 'settings.groupLibrariesByServer' => 'Group Libraries by Server', + 'settings.groupLibrariesByServerDescription' => 'Show a header for each Plex server in the sidebar when you\'re connected to multiple servers.', 'settings.alwaysKeepSidebarOpen' => 'Always Keep Sidebar Open', 'settings.alwaysKeepSidebarOpenDescription' => 'Sidebar stays expanded and content area adjusts to fit', 'settings.showUnwatchedCount' => 'Show Unwatched Count', @@ -3955,14 +3969,16 @@ extension on Translations { 'libraries.analysisStarted' => ({required Object title}) => 'Analysis started for "${title}"', 'libraries.failedToAnalyze' => ({required Object error}) => 'Failed to analyze library: ${error}', 'libraries.noLibrariesFound' => 'No libraries found', + 'libraries.allLibrariesHidden' => 'All libraries are hidden', + 'libraries.hiddenLibrariesCount' => ({required Object count}) => 'Hidden libraries (${count})', 'libraries.thisLibraryIsEmpty' => 'This library is empty', 'libraries.all' => 'All', + _ => null, + } ?? switch (path) { 'libraries.clearAll' => 'Clear All', 'libraries.scanLibraryConfirm' => ({required Object title}) => 'Are you sure you want to scan "${title}"?', 'libraries.analyzeLibraryConfirm' => ({required Object title}) => 'Are you sure you want to analyze "${title}"?', 'libraries.refreshMetadataConfirm' => ({required Object title}) => 'Are you sure you want to refresh metadata for "${title}"?', - _ => null, - } ?? switch (path) { 'libraries.emptyTrashConfirm' => ({required Object title}) => 'Are you sure you want to empty trash for "${title}"?', 'libraries.manageLibraries' => 'Manage Libraries', 'libraries.sort' => 'Sort', diff --git a/lib/i18n/strings_es.g.dart b/lib/i18n/strings_es.g.dart index d98a6529..c6b25f6b 100644 --- a/lib/i18n/strings_es.g.dart +++ b/lib/i18n/strings_es.g.dart @@ -230,6 +230,8 @@ class _TranslationsSettingsEs implements TranslationsSettingsEn { @override String get useGlobalHubsDescription => 'Mostrar los hubs de la página de inicio como el cliente oficial de Plex. Cuando está desactivado, muestra recomendaciones por biblioteca en su lugar.'; @override String get showServerNameOnHubs => 'Mostrar Nombre del Servidor en los Hubs'; @override String get showServerNameOnHubsDescription => 'Mostrar siempre el nombre del servidor en los títulos de los hubs. Cuando está desactivado, solo se muestra para nombres de hubs duplicados.'; + @override String get groupLibrariesByServer => 'Agrupar bibliotecas por servidor'; + @override String get groupLibrariesByServerDescription => 'Muestra un encabezado para cada servidor Plex en la barra lateral cuando estás conectado a varios servidores.'; @override String get alwaysKeepSidebarOpen => 'Mantener siempre la barra lateral abierta'; @override String get alwaysKeepSidebarOpenDescription => 'La barra lateral permanece expandida y el área de contenido se ajusta para adaptarse'; @override String get showUnwatchedCount => 'Mostrar conteo de no vistos'; @@ -747,6 +749,8 @@ class _TranslationsLibrariesEs implements TranslationsLibrariesEn { @override String analysisStarted({required Object title}) => 'Análisis iniciado para "${title}"'; @override String failedToAnalyze({required Object error}) => 'Error al analizar la biblioteca: ${error}'; @override String get noLibrariesFound => 'No se encontraron bibliotecas'; + @override String get allLibrariesHidden => 'Todas las bibliotecas están ocultas'; + @override String hiddenLibrariesCount({required Object count}) => 'Bibliotecas ocultas (${count})'; @override String get thisLibraryIsEmpty => 'Esta biblioteca está vacía'; @override String get all => 'Todos'; @override String get clearAll => 'Borrar Todo'; @@ -1623,6 +1627,8 @@ extension on TranslationsEs { 'settings.useGlobalHubsDescription' => 'Mostrar los hubs de la página de inicio como el cliente oficial de Plex. Cuando está desactivado, muestra recomendaciones por biblioteca en su lugar.', 'settings.showServerNameOnHubs' => 'Mostrar Nombre del Servidor en los Hubs', 'settings.showServerNameOnHubsDescription' => 'Mostrar siempre el nombre del servidor en los títulos de los hubs. Cuando está desactivado, solo se muestra para nombres de hubs duplicados.', + 'settings.groupLibrariesByServer' => 'Agrupar bibliotecas por servidor', + 'settings.groupLibrariesByServerDescription' => 'Muestra un encabezado para cada servidor Plex en la barra lateral cuando estás conectado a varios servidores.', 'settings.alwaysKeepSidebarOpen' => 'Mantener siempre la barra lateral abierta', 'settings.alwaysKeepSidebarOpenDescription' => 'La barra lateral permanece expandida y el área de contenido se ajusta para adaptarse', 'settings.showUnwatchedCount' => 'Mostrar conteo de no vistos', @@ -2031,14 +2037,16 @@ extension on TranslationsEs { 'libraries.analysisStarted' => ({required Object title}) => 'Análisis iniciado para "${title}"', 'libraries.failedToAnalyze' => ({required Object error}) => 'Error al analizar la biblioteca: ${error}', 'libraries.noLibrariesFound' => 'No se encontraron bibliotecas', + 'libraries.allLibrariesHidden' => 'Todas las bibliotecas están ocultas', + 'libraries.hiddenLibrariesCount' => ({required Object count}) => 'Bibliotecas ocultas (${count})', 'libraries.thisLibraryIsEmpty' => 'Esta biblioteca está vacía', 'libraries.all' => 'Todos', + _ => null, + } ?? switch (path) { 'libraries.clearAll' => 'Borrar Todo', 'libraries.scanLibraryConfirm' => ({required Object title}) => '¿Estás seguro de que quieres escanear "${title}"?', 'libraries.analyzeLibraryConfirm' => ({required Object title}) => '¿Estás seguro de que quieres analizar "${title}"?', 'libraries.refreshMetadataConfirm' => ({required Object title}) => '¿Estás seguro de que quieres actualizar los metadatos de "${title}"?', - _ => null, - } ?? switch (path) { 'libraries.emptyTrashConfirm' => ({required Object title}) => '¿Estás seguro de que quieres vaciar la papelera de "${title}"?', 'libraries.manageLibraries' => 'Gestionar Bibliotecas', 'libraries.sort' => 'Ordenar', diff --git a/lib/i18n/strings_fr.g.dart b/lib/i18n/strings_fr.g.dart index f337fa58..7918f038 100644 --- a/lib/i18n/strings_fr.g.dart +++ b/lib/i18n/strings_fr.g.dart @@ -230,6 +230,8 @@ class _TranslationsSettingsFr implements TranslationsSettingsEn { @override String get useGlobalHubsDescription => 'Afficher les hubs de la page d\'accueil comme le client Plex officiel. Lorsque cette option est désactivée, affiche à la place les recommandations par bibliothèque.'; @override String get showServerNameOnHubs => 'Afficher le nom du serveur sur les hubs'; @override String get showServerNameOnHubsDescription => 'Toujours afficher le nom du serveur dans les titres des hubs. Lorsque cette option est désactivée, seuls les noms de hubs en double s\'affichent.'; + @override String get groupLibrariesByServer => 'Grouper les bibliothèques par serveur'; + @override String get groupLibrariesByServerDescription => 'Affiche un en-tête pour chaque serveur Plex dans la barre latérale lorsque vous êtes connecté à plusieurs serveurs.'; @override String get alwaysKeepSidebarOpen => 'Toujours garder la barre latérale ouverte'; @override String get alwaysKeepSidebarOpenDescription => 'La barre latérale reste étendue et la zone de contenu s\'adapte'; @override String get showUnwatchedCount => 'Afficher le nombre non visionné'; @@ -747,6 +749,8 @@ class _TranslationsLibrariesFr implements TranslationsLibrariesEn { @override String analysisStarted({required Object title}) => 'L\'analyse a commencé pour "${title}"'; @override String failedToAnalyze({required Object error}) => 'Échec de l\'analyse de la bibliothèque: ${error}'; @override String get noLibrariesFound => 'Aucune bibliothèque trouvée'; + @override String get allLibrariesHidden => 'Toutes les bibliothèques sont masquées'; + @override String hiddenLibrariesCount({required Object count}) => 'Bibliothèques masquées (${count})'; @override String get thisLibraryIsEmpty => 'Cette bibliothèque est vide'; @override String get all => 'Tout'; @override String get clearAll => 'Tout effacer'; @@ -1623,6 +1627,8 @@ extension on TranslationsFr { 'settings.useGlobalHubsDescription' => 'Afficher les hubs de la page d\'accueil comme le client Plex officiel. Lorsque cette option est désactivée, affiche à la place les recommandations par bibliothèque.', 'settings.showServerNameOnHubs' => 'Afficher le nom du serveur sur les hubs', 'settings.showServerNameOnHubsDescription' => 'Toujours afficher le nom du serveur dans les titres des hubs. Lorsque cette option est désactivée, seuls les noms de hubs en double s\'affichent.', + 'settings.groupLibrariesByServer' => 'Grouper les bibliothèques par serveur', + 'settings.groupLibrariesByServerDescription' => 'Affiche un en-tête pour chaque serveur Plex dans la barre latérale lorsque vous êtes connecté à plusieurs serveurs.', 'settings.alwaysKeepSidebarOpen' => 'Toujours garder la barre latérale ouverte', 'settings.alwaysKeepSidebarOpenDescription' => 'La barre latérale reste étendue et la zone de contenu s\'adapte', 'settings.showUnwatchedCount' => 'Afficher le nombre non visionné', @@ -2031,14 +2037,16 @@ extension on TranslationsFr { 'libraries.analysisStarted' => ({required Object title}) => 'L\'analyse a commencé pour "${title}"', 'libraries.failedToAnalyze' => ({required Object error}) => 'Échec de l\'analyse de la bibliothèque: ${error}', 'libraries.noLibrariesFound' => 'Aucune bibliothèque trouvée', + 'libraries.allLibrariesHidden' => 'Toutes les bibliothèques sont masquées', + 'libraries.hiddenLibrariesCount' => ({required Object count}) => 'Bibliothèques masquées (${count})', 'libraries.thisLibraryIsEmpty' => 'Cette bibliothèque est vide', 'libraries.all' => 'Tout', + _ => null, + } ?? switch (path) { 'libraries.clearAll' => 'Tout effacer', 'libraries.scanLibraryConfirm' => ({required Object title}) => 'Êtes-vous sûr de vouloir lancer le scan de "${title}"?', 'libraries.analyzeLibraryConfirm' => ({required Object title}) => 'Êtes-vous sûr de vouloir analyser "${title}"?', 'libraries.refreshMetadataConfirm' => ({required Object title}) => 'Êtes-vous sûr de vouloir actualiser les métadonnées pour "${title}"?', - _ => null, - } ?? switch (path) { 'libraries.emptyTrashConfirm' => ({required Object title}) => 'Êtes-vous sûr de vouloir vider la corbeille pour "${title}"?', 'libraries.manageLibraries' => 'Gérer les bibliothèques', 'libraries.sort' => 'Trier', diff --git a/lib/i18n/strings_it.g.dart b/lib/i18n/strings_it.g.dart index 47c52a67..544f9a9b 100644 --- a/lib/i18n/strings_it.g.dart +++ b/lib/i18n/strings_it.g.dart @@ -230,6 +230,8 @@ class _TranslationsSettingsIt implements TranslationsSettingsEn { @override String get useGlobalHubsDescription => 'Mostra gli hub della home page come il client Plex ufficiale. Se disattivato, mostra invece i suggerimenti per libreria.'; @override String get showServerNameOnHubs => 'Mostra nome server sugli hub'; @override String get showServerNameOnHubsDescription => 'Mostra sempre il nome del server nei titoli degli hub. Se disattivato, solo per nomi hub duplicati.'; + @override String get groupLibrariesByServer => 'Raggruppa librerie per server'; + @override String get groupLibrariesByServerDescription => 'Mostra un\'intestazione per ogni server Plex nella barra laterale quando sei connesso a più server.'; @override String get alwaysKeepSidebarOpen => 'Mantieni sempre aperta la barra laterale'; @override String get alwaysKeepSidebarOpenDescription => 'La barra laterale rimane espansa e l\'area del contenuto si adatta'; @override String get showUnwatchedCount => 'Mostra conteggio non visti'; @@ -747,6 +749,8 @@ class _TranslationsLibrariesIt implements TranslationsLibrariesEn { @override String analysisStarted({required Object title}) => 'Analisi iniziata per "${title}"'; @override String failedToAnalyze({required Object error}) => 'Impossibile analizzare libreria: ${error}'; @override String get noLibrariesFound => 'Nessuna libreria trovata'; + @override String get allLibrariesHidden => 'Tutte le librerie sono nascoste'; + @override String hiddenLibrariesCount({required Object count}) => 'Librerie nascoste (${count})'; @override String get thisLibraryIsEmpty => 'Questa libreria è vuota'; @override String get all => 'Tutto'; @override String get clearAll => 'Cancella tutto'; @@ -1623,6 +1627,8 @@ extension on TranslationsIt { 'settings.useGlobalHubsDescription' => 'Mostra gli hub della home page come il client Plex ufficiale. Se disattivato, mostra invece i suggerimenti per libreria.', 'settings.showServerNameOnHubs' => 'Mostra nome server sugli hub', 'settings.showServerNameOnHubsDescription' => 'Mostra sempre il nome del server nei titoli degli hub. Se disattivato, solo per nomi hub duplicati.', + 'settings.groupLibrariesByServer' => 'Raggruppa librerie per server', + 'settings.groupLibrariesByServerDescription' => 'Mostra un\'intestazione per ogni server Plex nella barra laterale quando sei connesso a più server.', 'settings.alwaysKeepSidebarOpen' => 'Mantieni sempre aperta la barra laterale', 'settings.alwaysKeepSidebarOpenDescription' => 'La barra laterale rimane espansa e l\'area del contenuto si adatta', 'settings.showUnwatchedCount' => 'Mostra conteggio non visti', @@ -2031,14 +2037,16 @@ extension on TranslationsIt { 'libraries.analysisStarted' => ({required Object title}) => 'Analisi iniziata per "${title}"', 'libraries.failedToAnalyze' => ({required Object error}) => 'Impossibile analizzare libreria: ${error}', 'libraries.noLibrariesFound' => 'Nessuna libreria trovata', + 'libraries.allLibrariesHidden' => 'Tutte le librerie sono nascoste', + 'libraries.hiddenLibrariesCount' => ({required Object count}) => 'Librerie nascoste (${count})', 'libraries.thisLibraryIsEmpty' => 'Questa libreria è vuota', 'libraries.all' => 'Tutto', + _ => null, + } ?? switch (path) { 'libraries.clearAll' => 'Cancella tutto', 'libraries.scanLibraryConfirm' => ({required Object title}) => 'Sei sicuro di voler scansionare "${title}"?', 'libraries.analyzeLibraryConfirm' => ({required Object title}) => 'Sei sicuro di voler analizzare "${title}"?', 'libraries.refreshMetadataConfirm' => ({required Object title}) => 'Sei sicuro di voler aggiornare i metadati per "${title}"?', - _ => null, - } ?? switch (path) { 'libraries.emptyTrashConfirm' => ({required Object title}) => 'Sei sicuro di voler svuotare il cestino per "${title}"?', 'libraries.manageLibraries' => 'Gestisci librerie', 'libraries.sort' => 'Ordina', diff --git a/lib/i18n/strings_ja.g.dart b/lib/i18n/strings_ja.g.dart index 4c2679d1..69d7d20b 100644 --- a/lib/i18n/strings_ja.g.dart +++ b/lib/i18n/strings_ja.g.dart @@ -230,6 +230,8 @@ class _TranslationsSettingsJa implements TranslationsSettingsEn { @override String get useGlobalHubsDescription => '公式Plexクライアントのようにホームページのハブを表示。オフにすると、ライブラリごとのおすすめを表示。'; @override String get showServerNameOnHubs => 'ハブにサーバー名を表示'; @override String get showServerNameOnHubsDescription => 'ハブタイトルに常にサーバー名を表示。オフにすると、重複名のみ表示。'; + @override String get groupLibrariesByServer => 'サーバーごとにライブラリをグループ化'; + @override String get groupLibrariesByServerDescription => '複数のサーバーに接続しているとき、サイドバーに各 Plex サーバーのヘッダーを表示します。'; @override String get alwaysKeepSidebarOpen => 'サイドバーを常に開いておく'; @override String get alwaysKeepSidebarOpenDescription => 'サイドバーを展開したまま、コンテンツ領域が調整される'; @override String get showUnwatchedCount => '未視聴数を表示'; @@ -747,6 +749,8 @@ class _TranslationsLibrariesJa implements TranslationsLibrariesEn { @override String analysisStarted({required Object title}) => '"${title}"の解析を開始しました'; @override String failedToAnalyze({required Object error}) => 'ライブラリの解析に失敗しました: ${error}'; @override String get noLibrariesFound => 'ライブラリが見つかりません'; + @override String get allLibrariesHidden => 'すべてのライブラリが非表示です'; + @override String hiddenLibrariesCount({required Object count}) => '非表示のライブラリ (${count})'; @override String get thisLibraryIsEmpty => 'このライブラリは空です'; @override String get all => 'すべて'; @override String get clearAll => 'すべてクリア'; @@ -1623,6 +1627,8 @@ extension on TranslationsJa { 'settings.useGlobalHubsDescription' => '公式Plexクライアントのようにホームページのハブを表示。オフにすると、ライブラリごとのおすすめを表示。', 'settings.showServerNameOnHubs' => 'ハブにサーバー名を表示', 'settings.showServerNameOnHubsDescription' => 'ハブタイトルに常にサーバー名を表示。オフにすると、重複名のみ表示。', + 'settings.groupLibrariesByServer' => 'サーバーごとにライブラリをグループ化', + 'settings.groupLibrariesByServerDescription' => '複数のサーバーに接続しているとき、サイドバーに各 Plex サーバーのヘッダーを表示します。', 'settings.alwaysKeepSidebarOpen' => 'サイドバーを常に開いておく', 'settings.alwaysKeepSidebarOpenDescription' => 'サイドバーを展開したまま、コンテンツ領域が調整される', 'settings.showUnwatchedCount' => '未視聴数を表示', @@ -2031,14 +2037,16 @@ extension on TranslationsJa { 'libraries.analysisStarted' => ({required Object title}) => '"${title}"の解析を開始しました', 'libraries.failedToAnalyze' => ({required Object error}) => 'ライブラリの解析に失敗しました: ${error}', 'libraries.noLibrariesFound' => 'ライブラリが見つかりません', + 'libraries.allLibrariesHidden' => 'すべてのライブラリが非表示です', + 'libraries.hiddenLibrariesCount' => ({required Object count}) => '非表示のライブラリ (${count})', 'libraries.thisLibraryIsEmpty' => 'このライブラリは空です', 'libraries.all' => 'すべて', + _ => null, + } ?? switch (path) { 'libraries.clearAll' => 'すべてクリア', 'libraries.scanLibraryConfirm' => ({required Object title}) => '"${title}"をスキャンしてもよろしいですか?', 'libraries.analyzeLibraryConfirm' => ({required Object title}) => '"${title}"を解析してもよろしいですか?', 'libraries.refreshMetadataConfirm' => ({required Object title}) => '"${title}"のメタデータを更新してもよろしいですか?', - _ => null, - } ?? switch (path) { 'libraries.emptyTrashConfirm' => ({required Object title}) => '"${title}"のゴミ箱を空にしてもよろしいですか?', 'libraries.manageLibraries' => 'ライブラリを管理', 'libraries.sort' => '並べ替え', diff --git a/lib/i18n/strings_ko.g.dart b/lib/i18n/strings_ko.g.dart index 3f66f026..501d3ae5 100644 --- a/lib/i18n/strings_ko.g.dart +++ b/lib/i18n/strings_ko.g.dart @@ -230,6 +230,8 @@ class _TranslationsSettingsKo implements TranslationsSettingsEn { @override String get useGlobalHubsDescription => '공식 Plex 클라이언트처럼 홈 페이지 허브를 표시합니다. 끄면 라이브러리별 추천이 대신 표시됩니다.'; @override String get showServerNameOnHubs => '허브에 서버 이름 표시'; @override String get showServerNameOnHubsDescription => '허브 제목에 항상 서버 이름을 표시합니다. 끄면 중복된 허브 이름에만 표시됩니다.'; + @override String get groupLibrariesByServer => '서버별로 라이브러리 그룹화'; + @override String get groupLibrariesByServerDescription => '여러 서버에 연결되어 있을 때 사이드바에 각 Plex 서버의 헤더를 표시합니다.'; @override String get alwaysKeepSidebarOpen => '사이드바 항상 열어두기'; @override String get alwaysKeepSidebarOpenDescription => '사이드바가 확장된 상태로 유지되고 콘텐츠 영역이 맞춰집니다'; @override String get showUnwatchedCount => '미시청 수 표시'; @@ -747,6 +749,8 @@ class _TranslationsLibrariesKo implements TranslationsLibrariesEn { @override String analysisStarted({required Object title}) => '"${title}" 분석 시작됨'; @override String failedToAnalyze({required Object error}) => '미디어 라이브러리 분석 실패: ${error}'; @override String get noLibrariesFound => '미디어 라이브러리 없음'; + @override String get allLibrariesHidden => '모든 라이브러리가 숨겨졌습니다'; + @override String hiddenLibrariesCount({required Object count}) => '숨겨진 라이브러리 (${count})'; @override String get thisLibraryIsEmpty => '이 미디어 라이브러리는 비어 있습니다'; @override String get all => '전체'; @override String get clearAll => '모두 삭제'; @@ -1623,6 +1627,8 @@ extension on TranslationsKo { 'settings.useGlobalHubsDescription' => '공식 Plex 클라이언트처럼 홈 페이지 허브를 표시합니다. 끄면 라이브러리별 추천이 대신 표시됩니다.', 'settings.showServerNameOnHubs' => '허브에 서버 이름 표시', 'settings.showServerNameOnHubsDescription' => '허브 제목에 항상 서버 이름을 표시합니다. 끄면 중복된 허브 이름에만 표시됩니다.', + 'settings.groupLibrariesByServer' => '서버별로 라이브러리 그룹화', + 'settings.groupLibrariesByServerDescription' => '여러 서버에 연결되어 있을 때 사이드바에 각 Plex 서버의 헤더를 표시합니다.', 'settings.alwaysKeepSidebarOpen' => '사이드바 항상 열어두기', 'settings.alwaysKeepSidebarOpenDescription' => '사이드바가 확장된 상태로 유지되고 콘텐츠 영역이 맞춰집니다', 'settings.showUnwatchedCount' => '미시청 수 표시', @@ -2031,14 +2037,16 @@ extension on TranslationsKo { 'libraries.analysisStarted' => ({required Object title}) => '"${title}" 분석 시작됨', 'libraries.failedToAnalyze' => ({required Object error}) => '미디어 라이브러리 분석 실패: ${error}', 'libraries.noLibrariesFound' => '미디어 라이브러리 없음', + 'libraries.allLibrariesHidden' => '모든 라이브러리가 숨겨졌습니다', + 'libraries.hiddenLibrariesCount' => ({required Object count}) => '숨겨진 라이브러리 (${count})', 'libraries.thisLibraryIsEmpty' => '이 미디어 라이브러리는 비어 있습니다', 'libraries.all' => '전체', + _ => null, + } ?? switch (path) { 'libraries.clearAll' => '모두 삭제', 'libraries.scanLibraryConfirm' => ({required Object title}) => '「${title}」를 스캔 하시겠습니까?', 'libraries.analyzeLibraryConfirm' => ({required Object title}) => '「${title}」를 분석 하시겠습니까?', 'libraries.refreshMetadataConfirm' => ({required Object title}) => '「${title}」의 메타데이터를 새로고침 하시겠습니까?', - _ => null, - } ?? switch (path) { 'libraries.emptyTrashConfirm' => ({required Object title}) => '${title}의 휴지통을 비우시겠습니까?', 'libraries.manageLibraries' => '미디어 라이브러리 관리', 'libraries.sort' => '정렬', diff --git a/lib/i18n/strings_nb.g.dart b/lib/i18n/strings_nb.g.dart index 70091111..b39a59b4 100644 --- a/lib/i18n/strings_nb.g.dart +++ b/lib/i18n/strings_nb.g.dart @@ -230,6 +230,8 @@ class _TranslationsSettingsNb implements TranslationsSettingsEn { @override String get useGlobalHubsDescription => 'Vis hjemmeside-huber som den offisielle Plex-klienten. Når av, vises per-bibliotek-anbefalinger i stedet.'; @override String get showServerNameOnHubs => 'Vis servernavn på huber'; @override String get showServerNameOnHubsDescription => 'Vis alltid servernavnet i hubtitler. Når av, vises kun for dupliserte hubnavn.'; + @override String get groupLibrariesByServer => 'Grupper biblioteker etter server'; + @override String get groupLibrariesByServerDescription => 'Vis en overskrift for hver Plex-server i sidefeltet når du er koblet til flere servere.'; @override String get alwaysKeepSidebarOpen => 'Hold sidefeltet alltid åpent'; @override String get alwaysKeepSidebarOpenDescription => 'Sidefeltet forblir utvidet og innholdsområdet tilpasser seg'; @override String get showUnwatchedCount => 'Vis antall usette'; @@ -747,6 +749,8 @@ class _TranslationsLibrariesNb implements TranslationsLibrariesEn { @override String analysisStarted({required Object title}) => 'Analyse startet for "${title}"'; @override String failedToAnalyze({required Object error}) => 'Kunne ikke analysere bibliotek: ${error}'; @override String get noLibrariesFound => 'Ingen biblioteker funnet'; + @override String get allLibrariesHidden => 'Alle biblioteker er skjult'; + @override String hiddenLibrariesCount({required Object count}) => 'Skjulte biblioteker (${count})'; @override String get thisLibraryIsEmpty => 'Dette biblioteket er tomt'; @override String get all => 'Alle'; @override String get clearAll => 'Tøm alle'; @@ -1623,6 +1627,8 @@ extension on TranslationsNb { 'settings.useGlobalHubsDescription' => 'Vis hjemmeside-huber som den offisielle Plex-klienten. Når av, vises per-bibliotek-anbefalinger i stedet.', 'settings.showServerNameOnHubs' => 'Vis servernavn på huber', 'settings.showServerNameOnHubsDescription' => 'Vis alltid servernavnet i hubtitler. Når av, vises kun for dupliserte hubnavn.', + 'settings.groupLibrariesByServer' => 'Grupper biblioteker etter server', + 'settings.groupLibrariesByServerDescription' => 'Vis en overskrift for hver Plex-server i sidefeltet når du er koblet til flere servere.', 'settings.alwaysKeepSidebarOpen' => 'Hold sidefeltet alltid åpent', 'settings.alwaysKeepSidebarOpenDescription' => 'Sidefeltet forblir utvidet og innholdsområdet tilpasser seg', 'settings.showUnwatchedCount' => 'Vis antall usette', @@ -2031,14 +2037,16 @@ extension on TranslationsNb { 'libraries.analysisStarted' => ({required Object title}) => 'Analyse startet for "${title}"', 'libraries.failedToAnalyze' => ({required Object error}) => 'Kunne ikke analysere bibliotek: ${error}', 'libraries.noLibrariesFound' => 'Ingen biblioteker funnet', + 'libraries.allLibrariesHidden' => 'Alle biblioteker er skjult', + 'libraries.hiddenLibrariesCount' => ({required Object count}) => 'Skjulte biblioteker (${count})', 'libraries.thisLibraryIsEmpty' => 'Dette biblioteket er tomt', 'libraries.all' => 'Alle', + _ => null, + } ?? switch (path) { 'libraries.clearAll' => 'Tøm alle', 'libraries.scanLibraryConfirm' => ({required Object title}) => 'Er du sikker på at du vil skanne "${title}"?', 'libraries.analyzeLibraryConfirm' => ({required Object title}) => 'Er du sikker på at du vil analysere "${title}"?', 'libraries.refreshMetadataConfirm' => ({required Object title}) => 'Er du sikker på at du vil oppdatere metadata for "${title}"?', - _ => null, - } ?? switch (path) { 'libraries.emptyTrashConfirm' => ({required Object title}) => 'Er du sikker på at du vil tømme papirkurven for "${title}"?', 'libraries.manageLibraries' => 'Administrer biblioteker', 'libraries.sort' => 'Sorter', diff --git a/lib/i18n/strings_nl.g.dart b/lib/i18n/strings_nl.g.dart index bedeccac..436ce598 100644 --- a/lib/i18n/strings_nl.g.dart +++ b/lib/i18n/strings_nl.g.dart @@ -230,6 +230,8 @@ class _TranslationsSettingsNl implements TranslationsSettingsEn { @override String get useGlobalHubsDescription => 'Toon startpagina-hubs zoals de officiële Plex-client. Indien uitgeschakeld, worden in plaats daarvan aanbevelingen per bibliotheek getoond.'; @override String get showServerNameOnHubs => 'Servernaam tonen bij hubs'; @override String get showServerNameOnHubsDescription => 'Toon altijd de servernaam in hub-titels. Indien uitgeschakeld, alleen bij dubbele hub-namen.'; + @override String get groupLibrariesByServer => 'Bibliotheken groeperen per server'; + @override String get groupLibrariesByServerDescription => 'Toont een kop voor elke Plex-server in de zijbalk wanneer je met meerdere servers verbonden bent.'; @override String get alwaysKeepSidebarOpen => 'Zijbalk altijd open houden'; @override String get alwaysKeepSidebarOpenDescription => 'Zijbalk blijft uitgevouwen en inhoudsgebied past zich aan'; @override String get showUnwatchedCount => 'Aantal ongekeken tonen'; @@ -747,6 +749,8 @@ class _TranslationsLibrariesNl implements TranslationsLibrariesEn { @override String analysisStarted({required Object title}) => 'Analyse gestart voor "${title}"'; @override String failedToAnalyze({required Object error}) => 'Kon bibliotheek niet analyseren: ${error}'; @override String get noLibrariesFound => 'Geen bibliotheken gevonden'; + @override String get allLibrariesHidden => 'Alle bibliotheken zijn verborgen'; + @override String hiddenLibrariesCount({required Object count}) => 'Verborgen bibliotheken (${count})'; @override String get thisLibraryIsEmpty => 'Deze bibliotheek is leeg'; @override String get all => 'Alles'; @override String get clearAll => 'Alles wissen'; @@ -1623,6 +1627,8 @@ extension on TranslationsNl { 'settings.useGlobalHubsDescription' => 'Toon startpagina-hubs zoals de officiële Plex-client. Indien uitgeschakeld, worden in plaats daarvan aanbevelingen per bibliotheek getoond.', 'settings.showServerNameOnHubs' => 'Servernaam tonen bij hubs', 'settings.showServerNameOnHubsDescription' => 'Toon altijd de servernaam in hub-titels. Indien uitgeschakeld, alleen bij dubbele hub-namen.', + 'settings.groupLibrariesByServer' => 'Bibliotheken groeperen per server', + 'settings.groupLibrariesByServerDescription' => 'Toont een kop voor elke Plex-server in de zijbalk wanneer je met meerdere servers verbonden bent.', 'settings.alwaysKeepSidebarOpen' => 'Zijbalk altijd open houden', 'settings.alwaysKeepSidebarOpenDescription' => 'Zijbalk blijft uitgevouwen en inhoudsgebied past zich aan', 'settings.showUnwatchedCount' => 'Aantal ongekeken tonen', @@ -2031,14 +2037,16 @@ extension on TranslationsNl { 'libraries.analysisStarted' => ({required Object title}) => 'Analyse gestart voor "${title}"', 'libraries.failedToAnalyze' => ({required Object error}) => 'Kon bibliotheek niet analyseren: ${error}', 'libraries.noLibrariesFound' => 'Geen bibliotheken gevonden', + 'libraries.allLibrariesHidden' => 'Alle bibliotheken zijn verborgen', + 'libraries.hiddenLibrariesCount' => ({required Object count}) => 'Verborgen bibliotheken (${count})', 'libraries.thisLibraryIsEmpty' => 'Deze bibliotheek is leeg', 'libraries.all' => 'Alles', + _ => null, + } ?? switch (path) { 'libraries.clearAll' => 'Alles wissen', 'libraries.scanLibraryConfirm' => ({required Object title}) => 'Weet je zeker dat je "${title}" wilt scannen?', 'libraries.analyzeLibraryConfirm' => ({required Object title}) => 'Weet je zeker dat je "${title}" wilt analyseren?', 'libraries.refreshMetadataConfirm' => ({required Object title}) => 'Weet je zeker dat je metadata wilt vernieuwen voor "${title}"?', - _ => null, - } ?? switch (path) { 'libraries.emptyTrashConfirm' => ({required Object title}) => 'Weet je zeker dat je de prullenbak wilt legen voor "${title}"?', 'libraries.manageLibraries' => 'Beheer bibliotheken', 'libraries.sort' => 'Sorteren', diff --git a/lib/i18n/strings_pl.g.dart b/lib/i18n/strings_pl.g.dart index fca35718..21bab4a5 100644 --- a/lib/i18n/strings_pl.g.dart +++ b/lib/i18n/strings_pl.g.dart @@ -230,6 +230,8 @@ class _TranslationsSettingsPl implements TranslationsSettingsEn { @override String get useGlobalHubsDescription => 'Pokaż huby strony głównej jak w oficjalnym kliencie Plex. Gdy wyłączone, pokazuje rekomendacje per biblioteka.'; @override String get showServerNameOnHubs => 'Pokaż nazwę serwera w hubach'; @override String get showServerNameOnHubsDescription => 'Zawsze wyświetlaj nazwę serwera w tytułach hubów. Gdy wyłączone, pokazuje tylko dla zduplikowanych nazw.'; + @override String get groupLibrariesByServer => 'Grupuj biblioteki według serwera'; + @override String get groupLibrariesByServerDescription => 'Pokazuj nagłówek dla każdego serwera Plex na pasku bocznym, gdy jesteś połączony z wieloma serwerami.'; @override String get alwaysKeepSidebarOpen => 'Zawsze utrzymuj panel boczny otwarty'; @override String get alwaysKeepSidebarOpenDescription => 'Panel boczny jest rozwinięty, a obszar treści dostosowuje się'; @override String get showUnwatchedCount => 'Pokaż liczbę nieobejrzanych'; @@ -747,6 +749,8 @@ class _TranslationsLibrariesPl implements TranslationsLibrariesEn { @override String analysisStarted({required Object title}) => 'Analiza rozpoczęta dla "${title}"'; @override String failedToAnalyze({required Object error}) => 'Nie udało się przeanalizować biblioteki: ${error}'; @override String get noLibrariesFound => 'Nie znaleziono bibliotek'; + @override String get allLibrariesHidden => 'Wszystkie biblioteki są ukryte'; + @override String hiddenLibrariesCount({required Object count}) => 'Ukryte biblioteki (${count})'; @override String get thisLibraryIsEmpty => 'Ta biblioteka jest pusta'; @override String get all => 'Wszystkie'; @override String get clearAll => 'Wyczyść wszystko'; @@ -1623,6 +1627,8 @@ extension on TranslationsPl { 'settings.useGlobalHubsDescription' => 'Pokaż huby strony głównej jak w oficjalnym kliencie Plex. Gdy wyłączone, pokazuje rekomendacje per biblioteka.', 'settings.showServerNameOnHubs' => 'Pokaż nazwę serwera w hubach', 'settings.showServerNameOnHubsDescription' => 'Zawsze wyświetlaj nazwę serwera w tytułach hubów. Gdy wyłączone, pokazuje tylko dla zduplikowanych nazw.', + 'settings.groupLibrariesByServer' => 'Grupuj biblioteki według serwera', + 'settings.groupLibrariesByServerDescription' => 'Pokazuj nagłówek dla każdego serwera Plex na pasku bocznym, gdy jesteś połączony z wieloma serwerami.', 'settings.alwaysKeepSidebarOpen' => 'Zawsze utrzymuj panel boczny otwarty', 'settings.alwaysKeepSidebarOpenDescription' => 'Panel boczny jest rozwinięty, a obszar treści dostosowuje się', 'settings.showUnwatchedCount' => 'Pokaż liczbę nieobejrzanych', @@ -2031,14 +2037,16 @@ extension on TranslationsPl { 'libraries.analysisStarted' => ({required Object title}) => 'Analiza rozpoczęta dla "${title}"', 'libraries.failedToAnalyze' => ({required Object error}) => 'Nie udało się przeanalizować biblioteki: ${error}', 'libraries.noLibrariesFound' => 'Nie znaleziono bibliotek', + 'libraries.allLibrariesHidden' => 'Wszystkie biblioteki są ukryte', + 'libraries.hiddenLibrariesCount' => ({required Object count}) => 'Ukryte biblioteki (${count})', 'libraries.thisLibraryIsEmpty' => 'Ta biblioteka jest pusta', 'libraries.all' => 'Wszystkie', + _ => null, + } ?? switch (path) { 'libraries.clearAll' => 'Wyczyść wszystko', 'libraries.scanLibraryConfirm' => ({required Object title}) => 'Czy na pewno chcesz zeskanować "${title}"?', 'libraries.analyzeLibraryConfirm' => ({required Object title}) => 'Czy na pewno chcesz przeanalizować "${title}"?', 'libraries.refreshMetadataConfirm' => ({required Object title}) => 'Czy na pewno chcesz odświeżyć metadane dla "${title}"?', - _ => null, - } ?? switch (path) { 'libraries.emptyTrashConfirm' => ({required Object title}) => 'Czy na pewno chcesz opróżnić kosz dla "${title}"?', 'libraries.manageLibraries' => 'Zarządzaj bibliotekami', 'libraries.sort' => 'Sortuj', diff --git a/lib/i18n/strings_pt.g.dart b/lib/i18n/strings_pt.g.dart index 05351192..2f6de77e 100644 --- a/lib/i18n/strings_pt.g.dart +++ b/lib/i18n/strings_pt.g.dart @@ -230,6 +230,8 @@ class _TranslationsSettingsPt implements TranslationsSettingsEn { @override String get useGlobalHubsDescription => 'Mostrar hubs da página inicial como o cliente oficial Plex. Quando desativado, mostra recomendações por biblioteca.'; @override String get showServerNameOnHubs => 'Mostrar Nome do Servidor nos Hubs'; @override String get showServerNameOnHubsDescription => 'Sempre exibir o nome do servidor nos títulos dos hubs. Quando desativado, mostra apenas para nomes duplicados.'; + @override String get groupLibrariesByServer => 'Agrupar Bibliotecas por Servidor'; + @override String get groupLibrariesByServerDescription => 'Mostra um cabeçalho para cada servidor Plex na barra lateral quando você está conectado a vários servidores.'; @override String get alwaysKeepSidebarOpen => 'Manter Barra Lateral Sempre Aberta'; @override String get alwaysKeepSidebarOpenDescription => 'A barra lateral fica expandida e a área de conteúdo se ajusta'; @override String get showUnwatchedCount => 'Mostrar Contagem de Não Assistidos'; @@ -747,6 +749,8 @@ class _TranslationsLibrariesPt implements TranslationsLibrariesEn { @override String analysisStarted({required Object title}) => 'Análise iniciada para "${title}"'; @override String failedToAnalyze({required Object error}) => 'Falha ao analisar biblioteca: ${error}'; @override String get noLibrariesFound => 'Nenhuma biblioteca encontrada'; + @override String get allLibrariesHidden => 'Todas as bibliotecas estão ocultas'; + @override String hiddenLibrariesCount({required Object count}) => 'Bibliotecas ocultas (${count})'; @override String get thisLibraryIsEmpty => 'Esta biblioteca está vazia'; @override String get all => 'Todos'; @override String get clearAll => 'Limpar Tudo'; @@ -1623,6 +1627,8 @@ extension on TranslationsPt { 'settings.useGlobalHubsDescription' => 'Mostrar hubs da página inicial como o cliente oficial Plex. Quando desativado, mostra recomendações por biblioteca.', 'settings.showServerNameOnHubs' => 'Mostrar Nome do Servidor nos Hubs', 'settings.showServerNameOnHubsDescription' => 'Sempre exibir o nome do servidor nos títulos dos hubs. Quando desativado, mostra apenas para nomes duplicados.', + 'settings.groupLibrariesByServer' => 'Agrupar Bibliotecas por Servidor', + 'settings.groupLibrariesByServerDescription' => 'Mostra um cabeçalho para cada servidor Plex na barra lateral quando você está conectado a vários servidores.', 'settings.alwaysKeepSidebarOpen' => 'Manter Barra Lateral Sempre Aberta', 'settings.alwaysKeepSidebarOpenDescription' => 'A barra lateral fica expandida e a área de conteúdo se ajusta', 'settings.showUnwatchedCount' => 'Mostrar Contagem de Não Assistidos', @@ -2031,14 +2037,16 @@ extension on TranslationsPt { 'libraries.analysisStarted' => ({required Object title}) => 'Análise iniciada para "${title}"', 'libraries.failedToAnalyze' => ({required Object error}) => 'Falha ao analisar biblioteca: ${error}', 'libraries.noLibrariesFound' => 'Nenhuma biblioteca encontrada', + 'libraries.allLibrariesHidden' => 'Todas as bibliotecas estão ocultas', + 'libraries.hiddenLibrariesCount' => ({required Object count}) => 'Bibliotecas ocultas (${count})', 'libraries.thisLibraryIsEmpty' => 'Esta biblioteca está vazia', 'libraries.all' => 'Todos', + _ => null, + } ?? switch (path) { 'libraries.clearAll' => 'Limpar Tudo', 'libraries.scanLibraryConfirm' => ({required Object title}) => 'Tem certeza que deseja escanear "${title}"?', 'libraries.analyzeLibraryConfirm' => ({required Object title}) => 'Tem certeza que deseja analisar "${title}"?', 'libraries.refreshMetadataConfirm' => ({required Object title}) => 'Tem certeza que deseja atualizar os metadados de "${title}"?', - _ => null, - } ?? switch (path) { 'libraries.emptyTrashConfirm' => ({required Object title}) => 'Tem certeza que deseja esvaziar a lixeira de "${title}"?', 'libraries.manageLibraries' => 'Gerenciar Bibliotecas', 'libraries.sort' => 'Ordenar', diff --git a/lib/i18n/strings_ru.g.dart b/lib/i18n/strings_ru.g.dart index f5dd0908..7fbc4882 100644 --- a/lib/i18n/strings_ru.g.dart +++ b/lib/i18n/strings_ru.g.dart @@ -230,6 +230,8 @@ class _TranslationsSettingsRu implements TranslationsSettingsEn { @override String get useGlobalHubsDescription => 'Показывать хабы главной страницы как в официальном клиенте Plex. При выключении показывает рекомендации по библиотекам.'; @override String get showServerNameOnHubs => 'Показывать имя сервера в хабах'; @override String get showServerNameOnHubsDescription => 'Всегда показывать имя сервера в заголовках хабов. При выключении показывает только для дублирующихся имён.'; + @override String get groupLibrariesByServer => 'Группировать библиотеки по серверам'; + @override String get groupLibrariesByServerDescription => 'Показывать заголовок для каждого сервера Plex на боковой панели при подключении к нескольким серверам.'; @override String get alwaysKeepSidebarOpen => 'Всегда держать боковую панель открытой'; @override String get alwaysKeepSidebarOpenDescription => 'Боковая панель остаётся развёрнутой, область контента подстраивается'; @override String get showUnwatchedCount => 'Показывать количество непросмотренных'; @@ -747,6 +749,8 @@ class _TranslationsLibrariesRu implements TranslationsLibrariesEn { @override String analysisStarted({required Object title}) => 'Анализ начат для "${title}"'; @override String failedToAnalyze({required Object error}) => 'Не удалось проанализировать библиотеку: ${error}'; @override String get noLibrariesFound => 'Библиотеки не найдены'; + @override String get allLibrariesHidden => 'Все библиотеки скрыты'; + @override String hiddenLibrariesCount({required Object count}) => 'Скрытые библиотеки (${count})'; @override String get thisLibraryIsEmpty => 'Эта библиотека пуста'; @override String get all => 'Все'; @override String get clearAll => 'Очистить все'; @@ -1623,6 +1627,8 @@ extension on TranslationsRu { 'settings.useGlobalHubsDescription' => 'Показывать хабы главной страницы как в официальном клиенте Plex. При выключении показывает рекомендации по библиотекам.', 'settings.showServerNameOnHubs' => 'Показывать имя сервера в хабах', 'settings.showServerNameOnHubsDescription' => 'Всегда показывать имя сервера в заголовках хабов. При выключении показывает только для дублирующихся имён.', + 'settings.groupLibrariesByServer' => 'Группировать библиотеки по серверам', + 'settings.groupLibrariesByServerDescription' => 'Показывать заголовок для каждого сервера Plex на боковой панели при подключении к нескольким серверам.', 'settings.alwaysKeepSidebarOpen' => 'Всегда держать боковую панель открытой', 'settings.alwaysKeepSidebarOpenDescription' => 'Боковая панель остаётся развёрнутой, область контента подстраивается', 'settings.showUnwatchedCount' => 'Показывать количество непросмотренных', @@ -2031,14 +2037,16 @@ extension on TranslationsRu { 'libraries.analysisStarted' => ({required Object title}) => 'Анализ начат для "${title}"', 'libraries.failedToAnalyze' => ({required Object error}) => 'Не удалось проанализировать библиотеку: ${error}', 'libraries.noLibrariesFound' => 'Библиотеки не найдены', + 'libraries.allLibrariesHidden' => 'Все библиотеки скрыты', + 'libraries.hiddenLibrariesCount' => ({required Object count}) => 'Скрытые библиотеки (${count})', 'libraries.thisLibraryIsEmpty' => 'Эта библиотека пуста', 'libraries.all' => 'Все', + _ => null, + } ?? switch (path) { 'libraries.clearAll' => 'Очистить все', 'libraries.scanLibraryConfirm' => ({required Object title}) => 'Вы уверены, что хотите сканировать "${title}"?', 'libraries.analyzeLibraryConfirm' => ({required Object title}) => 'Вы уверены, что хотите проанализировать "${title}"?', 'libraries.refreshMetadataConfirm' => ({required Object title}) => 'Вы уверены, что хотите обновить метаданные для "${title}"?', - _ => null, - } ?? switch (path) { 'libraries.emptyTrashConfirm' => ({required Object title}) => 'Вы уверены, что хотите очистить корзину для "${title}"?', 'libraries.manageLibraries' => 'Управление библиотеками', 'libraries.sort' => 'Сортировка', diff --git a/lib/i18n/strings_sv.g.dart b/lib/i18n/strings_sv.g.dart index cd7af186..78ef5afa 100644 --- a/lib/i18n/strings_sv.g.dart +++ b/lib/i18n/strings_sv.g.dart @@ -230,6 +230,8 @@ class _TranslationsSettingsSv implements TranslationsSettingsEn { @override String get useGlobalHubsDescription => 'Visar startsidans hubbar som den officiella Plex-klienten. När av visas rekommendationer per bibliotek istället.'; @override String get showServerNameOnHubs => 'Visa servernamn på hubbar'; @override String get showServerNameOnHubsDescription => 'Visa alltid servernamnet i hubbtitlar. När av visas endast för duplicerade hubbnamn.'; + @override String get groupLibrariesByServer => 'Gruppera bibliotek efter server'; + @override String get groupLibrariesByServerDescription => 'Visa en rubrik för varje Plex-server i sidofältet när du är ansluten till flera servrar.'; @override String get alwaysKeepSidebarOpen => 'Håll sidofältet alltid öppet'; @override String get alwaysKeepSidebarOpenDescription => 'Sidofältet förblir expanderat och innehållsytan anpassas'; @override String get showUnwatchedCount => 'Visa antal osedda'; @@ -747,6 +749,8 @@ class _TranslationsLibrariesSv implements TranslationsLibrariesEn { @override String analysisStarted({required Object title}) => 'Analys startad för "${title}"'; @override String failedToAnalyze({required Object error}) => 'Misslyckades att analysera bibliotek: ${error}'; @override String get noLibrariesFound => 'Inga bibliotek hittades'; + @override String get allLibrariesHidden => 'Alla bibliotek är dolda'; + @override String hiddenLibrariesCount({required Object count}) => 'Dolda bibliotek (${count})'; @override String get thisLibraryIsEmpty => 'Detta bibliotek är tomt'; @override String get all => 'Alla'; @override String get clearAll => 'Rensa alla'; @@ -1623,6 +1627,8 @@ extension on TranslationsSv { 'settings.useGlobalHubsDescription' => 'Visar startsidans hubbar som den officiella Plex-klienten. När av visas rekommendationer per bibliotek istället.', 'settings.showServerNameOnHubs' => 'Visa servernamn på hubbar', 'settings.showServerNameOnHubsDescription' => 'Visa alltid servernamnet i hubbtitlar. När av visas endast för duplicerade hubbnamn.', + 'settings.groupLibrariesByServer' => 'Gruppera bibliotek efter server', + 'settings.groupLibrariesByServerDescription' => 'Visa en rubrik för varje Plex-server i sidofältet när du är ansluten till flera servrar.', 'settings.alwaysKeepSidebarOpen' => 'Håll sidofältet alltid öppet', 'settings.alwaysKeepSidebarOpenDescription' => 'Sidofältet förblir expanderat och innehållsytan anpassas', 'settings.showUnwatchedCount' => 'Visa antal osedda', @@ -2031,14 +2037,16 @@ extension on TranslationsSv { 'libraries.analysisStarted' => ({required Object title}) => 'Analys startad för "${title}"', 'libraries.failedToAnalyze' => ({required Object error}) => 'Misslyckades att analysera bibliotek: ${error}', 'libraries.noLibrariesFound' => 'Inga bibliotek hittades', + 'libraries.allLibrariesHidden' => 'Alla bibliotek är dolda', + 'libraries.hiddenLibrariesCount' => ({required Object count}) => 'Dolda bibliotek (${count})', 'libraries.thisLibraryIsEmpty' => 'Detta bibliotek är tomt', 'libraries.all' => 'Alla', + _ => null, + } ?? switch (path) { 'libraries.clearAll' => 'Rensa alla', 'libraries.scanLibraryConfirm' => ({required Object title}) => 'Är du säker på att du vill skanna "${title}"?', 'libraries.analyzeLibraryConfirm' => ({required Object title}) => 'Är du säker på att du vill analysera "${title}"?', 'libraries.refreshMetadataConfirm' => ({required Object title}) => 'Är du säker på att du vill uppdatera metadata för "${title}"?', - _ => null, - } ?? switch (path) { 'libraries.emptyTrashConfirm' => ({required Object title}) => 'Är du säker på att du vill tömma papperskorgen för "${title}"?', 'libraries.manageLibraries' => 'Hantera bibliotek', 'libraries.sort' => 'Sortera', diff --git a/lib/i18n/strings_zh.g.dart b/lib/i18n/strings_zh.g.dart index a0e570a7..02a0aaad 100644 --- a/lib/i18n/strings_zh.g.dart +++ b/lib/i18n/strings_zh.g.dart @@ -230,6 +230,8 @@ class _TranslationsSettingsZh implements TranslationsSettingsEn { @override String get useGlobalHubsDescription => '显示与官方 Plex 客户端相同的主页推荐。关闭时将显示按媒体库分类的推荐。'; @override String get showServerNameOnHubs => '在推荐栏显示服务器名称'; @override String get showServerNameOnHubsDescription => '始终在推荐栏标题中显示服务器名称。关闭时仅在推荐栏名称重复时显示。'; + @override String get groupLibrariesByServer => '按服务器分组媒体库'; + @override String get groupLibrariesByServerDescription => '当您连接到多个服务器时,在侧边栏中为每个 Plex 服务器显示一个标题。'; @override String get alwaysKeepSidebarOpen => '始终保持侧边栏展开'; @override String get alwaysKeepSidebarOpenDescription => '侧边栏保持展开状态,内容区域自动调整'; @override String get showUnwatchedCount => '显示未观看数量'; @@ -747,6 +749,8 @@ class _TranslationsLibrariesZh implements TranslationsLibrariesEn { @override String analysisStarted({required Object title}) => '已开始分析 “${title}”'; @override String failedToAnalyze({required Object error}) => '无法分析媒体库: ${error}'; @override String get noLibrariesFound => '未找到媒体库'; + @override String get allLibrariesHidden => '所有媒体库已隐藏'; + @override String hiddenLibrariesCount({required Object count}) => '已隐藏的媒体库 (${count})'; @override String get thisLibraryIsEmpty => '此媒体库为空'; @override String get all => '全部'; @override String get clearAll => '全部清除'; @@ -1623,6 +1627,8 @@ extension on TranslationsZh { 'settings.useGlobalHubsDescription' => '显示与官方 Plex 客户端相同的主页推荐。关闭时将显示按媒体库分类的推荐。', 'settings.showServerNameOnHubs' => '在推荐栏显示服务器名称', 'settings.showServerNameOnHubsDescription' => '始终在推荐栏标题中显示服务器名称。关闭时仅在推荐栏名称重复时显示。', + 'settings.groupLibrariesByServer' => '按服务器分组媒体库', + 'settings.groupLibrariesByServerDescription' => '当您连接到多个服务器时,在侧边栏中为每个 Plex 服务器显示一个标题。', 'settings.alwaysKeepSidebarOpen' => '始终保持侧边栏展开', 'settings.alwaysKeepSidebarOpenDescription' => '侧边栏保持展开状态,内容区域自动调整', 'settings.showUnwatchedCount' => '显示未观看数量', @@ -2031,14 +2037,16 @@ extension on TranslationsZh { 'libraries.analysisStarted' => ({required Object title}) => '已开始分析 “${title}”', 'libraries.failedToAnalyze' => ({required Object error}) => '无法分析媒体库: ${error}', 'libraries.noLibrariesFound' => '未找到媒体库', + 'libraries.allLibrariesHidden' => '所有媒体库已隐藏', + 'libraries.hiddenLibrariesCount' => ({required Object count}) => '已隐藏的媒体库 (${count})', 'libraries.thisLibraryIsEmpty' => '此媒体库为空', 'libraries.all' => '全部', + _ => null, + } ?? switch (path) { 'libraries.clearAll' => '全部清除', 'libraries.scanLibraryConfirm' => ({required Object title}) => '确定要扫描 “${title}” 吗?', 'libraries.analyzeLibraryConfirm' => ({required Object title}) => '确定要分析 “${title}” 吗?', 'libraries.refreshMetadataConfirm' => ({required Object title}) => '确定要刷新 “${title}” 的元数据吗?', - _ => null, - } ?? switch (path) { 'libraries.emptyTrashConfirm' => ({required Object title}) => '确定要清空 “${title}” 的回收站吗?', 'libraries.manageLibraries' => '管理媒体库', 'libraries.sort' => '排序', diff --git a/lib/i18n/sv.i18n.json b/lib/i18n/sv.i18n.json index 931b022b..b5736813 100644 --- a/lib/i18n/sv.i18n.json +++ b/lib/i18n/sv.i18n.json @@ -108,6 +108,8 @@ "useGlobalHubsDescription": "Visar startsidans hubbar som den officiella Plex-klienten. När av visas rekommendationer per bibliotek istället.", "showServerNameOnHubs": "Visa servernamn på hubbar", "showServerNameOnHubsDescription": "Visa alltid servernamnet i hubbtitlar. När av visas endast för duplicerade hubbnamn.", + "groupLibrariesByServer": "Gruppera bibliotek efter server", + "groupLibrariesByServerDescription": "Visa en rubrik för varje Plex-server i sidofältet när du är ansluten till flera servrar.", "alwaysKeepSidebarOpen": "Håll sidofältet alltid öppet", "alwaysKeepSidebarOpenDescription": "Sidofältet förblir expanderat och innehållsytan anpassas", "showUnwatchedCount": "Visa antal osedda", @@ -550,6 +552,8 @@ "analysisStarted": "Analys startad för \"${title}\"", "failedToAnalyze": "Misslyckades att analysera bibliotek: ${error}", "noLibrariesFound": "Inga bibliotek hittades", + "allLibrariesHidden": "Alla bibliotek är dolda", + "hiddenLibrariesCount": "Dolda bibliotek (${count})", "thisLibraryIsEmpty": "Detta bibliotek är tomt", "all": "Alla", "clearAll": "Rensa alla", diff --git a/lib/i18n/zh.i18n.json b/lib/i18n/zh.i18n.json index 854a799c..e97233cf 100644 --- a/lib/i18n/zh.i18n.json +++ b/lib/i18n/zh.i18n.json @@ -108,6 +108,8 @@ "useGlobalHubsDescription": "显示与官方 Plex 客户端相同的主页推荐。关闭时将显示按媒体库分类的推荐。", "showServerNameOnHubs": "在推荐栏显示服务器名称", "showServerNameOnHubsDescription": "始终在推荐栏标题中显示服务器名称。关闭时仅在推荐栏名称重复时显示。", + "groupLibrariesByServer": "按服务器分组媒体库", + "groupLibrariesByServerDescription": "当您连接到多个服务器时,在侧边栏中为每个 Plex 服务器显示一个标题。", "alwaysKeepSidebarOpen": "始终保持侧边栏展开", "alwaysKeepSidebarOpenDescription": "侧边栏保持展开状态,内容区域自动调整", "showUnwatchedCount": "显示未观看数量", @@ -550,6 +552,8 @@ "analysisStarted": "已开始分析 “${title}”", "failedToAnalyze": "无法分析媒体库: ${error}", "noLibrariesFound": "未找到媒体库", + "allLibrariesHidden": "所有媒体库已隐藏", + "hiddenLibrariesCount": "已隐藏的媒体库 (${count})", "thisLibraryIsEmpty": "此媒体库为空", "all": "全部", "clearAll": "全部清除", diff --git a/lib/providers/settings_provider.dart b/lib/providers/settings_provider.dart index 17682876..d20d2cda 100644 --- a/lib/providers/settings_provider.dart +++ b/lib/providers/settings_provider.dart @@ -10,6 +10,7 @@ class SettingsProvider extends ChangeNotifier { bool _showHeroSection = true; bool _useGlobalHubs = true; bool _showServerNameOnHubs = false; + bool _groupLibrariesByServer = true; bool _alwaysKeepSidebarOpen = false; bool _showUnwatchedCount = true; bool _showEpisodeNumberOnCards = true; @@ -53,6 +54,7 @@ class SettingsProvider extends ChangeNotifier { _showHeroSection = s.getShowHeroSection(); _useGlobalHubs = s.getUseGlobalHubs(); _showServerNameOnHubs = s.getShowServerNameOnHubs(); + _groupLibrariesByServer = s.getGroupLibrariesByServer(); _alwaysKeepSidebarOpen = s.getAlwaysKeepSidebarOpen(); _showUnwatchedCount = s.getShowUnwatchedCount(); _showEpisodeNumberOnCards = s.getShowEpisodeNumberOnCards(); @@ -87,6 +89,8 @@ class SettingsProvider extends ChangeNotifier { bool get showServerNameOnHubs => _showServerNameOnHubs; + bool get groupLibrariesByServer => _groupLibrariesByServer; + bool get alwaysKeepSidebarOpen => _alwaysKeepSidebarOpen; bool get showUnwatchedCount => _showUnwatchedCount; @@ -160,6 +164,13 @@ class SettingsProvider extends ChangeNotifier { persist: _settingsService!.setShowServerNameOnHubs, ); + Future setGroupLibrariesByServer(bool value) => _updateSetting( + current: _groupLibrariesByServer, + value: value, + setLocal: (v) => _groupLibrariesByServer = v, + persist: _settingsService!.setGroupLibrariesByServer, + ); + Future setAlwaysKeepSidebarOpen(bool value) => _updateSetting( current: _alwaysKeepSidebarOpen, value: value, diff --git a/lib/screens/libraries/libraries_screen.dart b/lib/screens/libraries/libraries_screen.dart index da8fd03b..1c4b3e25 100644 --- a/lib/screens/libraries/libraries_screen.dart +++ b/lib/screens/libraries/libraries_screen.dart @@ -424,22 +424,16 @@ class _LibrariesScreenState extends State } Future _loadLibraryContent(String libraryGlobalKey) async { - // Get libraries from provider final librariesProvider = context.read(); final allLibraries = librariesProvider.libraries; - // Compute visible libraries based on current provider state - final hiddenLibrariesProvider = Provider.of(context, listen: false); - final hiddenKeys = hiddenLibrariesProvider.hiddenLibraryKeys; - final visibleLibraries = allLibraries.where((lib) => !hiddenKeys.contains(lib.globalKey)).toList(); - - // Find the library by key - final libraryIndex = visibleLibraries.indexWhere((lib) => lib.globalKey == libraryGlobalKey); - if (libraryIndex == -1) return; // Library not found or hidden + // Resolve from allLibraries — hidden libraries are still navigable from the + // sidebar's "Hidden libraries" section. + final selectedLibrary = allLibraries.where((lib) => lib.globalKey == libraryGlobalKey).firstOrNull; + if (selectedLibrary == null) return; // Update visible tabs and state in the same synchronous block so no // intermediate rebuild can see a mismatched controller/key pair. - final selectedLibrary = visibleLibraries[libraryIndex]; _updateVisibleTabs(_getVisibleTabs(selectedLibrary)); _updateState(() { @@ -784,9 +778,9 @@ class _LibrariesScreenState extends State } /// Build the app bar title - either dropdown on mobile or simple title on desktop - Widget _buildAppBarTitle(List visibleLibraries) { - // No libraries or no selection - if (visibleLibraries.isEmpty || _selectedLibraryGlobalKey == null) { + Widget _buildAppBarTitle(List visibleLibraries, PlexLibrary? selectedLibrary) { + // No selection at all, or visible list is empty AND we're not browsing a hidden library + if (_selectedLibraryGlobalKey == null || (visibleLibraries.isEmpty && selectedLibrary == null)) { return Text(t.libraries.title); } @@ -884,7 +878,7 @@ class _LibrariesScreenState extends State controller: _outerScrollController, slivers: [ DesktopSliverAppBar( - title: _buildAppBarTitle(visibleLibraries), + title: _buildAppBarTitle(visibleLibraries, selectedLibrary), pinned: true, backgroundColor: Theme.of(context).scaffoldBackgroundColor, surfaceTintColor: Colors.transparent, @@ -913,7 +907,7 @@ class _LibrariesScreenState extends State ), if (isLoadingLibraries) const SliverFillRemaining(child: Center(child: CircularProgressIndicator())) - else if (_errorMessage != null && visibleLibraries.isEmpty) + else if (_errorMessage != null && visibleLibraries.isEmpty && selectedLibrary == null) SliverFillRemaining( child: ErrorStateWidget( message: _errorMessage!, @@ -924,9 +918,17 @@ class _LibrariesScreenState extends State }, ), ) - else if (visibleLibraries.isEmpty) + else if (visibleLibraries.isEmpty && selectedLibrary == null) SliverFillRemaining( - child: EmptyStateWidget(message: t.libraries.noLibrariesFound, icon: Symbols.video_library_rounded), + child: allLibraries.isEmpty + ? EmptyStateWidget(message: t.libraries.noLibrariesFound, icon: Symbols.video_library_rounded) + : EmptyStateWidget( + message: t.libraries.allLibrariesHidden, + icon: Symbols.visibility_off_rounded, + onAction: _showLibraryManagementSheet, + actionLabel: t.libraries.manageLibraries, + actionIcon: Symbols.edit_rounded, + ), ) else ...[ // Tab selector chips (only on mobile - desktop has them in app bar) diff --git a/lib/screens/libraries/state_messages.dart b/lib/screens/libraries/state_messages.dart index 3ef20250..43a3cff5 100644 --- a/lib/screens/libraries/state_messages.dart +++ b/lib/screens/libraries/state_messages.dart @@ -117,6 +117,9 @@ class EmptyStateWidget extends StatelessWidget { /// Optional label for the action button final String? actionLabel; + /// Optional icon for the action button (defaults to a generic add icon) + final IconData? actionIcon; + const EmptyStateWidget({ super.key, required this.message, @@ -125,6 +128,7 @@ class EmptyStateWidget extends StatelessWidget { this.iconSize = 64, this.onAction, this.actionLabel, + this.actionIcon, }); @override @@ -136,7 +140,7 @@ class EmptyStateWidget extends StatelessWidget { iconSize: iconSize, onAction: onAction, actionLabel: actionLabel, - actionIcon: Symbols.add_rounded, + actionIcon: actionIcon ?? Symbols.add_rounded, ); } } diff --git a/lib/screens/settings/appearance_settings_screen.dart b/lib/screens/settings/appearance_settings_screen.dart index 10e081e6..d0b6eb3f 100644 --- a/lib/screens/settings/appearance_settings_screen.dart +++ b/lib/screens/settings/appearance_settings_screen.dart @@ -80,6 +80,7 @@ class _AppearanceSettingsScreenState extends State { SettingsSectionHeader(t.settings.navigation), if (Platform.isAndroid) _buildForceTvMode(), if (PlatformDetector.shouldUseSideNavigation(context)) _buildAlwaysKeepSidebarOpen(), + if (PlatformDetector.shouldUseSideNavigation(context)) _buildGroupLibrariesByServer(), if (!PlatformDetector.shouldUseSideNavigation(context)) _buildShowNavBarLabels(), _buildShowUnwatchedCount(), @@ -278,6 +279,14 @@ class _AppearanceSettingsScreenState extends State { setter: (p, v) => p.setAlwaysKeepSidebarOpen(v), ); + Widget _buildGroupLibrariesByServer() => _buildBoolToggle( + icon: Symbols.dns_rounded, + title: t.settings.groupLibrariesByServer, + subtitle: t.settings.groupLibrariesByServerDescription, + getter: (p) => p.groupLibrariesByServer, + setter: (p, v) => p.setGroupLibrariesByServer(v), + ); + Widget _buildShowNavBarLabels() => _buildBoolToggle( icon: Symbols.label_rounded, title: t.settings.showNavBarLabels, diff --git a/lib/services/settings_service.dart b/lib/services/settings_service.dart index 89b602de..a9a09ccb 100644 --- a/lib/services/settings_service.dart +++ b/lib/services/settings_service.dart @@ -54,6 +54,7 @@ class SettingsService extends BaseSharedPreferencesService { static const String _keyShowHeroSection = 'show_hero_section'; static const String _keyUseGlobalHubs = 'use_global_hubs'; static const String _keyShowServerNameOnHubs = 'show_server_name_on_hubs'; + static const String _keyGroupLibrariesByServer = 'group_libraries_by_server'; static const String _keySleepTimerDuration = 'sleep_timer_duration'; static const String _keyAudioSyncOffset = 'audio_sync_offset'; static const String _keySubtitleSyncOffset = 'subtitle_sync_offset'; @@ -331,6 +332,15 @@ class SettingsService extends BaseSharedPreferencesService { return prefs.getBool(_keyShowServerNameOnHubs) ?? false; // Default: false (only show on duplicates) } + // Group Libraries by Server in the side rail + Future setGroupLibrariesByServer(bool enabled) async { + await prefs.setBool(_keyGroupLibrariesByServer, enabled); + } + + bool getGroupLibrariesByServer() { + return prefs.getBool(_keyGroupLibrariesByServer) ?? true; + } + // Seek Time Small (in seconds) Future setSeekTimeSmall(int seconds) async { await prefs.setInt(_keySeekTimeSmall, seconds); diff --git a/lib/widgets/side_navigation_rail.dart b/lib/widgets/side_navigation_rail.dart index 84e0b8dd..791f0456 100644 --- a/lib/widgets/side_navigation_rail.dart +++ b/lib/widgets/side_navigation_rail.dart @@ -13,6 +13,7 @@ import '../models/plex_library.dart'; import '../navigation/navigation_tabs.dart'; import '../providers/hidden_libraries_provider.dart'; import '../providers/libraries_provider.dart'; +import '../providers/settings_provider.dart'; import '../utils/platform_detector.dart'; import '../providers/multi_server_provider.dart'; import '../services/fullscreen_state_manager.dart'; @@ -181,6 +182,11 @@ class SideNavigationRailState extends State { static const _kSettings = 'settings'; static const _kReconnect = 'reconnect'; static const _kFullscreen = 'fullscreen'; + static const _kHiddenLibraries = 'hiddenLibraries'; + static const _kServerHeaderPrefix = 'serverHeader_'; + + bool _hiddenLibrariesExpanded = false; + final Set _collapsedServerIds = {}; // Unified focus state tracker for all nav items (main + libraries) late final FocusMemoryTracker _focusTracker; @@ -253,8 +259,8 @@ class SideNavigationRailState extends State { _focusTracker.restoreFocus(fallbackKey: _kHome); } - /// Build the set of valid focus keys (main nav + current libraries) - Set _buildValidFocusKeys(List libraries) { + /// Build the set of valid focus keys (main nav + current libraries + server headers) + Set _buildValidFocusKeys(List libraries, Set serverIds) { return { _kHome, _kLibraries, @@ -262,20 +268,56 @@ class SideNavigationRailState extends State { _kDownloads, _kSettings, _kReconnect, + _kHiddenLibraries, if (_showFullscreenToggle) _kFullscreen, 'liveTv', ...libraries.map((lib) => lib.globalKey), + ...serverIds.map((id) => _kServerHeaderPrefix + id), }; } + /// Build the visual order of library items inside the libraries body. Must + /// mirror what `_buildLibraryGroupedColumn` actually renders so D-pad + /// navigation lands on the visually next item. + List _buildLibraryBodyOrder(List libs, {required bool showServerHeaders}) { + if (!showServerHeaders) { + return libs.map((lib) => lib.globalKey).toList(); + } + final grouped = _groupByFirstAppearance(libs); + final result = []; + for (final serverKey in grouped.serverOrder) { + if (serverKey.isNotEmpty) { + result.add(_kServerHeaderPrefix + serverKey); + } + if (serverKey.isEmpty || !_collapsedServerIds.contains(serverKey)) { + for (final lib in grouped.byServer[serverKey]!) { + result.add(lib.globalKey); + } + } + } + return result; + } + /// Ordered list of focusable keys matching visual top-to-bottom order. - List _buildFocusOrder(List visibleLibraries, {required bool hasLiveTv}) { + List _buildFocusOrder( + List visibleLibraries, + List hiddenLibraries, { + required bool hasLiveTv, + required bool showServerHeaders, + }) { return [ if (widget.isOfflineMode && widget.onReconnect != null) _kReconnect, if (!widget.isOfflineMode) ...[ _kHome, _kLibraries, - if (_librariesExpanded) ...visibleLibraries.map((lib) => lib.globalKey), + if (_librariesExpanded) ...[ + ..._buildLibraryBodyOrder(visibleLibraries, showServerHeaders: showServerHeaders), + if (hiddenLibraries.isNotEmpty) ...[ + _kHiddenLibraries, + if (_hiddenLibrariesExpanded) + ..._buildLibraryBodyOrder(hiddenLibraries, showServerHeaders: showServerHeaders), + ], + ], if (hasLiveTv) 'liveTv', _kSearch, ], @@ -369,16 +411,35 @@ class SideNavigationRailState extends State { final hiddenLibrariesProvider = context.watch(); final hiddenKeys = hiddenLibrariesProvider.hiddenLibraryKeys; - // Get libraries from provider and filter visible ones final allLibraries = librariesProvider.libraries; - final visibleLibraries = allLibraries.where((lib) => !hiddenKeys.contains(lib.globalKey)).toList(); + final visibleLibraries = []; + final hiddenLibraries = []; + final serverIds = {}; + for (final lib in allLibraries) { + if (lib.serverId != null) serverIds.add(lib.serverId!); + if (hiddenKeys.contains(lib.globalKey)) { + hiddenLibraries.add(lib); + } else { + visibleLibraries.add(lib); + } + } - // Prune stale focus nodes when libraries change - _focusTracker.pruneExcept(_buildValidFocusKeys(allLibraries)); + _focusTracker.pruneExcept(_buildValidFocusKeys(allLibraries, serverIds)); + _collapsedServerIds.retainAll(serverIds); final isCollapsed = !_shouldExpand; final hasLiveTv = context.watch().hasLiveTv; - final focusOrder = _buildFocusOrder(visibleLibraries, hasLiveTv: hasLiveTv); + + // Server grouping: only when multi-server AND the user-facing toggle is on. + final groupByServerSetting = context.select((p) => p.groupLibrariesByServer); + final showServerHeaders = serverIds.length > 1 && groupByServerSetting; + + final focusOrder = _buildFocusOrder( + visibleLibraries, + hiddenLibraries, + hasLiveTv: hasLiveTv, + showServerHeaders: showServerHeaders, + ); // Listen to fullscreen changes for macOS return ListenableBuilder( @@ -442,7 +503,13 @@ class SideNavigationRailState extends State { const SizedBox(height: 8), // Libraries section - _buildLibrariesSection(visibleLibraries, t, isCollapsed: isCollapsed), + _buildLibrariesSection( + visibleLibraries, + hiddenLibraries, + t, + isCollapsed: isCollapsed, + showServerHeaders: showServerHeaders, + ), const SizedBox(height: 8), @@ -607,11 +674,18 @@ class SideNavigationRailState extends State { ); } - Widget _buildLibrariesSection(List visibleLibraries, dynamic t, {bool isCollapsed = false}) { + Widget _buildLibrariesSection( + List visibleLibraries, + List hiddenLibraries, + dynamic t, { + bool isCollapsed = false, + bool showServerHeaders = false, + }) { final librariesProvider = context.watch(); final isLoading = librariesProvider.isLoading; final isLibrariesSelected = widget.selectedTab == NavigationTabId.libraries && widget.selectedLibraryKey == null; final isLibrariesFocused = _focusTracker.isFocused(_kLibraries); + final allEmpty = visibleLibraries.isEmpty && hiddenLibraries.isEmpty; return Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -735,7 +809,7 @@ class SideNavigationRailState extends State { ), ), ) - else if (visibleLibraries.isEmpty) + else if (allEmpty) Padding( padding: const EdgeInsets.all(16), child: Text( @@ -743,8 +817,15 @@ class SideNavigationRailState extends State { style: TextStyle(fontSize: 12, color: t.textMuted), ), ) - else - _buildLibraryItems(visibleLibraries, t), + else ...[ + if (visibleLibraries.isNotEmpty) + _buildLibraryGroupedColumn(visibleLibraries, t, showServerHeaders: showServerHeaders), + if (hiddenLibraries.isNotEmpty) ...[ + _buildHiddenLibrariesHeader(hiddenLibraries.length, t), + if (_hiddenLibrariesExpanded) + _buildLibraryGroupedColumn(hiddenLibraries, t, showServerHeaders: showServerHeaders), + ], + ], ], ), ), @@ -762,16 +843,161 @@ class SideNavigationRailState extends State { return nameCounts.entries.where((e) => e.value > 1).map((e) => e.key).toSet(); } - Widget _buildLibraryItems(List visibleLibraries, dynamic t) { - // Find which library names are not unique - final nonUniqueNames = _getNonUniqueLibraryNames(visibleLibraries); + /// First-appearance grouping: walk libraries once, recording each serverId's + /// first-seen position and bucketing libraries underneath. Returns the server + /// order plus a per-server library list. Libraries without a serverId end up + /// in a synthetic '' bucket appearing at their first occurrence. + ({List serverOrder, Map> byServer}) _groupByFirstAppearance( + List libs, + ) { + final order = []; + final byServer = >{}; + for (final lib in libs) { + final key = lib.serverId ?? ''; + if (!byServer.containsKey(key)) { + order.add(key); + byServer[key] = []; + } + byServer[key]!.add(lib); + } + return (serverOrder: order, byServer: byServer); + } - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: visibleLibraries.map((library) { - final showServerName = nonUniqueNames.contains(library.title) && library.serverName != null; - return _buildLibraryItem(library, t, showServerName: showServerName); - }).toList(), + Widget _buildLibraryGroupedColumn(List libraries, dynamic t, {required bool showServerHeaders}) { + if (!showServerHeaders) { + final nonUniqueNames = _getNonUniqueLibraryNames(libraries); + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: libraries.map((library) { + final showServerName = nonUniqueNames.contains(library.title) && library.serverName != null; + return _buildLibraryItem(library, t, showServerName: showServerName); + }).toList(), + ); + } + + final grouped = _groupByFirstAppearance(libraries); + final children = []; + for (final serverKey in grouped.serverOrder) { + final bucket = grouped.byServer[serverKey]!; + // serverKey is '' when serverId is null — skip the header in that case. + if (serverKey.isNotEmpty) { + final serverName = bucket.first.serverName ?? serverKey; + children.add(_buildServerHeader(serverKey, serverName, t)); + } + if (serverKey.isEmpty || !_collapsedServerIds.contains(serverKey)) { + for (final library in bucket) { + children.add(_buildLibraryItem(library, t, showServerName: false)); + } + } + } + return Column(crossAxisAlignment: CrossAxisAlignment.start, children: children); + } + + Widget _buildServerHeader(String serverId, String serverName, dynamic t) { + return _buildCollapsibleHeader( + focusKey: _kServerHeaderPrefix + serverId, + icon: Symbols.dns_rounded, + iconSize: 14, + label: serverName, + labelStyle: TextStyle(fontSize: 11, fontWeight: FontWeight.w600, letterSpacing: 0.4, color: t.textMuted), + verticalPadding: 6, + isExpanded: !_collapsedServerIds.contains(serverId), + onToggle: () => _toggleServerCollapse(serverId), + t: t, + ); + } + + void _toggleServerCollapse(String serverId) { + setState(() { + if (!_collapsedServerIds.add(serverId)) { + _collapsedServerIds.remove(serverId); + } + }); + } + + Widget _buildHiddenLibrariesHeader(int count, dynamic t) { + return _buildCollapsibleHeader( + focusKey: _kHiddenLibraries, + icon: Symbols.visibility_off_rounded, + iconSize: 16, + label: Translations.of(context).libraries.hiddenLibrariesCount(count: count), + labelStyle: TextStyle(fontSize: 12, fontWeight: FontWeight.w500, color: t.textMuted), + verticalPadding: 8, + isExpanded: _hiddenLibrariesExpanded, + onToggle: () => setState(() => _hiddenLibrariesExpanded = !_hiddenLibrariesExpanded), + t: t, + ); + } + + Widget _buildCollapsibleHeader({ + required String focusKey, + required IconData icon, + required double iconSize, + required String label, + required TextStyle labelStyle, + required double verticalPadding, + required bool isExpanded, + required VoidCallback onToggle, + required dynamic t, + }) { + final isFocused = _focusTracker.isFocused(focusKey); + final radius = BorderRadius.circular(tokens(context).radiusSm); + // Match library-item indent: outer Padding(left: 12) + inner horizontal 17. + return Padding( + padding: const EdgeInsets.only(left: 12), + child: Focus( + focusNode: _focusTracker.get(focusKey), + onKeyEvent: (node, event) { + if (event is! KeyDownEvent) return KeyEventResult.ignored; + if (event.logicalKey.isSelectKey) { + onToggle(); + return KeyEventResult.handled; + } + if (event.logicalKey == LogicalKeyboardKey.arrowRight && widget.onNavigateToContent != null) { + widget.onNavigateToContent!(); + return KeyEventResult.handled; + } + return KeyEventResult.ignored; + }, + child: Material( + color: Colors.transparent, + child: InkWell( + canRequestFocus: false, + onTap: onToggle, + borderRadius: radius, + child: Container( + decoration: BoxDecoration(color: isFocused ? t.text.withValues(alpha: 0.08) : null, borderRadius: radius), + clipBehavior: Clip.hardEdge, + child: UnconstrainedBox( + alignment: Alignment.centerLeft, + constrainedAxis: Axis.vertical, + clipBehavior: Clip.hardEdge, + child: SizedBox( + width: expandedWidth - 24, + child: Padding( + padding: EdgeInsets.symmetric(vertical: verticalPadding, horizontal: 17), + child: Row( + children: [ + AppIcon(icon, fill: 1, size: iconSize, color: t.textMuted), + const SizedBox(width: 11), + Expanded( + child: Text(label, style: labelStyle, overflow: TextOverflow.ellipsis), + ), + AppIcon( + isExpanded ? Symbols.expand_less_rounded : Symbols.expand_more_rounded, + fill: 1, + size: 16, + color: t.textMuted, + ), + ], + ), + ), + ), + ), + ), + ), + ), + ), ); }