diff --git a/lib/focus/dpad_navigator.dart b/lib/focus/dpad_navigator.dart index 4760c46d..0148e672 100644 --- a/lib/focus/dpad_navigator.dart +++ b/lib/focus/dpad_navigator.dart @@ -137,9 +137,7 @@ class BackKeyPressTracker { /// fallback in case our tracking drifted out of sync. static bool get isBackKeyDown { if (_isBackKeyDown) return true; - return HardwareKeyboard.instance.logicalKeysPressed.any( - (key) => key.isBackKey, - ); + return HardwareKeyboard.instance.logicalKeysPressed.any((key) => key.isBackKey); } static bool handleKeyEvent(KeyEvent event) { diff --git a/lib/screens/video_player_screen.dart b/lib/screens/video_player_screen.dart index 68e70675..1ccd18ce 100644 --- a/lib/screens/video_player_screen.dart +++ b/lib/screens/video_player_screen.dart @@ -141,8 +141,9 @@ class VideoPlayerScreenState extends State with WidgetsBindin final partId = _currentMediaInfo?.partId; if (partId == null || widget.isOffline) return null; final client = _getClientForMetadata(context); - return '${client.config.baseUrl}/library/parts/$partId/indexes/sd/${time.inMilliseconds}' - .withPlexToken(client.config.token); + return '${client.config.baseUrl}/library/parts/$partId/indexes/sd/${time.inMilliseconds}'.withPlexToken( + client.config.token, + ); } final ValueNotifier _isBuffering = ValueNotifier(false); // Track if video is currently buffering diff --git a/lib/services/download_manager_service.dart b/lib/services/download_manager_service.dart index fa30918c..45b49b85 100644 --- a/lib/services/download_manager_service.dart +++ b/lib/services/download_manager_service.dart @@ -213,8 +213,7 @@ class DownloadManagerService { if (inner is SocketException) return true; if (inner is HttpException) return true; final msg = inner?.toString() ?? ''; - if (msg.contains('Connection closed') || - msg.contains('Connection reset')) { + if (msg.contains('Connection closed') || msg.contains('Connection reset')) { return true; } return false; @@ -256,9 +255,7 @@ class DownloadManagerService { Future recoverInterruptedDownloads() async { try { final allDownloads = await _database.select(_database.downloadedMedia).get(); - final interrupted = allDownloads.where( - (item) => item.status == DownloadStatus.downloading.index, - ).toList(); + final interrupted = allDownloads.where((item) => item.status == DownloadStatus.downloading.index).toList(); if (interrupted.isEmpty) return; @@ -648,8 +645,7 @@ class DownloadManagerService { responseType: ResponseType.stream, headers: headers, // Prevent Dio from throwing on 206/416 - validateStatus: (status) => - status != null && (status >= 200 && status < 300 || status == 416), + validateStatus: (status) => status != null && (status >= 200 && status < 300 || status == 416), ), cancelToken: cancelToken, ); @@ -687,9 +683,7 @@ class DownloadManagerService { totalBytes = match != null ? int.parse(match.group(1)!) : -1; } else { final contentLength = response.headers.value('content-length'); - totalBytes = contentLength != null - ? int.parse(contentLength) + offset - : -1; + totalBytes = contentLength != null ? int.parse(contentLength) + offset : -1; } appLogger.i('Resuming download at $offset/$totalBytes for $globalKey'); } else { @@ -719,15 +713,11 @@ class DownloadManagerService { final now = DateTime.now(); if (now.difference(lastUpdate).inMilliseconds >= 500) { final elapsed = now.difference(lastUpdate).inMilliseconds / 1000.0; - final bytesPerSecond = elapsed > 0 - ? (receivedBytes - lastReportedBytes) / elapsed - : 0.0; + final bytesPerSecond = elapsed > 0 ? (receivedBytes - lastReportedBytes) / elapsed : 0.0; lastUpdate = now; lastReportedBytes = receivedBytes; - final progress = totalBytes > 0 - ? ((receivedBytes / totalBytes) * 100).round() - : 0; + final progress = totalBytes > 0 ? ((receivedBytes / totalBytes) * 100).round() : 0; _progressController.add( DownloadProgress( @@ -741,9 +731,7 @@ class DownloadManagerService { ), ); - _database - .updateDownloadProgress(globalKey, progress, receivedBytes, totalBytes) - .catchError((e) { + _database.updateDownloadProgress(globalKey, progress, receivedBytes, totalBytes).catchError((e) { appLogger.w('Failed to update download progress in DB', error: e); }); } @@ -757,9 +745,7 @@ class DownloadManagerService { if (totalBytes > 0) { final actualSize = await partFile.length(); if (actualSize != totalBytes) { - throw Exception( - 'Download size mismatch: expected $totalBytes bytes but got $actualSize', - ); + throw Exception('Download size mismatch: expected $totalBytes bytes but got $actualSize'); } } @@ -775,12 +761,8 @@ class DownloadManagerService { // Ignore flush errors during error handling } // Persist current progress to DB for resume - final progress = totalBytes > 0 - ? ((receivedBytes / totalBytes) * 100).round() - : 0; - await _database - .updateDownloadProgress(globalKey, progress, receivedBytes, totalBytes) - .catchError((dbErr) { + final progress = totalBytes > 0 ? ((receivedBytes / totalBytes) * 100).round() : 0; + await _database.updateDownloadProgress(globalKey, progress, receivedBytes, totalBytes).catchError((dbErr) { appLogger.w('Failed to persist progress on error', error: dbErr); }); rethrow; diff --git a/lib/services/plex_auth_service.dart b/lib/services/plex_auth_service.dart index b4695e16..95dce5b2 100644 --- a/lib/services/plex_auth_service.dart +++ b/lib/services/plex_auth_service.dart @@ -13,9 +13,7 @@ import '../utils/connection_constants.dart'; /// E.g. `192.168.1.50` → `192.***.***.50`, `my.server.example.com` → `my.***.***. com`. String _redactHost(String host) { // Strip brackets from IPv6 - final bare = host.startsWith('[') && host.endsWith(']') - ? host.substring(1, host.length - 1) - : host; + final bare = host.startsWith('[') && host.endsWith(']') ? host.substring(1, host.length - 1) : host; // IPv6 if (bare.contains(':')) { @@ -413,17 +411,17 @@ class PlexServer { ); for (final conn in connections) { - final redactedUri = conn.uri.replaceAll( - RegExp(r'//[^:/]+'), - '//${_redactHost(conn.address)}', + final redactedUri = conn.uri.replaceAll(RegExp(r'//[^:/]+'), '//${_redactHost(conn.address)}'); + appLogger.d( + 'Raw API connection', + error: { + 'uri': redactedUri, + 'address': _redactHost(conn.address), + 'local': conn.local, + 'relay': conn.relay, + 'protocol': conn.protocol, + }, ); - appLogger.d('Raw API connection', error: { - 'uri': redactedUri, - 'address': _redactHost(conn.address), - 'local': conn.local, - 'relay': conn.relay, - 'protocol': conn.protocol, - }); } _ConnectionCandidate? firstCandidate; @@ -460,13 +458,16 @@ class PlexServer { completedTests++; if (!result.success) { - appLogger.w('Connection candidate failed', error: { - 'url': candidate.url, - 'type': candidate.connection.displayType, - 'https': candidate.isHttps, - 'error': result.error, - 'latencyMs': result.latencyMs, - }); + appLogger.w( + 'Connection candidate failed', + error: { + 'url': candidate.url, + 'type': candidate.connection.displayType, + 'https': candidate.isHttps, + 'error': result.error, + 'latencyMs': result.latencyMs, + }, + ); } if (result.success && !completer.isCompleted) { @@ -481,11 +482,14 @@ class PlexServer { firstCandidate = await completer.future; if (firstCandidate == null) { - appLogger.e('No working server connections after race', error: { - 'server': name, - 'candidateCount': totalCandidates, - 'types': candidates.map((c) => c.connection.displayType).toSet().toList(), - }); + appLogger.e( + 'No working server connections after race', + error: { + 'server': name, + 'candidateCount': totalCandidates, + 'types': candidates.map((c) => c.connection.displayType).toSet().toList(), + }, + ); return; // No working connections found } appLogger.i( diff --git a/lib/services/plex_client.dart b/lib/services/plex_client.dart index 9e7d06d4..3bde818b 100644 --- a/lib/services/plex_client.dart +++ b/lib/services/plex_client.dart @@ -174,10 +174,10 @@ class PlexClient { error = e.type == DioExceptionType.connectionTimeout ? 'Connection timeout' : e.type == DioExceptionType.receiveTimeout - ? 'Receive timeout' - : e.type == DioExceptionType.connectionError - ? 'Connection error' - : e.type.name; + ? 'Receive timeout' + : e.type == DioExceptionType.connectionError + ? 'Connection error' + : e.type.name; if (e.response?.statusCode != null) { error += ' (HTTP ${e.response!.statusCode})'; } diff --git a/lib/widgets/tv_color_picker.dart b/lib/widgets/tv_color_picker.dart index 40c71a1e..39d0b6da 100644 --- a/lib/widgets/tv_color_picker.dart +++ b/lib/widgets/tv_color_picker.dart @@ -43,10 +43,7 @@ class _TvColorPickerState extends State { _saturation = (hsv.saturation * 100).round(); _value = (hsv.value * 100).round(); _hexController = TextEditingController(text: _currentHex()); - _hexFocusNode = FocusNode( - debugLabel: 'TvColorPicker_hex', - onKeyEvent: _handleHexKeyEvent, - ); + _hexFocusNode = FocusNode(debugLabel: 'TvColorPicker_hex', onKeyEvent: _handleHexKeyEvent); } @override diff --git a/pubspec.lock b/pubspec.lock index 7465774c..88007384 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -459,6 +459,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.33" + flutter_svg: + dependency: "direct main" + description: + name: flutter_svg + sha256: "87fbd7c534435b6c5d9d98b01e1fd527812b82e68ddd8bd35fc45ed0fa8f0a95" + url: "https://pub.dev" + source: hosted + version: "2.2.3" flutter_test: dependency: "direct dev" description: flutter @@ -742,6 +750,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.9.1" + path_parsing: + dependency: transitive + description: + name: path_parsing + sha256: "883402936929eac138ee0a45da5b0f2c80f89913e6dc3bf77eb65b84b409c6ca" + url: "https://pub.dev" + source: hosted + version: "1.1.0" path_provider: dependency: "direct main" description: @@ -1307,6 +1323,30 @@ packages: url: "https://pub.dev" source: hosted version: "4.5.2" + vector_graphics: + dependency: transitive + description: + name: vector_graphics + sha256: a4f059dc26fc8295b5921376600a194c4ec7d55e72f2fe4c7d2831e103d461e6 + url: "https://pub.dev" + source: hosted + version: "1.1.19" + vector_graphics_codec: + dependency: transitive + description: + name: vector_graphics_codec + sha256: "99fd9fbd34d9f9a32efd7b6a6aae14125d8237b10403b422a6a6dfeac2806146" + url: "https://pub.dev" + source: hosted + version: "1.1.13" + vector_graphics_compiler: + dependency: transitive + description: + name: vector_graphics_compiler + sha256: "201e876b5d52753626af64b6359cd13ac6011b80728731428fd34bc840f71c9b" + url: "https://pub.dev" + source: hosted + version: "1.1.20" vector_math: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 2ebc9559..205f29c2 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -47,6 +47,7 @@ dependencies: web_socket_channel: ^3.0.1 in_app_review: ^2.0.11 dart_discord_presence: ^1.1.0 + flutter_svg: ^2.2.3 dev_dependencies: @@ -68,6 +69,7 @@ flutter: - assets/go-noto-current-regular.ttf - assets/shaders/nvscaler/ - assets/shaders/anime4k/ + - assets/rating_icons/ flutter_launcher_icons: android: true