diff --git a/index.ts b/index.ts index ddf40db..65c56b8 100644 --- a/index.ts +++ b/index.ts @@ -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 { }); 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() { diff --git a/package-lock.json b/package-lock.json index ad160b2..e87d816 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index fcc6c4e..180fe9d 100644 --- a/package.json +++ b/package.json @@ -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",