Files

319 lines
7.7 KiB
Dart
Raw Permalink Normal View History

2026-07-14 11:11:36 +08:00
import '../../core/contracts/library.dart';
import '../utils/url_utils.dart';
class EmbyImageUrl {
static final RegExp _typedUlidPattern = RegExp(
r'^[a-z][0-9A-HJKMNP-TV-Z]{26}$',
);
static String? primary({
required String baseUrl,
required String token,
required EmbyRawItem item,
int? maxWidth,
int? maxHeight,
int? quality,
String format = 'webp',
}) {
final tag = item.PrimaryImageTag ?? item.ImageTags?['Primary'];
if (tag == null) return null;
return _build(
baseUrl: baseUrl,
itemId: item.Id,
type: 'Primary',
tag: tag,
token: token,
maxWidth: maxWidth,
maxHeight: maxHeight,
quality: quality,
format: format,
);
}
static String? library({
required String baseUrl,
required String token,
required EmbyRawLibrary lib,
int? maxHeight,
int? quality,
}) {
final tag = lib.ImageTags?['Primary'];
if (tag == null) return null;
return _build(
baseUrl: baseUrl,
itemId: lib.Id,
type: 'Primary',
tag: tag,
token: token,
maxHeight: maxHeight,
quality: quality,
);
}
static String? backdrop({
required String baseUrl,
required String token,
required EmbyRawItem item,
int? maxWidth,
int? quality,
}) {
final tags = item.BackdropImageTags ?? const [];
if (tags.isEmpty) return null;
return _build(
baseUrl: baseUrl,
itemId: item.Id,
type: 'Backdrop',
tag: tags.first,
token: token,
maxWidth: maxWidth,
quality: quality,
);
}
static String detailBanner({
required String baseUrl,
required String token,
required EmbyRawItem item,
}) {
final artworkItemId = (item.SeriesId?.isNotEmpty ?? false)
? item.SeriesId!
: item.Id;
return backdrop(
baseUrl: baseUrl,
token: token,
item: item,
maxWidth: 1600,
quality: 90,
) ??
fanart(baseUrl: baseUrl, token: token, itemId: artworkItemId);
}
static String fanart({
required String baseUrl,
required String token,
required String itemId,
}) {
return _buildDirectImage(
baseUrl: baseUrl,
itemId: itemId,
imageKind: 'fanart',
extension: 'jpg',
token: token,
);
}
static String? logo({
required String baseUrl,
required String token,
required String itemId,
String? tag,
}) {
if (tag != null) {
return _build(
baseUrl: baseUrl,
itemId: itemId,
type: 'Logo',
tag: tag,
token: token,
);
}
final base = stripTrailingSlash(baseUrl);
return '$base/Items/$itemId/Images/Logo?api_key=$token';
}
static String? thumb({
required String baseUrl,
required String token,
required EmbyRawItem item,
int? maxWidth,
int? quality,
}) {
final tag = item.ImageTags?['Thumb'];
if (tag != null) {
return _build(
baseUrl: baseUrl,
itemId: item.Id,
type: 'Thumb',
tag: tag,
token: token,
maxWidth: maxWidth,
quality: quality,
);
}
return primary(
baseUrl: baseUrl,
token: token,
item: item,
quality: quality,
);
}
static String primaryById({
required String baseUrl,
required String token,
required String itemId,
int? maxHeight,
int? quality,
}) {
final base = stripTrailingSlash(baseUrl);
final params = <String, String>{
'api_key': token,
if (maxHeight != null) 'maxHeight': '$maxHeight',
'quality': '${quality ?? 90}',
'format': 'webp',
};
final query = params.entries.map((e) => '${e.key}=${e.value}').join('&');
return '$base/Items/$itemId/Images/Primary?$query';
}
static String? season({
required String baseUrl,
required String token,
required EmbyRawSeason season,
int? maxHeight,
int? quality,
}) {
final tag = season.PrimaryImageTag ?? season.ImageTags?['Primary'];
if (tag == null) return null;
return _build(
baseUrl: baseUrl,
itemId: season.Id,
type: 'Primary',
tag: tag,
token: token,
maxHeight: maxHeight,
quality: quality,
);
}
static String? landscapeCard({
required String baseUrl,
required String token,
required EmbyRawItem item,
int? maxWidth,
int? quality,
}) {
String? pick(String? itemId, String type, String? tag) =>
(itemId == null || tag == null)
? null
: _build(
baseUrl: baseUrl,
itemId: itemId,
type: type,
tag: tag,
token: token,
maxWidth: maxWidth,
quality: quality,
);
String? self(String type, String? tag) => pick(item.Id, type, tag);
final primary = item.PrimaryImageTag ?? item.ImageTags?['Primary'];
final thumb = item.ImageTags?['Thumb'];
final backdropTags = item.BackdropImageTags ?? const <String>[];
final backdrop = backdropTags.isEmpty ? null : backdropTags.first;
if (item.Type == 'Movie') {
return self('Backdrop', backdrop) ??
self('Thumb', thumb) ??
self('Primary', primary) ??
_build(
baseUrl: baseUrl,
itemId: item.Id,
type: 'Primary',
tag: null,
token: token,
maxWidth: maxWidth,
quality: quality,
);
}
final extra = item.extra;
final parentBackdropTags = extra['ParentBackdropImageTags'];
return self('Primary', primary) ??
self('Thumb', thumb) ??
self('Backdrop', backdrop) ??
pick(
extra['ParentThumbItemId'] as String?,
'Thumb',
extra['ParentThumbImageTag'] as String?,
) ??
pick(
extra['ParentBackdropItemId'] as String?,
'Backdrop',
parentBackdropTags is List && parentBackdropTags.isNotEmpty
? parentBackdropTags.first as String
: null,
) ??
pick(
extra['SeriesId'] as String?,
'Primary',
extra['SeriesPrimaryImageTag'] as String?,
);
}
static int targetPixelWidth(
double logicalWidth,
double devicePixelRatio, {
double maxScale = 3.0,
}) {
final scale = devicePixelRatio.clamp(1.0, maxScale);
return (logicalWidth * scale).ceil();
}
static String _build({
required String baseUrl,
required String itemId,
required String type,
required String? tag,
required String token,
int? maxWidth,
int? maxHeight,
int? quality,
String format = 'webp',
}) {
final base = stripTrailingSlash(baseUrl);
final params = <String, String>{
if (tag != null) 'tag': tag,
'api_key': token,
if (maxWidth != null) 'maxWidth': '$maxWidth',
if (maxHeight != null) 'maxHeight': '$maxHeight',
'quality': '${quality ?? 90}',
'format': format,
};
final query = params.entries.map((e) => '${e.key}=${e.value}').join('&');
return '$base/Items/$itemId/Images/$type?$query';
}
static String _buildDirectImage({
required String baseUrl,
required String itemId,
required String imageKind,
required String extension,
required String token,
}) {
final uri = Uri.parse(baseUrl);
final origin =
'${uri.scheme}://${uri.host}${uri.hasPort ? ':${uri.port}' : ''}';
final id = Uri.encodeComponent(_directImageId(itemId));
final key = Uri.encodeQueryComponent(token);
return '$origin/img/i/$imageKind/$id.$extension?api_key=$key';
}
static String _directImageId(String itemId) {
if (_typedUlidPattern.hasMatch(itemId)) {
return itemId.substring(1);
}
return itemId;
}
}