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:
edde746
2026-07-26 06:09:48 +02:00
parent 61344f7862
commit 352b88109b
217 changed files with 6813 additions and 8773 deletions
+131 -1
View File
@@ -1,3 +1,4 @@
import '../i18n/strings.g.dart';
import '../media/media_backend.dart';
import '../media/media_item.dart';
import '../media/media_kind.dart';
@@ -147,7 +148,9 @@ abstract class MetadataEditAdapter {
Future<List<MetadataArtworkOption>> fetchArtwork(MetadataEditDraft draft, MetadataEditField field);
Future<bool> applyArtworkOption(MetadataEditDraft draft, MetadataEditField field, MetadataArtworkOption option);
Future<bool> applyArtworkOption(MetadataEditDraft draft, MetadataEditField field, MetadataArtworkOption option) {
return applyArtworkFromUrl(draft, field, option.sourceUrl);
}
Future<bool> applyArtworkFromUrl(MetadataEditDraft draft, MetadataEditField field, String url);
@@ -160,6 +163,133 @@ abstract class MetadataEditAdapter {
}
}
/// Basic-info fields shared by every backend; only the studio field type differs.
List<MetadataEditField> metadataBasicFields(
MediaKind kind, {
MetadataEditFieldType studioType = MetadataEditFieldType.text,
}) {
return [
MetadataEditField(id: 'title', label: t.metadataEdit.title, type: MetadataEditFieldType.text),
if (kind != MediaKind.season)
MetadataEditField(id: 'titleSort', label: t.metadataEdit.sortTitle, type: MetadataEditFieldType.text),
if (kind == MediaKind.movie || kind == MediaKind.show)
MetadataEditField(id: 'originalTitle', label: t.metadataEdit.originalTitle, type: MetadataEditFieldType.text),
if (kind != MediaKind.season)
MetadataEditField(
id: 'originallyAvailableAt',
label: t.metadataEdit.releaseDate,
type: MetadataEditFieldType.date,
),
if (kind != MediaKind.season)
MetadataEditField(id: 'contentRating', label: t.metadataEdit.contentRating, type: MetadataEditFieldType.text),
if (kind == MediaKind.movie || kind == MediaKind.show)
MetadataEditField(id: 'studio', label: t.metadataEdit.studio, type: studioType),
if (kind == MediaKind.movie || kind == MediaKind.show)
MetadataEditField(id: 'tagline', label: t.metadataEdit.tagline, type: MetadataEditFieldType.text),
MetadataEditField(id: 'summary', label: t.metadataEdit.summary, type: MetadataEditFieldType.multilineText),
];
}
/// Artwork fields shared by every backend; each backend supplies its own artwork
/// key names, which kinds carry a logo, and whether square art exists.
List<MetadataEditField> metadataArtworkFields(
MediaKind kind, {
required String posterKey,
required String backdropKey,
required String logoKey,
String? squareKey,
Set<MediaKind> logoKinds = const {MediaKind.movie, MediaKind.show},
}) {
final fields = <MetadataEditField>[
// Episode "posters" are 16:9 thumbnails, not 2:3 poster art.
kind == MediaKind.episode
? metadataArtworkField(
posterKey,
t.metadataEdit.poster,
t.metadataEdit.selectPoster,
80,
45,
2,
16 / 9,
imageType: ImageType.thumb,
)
: metadataArtworkField(posterKey, t.metadataEdit.poster, t.metadataEdit.selectPoster, 40, 60, 3, 2 / 3),
];
if (kind == MediaKind.movie || kind == MediaKind.show || kind == MediaKind.episode) {
fields.add(
metadataArtworkField(
backdropKey,
t.metadataEdit.background,
t.metadataEdit.selectBackground,
80,
45,
2,
16 / 9,
imageType: ImageType.art,
),
);
}
if (logoKinds.contains(kind)) {
fields.add(
metadataArtworkField(
logoKey,
t.metadataEdit.logo,
t.metadataEdit.selectLogo,
80,
32,
2,
2.5,
fit: MetadataArtworkFit.contain,
imageType: ImageType.logo,
),
);
if (squareKey != null) {
fields.add(
metadataArtworkField(
squareKey,
t.metadataEdit.squareArt,
t.metadataEdit.selectSquareArt,
50,
50,
3,
1,
imageType: ImageType.avatar,
),
);
}
}
return fields;
}
MetadataEditField metadataArtworkField(
String key,
String label,
String title,
double width,
double height,
int columns,
double aspectRatio, {
MetadataArtworkFit fit = MetadataArtworkFit.cover,
ImageType imageType = ImageType.poster,
}) {
return MetadataEditField(
id: 'artwork:$key',
label: label,
type: MetadataEditFieldType.artwork,
saveMode: MetadataEditSaveMode.immediate,
artwork: MetadataArtworkConfig(
key: key,
selectTitle: title,
previewWidth: width,
previewHeight: height,
gridColumns: columns,
gridAspectRatio: aspectRatio,
fit: fit,
imageType: imageType,
),
);
}
bool metadataEditValueEquals(Object? a, Object? b) {
if (identical(a, b)) return true;
if (a is List && b is List) {