319 lines
7.7 KiB
Dart
319 lines
7.7 KiB
Dart
|
|
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;
|
||
|
|
}
|
||
|
|
}
|