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 = { '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 []; 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 = { 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; } }