style: fix CI formatting
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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],
|
||||
|
||||
Reference in New Issue
Block a user