From cc4eff98c052983e8327c084543cea63565b5903 Mon Sep 17 00:00:00 2001 From: edde746 <86283021+edde746@users.noreply.github.com> Date: Mon, 24 Aug 2026 12:32:05 +0200 Subject: [PATCH] build: migrate to Flutter 3.47.1 (#2092) --- .github/actions/setup-flutter-git/action.yml | 6 +-- .github/workflows/build.yml | 2 +- .github/workflows/ci.yml | 2 +- README.md | 2 +- analysis_options.yaml | 7 +++ ios/Podfile.lock | 2 +- lib/exceptions/media_server_exceptions.dart | 6 +-- .../cached_playback_metadata_service.dart | 5 ++- lib/services/local_playback_history.dart | 4 +- lib/services/multi_server_manager.dart | 5 ++- lib/services/playback_progress_tracker.dart | 5 ++- .../plex_client/parts/collections.dart | 2 +- .../plex_client/parts/play_queues.dart | 2 +- lib/utils/video_player_navigation.dart | 8 +++- macos/Podfile.lock | 2 +- packages/saf_util/analysis_options.yaml | 9 ++++ packages/saf_util/pubspec.lock | 16 +++---- .../shared_preferences_linux/pubspec.lock | 18 ++++---- .../shared_preferences_windows/pubspec.lock | 18 ++++---- packages/wakelock_plus/pubspec.lock | 18 ++++---- pubspec.lock | 22 +++++----- pubspec.yaml | 4 +- scripts/checks/check_build_workflow.py | 4 +- scripts/checks/test_check_build_workflow.py | 2 +- .../download_storage_service_test.dart | 42 ++++++++---------- .../jellyfin_client_emby_dialect_test.dart | 44 ++++++++----------- test/services/track_manager_test.dart | 16 +++---- tvos/engine.sha256 | 2 +- tvos/engine.version | 2 +- tvos/scripts/xcode_appletv.sh | 6 +++ windows/tool/install-patched-engine.ps1 | 8 ++-- windows/tool/swap-engine.ps1 | 2 +- 32 files changed, 159 insertions(+), 134 deletions(-) diff --git a/.github/actions/setup-flutter-git/action.yml b/.github/actions/setup-flutter-git/action.yml index 96ef0e87e..a905abf37 100644 --- a/.github/actions/setup-flutter-git/action.yml +++ b/.github/actions/setup-flutter-git/action.yml @@ -5,7 +5,7 @@ description: >- subosito/flutter-action cannot resolve the release for arm64 (no arm64 entry in the stable manifest) and `channel: master` would clone master HEAD, whose engine is not the patched revision install-patched-engine.ps1 asserts - (4c525dac). This file is the only place that pin lives: the tag is fetched so + (5d531788). This file is the only place that pin lives: the tag is fetched so the SDK reports its own version, then verified against the immutable commit and against the version Flutter itself reports, so a moved tag fails the job instead of quietly changing SDKs. @@ -16,8 +16,8 @@ runs: - name: Clone Flutter from its immutable commit shell: pwsh run: | - $version = "3.44.0" - $expectedCommit = "559ffa3f75e7402d65a8def9c28389a9b2e6fe42" + $version = "3.47.1" + $expectedCommit = "6655482ec06e547f90abf8ae7590466f4415978d" $root = "$env:RUNNER_TEMP\flutter" git init $root git -C $root remote add origin https://github.com/flutter/flutter.git diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0a7e33e2b..40a2cdd36 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -31,7 +31,7 @@ on: env: # Only place this workflow names the SDK; .github/actions/setup-flutter-git pins the same release. - FLUTTER_VERSION: "3.44.0" + FLUTTER_VERSION: "3.47.1" # Shared by every release build command; SENTRY_DIST stays per-platform. RELEASE_DART_DEFINES: --dart-define=ENABLE_UPDATE_CHECK=true ${{ github.repository == 'edde746/plezy' && '--dart-define=ENABLE_SENTRY=true' || '' }} --dart-define=GIT_COMMIT=${{ github.sha }} --dart-define=SENTRY_ENVIRONMENT=github --dart-define=ENABLE_DONATIONS=true TRUSTED_BUILD_CACHE_VERSION: trusted-build-v1 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ef8c9559a..80846af74 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,7 +20,7 @@ on: env: # Only place this workflow names the SDK; .github/actions/setup-flutter-git pins the same release. - FLUTTER_VERSION: "3.44.0" + FLUTTER_VERSION: "3.47.1" jobs: analyze: diff --git a/README.md b/README.md index 44dc7988e..c8944cb78 100644 --- a/README.md +++ b/README.md @@ -148,7 +148,7 @@ Package managers: ## Building from Source ### Prerequisites -- Flutter SDK 3.44.0+ +- Flutter SDK 3.47.0+ - A Plex account, or a Jellyfin or Emby server with user credentials ### Setup diff --git a/analysis_options.yaml b/analysis_options.yaml index c6b3ff7da..64f37a468 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -4,6 +4,13 @@ analyzer: exclude: - "**/*.g.dart" - "**/*.freezed.dart" + - build/** + - android/** + - ios/** + - web/** + - windows/** + - macos/** + - linux/** linter: rules: diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 9c07de938..66a67e660 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -19,7 +19,7 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/universal_gamepad/ios" SPEC CHECKSUMS: - Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467 + Flutter: 71a624a5bc0c04062bf19101d501e466baf2fb47 os_media_controls: 048eb9a75974191b2496d85575b6edcefc572d4e universal_gamepad: 7c0cc0c2e3909dfb803d325387e42963b3ed842c diff --git a/lib/exceptions/media_server_exceptions.dart b/lib/exceptions/media_server_exceptions.dart index c1f80964b..88d0b45fe 100644 --- a/lib/exceptions/media_server_exceptions.dart +++ b/lib/exceptions/media_server_exceptions.dart @@ -38,7 +38,7 @@ class MediaServerAuthException extends MediaServerException { /// Auth polling reached a terminal server-side expiry/rejection state before /// the user completed the external sign-in flow. class MediaServerPinExpiredException extends MediaServerAuthException { - const MediaServerPinExpiredException({String? display}) : super('PIN expired before sign-in', display: display); + const MediaServerPinExpiredException({super.display}) : super('PIN expired before sign-in'); } /// HTTP transport / non-2xx errors. Carries the status code (when known), @@ -57,11 +57,11 @@ class MediaServerHttpException extends MediaServerException { MediaServerHttpException({ required this.type, String? message, - String? display, + super.display, this.statusCode, this.responseData, this.requestUri, - }) : super(message ?? '', display: display); + }) : super(message ?? ''); /// Map a caught exception to a [MediaServerHttpException]. factory MediaServerHttpException.from(Object error, {Uri? uri}) { diff --git a/lib/services/cached_playback_metadata_service.dart b/lib/services/cached_playback_metadata_service.dart index 55ffb3527..d5497fa75 100644 --- a/lib/services/cached_playback_metadata_service.dart +++ b/lib/services/cached_playback_metadata_service.dart @@ -1,4 +1,5 @@ import 'dart:convert'; + import '../media/ids.dart'; import '../media/media_backend.dart'; @@ -21,7 +22,7 @@ class CachedPlaybackMetadataService { int mediaIndex = 0, }) async { try { - return switch (backend) { + return await switch (backend) { MediaBackend.plex => _fetchPlexMediaSourceInfo(ServerId(cacheServerId), itemId, mediaIndex: mediaIndex), MediaBackend.jellyfin || MediaBackend.emby => _fetchJellyfinMediaSourceInfo( cacheServerId, @@ -45,7 +46,7 @@ class CachedPlaybackMetadataService { bool forceChapterFallback = false, }) async { try { - return switch (backend) { + return await switch (backend) { MediaBackend.plex => _fetchPlexPlaybackExtras( ServerId(cacheServerId), itemId, diff --git a/lib/services/local_playback_history.dart b/lib/services/local_playback_history.dart index d2e5b37b4..87377e5ff 100644 --- a/lib/services/local_playback_history.dart +++ b/lib/services/local_playback_history.dart @@ -55,7 +55,9 @@ class LocalPlaybackHistory { static Future> snapshot() async { try { final settings = await SettingsService.getInstance(); - return settings.read(SettingsService.localLastPlayedAt); + // Explicit type argument: the async return context would otherwise + // infer T = FutureOr and trip UNAWAITED_RETURN_IN_TRY_BLOCK; read is sync. + return settings.read>(SettingsService.localLastPlayedAt); } catch (_) { return const {}; } diff --git a/lib/services/multi_server_manager.dart b/lib/services/multi_server_manager.dart index 8b161b0ff..9f4c335c4 100644 --- a/lib/services/multi_server_manager.dart +++ b/lib/services/multi_server_manager.dart @@ -1,4 +1,5 @@ import 'dart:async'; + import '../media/ids.dart'; import 'package:connectivity_plus/connectivity_plus.dart'; @@ -17,7 +18,9 @@ import '../utils/app_logger.dart'; import '../utils/media_server_timeouts.dart'; import '../utils/active_client_scope.dart'; import '../utils/future_extensions.dart'; + import 'package:sentry_flutter/sentry_flutter.dart'; + import 'plex_auth_service.dart'; import 'settings_service.dart'; import 'storage_service.dart'; @@ -675,7 +678,7 @@ class MultiServerManager { // before closing it, so a client found here is never mid-close. final existing = _jellyfinByCompoundId[connection.id]; if (existing != null && canReuseJellyfinClient(live: existing.connection, incoming: connection)) { - return _reuseJellyfinClient(existing); + return await _reuseJellyfinClient(existing); } var resolvedConnection = connection; diff --git a/lib/services/playback_progress_tracker.dart b/lib/services/playback_progress_tracker.dart index 806a6d3f2..2b401c842 100644 --- a/lib/services/playback_progress_tracker.dart +++ b/lib/services/playback_progress_tracker.dart @@ -1,4 +1,5 @@ import 'dart:async'; + import '../exceptions/media_server_exceptions.dart'; import '../media/ids.dart'; @@ -724,7 +725,9 @@ class PlaybackProgressTracker { Future _shouldReportTrackSelections() async { try { final settings = await SettingsService.getInstance(); - return settings.read(SettingsService.rememberTrackSelections); + // Explicit type argument: the async return context would otherwise + // infer T = FutureOr and trip UNAWAITED_RETURN_IN_TRY_BLOCK; read is sync. + return settings.read(SettingsService.rememberTrackSelections); } catch (e) { appLogger.d('Could not read track-selection persistence setting; reporting selected streams', error: e); return true; diff --git a/lib/services/plex_client/parts/collections.dart b/lib/services/plex_client/parts/collections.dart index 3318a2402..2bd56f2a1 100644 --- a/lib/services/plex_client/parts/collections.dart +++ b/lib/services/plex_client/parts/collections.dart @@ -38,7 +38,7 @@ mixin _PlexCollectionMethods on _PlexClientInternals { Future> _getLibraryCollections(String sectionId) async { try { - return _fetchAllPages( + return await _fetchAllPages( (start, size, abort) => _getLibraryCollectionsPage(sectionId, start: start, size: size, abort: abort), ); } catch (e, st) { diff --git a/lib/services/plex_client/parts/play_queues.dart b/lib/services/plex_client/parts/play_queues.dart index ad24db52f..0bc671d01 100644 --- a/lib/services/plex_client/parts/play_queues.dart +++ b/lib/services/plex_client/parts/play_queues.dart @@ -120,7 +120,7 @@ mixin _PlexPlayQueueMethods on _PlexClientInternals { // the client-side choice in [sortEpisodesByWatchOrder]. final leaf = effectiveSpecialsOrdering() == SpecialsOrdering.specialsLast ? 'children' : 'allLeaves'; final uri = '${await buildMetadataUri(showRatingKey)}/$leaf'; - return createPlayQueue( + return await createPlayQueue( uri: uri, type: 'video', shuffle: shuffle, diff --git a/lib/utils/video_player_navigation.dart b/lib/utils/video_player_navigation.dart index 41a44e441..b2fcf33ca 100644 --- a/lib/utils/video_player_navigation.dart +++ b/lib/utils/video_player_navigation.dart @@ -1,4 +1,5 @@ import 'dart:async'; + import '../media/ids.dart'; import 'package:flutter/material.dart'; @@ -310,6 +311,10 @@ Future navigateToVideoPlayer( } } + // Deliberately not awaited inside the try: the route future completes when + // the player pops, but the in-flight guard must release once the push is + // committed. Returned after the finally so the guard timing is unchanged. + Future? pushFuture; try { // Check if external player is enabled. The platform guard comes first so // platforms that can never launch an external player skip the settings @@ -386,12 +391,13 @@ Future navigateToVideoPlayer( ), ); - return usePushReplacement ? navigator.pushReplacement(route) : navigator.push(route); + pushFuture = usePushReplacement ? navigator.pushReplacement(route) : navigator.push(route); } finally { if (markedInFlight) { _videoPlayerNavigationInFlightGuard.finish(launchIdentity); } } + return pushFuture; } /// Navigates to the video player and optionally refreshes content when returning. diff --git a/macos/Podfile.lock b/macos/Podfile.lock index 0ad684b88..e836d5651 100644 --- a/macos/Podfile.lock +++ b/macos/Podfile.lock @@ -31,7 +31,7 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: auto_updater_macos: 3a42f1a06be6981f1a18be37e6e7bf86aa732118 - FlutterMacOS: d0db08ddef1a9af05a5ec4b724367152bb0500b1 + FlutterMacOS: c232990155153907050900a2e175c7773903ba4e os_media_controls: 20a765c7ebaae4fb3133cc47782eb99d7b50d851 Sparkle: f4355f9ebbe9b7d932df4980d70f13922ac97b2a universal_gamepad: 5d5c2ad9be4eaafcdc0bdc536b51725fe058667f diff --git a/packages/saf_util/analysis_options.yaml b/packages/saf_util/analysis_options.yaml index 266839abd..cece60e87 100644 --- a/packages/saf_util/analysis_options.yaml +++ b/packages/saf_util/analysis_options.yaml @@ -1 +1,10 @@ +analyzer: + exclude: + - build/** + - android/** + - ios/** + - web/** + - windows/** + - macos/** + - linux/** include: package:mgenware_dart_lints/flutter_lints.yaml diff --git a/packages/saf_util/pubspec.lock b/packages/saf_util/pubspec.lock index 9fd8e7b25..b8ce43afa 100644 --- a/packages/saf_util/pubspec.lock +++ b/packages/saf_util/pubspec.lock @@ -103,10 +103,10 @@ packages: dependency: transitive description: name: matcher - sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861 + sha256: "31bd099b47c10cd1aeb55146a2d46ce0277630ecef3f7dae54ad7873f36696cd" url: "https://pub.dev" source: hosted - version: "0.12.19" + version: "0.12.20" material_color_utilities: dependency: transitive description: @@ -119,10 +119,10 @@ packages: dependency: transitive description: name: meta - sha256: "1741988757a65eb6b36abe716829688cf01910bbf91c34354ff7ec1c3de2b349" + sha256: "307249ce4ff29d58a18e97f6345f539382eb9c9c29ecda628900f31de0443dd9" url: "https://pub.dev" source: hosted - version: "1.18.0" + version: "1.19.0" mgenware_dart_lints: dependency: "direct dev" description: @@ -196,18 +196,18 @@ packages: dependency: transitive description: name: test_api - sha256: "949a932224383300f01be9221c39180316445ecb8e7547f70a41a35bf421fb9e" + sha256: "2a122cbe059f8b610d3a5415f42e255b6c17b1f21eee1d960f31080237fb4f11" url: "https://pub.dev" source: hosted - version: "0.7.11" + version: "0.7.12" vector_math: dependency: transitive description: name: vector_math - sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b + sha256: f36f9f3be64c6198714492bb455c11056e33e2f85d9a0b676a48301e44fdcf47 url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.4.2" vm_service: dependency: transitive description: diff --git a/packages/shared_preferences_linux/pubspec.lock b/packages/shared_preferences_linux/pubspec.lock index 39c7a003a..8e0b544c8 100644 --- a/packages/shared_preferences_linux/pubspec.lock +++ b/packages/shared_preferences_linux/pubspec.lock @@ -103,10 +103,10 @@ packages: dependency: transitive description: name: matcher - sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861 + sha256: "31bd099b47c10cd1aeb55146a2d46ce0277630ecef3f7dae54ad7873f36696cd" url: "https://pub.dev" source: hosted - version: "0.12.19" + version: "0.12.20" material_color_utilities: dependency: transitive description: @@ -119,10 +119,10 @@ packages: dependency: transitive description: name: meta - sha256: "1741988757a65eb6b36abe716829688cf01910bbf91c34354ff7ec1c3de2b349" + sha256: "307249ce4ff29d58a18e97f6345f539382eb9c9c29ecda628900f31de0443dd9" url: "https://pub.dev" source: hosted - version: "1.18.0" + version: "1.19.0" path: dependency: "direct main" description: @@ -220,18 +220,18 @@ packages: dependency: transitive description: name: test_api - sha256: "949a932224383300f01be9221c39180316445ecb8e7547f70a41a35bf421fb9e" + sha256: "2a122cbe059f8b610d3a5415f42e255b6c17b1f21eee1d960f31080237fb4f11" url: "https://pub.dev" source: hosted - version: "0.7.11" + version: "0.7.12" vector_math: dependency: transitive description: name: vector_math - sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b + sha256: f36f9f3be64c6198714492bb455c11056e33e2f85d9a0b676a48301e44fdcf47 url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.4.2" vm_service: dependency: transitive description: @@ -249,5 +249,5 @@ packages: source: hosted version: "1.1.0" sdks: - dart: ">=3.10.0 <4.0.0" + dart: ">=3.11.0-0 <4.0.0" flutter: ">=3.38.0" diff --git a/packages/shared_preferences_windows/pubspec.lock b/packages/shared_preferences_windows/pubspec.lock index f412644a9..a173acf9a 100644 --- a/packages/shared_preferences_windows/pubspec.lock +++ b/packages/shared_preferences_windows/pubspec.lock @@ -103,10 +103,10 @@ packages: dependency: transitive description: name: matcher - sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861 + sha256: "31bd099b47c10cd1aeb55146a2d46ce0277630ecef3f7dae54ad7873f36696cd" url: "https://pub.dev" source: hosted - version: "0.12.19" + version: "0.12.20" material_color_utilities: dependency: transitive description: @@ -119,10 +119,10 @@ packages: dependency: transitive description: name: meta - sha256: "1741988757a65eb6b36abe716829688cf01910bbf91c34354ff7ec1c3de2b349" + sha256: "307249ce4ff29d58a18e97f6345f539382eb9c9c29ecda628900f31de0443dd9" url: "https://pub.dev" source: hosted - version: "1.18.0" + version: "1.19.0" path: dependency: "direct main" description: @@ -220,18 +220,18 @@ packages: dependency: transitive description: name: test_api - sha256: "949a932224383300f01be9221c39180316445ecb8e7547f70a41a35bf421fb9e" + sha256: "2a122cbe059f8b610d3a5415f42e255b6c17b1f21eee1d960f31080237fb4f11" url: "https://pub.dev" source: hosted - version: "0.7.11" + version: "0.7.12" vector_math: dependency: transitive description: name: vector_math - sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b + sha256: f36f9f3be64c6198714492bb455c11056e33e2f85d9a0b676a48301e44fdcf47 url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.4.2" vm_service: dependency: transitive description: @@ -241,5 +241,5 @@ packages: source: hosted version: "15.2.0" sdks: - dart: ">=3.10.0 <4.0.0" + dart: ">=3.11.0-0 <4.0.0" flutter: ">=3.38.0" diff --git a/packages/wakelock_plus/pubspec.lock b/packages/wakelock_plus/pubspec.lock index 9304e77bd..c0dcb0550 100644 --- a/packages/wakelock_plus/pubspec.lock +++ b/packages/wakelock_plus/pubspec.lock @@ -244,10 +244,10 @@ packages: dependency: transitive description: name: matcher - sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861 + sha256: "31bd099b47c10cd1aeb55146a2d46ce0277630ecef3f7dae54ad7873f36696cd" url: "https://pub.dev" source: hosted - version: "0.12.19" + version: "0.12.20" material_color_utilities: dependency: transitive description: @@ -260,10 +260,10 @@ packages: dependency: "direct main" description: name: meta - sha256: "1741988757a65eb6b36abe716829688cf01910bbf91c34354ff7ec1c3de2b349" + sha256: "307249ce4ff29d58a18e97f6345f539382eb9c9c29ecda628900f31de0443dd9" url: "https://pub.dev" source: hosted - version: "1.18.0" + version: "1.19.0" mocktail: dependency: "direct dev" description: @@ -385,10 +385,10 @@ packages: dependency: transitive description: name: test_api - sha256: "949a932224383300f01be9221c39180316445ecb8e7547f70a41a35bf421fb9e" + sha256: "2a122cbe059f8b610d3a5415f42e255b6c17b1f21eee1d960f31080237fb4f11" url: "https://pub.dev" source: hosted - version: "0.7.11" + version: "0.7.12" typed_data: dependency: transitive description: @@ -401,10 +401,10 @@ packages: dependency: transitive description: name: vector_math - sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b + sha256: f36f9f3be64c6198714492bb455c11056e33e2f85d9a0b676a48301e44fdcf47 url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.4.2" vm_service: dependency: transitive description: @@ -462,5 +462,5 @@ packages: source: hosted version: "3.1.3" sdks: - dart: ">=3.10.0 <4.0.0" + dart: ">=3.11.0-0 <4.0.0" flutter: ">=3.38.0" diff --git a/pubspec.lock b/pubspec.lock index 5ec4f73c8..e611bb55c 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -591,10 +591,10 @@ packages: dependency: "direct main" description: name: intl - sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" + sha256: "1ca20c894b1717686a2319b8548763d812bc0aabdac580420a44c5178c57a867" url: "https://pub.dev" source: hosted - version: "0.20.2" + version: "0.20.3" io: dependency: transitive description: @@ -687,10 +687,10 @@ packages: dependency: transitive description: name: matcher - sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861 + sha256: "31bd099b47c10cd1aeb55146a2d46ce0277630ecef3f7dae54ad7873f36696cd" url: "https://pub.dev" source: hosted - version: "0.12.19" + version: "0.12.20" material_color_utilities: dependency: transitive description: @@ -711,10 +711,10 @@ packages: dependency: transitive description: name: meta - sha256: "1741988757a65eb6b36abe716829688cf01910bbf91c34354ff7ec1c3de2b349" + sha256: "307249ce4ff29d58a18e97f6345f539382eb9c9c29ecda628900f31de0443dd9" url: "https://pub.dev" source: hosted - version: "1.18.0" + version: "1.19.0" mgenware_dart_lints: dependency: "direct dev" description: @@ -1274,10 +1274,10 @@ packages: dependency: transitive description: name: test_api - sha256: "949a932224383300f01be9221c39180316445ecb8e7547f70a41a35bf421fb9e" + sha256: "2a122cbe059f8b610d3a5415f42e255b6c17b1f21eee1d960f31080237fb4f11" url: "https://pub.dev" source: hosted - version: "0.7.11" + version: "0.7.12" typed_data: dependency: transitive description: @@ -1402,10 +1402,10 @@ packages: dependency: transitive description: name: vector_math - sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b + sha256: f36f9f3be64c6198714492bb455c11056e33e2f85d9a0b676a48301e44fdcf47 url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.4.2" vm_service: dependency: transitive description: @@ -1527,4 +1527,4 @@ packages: version: "2.2.4" sdks: dart: ">=3.12.0 <4.0.0" - flutter: ">=3.44.0" + flutter: ">=3.47.0" diff --git a/pubspec.yaml b/pubspec.yaml index 49dd607b9..5bfc3e167 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -4,8 +4,10 @@ publish_to: "none" version: 2.17.0+145 environment: + # Dart floor stays 3.12: freezed 3.x emits `final` constructor parameters, + # which language 3.13 removed. Raise together with a freezed 4 migration. sdk: ">=3.12.0 <4.0.0" - flutter: ">=3.44.0" + flutter: ">=3.47.0" dependencies: flutter: diff --git a/scripts/checks/check_build_workflow.py b/scripts/checks/check_build_workflow.py index 6ccc3db7c..d4ce992b6 100644 --- a/scripts/checks/check_build_workflow.py +++ b/scripts/checks/check_build_workflow.py @@ -10,8 +10,8 @@ from workflow_yaml import iter_uses_references, job_block ROOT = Path(__file__).resolve().parents[2] DEFAULT_WORKFLOW = ROOT / ".github/workflows/build.yml" -FLUTTER_VERSION = "3.44.0" -FLUTTER_COMMIT = "559ffa3f75e7402d65a8def9c28389a9b2e6fe42" +FLUTTER_VERSION = "3.47.1" +FLUTTER_COMMIT = "6655482ec06e547f90abf8ae7590466f4415978d" if len(sys.argv) > 2: raise SystemExit(f"Usage: {Path(sys.argv[0]).name} [workflow-path]") WORKFLOW = Path(sys.argv[1]).resolve() if len(sys.argv) == 2 else DEFAULT_WORKFLOW diff --git a/scripts/checks/test_check_build_workflow.py b/scripts/checks/test_check_build_workflow.py index 4b210901e..84ba65418 100755 --- a/scripts/checks/test_check_build_workflow.py +++ b/scripts/checks/test_check_build_workflow.py @@ -49,7 +49,7 @@ class BuildWorkflowGuardTest(unittest.TestCase): def test_windows_arm_flutter_without_release_tag_is_rejected(self) -> None: action = self._action().replace( 'git -C $root fetch --depth 1 origin "refs/tags/${version}:refs/tags/${version}"', - "git -C $root fetch --depth 1 origin 559ffa3f75e7402d65a8def9c28389a9b2e6fe42", + "git -C $root fetch --depth 1 origin 6655482ec06e547f90abf8ae7590466f4415978d", 1, ) self.assertNotEqual(action, self._action(), "fixture mutation no longer matches the action") diff --git a/test/services/download_storage_service_test.dart b/test/services/download_storage_service_test.dart index d4d398dda..28a48ca99 100644 --- a/test/services/download_storage_service_test.dart +++ b/test/services/download_storage_service_test.dart @@ -123,31 +123,27 @@ void main() { expect(dir.path, p.join(tmpRoot.path, 'support', 'downloads')); }); - test( - 'resolves under POSIX chmod restrictions (environment-dependent smoke)', - () async { - final settings = await SettingsService.getInstance(); - final readOnlyParent = Directory(p.join(tmpRoot.path, 'readonly'))..createSync(recursive: true); - try { - await Process.run('chmod', ['000', readOnlyParent.path]); - final blocked = p.join(readOnlyParent.path, 'forbidden'); - await settings.write(SettingsService.customDownloadPathType, 'file'); - await settings.write(SettingsService.customDownloadPath, blocked); + test('resolves under POSIX chmod restrictions (environment-dependent smoke)', () async { + final settings = await SettingsService.getInstance(); + final readOnlyParent = Directory(p.join(tmpRoot.path, 'readonly'))..createSync(recursive: true); + try { + await Process.run('chmod', ['000', readOnlyParent.path]); + final blocked = p.join(readOnlyParent.path, 'forbidden'); + await settings.write(SettingsService.customDownloadPathType, 'file'); + await settings.write(SettingsService.customDownloadPath, blocked); - final dss = DownloadStorageService.instance; - await dss.initialize(settings); + final dss = DownloadStorageService.instance; + await dss.initialize(settings); - final dir = await dss.getDownloadsDirectory(); - // The host may honor or ignore mode bits; either resolved root is - // valid for this smoke test as long as it exists. - expect(dir.existsSync(), isTrue); - expect(dir.path, anyOf(blocked, p.join(tmpRoot.path, 'support', 'downloads'))); - } finally { - await Process.run('chmod', ['755', readOnlyParent.path]); - } - }, - skip: Platform.isWindows ? 'Windows does not provide chmod permission semantics' : false, - ); + final dir = await dss.getDownloadsDirectory(); + // The host may honor or ignore mode bits; either resolved root is + // valid for this smoke test as long as it exists. + expect(dir.existsSync(), isTrue); + expect(dir.path, anyOf(blocked, p.join(tmpRoot.path, 'support', 'downloads'))); + } finally { + await Process.run('chmod', ['755', readOnlyParent.path]); + } + }, skip: Platform.isWindows ? 'Windows does not provide chmod permission semantics' : false); test('refreshCustomPath picks up settings changes', () async { final settings = await SettingsService.getInstance(); diff --git a/test/services/jellyfin_client_emby_dialect_test.dart b/test/services/jellyfin_client_emby_dialect_test.dart index fc6ef606a..67191bd84 100644 --- a/test/services/jellyfin_client_emby_dialect_test.dart +++ b/test/services/jellyfin_client_emby_dialect_test.dart @@ -158,11 +158,10 @@ void main() { ['GET /Users/user-1/Items/Resume'], reason: embyRequests.log.join('\n'), ); - expect( - jellyfinRequests.requests.map((request) => '${request.method} ${request.url.path}').toList(), - ['GET /UserItems/Resume', 'GET /Shows/NextUp'], - reason: jellyfinRequests.log.join('\n'), - ); + expect(jellyfinRequests.requests.map((request) => '${request.method} ${request.url.path}').toList(), [ + 'GET /UserItems/Resume', + 'GET /Shows/NextUp', + ], reason: jellyfinRequests.log.join('\n')); final embyResume = embyRequests.requests.first; final jellyfinResume = jellyfinRequests.requests.first; expect(embyResume.url.queryParameters, { @@ -1233,11 +1232,10 @@ void main() { final video = await client.fetchPlaylistsPage(playlistType: 'video'); final audio = await client.fetchPlaylistsPage(playlistType: 'audio'); - expect( - requests.requests.map((request) => '${request.method} ${request.url.path}').toList(), - ['GET /Items', 'GET /Items'], - reason: requests.log.join('\n'), - ); + expect(requests.requests.map((request) => '${request.method} ${request.url.path}').toList(), [ + 'GET /Items', + 'GET /Items', + ], reason: requests.log.join('\n')); for (final request in requests.requests) { expect(request.url.queryParameters, { 'userId': 'user-1', @@ -1304,11 +1302,10 @@ void main() { final video = await client.fetchPlaylistsPage(playlistType: 'video'); final audio = await client.fetchPlaylistsPage(playlistType: 'audio'); - expect( - requests.requests.map((request) => '${request.method} ${request.url.path}').toList(), - ['GET /Items', 'GET /Items'], - reason: requests.log.join('\n'), - ); + expect(requests.requests.map((request) => '${request.method} ${request.url.path}').toList(), [ + 'GET /Items', + 'GET /Items', + ], reason: requests.log.join('\n')); expect(requests.requests.map((request) => request.url.queryParameters['MediaTypes']).toList(), [ 'Video', 'Audio', @@ -1528,11 +1525,9 @@ void main() { final extras = await client.fetchPlaybackExtras('item-1'); - expect( - requests.requests.map((request) => '${request.method} ${request.url.path}').toList(), - ['GET /Users/user-1/Items/item-1'], - reason: requests.log.join('\n'), - ); + expect(requests.requests.map((request) => '${request.method} ${request.url.path}').toList(), [ + 'GET /Users/user-1/Items/item-1', + ], reason: requests.log.join('\n')); expect(requests.requests.where((request) => request.url.path == '/MediaSegments/item-1'), isEmpty); expect(extras.markers.map((marker) => marker.type).toList(), ['intro', 'credits']); expect(extras.markers.first.startTimeOffset, 10000); @@ -1560,11 +1555,10 @@ void main() { final extras = await client.fetchPlaybackExtras('item-1'); - expect( - requests.requests.map((request) => '${request.method} ${request.url.path}').toList(), - ['GET /Users/user-1/Items/item-1', 'GET /MediaSegments/item-1'], - reason: requests.log.join('\n'), - ); + expect(requests.requests.map((request) => '${request.method} ${request.url.path}').toList(), [ + 'GET /Users/user-1/Items/item-1', + 'GET /MediaSegments/item-1', + ], reason: requests.log.join('\n')); expect(requests.requests.last.url.query, isEmpty); expect(extras.markers.map((marker) => marker.type).toList(), ['intro']); expect(extras.markers.single.startTimeOffset, 5000); diff --git a/test/services/track_manager_test.dart b/test/services/track_manager_test.dart index b184b737a..acc3c79b4 100644 --- a/test/services/track_manager_test.dart +++ b/test/services/track_manager_test.dart @@ -457,11 +457,9 @@ void main() { ); await _drainAsync(); - expect( - player.selectedSecondarySubtitle.map((track) => track.id), - ['10'], - reason: 'the wait stayed armed, so the late secondary still landed', - ); + expect(player.selectedSecondarySubtitle.map((track) => track.id), [ + '10', + ], reason: 'the wait stayed armed, so the late secondary still landed'); }); test('a sidecar-backed secondary source row resolves against its loaded track', () async { @@ -508,11 +506,9 @@ void main() { ); await _drainAsync(); - expect( - player.selectedSecondarySubtitle.map((track) => track.id), - ['1'], - reason: 'the extracted file is the secondary row, however the two sides label it', - ); + expect(player.selectedSecondarySubtitle.map((track) => track.id), [ + '1', + ], reason: 'the extracted file is the secondary row, however the two sides label it'); }); test('complete metadata-free direct catalog applies tracks without the five-second fallback', () async { diff --git a/tvos/engine.sha256 b/tvos/engine.sha256 index b731f9447..53ad16037 100644 --- a/tvos/engine.sha256 +++ b/tvos/engine.sha256 @@ -1 +1 @@ -c9d14e12e35d15b0dc958662e51153291292ea24b08c9bd34825199929ae6041 +1cc6e4f1441ea2288512036f46589057eb54228732a27baf2e1419354d173c7c diff --git a/tvos/engine.version b/tvos/engine.version index 93c2bb839..01e60178b 100644 --- a/tvos/engine.version +++ b/tvos/engine.version @@ -1 +1 @@ -3.44.0+6 +3.47.1 diff --git a/tvos/scripts/xcode_appletv.sh b/tvos/scripts/xcode_appletv.sh index 26d5b2d5d..4fd5c19bd 100755 --- a/tvos/scripts/xcode_appletv.sh +++ b/tvos/scripts/xcode_appletv.sh @@ -410,7 +410,10 @@ BuildAppDebug() { # overwrite kernel_blob.bin and both snapshot blobs below with versions # from our tvOS engine so the tvOS VM can load them. ( + # Flutter 3.47's tool rejects FLUTTER_BUILD_NAME/NUMBER in the environment; + # they are consumed by this script's plist sync, not by `build bundle`. cd "$FLUTTER_APPLICATION_PATH" && \ + env -u FLUTTER_BUILD_NAME -u FLUTTER_BUILD_NUMBER \ "$FLUTTER_BIN" build bundle \ --asset-dir="$OUTDIR/App.framework/flutter_assets" \ --no-tree-shake-icons \ @@ -568,7 +571,10 @@ BuildAppRelease() { echo " └─Generate flutter_assets via flutter build bundle (release)" mkdir -p "$OUTDIR/App.framework/flutter_assets" ( + # Flutter 3.47's tool rejects FLUTTER_BUILD_NAME/NUMBER in the environment; + # they are consumed by this script's plist sync, not by `build bundle`. cd "$FLUTTER_APPLICATION_PATH" && \ + env -u FLUTTER_BUILD_NAME -u FLUTTER_BUILD_NUMBER \ "$FLUTTER_BIN" build bundle \ --release \ --asset-dir="$OUTDIR/App.framework/flutter_assets" \ diff --git a/windows/tool/install-patched-engine.ps1 b/windows/tool/install-patched-engine.ps1 index 91f519a15..7ef897519 100644 --- a/windows/tool/install-patched-engine.ps1 +++ b/windows/tool/install-patched-engine.ps1 @@ -20,15 +20,15 @@ # flutter precache --windows # windows/tool/install-patched-engine.ps1 param( - # Engine zip published at flutter-plezy release windows-v3.44.0+1 - x64 + arm64 + # Engine zip published at flutter-plezy release windows-v3.47.1 - x64 + arm64 # (cache dirs windows-{x64,arm64}{,-release}). The asset name is the same across # tags, so a version bump only changes the tag segment of the URL. - [string]$Url = 'https://github.com/edde746/flutter-plezy/releases/download/windows-v3.44.0+1/flutter-plezy-windows-3.44.0.zip', - [string]$Sha256 = '8de498d28f314c33971226856bea7aa54099e08e60cd7d208cbaf04693dc8274', + [string]$Url = 'https://github.com/edde746/flutter-plezy/releases/download/windows-v3.47.1/flutter-plezy-windows-3.47.1.zip', + [string]$Sha256 = '8ce715978b50cee636bacfca018ad2d5bfc53d6509e2b49c1a167266f2b16877', # Engine revision the artifacts were built from. Must match the SDK's # engine.stamp (gen_snapshot/dart in the SDK must come from the same # checkout), or the swapped binaries are ABI-incompatible with the build. - [string]$ExpectedEngine = '4c525dac5ebe5971c5708ef73558ed8edcf4a362' + [string]$ExpectedEngine = '5d531788691ec3404cac0cee66ead4007b177363' ) $ErrorActionPreference = 'Stop' diff --git a/windows/tool/swap-engine.ps1 b/windows/tool/swap-engine.ps1 index 0367a44ce..0366b8198 100644 --- a/windows/tool/swap-engine.ps1 +++ b/windows/tool/swap-engine.ps1 @@ -16,7 +16,7 @@ param( [switch]$Restore, # Engine revision these artifacts were built from. Guards against swapping # into a mismatched SDK (gen_snapshot/dart must come from the same checkout). - [string]$ExpectedEngine = '4c525dac5ebe5971c5708ef73558ed8edcf4a362' + [string]$ExpectedEngine = '5d531788691ec3404cac0cee66ead4007b177363' ) $ErrorActionPreference = 'Stop'