From ea64f1666107a366404192b2814b4f2d3a85d646 Mon Sep 17 00:00:00 2001 From: Manuel <30572287+manuel-rw@users.noreply.github.com> Date: Sun, 26 Mar 2023 01:29:42 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Add=20volume=20command=20(#132)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/clients/discord/discord.voice.service.ts | 13 +++- src/commands/command.module.ts | 2 + src/commands/volume/volume.command.ts | 70 ++++++++++++++++++++ src/commands/volume/volume.params.ts | 12 ++++ src/utils/timeUtils.ts | 4 ++ 5 files changed, 99 insertions(+), 2 deletions(-) create mode 100644 src/commands/volume/volume.command.ts create mode 100644 src/commands/volume/volume.params.ts diff --git a/src/clients/discord/discord.voice.service.ts b/src/clients/discord/discord.voice.service.ts index a2a9fb4..615fbc9 100644 --- a/src/clients/discord/discord.voice.service.ts +++ b/src/clients/discord/discord.voice.service.ts @@ -29,8 +29,9 @@ import { DiscordMessageService } from './discord.message.service'; @Injectable() export class DiscordVoiceService { private readonly logger = new Logger(DiscordVoiceService.name); - private audioPlayer: AudioPlayer; - private voiceConnection: VoiceConnection; + private audioPlayer: AudioPlayer | undefined; + private voiceConnection: VoiceConnection | undefined; + private audioResource: AudioResource | undefined; constructor( private readonly discordMessageService: DiscordMessageService, @@ -44,6 +45,9 @@ export class DiscordVoiceService { handleOnNewTrack(track: Track) { const resource = createAudioResource( track.getStreamUrl(this.jellyfinStreamBuilder), + { + inlineVolume: true, + }, ); this.playResource(resource); } @@ -99,9 +103,14 @@ export class DiscordVoiceService { }; } + changeVolume(volume: number) { + this.audioResource.volume.setVolume(volume); + } + playResource(resource: AudioResource) { this.logger.debug(`Playing audio resource with volume ${resource.volume}`); this.createAndReturnOrGetAudioPlayer().play(resource); + this.audioResource = resource; } /** diff --git a/src/commands/command.module.ts b/src/commands/command.module.ts index 9981a94..4b290bd 100644 --- a/src/commands/command.module.ts +++ b/src/commands/command.module.ts @@ -16,6 +16,7 @@ import { StopPlaybackCommand } from './stop.command'; import { SummonCommand } from './summon.command'; import { PlaylistInteractionCollector } from './playlist/playlist.interaction-collector'; import { EnqueueRandomItemsCommand } from './random/random.command'; +import { VolumeCommand } from './volume/volume.command'; @Module({ imports: [ @@ -38,6 +39,7 @@ import { EnqueueRandomItemsCommand } from './random/random.command'; SummonCommand, PlayItemCommand, PreviousTrackCommand, + VolumeCommand, ], exports: [], }) diff --git a/src/commands/volume/volume.command.ts b/src/commands/volume/volume.command.ts new file mode 100644 index 0000000..30f8f00 --- /dev/null +++ b/src/commands/volume/volume.command.ts @@ -0,0 +1,70 @@ +import { SlashCommandPipe } from '@discord-nestjs/common'; +import { Command, Handler, IA, InteractionEvent } from '@discord-nestjs/core'; +import { Logger } from '@nestjs/common'; + +import { Injectable } from '@nestjs/common/decorators'; + +import { CommandInteraction } from 'discord.js'; +import e from 'express'; +import { DiscordMessageService } from 'src/clients/discord/discord.message.service'; +import { DiscordVoiceService } from 'src/clients/discord/discord.voice.service'; +import { PlaybackService } from 'src/playback/playback.service'; +import { sleep } from 'src/utils/timeUtils'; +import { VolumeCommandParams } from './volume.params'; + +@Injectable() +@Command({ + name: 'volume', + description: 'Change the volume', +}) +export class VolumeCommand { + private readonly logger = new Logger(VolumeCommand.name); + + constructor( + private readonly discordVoiceService: DiscordVoiceService, + private readonly discordMessageService: DiscordMessageService, + private readonly playbackService: PlaybackService, + ) {} + + @Handler() + async handler( + @InteractionEvent(SlashCommandPipe) dto: VolumeCommandParams, + @IA() interaction: CommandInteraction, + ): Promise { + await interaction.deferReply(); + + if (!this.playbackService.getPlaylistOrDefault().hasActiveTrack()) { + await interaction.editReply({ + embeds: [ + this.discordMessageService.buildMessage({ + title: `Unable to change your volume`, + description: + 'The bot is not playing any music or is not straming to a channel', + }), + ], + }); + return; + } + + const volume = dto.volume / 100; + + this.logger.debug( + `Calculated volume ${volume} from dto param ${dto.volume}`, + ); + + this.discordVoiceService.changeVolume(volume); + + // Discord takes some time to react. Confirmation message should appear after actual change + await sleep(1500); + + await interaction.editReply({ + embeds: [ + this.discordMessageService.buildMessage({ + title: `Sucessfully set volume to ${dto.volume.toFixed(0)}%`, + description: + 'Updating may take a few seconds to take effect.\nPlease note that listening at a high volume for a long time may damage your hearing', + }), + ], + }); + } +} diff --git a/src/commands/volume/volume.params.ts b/src/commands/volume/volume.params.ts new file mode 100644 index 0000000..117a72c --- /dev/null +++ b/src/commands/volume/volume.params.ts @@ -0,0 +1,12 @@ +import { Param, ParamType } from '@discord-nestjs/core'; + +export class VolumeCommandParams { + @Param({ + required: true, + description: 'The desired volume', + type: ParamType.INTEGER, + minValue: 0, + maxValue: 150, + }) + volume: number; +} diff --git a/src/utils/timeUtils.ts b/src/utils/timeUtils.ts index c6b5167..ea3e860 100644 --- a/src/utils/timeUtils.ts +++ b/src/utils/timeUtils.ts @@ -15,3 +15,7 @@ export const formatMillisecondsAsHumanReadable = ( ); return duration; }; + +export function sleep(ms: number) { + return new Promise((resolve) => setTimeout(resolve, ms)); +}