Add search result embed

This commit is contained in:
Manuel Ruwe 2022-12-17 14:13:03 +01:00
parent 4693b2f75f
commit 2aa2d16e40
4 changed files with 99 additions and 6 deletions

View File

@ -3,6 +3,7 @@ import { JellyfinService } from './jellyfin.service';
import { SearchHint } from '@jellyfin/sdk/lib/generated-client/models'; import { SearchHint } from '@jellyfin/sdk/lib/generated-client/models';
import { getSearchApi } from '@jellyfin/sdk/lib/utils/api/search-api'; import { getSearchApi } from '@jellyfin/sdk/lib/utils/api/search-api';
import { getItemsApi } from '@jellyfin/sdk/lib/utils/api/items-api';
import { Logger } from '@nestjs/common/services'; import { Logger } from '@nestjs/common/services';
@Injectable() @Injectable()
@ -28,4 +29,20 @@ export class JellyfinSearchService {
return SearchHints; return SearchHints;
} }
async getById(id: string): Promise<SearchHint> {
const api = this.jellyfinService.getApi();
const searchApi = getItemsApi(api);
const { data } = await searchApi.getItems({
ids: [id],
});
if (data.Items.length !== 1) {
this.logger.warn(`Failed to retrieve item via id '${id}'`);
return null;
}
return data.Items[0];
}
} }

View File

@ -28,7 +28,8 @@ export class EnqueueCommand
dto: TrackRequestDto, dto: TrackRequestDto,
executionContext: TransformedCommandExecutionContext<any>, executionContext: TransformedCommandExecutionContext<any>,
): InteractionReplyOptions | string { ): InteractionReplyOptions | string {
const index = this.playbackService.eneuqueTrack({}); // const index = this.playbackService.eneuqueTrack({});
const index = 0;
return { return {
embeds: [ embeds: [
this.discordMessageService.buildMessage({ this.discordMessageService.buildMessage({

View File

@ -3,21 +3,31 @@ import { TransformPipe } from '@discord-nestjs/common';
import { import {
Command, Command,
DiscordTransformedCommand, DiscordTransformedCommand,
Param, On,
Payload, Payload,
TransformedCommandExecutionContext, TransformedCommandExecutionContext,
UsePipes, UsePipes,
} from '@discord-nestjs/core'; } from '@discord-nestjs/core';
import { SearchHint } from '@jellyfin/sdk/lib/generated-client/models';
import { Logger } from '@nestjs/common/services';
import { import {
APIEmbedField,
ComponentType, ComponentType,
EmbedBuilder, EmbedBuilder,
Events,
Interaction,
InteractionReplyOptions, InteractionReplyOptions,
} from 'discord.js'; } from 'discord.js';
import { JellyfinSearchService } from '../clients/jellyfin/jellyfin.search.service'; import { JellyfinSearchService } from '../clients/jellyfin/jellyfin.search.service';
import { TrackRequestDto } from '../models/track-request.dto'; import { TrackRequestDto } from '../models/track-request.dto';
import { DefaultJellyfinColor } from '../types/colors'; import { DefaultJellyfinColor } from '../types/colors';
import { v4 as uuidv4 } from 'uuid';
import { DiscordMessageService } from '../clients/discord/discord.message.service';
import { formatDuration, intervalToDuration } from 'date-fns';
import { format } from 'path';
import { PlaybackService } from '../playback/playback.service';
@Command({ @Command({
name: 'search', name: 'search',
description: 'Search for an item on your Jellyfin instance', description: 'Search for an item on your Jellyfin instance',
@ -26,7 +36,13 @@ import { DefaultJellyfinColor } from '../types/colors';
export class SearchItemCommand export class SearchItemCommand
implements DiscordTransformedCommand<TrackRequestDto> implements DiscordTransformedCommand<TrackRequestDto>
{ {
constructor(private readonly jellyfinSearchService: JellyfinSearchService) {} private readonly logger: Logger = new Logger(SearchItemCommand.name);
constructor(
private readonly jellyfinSearchService: JellyfinSearchService,
private readonly discordMessageService: DiscordMessageService,
private readonly playbackService: PlaybackService,
) {}
async handler( async handler(
@Payload() dto: TrackRequestDto, @Payload() dto: TrackRequestDto,
@ -88,7 +104,7 @@ export class SearchItemCommand
components: [ components: [
{ {
type: ComponentType.StringSelect, type: ComponentType.StringSelect,
customId: 'cool', customId: 'searchItemSelect',
options: selectOptions, options: selectOptions,
}, },
], ],
@ -97,6 +113,61 @@ export class SearchItemCommand
}; };
} }
@On(Events.InteractionCreate)
async onStringSelect(interaction: Interaction) {
if (!interaction.isStringSelectMenu()) return;
if (interaction.customId !== 'searchItemSelect') {
return;
}
if (interaction.values.length !== 1) {
this.logger.warn(
`Failed to process interaction select with values [${interaction.values.length}]`,
);
return;
}
const item = await this.jellyfinSearchService.getById(
interaction.values[0],
);
const milliseconds = item.RunTimeTicks / 10000;
const duration = formatDuration(
intervalToDuration({
start: milliseconds,
end: 0,
}),
);
const artists = item.Artists.join(', ');
const addedIndex = this.playbackService.eneuqueTrack({
jellyfinId: item.Id,
name: item.Name,
durationInMilliseconds: milliseconds,
});
await interaction.update({
embeds: [
new EmbedBuilder()
.setAuthor({
name: 'Jellyfin Search',
iconURL:
'https://github.com/walkxcode/dashboard-icons/blob/main/png/jellyfin.png?raw=true',
})
.setTitle(item.Name)
.setDescription(
`**Duration**: ${duration}\n**Artists**: ${artists}\n\nTrack was added to the queue at position ${addedIndex}`,
)
.setColor(DefaultJellyfinColor)
.toJSON(),
],
components: [],
});
}
private markSearchTermOverlap(value: string, searchTerm: string) { private markSearchTermOverlap(value: string, searchTerm: string) {
const startIndex = value.indexOf(searchTerm); const startIndex = value.indexOf(searchTerm);
const actualValue = value.substring( const actualValue = value.substring(

View File

@ -1 +1,5 @@
export interface Track {} export interface Track {
jellyfinId: string;
name: string;
durationInMilliseconds: number;
}