Skip to content

Commit

Permalink
Implement remove, transfer config commands; Implement jellyfin next u…
Browse files Browse the repository at this point in the history
…p episodes command
  • Loading branch information
l7ssha committed Oct 25, 2024
1 parent e9b7ef6 commit c693ac8
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 42 deletions.
101 changes: 60 additions & 41 deletions lib/src/commands/jellyfin.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import 'package:running_on_dart/running_on_dart.dart';
import 'package:running_on_dart/src/checks.dart';
import 'package:running_on_dart/src/external/wizarr.dart';
import 'package:running_on_dart/src/models/jellyfin_config.dart';
import 'package:running_on_dart/src/repository/jellyfin_config.dart';
import 'package:running_on_dart/src/util/jellyfin.dart';
import 'package:running_on_dart/src/util/pipelines.dart';
import 'package:running_on_dart/src/util/util.dart';
Expand Down Expand Up @@ -335,6 +336,19 @@ final jellyfin = ChatGroup("jellyfin", "Jellyfin Testing Commands", checks: [
context.respond(MessageBuilder(embeds: embeds));
}),
),
ChatCommand(
"next-up-episodes",
"Show next up episodes to watch",
id('jellyfin-next-up-episodes', (ChatContext context,
[@Description("Instance to use. Default selected if not provided") JellyfinConfigUser? config]) async {
final client = await getJellyfinClient(config, context);

final results = await client.getNextUpEpisodes();

final paginator = await pagination.builders(await buildMediaInfoBuilders(results, client).toList());
return context.respond(paginator);
}),
),
ChatGroup("settings", "Settings for jellyfin", children: [
ChatCommand(
'add-instance',
Expand Down Expand Up @@ -453,47 +467,52 @@ final jellyfin = ChatGroup("jellyfin", "Jellyfin Testing Commands", checks: [
checks: [
jellyfinFeatureCreateInstanceCommandCheck,
]),
// ChatCommand(
// "transfer-config",
// "Transfers jellyfin instance config to another guild",
// id("jellyfin-settings-transfer-config", (
// ChatContext context,
// @Description("Name of instance") @UseConverter(jellyfinConfigConverter) JellyfinConfig config,
// @Description("Guild or user id to copy to") Snowflake targetParentId, [
// @Description("Copy default flag?") bool copyDefaultFlag = false,
// @Description("New name for config. Copied from original if not provided") String? configName,
// ]) async {
// final newConfig =
// await Injector.appInstance.get<JellyfinConfigRepository>().createJellyfinConfig(JellyfinConfig(
// name: configName ?? config.name,
// basePath: config.basePath,
// token: config.token,
// isDefault: copyDefaultFlag && config.isDefault,
// parentId: targetParentId,
// sonarrBasePath: config.sonarrBasePath,
// sonarrToken: config.sonarrToken,
// wizarrBasePath: config.wizarrBasePath,
// wizarrToken: config.wizarrToken,
// ));
//
// context.respond(
// MessageBuilder(content: 'Copied config: "${newConfig.name}" to parent: "${newConfig.parentId}"'));
// }),
// checks: [
// jellyfinFeatureAdminCommandCheck,
// ]),
// ChatCommand(
// "remove-config",
// "Removes config from current guild",
// id("jellyfin-settings-remove-config", (ChatContext context,
// @Description("Name of instance") @UseConverter(jellyfinConfigConverter) JellyfinConfig config) async {
// await Injector.appInstance.get<JellyfinModule>().deleteJellyfinConfig(config);
//
// context.respond(MessageBuilder(content: 'Delete config with name: "${config.name}"'));
// }),
// checks: [
// jellyfinFeatureAdminCommandCheck,
// ]),
ChatCommand(
"transfer-config",
"Transfers jellyfin instance config to another guild",
id("jellyfin-settings-transfer-config", (
ChatContext context,
@Description("Instance to use. Default selected if not provided") JellyfinConfigUser userConfig,
@Description("Guild or user id to copy to") Snowflake targetParentId, [
@Description("Copy default flag?") bool copyDefaultFlag = false,
@Description("New name for config. Copied from original if not provided") String? configName,
]) async {
final client = await getJellyfinClient(userConfig, context);
await ensureAdminJellyfinUser(client);

final config = userConfig.config;
if (config == null) {
return context.respond(MessageBuilder(content: 'Cannot find valid jellyfin config'));
}

final newConfig =
await Injector.appInstance.get<JellyfinConfigRepository>().createJellyfinConfig(JellyfinConfig(
name: configName ?? config.name,
basePath: config.basePath,
isDefault: copyDefaultFlag && config.isDefault,
parentId: targetParentId,
sonarrBasePath: config.sonarrBasePath,
sonarrToken: config.sonarrToken,
wizarrBasePath: config.wizarrBasePath,
wizarrToken: config.wizarrToken,
));

context.respond(
MessageBuilder(content: 'Copied config: "${newConfig.name}" to parent: "${newConfig.parentId}"'));
})),
ChatCommand(
"remove-config",
"Removes config from current guild",
id("jellyfin-settings-remove-config", (ChatContext context,
@Description("Instance to use. Default selected if not provided") JellyfinConfigUser config) async {
final client = await getJellyfinClient(config, context);
await ensureAdminJellyfinUser(client);

await Injector.appInstance.get<JellyfinConfigRepository>().removeJellyfinConfig(config.config!);

context.respond(MessageBuilder(content: 'Delete config with name: "${config.config?.name}"'));
}),
),
]),
ChatGroup("util", "Util commands for jellyfin", children: [
ChatCommand(
Expand Down
11 changes: 11 additions & 0 deletions lib/src/modules/jellyfin.dart
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,17 @@ class AuthenticatedJellyfinClient {
return response.data!;
}

Future<List<BaseItemDto>> getNextUpEpisodes({int limit = 10}) async {
final result = await jellyfinClient.getTvShowsApi().getNextUp(
limit: limit,
enableTotalRecordCount: false,
disableFirstEpisode: true,
enableRewatching: false,
fields: BuiltList([ItemFields.overview]));

return result.data?.items?.toList() ?? [];
}

Future<void> startTask(String taskId) => jellyfinClient.getScheduledTasksApi().startTask(taskId: taskId);
Uri getItemPrimaryImage(String itemId) => Uri.parse("${configUser.config?.basePath}/Items/$itemId/Images/Primary");
Uri getJellyfinItemUrl(String itemId) => Uri.parse("${configUser.config?.basePath}/#/details?id=$itemId");
Expand Down
9 changes: 9 additions & 0 deletions lib/src/repository/jellyfin_config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,15 @@ import 'package:running_on_dart/src/services/db.dart';
class JellyfinConfigRepository {
final _database = Injector.appInstance.get<DatabaseService>();

Future<void> removeJellyfinConfig(JellyfinConfig config) async {
await _database.getConnection().execute(
Sql.named('DELETE FROM jellyfin_user_configs WHERE jellyfin_config_id = @id'),
parameters: {'id': config.id});
await _database
.getConnection()
.execute(Sql.named('DELETE FROM jellyfin_configs WHERE id = @id'), parameters: {'id': config.id});
}

Future<Iterable<JellyfinConfig>> getConfigsForParent(String parentId) async {
final result = await _database.getConnection().execute(
Sql.named('SELECT * FROM jellyfin_configs WHERE guild_id = @parentId'),
Expand Down
3 changes: 2 additions & 1 deletion lib/src/util/jellyfin.dart
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,8 @@ EmbedBuilder? buildMediaEmbedBuilder(BaseItemDto item, AuthenticatedJellyfinClie
EmbedFieldBuilder(name: "Runtime", value: runtime, isInline: true),
EmbedFieldBuilder(name: "Status", value: item.status.toString(), isInline: true),
...fields,
EmbedFieldBuilder(name: 'Avg Length', value: parseDurationFromTicks(item.runTimeTicks!).formatShort(), isInline: true)
EmbedFieldBuilder(
name: 'Avg Length', value: parseDurationFromTicks(item.runTimeTicks!).formatShort(), isInline: true)
],
);
}
Expand Down

0 comments on commit c693ac8

Please sign in to comment.