mirror of
https://github.com/informaticker/discord-jellyfin-bot.git
synced 2024-11-23 18:21:55 +01:00
✨ Add album playback
This commit is contained in:
parent
c218c1273d
commit
1a1e12506f
@ -1,12 +1,17 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { JellyfinService } from './jellyfin.service';
|
||||
|
||||
import { SearchHint } from '@jellyfin/sdk/lib/generated-client/models';
|
||||
import {
|
||||
BaseItemDto,
|
||||
BaseItemKind,
|
||||
SearchHint,
|
||||
SearchHintResult,
|
||||
} from '@jellyfin/sdk/lib/generated-client/models';
|
||||
import { getItemsApi } from '@jellyfin/sdk/lib/utils/api/items-api';
|
||||
import { getPlaylistsApi } from '@jellyfin/sdk/lib/utils/api/playlists-api';
|
||||
import { getSearchApi } from '@jellyfin/sdk/lib/utils/api/search-api';
|
||||
import { Logger } from '@nestjs/common/services';
|
||||
import { JellyfinAudioPlaylist } from '../../models/jellyfinAudioItems';
|
||||
import { JellyfinAudioPlaylist, JellyfinMusicAlbum } from '../../models/jellyfinAudioItems';
|
||||
|
||||
@Injectable()
|
||||
export class JellyfinSearchService {
|
||||
@ -25,9 +30,15 @@ export class JellyfinSearchService {
|
||||
status,
|
||||
} = await searchApi.get({
|
||||
searchTerm: searchTerm,
|
||||
mediaTypes: ['Audio', 'MusicAlbum', 'Playlist'],
|
||||
includeItemTypes: [
|
||||
BaseItemKind.Audio,
|
||||
BaseItemKind.MusicAlbum,
|
||||
BaseItemKind.Playlist,
|
||||
],
|
||||
});
|
||||
|
||||
console.log(SearchHints);
|
||||
|
||||
if (status !== 200) {
|
||||
this.logger.error(`Jellyfin Search failed with status code ${status}`);
|
||||
return [];
|
||||
@ -48,13 +59,35 @@ export class JellyfinSearchService {
|
||||
});
|
||||
|
||||
if (axiosResponse.status !== 200) {
|
||||
this.logger.error(`Jellyfin Search failed with status code ${status}`);
|
||||
this.logger.error(
|
||||
`Jellyfin Search failed with status code ${axiosResponse.status}`,
|
||||
);
|
||||
return new JellyfinAudioPlaylist();
|
||||
}
|
||||
|
||||
return axiosResponse.data as JellyfinAudioPlaylist;
|
||||
}
|
||||
|
||||
async getItemsByAlbum(albumId: string): Promise<JellyfinMusicAlbum> {
|
||||
const api = this.jellyfinService.getApi();
|
||||
const searchApi = getSearchApi(api);
|
||||
const axiosResponse = await searchApi.get({
|
||||
parentId: albumId,
|
||||
userId: this.jellyfinService.getUserId(),
|
||||
mediaTypes: [BaseItemKind[BaseItemKind.Audio]],
|
||||
searchTerm: '%',
|
||||
});
|
||||
|
||||
if (axiosResponse.status !== 200) {
|
||||
this.logger.error(
|
||||
`Jellyfin Search failed with status code ${axiosResponse.status}`,
|
||||
);
|
||||
return new JellyfinMusicAlbum();
|
||||
}
|
||||
|
||||
return axiosResponse.data as JellyfinMusicAlbum;
|
||||
}
|
||||
|
||||
async getById(id: string): Promise<SearchHint> {
|
||||
const api = this.jellyfinService.getApi();
|
||||
|
||||
|
@ -190,6 +190,21 @@ export class PlayItemCommand
|
||||
components: [],
|
||||
});
|
||||
break;
|
||||
case 'album':
|
||||
const album = await this.jellyfinSearchService.getItemsByAlbum(id);
|
||||
console.log(album);
|
||||
album.SearchHints.forEach((item) => {
|
||||
this.enqueueSingleTrack(item as BaseJellyfinAudioPlayable, bitrate);
|
||||
});
|
||||
interaction.update({
|
||||
embeds: [
|
||||
this.discordMessageService.buildMessage({
|
||||
title: `Added ${album.TotalRecordCount} items from your album`,
|
||||
}),
|
||||
],
|
||||
components: [],
|
||||
});
|
||||
break;
|
||||
case 'playlist':
|
||||
const playlist = await this.jellyfinSearchService.getPlaylistById(id);
|
||||
playlist.Items.forEach((item) => {
|
||||
@ -209,7 +224,7 @@ export class PlayItemCommand
|
||||
embeds: [
|
||||
this.discordMessageService.buildErrorMessage({
|
||||
title: 'Unable to process your selection',
|
||||
description: `Sorry. I don't know the type you selected: \`\`${type}\`\`. Please report this bug to the developers.\n\nDebug Information:\`\`${interaction.values.join(
|
||||
description: `Sorry. I don't know the type you selected: \`\`${type}\`\`. Please report this bug to the developers.\n\nDebug Information: \`\`${interaction.values.join(
|
||||
', ',
|
||||
)}\`\``,
|
||||
}),
|
||||
|
@ -56,7 +56,7 @@ export class PlaylistCommand implements DiscordCommand {
|
||||
|
||||
return point;
|
||||
})
|
||||
.join(',\n');
|
||||
.join('\n');
|
||||
|
||||
return {
|
||||
embeds: [
|
||||
|
@ -1,4 +1,7 @@
|
||||
import { SearchHint } from '@jellyfin/sdk/lib/generated-client/models';
|
||||
import {
|
||||
BaseItemKind,
|
||||
SearchHint,
|
||||
} from '@jellyfin/sdk/lib/generated-client/models';
|
||||
import { JellyfinStreamBuilderService } from '../clients/jellyfin/jellyfin.stream.builder.service';
|
||||
import { Track } from '../types/track';
|
||||
import { trimStringToFixedLength } from '../utils/stringUtils';
|
||||
@ -161,22 +164,70 @@ export class JellyfinAudioPlaylist implements BaseJellyfinAudioPlayable {
|
||||
TotalRecordCount: number;
|
||||
}
|
||||
|
||||
export class JellyfinMusicAlbum implements BaseJellyfinAudioPlayable {
|
||||
Id: string;
|
||||
Name: string;
|
||||
RunTimeTicks: number;
|
||||
SearchHints: JellyfinAudioItem[];
|
||||
TotalRecordCount: number;
|
||||
|
||||
async fromSearchHint(
|
||||
jellyfinSearchService: JellyfinSearchService,
|
||||
searchHint: SearchHint,
|
||||
): Promise<JellyfinMusicAlbum> {
|
||||
this.Id = searchHint.Id;
|
||||
this.Name = searchHint.Name;
|
||||
this.RunTimeTicks = searchHint.RunTimeTicks;
|
||||
const album = await jellyfinSearchService.getItemsByAlbum(searchHint.Id);
|
||||
this.SearchHints = album.SearchHints;
|
||||
this.TotalRecordCount = album.TotalRecordCount;
|
||||
return this;
|
||||
}
|
||||
fetchTracks(
|
||||
jellyfinStreamBuilder: JellyfinStreamBuilderService,
|
||||
bitrate: number,
|
||||
): Track[] {
|
||||
return this.SearchHints.flatMap((item) =>
|
||||
item.fetchTracks(jellyfinStreamBuilder, bitrate),
|
||||
);
|
||||
}
|
||||
prettyPrint(search: string): string {
|
||||
return `${markSearchTermOverlap(this.Name, search)} (${
|
||||
this.TotalRecordCount
|
||||
} items) (Album)`;
|
||||
}
|
||||
getId(): string {
|
||||
return this.Id;
|
||||
}
|
||||
getValueId(): string {
|
||||
return `album_${this.getId()}`;
|
||||
}
|
||||
getEmoji(): string {
|
||||
return '📀';
|
||||
}
|
||||
}
|
||||
|
||||
export const searchResultAsJellyfinAudio = async (
|
||||
logger: Logger,
|
||||
jellyfinSearchService: JellyfinSearchService,
|
||||
searchHint: SearchHint,
|
||||
) => {
|
||||
switch (searchHint.Type) {
|
||||
case 'Audio':
|
||||
case BaseItemKind[BaseItemKind.Audio]:
|
||||
return await new JellyfinAudioItem().fromSearchHint(
|
||||
jellyfinSearchService,
|
||||
searchHint,
|
||||
);
|
||||
case 'Playlist':
|
||||
case BaseItemKind[BaseItemKind.Playlist]:
|
||||
return await new JellyfinAudioPlaylist().fromSearchHint(
|
||||
jellyfinSearchService,
|
||||
searchHint,
|
||||
);
|
||||
case BaseItemKind[BaseItemKind.MusicAlbum]:
|
||||
return await new JellyfinMusicAlbum().fromSearchHint(
|
||||
jellyfinSearchService,
|
||||
searchHint,
|
||||
);
|
||||
default:
|
||||
logger.error(
|
||||
`Failed to parse Jellyfin response for item type ${searchHint.Type}`,
|
||||
|
Loading…
Reference in New Issue
Block a user