Improve stream handling

This commit is contained in:
Alex Thomassen 2024-10-07 16:46:24 +00:00
parent 08f6951ffc
commit a745e28eb9
Signed by: Alex
GPG Key ID: 10BD786B5F6FF5DE
3 changed files with 145 additions and 38 deletions

View File

@ -11,6 +11,7 @@ import {
} from 'discord.js';
import { joinVoiceChannel, VoiceConnection, createAudioPlayer, createAudioResource, AudioPlayerStatus, PlayerSubscription, StreamType } from '@discordjs/voice';
import * as Duration from 'tinyduration';
import axios from 'axios';
// @ts-ignore - Regardless of what TS says, we cannot import yargs-parser as a default import
import * as parser from 'yargs-parser';
@ -59,6 +60,7 @@ const client = new Client({
let ytdlp: YTDlpWrap;
const player = createAudioPlayer();
const yt = new YouTube(config.youtubeApiKey);
const httpClient = axios.create();
/**
* Bot state
@ -69,7 +71,6 @@ let voiceConnection: VoiceConnection;
let playerSubscription: PlayerSubscription;
let volume = config.defaultVolume;
let ffmpegProcess = null;
let keepStreamActive = false;
function logError(message: string | Object)
{
@ -190,7 +191,6 @@ async function newVoiceConnection(destroy: boolean = false) : Promise<VoiceConne
async function playNext(message: Message | null = null, words: string[] = [])
{
if (currentSong && currentSong.isStream) {
keepStreamActive = false;
killFfmpeg();
player.pause();
}
@ -240,16 +240,9 @@ player.on('stateChange', (oldState, newState) => {
});
player.on(AudioPlayerStatus.Idle, () => {
if (!keepStreamActive) {
killFfmpeg();
log('Player idle, playing next song');
playNext();
return;
}
log('Player idle, keeping stream active, triggering new stream');
const { message, words, options } = currentSong.botMeta;
stream(message, words, options);
killFfmpeg();
log('Player idle, playing next song');
playNext();
});
player.on('error', (error) => {
@ -287,6 +280,26 @@ async function stream(message: Message, words: string[], options: Object | undef
return false;
}
const response = await httpClient.head(url);
const headers = response.headers;
const contentType = (headers['content-type'] || '').trim().toLowerCase();
console.log('Content type:', contentType);
if (contentType === 'audio/x-mpegurl') {
const getRequest = await httpClient.get(url);
const newUrl = getRequest.data.trim();
options['_'] = [newUrl];
log('Calling stream() with new URL: ' + newUrl);
return await stream(message, words, options);
}
if (!contentType.includes('audio')) {
channel.send(`Invalid content type: ${headers['content-type']}`);
return false;
}
url = parsed.href;
const songMeta = {
title: parsed.hostname,
@ -313,13 +326,6 @@ async function stream(message: Message, words: string[], options: Object | undef
ffmpegProcess.on('close', (code) => {
console.log(`FFmpeg process closed with code ${code}`);
if (code === 0) {
return;
}
if (keepStreamActive) {
setTimeout(() => stream(message, words), 5000);
}
});
const audioResource = createAudioResource(ffmpegProcess.stdout, {
@ -329,25 +335,20 @@ async function stream(message: Message, words: string[], options: Object | undef
audioResource.volume.setVolume(volume);
// Don't add the stream to the queue if it's an automatic trigger, as it's already in the queue
if (!keepStreamActive) {
songQueue.push({
url: url,
path: url,
message: message,
audioResource: audioResource,
isStream: true,
videoInfo: null,
songMeta: songMeta,
botMeta: {
message,
words,
options,
}
});
}
keepStreamActive = true;
songQueue.push({
url: url,
path: url,
message: message,
audioResource: audioResource,
isStream: true,
videoInfo: null,
songMeta: songMeta,
botMeta: {
message,
words,
options,
}
});
if (!currentSong) {
log('Nothing currently playing, triggering playNext()');
@ -801,6 +802,10 @@ client.on('messageCreate', async (message: Message) => {
return;
}
if (result === 'noreact') {
return;
}
await addReaction(message, '✅');
});
@ -851,6 +856,7 @@ async function shutdown()
}
process.on('SIGINT', shutdown);
process.on('SIGTERM', shutdown);
async function init()
{

100
package-lock.json generated
View File

@ -11,6 +11,7 @@
"dependencies": {
"@discordjs/opus": "^0.8.0",
"@discordjs/voice": "^0.14.0",
"axios": "^1.7.7",
"bufferutil": "^4.0.8",
"discord.js": "^14.16.3",
"erlpack": "github:discord/erlpack",
@ -368,6 +369,23 @@
"node": ">=8"
}
},
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
"license": "MIT"
},
"node_modules/axios": {
"version": "1.7.7",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz",
"integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==",
"license": "MIT",
"dependencies": {
"follow-redirects": "^1.15.6",
"form-data": "^4.0.0",
"proxy-from-env": "^1.1.0"
}
},
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@ -490,6 +508,18 @@
"color-support": "bin.js"
}
},
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"license": "MIT",
"dependencies": {
"delayed-stream": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@ -551,6 +581,15 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"license": "MIT",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/delegates": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
@ -698,6 +737,40 @@
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
"license": "MIT"
},
"node_modules/follow-redirects": {
"version": "1.15.9",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
"integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/RubenVerborgh"
}
],
"license": "MIT",
"engines": {
"node": ">=4.0"
},
"peerDependenciesMeta": {
"debug": {
"optional": true
}
}
},
"node_modules/form-data": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
"license": "MIT",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/fs-minipass": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
@ -1137,6 +1210,27 @@
"semver": "bin/semver.js"
}
},
"node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"license": "MIT",
"dependencies": {
"mime-db": "1.52.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
@ -1360,6 +1454,12 @@
"node": ">=0.4.0"
}
},
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
"license": "MIT"
},
"node_modules/qs": {
"version": "6.13.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",

View File

@ -15,6 +15,7 @@
"dependencies": {
"@discordjs/opus": "^0.8.0",
"@discordjs/voice": "^0.14.0",
"axios": "^1.7.7",
"bufferutil": "^4.0.8",
"discord.js": "^14.16.3",
"erlpack": "github:discord/erlpack",