From 5cb4f79476d55c2181fa4962aef8ab6955ad39ff Mon Sep 17 00:00:00 2001 From: edde746 <86283021+edde746@users.noreply.github.com> Date: Wed, 14 Jan 2026 14:17:56 +0100 Subject: [PATCH] feat: add server name display on hub titles --- lib/i18n/strings.g.dart | 32 +++++++++++++++++++-- lib/i18n/strings.i18n.json | 2 ++ lib/i18n/strings_de.i18n.json | 2 ++ lib/i18n/strings_it.i18n.json | 2 ++ lib/i18n/strings_ko.i18n.json | 2 ++ lib/i18n/strings_nl.i18n.json | 2 ++ lib/i18n/strings_sv.i18n.json | 2 ++ lib/i18n/strings_zh.i18n.json | 2 ++ lib/providers/settings_provider.dart | 13 +++++++++ lib/screens/discover_screen.dart | 17 ++++++++++- lib/screens/settings/settings_screen.dart | 13 +++++++++ lib/services/settings_service.dart | 10 +++++++ lib/widgets/hub_section.dart | 35 +++++++++++++++++------ 13 files changed, 122 insertions(+), 12 deletions(-) diff --git a/lib/i18n/strings.g.dart b/lib/i18n/strings.g.dart index ce6d017b..c06441de 100644 --- a/lib/i18n/strings.g.dart +++ b/lib/i18n/strings.g.dart @@ -4,9 +4,9 @@ /// To regenerate, run: `dart run slang` /// /// Locales: 7 -/// Strings: 3617 (516 per locale) +/// Strings: 3631 (518 per locale) /// -/// Built on 2026-01-09 at 12:57 UTC +/// Built on 2026-01-14 at 12:24 UTC // coverage:ignore-file // ignore_for_file: type=lint @@ -320,6 +320,8 @@ class _StringsSettingsEn { String get showHeroSection => 'Show Hero Section'; String get useGlobalHubs => 'Use Plex Home Layout'; String get useGlobalHubsDescription => 'Show home page hubs like the official Plex client. When off, shows per-library recommendations instead.'; + String get showServerNameOnHubs => 'Show Server Name on Hubs'; + String get showServerNameOnHubsDescription => 'Always display the server name in hub titles. When off, only shows for duplicate hub names.'; String get hardwareDecoding => 'Hardware Decoding'; String get hardwareDecodingDescription => 'Use hardware acceleration when available'; String get bufferSize => 'Buffer Size'; @@ -1202,6 +1204,8 @@ class _StringsSettingsDe implements _StringsSettingsEn { @override String get showHeroSection => 'Hero-Bereich anzeigen'; @override String get useGlobalHubs => 'Plex-Startseiten-Layout verwenden'; @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 hardwareDecoding => 'Hardware-Decodierung'; @override String get hardwareDecodingDescription => 'Hardwarebeschleunigung verwenden, sofern verfügbar'; @override String get bufferSize => 'Puffergröße'; @@ -2085,6 +2089,8 @@ class _StringsSettingsIt implements _StringsSettingsEn { @override String get showHeroSection => 'Mostra sezione principale'; @override String get useGlobalHubs => 'Usa layout Home di Plex'; @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 hardwareDecoding => 'Decodifica Hardware'; @override String get hardwareDecodingDescription => 'Utilizza l\'accelerazione hardware quando disponibile'; @override String get bufferSize => 'Dimensione buffer'; @@ -2968,6 +2974,8 @@ class _StringsSettingsKo implements _StringsSettingsEn { @override String get showHeroSection => '주요 추천 영역 표시'; @override String get useGlobalHubs => 'Plex 홈 레이아웃 사용'; @override String get useGlobalHubsDescription => '공식 Plex 클라이언트처럼 홈 페이지 허브를 표시합니다. 끄면 라이브러리별 추천이 대신 표시됩니다.'; + @override String get showServerNameOnHubs => '허브에 서버 이름 표시'; + @override String get showServerNameOnHubsDescription => '허브 제목에 항상 서버 이름을 표시합니다. 끄면 중복된 허브 이름에만 표시됩니다.'; @override String get hardwareDecoding => '하드웨어 디코딩'; @override String get hardwareDecodingDescription => '가능한 경우 하드웨어 가속을 사용합니다'; @override String get bufferSize => '버퍼 크기'; @@ -3851,6 +3859,8 @@ class _StringsSettingsNl implements _StringsSettingsEn { @override String get showHeroSection => 'Toon hoofdsectie'; @override String get useGlobalHubs => 'Plex Home-indeling gebruiken'; @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 hardwareDecoding => 'Hardware decodering'; @override String get hardwareDecodingDescription => 'Gebruik hardware versnelling indien beschikbaar'; @override String get bufferSize => 'Buffer grootte'; @@ -4734,6 +4744,8 @@ class _StringsSettingsSv implements _StringsSettingsEn { @override String get showHeroSection => 'Visa hjältesektion'; @override String get useGlobalHubs => 'Använd Plex hem-layout'; @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 hardwareDecoding => 'Hårdvaruavkodning'; @override String get hardwareDecodingDescription => 'Använd hårdvaruacceleration när tillgängligt'; @override String get bufferSize => 'Bufferstorlek'; @@ -5617,6 +5629,8 @@ class _StringsSettingsZh implements _StringsSettingsEn { @override String get showHeroSection => '显示主要精选区'; @override String get useGlobalHubs => '使用 Plex 主页布局'; @override String get useGlobalHubsDescription => '显示与官方 Plex 客户端相同的主页推荐。关闭时将显示按媒体库分类的推荐。'; + @override String get showServerNameOnHubs => '在推荐栏显示服务器名称'; + @override String get showServerNameOnHubsDescription => '始终在推荐栏标题中显示服务器名称。关闭时仅在推荐栏名称重复时显示。'; @override String get hardwareDecoding => '硬件解码'; @override String get hardwareDecodingDescription => '如果可用,使用硬件加速'; @override String get bufferSize => '缓冲区大小'; @@ -6395,6 +6409,8 @@ extension on Translations { case 'settings.showHeroSection': return 'Show Hero Section'; case 'settings.useGlobalHubs': return 'Use Plex Home Layout'; case 'settings.useGlobalHubsDescription': return 'Show home page hubs like the official Plex client. When off, shows per-library recommendations instead.'; + case 'settings.showServerNameOnHubs': return 'Show Server Name on Hubs'; + case 'settings.showServerNameOnHubsDescription': return 'Always display the server name in hub titles. When off, only shows for duplicate hub names.'; case 'settings.hardwareDecoding': return 'Hardware Decoding'; case 'settings.hardwareDecodingDescription': return 'Use hardware acceleration when available'; case 'settings.bufferSize': return 'Buffer Size'; @@ -6919,6 +6935,8 @@ extension on _StringsDe { case 'settings.showHeroSection': return 'Hero-Bereich anzeigen'; case 'settings.useGlobalHubs': return 'Plex-Startseiten-Layout verwenden'; case 'settings.useGlobalHubsDescription': return 'Zeigt Startseiten-Hubs wie der offizielle Plex-Client. Wenn deaktiviert, werden stattdessen Empfehlungen pro Bibliothek angezeigt.'; + case 'settings.showServerNameOnHubs': return 'Servername bei Hubs anzeigen'; + case 'settings.showServerNameOnHubsDescription': return 'Zeigt immer den Servernamen in Hub-Titeln an. Wenn deaktiviert, nur bei doppelten Hub-Namen.'; case 'settings.hardwareDecoding': return 'Hardware-Decodierung'; case 'settings.hardwareDecodingDescription': return 'Hardwarebeschleunigung verwenden, sofern verfügbar'; case 'settings.bufferSize': return 'Puffergröße'; @@ -7444,6 +7462,8 @@ extension on _StringsIt { case 'settings.showHeroSection': return 'Mostra sezione principale'; case 'settings.useGlobalHubs': return 'Usa layout Home di Plex'; case 'settings.useGlobalHubsDescription': return 'Mostra gli hub della home page come il client Plex ufficiale. Se disattivato, mostra invece i suggerimenti per libreria.'; + case 'settings.showServerNameOnHubs': return 'Mostra nome server sugli hub'; + case 'settings.showServerNameOnHubsDescription': return 'Mostra sempre il nome del server nei titoli degli hub. Se disattivato, solo per nomi hub duplicati.'; case 'settings.hardwareDecoding': return 'Decodifica Hardware'; case 'settings.hardwareDecodingDescription': return 'Utilizza l\'accelerazione hardware quando disponibile'; case 'settings.bufferSize': return 'Dimensione buffer'; @@ -7969,6 +7989,8 @@ extension on _StringsKo { case 'settings.showHeroSection': return '주요 추천 영역 표시'; case 'settings.useGlobalHubs': return 'Plex 홈 레이아웃 사용'; case 'settings.useGlobalHubsDescription': return '공식 Plex 클라이언트처럼 홈 페이지 허브를 표시합니다. 끄면 라이브러리별 추천이 대신 표시됩니다.'; + case 'settings.showServerNameOnHubs': return '허브에 서버 이름 표시'; + case 'settings.showServerNameOnHubsDescription': return '허브 제목에 항상 서버 이름을 표시합니다. 끄면 중복된 허브 이름에만 표시됩니다.'; case 'settings.hardwareDecoding': return '하드웨어 디코딩'; case 'settings.hardwareDecodingDescription': return '가능한 경우 하드웨어 가속을 사용합니다'; case 'settings.bufferSize': return '버퍼 크기'; @@ -8494,6 +8516,8 @@ extension on _StringsNl { case 'settings.showHeroSection': return 'Toon hoofdsectie'; case 'settings.useGlobalHubs': return 'Plex Home-indeling gebruiken'; case 'settings.useGlobalHubsDescription': return 'Toon startpagina-hubs zoals de officiële Plex-client. Indien uitgeschakeld, worden in plaats daarvan aanbevelingen per bibliotheek getoond.'; + case 'settings.showServerNameOnHubs': return 'Servernaam tonen bij hubs'; + case 'settings.showServerNameOnHubsDescription': return 'Toon altijd de servernaam in hub-titels. Indien uitgeschakeld, alleen bij dubbele hub-namen.'; case 'settings.hardwareDecoding': return 'Hardware decodering'; case 'settings.hardwareDecodingDescription': return 'Gebruik hardware versnelling indien beschikbaar'; case 'settings.bufferSize': return 'Buffer grootte'; @@ -9019,6 +9043,8 @@ extension on _StringsSv { case 'settings.showHeroSection': return 'Visa hjältesektion'; case 'settings.useGlobalHubs': return 'Använd Plex hem-layout'; case 'settings.useGlobalHubsDescription': return 'Visar startsidans hubbar som den officiella Plex-klienten. När av visas rekommendationer per bibliotek istället.'; + case 'settings.showServerNameOnHubs': return 'Visa servernamn på hubbar'; + case 'settings.showServerNameOnHubsDescription': return 'Visa alltid servernamnet i hubbtitlar. När av visas endast för duplicerade hubbnamn.'; case 'settings.hardwareDecoding': return 'Hårdvaruavkodning'; case 'settings.hardwareDecodingDescription': return 'Använd hårdvaruacceleration när tillgängligt'; case 'settings.bufferSize': return 'Bufferstorlek'; @@ -9544,6 +9570,8 @@ extension on _StringsZh { case 'settings.showHeroSection': return '显示主要精选区'; case 'settings.useGlobalHubs': return '使用 Plex 主页布局'; case 'settings.useGlobalHubsDescription': return '显示与官方 Plex 客户端相同的主页推荐。关闭时将显示按媒体库分类的推荐。'; + case 'settings.showServerNameOnHubs': return '在推荐栏显示服务器名称'; + case 'settings.showServerNameOnHubsDescription': return '始终在推荐栏标题中显示服务器名称。关闭时仅在推荐栏名称重复时显示。'; case 'settings.hardwareDecoding': return '硬件解码'; case 'settings.hardwareDecodingDescription': return '如果可用,使用硬件加速'; case 'settings.bufferSize': return '缓冲区大小'; diff --git a/lib/i18n/strings.i18n.json b/lib/i18n/strings.i18n.json index 2d2baa7d..fc776a72 100644 --- a/lib/i18n/strings.i18n.json +++ b/lib/i18n/strings.i18n.json @@ -91,6 +91,8 @@ "showHeroSection": "Show Hero Section", "useGlobalHubs": "Use Plex Home Layout", "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.", "hardwareDecoding": "Hardware Decoding", "hardwareDecodingDescription": "Use hardware acceleration when available", "bufferSize": "Buffer Size", diff --git a/lib/i18n/strings_de.i18n.json b/lib/i18n/strings_de.i18n.json index 9c08c119..a73057f8 100644 --- a/lib/i18n/strings_de.i18n.json +++ b/lib/i18n/strings_de.i18n.json @@ -92,6 +92,8 @@ "showHeroSection": "Hero-Bereich anzeigen", "useGlobalHubs": "Plex-Startseiten-Layout verwenden", "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.", "hardwareDecoding": "Hardware-Decodierung", "hardwareDecodingDescription": "Hardwarebeschleunigung verwenden, sofern verfügbar", "bufferSize": "Puffergröße", diff --git a/lib/i18n/strings_it.i18n.json b/lib/i18n/strings_it.i18n.json index 42a61d20..3c823c18 100644 --- a/lib/i18n/strings_it.i18n.json +++ b/lib/i18n/strings_it.i18n.json @@ -92,6 +92,8 @@ "showHeroSection": "Mostra sezione principale", "useGlobalHubs": "Usa layout Home di Plex", "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.", "hardwareDecoding": "Decodifica Hardware", "hardwareDecodingDescription": "Utilizza l'accelerazione hardware quando disponibile", "bufferSize": "Dimensione buffer", diff --git a/lib/i18n/strings_ko.i18n.json b/lib/i18n/strings_ko.i18n.json index 91eaaaa0..41cac9ee 100644 --- a/lib/i18n/strings_ko.i18n.json +++ b/lib/i18n/strings_ko.i18n.json @@ -92,6 +92,8 @@ "showHeroSection": "주요 추천 영역 표시", "useGlobalHubs": "Plex 홈 레이아웃 사용", "useGlobalHubsDescription": "공식 Plex 클라이언트처럼 홈 페이지 허브를 표시합니다. 끄면 라이브러리별 추천이 대신 표시됩니다.", + "showServerNameOnHubs": "허브에 서버 이름 표시", + "showServerNameOnHubsDescription": "허브 제목에 항상 서버 이름을 표시합니다. 끄면 중복된 허브 이름에만 표시됩니다.", "hardwareDecoding": "하드웨어 디코딩", "hardwareDecodingDescription": "가능한 경우 하드웨어 가속을 사용합니다", "bufferSize": "버퍼 크기", diff --git a/lib/i18n/strings_nl.i18n.json b/lib/i18n/strings_nl.i18n.json index 23eaeaf9..a813a419 100644 --- a/lib/i18n/strings_nl.i18n.json +++ b/lib/i18n/strings_nl.i18n.json @@ -92,6 +92,8 @@ "showHeroSection": "Toon hoofdsectie", "useGlobalHubs": "Plex Home-indeling gebruiken", "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.", "hardwareDecoding": "Hardware decodering", "hardwareDecodingDescription": "Gebruik hardware versnelling indien beschikbaar", "bufferSize": "Buffer grootte", diff --git a/lib/i18n/strings_sv.i18n.json b/lib/i18n/strings_sv.i18n.json index be1fc155..c652dd28 100644 --- a/lib/i18n/strings_sv.i18n.json +++ b/lib/i18n/strings_sv.i18n.json @@ -92,6 +92,8 @@ "showHeroSection": "Visa hjältesektion", "useGlobalHubs": "Använd Plex hem-layout", "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.", "hardwareDecoding": "Hårdvaruavkodning", "hardwareDecodingDescription": "Använd hårdvaruacceleration när tillgängligt", "bufferSize": "Bufferstorlek", diff --git a/lib/i18n/strings_zh.i18n.json b/lib/i18n/strings_zh.i18n.json index 599025da..aa01bc55 100644 --- a/lib/i18n/strings_zh.i18n.json +++ b/lib/i18n/strings_zh.i18n.json @@ -92,6 +92,8 @@ "showHeroSection": "显示主要精选区", "useGlobalHubs": "使用 Plex 主页布局", "useGlobalHubsDescription": "显示与官方 Plex 客户端相同的主页推荐。关闭时将显示按媒体库分类的推荐。", + "showServerNameOnHubs": "在推荐栏显示服务器名称", + "showServerNameOnHubsDescription": "始终在推荐栏标题中显示服务器名称。关闭时仅在推荐栏名称重复时显示。", "hardwareDecoding": "硬件解码", "hardwareDecodingDescription": "如果可用,使用硬件加速", "bufferSize": "缓冲区大小", diff --git a/lib/providers/settings_provider.dart b/lib/providers/settings_provider.dart index 685bd9ac..dc60a6be 100644 --- a/lib/providers/settings_provider.dart +++ b/lib/providers/settings_provider.dart @@ -9,6 +9,7 @@ class SettingsProvider extends ChangeNotifier { EpisodePosterMode _episodePosterMode = EpisodePosterMode.seriesPoster; bool _showHeroSection = true; bool _useGlobalHubs = true; + bool _showServerNameOnHubs = false; bool _isInitialized = false; Future? _initFuture; @@ -30,6 +31,7 @@ class SettingsProvider extends ChangeNotifier { _episodePosterMode = _settingsService!.getEpisodePosterMode(); _showHeroSection = _settingsService!.getShowHeroSection(); _useGlobalHubs = _settingsService!.getUseGlobalHubs(); + _showServerNameOnHubs = _settingsService!.getShowServerNameOnHubs(); _isInitialized = true; notifyListeners(); } @@ -47,6 +49,8 @@ class SettingsProvider extends ChangeNotifier { bool get useGlobalHubs => _useGlobalHubs; + bool get showServerNameOnHubs => _showServerNameOnHubs; + Future setLibraryDensity(LibraryDensity density) async { if (!_isInitialized) await _initializeSettings(); if (_libraryDensity != density) { @@ -92,6 +96,15 @@ class SettingsProvider extends ChangeNotifier { } } + Future setShowServerNameOnHubs(bool value) async { + if (!_isInitialized) await _initializeSettings(); + if (_showServerNameOnHubs != value) { + _showServerNameOnHubs = value; + await _settingsService!.setShowServerNameOnHubs(value); + notifyListeners(); + } + } + String get libraryDensityDisplayName { switch (_libraryDensity) { case LibraryDensity.compact: diff --git a/lib/screens/discover_screen.dart b/lib/screens/discover_screen.dart index a4bc711f..7ca2ed39 100644 --- a/lib/screens/discover_screen.dart +++ b/lib/screens/discover_screen.dart @@ -489,8 +489,9 @@ class _DiscoverScreenState extends State // Get hidden libraries for filtering final hiddenLibrariesProvider = Provider.of(context, listen: false); - // Get settings for hub mode preference + // Get settings for hub mode preference (ensure initialized before accessing) final settingsProvider = Provider.of(context, listen: false); + await settingsProvider.ensureInitialized(); // Start OnDeck and hubs fetch in parallel final onDeckFuture = multiServerProvider.aggregationService.getOnDeckFromAllServers(limit: 20); @@ -698,6 +699,15 @@ class _DiscoverScreenState extends State return Symbols.auto_awesome_rounded; } + /// Get the set of hub titles that appear more than once (duplicates) + Set _getDuplicateHubTitles() { + final titleCounts = {}; + for (final hub in _hubs) { + titleCounts[hub.title] = (titleCounts[hub.title] ?? 0) + 1; + } + return titleCounts.entries.where((e) => e.value > 1).map((e) => e.key).toSet(); + } + @override void updateItemInLists(String ratingKey, PlexMetadata updatedMetadata) { // Check and update in _onDeck list @@ -949,6 +959,10 @@ class _DiscoverScreenState extends State @override Widget build(BuildContext context) { + // Get settings for server name display + final showServerNameOnHubs = context.watch().showServerNameOnHubs; + final duplicateHubTitles = _getDuplicateHubTitles(); + return Scaffold( body: Stack( children: [ @@ -1008,6 +1022,7 @@ class _DiscoverScreenState extends State key: i < _hubKeys.length ? _hubKeys[i] : null, hub: _hubs[i], icon: _getHubIcon(_hubs[i].title), + showServerName: showServerNameOnHubs || duplicateHubTitles.contains(_hubs[i].title), onRefresh: updateItem, // Hub index is i + 1 if continue watching exists, otherwise i onVerticalNavigation: (isUp) => _handleVerticalNavigation(_onDeck.isNotEmpty ? i + 1 : i, isUp), diff --git a/lib/screens/settings/settings_screen.dart b/lib/screens/settings/settings_screen.dart index 5131d493..7fb5cbab 100644 --- a/lib/screens/settings/settings_screen.dart +++ b/lib/screens/settings/settings_screen.dart @@ -226,6 +226,19 @@ class _SettingsScreenState extends State { ); }, ), + Consumer( + builder: (context, settingsProvider, child) { + return SwitchListTile( + secondary: const AppIcon(Symbols.dns_rounded, fill: 1), + title: Text(t.settings.showServerNameOnHubs), + subtitle: Text(t.settings.showServerNameOnHubsDescription), + value: settingsProvider.showServerNameOnHubs, + onChanged: (value) async { + await settingsProvider.setShowServerNameOnHubs(value); + }, + ); + }, + ), ], ), ); diff --git a/lib/services/settings_service.dart b/lib/services/settings_service.dart index 201ea646..373197d7 100644 --- a/lib/services/settings_service.dart +++ b/lib/services/settings_service.dart @@ -34,6 +34,7 @@ class SettingsService extends BaseSharedPreferencesService { static const String _keyMediaVersionPreferences = 'media_version_preferences'; 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 _keySleepTimerDuration = 'sleep_timer_duration'; static const String _keyAudioSyncOffset = 'audio_sync_offset'; static const String _keySubtitleSyncOffset = 'subtitle_sync_offset'; @@ -194,6 +195,15 @@ class SettingsService extends BaseSharedPreferencesService { return prefs.getBool(_keyUseGlobalHubs) ?? true; // Default: true (use global hubs like official Plex) } + // Show Server Name on Hubs (false = only on duplicates, true = always) + Future setShowServerNameOnHubs(bool enabled) async { + await prefs.setBool(_keyShowServerNameOnHubs, enabled); + } + + bool getShowServerNameOnHubs() { + return prefs.getBool(_keyShowServerNameOnHubs) ?? false; // Default: false (only show on duplicates) + } + // Seek Time Small (in seconds) Future setSeekTimeSmall(int seconds) async { await prefs.setInt(_keySeekTimeSmall, seconds); diff --git a/lib/widgets/hub_section.dart b/lib/widgets/hub_section.dart index 16b0d200..59b16af9 100644 --- a/lib/widgets/hub_section.dart +++ b/lib/widgets/hub_section.dart @@ -30,6 +30,7 @@ class HubSection extends StatefulWidget { final void Function(String)? onRefresh; final VoidCallback? onRemoveFromContinueWatching; final bool isInContinueWatching; + final bool showServerName; /// Callback for vertical navigation (up/down). Return true if handled. final bool Function(bool isUp)? onVerticalNavigation; @@ -45,6 +46,7 @@ class HubSection extends StatefulWidget { this.onRefresh, this.onRemoveFromContinueWatching, this.isInContinueWatching = false, + this.showServerName = false, this.onVerticalNavigation, this.onBack, }); @@ -263,7 +265,7 @@ class HubSectionState extends State { children: [ AppIcon(widget.icon, fill: 1), const SizedBox(width: 8), - Expanded( + Flexible( child: Text( widget.hub.title, style: Theme.of(context).textTheme.titleLarge, @@ -271,6 +273,22 @@ class HubSectionState extends State { maxLines: 1, ), ), + if (widget.showServerName && widget.hub.serverName != null) ...[ + const SizedBox(width: 8), + Text( + '•', + style: Theme.of(context).textTheme.titleMedium?.copyWith( + color: Theme.of(context).colorScheme.primary.withValues(alpha: 0.7), + ), + ), + const SizedBox(width: 8), + Text( + widget.hub.serverName!, + style: Theme.of(context).textTheme.titleMedium?.copyWith( + color: Theme.of(context).colorScheme.primary.withValues(alpha: 0.7), + ), + ), + ], if (widget.hub.more) ...[ const SizedBox(width: 4), const AppIcon(Symbols.chevron_right_rounded, fill: 1, size: 20), @@ -303,10 +321,8 @@ class HubSectionState extends State { final episodePosterMode = context.watch().episodePosterMode; // Determine hub content type for layout decisions - final hasEpisodes = widget.hub.items.any((item) => - item.usesWideAspectRatio(episodePosterMode)); - final hasNonEpisodes = widget.hub.items.any((item) => - !item.usesWideAspectRatio(episodePosterMode)); + final hasEpisodes = widget.hub.items.any((item) => item.usesWideAspectRatio(episodePosterMode)); + final hasNonEpisodes = widget.hub.items.any((item) => !item.usesWideAspectRatio(episodePosterMode)); // Mixed hub = has both episodes AND non-episodes (like Continue Watching) final isMixedHub = hasEpisodes && hasNonEpisodes; @@ -315,16 +331,17 @@ class HubSectionState extends State { final isEpisodeOnlyHub = hasEpisodes && !hasNonEpisodes; // Use 16:9 for episode-only hubs OR mixed hubs (with episode thumbnail mode) - final useWideLayout = episodePosterMode == EpisodePosterMode.episodeThumbnail && - (isEpisodeOnlyHub || isMixedHub); + final useWideLayout = + episodePosterMode == EpisodePosterMode.episodeThumbnail && (isEpisodeOnlyHub || isMixedHub); // Card dimensions based on hub type const wideCardMultiplier = 1.5; final cardWidth = useWideLayout ? baseCardWidth * wideCardMultiplier : baseCardWidth; final posterWidth = cardWidth - 16; // 8px padding on each side final posterHeight = useWideLayout - ? posterWidth * (9 / 16) // 16:9 for wide layout - : posterWidth * 1.5; // 2:3 for poster layout + ? posterWidth * + (9 / 16) // 16:9 for wide layout + : posterWidth * 1.5; // 2:3 for poster layout final containerHeight = posterHeight + 66; _itemExtent = cardWidth + 4;