From 74cae73844fd6a68d85a3a3a36db356c70bbe3a3 Mon Sep 17 00:00:00 2001 From: edde746 <86283021+edde746@users.noreply.github.com> Date: Wed, 15 Jul 2026 08:25:42 +0200 Subject: [PATCH] fix(ci): restore Dart formatting gate --- lib/services/jellyfin_endpoint_discovery.dart | 270 +++++------------- lib/utils/media_server_http_client.dart | 104 ++----- lib/widgets/tv_spotlight_background.dart | 5 +- 3 files changed, 89 insertions(+), 290 deletions(-) diff --git a/lib/services/jellyfin_endpoint_discovery.dart b/lib/services/jellyfin_endpoint_discovery.dart index 04743df9..985f33d0 100644 --- a/lib/services/jellyfin_endpoint_discovery.dart +++ b/lib/services/jellyfin_endpoint_discovery.dart @@ -19,11 +19,7 @@ class JellyfinServerInfo { /// Server's reported version string. final String version; - const JellyfinServerInfo({ - required this.serverName, - required this.machineId, - required this.version, - }); + const JellyfinServerInfo({required this.serverName, required this.machineId, required this.version}); } class JellyfinEndpointRaceResult { @@ -31,11 +27,7 @@ class JellyfinEndpointRaceResult { final List baseUrls; final JellyfinServerInfo serverInfo; - const JellyfinEndpointRaceResult({ - required this.activeBaseUrl, - required this.baseUrls, - required this.serverInfo, - }); + const JellyfinEndpointRaceResult({required this.activeBaseUrl, required this.baseUrls, required this.serverInfo}); } class JellyfinEndpointProbeResult { @@ -82,30 +74,23 @@ class JellyfinEndpointDiscovery { MediaServerHttpClient _buildHttpClient({required String baseUrl}) { LogRedactionManager.registerServerUrl(baseUrl); - return MediaServerHttpClient( - baseUrl: baseUrl, - client: _testHttpClientFactory?.call(), - ); + return MediaServerHttpClient(baseUrl: baseUrl, client: _testHttpClientFactory?.call()); } /// Probe the server identified by [baseUrl] without authenticating. - Future probe( - String baseUrl, { - Duration timeout = MediaServerTimeouts.jellyfinProbe, - }) async { + Future probe(String baseUrl, {Duration timeout = MediaServerTimeouts.jellyfinProbe}) async { final result = await _probeServer(baseUrl, timeout: timeout); return result.serverInfo; } - Future<({JellyfinServerInfo serverInfo, String effectiveBaseUrl})> - _probeServer(String baseUrl, {required Duration timeout}) async { + Future<({JellyfinServerInfo serverInfo, String effectiveBaseUrl})> _probeServer( + String baseUrl, { + required Duration timeout, + }) async { final normalised = normalizeBaseUrl(baseUrl); final client = _buildHttpClient(baseUrl: normalised); try { - final response = await client.get( - '/System/Info/Public', - timeout: timeout, - ); + final response = await client.get('/System/Info/Public', timeout: timeout); throwIfHttpError(response); final effectiveBaseUrl = _resolveEffectiveBaseUrl(normalised, response); if (effectiveBaseUrl != normalised) { @@ -118,16 +103,10 @@ class JellyfinEndpointDiscovery { final id = data['Id']; final name = data['ServerName'] ?? data['LocalAddress']; if (id is! String || name is! String) { - throw MediaServerUrlException( - 'Server response missing Id/ServerName — not a Jellyfin server?', - ); + throw MediaServerUrlException('Server response missing Id/ServerName — not a Jellyfin server?'); } return ( - serverInfo: JellyfinServerInfo( - serverName: name, - machineId: id, - version: data['Version'] as String? ?? '', - ), + serverInfo: JellyfinServerInfo(serverName: name, machineId: id, version: data['Version'] as String? ?? ''), effectiveBaseUrl: effectiveBaseUrl, ); } on MediaServerUrlException { @@ -156,57 +135,28 @@ class JellyfinEndpointDiscovery { throw MediaServerUrlException('Enter at least one Jellyfin server URL'); } - final persistUrls = baseUrlsToPersist == null - ? urls - : normalizeBaseUrls(baseUrlsToPersist); - final validateUrls = baseUrlsToValidate == null - ? urls - : normalizeBaseUrls(baseUrlsToValidate); + final persistUrls = baseUrlsToPersist == null ? urls : normalizeBaseUrls(baseUrlsToPersist); + final validateUrls = baseUrlsToValidate == null ? urls : normalizeBaseUrls(baseUrlsToValidate); final validateUrlSet = validateUrls.toSet(); - final validationGroups = baseUrlValidationGroups == null - ? null - : _normalizeBaseUrlGroups(baseUrlValidationGroups); + final validationGroups = baseUrlValidationGroups == null ? null : _normalizeBaseUrlGroups(baseUrlValidationGroups); - final preferred = preferredUrl == null || preferredUrl.trim().isEmpty - ? null - : normalizeBaseUrl(preferredUrl); - final candidates = [ - for (var i = 0; i < urls.length; i++) - JellyfinEndpointCandidate(url: urls[i], index: i), - ]; + final preferred = preferredUrl == null || preferredUrl.trim().isEmpty ? null : normalizeBaseUrl(preferredUrl); + final candidates = [for (var i = 0; i < urls.length; i++) JellyfinEndpointCandidate(url: urls[i], index: i)]; - EndpointRaceSelection< - JellyfinEndpointCandidate, - JellyfinEndpointProbeResult - >? - firstSelection; - EndpointRaceSelection< - JellyfinEndpointCandidate, - JellyfinEndpointProbeResult - >? - bestSelection; + EndpointRaceSelection? firstSelection; + EndpointRaceSelection? bestSelection; - await for (final selection - in raceEndpointCandidates< - JellyfinEndpointCandidate, - JellyfinEndpointProbeResult - >( - label: 'Jellyfin server URL', - candidates: candidates, - preferredUrl: preferred, - urlOf: (candidate) => candidate.url, - failureLogFields: (candidate, result) => { - 'error': result.error, - 'latencyMs': result.latencyMs, - }, - probe: (candidate, timeout) => - _probeWithLatency(candidate.url, timeout: timeout), - measure: (candidate) => - _probeWithAverageLatency(candidate.url, attempts: 2), - isSuccess: (result) => result.success, - selectBestCandidate: (results) => - _selectLowestLatencyCandidate(results), - )) { + await for (final selection in raceEndpointCandidates( + label: 'Jellyfin server URL', + candidates: candidates, + preferredUrl: preferred, + urlOf: (candidate) => candidate.url, + failureLogFields: (candidate, result) => {'error': result.error, 'latencyMs': result.latencyMs}, + probe: (candidate, timeout) => _probeWithLatency(candidate.url, timeout: timeout), + measure: (candidate) => _probeWithAverageLatency(candidate.url, attempts: 2), + isSuccess: (result) => result.success, + selectBestCandidate: (results) => _selectLowestLatencyCandidate(results), + )) { if (selection.phase == EndpointRacePhase.first) { firstSelection = selection; } else { @@ -219,31 +169,19 @@ class JellyfinEndpointDiscovery { throw MediaServerUrlException('No reachable Jellyfin server found'); } - final Map - successfulResults = - bestSelection?.successfulResults ?? - firstSelection?.successfulResults ?? - const {}; + final Map successfulResults = + bestSelection?.successfulResults ?? firstSelection?.successfulResults ?? const {}; var selectedCandidate = selected.candidate; var selectedResult = selected.result; final expectedMachineIdTrimmed = expectedMachineId?.trim(); final hasExpectedMachineId = expectedMachineIdTrimmed?.isNotEmpty == true; if (hasExpectedMachineId) { - final matchingResults = - Map< - JellyfinEndpointCandidate, - JellyfinEndpointProbeResult - >.fromEntries( - successfulResults.entries.where( - (entry) => - entry.value.serverInfo?.machineId == expectedMachineIdTrimmed, - ), - ); + final matchingResults = Map.fromEntries( + successfulResults.entries.where((entry) => entry.value.serverInfo?.machineId == expectedMachineIdTrimmed), + ); final matchingCandidate = _selectLowestLatencyCandidate(matchingResults); - final matchingResult = matchingCandidate == null - ? null - : matchingResults[matchingCandidate]; + final matchingResult = matchingCandidate == null ? null : matchingResults[matchingCandidate]; if (matchingCandidate != null && matchingResult != null) { selectedCandidate = matchingCandidate; selectedResult = matchingResult; @@ -255,33 +193,18 @@ class JellyfinEndpointDiscovery { throw MediaServerUrlException('No reachable Jellyfin server found'); } - final expected = hasExpectedMachineId - ? expectedMachineIdTrimmed! - : selectedInfo.machineId; + final expected = hasExpectedMachineId ? expectedMachineIdTrimmed! : selectedInfo.machineId; if (validationGroups != null) { if (validationGroups.length > 1) { for (final group in validationGroups) { final groupSet = group.toSet(); - final groupResults = - Map< - JellyfinEndpointCandidate, - JellyfinEndpointProbeResult - >.fromEntries( - successfulResults.entries.where( - (entry) => groupSet.contains(entry.key.url), - ), - ); - final candidate = _selectValidationCandidate( - groupResults, - expectedMachineId: expectedMachineIdTrimmed, + final groupResults = Map.fromEntries( + successfulResults.entries.where((entry) => groupSet.contains(entry.key.url)), ); - final info = candidate == null - ? null - : groupResults[candidate]?.serverInfo; + final candidate = _selectValidationCandidate(groupResults, expectedMachineId: expectedMachineIdTrimmed); + final info = candidate == null ? null : groupResults[candidate]?.serverInfo; if (info != null && info.machineId != expected) { - throw MediaServerUrlException( - 'The URLs point to different Jellyfin servers', - ); + throw MediaServerUrlException('The URLs point to different Jellyfin servers'); } } } @@ -290,17 +213,13 @@ class JellyfinEndpointDiscovery { if (!validateUrlSet.contains(entry.key.url)) continue; final info = entry.value.serverInfo; if (info != null && info.machineId != expected) { - throw MediaServerUrlException( - 'The URLs point to different Jellyfin servers', - ); + throw MediaServerUrlException('The URLs point to different Jellyfin servers'); } } } if (selectedInfo.machineId != expected) { - throw MediaServerUrlException( - 'The URL does not match this Jellyfin server', - ); + throw MediaServerUrlException('The URL does not match this Jellyfin server'); } final effectiveUrls = {}; @@ -310,12 +229,9 @@ class JellyfinEndpointDiscovery { effectiveUrls[entry.key.url] = effectiveBaseUrl; } } - final activeBaseUrl = - selectedResult.effectiveBaseUrl ?? selectedCandidate.url; + final activeBaseUrl = selectedResult.effectiveBaseUrl ?? selectedCandidate.url; effectiveUrls[selectedCandidate.url] = activeBaseUrl; - final persistedUrls = [ - for (final url in persistUrls) effectiveUrls[url] ?? url, - ]; + final persistedUrls = [for (final url in persistUrls) effectiveUrls[url] ?? url]; return JellyfinEndpointRaceResult( activeBaseUrl: activeBaseUrl, @@ -324,10 +240,7 @@ class JellyfinEndpointDiscovery { ); } - Future _probeWithLatency( - String baseUrl, { - required Duration timeout, - }) async { + Future _probeWithLatency(String baseUrl, {required Duration timeout}) async { final stopwatch = Stopwatch()..start(); try { final probe = await _probeServer(baseUrl, timeout: timeout); @@ -340,40 +253,24 @@ class JellyfinEndpointDiscovery { ); } catch (e) { stopwatch.stop(); - return JellyfinEndpointProbeResult( - success: false, - latencyMs: stopwatch.elapsedMilliseconds, - error: e.toString(), - ); + return JellyfinEndpointProbeResult(success: false, latencyMs: stopwatch.elapsedMilliseconds, error: e.toString()); } } - Future _probeWithAverageLatency( - String baseUrl, { - required int attempts, - }) async { + Future _probeWithAverageLatency(String baseUrl, {required int attempts}) async { final results = []; JellyfinServerInfo? info; String? effectiveBaseUrl; for (var i = 0; i < attempts; i++) { - final result = await _probeWithLatency( - baseUrl, - timeout: MediaServerTimeouts.connectionRace, - ); + final result = await _probeWithLatency(baseUrl, timeout: MediaServerTimeouts.connectionRace); if (!result.success) { - return JellyfinEndpointProbeResult( - success: false, - latencyMs: result.latencyMs, - error: result.error, - ); + return JellyfinEndpointProbeResult(success: false, latencyMs: result.latencyMs, error: result.error); } info = result.serverInfo; effectiveBaseUrl = result.effectiveBaseUrl; results.add(result); } - final avgLatency = - results.map((result) => result.latencyMs).reduce((a, b) => a + b) ~/ - results.length; + final avgLatency = results.map((result) => result.latencyMs).reduce((a, b) => a + b) ~/ results.length; return JellyfinEndpointProbeResult( success: true, latencyMs: avgLatency, @@ -400,30 +297,19 @@ class JellyfinEndpointDiscovery { required String? expectedMachineId, }) { if (expectedMachineId?.isNotEmpty == true) { - final matchingResults = - Map< - JellyfinEndpointCandidate, - JellyfinEndpointProbeResult - >.fromEntries( - results.entries.where( - (entry) => entry.value.serverInfo?.machineId == expectedMachineId, - ), - ); + final matchingResults = Map.fromEntries( + results.entries.where((entry) => entry.value.serverInfo?.machineId == expectedMachineId), + ); final match = _selectLowestLatencyCandidate(matchingResults); if (match != null) return match; } return _selectLowestLatencyCandidate(results); } - static String _resolveEffectiveBaseUrl( - String requestedBaseUrl, - MediaServerResponse response, - ) { + static String _resolveEffectiveBaseUrl(String requestedBaseUrl, MediaServerResponse response) { final requestedUri = response.requestUri; final effectiveUri = response.effectiveUri; - if (requestedUri == null || - effectiveUri == null || - effectiveUri == requestedUri) { + if (requestedUri == null || effectiveUri == null || effectiveUri == requestedUri) { return requestedBaseUrl; } @@ -434,34 +320,19 @@ class JellyfinEndpointDiscovery { (effectiveScheme != 'http' && effectiveScheme != 'https')) { throw MediaServerUrlException('Server redirected to an unsupported URL'); } - if (requestedBaseUri.host.toLowerCase() != - effectiveUri.host.toLowerCase()) { - throw MediaServerUrlException( - 'Server redirected to a different host. Enter the final Jellyfin URL directly', - ); + if (requestedBaseUri.host.toLowerCase() != effectiveUri.host.toLowerCase()) { + throw MediaServerUrlException('Server redirected to a different host. Enter the final Jellyfin URL directly'); } - if (requestedBaseUri.scheme.toLowerCase() == 'https' && - effectiveScheme != 'https') { - throw MediaServerUrlException( - 'Server redirected from HTTPS to an insecure URL', - ); + if (requestedBaseUri.scheme.toLowerCase() == 'https' && effectiveScheme != 'https') { + throw MediaServerUrlException('Server redirected from HTTPS to an insecure URL'); } const publicInfoPath = '/System/Info/Public'; if (!effectiveUri.path.endsWith(publicInfoPath)) { - throw MediaServerUrlException( - 'Server redirected to an unsupported URL. Enter the final Jellyfin URL directly', - ); + throw MediaServerUrlException('Server redirected to an unsupported URL. Enter the final Jellyfin URL directly'); } - final basePath = effectiveUri.path.substring( - 0, - effectiveUri.path.length - publicInfoPath.length, - ); - return normalizeBaseUrl( - effectiveUri - .replace(path: basePath, query: null, fragment: null) - .toString(), - ); + final basePath = effectiveUri.path.substring(0, effectiveUri.path.length - publicInfoPath.length); + return normalizeBaseUrl(effectiveUri.replace(path: basePath, query: null, fragment: null).toString()); } /// Normalizes a concrete Jellyfin base URL without inventing a scheme or port. @@ -480,9 +351,7 @@ class JellyfinEndpointDiscovery { final result = []; final seen = {}; void add(Uri uri) { - final normalized = stripTrailingSlash( - uri.replace(query: null, fragment: null).toString(), - ); + final normalized = stripTrailingSlash(uri.replace(query: null, fragment: null).toString()); if (normalized.isEmpty || !seen.add(normalized)) return; result.add(normalized); } @@ -499,9 +368,7 @@ class JellyfinEndpointDiscovery { return List.unmodifiable(result); } - static JellyfinEndpointUserInputCandidates buildUserInputCandidates( - Iterable input, - ) { + static JellyfinEndpointUserInputCandidates buildUserInputCandidates(Iterable input) { final probeBaseUrls = []; final explicitBaseUrls = []; final validationBaseUrlGroups = >[]; @@ -557,9 +424,7 @@ class JellyfinEndpointDiscovery { return List.unmodifiable(result); } - static List> _normalizeBaseUrlGroups( - Iterable> groups, - ) { + static List> _normalizeBaseUrlGroups(Iterable> groups) { final result = >[]; for (final group in groups) { final normalized = normalizeBaseUrls(group); @@ -568,8 +433,7 @@ class JellyfinEndpointDiscovery { return List.unmodifiable(result); } - static bool _hasScheme(String input) => - RegExp(r'^[a-zA-Z][a-zA-Z\d+.-]*://').hasMatch(input); + static bool _hasScheme(String input) => RegExp(r'^[a-zA-Z][a-zA-Z\d+.-]*://').hasMatch(input); static List _activeFirst(String activeBaseUrl, List urls) { final result = []; diff --git a/lib/utils/media_server_http_client.dart b/lib/utils/media_server_http_client.dart index c9d05318..8d4559bf 100644 --- a/lib/utils/media_server_http_client.dart +++ b/lib/utils/media_server_http_client.dart @@ -13,9 +13,7 @@ import 'managed_http_client.dart'; import '../exceptions/media_server_exceptions.dart'; // Platform-specific imports are conditional -import 'platform_http_client_stub.dart' - if (dart.library.io) 'platform_http_client_io.dart' - as platform; +import 'platform_http_client_stub.dart' if (dart.library.io) 'platform_http_client_io.dart' as platform; /// Response from [MediaServerHttpClient] requests. class MediaServerResponse { @@ -32,13 +30,8 @@ class MediaServerResponse { /// does not expose redirect metadata. final Uri? effectiveUri; - MediaServerResponse({ - required this.statusCode, - this.data, - required this.headers, - this.requestUri, - Uri? effectiveUri, - }) : effectiveUri = effectiveUri ?? requestUri; + MediaServerResponse({required this.statusCode, this.data, required this.headers, this.requestUri, Uri? effectiveUri}) + : effectiveUri = effectiveUri ?? requestUri; } /// Throw [MediaServerHttpException] for non-2xx responses so callers don't blindly @@ -88,11 +81,7 @@ class MediaServerHttpClient { // Plex home loads fan out many HTTP/1.1 calls on Linux. Keep that tuning // opt-in so generic tracker/auth clients stay disposable and closeable. bool usePlexApiClient = false, - }) : _client = - client ?? - (usePlexApiClient - ? platform.createPlexApiClient() - : platform.createPlatformClient()), + }) : _client = client ?? (usePlexApiClient ? platform.createPlexApiClient() : platform.createPlatformClient()), defaultHeaders = Map.of(defaultHeaders); /// The underlying [http.Client] for direct streaming / multipart requests. @@ -109,14 +98,7 @@ class MediaServerHttpClient { Map? headers, Duration? timeout, AbortController? abort, - }) => _send( - 'GET', - path, - queryParameters: queryParameters, - headers: headers, - timeout: timeout, - abort: abort, - ); + }) => _send('GET', path, queryParameters: queryParameters, headers: headers, timeout: timeout, abort: abort); Future post( String path, { @@ -158,14 +140,7 @@ class MediaServerHttpClient { Map? headers, Duration? timeout, AbortController? abort, - }) => _send( - 'DELETE', - path, - queryParameters: queryParameters, - headers: headers, - timeout: timeout, - abort: abort, - ); + }) => _send('DELETE', path, queryParameters: queryParameters, headers: headers, timeout: timeout, abort: abort); /// Fetch raw bytes (e.g. images, BIF files, subtitles). Future getBytes( @@ -175,20 +150,13 @@ class MediaServerHttpClient { AbortController? abort, }) async { if (_closing) { - throw MediaServerHttpException( - type: MediaServerHttpErrorType.cancelled, - message: 'HTTP client is closing', - ); + throw MediaServerHttpException(type: MediaServerHttpErrorType.cancelled, message: 'HTTP client is closing'); } final uri = _isAbsoluteUrl(url) ? Uri.parse(url) : _buildUri(url, null); final requestAbort = AbortController(); _activeAborts.add(requestAbort); - final request = http.AbortableRequest( - 'GET', - uri, - abortTrigger: _abortTrigger(requestAbort, abort), - ); + final request = http.AbortableRequest('GET', uri, abortTrigger: _abortTrigger(requestAbort, abort)); request.headers.addAll({...defaultHeaders, ...?headers}); final sw = Stopwatch()..start(); @@ -228,20 +196,13 @@ class MediaServerHttpClient { AbortController? abort, }) async { if (_closing) { - throw MediaServerHttpException( - type: MediaServerHttpErrorType.cancelled, - message: 'HTTP client is closing', - ); + throw MediaServerHttpException(type: MediaServerHttpErrorType.cancelled, message: 'HTTP client is closing'); } final uri = _isAbsoluteUrl(url) ? Uri.parse(url) : _buildUri(url, null); final requestAbort = AbortController(); _activeAborts.add(requestAbort); - final request = http.AbortableRequest( - 'GET', - uri, - abortTrigger: _abortTrigger(requestAbort, abort), - ); + final request = http.AbortableRequest('GET', uri, abortTrigger: _abortTrigger(requestAbort, abort)); request.headers.addAll({...defaultHeaders, ...?headers}); try { @@ -299,9 +260,7 @@ class MediaServerHttpClient { _client.close(); } - Future closeGracefully({ - Duration drainTimeout = const Duration(seconds: 2), - }) async { + Future closeGracefully({Duration drainTimeout = const Duration(seconds: 2)}) async { _closing = true; _abortActiveRequests(); if (_client case final ManagedHttpClient managed) { @@ -321,10 +280,7 @@ class MediaServerHttpClient { AbortController? abort, }) async { if (_closing) { - throw MediaServerHttpException( - type: MediaServerHttpErrorType.cancelled, - message: 'HTTP client is closing', - ); + throw MediaServerHttpException(type: MediaServerHttpErrorType.cancelled, message: 'HTTP client is closing'); } final uri = _isAbsoluteUrl(path) @@ -335,11 +291,7 @@ class MediaServerHttpClient { final requestAbort = AbortController(); _activeAborts.add(requestAbort); - final request = http.AbortableRequest( - method, - uri, - abortTrigger: _abortTrigger(requestAbort, abort), - ); + final request = http.AbortableRequest(method, uri, abortTrigger: _abortTrigger(requestAbort, abort)); request.headers.addAll(mergedHeaders); _setBody(request, body); @@ -403,9 +355,7 @@ class MediaServerHttpClient { Future _abortTrigger(AbortController owned, AbortController? external) { final externalTrigger = external?.trigger; - return externalTrigger == null - ? owned.trigger - : Future.any([owned.trigger, externalTrigger]); + return externalTrigger == null ? owned.trigger : Future.any([owned.trigger, externalTrigger]); } Future _withAbortOnTimeout( @@ -426,8 +376,7 @@ class MediaServerHttpClient { /// Use this from callers that need to construct URLs with the client's /// current (possibly failover-switched) base, rather than reading /// `config.baseUrl` directly. - Uri buildUri(String path, {Map? queryParameters}) => - _buildUri(path, queryParameters); + Uri buildUri(String path, {Map? queryParameters}) => _buildUri(path, queryParameters); /// Build a full URI from [baseUrl] + [path] + [queryParameters]. /// Uses [Uri.encodeComponent] which encodes spaces as `%20` (not `+`). @@ -478,8 +427,7 @@ class MediaServerHttpClient { return parts.join('&'); } - static bool _isAbsoluteUrl(String url) => - url.startsWith('http://') || url.startsWith('https://'); + static bool _isAbsoluteUrl(String url) => url.startsWith('http://') || url.startsWith('https://'); /// Set the request body, choosing encoding based on the body type. void _setBody(http.Request request, Object? body) { @@ -499,9 +447,7 @@ class MediaServerHttpClient { // http.BaseRequest's headers map is case-sensitive; Jellyfin returns 415 // if both `Content-Type` (from defaults) and `content-type` (added below) // end up coexisting, so check both casings before adding. - final hasContentType = request.headers.keys.any( - (k) => k.toLowerCase() == 'content-type', - ); + final hasContentType = request.headers.keys.any((k) => k.toLowerCase() == 'content-type'); if (!hasContentType) { request.headers['content-type'] = 'application/json'; } @@ -509,22 +455,16 @@ class MediaServerHttpClient { /// Decode the response body: lenient UTF-8, then JSON parse if applicable. /// Large payloads are decoded in a background isolate. - Future _decodeBody( - List bytes, - Map headers, - ) async { + Future _decodeBody(List bytes, Map headers) async { if (bytes.isEmpty) return null; - final contentType = (_headerValue(headers, 'content-type') ?? '') - .toLowerCase(); + final contentType = (_headerValue(headers, 'content-type') ?? '').toLowerCase(); final isJson = contentType.contains('json'); // For large JSON payloads, do both UTF-8 decode and JSON parse in a // single isolate roundtrip to avoid two context switches. if (isJson && bytes.length > 50 * 1024) { - return await tryIsolateRun( - () => jsonDecode(utf8.decode(bytes, allowMalformed: true)), - ); + return await tryIsolateRun(() => jsonDecode(utf8.decode(bytes, allowMalformed: true))); } final body = await _decodeTextBody(bytes); @@ -547,9 +487,7 @@ class MediaServerHttpClient { } void _logResponse(String method, Uri uri, int statusCode, int ms) { - appLogger.d( - '$method ${LogRedactionManager.redact(uri.toString())} → $statusCode (${ms}ms)', - ); + appLogger.d('$method ${LogRedactionManager.redact(uri.toString())} → $statusCode (${ms}ms)'); } } diff --git a/lib/widgets/tv_spotlight_background.dart b/lib/widgets/tv_spotlight_background.dart index f8221b81..5721edc5 100644 --- a/lib/widgets/tv_spotlight_background.dart +++ b/lib/widgets/tv_spotlight_background.dart @@ -68,10 +68,7 @@ class TvSpotlightBackground extends StatelessWidget { final containerAspect = size.width / size.height; final fallbackPaths = media == null ? const [] - : [ - ...media.heroArtCandidates(containerAspectRatio: containerAspect), - ?media.thumbPath, - ]; + : [...media.heroArtCandidates(containerAspectRatio: containerAspect), ?media.thumbPath]; return Stack( fit: StackFit.expand, children: [