mirror of
https://github.com/informaticker/discord-jellyfin-bot.git
synced 2024-11-23 18:21:55 +01:00
improve play message
This commit is contained in:
parent
a218a71ac2
commit
0b76f59077
@ -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
|
||||
```
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
};
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
};
|
||||
|
16
src/util.js
16
src/util.js
@ -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
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user