Initial commit
This commit is contained in:
@@ -0,0 +1,346 @@
|
||||
import 'dart:async';
|
||||
|
||||
import '../../../shared/utils/app_logger.dart';
|
||||
import '../../contracts/contracts.dart';
|
||||
import '../ports/emby_gateway.dart';
|
||||
import '../ports/server_repository.dart';
|
||||
import '../ports/session_repository.dart';
|
||||
import 'usecase_helpers.dart';
|
||||
|
||||
const _ucTag = 'UseCase';
|
||||
|
||||
|
||||
Stream<GlobalSearchServerResult> _fanOutPerServer({
|
||||
required SessionRepository sessionRepository,
|
||||
required ServerRepository serverRepository,
|
||||
required Future<List<EmbyRawItem>?> Function(AuthedRequestContext context)
|
||||
fetchItems,
|
||||
required String logTag,
|
||||
}) {
|
||||
final controller = StreamController<GlobalSearchServerResult>();
|
||||
() async {
|
||||
try {
|
||||
final allSessions = await sessionRepository.listAll();
|
||||
final allServers = await serverRepository.list();
|
||||
final serverMap = {for (final s in allServers) s.id: s};
|
||||
|
||||
final futures = allSessions.map((session) async {
|
||||
final server = serverMap[session.serverId];
|
||||
if (server == null) return;
|
||||
final context = AuthedRequestContext(
|
||||
baseUrl: server.baseUrl,
|
||||
token: session.token,
|
||||
userId: session.userId,
|
||||
);
|
||||
try {
|
||||
final items = await fetchItems(context);
|
||||
if (items == null || items.isEmpty) return;
|
||||
controller.add(
|
||||
GlobalSearchServerResult(
|
||||
serverId: server.id,
|
||||
serverName: server.name,
|
||||
serverUrl: server.baseUrl,
|
||||
token: session.token,
|
||||
userId: session.userId,
|
||||
items: items,
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
AppLogger.warn(_ucTag, '$logTag on ${server.name} failed', e);
|
||||
}
|
||||
});
|
||||
|
||||
await Future.wait(futures);
|
||||
} catch (e) {
|
||||
controller.addError(e);
|
||||
} finally {
|
||||
controller.close();
|
||||
}
|
||||
}();
|
||||
return controller.stream;
|
||||
}
|
||||
|
||||
class GetLibraryViewsUseCase extends AuthedUseCase {
|
||||
GetLibraryViewsUseCase({
|
||||
required super.sessionRepository,
|
||||
required super.serverRepository,
|
||||
required super.embyGateway,
|
||||
});
|
||||
|
||||
Future<LibraryViewsRes> execute() async {
|
||||
return embyGateway.getUserViews(await ctx());
|
||||
}
|
||||
}
|
||||
|
||||
class GetLibraryResumeUseCase extends AuthedUseCase {
|
||||
GetLibraryResumeUseCase({
|
||||
required super.sessionRepository,
|
||||
required super.serverRepository,
|
||||
required super.embyGateway,
|
||||
});
|
||||
|
||||
Future<LibraryResumeRes> execute() async {
|
||||
return embyGateway.getResumeItems(await ctx(), limit: 20);
|
||||
}
|
||||
}
|
||||
|
||||
class GetLibraryLatestUseCase extends AuthedUseCase {
|
||||
GetLibraryLatestUseCase({
|
||||
required super.sessionRepository,
|
||||
required super.serverRepository,
|
||||
required super.embyGateway,
|
||||
});
|
||||
|
||||
Future<LibraryLatestRes> execute(
|
||||
LibraryLatestReq input, {
|
||||
CancellationToken? cancelToken,
|
||||
}) async {
|
||||
return embyGateway.getLatestItems(
|
||||
await ctx(),
|
||||
input.parentId,
|
||||
limit: input.limit ?? 16,
|
||||
cancelToken: cancelToken,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class GetLibraryItemsUseCase extends AuthedUseCase {
|
||||
GetLibraryItemsUseCase({
|
||||
required super.sessionRepository,
|
||||
required super.serverRepository,
|
||||
required super.embyGateway,
|
||||
});
|
||||
|
||||
Future<LibraryItemsRes> execute(LibraryItemsReq input) async {
|
||||
final items = await embyGateway.getLibraryItems(
|
||||
ctx: await ctx(),
|
||||
parentId: input.parentId,
|
||||
includeItemTypes: input.includeItemTypes,
|
||||
genreIds: input.genreIds,
|
||||
searchTerm: input.searchTerm,
|
||||
anyProviderIdEquals: input.anyProviderIdEquals,
|
||||
fields: input.fields,
|
||||
enableImageTypes: input.enableImageTypes,
|
||||
groupProgramsBySeries: input.groupProgramsBySeries,
|
||||
imageTypeLimit: input.imageTypeLimit,
|
||||
startIndex: input.startIndex,
|
||||
limit: input.limit,
|
||||
recursive: input.recursive,
|
||||
sortBy: input.sortBy,
|
||||
sortOrder: input.sortOrder,
|
||||
);
|
||||
return LibraryItemsRes(
|
||||
parentId: input.parentId ?? '__global__',
|
||||
items: items,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class GetHomeBannerUseCase extends AuthedUseCase {
|
||||
GetHomeBannerUseCase({
|
||||
required super.sessionRepository,
|
||||
required super.serverRepository,
|
||||
required super.embyGateway,
|
||||
});
|
||||
|
||||
Future<List<EmbyRawItem>> execute({int fetchLimit = 24, int take = 8}) async {
|
||||
final items = await embyGateway.getLibraryItems(
|
||||
ctx: await ctx(),
|
||||
includeItemTypes: 'Movie,Series',
|
||||
recursive: true,
|
||||
sortBy: 'DateCreated',
|
||||
sortOrder: SortOrder.descending,
|
||||
fields:
|
||||
'BasicSyncInfo,CommunityRating,ProductionYear,Overview,Genres,ProviderIds,UserData',
|
||||
enableImageTypes: 'Backdrop,Logo,Primary,Thumb',
|
||||
imageTypeLimit: 2,
|
||||
limit: fetchLimit,
|
||||
);
|
||||
return items
|
||||
.where((it) => (it.BackdropImageTags ?? const []).isNotEmpty)
|
||||
.take(take)
|
||||
.toList(growable: false);
|
||||
}
|
||||
}
|
||||
|
||||
class GlobalKeywordSearchUseCase extends AuthedUseCase {
|
||||
GlobalKeywordSearchUseCase({
|
||||
required super.sessionRepository,
|
||||
required super.serverRepository,
|
||||
required super.embyGateway,
|
||||
});
|
||||
|
||||
static const _defaultTypes = 'Movie,Series';
|
||||
|
||||
Stream<GlobalSearchServerResult> executeStream(GlobalSearchReq input) {
|
||||
final controller = StreamController<GlobalSearchServerResult>();
|
||||
() async {
|
||||
final keyword = input.keyword.trim();
|
||||
if (keyword.isEmpty) {
|
||||
controller.close();
|
||||
return;
|
||||
}
|
||||
try {
|
||||
var sessions = await sessionRepository.listAll();
|
||||
if (input.serverId != null) {
|
||||
sessions = sessions
|
||||
.where((s) => s.serverId == input.serverId)
|
||||
.toList();
|
||||
}
|
||||
final allServers = await serverRepository.list();
|
||||
final serverMap = {for (final s in allServers) s.id: s};
|
||||
final types = input.includeItemTypes ?? _defaultTypes;
|
||||
|
||||
final futures = sessions.map((session) async {
|
||||
final result = await _searchOne(
|
||||
session: session,
|
||||
server: serverMap[session.serverId],
|
||||
keyword: keyword,
|
||||
types: types,
|
||||
limit: input.limitPerServer ?? 30,
|
||||
);
|
||||
if (result != null) controller.add(result);
|
||||
});
|
||||
|
||||
await Future.wait(futures);
|
||||
} catch (error, stackTrace) {
|
||||
controller.addError(error, stackTrace);
|
||||
} finally {
|
||||
controller.close();
|
||||
}
|
||||
}();
|
||||
return controller.stream;
|
||||
}
|
||||
|
||||
Future<GlobalSearchServerResult?> _searchOne({
|
||||
required SessionData session,
|
||||
required EmbyServer? server,
|
||||
required String keyword,
|
||||
required String types,
|
||||
required int limit,
|
||||
}) async {
|
||||
if (server == null) return null;
|
||||
final c = AuthedRequestContext(
|
||||
baseUrl: server.baseUrl,
|
||||
token: session.token,
|
||||
userId: session.userId,
|
||||
);
|
||||
try {
|
||||
final items = await embyGateway.getLibraryItems(
|
||||
ctx: c,
|
||||
searchTerm: keyword,
|
||||
recursive: true,
|
||||
includeItemTypes: types,
|
||||
limit: limit,
|
||||
sortBy: 'SortName',
|
||||
sortOrder: SortOrder.ascending,
|
||||
);
|
||||
if (items.isEmpty) return null;
|
||||
return GlobalSearchServerResult(
|
||||
serverId: server.id,
|
||||
serverName: server.name,
|
||||
serverUrl: server.baseUrl,
|
||||
token: session.token,
|
||||
userId: session.userId,
|
||||
items: items,
|
||||
);
|
||||
} catch (e) {
|
||||
AppLogger.warn(_ucTag, 'GlobalKeywordSearch on ${server.name} failed', e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class GetAggregatedResumeUseCase extends AuthedUseCase {
|
||||
GetAggregatedResumeUseCase({
|
||||
required super.sessionRepository,
|
||||
required super.serverRepository,
|
||||
required super.embyGateway,
|
||||
});
|
||||
|
||||
Stream<GlobalSearchServerResult> executeStream({int? limitPerServer}) {
|
||||
return _fanOutPerServer(
|
||||
sessionRepository: sessionRepository,
|
||||
serverRepository: serverRepository,
|
||||
logTag: 'AggregatedResume',
|
||||
fetchItems: (context) async {
|
||||
final res = await embyGateway.getResumeItems(
|
||||
context,
|
||||
limit: limitPerServer ?? 20,
|
||||
);
|
||||
return res.Items;
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class GetAggregatedFavoritesUseCase extends AuthedUseCase {
|
||||
static const _defaultTypes = 'Movie,Series,Episode';
|
||||
|
||||
GetAggregatedFavoritesUseCase({
|
||||
required super.sessionRepository,
|
||||
required super.serverRepository,
|
||||
required super.embyGateway,
|
||||
});
|
||||
|
||||
Stream<GlobalSearchServerResult> executeStream({
|
||||
String includeItemTypes = _defaultTypes,
|
||||
int? limitPerServer,
|
||||
}) {
|
||||
final types = includeItemTypes.trim().isEmpty
|
||||
? _defaultTypes
|
||||
: includeItemTypes;
|
||||
return _fanOutPerServer(
|
||||
sessionRepository: sessionRepository,
|
||||
serverRepository: serverRepository,
|
||||
logTag: 'AggregatedFavorites',
|
||||
fetchItems: (context) async {
|
||||
final items = await embyGateway.getFavoriteItems(
|
||||
ctx: context,
|
||||
includeItemTypes: types,
|
||||
sortBy: 'SortName',
|
||||
sortOrder: SortOrder.ascending,
|
||||
);
|
||||
return limitPerServer == null
|
||||
? items
|
||||
: items.take(limitPerServer).toList(growable: false);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class GetAggregatedRecentUseCase extends AuthedUseCase {
|
||||
static const _defaultTypes = 'Movie,Series';
|
||||
|
||||
GetAggregatedRecentUseCase({
|
||||
required super.sessionRepository,
|
||||
required super.serverRepository,
|
||||
required super.embyGateway,
|
||||
});
|
||||
|
||||
Stream<GlobalSearchServerResult> executeStream({
|
||||
String includeItemTypes = _defaultTypes,
|
||||
int? limitPerServer,
|
||||
}) {
|
||||
return _fanOutPerServer(
|
||||
sessionRepository: sessionRepository,
|
||||
serverRepository: serverRepository,
|
||||
logTag: 'AggregatedRecent',
|
||||
fetchItems: (context) async {
|
||||
return embyGateway.getLibraryItems(
|
||||
ctx: context,
|
||||
includeItemTypes: includeItemTypes,
|
||||
fields:
|
||||
'BasicSyncInfo,CollectionType,PrimaryImageAspectRatio,UserData,'
|
||||
'CommunityRating,ProviderIds,ProductionYear,ChildCount,Container,'
|
||||
'CanDelete,DateCreated',
|
||||
sortBy: 'DateCreated',
|
||||
sortOrder: SortOrder.descending,
|
||||
recursive: true,
|
||||
limit: limitPerServer ?? 20,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user