refactor: extract shared mixins and helpers, drop dead abstractions
Introduces shared seams for paginated views, D-pad reorder, media control routing, async singletons and the device method channel, then points the open-coded copies at them. Also removes unused models and duplicated provider/server plumbing, folds the twice-implemented artifact store in the server, and factors the repeated Flutter toolchain prologue in CI into a composite action.
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
// ignore_for_file: invalid_annotation_target
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
import '../utils/media_server_http_client.dart' show AbortController;
|
||||
import 'media_kind.dart';
|
||||
|
||||
part 'library_query.freezed.dart';
|
||||
@@ -84,3 +85,33 @@ int fallbackPageTotal({required int offset, required int itemCount, int? request
|
||||
final fullPage = requestedSize != null && requestedSize > 0 && itemCount >= requestedSize;
|
||||
return offset + itemCount + (fullPage ? 1 : 0);
|
||||
}
|
||||
|
||||
/// Walk every page of a paginated endpoint and concatenate the results.
|
||||
///
|
||||
/// [fetchPage] receives a zero-based offset and [pageSize] and is called until
|
||||
/// a page comes back empty, the accumulated count reaches the page's
|
||||
/// [LibraryPage.totalCount], or — when [stopOnShortPage] is set — a page comes
|
||||
/// back shorter than [pageSize]. The short-page break is for backends whose
|
||||
/// total is unreliable; leave it off when the total is authoritative.
|
||||
///
|
||||
/// [abort] is checked before and after every request. Errors propagate.
|
||||
Future<List<T>> drainPages<T>(
|
||||
Future<LibraryPage<T>> Function(int start, int size) fetchPage, {
|
||||
required int pageSize,
|
||||
AbortController? abort,
|
||||
bool stopOnShortPage = false,
|
||||
}) async {
|
||||
final all = <T>[];
|
||||
var start = 0;
|
||||
while (true) {
|
||||
abort?.throwIfAborted();
|
||||
final page = await fetchPage(start, pageSize);
|
||||
abort?.throwIfAborted();
|
||||
if (page.items.isEmpty) break;
|
||||
all.addAll(page.items);
|
||||
start += page.items.length;
|
||||
if (start >= page.totalCount) break;
|
||||
if (stopOnShortPage && page.items.length < pageSize) break;
|
||||
}
|
||||
return all;
|
||||
}
|
||||
|
||||
@@ -212,55 +212,35 @@ class ServerCapabilities {
|
||||
audioTranscoding: true,
|
||||
);
|
||||
|
||||
ServerCapabilities copyWith({
|
||||
bool? serverSidePlayQueue,
|
||||
bool? serverSidePlaylists,
|
||||
bool? liveTv,
|
||||
bool? liveTvDvr,
|
||||
bool? subtitleSearch,
|
||||
bool? videoTranscoding,
|
||||
bool? serverSideSync,
|
||||
bool? richHubs,
|
||||
bool? numericUserRating,
|
||||
bool? userFavorites,
|
||||
bool? continueWatchingRemoval,
|
||||
bool? externalSubtitleSearch,
|
||||
bool? trackPreferencePersistence,
|
||||
bool? endpointFailover,
|
||||
bool? offlineWatchQueue,
|
||||
bool? discordRpc,
|
||||
bool? richMetadataEdit,
|
||||
AlphaBarMode? alphaBar,
|
||||
bool? scrubThumbnails,
|
||||
bool? folderGrouping,
|
||||
bool? lyrics,
|
||||
bool? instantMix,
|
||||
bool? audioTranscoding,
|
||||
}) {
|
||||
/// Every flag here is fixed per backend *kind* except [videoTranscoding],
|
||||
/// which Plex probes per server (`PlexClient.capabilities`) — so that is the
|
||||
/// only override this type needs. Widen the parameter list if another flag
|
||||
/// ever becomes a runtime probe.
|
||||
ServerCapabilities copyWith({bool? videoTranscoding}) {
|
||||
return ServerCapabilities(
|
||||
serverSidePlayQueue: serverSidePlayQueue ?? this.serverSidePlayQueue,
|
||||
serverSidePlaylists: serverSidePlaylists ?? this.serverSidePlaylists,
|
||||
liveTv: liveTv ?? this.liveTv,
|
||||
liveTvDvr: liveTvDvr ?? this.liveTvDvr,
|
||||
subtitleSearch: subtitleSearch ?? this.subtitleSearch,
|
||||
serverSidePlayQueue: serverSidePlayQueue,
|
||||
serverSidePlaylists: serverSidePlaylists,
|
||||
liveTv: liveTv,
|
||||
liveTvDvr: liveTvDvr,
|
||||
subtitleSearch: subtitleSearch,
|
||||
videoTranscoding: videoTranscoding ?? this.videoTranscoding,
|
||||
serverSideSync: serverSideSync ?? this.serverSideSync,
|
||||
richHubs: richHubs ?? this.richHubs,
|
||||
numericUserRating: numericUserRating ?? this.numericUserRating,
|
||||
userFavorites: userFavorites ?? this.userFavorites,
|
||||
continueWatchingRemoval: continueWatchingRemoval ?? this.continueWatchingRemoval,
|
||||
externalSubtitleSearch: externalSubtitleSearch ?? this.externalSubtitleSearch,
|
||||
trackPreferencePersistence: trackPreferencePersistence ?? this.trackPreferencePersistence,
|
||||
endpointFailover: endpointFailover ?? this.endpointFailover,
|
||||
offlineWatchQueue: offlineWatchQueue ?? this.offlineWatchQueue,
|
||||
discordRpc: discordRpc ?? this.discordRpc,
|
||||
richMetadataEdit: richMetadataEdit ?? this.richMetadataEdit,
|
||||
alphaBar: alphaBar ?? this.alphaBar,
|
||||
scrubThumbnails: scrubThumbnails ?? this.scrubThumbnails,
|
||||
folderGrouping: folderGrouping ?? this.folderGrouping,
|
||||
lyrics: lyrics ?? this.lyrics,
|
||||
instantMix: instantMix ?? this.instantMix,
|
||||
audioTranscoding: audioTranscoding ?? this.audioTranscoding,
|
||||
serverSideSync: serverSideSync,
|
||||
richHubs: richHubs,
|
||||
numericUserRating: numericUserRating,
|
||||
userFavorites: userFavorites,
|
||||
continueWatchingRemoval: continueWatchingRemoval,
|
||||
externalSubtitleSearch: externalSubtitleSearch,
|
||||
trackPreferencePersistence: trackPreferencePersistence,
|
||||
endpointFailover: endpointFailover,
|
||||
offlineWatchQueue: offlineWatchQueue,
|
||||
discordRpc: discordRpc,
|
||||
richMetadataEdit: richMetadataEdit,
|
||||
alphaBar: alphaBar,
|
||||
scrubThumbnails: scrubThumbnails,
|
||||
folderGrouping: folderGrouping,
|
||||
lyrics: lyrics,
|
||||
instantMix: instantMix,
|
||||
audioTranscoding: audioTranscoding,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user