106 lines
3.2 KiB
Dart
106 lines
3.2 KiB
Dart
|
|
import '../../core/contracts/tmdb.dart';
|
||
|
|
import 'tmdb_image_url.dart';
|
||
|
|
|
||
|
|
class TmdbImageSelector {
|
||
|
|
static TmdbImage? bestImage(
|
||
|
|
List<TmdbImage> images, {
|
||
|
|
bool preferNeutral = false,
|
||
|
|
}) {
|
||
|
|
if (images.isEmpty) return null;
|
||
|
|
if (preferNeutral) {
|
||
|
|
final neutrals = images
|
||
|
|
.where((i) => i.filePath.isNotEmpty && i.isLanguageNeutral)
|
||
|
|
.toList();
|
||
|
|
if (neutrals.isNotEmpty) return _pickBest(neutrals);
|
||
|
|
}
|
||
|
|
return _pickBest(images);
|
||
|
|
}
|
||
|
|
|
||
|
|
static TmdbImage? _pickBest(List<TmdbImage> images) {
|
||
|
|
TmdbImage? best;
|
||
|
|
for (final image in images) {
|
||
|
|
if (image.filePath.isEmpty) continue;
|
||
|
|
if (best == null || _isBetter(image, best)) best = image;
|
||
|
|
}
|
||
|
|
return best;
|
||
|
|
}
|
||
|
|
|
||
|
|
static String? backdropUrl(
|
||
|
|
TmdbImageSet? imageSet,
|
||
|
|
String? imageBaseUrl, {
|
||
|
|
String size = 'original',
|
||
|
|
}) {
|
||
|
|
if (imageBaseUrl == null || imageBaseUrl.isEmpty) return null;
|
||
|
|
final image = bestImage(
|
||
|
|
imageSet?.backdrops ?? const [],
|
||
|
|
preferNeutral: true,
|
||
|
|
);
|
||
|
|
return TmdbImageUrl.backdrop(imageBaseUrl, image?.filePath, size: size);
|
||
|
|
}
|
||
|
|
|
||
|
|
static String? posterUrl(
|
||
|
|
TmdbImageSet? imageSet,
|
||
|
|
String? imageBaseUrl, {
|
||
|
|
String size = 'w500',
|
||
|
|
}) {
|
||
|
|
if (imageBaseUrl == null || imageBaseUrl.isEmpty) return null;
|
||
|
|
final image = bestImage(imageSet?.posters ?? const []);
|
||
|
|
return TmdbImageUrl.poster(imageBaseUrl, image?.filePath, size: size);
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
static String? logoUrl(
|
||
|
|
TmdbImageSet? imageSet,
|
||
|
|
String? imageBaseUrl, {
|
||
|
|
String? language,
|
||
|
|
String size = 'w500',
|
||
|
|
}) {
|
||
|
|
if (imageBaseUrl == null || imageBaseUrl.isEmpty) return null;
|
||
|
|
final logos = imageSet?.logos ?? const <TmdbImage>[];
|
||
|
|
if (logos.isEmpty) return null;
|
||
|
|
|
||
|
|
final lang = _iso639Of(language);
|
||
|
|
final image =
|
||
|
|
(lang != null ? _pickBest(_withLang(logos, lang)) : null) ??
|
||
|
|
_pickBest(_withLang(logos, 'en')) ??
|
||
|
|
_pickBest(logos.where((i) => i.isLanguageNeutral).toList()) ??
|
||
|
|
_pickBest(logos);
|
||
|
|
return TmdbImageUrl.logo(imageBaseUrl, image?.filePath, size: size);
|
||
|
|
}
|
||
|
|
|
||
|
|
static List<TmdbImage> _withLang(List<TmdbImage> images, String iso) =>
|
||
|
|
images.where((i) => i.iso639 == iso).toList();
|
||
|
|
|
||
|
|
|
||
|
|
static String? _iso639Of(String? language) {
|
||
|
|
if (language == null || language.isEmpty) return null;
|
||
|
|
final dash = language.indexOf('-');
|
||
|
|
final code = dash >= 0 ? language.substring(0, dash) : language;
|
||
|
|
return code.isEmpty ? null : code.toLowerCase();
|
||
|
|
}
|
||
|
|
|
||
|
|
static List<String> buildFallbackUrls({
|
||
|
|
required String? primaryUrl,
|
||
|
|
required Iterable<String?> candidates,
|
||
|
|
}) {
|
||
|
|
final urls = <String>[];
|
||
|
|
for (final url in candidates) {
|
||
|
|
if (url == null || url.isEmpty || url == primaryUrl) continue;
|
||
|
|
if (!urls.contains(url)) urls.add(url);
|
||
|
|
}
|
||
|
|
return urls;
|
||
|
|
}
|
||
|
|
|
||
|
|
static bool _isBetter(TmdbImage candidate, TmdbImage current) {
|
||
|
|
final voteCompare = (candidate.voteAverage ?? 0).compareTo(
|
||
|
|
current.voteAverage ?? 0,
|
||
|
|
);
|
||
|
|
if (voteCompare != 0) return voteCompare > 0;
|
||
|
|
final pixelsCompare = (candidate.width * candidate.height).compareTo(
|
||
|
|
current.width * current.height,
|
||
|
|
);
|
||
|
|
if (pixelsCompare != 0) return pixelsCompare > 0;
|
||
|
|
return candidate.filePath.compareTo(current.filePath) < 0;
|
||
|
|
}
|
||
|
|
}
|