chore: format

This commit is contained in:
edde746
2025-11-15 04:08:57 +01:00
parent 2324590175
commit f14ed7d762
11 changed files with 155 additions and 168 deletions
@@ -25,10 +25,7 @@ class EndpointFailoverManager {
}
/// Replace the endpoint list and optionally set the active endpoint.
void reset(
List<String> urls, {
String? currentBaseUrl,
}) {
void reset(List<String> urls, {String? currentBaseUrl}) {
_setEndpoints(urls);
if (currentBaseUrl != null) {
final index = _endpoints.indexOf(currentBaseUrl);
@@ -60,8 +57,8 @@ class EndpointFailoverInterceptor extends Interceptor {
required Dio dio,
required this.endpointManager,
required Future<void> Function(String newBaseUrl) onEndpointSwitch,
}) : _dio = dio,
_onEndpointSwitch = onEndpointSwitch;
}) : _dio = dio,
_onEndpointSwitch = onEndpointSwitch;
final Dio _dio;
final EndpointFailoverManager endpointManager;
+2 -1
View File
@@ -27,7 +27,8 @@ class PlaybackStateProvider with ChangeNotifier {
bool get isPlaylistActive => _playbackMode == PlaybackMode.playlist;
/// Whether any queue-based playback is active
bool get isQueueActive => _queue.isNotEmpty && _playbackMode != PlaybackMode.none;
bool get isQueueActive =>
_queue.isNotEmpty && _playbackMode != PlaybackMode.none;
/// The context key (show/season/playlist ratingKey) for the current session
String? get shuffleContextKey => _contextKey;
+1 -3
View File
@@ -1130,9 +1130,7 @@ class _LibrariesScreenState extends State<LibrariesScreen>
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const PlaylistsScreen(),
),
MaterialPageRoute(builder: (context) => const PlaylistsScreen()),
);
},
tooltip: t.playlists.title,
+40 -37
View File
@@ -61,7 +61,9 @@ class _PlaylistDetailScreenState extends State<PlaylistDetailScreen>
_isLoading = false;
});
appLogger.d('Loaded ${items.length} items for playlist: ${widget.playlist.title}');
appLogger.d(
'Loaded ${items.length} items for playlist: ${widget.playlist.title}',
);
} catch (e) {
appLogger.e('Failed to load playlist items', error: e);
setState(() {
@@ -96,14 +98,14 @@ class _PlaylistDetailScreenState extends State<PlaylistDetailScreen>
if (mounted) {
if (success) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(t.playlists.deleted)),
);
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text(t.playlists.deleted)));
Navigator.pop(context); // Return to playlists screen
} else {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(t.playlists.errorDeleting)),
);
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text(t.playlists.errorDeleting)));
}
}
}
@@ -124,9 +126,9 @@ class _PlaylistDetailScreenState extends State<PlaylistDetailScreen>
if (movedItem.playlistItemID == null) {
appLogger.e('Cannot reorder: item missing playlistItemID');
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(t.playlists.errorReordering)),
);
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text(t.playlists.errorReordering)));
}
return;
}
@@ -142,16 +144,18 @@ class _PlaylistDetailScreenState extends State<PlaylistDetailScreen>
if (afterItem.playlistItemID == null) {
appLogger.e('Cannot reorder: after item missing playlistItemID');
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(t.playlists.errorReordering)),
);
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text(t.playlists.errorReordering)));
}
return;
}
afterPlaylistItemId = afterItem.playlistItemID!;
}
appLogger.d('Reordering item from $oldIndex to $newIndex (after ID: $afterPlaylistItemId)');
appLogger.d(
'Reordering item from $oldIndex to $newIndex (after ID: $afterPlaylistItemId)',
);
// Optimistically update UI
setState(() {
@@ -175,9 +179,9 @@ class _PlaylistDetailScreenState extends State<PlaylistDetailScreen>
_items.insert(oldIndex, item);
});
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(t.playlists.errorReordering)),
);
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text(t.playlists.errorReordering)));
}
}
}
@@ -189,14 +193,16 @@ class _PlaylistDetailScreenState extends State<PlaylistDetailScreen>
if (item.playlistItemID == null) {
appLogger.e('Cannot remove: item missing playlistItemID');
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(t.playlists.errorRemoving)),
);
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text(t.playlists.errorRemoving)));
}
return;
}
appLogger.d('Removing item ${item.title} (playlistItemID: ${item.playlistItemID}) from playlist');
appLogger.d(
'Removing item ${item.title} (playlistItemID: ${item.playlistItemID}) from playlist',
);
// Optimistically update UI
setState(() {
@@ -211,9 +217,9 @@ class _PlaylistDetailScreenState extends State<PlaylistDetailScreen>
if (mounted) {
if (success) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(t.playlists.itemRemoved)),
);
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text(t.playlists.itemRemoved)));
} else {
// Revert on failure
appLogger.e('Failed to remove playlist item, reverting UI');
@@ -221,9 +227,9 @@ class _PlaylistDetailScreenState extends State<PlaylistDetailScreen>
_items.insert(index, item);
});
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(t.playlists.errorRemoving)),
);
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text(t.playlists.errorRemoving)));
}
}
}
@@ -244,9 +250,9 @@ class _PlaylistDetailScreenState extends State<PlaylistDetailScreen>
Future<void> _playPlaylist() async {
if (_items.isEmpty) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(t.playlists.emptyPlaylist)),
);
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text(t.playlists.emptyPlaylist)));
}
return;
}
@@ -265,9 +271,9 @@ class _PlaylistDetailScreenState extends State<PlaylistDetailScreen>
Future<void> _shufflePlayPlaylist() async {
if (_items.isEmpty) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(t.playlists.emptyPlaylist)),
);
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text(t.playlists.emptyPlaylist)));
}
return;
}
@@ -422,10 +428,7 @@ class _PlaylistDetailScreenState extends State<PlaylistDetailScreen>
mainAxisSpacing: 0,
),
delegate: SliverChildBuilderDelegate((context, index) {
return MediaCard(
item: _items[index],
onRefresh: updateItem,
);
return MediaCard(item: _items[index], onRefresh: updateItem);
}, childCount: _items.length),
),
)
+14 -20
View File
@@ -156,9 +156,8 @@ class _PlaylistsScreenState extends State<PlaylistsScreen> with Refreshable {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => PlaylistDetailScreen(
playlist: _playlists[index],
),
builder: (context) =>
PlaylistDetailScreen(playlist: _playlists[index]),
),
).then((_) => _loadPlaylists()); // Refresh on return
},
@@ -275,14 +274,14 @@ class _PlaylistCard extends StatelessWidget {
if (context.mounted) {
if (success) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(t.playlists.deleted)),
);
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text(t.playlists.deleted)));
onDeleted();
} else {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(t.playlists.errorDeleting)),
);
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text(t.playlists.errorDeleting)));
}
}
}
@@ -370,13 +369,12 @@ class _PlaylistCard extends StatelessWidget {
Text(
playlist.leafCount != null && playlist.leafCount! > 0
? (playlist.leafCount == 1
? t.playlists.oneItem
: t.playlists.itemCount(count: playlist.leafCount!))
? t.playlists.oneItem
: t.playlists.itemCount(
count: playlist.leafCount!,
))
: t.playlists.emptyPlaylist,
style: TextStyle(
fontSize: 12,
color: Colors.grey[600],
),
style: TextStyle(fontSize: 12, color: Colors.grey[600]),
),
],
),
@@ -393,11 +391,7 @@ class _PlaylistCard extends StatelessWidget {
return Container(
color: Colors.grey[850],
child: const Center(
child: Icon(
Icons.playlist_play,
size: 48,
color: Colors.grey,
),
child: Icon(Icons.playlist_play, size: 48, color: Colors.grey),
),
);
}
+4 -4
View File
@@ -135,7 +135,9 @@ class VideoPlayerScreenState extends State<VideoPlayerScreen>
// Clear media controls when app goes to background or screen locks
// (we don't support background playback)
OsMediaControls.clear();
appLogger.d('Media controls cleared due to app lifecycle state: $state');
appLogger.d(
'Media controls cleared due to app lifecycle state: $state',
);
break;
case AppLifecycleState.resumed:
// Restore media controls when app is resumed
@@ -303,9 +305,7 @@ class VideoPlayerScreenState extends State<VideoPlayerScreen>
widget.metadata.ratingKey,
loopQueue: false, // Don't loop playlists by default
);
previous = playbackState.getPreviousEpisode(
widget.metadata.ratingKey,
);
previous = playbackState.getPreviousEpisode(widget.metadata.ratingKey);
}
// Check if shuffle mode is active
else if (playbackState.isShuffleActive) {
+13 -22
View File
@@ -418,10 +418,7 @@ class PlexServer {
final totalCandidates = candidates.length;
appLogger.d(
'Starting server connection discovery',
error: {
'preferred': preferredUri,
'candidateCount': totalCandidates,
},
error: {'preferred': preferredUri, 'candidateCount': totalCandidates},
);
_ConnectionCandidate? firstCandidate;
@@ -466,13 +463,11 @@ class PlexServer {
);
for (final candidate in candidates) {
PlexClient
.testConnectionWithLatency(
PlexClient.testConnectionWithLatency(
candidate.url,
accessToken,
timeout: raceTimeout,
)
.then((result) {
).then((result) {
completedTests++;
if (result.success && !completer.isCompleted) {
@@ -545,7 +540,7 @@ class PlexServer {
if (bestCandidate != null) {
final upgradedCandidate =
await _upgradeCandidateToHttpsIfPossible(bestCandidate) ??
bestCandidate;
bestCandidate;
final bestConnection = _updateConnectionUrl(
upgradedCandidate.connection,
@@ -596,8 +591,7 @@ class PlexServer {
if (uri == url) {
final isHttps = uri.startsWith('https://');
final parsedHost = Uri.tryParse(uri)?.host ?? '';
final isPlexDirect =
parsedHost.toLowerCase().contains('plex.direct');
final isPlexDirect = parsedHost.toLowerCase().contains('plex.direct');
return _ConnectionCandidate(connection, uri, isPlexDirect, isHttps);
}
}
@@ -644,18 +638,14 @@ class PlexServer {
return;
}
seen.add(url);
bucketFor(connection, isHttps).add(
_ConnectionCandidate(connection, url, isPlexDirectUri, isHttps),
);
bucketFor(
connection,
isHttps,
).add(_ConnectionCandidate(connection, url, isPlexDirectUri, isHttps));
}
for (final connection in connections) {
addCandidate(
connection,
connection.httpDirectUrl,
false,
false,
);
addCandidate(connection, connection.httpDirectUrl, false, false);
}
return [
@@ -789,8 +779,9 @@ class PlexServer {
current.uri.contains('.plex.direct'),
current.uri.startsWith('https://'),
);
final upgradedCandidate =
await _upgradeCandidateToHttpsIfPossible(candidate);
final upgradedCandidate = await _upgradeCandidateToHttpsIfPossible(
candidate,
);
if (upgradedCandidate == null) {
return null;
}
+36 -36
View File
@@ -24,7 +24,7 @@ class ServerConnectionResult {
/// Implements fast-first connection with background optimization
class ServerConnectionService {
static StreamSubscription<List<ConnectivityResult>>?
_connectivitySubscription;
_connectivitySubscription;
static Future<void>? _activeOptimization;
static PlexServer? _activeServer;
static PlexClient? _activeClient;
@@ -51,28 +51,31 @@ class ServerConnectionService {
}) async {
final storage = await StorageService.getInstance();
final connectionStream =
server.findBestWorkingConnection().asBroadcastStream();
final connectionStream = server
.findBestWorkingConnection()
.asBroadcastStream();
PlexClient? client;
final optimizationSubscription = connectionStream.skip(1).listen(
(connection) async {
await _handleOptimizedConnection(
connection: connection,
storage: storage,
server: server,
client: client,
reason: 'initial_latency_sweep',
final optimizationSubscription = connectionStream
.skip(1)
.listen(
(connection) async {
await _handleOptimizedConnection(
connection: connection,
storage: storage,
server: server,
client: client,
reason: 'initial_latency_sweep',
);
},
onError: (error, stackTrace) {
appLogger.w(
'Background connection optimization error',
error: error,
stackTrace: stackTrace,
);
},
);
},
onError: (error, stackTrace) {
appLogger.w(
'Background connection optimization error',
error: error,
stackTrace: stackTrace,
);
},
);
try {
final connection = await connectionStream.first;
@@ -132,9 +135,7 @@ class ServerConnectionService {
await optimizationSubscription.cancel();
appLogger.w('Server identity verification failed', error: e);
await storage.clearCredentials();
return ServerConnectionResult(
error: 'Server is not accessible: $e',
);
return ServerConnectionResult(error: 'Server is not accessible: $e');
}
}
@@ -149,10 +150,7 @@ class ServerConnectionService {
_activeClient = client;
_startConnectivityMonitoring(server);
return ServerConnectionResult(
client: client,
userProfile: userProfile,
);
return ServerConnectionResult(client: client, userProfile: userProfile);
} on StateError catch (e, stackTrace) {
await optimizationSubscription.cancel();
appLogger.e(
@@ -205,8 +203,9 @@ class ServerConnectionService {
final connectivity = Connectivity();
_connectivitySubscription = connectivity.onConnectivityChanged.listen(
(results) {
final status =
results.isNotEmpty ? results.first : ConnectivityResult.none;
final status = results.isNotEmpty
? results.first
: ConnectivityResult.none;
if (status == ConnectivityResult.none) {
appLogger.w(
'Connectivity lost, pausing optimization until network returns',
@@ -250,13 +249,14 @@ class ServerConnectionService {
return;
}
_activeOptimization = _runOptimization(
server: _activeServer!,
client: _activeClient,
reason: reason,
).whenComplete(() {
_activeOptimization = null;
});
_activeOptimization =
_runOptimization(
server: _activeServer!,
client: _activeClient,
reason: reason,
).whenComplete(() {
_activeOptimization = null;
});
}
static Future<void> _runOptimization({
+7 -6
View File
@@ -2,10 +2,10 @@ class LogRedactionManager {
static final Set<String> _tokens = <String>{};
static final Set<String> _urls = <String>{};
static final Set<String> _customValues = <String>{};
static final RegExp _ipv4Pattern =
RegExp(r'\b(\d{1,3})([.-])(\d{1,3})\2(\d{1,3})\2(\d{1,3})\b');
static final RegExp _ipv4HostPattern =
RegExp(r'^\d{1,3}([.-]\d{1,3}){3}$');
static final RegExp _ipv4Pattern = RegExp(
r'\b(\d{1,3})([.-])(\d{1,3})\2(\d{1,3})\2(\d{1,3})\b',
);
static final RegExp _ipv4HostPattern = RegExp(r'^\d{1,3}([.-]\d{1,3}){3}$');
/// Register a server access token or Plex.tv token for redaction.
static void registerToken(String? token) {
@@ -129,8 +129,9 @@ class LogRedactionManager {
? (url.length / 2).ceil()
: startPreviewLength;
final remainingForEnd = url.length - startLength;
final endLength =
remainingForEnd <= endPreviewLength ? remainingForEnd : endPreviewLength;
final endLength = remainingForEnd <= endPreviewLength
? remainingForEnd
: endPreviewLength;
final start = url.substring(0, startLength);
if (endLength <= 0) {
+32 -21
View File
@@ -146,7 +146,10 @@ class _MediaContextMenuState extends State<MediaContextMenu> {
}
// Add to Playlist (for episodes, movies, shows, and seasons)
if (itemType == 'episode' || itemType == 'movie' || itemType == 'show' || itemType == 'season') {
if (itemType == 'episode' ||
itemType == 'movie' ||
itemType == 'show' ||
itemType == 'season') {
menuActions.add(
_MenuAction(
value: 'add_to_playlist',
@@ -445,9 +448,7 @@ class _MediaContextMenuState extends State<MediaContextMenu> {
// Show dialog to select playlist or create new
final result = await showDialog<String>(
context: context,
builder: (context) => _PlaylistSelectionDialog(
playlists: playlists,
),
builder: (context) => _PlaylistSelectionDialog(playlists: playlists),
);
if (result == null || !context.mounted) return;
@@ -469,7 +470,9 @@ class _MediaContextMenuState extends State<MediaContextMenu> {
}
// Create playlist with the item(s)
appLogger.d('Creating playlist "$playlistName" with URI length: ${itemUri.length}');
appLogger.d(
'Creating playlist "$playlistName" with URI length: ${itemUri.length}',
);
final newPlaylist = await client.createPlaylist(
title: playlistName,
uri: itemUri,
@@ -478,14 +481,14 @@ class _MediaContextMenuState extends State<MediaContextMenu> {
if (context.mounted) {
if (newPlaylist != null) {
appLogger.d('Successfully created playlist: ${newPlaylist.title}');
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(t.playlists.created)),
);
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text(t.playlists.created)));
} else {
appLogger.e('Failed to create playlist - API returned null');
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(t.playlists.errorCreating)),
);
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text(t.playlists.errorCreating)));
}
}
} else {
@@ -499,19 +502,25 @@ class _MediaContextMenuState extends State<MediaContextMenu> {
if (context.mounted) {
if (success) {
appLogger.d('Successfully added item(s) to playlist $result');
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(t.playlists.itemAdded)),
);
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text(t.playlists.itemAdded)));
} else {
appLogger.e('Failed to add item(s) to playlist $result - API returned false');
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(t.playlists.errorAdding)),
appLogger.e(
'Failed to add item(s) to playlist $result - API returned false',
);
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text(t.playlists.errorAdding)));
}
}
}
} catch (e, stackTrace) {
appLogger.e('Error in add to playlist flow', error: e, stackTrace: stackTrace);
appLogger.e(
'Error in add to playlist flow',
error: e,
stackTrace: stackTrace,
);
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
@@ -568,9 +577,11 @@ class _PlaylistSelectionDialog extends StatelessWidget {
: const Icon(Icons.playlist_play),
title: Text(playlist.title),
subtitle: playlist.leafCount != null
? Text(playlist.leafCount == 1
? t.playlists.oneItem
: t.playlists.itemCount(count: playlist.leafCount!))
? Text(
playlist.leafCount == 1
? t.playlists.oneItem
: t.playlists.itemCount(count: playlist.leafCount!),
)
: null,
onTap: playlist.smart
? null // Disable smart playlists
+3 -12
View File
@@ -39,10 +39,7 @@ class PlaylistItemCard extends StatelessWidget {
index: index,
child: const Padding(
padding: EdgeInsets.only(right: 12),
child: Icon(
Icons.drag_indicator,
color: Colors.grey,
),
child: Icon(Icons.drag_indicator, color: Colors.grey),
),
),
@@ -73,10 +70,7 @@ class PlaylistItemCard extends StatelessWidget {
// Subtitle (episode info or type)
Text(
_buildSubtitle(),
style: TextStyle(
fontSize: 13,
color: Colors.grey[400],
),
style: TextStyle(fontSize: 13, color: Colors.grey[400]),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
@@ -104,10 +98,7 @@ class PlaylistItemCard extends StatelessWidget {
if (item.duration != null)
Text(
_formatDuration(item.duration!),
style: TextStyle(
fontSize: 13,
color: Colors.grey[400],
),
style: TextStyle(fontSize: 13, color: Colors.grey[400]),
),
const SizedBox(width: 8),