style: fix CI formatting

This commit is contained in:
edde746
2026-06-27 10:36:21 +02:00
parent 78090d8bd6
commit 79b75483bd
20 changed files with 115 additions and 96 deletions
@@ -193,6 +193,7 @@ class ExoPlayerCore(private val activity: Activity) : Player.Listener {
private var subtitlePositionPercent: Int = 100
private var subtitleFontSize: Float = 55f
private var lastSubtitleCues: List<Cue> = emptyList()
// Tracks whether a text track was selected on the previous onTracksChanged so we
// can detect the transition to "no subtitle" and clear the painted overlays (#1387).
private var hadSelectedTextTrack: Boolean = false
@@ -1783,8 +1784,7 @@ class ExoPlayerCore(private val activity: Activity) : Player.Listener {
selectionFlags = format.selectionFlags
)
private fun hasSelectedTextTrack(tracks: Tracks): Boolean =
tracks.groups.any { it.type == C.TRACK_TYPE_TEXT && it.isSelected }
private fun hasSelectedTextTrack(tracks: Tracks): Boolean = tracks.groups.any { it.type == C.TRACK_TYPE_TEXT && it.isSelected }
private fun restorePendingDvTrackSelection(tracks: Tracks): Boolean {
val pending = pendingDvTrackRestore ?: return false
+9 -9
View File
@@ -126,11 +126,9 @@ class DiscoverProvider extends ChangeNotifier with DisposableChangeNotifierMixin
/// and merged in; already-loaded servers are not refetched.
Future<void> syncToOnlineServers(Set<String> onlineServerIds) {
if (onlineServerIds.isEmpty || isProfileBinding()) return Future<void>.value();
if (
_onDeckState == DiscoverLoadState.loaded &&
_hubsState == DiscoverLoadState.loaded &&
_fullyLoadedServerIds.containsAll(onlineServerIds)
) {
if (_onDeckState == DiscoverLoadState.loaded &&
_hubsState == DiscoverLoadState.loaded &&
_fullyLoadedServerIds.containsAll(onlineServerIds)) {
return Future<void>.value();
}
// Nothing (or a failed pass) to merge into yet — run the full load.
@@ -490,10 +488,12 @@ class DiscoverProvider extends ChangeNotifier with DisposableChangeNotifierMixin
try {
final settings = await SettingsService.getInstance();
final syncableOnDeck = onDeck.where((item) {
final serverId = item.serverId;
return serverId != null && _multiServer.getClientForServer(ServerId(serverId)) != null;
}).toList(growable: false);
final syncableOnDeck = onDeck
.where((item) {
final serverId = item.serverId;
return serverId != null && _multiServer.getClientForServer(ServerId(serverId)) != null;
})
.toList(growable: false);
await SystemShelfService().syncFromContinueWatching(
syncableOnDeck,
_clientForShelfItem,
@@ -140,12 +140,8 @@ class _LibraryCollectionsTabState extends BaseLibraryTabState<MediaItem, Library
padding: _effectivePadding,
sliver: SliverList.builder(
itemCount: totalSize,
itemBuilder: (context, index) => _buildMediaCardItem(
index,
isFirstRow: index == 0,
isFirstColumn: true,
disableScale: true,
),
itemBuilder: (context, index) =>
_buildMediaCardItem(index, isFirstRow: index == 0, isFirstColumn: true, disableScale: true),
),
);
}
@@ -142,12 +142,8 @@ class _LibraryPlaylistsTabState extends BaseLibraryTabState<MediaPlaylist, Libra
padding: _effectivePadding,
sliver: SliverList.builder(
itemCount: totalSize,
itemBuilder: (context, index) => _buildPlaylistCard(
index,
isFirstRow: index == 0,
isFirstColumn: true,
disableScale: true,
),
itemBuilder: (context, index) =>
_buildPlaylistCard(index, isFirstRow: index == 0, isFirstColumn: true, disableScale: true),
),
);
}
@@ -62,9 +62,10 @@ class SubtitleStylingScreen extends StatelessWidget {
icon: Symbols.aspect_ratio_rounded,
title: t.subtitlingStyling.renderResolution,
subtitleBuilder: _renderResolutionLabel,
options: const [SubtitleRenderResolution.screen, SubtitleRenderResolution.video]
.map((v) => DialogOption(value: v, title: _renderResolutionLabel(v)))
.toList(),
options: const [
SubtitleRenderResolution.screen,
SubtitleRenderResolution.video,
].map((v) => DialogOption(value: v, title: _renderResolutionLabel(v))).toList(),
decode: (v) => v,
encode: (v) => v,
),
@@ -483,7 +483,11 @@ class _JellyfinLiveTvPlaybackSession implements LiveTvPlaybackSession {
Future<String?> streamUrlAt({int? offsetSeconds}) async => offsetSeconds == null ? _url : null;
@override
Future<CaptureBuffer?> reportTimeline({required String state, required int positionMs, required int durationMs}) async {
Future<CaptureBuffer?> reportTimeline({
required String state,
required int positionMs,
required int durationMs,
}) async {
await _tracker.report(
client: _client,
itemId: _channelKey,
+1 -5
View File
@@ -16,11 +16,7 @@ import '../utils/track_label_builder.dart';
/// stream indexes) or lack server-side stream selection leave this null.
/// [trackType] is `'audio'` or `'subtitle'`.
typedef TrackPreferencePersister =
Future<void> Function({
required int partId,
required String trackType,
int? streamID,
});
Future<void> Function({required int partId, required String trackType, int? streamID});
/// Manages track (audio + subtitle) lifecycle: external subtitle loading,
/// automatic track selection, server preference sync, and cycling.
+10 -2
View File
@@ -87,7 +87,13 @@ class FailoverHttpClient extends MediaServerHttpClient {
final generation = _endpointManager?.generation;
final MediaServerResponse response;
try {
response = await super.get(path, queryParameters: queryParameters, headers: headers, timeout: timeout, abort: abort);
response = await super.get(
path,
queryParameters: queryParameters,
headers: headers,
timeout: timeout,
abort: abort,
);
} on MediaServerHttpException catch (e) {
if (!allowEndpointFailover || !_shouldAttemptFailover(exception: e) || !_canFailover(generation)) {
rethrow;
@@ -102,7 +108,9 @@ class FailoverHttpClient extends MediaServerHttpClient {
if (retried == null) rethrow;
return retried;
}
if (!allowEndpointFailover || !_shouldAttemptFailover(statusCode: response.statusCode) || !_canFailover(generation)) {
if (!allowEndpointFailover ||
!_shouldAttemptFailover(statusCode: response.statusCode) ||
!_canFailover(generation)) {
return response;
}
return await _failoverOnce(
+4 -1
View File
@@ -56,7 +56,10 @@ List<dynamic>? flexibleList(Object? v) => switch (v) {
List<String>? flexibleStringList(Object? v) {
final list = flexibleList(v);
if (list == null) return null;
final result = [for (final e in list) if (e is String) e];
final result = [
for (final e in list)
if (e is String) e,
];
return result.isEmpty ? null : result;
}
+1 -5
View File
@@ -68,11 +68,7 @@ Future<void> navigateToLiveTv(
settings: const RouteSettings(name: kVideoPlayerRouteName),
pageBuilder: (context, animation, secondaryAnimation) => VideoPlayerScreen(
metadata: placeholder,
live: LiveTvSessionArgs(
channel: channel,
channels: normalizedChannels,
currentChannelIndex: currentChannelIndex,
),
live: LiveTvSessionArgs(channel: channel, channels: normalizedChannels, currentChannelIndex: currentChannelIndex),
),
transitionDuration: Duration.zero,
reverseTransitionDuration: Duration.zero,
@@ -202,12 +202,12 @@ extension _PlexVideoControlsMarkerMethods on _PlexVideoControlsState {
}
bool get _isSkipMarkerButtonVisible => shouldShowSkipMarkerButton(
hasFirstFrame: _hasRenderedFirstFrame,
hasMarker: _currentMarker != null,
hasPlayNextPrompt: widget.playNextFocusNode != null,
skipButtonDismissed: _skipButtonDismissed,
controlsVisible: _showControls,
);
hasFirstFrame: _hasRenderedFirstFrame,
hasMarker: _currentMarker != null,
hasPlayNextPrompt: widget.playNextFocusNode != null,
skipButtonDismissed: _skipButtonDismissed,
controlsVisible: _showControls,
);
void _activateSkipMarker() {
if (!_isSkipMarkerButtonVisible) return;
+18 -2
View File
@@ -14,8 +14,24 @@ import 'unwatched_count_badge.dart';
/// [compact] for dense surfaces (folder tree rows, episode thumbnails).
/// Add a preset here instead of hand-rolling a new overlay variant.
enum WatchedIndicatorSize {
standard(checkInset: 4, checkPadding: 4, checkIconSize: 16, badgeSize: 24, badgeFontSize: 12, barRadius: 8, barMinHeight: 4),
compact(checkInset: 3, checkPadding: 2, checkIconSize: 12, badgeSize: 20, badgeFontSize: 10, barRadius: 6, barMinHeight: 3);
standard(
checkInset: 4,
checkPadding: 4,
checkIconSize: 16,
badgeSize: 24,
badgeFontSize: 12,
barRadius: 8,
barMinHeight: 4,
),
compact(
checkInset: 3,
checkPadding: 2,
checkIconSize: 12,
badgeSize: 20,
badgeFontSize: 10,
barRadius: 6,
barMinHeight: 3,
);
const WatchedIndicatorSize({
required this.checkInset,
+19 -21
View File
@@ -28,17 +28,22 @@ MediaItem _item(String id, {String? parentId, String serverId = 'server_1'}) =>
parentId: parentId,
);
MediaHub _hub(String id, {String? identifier, String? libraryId, List<MediaItem>? items, String serverId = 'server_1'}) =>
MediaHub(
id: id,
title: id,
type: 'movie',
identifier: identifier,
items: items ?? [_item('$id-item', serverId: serverId)],
size: 1,
libraryId: libraryId,
serverId: serverId,
);
MediaHub _hub(
String id, {
String? identifier,
String? libraryId,
List<MediaItem>? items,
String serverId = 'server_1',
}) => MediaHub(
id: id,
title: id,
type: 'movie',
identifier: identifier,
items: items ?? [_item('$id-item', serverId: serverId)],
size: 1,
libraryId: libraryId,
serverId: serverId,
);
/// Counting fake — the provider's fetch-cost policy is the contract under
/// test: a watch event must cost exactly one on-deck call and zero hub
@@ -80,10 +85,7 @@ class _FakeAggregationService extends DataAggregationService {
}) async {
hubCalls++;
lastHubsServerIds = serverIds;
return (
hubs: hubsResult(),
succeededServerIds: hubSucceededServerIds ?? serverIds ?? const {'server_1'},
);
return (hubs: hubsResult(), succeededServerIds: hubSucceededServerIds ?? serverIds ?? const {'server_1'});
}
}
@@ -215,16 +217,12 @@ void main() {
});
test('library order change re-sorts hubs without any refetch', () async {
aggregation.hubsResult = () => [
_hub('hub-lib2', libraryId: 'lib-2'),
_hub('hub-lib1', libraryId: 'lib-1'),
];
aggregation.hubsResult = () => [_hub('hub-lib2', libraryId: 'lib-2'), _hub('hub-lib1', libraryId: 'lib-1')];
await provider.load();
expect(provider.hubs.map((h) => h.id), ['hub-lib2', 'hub-lib1']);
final hubCallsBefore = aggregation.hubCalls;
MediaLibrary lib(String id) =>
MediaLibrary(id: id, backend: MediaBackend.plex, title: id, serverId: 'server_1');
MediaLibrary lib(String id) => MediaLibrary(id: id, backend: MediaBackend.plex, title: id, serverId: 'server_1');
await libraries.updateLibraryOrder([lib('lib-1'), lib('lib-2')]);
await pumpEventQueue();
@@ -25,7 +25,11 @@ void main() {
serverManager = MultiServerManager();
syncService = OfflineWatchSyncService(database: db, serverManager: serverManager);
downloadManager = DownloadManagerService(database: db, storageService: DownloadStorageService.instance, clientResolver: (serverId, {clientScopeId}) => null);
downloadManager = DownloadManagerService(
database: db,
storageService: DownloadStorageService.instance,
clientResolver: (serverId, {clientScopeId}) => null,
);
downloadManager.recoveryFuture = Future<void>.value();
downloadProvider = DownloadProvider.forTesting(downloadManager: downloadManager, database: db);
await downloadProvider.ensureInitialized();
@@ -44,7 +44,11 @@ void main() {
PlexApiCache.initialize(db);
JellyfinApiCache.initialize(db);
final downloadManager = DownloadManagerService(database: db, storageService: DownloadStorageService.instance, clientResolver: (serverId, {clientScopeId}) => null);
final downloadManager = DownloadManagerService(
database: db,
storageService: DownloadStorageService.instance,
clientResolver: (serverId, {clientScopeId}) => null,
);
downloadProvider = DownloadProvider.forTesting(downloadManager: downloadManager, database: db);
await downloadProvider.ensureInitialized();
@@ -103,7 +103,11 @@ void main() {
db = AppDatabase.forTesting(NativeDatabase.memory());
PlexApiCache.initialize(db);
JellyfinApiCache.initialize(db);
downloadManager = DownloadManagerService(database: db, storageService: DownloadStorageService.instance, clientResolver: (serverId, {clientScopeId}) => null);
downloadManager = DownloadManagerService(
database: db,
storageService: DownloadStorageService.instance,
clientResolver: (serverId, {clientScopeId}) => null,
);
downloadProvider = DownloadProvider.forTesting(downloadManager: downloadManager, database: db);
await downloadProvider.ensureInitialized();
serverManager = MultiServerManager();
@@ -159,7 +159,11 @@ Future<_PlaylistHarness> _createHarness(List<MediaItem> items) async {
PlexApiCache.initialize(db);
JellyfinApiCache.initialize(db);
final downloadManager = DownloadManagerService(database: db, storageService: DownloadStorageService.instance, clientResolver: (serverId, {clientScopeId}) => null);
final downloadManager = DownloadManagerService(
database: db,
storageService: DownloadStorageService.instance,
clientResolver: (serverId, {clientScopeId}) => null,
);
downloadManager.recoveryFuture = Future<void>.value();
final downloadProvider = DownloadProvider.forTesting(downloadManager: downloadManager, database: db);
await downloadProvider.ensureInitialized();
@@ -1,4 +1,3 @@
import 'package:drift/drift.dart' show Value;
import 'package:drift/native.dart';
import 'package:flutter_test/flutter_test.dart';
+11 -16
View File
@@ -47,8 +47,11 @@ MediaItem _meta({String id = 'rk1'}) => MediaItem(id: id, backend: MediaBackend.
/// Player that records calls and can be configured per-test.
class _FakePlayer with PlayerStreamControllersMixin implements Player {
PlayerState _state;
_FakePlayer({Tracks tracks = const Tracks(), TrackSelection track = const TrackSelection(), this.attachesExternalSubtitlesAtOpen = false})
: _state = PlayerState(tracks: tracks, track: track);
_FakePlayer({
Tracks tracks = const Tracks(),
TrackSelection track = const TrackSelection(),
this.attachesExternalSubtitlesAtOpen = false,
}) : _state = PlayerState(tracks: tracks, track: track);
@override
PlayerState get state => _state;
@@ -126,13 +129,7 @@ MediaSourceInfo _mediaInfoWithSubtitles({bool selected = false}) {
videoUrl: 'https://example.com/video.mp4',
audioTracks: [MediaAudioTrack(id: 1, language: 'English', languageCode: 'eng', selected: true)],
subtitleTracks: [
MediaSubtitleTrack(
id: 10,
language: 'English',
languageCode: 'eng',
selected: selected,
forced: false,
),
MediaSubtitleTrack(id: 10, language: 'English', languageCode: 'eng', selected: selected, forced: false),
],
chapters: const [],
);
@@ -352,7 +349,9 @@ void main() {
test('waits for player subtitle tracks when Plex metadata advertises subtitles', () async {
await SettingsService.getInstance();
final player = _FakePlayer(
tracks: const Tracks(audio: [AudioTrack(id: '1', language: 'eng')]),
tracks: const Tracks(
audio: [AudioTrack(id: '1', language: 'eng')],
),
);
final mgr = _make(player: player, mediaInfo: _mediaInfoWithSubtitles());
addTearDown(mgr.dispose);
@@ -485,9 +484,7 @@ void main() {
final mgr = _make(player: player);
addTearDown(mgr.dispose);
mgr.cacheExternalSubtitles([
SubtitleTrack.uri('https://example/fallback.srt', title: 'EN'),
]);
mgr.cacheExternalSubtitles([SubtitleTrack.uri('https://example/fallback.srt', title: 'EN')]);
await mgr.onBackendSwitched();
@@ -499,9 +496,7 @@ void main() {
final mgr = _make(player: player);
addTearDown(mgr.dispose);
mgr.cacheExternalSubtitles([
SubtitleTrack.uri('https://example/fallback.srt', title: 'EN'),
]);
mgr.cacheExternalSubtitles([SubtitleTrack.uri('https://example/fallback.srt', title: 'EN')]);
await mgr.onBackendSwitched();
+1 -6
View File
@@ -18,12 +18,7 @@ void main() {
http.Response ok([String id = 'ok']) =>
http.Response(jsonEncode({'id': id}), 200, headers: {'content-type': 'application/json'});
({
FailoverHttpClient client,
List<({String url, bool persist})> switches,
List<String> exhausted,
List<Uri> requests,
})
({FailoverHttpClient client, List<({String url, bool persist})> switches, List<String> exhausted, List<Uri> requests})
build({
required Future<http.Response> Function(http.Request request, List<Uri> seen) handler,
List<String> endpoints = const [primary, fallback],