mirror of
https://github.com/informaticker/discord-jellyfin-bot.git
synced 2024-11-25 02:51:57 +01:00
parent
9ecce22f14
commit
1ec65c93a8
@ -10,11 +10,9 @@ import { DiscordConfigService } from './clients/discord/discord.config.service';
|
||||
import { DiscordClientModule } from './clients/discord/discord.module';
|
||||
import { JellyfinClientModule } from './clients/jellyfin/jellyfin.module';
|
||||
import { CommandModule } from './commands/command.module';
|
||||
import { HealthModule } from './health/health.module';
|
||||
import { PlaybackModule } from './playback/playback.module';
|
||||
import { UpdatesModule } from './updates/updates.module';
|
||||
import { HealthController } from './health/health.controller';
|
||||
import { HealthModule } from './health/health.module';
|
||||
import { TerminusModule } from '@nestjs/terminus';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
|
@ -11,7 +11,7 @@ import {
|
||||
} from '@discordjs/voice';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { Logger } from '@nestjs/common/services';
|
||||
import { OnEvent } from '@nestjs/event-emitter';
|
||||
import { EventEmitter2, OnEvent } from '@nestjs/event-emitter';
|
||||
import { GuildMember } from 'discord.js';
|
||||
import { GenericTryHandler } from '../../models/generic-try-handler';
|
||||
import { PlaybackService } from '../../playback/playback.service';
|
||||
@ -29,6 +29,7 @@ export class DiscordVoiceService {
|
||||
private readonly discordMessageService: DiscordMessageService,
|
||||
private readonly playbackService: PlaybackService,
|
||||
private readonly jellyfinWebSocketService: JellyfinWebSocketService,
|
||||
private readonly eventEmitter: EventEmitter2,
|
||||
) {}
|
||||
|
||||
@OnEvent('playback.newTrack')
|
||||
@ -95,15 +96,20 @@ export class DiscordVoiceService {
|
||||
/**
|
||||
* Pauses the current audio player
|
||||
*/
|
||||
@OnEvent('playback.control.pause')
|
||||
pause() {
|
||||
this.createAndReturnOrGetAudioPlayer().pause();
|
||||
this.eventEmitter.emit('playback.state.pause', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the audio player
|
||||
*/
|
||||
@OnEvent('playback.control.stop')
|
||||
stop(force: boolean): boolean {
|
||||
return this.createAndReturnOrGetAudioPlayer().stop(force);
|
||||
const stopped = this.createAndReturnOrGetAudioPlayer().stop(force);
|
||||
this.eventEmitter.emit('playback.state.stop');
|
||||
return stopped;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -111,6 +117,7 @@ export class DiscordVoiceService {
|
||||
*/
|
||||
unpause() {
|
||||
this.createAndReturnOrGetAudioPlayer().unpause();
|
||||
this.eventEmitter.emit('playback.state.pause', false);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -136,6 +143,7 @@ export class DiscordVoiceService {
|
||||
* Checks if the current state is paused or not and toggles the states to the opposite.
|
||||
* @returns The new paused state - true: paused, false: unpaused
|
||||
*/
|
||||
@OnEvent('playback.control.togglePause')
|
||||
togglePaused(): boolean {
|
||||
if (this.isPaused()) {
|
||||
this.unpause();
|
||||
|
@ -9,12 +9,17 @@ import {
|
||||
} from '@jellyfin/sdk/lib/generated-client/models';
|
||||
import { getPlaystateApi } from '@jellyfin/sdk/lib/utils/api/playstate-api';
|
||||
import { getSessionApi } from '@jellyfin/sdk/lib/utils/api/session-api';
|
||||
import { OnEvent } from '@nestjs/event-emitter';
|
||||
import { Track } from '../../types/track';
|
||||
import { PlaybackService } from '../../playback/playback.service';
|
||||
|
||||
@Injectable()
|
||||
export class JellyinPlaystateService {
|
||||
private playstateApi: PlaystateApi;
|
||||
private sessionApi: SessionApi;
|
||||
|
||||
constructor(private readonly playbackService: PlaybackService) {}
|
||||
|
||||
private readonly logger = new Logger(JellyinPlaystateService.name);
|
||||
|
||||
async initializePlayState(api: Api) {
|
||||
@ -32,7 +37,6 @@ export class JellyinPlaystateService {
|
||||
playableMediaTypes: [BaseItemKind[BaseItemKind.Audio]],
|
||||
supportsMediaControl: true,
|
||||
supportedCommands: [
|
||||
GeneralCommandType.SetRepeatMode,
|
||||
GeneralCommandType.Play,
|
||||
GeneralCommandType.PlayState,
|
||||
],
|
||||
@ -40,4 +44,36 @@ export class JellyinPlaystateService {
|
||||
|
||||
this.logger.debug('Reported playback capabilities sucessfully');
|
||||
}
|
||||
|
||||
@OnEvent('playback.newTrack')
|
||||
private async onPlaybackNewTrack(track: Track) {
|
||||
await this.playstateApi.reportPlaybackStart({
|
||||
playbackStartInfo: {
|
||||
ItemId: track.jellyfinId,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@OnEvent('playback.state.pause')
|
||||
private async onPlaybackPaused(isPaused: boolean) {
|
||||
const activeTrack = this.playbackService.getActiveTrack();
|
||||
|
||||
await this.playstateApi.reportPlaybackProgress({
|
||||
playbackProgressInfo: {
|
||||
ItemId: activeTrack.track.jellyfinId,
|
||||
IsPaused: isPaused,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@OnEvent('playback.state.stop')
|
||||
private async onPlaybackStopped() {
|
||||
const activeTrack = this.playbackService.getActiveTrack();
|
||||
|
||||
await this.playstateApi.reportPlaybackStopped({
|
||||
playbackStopInfo: {
|
||||
ItemId: activeTrack.track.jellyfinId,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -2,13 +2,17 @@ import { Injectable, Logger, OnModuleDestroy } from '@nestjs/common';
|
||||
import { Cron } from '@nestjs/schedule';
|
||||
import { JellyfinService } from './jellyfin.service';
|
||||
|
||||
import { SessionMessageType } from '@jellyfin/sdk/lib/generated-client/models';
|
||||
import {
|
||||
PlaystateCommand,
|
||||
SessionMessageType,
|
||||
} from '@jellyfin/sdk/lib/generated-client/models';
|
||||
import { WebSocket } from 'ws';
|
||||
import { PlaybackService } from '../../playback/playback.service';
|
||||
import { JellyfinSearchService } from './jellyfin.search.service';
|
||||
import { JellyfinStreamBuilderService } from './jellyfin.stream.builder.service';
|
||||
import { Track } from '../../types/track';
|
||||
import { PlayNowCommand } from '../../types/websocket';
|
||||
import { PlayNowCommand, SessionApiSendPlaystateCommandRequest } from '../../types/websocket';
|
||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||
|
||||
@Injectable()
|
||||
export class JellyfinWebSocketService implements OnModuleDestroy {
|
||||
@ -21,6 +25,7 @@ export class JellyfinWebSocketService implements OnModuleDestroy {
|
||||
private readonly jellyfinSearchService: JellyfinSearchService,
|
||||
private readonly playbackService: PlaybackService,
|
||||
private readonly jellyfinStreamBuilderService: JellyfinStreamBuilderService,
|
||||
private readonly eventEmitter: EventEmitter2,
|
||||
) {}
|
||||
|
||||
@Cron('*/30 * * * * *')
|
||||
@ -130,6 +135,11 @@ export class JellyfinWebSocketService implements OnModuleDestroy {
|
||||
});
|
||||
});
|
||||
break;
|
||||
case SessionMessageType[SessionMessageType.Playstate]:
|
||||
const sendPlaystateCommandRequest =
|
||||
msg.Data as SessionApiSendPlaystateCommandRequest;
|
||||
this.handleSendPlaystateCommandRequest(sendPlaystateCommandRequest);
|
||||
break;
|
||||
default:
|
||||
this.logger.warn(
|
||||
`Received a package from the socket of unknown type: ${msg.MessageType}`,
|
||||
@ -138,6 +148,27 @@ export class JellyfinWebSocketService implements OnModuleDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
private async handleSendPlaystateCommandRequest(
|
||||
request: SessionApiSendPlaystateCommandRequest,
|
||||
) {
|
||||
switch (request.Command) {
|
||||
case PlaystateCommand.PlayPause:
|
||||
this.eventEmitter.emitAsync('playback.control.togglePause');
|
||||
break;
|
||||
case PlaystateCommand.Pause:
|
||||
this.eventEmitter.emitAsync('playback.control.pause');
|
||||
break;
|
||||
case PlaystateCommand.Stop:
|
||||
this.eventEmitter.emitAsync('playback.control.stop');
|
||||
break;
|
||||
default:
|
||||
this.logger.warn(
|
||||
`Unable to process incoming playstate command request: ${request.Command}`,
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private bindWebSocketEvents() {
|
||||
this.webSocket.on('message', this.messageHandler.bind(this));
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
import { PlaystateCommand } from '@jellyfin/sdk/lib/generated-client/models';
|
||||
|
||||
export class PlayNowCommand {
|
||||
/**
|
||||
* A list of all items available in the parent element.
|
||||
@ -35,3 +37,24 @@ export class PlayNowCommand {
|
||||
return this.ItemIds;
|
||||
}
|
||||
}
|
||||
|
||||
export interface SessionApiSendPlaystateCommandRequest {
|
||||
/**
|
||||
* The MediaBrowser.Model.Session.PlaystateCommand.
|
||||
* @type {PlaystateCommand}
|
||||
* @memberof SessionApiSendPlaystateCommand
|
||||
*/
|
||||
readonly Command: PlaystateCommand;
|
||||
/**
|
||||
* The optional position ticks.
|
||||
* @type {number}
|
||||
* @memberof SessionApiSendPlaystateCommand
|
||||
*/
|
||||
readonly SeekPositionTicks?: number;
|
||||
/**
|
||||
* The optional controlling user id.
|
||||
* @type {string}
|
||||
* @memberof SessionApiSendPlaystateCommand
|
||||
*/
|
||||
readonly ControllingUserId?: string;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user