discord-jellyfin-bot/src/playbackmanager.js

324 lines
9.8 KiB
JavaScript
Raw Normal View History

2020-09-25 13:11:13 +02:00
const interactivemsghandler = require("./interactivemsghandler");
2020-09-30 17:27:09 +02:00
const CONFIG = require("../config.json");
2020-09-23 16:47:12 +02:00
const discordclientmanager = require("./discordclientmanager");
2021-01-18 06:49:32 +01:00
const log = require("loglevel");
const {
2020-09-21 05:30:39 +02:00
getAudioDispatcher,
setAudioDispatcher
} = require("./dispachermanager");
2020-09-23 16:47:12 +02:00
const {
ticksToSeconds
} = require("./util");
2020-09-24 13:37:17 +02:00
// this whole thing should be a class but its probably too late now.
2020-09-24 13:34:39 +02:00
var currentPlayingPlaylist;
var currentPlayingPlaylistIndex;
var isPaused;
2020-09-24 13:34:39 +02:00
var isRepeat;
2020-09-23 16:47:12 +02:00
var _disconnectOnFinish;
var _seek;
2020-09-21 05:30:39 +02:00
const jellyfinClientManager = require("./jellyfinclientmanager");
2020-09-24 13:34:39 +02:00
2020-09-24 13:37:17 +02:00
function streamURLbuilder (itemID, bitrate) {
2020-09-29 13:32:52 +02:00
// so the server transcodes. Seems appropriate as it has the source file.(doesnt yet work i dont know why)
2020-09-21 05:30:39 +02:00
const supportedCodecs = "opus";
const supportedContainers = "ogg,opus";
return `${jellyfinClientManager.getJellyfinClient().serverAddress()}/Audio/${itemID}/universal?UserId=${jellyfinClientManager.getJellyfinClient().getCurrentUserId()}&DeviceId=${jellyfinClientManager.getJellyfinClient().deviceId()}&MaxStreamingBitrate=${bitrate}&Container=${supportedContainers}&AudioCodec=${supportedCodecs}&api_key=${jellyfinClientManager.getJellyfinClient().accessToken()}&TranscodingContainer=ts&TranscodingProtocol=hls`;
}
2020-09-24 13:37:17 +02:00
function startPlaying (voiceconnection = discordclientmanager.getDiscordClient().user.client.voice.connections.first(), itemIDPlaylist = currentPlayingPlaylist, playlistIndex = currentPlayingPlaylistIndex, seekTo, disconnectOnFinish = _disconnectOnFinish) {
2021-01-18 07:15:26 +01:00
log.debug("start playing ",playlistIndex, ". of list: ",itemIDPlaylist," in a voiceconnection?: ", typeof voiceconnection !== "undefined");
2020-09-21 05:30:39 +02:00
isPaused = false;
2020-09-24 13:34:39 +02:00
currentPlayingPlaylist = itemIDPlaylist;
currentPlayingPlaylistIndex = playlistIndex;
2020-09-23 16:47:12 +02:00
_disconnectOnFinish = disconnectOnFinish;
2020-09-23 17:06:20 +02:00
_seek = seekTo * 1000;
2020-09-30 17:27:09 +02:00
updatePlayMessage();
2020-09-24 13:37:17 +02:00
async function playasync () {
2020-09-24 13:34:39 +02:00
const url = streamURLbuilder(itemIDPlaylist[playlistIndex], voiceconnection.channel.bitrate);
setAudioDispatcher(voiceconnection.play(url, {
seek: seekTo
}));
2020-09-23 17:06:20 +02:00
if (seekTo) {
2020-09-23 16:47:12 +02:00
jellyfinClientManager.getJellyfinClient().reportPlaybackProgress(getProgressPayload());
2020-09-23 17:06:20 +02:00
} else {
2020-09-24 13:34:39 +02:00
jellyfinClientManager.getJellyfinClient().reportPlaybackStart({
userID: `${jellyfinClientManager.getJellyfinClient().getCurrentUserId()}`,
itemID: `${itemIDPlaylist[playlistIndex]}`,
canSeek: true,
playSessionId: getPlaySessionId(),
playMethod: getPlayMethod()
});
2020-09-23 16:47:12 +02:00
}
2020-09-21 05:30:39 +02:00
getAudioDispatcher().on("finish", () => {
2020-10-06 00:47:26 +02:00
if (isRepeat) {
startPlaying(voiceconnection, undefined, currentPlayingPlaylistIndex, 0);
} else {
if (currentPlayingPlaylist.length < playlistIndex) {
if (disconnectOnFinish) {
stop(voiceconnection, currentPlayingPlaylist[playlistIndex - 1]);
} else {
stop(undefined, currentPlayingPlaylist[playlistIndex - 1]);
}
2020-09-24 13:37:17 +02:00
} else {
2020-10-06 00:47:26 +02:00
startPlaying(voiceconnection, undefined, currentPlayingPlaylistIndex + 1, 0);
2020-09-24 13:34:39 +02:00
}
2020-10-06 00:47:26 +02:00
}
2020-09-21 05:30:39 +02:00
});
}
2020-09-24 13:34:39 +02:00
playasync().catch((rsn) => {
console.error(rsn);
});
}
2020-09-30 17:27:09 +02:00
async function spawnPlayMessage (message) {
2021-01-18 07:15:26 +01:00
log.debug("spawned Play Message?: ",typeof message !== "undefined");
2020-09-30 17:27:09 +02:00
const itemIdDetails = await jellyfinClientManager.getJellyfinClient().getItem(jellyfinClientManager.getJellyfinClient().getCurrentUserId(), getItemId());
const imageURL = await jellyfinClientManager.getJellyfinClient().getImageUrl(itemIdDetails.AlbumId || getItemId(), { type: "Primary" });
2020-09-30 17:27:09 +02:00
try {
interactivemsghandler.init(message, itemIdDetails.Name, itemIdDetails.Artists[0] || "VA", imageURL,
`${jellyfinClientManager.getJellyfinClient().serverAddress()}/web/index.html#!/details?id=${itemIdDetails.AlbumId}`,
itemIdDetails.RunTimeTicks,
((ticksToSeconds(getPostitionTicks()) > 10) ? previousTrack : seek),
playPause,
() => { stop(_disconnectOnFinish ? discordclientmanager.getDiscordClient().user.client.voice.connections.first() : undefined); },
nextTrack,
2020-10-06 00:47:26 +02:00
() => { setIsRepeat(!isRepeat); },
2020-09-30 17:27:09 +02:00
currentPlayingPlaylist.length);
if (typeof CONFIG["interactive-seek-bar-update-intervall"] === "number") {
interactivemsghandler.startUpate(getPostitionTicks);
}
} catch (error) {
console.error(error);
}
}
async function updatePlayMessage () {
2020-10-06 00:47:26 +02:00
if (getItemId() !== undefined) {
2020-10-06 00:18:39 +02:00
const itemIdDetails = await jellyfinClientManager.getJellyfinClient().getItem(jellyfinClientManager.getJellyfinClient().getCurrentUserId(), getItemId());
const imageURL = await jellyfinClientManager.getJellyfinClient().getImageUrl(itemIdDetails.AlbumId || getItemId(), { type: "Primary" });
2020-10-06 00:18:39 +02:00
interactivemsghandler.updateCurrentSongMessage(itemIdDetails.Name, itemIdDetails.Artists[0] || "VA", imageURL,
`${jellyfinClientManager.getJellyfinClient().serverAddress()}/web/index.html#!/details?id=${itemIdDetails.AlbumId}`, itemIdDetails.RunTimeTicks, currentPlayingPlaylistIndex + 1, currentPlayingPlaylist.length);
}
2020-09-30 17:27:09 +02:00
}
2020-09-23 16:47:12 +02:00
/**
* @param {Number} toSeek - where to seek in ticks
*/
2020-09-24 13:37:17 +02:00
function seek (toSeek = 0) {
2021-01-18 06:49:32 +01:00
log.debug("seek to: ", toSeek);
2020-09-23 17:06:20 +02:00
if (getAudioDispatcher()) {
2020-09-24 13:34:39 +02:00
startPlaying(undefined, undefined, undefined, ticksToSeconds(toSeek), _disconnectOnFinish);
2020-09-23 16:47:12 +02:00
jellyfinClientManager.getJellyfinClient().reportPlaybackProgress(getProgressPayload());
2020-09-23 17:42:13 +02:00
} else {
2020-09-23 17:39:41 +02:00
throw Error("No Song Playing");
2020-09-23 16:47:12 +02:00
}
}
2020-10-06 00:18:39 +02:00
/**
2020-10-06 00:47:26 +02:00
*
* @param {Array} itemID - array of itemIDs to be added
2020-10-06 00:18:39 +02:00
*/
function addTracks (itemID) {
2021-01-18 06:49:32 +01:00
log.debug("added track: ",itemID);
2020-10-06 00:47:26 +02:00
currentPlayingPlaylist = currentPlayingPlaylist.concat(itemID);
2020-09-30 17:27:09 +02:00
}
2020-09-24 13:37:17 +02:00
function nextTrack () {
2021-01-18 06:49:32 +01:00
log.debug("nextTrack");
2020-09-24 13:37:17 +02:00
if (!(currentPlayingPlaylist)) {
2020-09-24 13:34:39 +02:00
throw Error("There is currently nothing playing");
2020-09-24 13:37:17 +02:00
} else if (currentPlayingPlaylistIndex + 1 >= currentPlayingPlaylist.length) {
2020-09-24 13:34:39 +02:00
throw Error("This is the Last song");
}
jellyfinClientManager.getJellyfinClient().reportPlaybackStopped(getStopPayload());
2020-09-24 13:37:17 +02:00
startPlaying(undefined, undefined, currentPlayingPlaylistIndex + 1, 0, _disconnectOnFinish);
2020-09-24 13:34:39 +02:00
}
2020-09-24 13:37:17 +02:00
function previousTrack () {
2021-01-18 06:49:32 +01:00
log.debug("previousTrack");
2020-09-24 13:37:17 +02:00
if (ticksToSeconds(getPostitionTicks()) < 10) {
if (!(currentPlayingPlaylist)) {
2020-09-24 13:34:39 +02:00
throw Error("There is currently nothing playing");
2020-09-24 13:37:17 +02:00
} else if (currentPlayingPlaylistIndex - 1 < 0) {
startPlaying(undefined, undefined, currentPlayingPlaylistIndex, 0, _disconnectOnFinish);
2020-09-24 13:34:39 +02:00
throw Error("This is the First song");
}
jellyfinClientManager.getJellyfinClient().reportPlaybackStopped(getStopPayload());
2020-09-24 13:37:17 +02:00
startPlaying(undefined, undefined, currentPlayingPlaylistIndex - 1, 0, _disconnectOnFinish);
2020-09-24 13:34:39 +02:00
}
}
/**
* @param {Object=} disconnectVoiceConnection - Optional The voice Connection do disconnect from
*/
2020-09-24 13:37:17 +02:00
function stop (disconnectVoiceConnection, itemId = getItemId()) {
2021-01-18 07:02:50 +01:00
2020-09-21 05:30:39 +02:00
isPaused = true;
2020-09-25 13:11:13 +02:00
if (interactivemsghandler.hasMessage()) {
interactivemsghandler.destroy();
}
2020-09-21 05:30:39 +02:00
if (disconnectVoiceConnection) {
disconnectVoiceConnection.disconnect();
}
2021-01-18 07:02:50 +01:00
log.debug("stop playback and send following payload to the server: ",getStopPayload());
jellyfinClientManager.getJellyfinClient().reportPlaybackStopped(getStopPayload());
2020-09-24 13:34:39 +02:00
if (getAudioDispatcher()) {
2020-09-30 17:27:09 +02:00
try {
getAudioDispatcher().destroy();
} catch (error) {
console.error(error);
}
2020-09-24 13:34:39 +02:00
}
2020-09-21 05:30:39 +02:00
setAudioDispatcher(undefined);
}
2020-09-24 13:34:39 +02:00
2020-09-24 13:37:17 +02:00
function pause () {
2021-01-18 06:49:32 +01:00
log.debug("pause");
2020-09-21 05:30:39 +02:00
isPaused = true;
jellyfinClientManager.getJellyfinClient().reportPlaybackProgress(getProgressPayload());
getAudioDispatcher().pause(true);
}
2020-09-24 13:34:39 +02:00
2020-09-24 13:37:17 +02:00
function resume () {
2021-01-18 06:49:32 +01:00
log.debug("resume");
2020-09-21 05:30:39 +02:00
isPaused = false;
jellyfinClientManager.getJellyfinClient().reportPlaybackProgress(getProgressPayload());
getAudioDispatcher().resume();
}
2020-09-24 13:34:39 +02:00
2020-09-24 13:37:17 +02:00
function playPause () {
if (!(getAudioDispatcher())) {
2020-09-24 13:34:39 +02:00
throw Error("There is nothing Playing right now!");
}
if (getAudioDispatcher().paused) {
resume();
} else {
pause();
}
}
2020-09-24 13:37:17 +02:00
function getPostitionTicks () {
2020-09-21 05:30:39 +02:00
// this is very sketchy but i dont know how else to do it
2020-09-23 17:06:20 +02:00
return (_seek + getAudioDispatcher().streamTime - getAudioDispatcher().pausedTime) * 10000;
}
2020-09-24 13:37:17 +02:00
function getPlayMethod () {
2020-09-21 05:30:39 +02:00
// TODO figure out how to figure this out
2020-09-24 13:34:39 +02:00
return "Transcode";
}
2020-09-24 13:37:17 +02:00
function getRepeatMode () {
if (isRepeat) {
2020-09-24 13:34:39 +02:00
return "RepeatOne";
2020-09-24 13:37:17 +02:00
} else {
2020-09-24 13:34:39 +02:00
return "RepeatNone";
}
}
2020-09-24 13:37:17 +02:00
function getPlaylistItemId () {
2020-09-24 13:34:39 +02:00
return getItemId();
}
2020-09-24 13:37:17 +02:00
function getPlaySessionId () {
2020-09-21 05:30:39 +02:00
// i think its just a number which you dont need to retrieve but need to report
return "ae2436edc6b91b11d72aeaa67f84e0ea";
}
2020-09-24 13:37:17 +02:00
function getNowPLayingQueue () {
2020-09-21 05:30:39 +02:00
return [{
2020-09-24 13:34:39 +02:00
Id: getItemId(),
2020-09-21 05:30:39 +02:00
// as I curently dont support Playlists
PlaylistItemId: getPlaylistItemId()
}];
}
2020-09-24 13:37:17 +02:00
function getCanSeek () {
2020-09-23 16:47:12 +02:00
return true;
}
2020-09-24 13:37:17 +02:00
function getIsMuted () {
2020-09-21 05:30:39 +02:00
return false;
}
2020-09-24 13:37:17 +02:00
function getVolumeLevel () {
2020-09-21 05:30:39 +02:00
return 100;
}
2020-09-24 13:37:17 +02:00
function getItemId () {
2020-09-25 13:11:13 +02:00
if (typeof currentPlayingPlaylist !== "undefined") {
return currentPlayingPlaylist[currentPlayingPlaylistIndex];
}
2020-10-06 00:18:39 +02:00
return undefined;
}
2020-09-24 13:37:17 +02:00
function getIsPaused () {
2020-09-21 05:30:39 +02:00
// AudioDispacker Paused is to slow
2020-09-21 05:30:39 +02:00
if (isPaused === undefined) {
isPaused = false;
}
return isPaused;
}
2020-09-24 13:37:17 +02:00
function setIsRepeat (arg) {
2020-09-25 13:11:13 +02:00
if (arg === undefined) {
if (!(isRepeat === undefined)) {
isRepeat = !isRepeat;
}
}
2020-09-24 13:37:17 +02:00
isRepeat = arg;
2020-09-24 13:34:39 +02:00
}
2020-09-24 13:37:17 +02:00
function getProgressPayload () {
2020-09-21 05:30:39 +02:00
const payload = {
CanSeek: getCanSeek(),
IsMuted: getIsMuted(),
IsPaused: getIsPaused(),
ItemId: getItemId(),
MediaSourceId: getItemId(),
NowPlayingQueue: getNowPLayingQueue(),
PlayMethod: getPlayMethod(),
PlaySessionId: getPlaySessionId(),
PlaylistItemId: getPlaylistItemId(),
PositionTicks: getPostitionTicks(),
RepeatMode: getRepeatMode(),
2020-09-21 09:56:46 +02:00
VolumeLevel: getVolumeLevel(),
EventName: "pauseplayupdate"
2020-09-21 05:30:39 +02:00
};
return payload;
}
function getStopPayload () {
const payload = {
userId: jellyfinClientManager.getJellyfinClient().getCurrentUserId(),
itemId: getItemId(),
sessionID: getPlaySessionId(),
playSessionId: getPlaySessionId(),
positionTicks: getPostitionTicks()
};
return payload;
}
module.exports = {
2020-09-21 05:30:39 +02:00
startPlaying,
stop,
playPause,
resume,
2020-09-23 16:47:12 +02:00
pause,
2020-09-24 13:34:39 +02:00
seek,
setIsRepeat,
nextTrack,
previousTrack,
2020-10-06 00:18:39 +02:00
addTracks,
2020-09-30 17:27:09 +02:00
getPostitionTicks,
spawnPlayMessage
2020-09-24 13:37:17 +02:00
};