Fladder/lib/providers/service_provider.dart
PartyDonut 3b4b8a9101
fix: Incorrect playback reporting (#221)
Co-authored-by: PartyDonut <PartyDonut@users.noreply.github.com>
2025-02-08 17:05:53 +01:00

918 lines
27 KiB
Dart

import 'dart:developer';
import 'dart:typed_data';
import 'package:chopper/chopper.dart';
import 'package:collection/collection.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:path/path.dart';
import 'package:fladder/fake/fake_jellyfin_open_api.dart';
import 'package:fladder/jellyfin/enum_models.dart';
import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart';
import 'package:fladder/models/account_model.dart';
import 'package:fladder/models/credentials_model.dart';
import 'package:fladder/models/item_base_model.dart';
import 'package:fladder/models/items/media_segments_model.dart';
import 'package:fladder/models/items/trick_play_model.dart';
import 'package:fladder/providers/auth_provider.dart';
import 'package:fladder/providers/image_provider.dart';
import 'package:fladder/providers/user_provider.dart';
import 'package:fladder/util/jellyfin_extension.dart';
class ServerQueryResult {
final List<BaseItemDto> original;
final List<ItemBaseModel> items;
final int? totalRecordCount;
final int? startIndex;
ServerQueryResult({
required this.original,
required this.items,
this.totalRecordCount,
this.startIndex,
});
factory ServerQueryResult.fromBaseQuery(
BaseItemDtoQueryResult baseQuery,
Ref ref,
) {
return ServerQueryResult(
original: baseQuery.items ?? [],
items: baseQuery.items
?.map(
(e) => ItemBaseModel.fromBaseDto(e, ref),
)
.toList() ??
[],
totalRecordCount: baseQuery.totalRecordCount,
startIndex: baseQuery.startIndex,
);
}
ServerQueryResult copyWith({
List<BaseItemDto>? original,
List<ItemBaseModel>? items,
int? totalRecordCount,
int? startIndex,
}) {
return ServerQueryResult(
original: original ?? this.original,
items: items ?? this.items,
totalRecordCount: totalRecordCount ?? this.totalRecordCount,
startIndex: startIndex ?? this.startIndex,
);
}
}
class JellyService {
JellyService(this.ref, this._api);
final JellyfinOpenApi _api;
JellyfinOpenApi get api {
var authServer = ref.read(authProvider).tempCredentials.server;
var currentServer = ref.read(userProvider)?.credentials.server;
if ((authServer.isNotEmpty ? authServer : currentServer) == FakeHelper.fakeTestServerUrl) {
return FakeJellyfinOpenApi();
} else {
return _api;
}
}
final Ref ref;
AccountModel? get account => ref.read(userProvider);
Future<Response<ItemBaseModel>> usersUserIdItemsItemIdGet({
String? itemId,
}) async {
final response = await api.itemsItemIdGet(
userId: account?.id,
itemId: itemId,
);
return response.copyWith(body: ItemBaseModel.fromBaseDto(response.bodyOrThrow, ref));
}
Future<Response<BaseItemDto>> usersUserIdItemsItemIdGetBaseItem({
String? itemId,
}) async {
final response = await api.itemsItemIdGet(
userId: account?.id,
itemId: itemId,
);
return response;
}
Future<Response<ServerQueryResult>> itemsGet({
String? maxOfficialRating,
bool? hasThemeSong,
bool? hasThemeVideo,
bool? hasSubtitles,
bool? hasSpecialFeature,
bool? hasTrailer,
String? adjacentTo,
int? parentIndexNumber,
bool? hasParentalRating,
bool? isHd,
bool? is4K,
List<LocationType>? locationTypes,
List<LocationType>? excludeLocationTypes,
bool? isMissing,
bool? isUnaired,
num? minCommunityRating,
num? minCriticRating,
DateTime? minPremiereDate,
DateTime? minDateLastSaved,
DateTime? minDateLastSavedForUser,
DateTime? maxPremiereDate,
bool? hasOverview,
bool? hasImdbId,
bool? hasTmdbId,
bool? hasTvdbId,
bool? isMovie,
bool? isSeries,
bool? isNews,
bool? isKids,
bool? isSports,
List<String>? excludeItemIds,
int? startIndex,
int? limit,
bool? recursive,
String? searchTerm,
List<SortOrder>? sortOrder,
String? parentId,
List<ItemFields>? fields,
List<BaseItemKind>? excludeItemTypes,
List<BaseItemKind>? includeItemTypes,
List<ItemFilter>? filters,
bool? isFavorite,
List<MediaType>? mediaTypes,
List<ImageType>? imageTypes,
List<ItemSortBy>? sortBy,
bool? isPlayed,
List<String>? genres,
List<String>? officialRatings,
List<String>? tags,
List<int>? years,
bool? enableUserData,
int? imageTypeLimit,
List<ImageType>? enableImageTypes,
String? person,
List<String>? personIds,
List<String>? personTypes,
List<String>? studios,
List<String>? artists,
List<String>? excludeArtistIds,
List<String>? artistIds,
List<String>? albumArtistIds,
List<String>? contributingArtistIds,
List<String>? albums,
List<String>? albumIds,
List<String>? ids,
List<VideoType>? videoTypes,
String? minOfficialRating,
bool? isLocked,
bool? isPlaceHolder,
bool? hasOfficialRating,
bool? collapseBoxSetItems,
int? minWidth,
int? minHeight,
int? maxWidth,
int? maxHeight,
bool? is3D,
List<SeriesStatus>? seriesStatus,
String? nameStartsWithOrGreater,
String? nameStartsWith,
String? nameLessThan,
List<String>? studioIds,
List<String>? genreIds,
bool? enableTotalRecordCount,
bool? enableImages,
}) async {
final response = await api.itemsGet(
userId: account?.id,
maxOfficialRating: maxOfficialRating,
hasThemeSong: hasThemeSong,
hasThemeVideo: hasThemeVideo,
hasSubtitles: hasSubtitles,
hasSpecialFeature: hasSpecialFeature,
hasTrailer: hasTrailer,
adjacentTo: adjacentTo,
parentIndexNumber: parentIndexNumber,
hasParentalRating: hasParentalRating,
isHd: isHd,
is4K: is4K,
locationTypes: locationTypes,
excludeLocationTypes: excludeLocationTypes,
isMissing: isMissing,
isUnaired: isUnaired,
minCommunityRating: minCommunityRating,
minCriticRating: minCriticRating,
minPremiereDate: minPremiereDate,
minDateLastSaved: minDateLastSaved,
minDateLastSavedForUser: minDateLastSavedForUser,
maxPremiereDate: maxPremiereDate,
hasOverview: hasOverview,
hasImdbId: hasImdbId,
hasTmdbId: hasTmdbId,
hasTvdbId: hasTvdbId,
isMovie: isMovie,
isSeries: isSeries,
isNews: isNews,
isKids: isKids,
isSports: isSports,
excludeItemIds: excludeItemIds,
startIndex: startIndex,
limit: limit,
recursive: recursive,
searchTerm: searchTerm,
sortOrder: sortOrder,
sortBy: sortBy,
parentId: parentId,
fields: {...?fields, ItemFields.candelete, ItemFields.candownload}.toList(),
excludeItemTypes: excludeItemTypes,
includeItemTypes: includeItemTypes,
filters: filters,
isFavorite: isFavorite,
mediaTypes: mediaTypes,
imageTypes: imageTypes,
isPlayed: isPlayed,
genres: genres,
officialRatings: officialRatings,
tags: tags,
years: years,
enableUserData: enableUserData,
imageTypeLimit: imageTypeLimit,
enableImageTypes: enableImageTypes,
person: person,
personIds: personIds,
personTypes: personTypes,
studios: studios,
artists: artists,
excludeArtistIds: excludeArtistIds,
artistIds: artistIds,
albumArtistIds: albumArtistIds,
contributingArtistIds: contributingArtistIds,
albums: albums,
albumIds: albumIds,
ids: ids,
videoTypes: videoTypes,
minOfficialRating: minOfficialRating,
isLocked: isLocked,
isPlaceHolder: isPlaceHolder,
hasOfficialRating: hasOfficialRating,
collapseBoxSetItems: collapseBoxSetItems,
minWidth: minWidth,
minHeight: minHeight,
maxWidth: maxWidth,
maxHeight: maxHeight,
is3D: is3D,
seriesStatus: seriesStatus,
nameStartsWithOrGreater: nameStartsWithOrGreater,
nameStartsWith: nameStartsWith,
nameLessThan: nameLessThan,
studioIds: studioIds,
genreIds: genreIds,
enableTotalRecordCount: enableTotalRecordCount,
enableImages: enableImages,
);
return response.copyWith(
body: ServerQueryResult.fromBaseQuery(response.bodyOrThrow, ref),
);
}
Future<Response<List<ItemBaseModel>>> personsGet({
String? searchTerm,
int? limit,
bool? isFavorite,
}) async {
final response = await api.personsGet(
userId: account?.id,
limit: limit,
isFavorite: isFavorite,
);
return response.copyWith(
body: response.body?.items
?.map(
(e) => ItemBaseModel.fromBaseDto(e, ref),
)
.toList() ??
[],
);
}
Future<Response<List<ImageInfo>>> itemsItemIdImagesGet({
String? itemId,
bool? isFavorite,
}) async {
final response = await api.itemsItemIdImagesGet(itemId: itemId);
return response;
}
Future<Response<MetadataEditorInfo>> itemsItemIdMetadataEditorGet({
String? itemId,
}) async {
return api.itemsItemIdMetadataEditorGet(itemId: itemId);
}
Future<Response<RemoteImageResult>> itemsItemIdRemoteImagesGet({
String? itemId,
ImageType? type,
bool? includeAllLanguages,
}) async {
return api.itemsItemIdRemoteImagesGet(
itemId: itemId,
type: ItemsItemIdRemoteImagesGetType.values.firstWhereOrNull(
(element) => element.value == type?.value,
),
includeAllLanguages: includeAllLanguages,
);
}
Future<Response> itemsItemIdPost({
String? itemId,
required BaseItemDto? body,
}) async {
return api.itemsItemIdPost(
itemId: itemId,
body: body,
);
}
Future<Response<dynamic>?> itemIdImagesImageTypePost(
ImageType type,
String itemId,
Uint8List data,
) async {
return api.itemIdImagesImageTypePost(
type,
itemId,
data,
);
}
Future<Response> itemsItemIdRemoteImagesDownloadPost({
required String? itemId,
required ImageType? type,
String? imageUrl,
}) async {
return api.itemsItemIdRemoteImagesDownloadPost(
itemId: itemId,
type: ItemsItemIdRemoteImagesDownloadPostType.values.firstWhereOrNull(
(element) => element.value == type?.value,
),
imageUrl: imageUrl,
);
}
Future<Response> itemsItemIdImagesImageTypeDelete({
required String? itemId,
required ImageType? imageType,
int? imageIndex,
}) async {
return api.itemsItemIdImagesImageTypeDelete(
itemId: itemId,
imageType: ItemsItemIdImagesImageTypeDeleteImageType.values.firstWhereOrNull(
(element) => element.value == imageType?.value,
),
imageIndex: imageIndex,
);
}
Future<Response<BaseItemDtoQueryResult>> usersUserIdItemsResumeGet({
int? startIndex,
int? limit,
String? searchTerm,
String? parentId,
List<ItemFields>? fields,
List<MediaType>? mediaTypes,
bool? enableUserData,
bool? enableTotalRecordCount,
List<ImageType>? enableImageTypes,
List<BaseItemKind>? excludeItemTypes,
List<BaseItemKind>? includeItemTypes,
}) async {
return api.userItemsResumeGet(
userId: account?.id,
searchTerm: searchTerm,
parentId: parentId,
limit: limit,
fields: fields,
mediaTypes: mediaTypes,
enableTotalRecordCount: enableTotalRecordCount,
enableImageTypes: enableImageTypes,
enableUserData: enableUserData,
includeItemTypes: includeItemTypes,
excludeItemTypes: excludeItemTypes,
);
}
Future<Response<List<BaseItemDto>>> usersUserIdItemsLatestGet({
String? parentId,
List<ItemFields>? fields,
List<BaseItemKind>? includeItemTypes,
bool? isPlayed,
bool? enableImages,
int? imageTypeLimit,
List<ImageType>? enableImageTypes,
bool? enableUserData,
int? limit,
bool? groupItems,
}) async {
return api.itemsLatestGet(
parentId: parentId,
userId: account?.id,
fields: fields,
includeItemTypes: includeItemTypes,
isPlayed: isPlayed,
enableImages: enableImages,
imageTypeLimit: imageTypeLimit,
enableImageTypes: enableImageTypes,
enableUserData: enableUserData,
limit: limit,
groupItems: groupItems,
);
}
Future<Response<List<RecommendationDto>>> moviesRecommendationsGet({
String? parentId,
List<ItemFields>? fields,
int? categoryLimit,
int? itemLimit,
}) async {
return api.moviesRecommendationsGet(
userId: account?.id,
parentId: parentId,
fields: fields,
categoryLimit: categoryLimit,
itemLimit: itemLimit,
);
}
Future<Response<BaseItemDtoQueryResult>> showsNextUpGet({
int? startIndex,
int? limit,
String? parentId,
DateTime? nextUpDateCutoff,
List<ItemFields>? fields,
bool? enableUserData,
List<ImageType>? enableImageTypes,
int? imageTypeLimit,
}) async {
return api.showsNextUpGet(
userId: account?.id,
parentId: parentId,
limit: limit,
fields: fields,
enableResumable: false,
enableRewatching: false,
disableFirstEpisode: false,
nextUpDateCutoff: nextUpDateCutoff,
enableImageTypes: enableImageTypes,
enableUserData: enableUserData,
imageTypeLimit: imageTypeLimit,
);
}
Future<Response<BaseItemDtoQueryResult>> genresGet({
String? parentId,
List<ItemSortBy>? sortBy,
List<SortOrder>? sortOrder,
List<BaseItemKind>? includeItemTypes,
}) async {
return api.genresGet(
parentId: parentId,
userId: account?.id,
sortBy: sortBy,
sortOrder: sortOrder,
);
}
Future<Response> sessionsPlayingPost({required PlaybackStartInfo? body}) async => api.sessionsPlayingPost(body: body);
Future<Response> sessionsPlayingStoppedPost({
required PlaybackStopInfo? body,
}) =>
api.sessionsPlayingStoppedPost(body: body);
Future<Response> sessionsPlayingProgressPost({required PlaybackProgressInfo? body}) async =>
api.sessionsPlayingProgressPost(body: body);
Future<Response<PlaybackInfoResponse>> itemsItemIdPlaybackInfoPost({
required String? itemId,
int? maxStreamingBitrate,
int? startTimeTicks,
int? audioStreamIndex,
int? subtitleStreamIndex,
int? maxAudioChannels,
String? mediaSourceId,
String? liveStreamId,
bool? autoOpenLiveStream,
bool? enableDirectPlay,
bool? enableDirectStream,
bool? enableTranscoding,
bool? allowVideoStreamCopy,
bool? allowAudioStreamCopy,
required PlaybackInfoDto? body,
}) async =>
api.itemsItemIdPlaybackInfoPost(
itemId: itemId,
userId: account?.id,
enableDirectPlay: enableDirectPlay,
enableDirectStream: enableDirectStream,
enableTranscoding: enableTranscoding,
autoOpenLiveStream: autoOpenLiveStream,
maxStreamingBitrate: maxStreamingBitrate,
liveStreamId: liveStreamId,
startTimeTicks: startTimeTicks,
mediaSourceId: mediaSourceId,
audioStreamIndex: audioStreamIndex,
subtitleStreamIndex: subtitleStreamIndex,
body: body,
);
Future<Response<BaseItemDtoQueryResult>> showsSeriesIdEpisodesGet({
required String? seriesId,
List<ItemFields>? fields,
int? season,
String? seasonId,
bool? isMissing,
String? adjacentTo,
String? startItemId,
int? startIndex,
int? limit,
bool? enableImages,
int? imageTypeLimit,
List<ImageType>? enableImageTypes,
bool? enableUserData,
ShowsSeriesIdEpisodesGetSortBy? sortBy,
}) async {
return api.showsSeriesIdEpisodesGet(
seriesId: seriesId,
userId: account?.id,
fields: fields,
isMissing: isMissing,
limit: limit,
sortBy: sortBy,
enableUserData: enableUserData,
startIndex: startIndex,
adjacentTo: adjacentTo,
startItemId: startItemId,
season: season,
seasonId: seasonId,
enableImages: enableImages,
enableImageTypes: enableImageTypes,
);
}
Future<List<ItemBaseModel>> fetchEpisodeFromShow({
required String? seriesId,
String? seasonId,
}) async {
final response = await showsSeriesIdEpisodesGet(seriesId: seriesId, seasonId: seasonId);
return response.body?.items?.map((e) => ItemBaseModel.fromBaseDto(e, ref)).toList() ?? [];
}
Future<Response<BaseItemDtoQueryResult>> itemsItemIdSimilarGet({
String? itemId,
int? limit,
}) async {
return api.itemsItemIdSimilarGet(
userId: account?.id,
itemId: itemId,
limit: limit,
);
}
Future<Response<BaseItemDtoQueryResult>> usersUserIdItemsGet({
String? parentId,
bool? recursive,
List<BaseItemKind>? includeItemTypes,
}) async {
return api.itemsGet(
parentId: parentId,
userId: account?.id,
recursive: recursive,
includeItemTypes: includeItemTypes,
);
}
Future<Response<dynamic>> playlistsPlaylistIdItemsPost({
String? playlistId,
List<String>? ids,
}) async {
return api.playlistsPlaylistIdItemsPost(
playlistId: playlistId,
ids: ids,
userId: account?.id,
);
}
Future<Response<dynamic>> playlistsPost({
String? name,
List<String>? ids,
required CreatePlaylistDto? body,
}) async {
return api.playlistsPost(
name: name,
ids: ids,
userId: account?.id,
body: body,
);
}
Future<Response<List<AccountModel>>> usersPublicGet(
CredentialsModel credentials,
) async {
final response = await api.usersPublicGet();
return response.copyWith(
body: response.body?.map(
(e) {
var imageUrl = ref.read(imageUtilityProvider).getUserImageUrl(e.id ?? "");
return AccountModel(
name: e.name ?? "",
credentials: credentials,
id: e.id ?? "",
avatar: imageUrl,
lastUsed: DateTime.now(),
);
},
).toList(),
);
}
Future<Response<AuthenticationResult>> usersAuthenticateByNamePost({
required String userName,
required String password,
}) async {
return api.usersAuthenticateByNamePost(body: AuthenticateUserByName(username: userName, pw: password));
}
Future<Response<ServerConfiguration>> systemConfigurationGet() => api.systemConfigurationGet();
Future<Response<PublicSystemInfo>> systemInfoPublicGet() => api.systemInfoPublicGet();
Future<Response> sessionsLogoutPost() => api.sessionsLogoutPost();
Future<Response<String>> itemsItemIdDownloadGet({
String? itemId,
}) =>
api.itemsItemIdDownloadGet(itemId: itemId);
Future<Response> collectionsCollectionIdItemsPost({required String? collectionId, required List<String>? ids}) =>
api.collectionsCollectionIdItemsPost(collectionId: collectionId, ids: ids);
Future<Response> collectionsCollectionIdItemsDelete({required String? collectionId, required List<String>? ids}) =>
api.collectionsCollectionIdItemsDelete(collectionId: collectionId, ids: ids);
Future<Response> collectionsPost({String? name, List<String>? ids, String? parentId, bool? isLocked}) =>
api.collectionsPost(name: name, ids: ids, parentId: parentId, isLocked: isLocked);
Future<Response<BaseItemDtoQueryResult>> usersUserIdViewsGet({
bool? includeExternalContent,
List<CollectionType>? presetViews,
bool? includeHidden,
}) =>
api.userViewsGet(
userId: account?.id,
includeExternalContent: includeExternalContent,
presetViews: presetViews,
includeHidden: includeHidden);
Future<Response<List<ExternalIdInfo>>> itemsItemIdExternalIdInfosGet({required String? itemId}) =>
api.itemsItemIdExternalIdInfosGet(itemId: itemId);
Future<Response<List<RemoteSearchResult>>> itemsRemoteSearchSeriesPost(
{required SeriesInfoRemoteSearchQuery? body}) =>
api.itemsRemoteSearchSeriesPost(body: body);
Future<Response<List<RemoteSearchResult>>> itemsRemoteSearchMoviePost({required MovieInfoRemoteSearchQuery? body}) =>
api.itemsRemoteSearchMoviePost(body: body);
Future<Response<dynamic>> itemsRemoteSearchApplyItemIdPost({
required String? itemId,
bool? replaceAllImages,
required RemoteSearchResult? body,
}) =>
api.itemsRemoteSearchApplyItemIdPost(
itemId: itemId,
replaceAllImages: replaceAllImages,
body: body,
);
Future<Response<BaseItemDtoQueryResult>> showsSeriesIdSeasonsGet({
required String? seriesId,
bool? enableUserData,
bool? isMissing,
List<ItemFields>? fields,
}) =>
api.showsSeriesIdSeasonsGet(
seriesId: seriesId,
isMissing: isMissing,
enableUserData: enableUserData,
fields: fields,
);
Future<Response<QueryFilters>> itemsFilters2Get({
String? parentId,
List<BaseItemKind>? includeItemTypes,
bool? isAiring,
bool? isMovie,
bool? isSports,
bool? isKids,
bool? isNews,
bool? isSeries,
bool? recursive,
}) =>
api.itemsFilters2Get(
parentId: parentId,
includeItemTypes: includeItemTypes,
isAiring: isAiring,
isMovie: isMovie,
isSports: isSports,
isKids: isKids,
isNews: isNews,
isSeries: isSeries,
recursive: recursive,
);
Future<Response<BaseItemDtoQueryResult>> studiosGet({
int? startIndex,
int? limit,
String? searchTerm,
String? parentId,
List<ItemFields>? fields,
List<BaseItemKind>? excludeItemTypes,
List<BaseItemKind>? includeItemTypes,
bool? isFavorite,
bool? enableUserData,
int? imageTypeLimit,
List<ImageType>? enableImageTypes,
String? userId,
String? nameStartsWithOrGreater,
String? nameStartsWith,
String? nameLessThan,
bool? enableImages,
bool? enableTotalRecordCount,
}) =>
api.studiosGet(
startIndex: startIndex,
limit: limit,
searchTerm: searchTerm,
parentId: parentId,
fields: fields,
excludeItemTypes: excludeItemTypes,
includeItemTypes: includeItemTypes,
isFavorite: isFavorite,
enableUserData: enableUserData,
imageTypeLimit: imageTypeLimit,
enableImageTypes: enableImageTypes,
nameStartsWithOrGreater: nameStartsWithOrGreater,
nameStartsWith: nameStartsWith,
nameLessThan: nameLessThan,
enableImages: enableImages,
enableTotalRecordCount: enableTotalRecordCount,
);
Future<Response<ServerQueryResult>> playlistsPlaylistIdItemsGet({
required String? playlistId,
int? startIndex,
int? limit,
List<ItemFields>? fields,
bool? enableImages,
bool? enableUserData,
int? imageTypeLimit,
List<ImageType>? enableImageTypes,
}) async {
final response = await api.playlistsPlaylistIdItemsGet(
playlistId: playlistId,
userId: account?.id,
startIndex: startIndex,
limit: limit,
fields: fields,
enableImages: enableImages,
enableUserData: enableUserData,
imageTypeLimit: imageTypeLimit,
enableImageTypes: enableImageTypes,
);
return response.copyWith(
body: ServerQueryResult.fromBaseQuery(response.bodyOrThrow, ref),
);
}
Future<Response> playlistsPlaylistIdItemsDelete({required String? playlistId, List<String>? entryIds}) =>
api.playlistsPlaylistIdItemsDelete(
playlistId: playlistId,
entryIds: entryIds,
);
Future<Response<UserDto>> usersMeGet() => api.usersMeGet();
Future<Response> configuration() => api.systemConfigurationGet();
Future<Response> itemsItemIdRefreshPost({
required String? itemId,
MetadataRefresh? metadataRefreshMode,
MetadataRefresh? imageRefreshMode,
bool? replaceAllMetadata,
bool? replaceAllImages,
}) =>
api.itemsItemIdRefreshPost(
itemId: itemId,
metadataRefreshMode: metadataRefreshMode?.metadataRefreshMode,
imageRefreshMode: imageRefreshMode?.imageRefreshMode,
replaceAllMetadata: replaceAllMetadata,
replaceAllImages: replaceAllImages,
);
Future<Response<UserItemDataDto>> usersUserIdFavoriteItemsItemIdPost({
required String? itemId,
}) =>
api.userFavoriteItemsItemIdPost(
itemId: itemId,
userId: account?.id,
);
Future<Response<UserItemDataDto>> usersUserIdFavoriteItemsItemIdDelete({
required String? itemId,
}) =>
api.userFavoriteItemsItemIdDelete(
itemId: itemId,
userId: account?.id,
);
Future<Response<UserItemDataDto>> usersUserIdPlayedItemsItemIdPost({
required String? itemId,
DateTime? datePlayed,
}) =>
api.userPlayedItemsItemIdPost(itemId: itemId, userId: account?.id, datePlayed: datePlayed);
Future<Response<UserItemDataDto>> usersUserIdPlayedItemsItemIdDelete({
required String? itemId,
}) =>
api.userPlayedItemsItemIdDelete(
itemId: itemId,
userId: account?.id,
);
Future<Response<MediaSegmentsModel>?> mediaSegmentsGet({
required String id,
}) async {
try {
final response = await api.mediaSegmentsItemIdGet(itemId: id);
final newSegments = response.body?.items?.map((e) => e.toSegment).toList() ?? [];
return response.copyWith(
body: MediaSegmentsModel(segments: newSegments),
);
} catch (e) {
log(e.toString());
return null;
}
}
Future<Response<TrickPlayModel>?> getTrickPlay({
required ItemBaseModel? item,
int? width,
required Ref ref,
}) async {
try {
if (item == null) return null;
if (item.overview.trickPlayInfo?.isEmpty == true) {
return null;
}
final trickPlayModel = item.overview.trickPlayInfo?.values.lastOrNull;
if (trickPlayModel == null) return null;
final response = await api.videosItemIdTrickplayWidthTilesM3u8Get(
itemId: item.id,
width: trickPlayModel.width,
);
final server = ref.read(userProvider)?.server;
if (server == null) return null;
final sanitizedUrls = response.bodyString
.split('\n')
.where((line) => line.isNotEmpty && !line.startsWith('#'))
.map((line) => line.trim())
.map((line) => Uri.parse(line).toString())
.toList();
return response.copyWith(
body: trickPlayModel.copyWith(
images: sanitizedUrls
.map(
(e) => joinAll([server, 'Videos/${item.id}/Trickplay/${trickPlayModel.width}', e]),
)
.toList()));
} catch (e) {
log(e.toString());
return null;
}
}
Future<Response<List<SessionInfoDto>>> sessionsInfo(String deviceId) async => api.sessionsGet(deviceId: deviceId);
Future<Response<bool>> quickConnect(String code) async => api.quickConnectAuthorizePost(code: code);
Future<Response<bool>> quickConnectEnabled() async => api.quickConnectEnabledGet();
Future<Response<dynamic>> deleteItem(String itemId) => api.itemsItemIdDelete(itemId: itemId);
}