♻️ Model names (#246)

This commit is contained in:
Manuel 2023-11-19 19:44:30 +01:00 committed by GitHub
parent 56d7f0f03d
commit e04e5cae50
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 60 additions and 62 deletions

View File

@ -16,10 +16,10 @@ import { Logger } from '@nestjs/common/services';
import { EventEmitter2, OnEvent } from '@nestjs/event-emitter'; import { EventEmitter2, OnEvent } from '@nestjs/event-emitter';
import { Interval } from '@nestjs/schedule'; import { Interval } from '@nestjs/schedule';
import { GuildMember } from 'discord.js'; import { APIEmbed, GuildMember, InteractionEditReplyOptions, InteractionReplyOptions, MessagePayload } from 'discord.js';
import { GenericTryHandler } from '../../models/generic-try-handler'; import { TryResult } from '../../models/TryResult';
import { Track } from '../../models/shared/Track'; import { Track } from '../../models/music/Track';
import { PlaybackService } from '../../playback/playback.service'; import { PlaybackService } from '../../playback/playback.service';
import { JellyfinStreamBuilderService } from '../jellyfin/jellyfin.stream.builder.service'; import { JellyfinStreamBuilderService } from '../jellyfin/jellyfin.stream.builder.service';
import { JellyfinWebSocketService } from '../jellyfin/jellyfin.websocket.service'; import { JellyfinWebSocketService } from '../jellyfin/jellyfin.websocket.service';
@ -54,7 +54,7 @@ export class DiscordVoiceService {
tryJoinChannelAndEstablishVoiceConnection( tryJoinChannelAndEstablishVoiceConnection(
member: GuildMember, member: GuildMember,
): GenericTryHandler { ): TryResult<InteractionReplyOptions> {
if (this.voiceConnection !== undefined) { if (this.voiceConnection !== undefined) {
this.logger.debug( this.logger.debug(
'Avoided joining the voice channel because voice connection is already defined', 'Avoided joining the voice channel because voice connection is already defined',
@ -182,7 +182,7 @@ export class DiscordVoiceService {
return true; return true;
} }
disconnect(): GenericTryHandler { disconnect(): TryResult<string | MessagePayload | InteractionEditReplyOptions> {
if (this.voiceConnection === undefined) { if (this.voiceConnection === undefined) {
return { return {
success: false, success: false,

View File

@ -11,7 +11,7 @@ import { getSessionApi } from '@jellyfin/sdk/lib/utils/api/session-api';
import { Injectable, Logger } from '@nestjs/common'; import { Injectable, Logger } from '@nestjs/common';
import { OnEvent } from '@nestjs/event-emitter'; import { OnEvent } from '@nestjs/event-emitter';
import { Interval } from '@nestjs/schedule'; import { Interval } from '@nestjs/schedule';
import { Track } from '../../models/shared/Track'; import { Track } from '../../models/music/Track';
import { PlaybackService } from '../../playback/playback.service'; import { PlaybackService } from '../../playback/playback.service';

View File

@ -12,9 +12,9 @@ import { getSearchApi } from '@jellyfin/sdk/lib/utils/api/search-api';
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import { Logger } from '@nestjs/common/services'; import { Logger } from '@nestjs/common/services';
import { AlbumSearchHint } from '../../models/search/AlbumSearchHint'; import { AlbumSearchItem } from '../../models/search/AlbumSearchItem';
import { PlaylistSearchHint } from '../../models/search/PlaylistSearchHint'; import { PlaylistSearchItem } from '../../models/search/PlaylistSearchItem';
import { SearchHint } from '../../models/search/SearchHint'; import { SearchItem } from '../../models/search/SearchItem';
import { JellyfinService } from './jellyfin.service'; import { JellyfinService } from './jellyfin.service';
@ -32,7 +32,7 @@ export class JellyfinSearchService {
BaseItemKind.MusicAlbum, BaseItemKind.MusicAlbum,
BaseItemKind.Playlist, BaseItemKind.Playlist,
], ],
): Promise<SearchHint[]> { ): Promise<SearchItem[]> {
const api = this.jellyfinService.getApi(); const api = this.jellyfinService.getApi();
const searchApi = getSearchApi(api); const searchApi = getSearchApi(api);
@ -64,14 +64,14 @@ export class JellyfinSearchService {
return SearchHints.map((hint) => return SearchHints.map((hint) =>
this.transformToSearchHintFromHint(hint), this.transformToSearchHintFromHint(hint),
).filter((x) => x !== null) as SearchHint[]; ).filter((x) => x !== null) as SearchItem[];
} catch (err) { } catch (err) {
this.logger.error(`Failed to search on Jellyfin: ${err}`); this.logger.error(`Failed to search on Jellyfin: ${err}`);
return []; return [];
} }
} }
async getPlaylistitems(id: string): Promise<SearchHint[]> { async getPlaylistitems(id: string): Promise<SearchItem[]> {
const api = this.jellyfinService.getApi(); const api = this.jellyfinService.getApi();
const searchApi = getPlaylistsApi(api); const searchApi = getPlaylistsApi(api);
@ -95,11 +95,11 @@ export class JellyfinSearchService {
} }
return axiosResponse.data.Items.map((hint) => return axiosResponse.data.Items.map((hint) =>
SearchHint.constructFromBaseItem(hint), SearchItem.constructFromBaseItem(hint),
); );
} }
async getAlbumItems(albumId: string): Promise<SearchHint[]> { async getAlbumItems(albumId: string): Promise<SearchItem[]> {
const api = this.jellyfinService.getApi(); const api = this.jellyfinService.getApi();
const searchApi = getSearchApi(api); const searchApi = getSearchApi(api);
const axiosResponse = await searchApi.get({ const axiosResponse = await searchApi.get({
@ -125,13 +125,13 @@ export class JellyfinSearchService {
return [...axiosResponse.data.SearchHints] return [...axiosResponse.data.SearchHints]
.reverse() .reverse()
.map((hint) => SearchHint.constructFromHint(hint)); .map((hint) => SearchItem.constructFromHint(hint));
} }
async getById( async getById(
id: string, id: string,
includeItemTypes: BaseItemKind[], includeItemTypes: BaseItemKind[],
): Promise<SearchHint | undefined> { ): Promise<SearchItem | undefined> {
const api = this.jellyfinService.getApi(); const api = this.jellyfinService.getApi();
const searchApi = getItemsApi(api); const searchApi = getItemsApi(api);
@ -152,7 +152,7 @@ export class JellyfinSearchService {
async getAllById( async getAllById(
ids: string[], ids: string[],
includeItemTypes: BaseItemKind[] = [BaseItemKind.Audio], includeItemTypes: BaseItemKind[] = [BaseItemKind.Audio],
): Promise<SearchHint[]> { ): Promise<SearchItem[]> {
const api = this.jellyfinService.getApi(); const api = this.jellyfinService.getApi();
const searchApi = getItemsApi(api); const searchApi = getItemsApi(api);
@ -169,7 +169,7 @@ export class JellyfinSearchService {
return data.Items.map((item) => return data.Items.map((item) =>
this.transformToSearchHintFromBaseItemDto(item), this.transformToSearchHintFromBaseItemDto(item),
).filter((searchHint) => searchHint !== undefined) as SearchHint[]; ).filter((searchHint) => searchHint !== undefined) as SearchItem[];
} }
async getRemoteImageById(id: string, limit = 20): Promise<RemoteImageResult> { async getRemoteImageById(id: string, limit = 20): Promise<RemoteImageResult> {
@ -233,7 +233,7 @@ export class JellyfinSearchService {
} }
return response.data.Items.map((item) => { return response.data.Items.map((item) => {
return SearchHint.constructFromBaseItem(item); return SearchItem.constructFromBaseItem(item);
}); });
} catch (err) { } catch (err) {
this.logger.error( this.logger.error(
@ -246,11 +246,11 @@ export class JellyfinSearchService {
private transformToSearchHintFromHint(jellyifnHint: JellyfinSearchHint) { private transformToSearchHintFromHint(jellyifnHint: JellyfinSearchHint) {
switch (jellyifnHint.Type) { switch (jellyifnHint.Type) {
case BaseItemKind[BaseItemKind.Audio]: case BaseItemKind[BaseItemKind.Audio]:
return SearchHint.constructFromHint(jellyifnHint); return SearchItem.constructFromHint(jellyifnHint);
case BaseItemKind[BaseItemKind.MusicAlbum]: case BaseItemKind[BaseItemKind.MusicAlbum]:
return AlbumSearchHint.constructFromHint(jellyifnHint); return AlbumSearchItem.constructFromHint(jellyifnHint);
case BaseItemKind[BaseItemKind.Playlist]: case BaseItemKind[BaseItemKind.Playlist]:
return PlaylistSearchHint.constructFromHint(jellyifnHint); return PlaylistSearchItem.constructFromHint(jellyifnHint);
default: default:
this.logger.warn( this.logger.warn(
`Received unexpected item type from Jellyfin search: ${jellyifnHint.Type}`, `Received unexpected item type from Jellyfin search: ${jellyifnHint.Type}`,
@ -262,11 +262,11 @@ export class JellyfinSearchService {
private transformToSearchHintFromBaseItemDto(baseItemDto: BaseItemDto) { private transformToSearchHintFromBaseItemDto(baseItemDto: BaseItemDto) {
switch (baseItemDto.Type) { switch (baseItemDto.Type) {
case BaseItemKind[BaseItemKind.Audio]: case BaseItemKind[BaseItemKind.Audio]:
return SearchHint.constructFromBaseItem(baseItemDto); return SearchItem.constructFromBaseItem(baseItemDto);
case BaseItemKind[BaseItemKind.MusicAlbum]: case BaseItemKind[BaseItemKind.MusicAlbum]:
return AlbumSearchHint.constructFromBaseItem(baseItemDto); return AlbumSearchItem.constructFromBaseItem(baseItemDto);
case BaseItemKind[BaseItemKind.Playlist]: case BaseItemKind[BaseItemKind.Playlist]:
return PlaylistSearchHint.constructFromBaseItem(baseItemDto); return PlaylistSearchItem.constructFromBaseItem(baseItemDto);
default: default:
this.logger.warn( this.logger.warn(
`Received unexpected item type from Jellyfin search: ${baseItemDto.Type}`, `Received unexpected item type from Jellyfin search: ${baseItemDto.Type}`,

View File

@ -6,7 +6,7 @@ import {
import { Injectable, Logger, OnModuleDestroy } from '@nestjs/common'; import { Injectable, Logger, OnModuleDestroy } from '@nestjs/common';
import { EventEmitter2 } from '@nestjs/event-emitter'; import { EventEmitter2 } from '@nestjs/event-emitter';
import { Cron } from '@nestjs/schedule'; import { Cron } from '@nestjs/schedule';
import { convertToTracks } from 'src/utils/trackConverter'; import { flatMapTrackItems } from 'src/utils/trackConverter';
import { WebSocket } from 'ws'; import { WebSocket } from 'ws';
@ -105,7 +105,7 @@ export class JellyfinWebSocketService implements OnModuleDestroy {
`Processing ${ids.length} ids received via websocket and adding them to the queue`, `Processing ${ids.length} ids received via websocket and adding them to the queue`,
); );
const searchHints = await this.jellyfinSearchService.getAllById(ids); const searchHints = await this.jellyfinSearchService.getAllById(ids);
const tracks = convertToTracks(searchHints, this.jellyfinSearchService); const tracks = flatMapTrackItems(searchHints, this.jellyfinSearchService);
this.playbackService.getPlaylistOrDefault().enqueueTracks(tracks); this.playbackService.getPlaylistOrDefault().enqueueTracks(tracks);
break; break;
case SessionMessageType[SessionMessageType.Playstate]: case SessionMessageType[SessionMessageType.Playstate]:

View File

@ -23,7 +23,7 @@ import {
import { DiscordMessageService } from '../../clients/discord/discord.message.service'; import { DiscordMessageService } from '../../clients/discord/discord.message.service';
import { DiscordVoiceService } from '../../clients/discord/discord.voice.service'; import { DiscordVoiceService } from '../../clients/discord/discord.voice.service';
import { JellyfinSearchService } from '../../clients/jellyfin/jellyfin.search.service'; import { JellyfinSearchService } from '../../clients/jellyfin/jellyfin.search.service';
import { SearchHint } from '../../models/search/SearchHint'; import { SearchItem } from '../../models/search/SearchItem';
import { PlaybackService } from '../../playback/playback.service'; import { PlaybackService } from '../../playback/playback.service';
import { formatMillisecondsAsHumanReadable } from '../../utils/timeUtils'; import { formatMillisecondsAsHumanReadable } from '../../utils/timeUtils';
@ -55,7 +55,7 @@ export class PlayItemCommand {
const baseItems = PlayCommandParams.getBaseItemKinds(dto.type); const baseItems = PlayCommandParams.getBaseItemKinds(dto.type);
let item: SearchHint | undefined; let item: SearchItem | undefined;
if (dto.name.startsWith('native-')) { if (dto.name.startsWith('native-')) {
item = await this.jellyfinSearchService.getById( item = await this.jellyfinSearchService.getById(
dto.name.replace('native-', ''), dto.name.replace('native-', ''),

View File

@ -20,7 +20,7 @@ import {
} from 'discord.js'; } from 'discord.js';
import { DiscordMessageService } from '../../clients/discord/discord.message.service'; import { DiscordMessageService } from '../../clients/discord/discord.message.service';
import { Track } from '../../models/shared/Track'; import { Track } from '../../models/music/Track';
import { PlaybackService } from '../../playback/playback.service'; import { PlaybackService } from '../../playback/playback.service';
import { chunkArray } from '../../utils/arrayUtils'; import { chunkArray } from '../../utils/arrayUtils';
import { import {

View File

@ -9,7 +9,7 @@ import {
import { DiscordMessageService } from 'src/clients/discord/discord.message.service'; import { DiscordMessageService } from 'src/clients/discord/discord.message.service';
import { DiscordVoiceService } from 'src/clients/discord/discord.voice.service'; import { DiscordVoiceService } from 'src/clients/discord/discord.voice.service';
import { JellyfinSearchService } from 'src/clients/jellyfin/jellyfin.search.service'; import { JellyfinSearchService } from 'src/clients/jellyfin/jellyfin.search.service';
import { SearchHint } from 'src/models/search/SearchHint'; import { SearchItem } from 'src/models/search/SearchItem';
import { PlaybackService } from 'src/playback/playback.service'; import { PlaybackService } from 'src/playback/playback.service';
import { RandomCommandParams } from './random.params'; import { RandomCommandParams } from './random.params';
import { defaultMemberPermissions } from 'src/utils/environment'; import { defaultMemberPermissions } from 'src/utils/environment';
@ -65,7 +65,7 @@ export class EnqueueRandomItemsCommand {
}); });
} }
private async getTracks(hints: SearchHint[]) { private async getTracks(hints: SearchItem[]) {
const promises = await Promise.all( const promises = await Promise.all(
hints.flatMap(async (item) => { hints.flatMap(async (item) => {
const tracks = await item.toTracks(this.jellyfinSearchService); const tracks = await item.toTracks(this.jellyfinSearchService);

View File

@ -8,7 +8,7 @@ import { CommandInteraction } from 'discord.js';
import { DiscordMessageService } from 'src/clients/discord/discord.message.service'; import { DiscordMessageService } from 'src/clients/discord/discord.message.service';
import { DiscordVoiceService } from 'src/clients/discord/discord.voice.service'; import { DiscordVoiceService } from 'src/clients/discord/discord.voice.service';
import { PlaybackService } from 'src/playback/playback.service'; import { PlaybackService } from 'src/playback/playback.service';
import { sleep } from 'src/utils/timeUtils'; import { sleepAsync } from 'src/utils/timeUtils';
import { VolumeCommandParams } from './volume.params'; import { VolumeCommandParams } from './volume.params';
import { defaultMemberPermissions } from 'src/utils/environment'; import { defaultMemberPermissions } from 'src/utils/environment';
@ -56,7 +56,7 @@ export class VolumeCommand {
this.discordVoiceService.changeVolume(volume); this.discordVoiceService.changeVolume(volume);
// Discord takes some time to react. Confirmation message should appear after actual change // Discord takes some time to react. Confirmation message should appear after actual change
await sleep(1500); await sleepAsync(1500);
await interaction.editReply({ await interaction.editReply({
embeds: [ embeds: [

4
src/models/TryResult.ts Normal file
View File

@ -0,0 +1,4 @@
export interface TryResult<T> {
success: boolean;
reply: T;
}

View File

@ -1,6 +0,0 @@
import { InteractionEditReplyOptions, MessagePayload } from 'discord.js';
export interface GenericTryHandler {
success: boolean;
reply: string | MessagePayload | InteractionEditReplyOptions;
}

View File

@ -1,12 +1,12 @@
import { SearchHint as JellyfinSearchHint } from '@jellyfin/sdk/lib/generated-client/models'; import { SearchHint as JellyfinSearchHint } from '@jellyfin/sdk/lib/generated-client/models';
import { Track } from '../shared/Track'; import { Track } from '../music/Track';
import { JellyfinSearchService } from '../../clients/jellyfin/jellyfin.search.service'; import { JellyfinSearchService } from '../../clients/jellyfin/jellyfin.search.service';
import { SearchHint } from './SearchHint'; import { SearchItem } from './SearchItem';
import { trimStringToFixedLength } from 'src/utils/stringUtils/stringUtils'; import { trimStringToFixedLength } from 'src/utils/stringUtils/stringUtils';
export class AlbumSearchHint extends SearchHint { export class AlbumSearchItem extends SearchItem {
override toString(): string { override toString(): string {
return `🎶 ${this.name}`; return `🎶 ${this.name}`;
} }
@ -22,7 +22,7 @@ export class AlbumSearchHint extends SearchHint {
artist = hint.AlbumArtist + " - " artist = hint.AlbumArtist + " - "
} }
return new AlbumSearchHint( return new AlbumSearchItem(
hint.Id, hint.Id,
trimStringToFixedLength(artist + hint.Name, 70), trimStringToFixedLength(artist + hint.Name, 70),
hint.RunTimeTicks / 10000, hint.RunTimeTicks / 10000,

View File

@ -1,13 +1,13 @@
import { SearchHint as JellyfinSearchHint } from '@jellyfin/sdk/lib/generated-client/models'; import { SearchHint as JellyfinSearchHint } from '@jellyfin/sdk/lib/generated-client/models';
import { Track } from '../shared/Track'; import { Track } from '../music/Track';
import { JellyfinSearchService } from '../../clients/jellyfin/jellyfin.search.service'; import { JellyfinSearchService } from '../../clients/jellyfin/jellyfin.search.service';
import { SearchHint } from './SearchHint'; import { SearchItem } from './SearchItem';
import { convertToTracks } from 'src/utils/trackConverter'; import { flatMapTrackItems } from 'src/utils/trackConverter';
import { trimStringToFixedLength } from 'src/utils/stringUtils/stringUtils'; import { trimStringToFixedLength } from 'src/utils/stringUtils/stringUtils';
export class PlaylistSearchHint extends SearchHint { export class PlaylistSearchItem extends SearchItem {
override toString(): string { override toString(): string {
return `🎧 ${this.name}`; return `🎧 ${this.name}`;
} }
@ -19,7 +19,7 @@ export class PlaylistSearchHint extends SearchHint {
); );
} }
return new PlaylistSearchHint( return new PlaylistSearchItem(
hint.Id, hint.Id,
trimStringToFixedLength(hint.Name, 50), trimStringToFixedLength(hint.Name, 50),
hint.RunTimeTicks / 10000, hint.RunTimeTicks / 10000,
@ -30,6 +30,6 @@ export class PlaylistSearchHint extends SearchHint {
searchService: JellyfinSearchService, searchService: JellyfinSearchService,
): Promise<Track[]> { ): Promise<Track[]> {
const playlistItems = await searchService.getPlaylistitems(this.id); const playlistItems = await searchService.getPlaylistitems(this.id);
return convertToTracks(playlistItems, searchService); return flatMapTrackItems(playlistItems, searchService);
} }
} }

View File

@ -5,10 +5,10 @@ import {
import { z } from 'zod'; import { z } from 'zod';
import { JellyfinSearchService } from '../../clients/jellyfin/jellyfin.search.service'; import { JellyfinSearchService } from '../../clients/jellyfin/jellyfin.search.service';
import { Track } from '../shared/Track'; import { Track } from '../music/Track';
import { trimStringToFixedLength } from 'src/utils/stringUtils/stringUtils'; import { trimStringToFixedLength } from 'src/utils/stringUtils/stringUtils';
export class SearchHint { export class SearchItem {
constructor( constructor(
protected readonly id: string, protected readonly id: string,
protected readonly name: string, protected readonly name: string,
@ -54,7 +54,7 @@ export class SearchHint {
artist += " - " artist += " - "
} }
} }
return new SearchHint( return new SearchItem(
result.data.Id, result.data.Id,
trimStringToFixedLength(artist + result.data.Name, 70), trimStringToFixedLength(artist + result.data.Name, 70),
result.data.RunTimeTicks / 10000, result.data.RunTimeTicks / 10000,
@ -67,7 +67,7 @@ export class SearchHint {
'Unable to construct search hint from base item, required properties were undefined', 'Unable to construct search hint from base item, required properties were undefined',
); );
} }
return new SearchHint( return new SearchItem(
baseItem.Id, baseItem.Id,
trimStringToFixedLength(baseItem.Name, 50), trimStringToFixedLength(baseItem.Name, 50),
baseItem.RunTimeTicks / 10000, baseItem.RunTimeTicks / 10000,

View File

@ -1,7 +1,7 @@
import { Injectable, Logger } from '@nestjs/common'; import { Injectable, Logger } from '@nestjs/common';
import { EventEmitter2, OnEvent } from '@nestjs/event-emitter'; import { EventEmitter2, OnEvent } from '@nestjs/event-emitter';
import { Playlist } from '../models/shared/Playlist'; import { Playlist } from '../models/music/Playlist';
@Injectable() @Injectable()
export class PlaybackService { export class PlaybackService {

View File

@ -3,7 +3,7 @@ import axios from 'axios';
import { Client, GuildMember } from 'discord.js'; import { Client, GuildMember } from 'discord.js';
import { Constants } from '../utils/constants'; import { Constants } from '../utils/constants';
import { DiscordMessageService } from '../clients/discord/discord.message.service'; import { DiscordMessageService } from '../clients/discord/discord.message.service';
import { GithubRelease } from '../models/github-release'; import { GithubRelease } from '../models/GithubRelease';
import { useDefaultMockerToken } from '../utils/tests/defaultMockerToken'; import { useDefaultMockerToken } from '../utils/tests/defaultMockerToken';
import { UpdatesService } from './updates.service'; import { UpdatesService } from './updates.service';
import { InjectionToken } from '@nestjs/common'; import { InjectionToken } from '@nestjs/common';

View File

@ -6,7 +6,7 @@ import axios from 'axios';
import { formatRelative, parseISO } from 'date-fns'; import { formatRelative, parseISO } from 'date-fns';
import { ActionRowBuilder, ButtonStyle, Client } from 'discord.js'; import { ActionRowBuilder, ButtonStyle, Client } from 'discord.js';
import { DiscordMessageService } from '../clients/discord/discord.message.service'; import { DiscordMessageService } from '../clients/discord/discord.message.service';
import { GithubRelease } from '../models/github-release'; import { GithubRelease } from '../models/GithubRelease';
import { Constants } from '../utils/constants'; import { Constants } from '../utils/constants';
@Injectable() @Injectable()

View File

@ -16,6 +16,6 @@ export const formatMillisecondsAsHumanReadable = (
return duration; return duration;
}; };
export function sleep(ms: number) { export function sleepAsync(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms)); return new Promise((resolve) => setTimeout(resolve, ms));
} }

View File

@ -1,9 +1,9 @@
import { JellyfinSearchService } from 'src/clients/jellyfin/jellyfin.search.service'; import { JellyfinSearchService } from 'src/clients/jellyfin/jellyfin.search.service';
import { SearchHint } from 'src/models/search/SearchHint'; import { SearchItem } from 'src/models/search/SearchItem';
import { Track } from 'src/models/shared/Track'; import { Track } from 'src/models/music/Track';
export const convertToTracks = ( export const flatMapTrackItems = (
hints: SearchHint[], hints: SearchItem[],
jellyfinSearchService: JellyfinSearchService, jellyfinSearchService: JellyfinSearchService,
): Track[] => { ): Track[] => {
let tracks: Track[] = []; let tracks: Track[] = [];