Add enqueue random items command (#130)

This commit is contained in:
Manuel 2023-03-26 00:02:57 +01:00 committed by GitHub
parent 2141880b44
commit 2c15e38b94
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 129 additions and 3 deletions

View File

@ -39,7 +39,7 @@ export class DiscordMessageService {
}: {
title: string;
description?: string;
authorUrl?: string;
authorUrl?: string | undefined;
mixin?: (embedBuilder: EmbedBuilder) => EmbedBuilder;
}): APIEmbed {
let embedBuilder = new EmbedBuilder()

View File

@ -170,6 +170,30 @@ export class JellyfinSearchService {
}
}
async getRandomTracks(limit: number) {
const api = this.jellyfinService.getApi();
const searchApi = getItemsApi(api);
try {
const response = await searchApi.getItems({
includeItemTypes: [BaseItemKind.Audio],
limit: limit,
sortBy: ['random'],
userId: this.jellyfinService.getUserId(),
recursive: true,
});
return response.data.Items.map((item) => {
return SearchHint.constructFromBaseItem(item);
});
} catch (err) {
this.logger.error(
`Unabele to retrieve random items from Jellyfin: ${err}`,
);
return [];
}
}
private transformToSearchHint(jellyifnHint: JellyfinSearchHint) {
switch (jellyifnHint.Type) {
case BaseItemKind[BaseItemKind.Audio]:

View File

@ -15,6 +15,7 @@ import { StatusCommand } from './status.command';
import { StopPlaybackCommand } from './stop.command';
import { SummonCommand } from './summon.command';
import { PlaylistInteractionCollector } from './playlist/playlist.interaction-collector';
import { EnqueueRandomItemsCommand } from './random/random.command';
@Module({
imports: [
@ -28,6 +29,7 @@ import { PlaylistInteractionCollector } from './playlist/playlist.interaction-co
PlaylistInteractionCollector,
HelpCommand,
StatusCommand,
EnqueueRandomItemsCommand,
PlaylistCommand,
DisconnectCommand,
PausePlaybackCommand,

View File

@ -0,0 +1,77 @@
import { SlashCommandPipe } from '@discord-nestjs/common';
import { Command, Handler, IA, InteractionEvent } from '@discord-nestjs/core';
import { Injectable } from '@nestjs/common';
import {
CommandInteraction,
GuildMember,
InteractionReplyOptions,
} from 'discord.js';
import { DiscordMessageService } from 'src/clients/discord/discord.message.service';
import { DiscordVoiceService } from 'src/clients/discord/discord.voice.service';
import { JellyfinSearchService } from 'src/clients/jellyfin/jellyfin.search.service';
import { SearchHint } from 'src/models/search/SearchHint';
import { Track } from 'src/models/shared/Track';
import { PlaybackService } from 'src/playback/playback.service';
import { RandomCommandParams } from './random.params';
@Command({
name: 'random',
description: 'Enqueues a random selection of tracks to your playlist',
})
@Injectable()
export class EnqueueRandomItemsCommand {
constructor(
private readonly playbackService: PlaybackService,
private readonly discordVoiceService: DiscordVoiceService,
private readonly discordMessageService: DiscordMessageService,
private readonly jellyfinSearchService: JellyfinSearchService,
) {}
@Handler()
async handler(
@InteractionEvent(SlashCommandPipe) dto: RandomCommandParams,
@IA() interaction: CommandInteraction,
): Promise<void> {
await interaction.deferReply();
const guildMember = interaction.member as GuildMember;
const tryResult =
this.discordVoiceService.tryJoinChannelAndEstablishVoiceConnection(
guildMember,
);
if (!tryResult.success) {
const replyOptions = tryResult.reply as InteractionReplyOptions;
await interaction.editReply({
embeds: replyOptions.embeds,
});
return;
}
const items = await this.jellyfinSearchService.getRandomTracks(dto.count);
const tracks = await this.getTracks(items);
this.playbackService.getPlaylistOrDefault().enqueueTracks(tracks);
await interaction.editReply({
embeds: [
this.discordMessageService.buildMessage({
title: `Added ${tracks.length} tracks to your playlist`,
description: 'Use ``/playlist`` to see them',
}),
],
});
}
private async getTracks(hints: SearchHint[]) {
const promises = await Promise.all(
hints.flatMap(async (item) => {
const tracks = await item.toTracks(this.jellyfinSearchService);
return tracks;
}),
);
return promises.flatMap((x) => x);
}
}

View File

@ -0,0 +1,12 @@
import { Param, ParamType } from '@discord-nestjs/core';
export class RandomCommandParams {
@Param({
required: false,
description: 'Count of items to search for',
type: ParamType.INTEGER,
minValue: 0,
maxValue: 10000,
})
count = 20;
}

View File

@ -1,7 +1,10 @@
import { SearchHint as JellyfinSearchHint } from '@jellyfin/sdk/lib/generated-client/models';
import {
BaseItemDto,
SearchHint as JellyfinSearchHint,
} from '@jellyfin/sdk/lib/generated-client/models';
import { Track } from '../shared/Track';
import { JellyfinSearchService } from '../../clients/jellyfin/jellyfin.search.service';
import { Track } from '../shared/Track';
export class SearchHint {
constructor(
@ -28,4 +31,12 @@ export class SearchHint {
static constructFromHint(hint: JellyfinSearchHint) {
return new SearchHint(hint.Id, hint.Name, hint.RunTimeTicks / 10000);
}
static constructFromBaseItem(baseItem: BaseItemDto) {
return new SearchHint(
baseItem.Id,
baseItem.Name,
baseItem.RunTimeTicks / 10000,
);
}
}