improve play message

This commit is contained in:
KGT1 2020-09-30 17:27:09 +02:00
parent a218a71ac2
commit 0b76f59077
7 changed files with 182 additions and 48 deletions

View File

@ -42,6 +42,7 @@ docker run -d \
-e JELLYFIN_USERNAME="" \
-e JELLYFIN_PASSWORD="" \
-e JELLYFIN_APP_NAME="Jellyfin Discord Music Bot" \
-e MESSAGE_UPDATE_INTERVAL="2000" \
--restart unless-stopped \
kgt1/jellyfin-discord-music-bot
```

View File

@ -8,6 +8,7 @@ if (!configfile["server-adress"]) { configfile["server-adress"] = process.env.JE
if (!configfile["jellyfin-username"]) { configfile["jellyfin-username"] = process.env.JELLYFIN_USERNAME; }
if (!configfile["jellyfin-password"]) { configfile["jellyfin-password"] = process.env.JELLYFIN_PASSWORD; }
if (!configfile["jellyfin-app-name"]) { configfile["jellyfin-app-name"] = process.env.JELLYFIN_APP_NAME; }
if (!configfile["interactive-seek-bar-update-intervall"]) { configfile["interactive-seek-bar-update-intervall"] = parseInt(process.env.MESSAGE_UPDATE_INTERVAL); }
fs.writeFile(filename, JSON.stringify(configfile, null, 1), (err) => {
if (err) return console.error(err);

View File

@ -1,48 +1,81 @@
const discordclientmanager = require("./discordclientmanager");
const CONFIG = require("../config.json");
const {
secondsToHms,
ticksToSeconds
} = require("./util");
// eslint-disable-next-line
function getProgressString (percent) {
// the min with of the discord window allows for this many chars
const NUMBER_OF_CHARS = 12;
let string = "";
for (let iX = 0; iX < NUMBER_OF_CHARS; iX++) {
if (percent > (iX) / NUMBER_OF_CHARS) { string += "█"; } else { string += "▒"; }
if (percent > (iX) / NUMBER_OF_CHARS) {
string += "█";
} else {
string += "▒";
}
}
return string;
}
/**
*
* @param {String} string
* @returns {String}
*/
// TODO do this with something like wcwidth
function getMaxWidthString (string) {
const NUMBER_OF_CHARS = 12;
if (string.length > NUMBER_OF_CHARS) {
return string.slice(0, NUMBER_OF_CHARS - 3) + "...";
}
return string;
}
class InterActivePlayMessage {
// musicplayermessage
// probably should have done events instead of.
/**
*
* @param {Object} message
* @param {String} title
* @param {String} artist
* @param {String} imageURL
* @param {String} itemURL
* @param {Function} getProgress
* @param {Function} onPrevious
* @param {Function} onPausePlay
* @param {Function} onStop
* @param {Function} onNext
* @param {Function} onRepeat
*/
constructor (message, title, artist, imageURL, itemURL, getProgress, onPrevious, onPausePlay, onStop, onNext, onRepeat) {
*
* @param {Object} message
* @param {String} title
* @param {String} artist
* @param {String} imageURL
* @param {String} itemURL
* @param {Number} ticksLength
* @param {Function} onPrevious
* @param {Function} onPausePlay
* @param {Function} onStop
* @param {Function} onNext
* @param {Function} onRepeat
*/
constructor (message, title, artist, imageURL, itemURL, ticksLength, onPrevious, onPausePlay, onStop, onNext, onRepeat, playlistLenth) {
this.ticksLength = ticksLength;
var exampleEmbed = {
color: 0x0099ff,
title: "Now Playing",
url: itemURL,
description: `${title} by ${artist}`,
description: `${getMaxWidthString(title)}\nby\n ${getMaxWidthString(artist)}`,
thumbnail: {
url: imageURL
},
/* fields: [{
name: getProgressString(0),
value: `${0}`,
inline: false,
}, ], */
fields: [],
timestamp: new Date()
};
if (typeof CONFIG["interactive-seek-bar-update-intervall"] === "number") {
exampleEmbed.fields.push({
name: getProgressString(0 / this.ticksLength),
value: `${secondsToHms(0)} / ${secondsToHms(ticksToSeconds(this.ticksLength))}`,
inline: false
});
}
if (playlistLenth) {
exampleEmbed.fields.push({
name: `1 of ${playlistLenth}`,
value: "Playlist",
inline: false
});
}
message.channel.send({
embed: exampleEmbed
})
@ -91,6 +124,37 @@ class InterActivePlayMessage {
});
}
updateProgress (ticks) {
this.musicplayermessage.embeds[0].fields[0] = {
name: getProgressString(ticks / this.ticksLength),
value: `${secondsToHms(ticksToSeconds(ticks))} / ${secondsToHms(ticksToSeconds(this.ticksLength))}`,
inline: false
};
this.musicplayermessage.timestamp = new Date();
this.musicplayermessage.edit(this.musicplayermessage.embeds[0]);
}
updateCurrentSongMessage (title, artist, imageURL, itemURL, ticksLength, playlistIndex, playlistLenth) {
this.musicplayermessage.embeds[0].url = itemURL;
this.musicplayermessage.embeds[0].description = `${getMaxWidthString(title)}\nby\n${getMaxWidthString(artist)}`;
this.musicplayermessage.embeds[0].thumbnail = { url: imageURL };
const indexOfPlaylistMessage = this.musicplayermessage.embeds[0].fields.findIndex((element) => { return element.value === "Playlist"; });
if (indexOfPlaylistMessage === -1) {
this.musicplayermessage.embeds[0].fields.push({
name: `${playlistIndex} of ${playlistLenth}`,
value: "Playlist",
inline: false
});
} else {
this.musicplayermessage.embeds[0].fields[indexOfPlaylistMessage].name = `${playlistIndex} of ${playlistLenth}`;
}
this.ticksLength = ticksLength;
this.musicplayermessage.timestamp = new Date();
this.musicplayermessage.edit(this.musicplayermessage.embeds[0]);
}
destroy () {
this.musicplayermessage.delete();
delete this;

View File

@ -1,12 +1,15 @@
const InterActivePlayMessage = require("./InterActivePlayMessage");
const CONFIG = require("../config.json");
var iapm;
function init (message, title, artist, imageURL, itemURL, getProgress, onPrevious, onPausePlay, onStop, onNext, onRepeat) {
var updateInterval;
function init (message, title, artist, imageURL, itemURL, getProgress, onPrevious, onPausePlay, onStop, onNext, onRepeat, playlistLenth) {
if (typeof iapm !== "undefined") {
destroy();
}
iapm = new InterActivePlayMessage(message, title, artist, imageURL, itemURL, getProgress, onPrevious, onPausePlay, onStop, onNext, onRepeat);
iapm = new InterActivePlayMessage(message, title, artist, imageURL, itemURL, getProgress, onPrevious, onPausePlay, onStop, onNext, onRepeat, playlistLenth);
}
function destroy () {
@ -16,6 +19,11 @@ function destroy () {
} else {
throw Error("No Interactive Message Found");
}
if (updateInterval !== "undefined") {
clearInterval(updateInterval);
updateInterval = undefined;
}
}
function hasMessage () {
@ -25,9 +33,24 @@ function hasMessage () {
return true;
}
}
/**
*
* @param {Function} callback function to retrieve current ticks
*/
function startUpate (callback) {
updateInterval = setInterval(() => {
iapm.updateProgress(callback());
}, CONFIG["interactive-seek-bar-update-intervall"]);
}
function updateCurrentSongMessage (title, artist, imageURL, itemURL, ticksLength, playlistIndex, playlistLenth) {
iapm.updateCurrentSongMessage(title, artist, imageURL, itemURL, ticksLength, playlistIndex, playlistLenth);
}
module.exports = {
init,
destroy,
hasMessage
hasMessage,
startUpate,
updateCurrentSongMessage
};

View File

@ -5,11 +5,9 @@ const {
} = require("./util");
const {
hmsToSeconds,
ticksToSeconds,
getDiscordEmbedError
} = require("./util");
const interactivemsghandler = require("./interactivemsghandler");
const discordclientmanager = require("./discordclientmanager");
const jellyfinClientManager = require("./jellyfinclientmanager");
const playbackmanager = require("./playbackmanager");
@ -83,23 +81,6 @@ function summonMessage (message) {
}
}
async function songPlayMessage (message, itemId) {
const itemIdDetails = await jellyfinClientManager.getJellyfinClient().getItem(jellyfinClientManager.getJellyfinClient().getCurrentUserId(), itemId);
const imageURL = await jellyfinClientManager.getJellyfinClient().getImageUrl(itemIdDetails.AlbumId, { type: "Primary" });
try {
interactivemsghandler.init(message, itemIdDetails.Name, itemIdDetails.Artists[0] || "VA", imageURL,
`${jellyfinClientManager.getJellyfinClient().serverAddress()}/web/index.html#!/details?id=${itemIdDetails.AlbumId}`,
undefined,
((ticksToSeconds(playbackmanager.getPostitionTicks()) > 10) ? playbackmanager.previousTrack : playbackmanager.seek),
playbackmanager.playPause,
playbackmanager.stop,
playbackmanager.nextTrack,
playbackmanager.setIsRepeat);
} catch (error) {
}
}
async function playThis (message) {
const indexOfItemID = message.content.indexOf(CONFIG["discord-prefix"] + "play") + (CONFIG["discord-prefix"] + "play").length + 1;
const argument = message.content.slice(indexOfItemID);
@ -120,8 +101,8 @@ async function playThis (message) {
}
discordClient.user.client.voice.connections.forEach((element) => {
songPlayMessage(message, itemID);
playbackmanager.startPlaying(element, [itemID], 0, 0, isSummendByPlay);
playbackmanager.spawnPlayMessage(message);
});
}
@ -191,6 +172,15 @@ function handleChannelMessage (message) {
const errorMessage = getDiscordEmbedError(error);
message.channel.send(errorMessage);
}
} else if (message.content.startsWith(CONFIG["discord-prefix"] + "add")) {
const indexOfArgument = message.content.indexOf(CONFIG["discord-prefix"] + "add") + (CONFIG["discord-prefix"] + "add").length + 1;
const argument = message.content.slice(indexOfArgument);
try {
playbackmanager.addTrack(argument);
} catch (error) {
const errorMessage = getDiscordEmbedError(error);
message.channel.send(errorMessage);
}
} else if (message.content.startsWith(CONFIG["discord-prefix"] + "help")) {
/* eslint-disable quotes */
const reply = new Discord.MessageEmbed()

View File

@ -1,5 +1,6 @@
const interactivemsghandler = require("./interactivemsghandler");
const CONFIG = require("../config.json");
const discordclientmanager = require("./discordclientmanager");
const {
getAudioDispatcher,
@ -33,6 +34,7 @@ function startPlaying (voiceconnection = discordclientmanager.getDiscordClient()
currentPlayingPlaylistIndex = playlistIndex;
_disconnectOnFinish = disconnectOnFinish;
_seek = seekTo * 1000;
updatePlayMessage();
async function playasync () {
const url = streamURLbuilder(itemIDPlaylist[playlistIndex], voiceconnection.channel.bitrate);
setAudioDispatcher(voiceconnection.play(url, {
@ -66,6 +68,35 @@ function startPlaying (voiceconnection = discordclientmanager.getDiscordClient()
console.error(rsn);
});
}
async function spawnPlayMessage (message) {
const itemIdDetails = await jellyfinClientManager.getJellyfinClient().getItem(jellyfinClientManager.getJellyfinClient().getCurrentUserId(), getItemId());
const imageURL = await jellyfinClientManager.getJellyfinClient().getImageUrl(itemIdDetails.AlbumId, { type: "Primary" });
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,
setIsRepeat,
currentPlayingPlaylist.length);
if (typeof CONFIG["interactive-seek-bar-update-intervall"] === "number") {
interactivemsghandler.startUpate(getPostitionTicks);
}
} catch (error) {
console.error(error);
}
}
async function updatePlayMessage () {
const itemIdDetails = await jellyfinClientManager.getJellyfinClient().getItem(jellyfinClientManager.getJellyfinClient().getCurrentUserId(), getItemId());
const imageURL = await jellyfinClientManager.getJellyfinClient().getImageUrl(itemIdDetails.AlbumId, { type: "Primary" });
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);
}
/**
* @param {Number} toSeek - where to seek in ticks
*/
@ -78,6 +109,10 @@ function seek (toSeek = 0) {
}
}
function addTrack (itemID) {
currentPlayingPlaylist.push(itemID);
}
function nextTrack () {
// console.log(currentPlayingPlaylistIndex + 1, currentPlayingPlaylist.length);
if (!(currentPlayingPlaylist)) {
@ -117,7 +152,11 @@ function stop (disconnectVoiceConnection, itemId = getItemId()) {
playSessionId: getPlaySessionId()
});
if (getAudioDispatcher()) {
getAudioDispatcher().destroy();
try {
getAudioDispatcher().destroy();
} catch (error) {
console.error(error);
}
}
setAudioDispatcher(undefined);
}
@ -246,5 +285,7 @@ module.exports = {
setIsRepeat,
nextTrack,
previousTrack,
getPostitionTicks
addTrack,
getPostitionTicks,
spawnPlayMessage
};

View File

@ -19,6 +19,19 @@ function hmsToSeconds (str) {
return s;
}
function secondsToHms (totalSeconds) {
const hours = Math.floor(totalSeconds / 3600);
totalSeconds %= 3600;
const minutes = Math.floor(totalSeconds / 60);
let seconds = Math.floor(totalSeconds % 60);
seconds = seconds < 10 && seconds > 0 ? `0${seconds}` : `${seconds}`;
if (hours > 0) {
return `${hours}:${minutes}:${seconds}`;
} else {
return `${minutes}:${seconds}`;
}
}
function getDiscordEmbedError (e) {
const Discord = require("discord.js");
return new Discord.MessageEmbed()
@ -32,5 +45,6 @@ module.exports = {
checkJellyfinItemIDRegex,
ticksToSeconds,
hmsToSeconds,
getDiscordEmbedError
getDiscordEmbedError,
secondsToHms
};