add playlist support

This commit is contained in:
KGT1 2020-09-24 13:34:39 +02:00
parent bd4ad6b424
commit e019f4c6e5
3 changed files with 154 additions and 52 deletions

View File

@ -121,7 +121,7 @@ async function playThis (message) {
discordClient.user.client.voice.connections.forEach((element) => { discordClient.user.client.voice.connections.forEach((element) => {
songPlayMessage(message, argument); songPlayMessage(message, argument);
playbackmanager.startPlaying(element, itemID, 0, isSummendByPlay); playbackmanager.startPlaying(element, [itemID], 0, 0, isSummendByPlay);
}); });
} }
@ -149,19 +149,16 @@ function handleChannelMessage (message) {
.setDescription("<:wave:757938481585586226> " + desc); .setDescription("<:wave:757938481585586226> " + desc);
message.channel.send(vcJoin); message.channel.send(vcJoin);
} else if ((message.content.startsWith(CONFIG["discord-prefix"] + "pause")) || (message.content.startsWith(CONFIG["discord-prefix"] + "resume"))) { } else if ((message.content.startsWith(CONFIG["discord-prefix"] + "pause")) || (message.content.startsWith(CONFIG["discord-prefix"] + "resume"))) {
if (getAudioDispatcher() !== undefined) { try {
playbackmanager.playPause(); playbackmanager.playPause();
const noPlay = new Discord.MessageEmbed() const noPlay = new Discord.MessageEmbed()
.setColor(0xff0000) .setColor(0xff0000)
.setTitle("<:play_pause:757940598106882049> " + "Paused/Resumed.") .setTitle("<:play_pause:757940598106882049> " + "Paused/Resumed.")
.setTimestamp(); .setTimestamp();
message.channel.send(noPlay); message.channel.send(noPlay);
} else { } catch (error) {
const noPlay = new Discord.MessageEmbed() const errorMessage = getDiscordEmbedError(error);
.setColor(0xff0000) message.channel.send(errorMessage);
.setTitle("<:x:757935515445231651> " + "There is nothing Playing right now!")
.setTimestamp();
message.channel.send(noPlay);
} }
} else if (message.content.startsWith(CONFIG["discord-prefix"] + "play")) { } else if (message.content.startsWith(CONFIG["discord-prefix"] + "play")) {
if (discordClient.user.client.voice.connections.size < 1) { if (discordClient.user.client.voice.connections.size < 1) {
@ -187,6 +184,13 @@ function handleChannelMessage (message) {
const errorMessage = getDiscordEmbedError(error); const errorMessage = getDiscordEmbedError(error);
message.channel.send(errorMessage); message.channel.send(errorMessage);
} }
} else if (message.content.startsWith(CONFIG["discord-prefix"] + "skip")) {
try {
playbackmanager.nextTrack()
} catch (error) {
const errorMessage = getDiscordEmbedError(error);
message.channel.send(errorMessage);
}
} else if (message.content.startsWith(CONFIG["discord-prefix"] + "help")) { } else if (message.content.startsWith(CONFIG["discord-prefix"] + "help")) {
/* eslint-disable quotes */ /* eslint-disable quotes */
const reply = new Discord.MessageEmbed() const reply = new Discord.MessageEmbed()
@ -207,6 +211,9 @@ function handleChannelMessage (message) {
}, { }, {
name: `${CONFIG["discord-prefix"]}seek`, name: `${CONFIG["discord-prefix"]}seek`,
value: "Where to Seek to in seconds or MM:SS" value: "Where to Seek to in seconds or MM:SS"
}, {
name: `${CONFIG["discord-prefix"]}skip`,
value: "Skip this Song"
}, { }, {
name: `${CONFIG["discord-prefix"]}help`, name: `${CONFIG["discord-prefix"]}help`,
value: "Display this help message" value: "Display this help message"

View File

@ -7,13 +7,19 @@ const {
ticksToSeconds ticksToSeconds
} = require("./util"); } = require("./util");
var currentPlayingItemId;
//this whole thing should be a class but its probably too late now.
var currentPlayingPlaylist;
var currentPlayingPlaylistIndex;
var progressInterval; var progressInterval;
var isPaused; var isPaused;
var isRepeat;
var _disconnectOnFinish; var _disconnectOnFinish;
var _seek; var _seek;
const jellyfinClientManager = require("./jellyfinclientmanager"); const jellyfinClientManager = require("./jellyfinclientmanager");
function streamURLbuilder(itemID, bitrate) { function streamURLbuilder(itemID, bitrate) {
// so the server transcodes. Seems appropriate as it has the source file. // so the server transcodes. Seems appropriate as it has the source file.
const supportedCodecs = "opus"; const supportedCodecs = "opus";
@ -21,86 +27,149 @@ function streamURLbuilder (itemID, bitrate) {
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`; 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`;
} }
function startPlaying (voiceconnection = discordclientmanager.getDiscordClient().user.client.voice.connections.first(), itemID = currentPlayingItemId, seekTo, disconnectOnFinish = _disconnectOnFinish) { function startPlaying(voiceconnection = discordclientmanager.getDiscordClient().user.client.voice.connections.first(), itemIDPlaylist = currentPlayingPlaylist, playlistIndex = currentPlayingPlaylistIndex, seekTo, disconnectOnFinish = _disconnectOnFinish) {
isPaused = false; isPaused = false;
currentPlayingItemId = itemID; currentPlayingPlaylist = itemIDPlaylist;
currentPlayingPlaylistIndex = playlistIndex;
_disconnectOnFinish = disconnectOnFinish; _disconnectOnFinish = disconnectOnFinish;
_seek = seekTo * 1000; _seek = seekTo * 1000;
async function playasync() { async function playasync() {
const url = streamURLbuilder(itemID, voiceconnection.channel.bitrate); const url = streamURLbuilder(itemIDPlaylist[playlistIndex], voiceconnection.channel.bitrate);
setAudioDispatcher(voiceconnection.play(url, { seek: seekTo })); setAudioDispatcher(voiceconnection.play(url, {
seek: seekTo
}));
if (seekTo) { if (seekTo) {
jellyfinClientManager.getJellyfinClient().reportPlaybackProgress(getProgressPayload()); jellyfinClientManager.getJellyfinClient().reportPlaybackProgress(getProgressPayload());
} else { } else {
jellyfinClientManager.getJellyfinClient().reportPlaybackStart({ userID: `${jellyfinClientManager.getJellyfinClient().getCurrentUserId()}`, itemID: `${itemID}`, canSeek: true, playSessionId: getPlaySessionId(), playMethod: getPlayMethod() }); jellyfinClientManager.getJellyfinClient().reportPlaybackStart({
userID: `${jellyfinClientManager.getJellyfinClient().getCurrentUserId()}`,
itemID: `${itemIDPlaylist[playlistIndex]}`,
canSeek: true,
playSessionId: getPlaySessionId(),
playMethod: getPlayMethod()
});
} }
getAudioDispatcher().on("finish", () => { getAudioDispatcher().on("finish", () => {
if (currentPlayingPlaylist.length < playlistIndex) {
console.log("PLAYLIST END")
if (disconnectOnFinish) { if (disconnectOnFinish) {
stop(voiceconnection); stop(voiceconnection, currentPlayingPlaylist[playlistIndex - 1]);
}else { }else {
stop(); stop(undefined, currentPlayingPlaylist[playlistIndex - 1]);
}
} else {
startPlaying(voiceconnection, itemIDPlaylist, currentPlayingPlaylistIndex+1, 0)
} }
}); });
} }
playasync().catch((rsn) => { console.error(rsn); }); playasync().catch((rsn) => {
console.error(rsn);
});
} }
/** /**
* @param {Number} toSeek - where to seek in ticks * @param {Number} toSeek - where to seek in ticks
*/ */
function seek(toSeek = 0) { function seek(toSeek = 0) {
if (getAudioDispatcher()) { if (getAudioDispatcher()) {
startPlaying(undefined, undefined, ticksToSeconds(toSeek), _disconnectOnFinish); startPlaying(undefined, undefined, undefined, ticksToSeconds(toSeek), _disconnectOnFinish);
jellyfinClientManager.getJellyfinClient().reportPlaybackProgress(getProgressPayload()); jellyfinClientManager.getJellyfinClient().reportPlaybackProgress(getProgressPayload());
} else { } else {
throw Error("No Song Playing"); throw Error("No Song Playing");
} }
} }
function nextTrack(){
//console.log(currentPlayingPlaylistIndex + 1, currentPlayingPlaylist.length);
if(!(currentPlayingPlaylist)){
throw Error("There is currently nothing playing");
}else if(currentPlayingPlaylistIndex + 1 >= currentPlayingPlaylist.length){
throw Error("This is the Last song");
}
startPlaying(undefined, undefined, currentPlayingPlaylistIndex + 1, 0, _disconnectOnFinish)
}
function previousTrack(){
if(ticksToSeconds(getPostitionTicks())<10){
console.log(currentPlayingPlaylistIndex , currentPlayingPlaylist.length);
if(!(currentPlayingPlaylist)){
throw Error("There is currently nothing playing");
}else if(currentPlayingPlaylistIndex -1 < 0){
startPlaying(undefined, undefined, currentPlayingPlaylistIndex, 0, _disconnectOnFinish)
throw Error("This is the First song");
}
startPlaying(undefined, undefined, currentPlayingPlaylistIndex - 1, 0, _disconnectOnFinish)
}
}
/** /**
* @param {Object=} disconnectVoiceConnection - Optional The voice Connection do disconnect from * @param {Object=} disconnectVoiceConnection - Optional The voice Connection do disconnect from
*/ */
function stop (disconnectVoiceConnection) { function stop(disconnectVoiceConnection, itemId = getItemId()) {
console.log("im getting called");
console
isPaused = true; isPaused = true;
if (disconnectVoiceConnection) { if (disconnectVoiceConnection) {
disconnectVoiceConnection.disconnect(); disconnectVoiceConnection.disconnect();
} }
jellyfinClientManager.getJellyfinClient().reportPlaybackStopped({ userId: jellyfinClientManager.getJellyfinClient().getCurrentUserId(), itemId: currentPlayingItemId, playSessionId: getPlaySessionId() }); jellyfinClientManager.getJellyfinClient().reportPlaybackStopped({
if (getAudioDispatcher()) { getAudioDispatcher().destroy(); } userId: jellyfinClientManager.getJellyfinClient().getCurrentUserId(),
itemId: itemId,
playSessionId: getPlaySessionId()
});
if (getAudioDispatcher()) {
getAudioDispatcher().destroy();
}
setAudioDispatcher(undefined); setAudioDispatcher(undefined);
clearInterval(progressInterval); clearInterval(progressInterval);
} }
function pause() { function pause() {
isPaused = true; isPaused = true;
jellyfinClientManager.getJellyfinClient().reportPlaybackProgress(getProgressPayload()); jellyfinClientManager.getJellyfinClient().reportPlaybackProgress(getProgressPayload());
getAudioDispatcher().pause(true); getAudioDispatcher().pause(true);
} }
function resume() { function resume() {
isPaused = false; isPaused = false;
jellyfinClientManager.getJellyfinClient().reportPlaybackProgress(getProgressPayload()); jellyfinClientManager.getJellyfinClient().reportPlaybackProgress(getProgressPayload());
getAudioDispatcher().resume(); getAudioDispatcher().resume();
} }
function playPause() { function playPause() {
if (getAudioDispatcher().paused) { resume(); } else { pause(); } if(!(getAudioDispatcher())){
throw Error("There is nothing Playing right now!");
}
if (getAudioDispatcher().paused) {
resume();
} else {
pause();
}
} }
function getPostitionTicks() { function getPostitionTicks() {
// this is very sketchy but i dont know how else to do it // this is very sketchy but i dont know how else to do it
console.log((_seek + getAudioDispatcher().streamTime - getAudioDispatcher().pausedTime) * 10000);
return (_seek + getAudioDispatcher().streamTime - getAudioDispatcher().pausedTime) * 10000; return (_seek + getAudioDispatcher().streamTime - getAudioDispatcher().pausedTime) * 10000;
} }
function getPlayMethod() { function getPlayMethod() {
// TODO figure out how to figure this out // TODO figure out how to figure this out
return 0; return "Transcode";
} }
function getRepeatMode() { function getRepeatMode() {
if(isRepeat){
return "RepeatOne";
}else{
return "RepeatNone"; return "RepeatNone";
} }
}
function getPlaylistItemId() { function getPlaylistItemId() {
// as I curently dont support Playlists return getItemId();
return "playlistItem0";
} }
function getPlaySessionId() { function getPlaySessionId() {
@ -110,7 +179,7 @@ function getPlaySessionId () {
function getNowPLayingQueue() { function getNowPLayingQueue() {
return [{ return [{
Id: currentPlayingItemId, Id: getItemId(),
// as I curently dont support Playlists // as I curently dont support Playlists
PlaylistItemId: getPlaylistItemId() PlaylistItemId: getPlaylistItemId()
}]; }];
@ -129,7 +198,7 @@ function getVolumeLevel () {
} }
function getItemId() { function getItemId() {
return currentPlayingItemId; return currentPlayingPlaylist[currentPlayingPlaylistIndex];
} }
function getIsPaused() { function getIsPaused() {
@ -142,6 +211,10 @@ function getIsPaused () {
return isPaused; return isPaused;
} }
function setIsRepeat(arg){
isRepeat=arg;
}
function getProgressPayload() { function getProgressPayload() {
const payload = { const payload = {
CanSeek: getCanSeek(), CanSeek: getCanSeek(),
@ -167,5 +240,9 @@ module.exports = {
playPause, playPause,
resume, resume,
pause, pause,
seek seek,
setIsRepeat,
nextTrack,
previousTrack,
getPostitionTicks
}; };

View File

@ -1,5 +1,6 @@
const jellyfinClientManager = require("./jellyfinclientmanager"); const jellyfinClientManager = require("./jellyfinclientmanager");
const playbackmanager = require("./playbackmanager"); const playbackmanager = require("./playbackmanager");
const { ticksToSeconds }= require("./util");
function openSocket () { function openSocket () {
jellyfinClientManager.getJellyfinClient().openWebSocket(); jellyfinClientManager.getJellyfinClient().openWebSocket();
@ -7,13 +8,14 @@ function openSocket () {
{ {
PlayableMediaTypes: "Audio", PlayableMediaTypes: "Audio",
SupportsMediaControl: "True", SupportsMediaControl: "True",
SupportedCommands: "Play,Playstate" SupportedCommands: "SetRepeatMode,Play,Playstate"
} }
); );
jellyfinClientManager.getJellyfinEvents().on(jellyfinClientManager.getJellyfinClient(), "message", (type, data) => { jellyfinClientManager.getJellyfinEvents().on(jellyfinClientManager.getJellyfinClient(), "message", (type, data) => {
console.log(data);
if (data.MessageType === "Play") { if (data.MessageType === "Play") {
if (data.Data.PlayCommand === "PlayNow") { if (data.Data.PlayCommand === "PlayNow") {
playbackmanager.startPlaying(undefined, data.Data.ItemIds[data.Data.StartIndex || 0], 0, false); playbackmanager.startPlaying(undefined, data.Data.ItemIds, data.Data.StartIndex || 0, 0, false);
} }
} else if (data.MessageType === "Playstate") { } else if (data.MessageType === "Playstate") {
if (data.Data.Command === "PlayPause") { if (data.Data.Command === "PlayPause") {
@ -21,7 +23,23 @@ function openSocket () {
} else if (data.Data.Command === "Stop") { } else if (data.Data.Command === "Stop") {
playbackmanager.stop(); playbackmanager.stop();
} else if (data.Data.Command === "Seek") { } else if (data.Data.Command === "Seek") {
playbackmanager.seek(data.Data.SeekPositionTicks); //because the server sends seek an privious track at same time so i have to do timing
setTimeout(async()=>{playbackmanager.seek(data.Data.SeekPositionTicks);},20)
} else if (data.Data.Command === "NextTrack") {
try {
playbackmanager.nextTrack();
} catch (error) {
console.error(error);
}
} else if (data.Data.Command === "PreviousTrack") {
try{
console.log(ticksToSeconds(playbackmanager.getPostitionTicks())<10,ticksToSeconds(playbackmanager.getPostitionTicks()),` (${playbackmanager.getPostitionTicks()})`," < ",10)
if(ticksToSeconds(playbackmanager.getPostitionTicks())<10){
playbackmanager.previousTrack();
}
}catch(error){
console.error(error);
}
} }
} }
}); });