mirror of
https://github.com/informaticker/discord-jellyfin-bot.git
synced 2024-11-25 02:51:57 +01:00
✨ Reimplement websocket controls
This commit is contained in:
parent
8c5739a9e5
commit
fc74d1b4f3
@ -116,6 +116,7 @@ export class DiscordVoiceService {
|
|||||||
/**
|
/**
|
||||||
* Pauses the current audio player
|
* Pauses the current audio player
|
||||||
*/
|
*/
|
||||||
|
@OnEvent('internal.voice.controls.pause')
|
||||||
pause() {
|
pause() {
|
||||||
this.createAndReturnOrGetAudioPlayer().pause();
|
this.createAndReturnOrGetAudioPlayer().pause();
|
||||||
this.eventEmitter.emit('playback.state.pause', true);
|
this.eventEmitter.emit('playback.state.pause', true);
|
||||||
@ -124,6 +125,7 @@ export class DiscordVoiceService {
|
|||||||
/**
|
/**
|
||||||
* Stops the audio player
|
* Stops the audio player
|
||||||
*/
|
*/
|
||||||
|
@OnEvent('internal.voice.controls.stop')
|
||||||
stop(force: boolean): boolean {
|
stop(force: boolean): boolean {
|
||||||
const stopped = this.createAndReturnOrGetAudioPlayer().stop(force);
|
const stopped = this.createAndReturnOrGetAudioPlayer().stop(force);
|
||||||
this.eventEmitter.emit('playback.state.stop');
|
this.eventEmitter.emit('playback.state.stop');
|
||||||
@ -161,6 +163,7 @@ export class DiscordVoiceService {
|
|||||||
* Checks if the current state is paused or not and toggles the states to the opposite.
|
* 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
|
* @returns The new paused state - true: paused, false: unpaused
|
||||||
*/
|
*/
|
||||||
|
@OnEvent('internal.voice.controls.togglePause')
|
||||||
togglePaused(): boolean {
|
togglePaused(): boolean {
|
||||||
if (this.isPaused()) {
|
if (this.isPaused()) {
|
||||||
this.unpause();
|
this.unpause();
|
||||||
|
@ -71,4 +71,23 @@ export class JellyinPlaystateService {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OnEvent('playback.state.pause')
|
||||||
|
private async onPlaybackPause(paused: boolean) {
|
||||||
|
const track = this.playbackService.getPlaylistOrDefault().getActiveTrack();
|
||||||
|
|
||||||
|
if (!track) {
|
||||||
|
this.logger.error(
|
||||||
|
`Unable to report changed playstate to Jellyfin because no track was active`,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.playstateApi.reportPlaybackProgress({
|
||||||
|
playbackProgressInfo: {
|
||||||
|
IsPaused: paused,
|
||||||
|
ItemId: track.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -130,6 +130,27 @@ export class JellyfinSearchService {
|
|||||||
return this.transformToSearchHint(data.Items[0]);
|
return this.transformToSearchHint(data.Items[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getAllById(
|
||||||
|
ids: string[],
|
||||||
|
includeItemTypes: BaseItemKind[] = [BaseItemKind.Audio],
|
||||||
|
): Promise<SearchHint[]> | undefined {
|
||||||
|
const api = this.jellyfinService.getApi();
|
||||||
|
|
||||||
|
const searchApi = getItemsApi(api);
|
||||||
|
const { data } = await searchApi.getItems({
|
||||||
|
ids: ids,
|
||||||
|
userId: this.jellyfinService.getUserId(),
|
||||||
|
includeItemTypes: includeItemTypes,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (data.Items.length !== 1) {
|
||||||
|
this.logger.warn(`Failed to retrieve item via id '${ids}'`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return data.Items.map((item) => this.transformToSearchHint(item));
|
||||||
|
}
|
||||||
|
|
||||||
async getRemoteImageById(id: string, limit = 20): Promise<RemoteImageResult> {
|
async getRemoteImageById(id: string, limit = 20): Promise<RemoteImageResult> {
|
||||||
const api = this.jellyfinService.getApi();
|
const api = this.jellyfinService.getApi();
|
||||||
const remoteImageApi = getRemoteImageApi(api);
|
const remoteImageApi = getRemoteImageApi(api);
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
import {
|
import {
|
||||||
PlaystateCommand,
|
PlaystateCommand,
|
||||||
SessionMessageType,
|
SessionMessageType,
|
||||||
|
UserItemDataDto,
|
||||||
} from '@jellyfin/sdk/lib/generated-client/models';
|
} from '@jellyfin/sdk/lib/generated-client/models';
|
||||||
|
|
||||||
import { Injectable, Logger, OnModuleDestroy } from '@nestjs/common';
|
import { Injectable, Logger, OnModuleDestroy } from '@nestjs/common';
|
||||||
import { Cron } from '@nestjs/schedule';
|
|
||||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||||
|
import { Cron } from '@nestjs/schedule';
|
||||||
|
import { Session } from 'inspector';
|
||||||
|
|
||||||
import { WebSocket } from 'ws';
|
import { WebSocket } from 'ws';
|
||||||
|
|
||||||
@ -14,11 +16,9 @@ import {
|
|||||||
PlayNowCommand,
|
PlayNowCommand,
|
||||||
SessionApiSendPlaystateCommandRequest,
|
SessionApiSendPlaystateCommandRequest,
|
||||||
} from '../../types/websocket';
|
} from '../../types/websocket';
|
||||||
import { Track } from '../../models/shared/Track';
|
|
||||||
|
|
||||||
import { JellyfinSearchService } from './jellyfin.search.service';
|
import { JellyfinSearchService } from './jellyfin.search.service';
|
||||||
import { JellyfinService } from './jellyfin.service';
|
import { JellyfinService } from './jellyfin.service';
|
||||||
import { JellyfinStreamBuilderService } from './jellyfin.stream.builder.service';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class JellyfinWebSocketService implements OnModuleDestroy {
|
export class JellyfinWebSocketService implements OnModuleDestroy {
|
||||||
@ -28,9 +28,8 @@ export class JellyfinWebSocketService implements OnModuleDestroy {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly jellyfinService: JellyfinService,
|
private readonly jellyfinService: JellyfinService,
|
||||||
private readonly jellyfinSearchService: JellyfinSearchService,
|
|
||||||
private readonly playbackService: PlaybackService,
|
private readonly playbackService: PlaybackService,
|
||||||
private readonly jellyfinStreamBuilderService: JellyfinStreamBuilderService,
|
private readonly jellyfinSearchService: JellyfinSearchService,
|
||||||
private readonly eventEmitter: EventEmitter2,
|
private readonly eventEmitter: EventEmitter2,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@ -103,14 +102,29 @@ export class JellyfinWebSocketService implements OnModuleDestroy {
|
|||||||
data.hasSelection = PlayNowCommand.prototype.hasSelection;
|
data.hasSelection = PlayNowCommand.prototype.hasSelection;
|
||||||
data.getSelection = PlayNowCommand.prototype.getSelection;
|
data.getSelection = PlayNowCommand.prototype.getSelection;
|
||||||
const ids = data.getSelection();
|
const ids = data.getSelection();
|
||||||
|
this.logger.log(
|
||||||
|
`Processing ${ids.length} ids received via websocket and adding them to the queue`,
|
||||||
|
);
|
||||||
|
const searchHints = await this.jellyfinSearchService.getAllById(ids);
|
||||||
|
|
||||||
// TODO: Implement this again
|
const tracks = await Promise.all(
|
||||||
|
searchHints.map(async (x) =>
|
||||||
|
(
|
||||||
|
await x.toTracks(this.jellyfinSearchService)
|
||||||
|
).find((x) => x !== null),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
this.playbackService.getPlaylistOrDefault().enqueueTracks(tracks);
|
||||||
break;
|
break;
|
||||||
case SessionMessageType[SessionMessageType.Playstate]:
|
case SessionMessageType[SessionMessageType.Playstate]:
|
||||||
const sendPlaystateCommandRequest =
|
const sendPlaystateCommandRequest =
|
||||||
msg.Data as SessionApiSendPlaystateCommandRequest;
|
msg.Data as SessionApiSendPlaystateCommandRequest;
|
||||||
this.handleSendPlaystateCommandRequest(sendPlaystateCommandRequest);
|
this.handleSendPlaystateCommandRequest(sendPlaystateCommandRequest);
|
||||||
break;
|
break;
|
||||||
|
case SessionMessageType[SessionMessageType.UserDataChanged]:
|
||||||
|
this.logger.debug(`Received update for user session data`);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
this.logger.warn(
|
this.logger.warn(
|
||||||
`Received a package from the socket of unknown type: ${msg.MessageType}`,
|
`Received a package from the socket of unknown type: ${msg.MessageType}`,
|
||||||
@ -124,13 +138,13 @@ export class JellyfinWebSocketService implements OnModuleDestroy {
|
|||||||
) {
|
) {
|
||||||
switch (request.Command) {
|
switch (request.Command) {
|
||||||
case PlaystateCommand.PlayPause:
|
case PlaystateCommand.PlayPause:
|
||||||
this.eventEmitter.emitAsync('playback.control.togglePause');
|
this.eventEmitter.emitAsync('internal.voice.controls.togglePause');
|
||||||
break;
|
break;
|
||||||
case PlaystateCommand.Pause:
|
case PlaystateCommand.Pause:
|
||||||
this.eventEmitter.emitAsync('playback.control.pause');
|
this.eventEmitter.emitAsync('internal.voice.controls.pause');
|
||||||
break;
|
break;
|
||||||
case PlaystateCommand.Stop:
|
case PlaystateCommand.Stop:
|
||||||
this.eventEmitter.emitAsync('playback.control.stop');
|
this.eventEmitter.emitAsync('internal.voice.controls.stop');
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
this.logger.warn(
|
this.logger.warn(
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
import { EventEmitter2, OnEvent } from '@nestjs/event-emitter';
|
||||||
|
|
||||||
import { Track } from './Track';
|
import { Track } from './Track';
|
||||||
|
|
||||||
|
@ -39,6 +39,7 @@ export class Track {
|
|||||||
this.name = name;
|
this.name = name;
|
||||||
this.duration = duration;
|
this.duration = duration;
|
||||||
this.remoteImages = remoteImages;
|
this.remoteImages = remoteImages;
|
||||||
|
this.playing = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
getDuration() {
|
getDuration() {
|
||||||
|
Loading…
Reference in New Issue
Block a user