Initial commit
This commit is contained in:
@@ -0,0 +1,164 @@
|
||||
import '../../core/contracts/library.dart';
|
||||
import '../../shared/utils/cross_server_search_key.dart';
|
||||
import '../../shared/utils/emby_ticks.dart';
|
||||
import '../../shared/utils/format_utils.dart';
|
||||
import '../../shared/utils/tmdb_key_utils.dart';
|
||||
|
||||
|
||||
class TmdbMediaRef {
|
||||
final String id;
|
||||
final String mediaType;
|
||||
|
||||
const TmdbMediaRef({required this.id, required this.mediaType});
|
||||
}
|
||||
|
||||
List<String> buildDetailMetaParts(
|
||||
EmbyRawItem item, {
|
||||
EmbyRawMediaSource? source,
|
||||
}) {
|
||||
final parts = <String>[];
|
||||
final seriesName = item.SeriesName;
|
||||
if (seriesName != null && seriesName.isNotEmpty) {
|
||||
parts.add(seriesName);
|
||||
}
|
||||
if (item.ProductionYear != null) {
|
||||
parts.add('${item.ProductionYear}');
|
||||
}
|
||||
final episode = episodeLabel(item);
|
||||
if (episode != null) {
|
||||
parts.add(episode);
|
||||
}
|
||||
final runtime = item.RunTimeTicks;
|
||||
if (runtime != null && runtime > 0) {
|
||||
final label = formatRuntimeMinutes(runtime ~/ kTicksPerMinute);
|
||||
if (label != null) parts.add(label);
|
||||
}
|
||||
if (source != null) {
|
||||
final videoStream = findVideoStream(source);
|
||||
if (videoStream != null) {
|
||||
final h = videoStream.extra['Height'] as int? ?? source.Height;
|
||||
final range = videoStream.extra['VideoRange'] as String?;
|
||||
if (h != null) {
|
||||
final label = heightToLabel(h);
|
||||
parts.add(range != null && range.isNotEmpty ? '$label $range' : label);
|
||||
}
|
||||
}
|
||||
final size = source.Size;
|
||||
if (size != null && size > 0) parts.add(formatFileSize(size));
|
||||
}
|
||||
return parts;
|
||||
}
|
||||
|
||||
EmbyRawMediaStream? findVideoStream(EmbyRawMediaSource source) {
|
||||
final streams = source.MediaStreams;
|
||||
if (streams == null) return null;
|
||||
for (final s in streams) {
|
||||
if (s.Type == 'Video') return s;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
String heightToLabel(int h) {
|
||||
if (h >= 2160) return '4K';
|
||||
if (h >= 1440) return '2K';
|
||||
if (h >= 1080) return '1080P';
|
||||
if (h >= 720) return '720P';
|
||||
return '${h}P';
|
||||
}
|
||||
|
||||
String? episodeLabel(EmbyRawItem item) {
|
||||
final seasonName = item.SeasonName;
|
||||
final index = item.IndexNumber;
|
||||
if (seasonName == null && index == null) return null;
|
||||
if (seasonName != null && seasonName.isNotEmpty && index != null) {
|
||||
return '$seasonName ${formatEpisodeNumber(index)}';
|
||||
}
|
||||
if (seasonName != null && seasonName.isNotEmpty) return seasonName;
|
||||
return formatEpisodeNumber(index);
|
||||
}
|
||||
|
||||
String detailNavTitle(EmbyRawItem item, EmbyRawItem? seriesItem) {
|
||||
if (item.Type != 'Episode') return item.Name;
|
||||
final seriesName = item.SeriesName;
|
||||
if (seriesName != null && seriesName.isNotEmpty) return seriesName;
|
||||
final loadedSeriesName = seriesItem?.Name;
|
||||
if (loadedSeriesName != null && loadedSeriesName.isNotEmpty) {
|
||||
return loadedSeriesName;
|
||||
}
|
||||
return item.Name;
|
||||
}
|
||||
|
||||
String? seriesIdForEpisode(EmbyRawItem item, String routeItemId) {
|
||||
if (item.Type != 'Episode') return null;
|
||||
final seriesId = item.SeriesId;
|
||||
if (seriesId != null && seriesId.isNotEmpty) return seriesId;
|
||||
if (routeItemId.isNotEmpty && routeItemId != item.Id) return routeItemId;
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
Map<String, String> lowercaseProviderIds(Map<String, String> ids) {
|
||||
return {
|
||||
for (final e in ids.entries)
|
||||
if (e.key.isNotEmpty && e.value.isNotEmpty) e.key.toLowerCase(): e.value,
|
||||
};
|
||||
}
|
||||
|
||||
List<String> extractGenres(EmbyRawItem item) {
|
||||
final genresRaw = item.extra['Genres'];
|
||||
if (genresRaw is List) {
|
||||
return genresRaw.whereType<String>().where((g) => g.isNotEmpty).toList();
|
||||
}
|
||||
return const [];
|
||||
}
|
||||
|
||||
double? detailProgressPercent(EmbyRawItem item) {
|
||||
final percent = playbackProgress(item) * 100;
|
||||
return percent < 1 ? null : percent;
|
||||
}
|
||||
|
||||
String tmdbIdFromDetailItem(EmbyRawItem item) => tmdbIdFromItem(item);
|
||||
|
||||
TmdbMediaRef? tmdbMediaRefFor(EmbyRawItem item, EmbyRawItem? tmdbSeriesItem) {
|
||||
final String mediaType;
|
||||
final String tmdbId;
|
||||
switch (item.Type) {
|
||||
case 'Movie':
|
||||
mediaType = 'movie';
|
||||
tmdbId = tmdbIdFromDetailItem(item);
|
||||
case 'Series':
|
||||
mediaType = 'tv';
|
||||
tmdbId = tmdbIdFromDetailItem(item);
|
||||
case 'Episode':
|
||||
final seriesItem = tmdbSeriesItem;
|
||||
if (seriesItem == null) return null;
|
||||
mediaType = 'tv';
|
||||
tmdbId = tmdbIdFromDetailItem(seriesItem);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
if (tmdbId.isEmpty) return null;
|
||||
return TmdbMediaRef(id: tmdbId, mediaType: mediaType);
|
||||
}
|
||||
|
||||
CrossServerSearchReq? buildCrossServerReqForDetail(
|
||||
EmbyRawItem displayItem,
|
||||
EmbyRawItem? seriesItem,
|
||||
) {
|
||||
final providerIds = providerIdsOfItem(displayItem);
|
||||
if (displayItem.Type == 'Movie') {
|
||||
if (providerIds.isEmpty && displayItem.Name.isEmpty) return null;
|
||||
return CrossServerSearchReq(
|
||||
anchorType: 'Movie',
|
||||
itemName: displayItem.Name,
|
||||
providerIds: providerIds,
|
||||
);
|
||||
}
|
||||
if (displayItem.Type == 'Episode') {
|
||||
return buildEpisodeCrossServerReq(
|
||||
episode: displayItem,
|
||||
seriesItem: seriesItem,
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
Reference in New Issue
Block a user