Skip to content

Commit

Permalink
feat: remove play-dl
Browse files Browse the repository at this point in the history
- Use Distube ytdl
- upgraded dependencies
  • Loading branch information
DestroyCom committed Feb 2, 2025
1 parent 14c2e10 commit 1a4f00e
Show file tree
Hide file tree
Showing 13 changed files with 3,917 additions and 1,954 deletions.
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,6 @@ module.exports = {
'simple-import-sort/imports': 'error',
'simple-import-sort/exports': 'error',
'@typescript-eslint/no-unused-vars': ['error'],
'no-useless-escape': ['warn'],
},
};
5,698 changes: 3,791 additions & 1,907 deletions package-lock.json

Large diffs are not rendered by default.

25 changes: 15 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
{
"name": "stroycord",
"version": "1.0.0",
"version": "1.3.0",
"description": "A discord bot to play music in VC",
"main": "src/Bot.ts",
"scripts": {
"dev": "tsx watch ./src/Bot.ts",
"start": "node ./dist/Bot.js",
"build": "npm run lint && tsup ./src/Bot.ts --minify",
"lint": "eslint --ignore-path .eslintignore --ext .js,.ts .",
"prepare": "husky install"
"lint": "eslint --ignore-path .eslintignore --ext .js,.ts . --fix",
"prepare": "husky install",
"knip": "knip"
},
"husky": {
"hooks": {
Expand All @@ -19,21 +20,24 @@
"author": "Dest.Com",
"license": "GPL-3.0",
"dependencies": {
"@discordjs/opus": "^0.9.0",
"@discordjs/voice": "^0.16.0",
"@discordjs/opus": "0.10.0",
"@discordjs/voice": "0.18.0",
"@distube/ytdl-core": "^4.16.0",
"@distube/ytpl": "^1.2.1",
"@distube/ytsr": "^2.0.4",
"axios": "^1.7.9",
"discord.js": "^14.13.0",
"dotenv": "^16.3.1",
"i18n-js": "^4.3.0",
"libsodium-wrappers": "^0.7.11",
"libsodium-wrappers": "0.7.15",
"mongodb": "^5.8.1",
"mongoose": "^7.6.0",
"play-dl": "^1.9.7"
"mongoose": "^7.6.0"
},
"devDependencies": {
"@commitlint/cli": "^17.7.1",
"@commitlint/config-conventional": "^17.7.0",
"@types/i18n-js": "^3.8.5",
"@types/node": "^20.5.4",
"@types/node": "^20.17.16",
"@typescript-eslint/eslint-plugin": "^6.7.4",
"@typescript-eslint/parser": "^6.4.1",
"eslint": "^8.51.0",
Expand All @@ -45,10 +49,11 @@
"eslint-plugin-promise": "^6.1.1",
"eslint-plugin-simple-import-sort": "^10.0.0",
"husky": "^8.0.0",
"knip": "^5.43.6",
"prettier": "^3.0.2",
"tsup": "^7.2.0",
"tsx": "^3.12.7",
"typescript": "^5.2.2"
"typescript": "^5.7.3"
},
"overrides": {
"discord-api-types": ">=0.30.0"
Expand Down
25 changes: 22 additions & 3 deletions src/commands/textCommands.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import { AudioPlayerStatus } from '@discordjs/voice';
import { PermissionFlagsBits, TextBasedChannel, User, VoiceBasedChannel, VoiceChannel } from 'discord.js';
import { yt_validate } from 'play-dl';
import {
PartialGroupDMChannel,
PermissionFlagsBits,
TextBasedChannel,
User,
VoiceBasedChannel,
VoiceChannel,
} from 'discord.js';
import { activePlayers } from 'src/Bot';
import i18n from 'src/config/i18n';
import { authorNotInVoiceChannel, noMusicCurrentlyPlaying } from 'src/core/errors';
Expand All @@ -14,6 +20,7 @@ import { missingRequiredArgument, unknownError, unreconizedArgumentEmbed } from
import { queueEmbed } from 'src/utils/embeds/listSongEmbed';
import { pauseEmbed, removeEmbed, resumeEmbed, skipEmbed } from 'src/utils/embeds/playerEmbeds';
import { nowPlayingEmbed } from 'src/utils/embeds/songEmbed';
import { yt_validate } from 'src/utils/utils';

export const playCommand = (
splittedMessage: string[],
Expand All @@ -37,7 +44,8 @@ export const playCommand = (
const request = splittedMessage.slice(1).join(' ');
const requestType = yt_validate(request);

if (request === '') return textChannel.send({ embeds: [missingRequiredArgument()] });
if (request === '' && !(textChannel instanceof PartialGroupDMChannel))
return textChannel.send({ embeds: [missingRequiredArgument()] });

if (request.startsWith('https') && requestType === 'video') {
songRequest(request, guildId, requestAuthor, textChannel.id, voiceChannel);
Expand All @@ -49,6 +57,7 @@ export const playCommand = (
if (request.startsWith('https://www.youtube.com/live/')) {
songRequest(request, guildId, requestAuthor, textChannel.id, voiceChannel);
} else {
if (textChannel instanceof PartialGroupDMChannel) return;
return textChannel.send({
embeds: [unreconizedArgumentEmbed()],
});
Expand All @@ -72,6 +81,7 @@ export const skipCommand = async (
const nextSongs = await getNextSongs(guildId);

if (nextSongs.length != 0) {
if (textChannel instanceof PartialGroupDMChannel) return;
await textChannel.send({
embeds: [await skipEmbed(author, guildId)],
});
Expand All @@ -93,8 +103,10 @@ export const pauseCommand = async (
if (guildPlayerState === AudioPlayerStatus.Playing || guildPlayerState === AudioPlayerStatus.Buffering) {
await pause(guildId);

if (textChannel instanceof PartialGroupDMChannel) return;
textChannel.send({ embeds: [pauseEmbed(author)] });
} else {
if (textChannel instanceof PartialGroupDMChannel) return;
textChannel.send({
embeds: [unknownError(i18n.t('commandContext.unableToPause'))],
});
Expand All @@ -115,10 +127,12 @@ export const resumeCommand = async (

if (guildPlayerState === AudioPlayerStatus.Paused || guildPlayerState === AudioPlayerStatus.AutoPaused) {
await resume(guildId);
if (textChannel instanceof PartialGroupDMChannel) return;
textChannel.send({
embeds: [resumeEmbed(author)],
});
} else {
if (textChannel instanceof PartialGroupDMChannel) return;
textChannel.send({
embeds: [unknownError(i18n.t('commandContext.unableToResume'))],
});
Expand All @@ -137,6 +151,7 @@ export const queueCommand = async (

const nextSongs = await getNextSongs(guildId);

if (textChannel instanceof PartialGroupDMChannel) return;
textChannel.send({
embeds: [await queueEmbed(nextSongs)],
});
Expand All @@ -155,6 +170,7 @@ export const removeCommand = async (
await shiftSongs(guildId);
await remove(guildId);

if (textChannel instanceof PartialGroupDMChannel) return;
textChannel.send({
embeds: [removeEmbed(author)],
});
Expand All @@ -171,6 +187,7 @@ export const currentCommand = async (

const currentSong = await getFirstSong(guildId);

if (textChannel instanceof PartialGroupDMChannel) return;
textChannel.send({
embeds: [nowPlayingEmbed(currentSong)],
});
Expand Down Expand Up @@ -205,6 +222,8 @@ const hasPermission = (voiceChannel: VoiceChannel, textChannel: TextBasedChannel
const retreivePermission = voiceChannel.permissionsFor(textChannel.client.user);

if (!retreivePermission?.has(PermissionFlagsBits.Connect) || !retreivePermission?.has(PermissionFlagsBits.Speak)) {
if (textChannel instanceof PartialGroupDMChannel) return;

textChannel.send({
embeds: [unknownError(i18n.t('botBehaviour.needPermission'))],
});
Expand Down
4 changes: 3 additions & 1 deletion src/core/errors.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { TextBasedChannel } from 'discord.js';
import { PartialGroupDMChannel, TextBasedChannel } from 'discord.js';
import { noMusicCurrentlyPlayingEmbed, noPresenceInVoiceChannelEmbed } from 'src/utils/embeds/errorsEmbed';

export const authorNotInVoiceChannel = (textChannel: TextBasedChannel, commandTried: string) => {
if (textChannel instanceof PartialGroupDMChannel) return;
return textChannel.send({
embeds: [noPresenceInVoiceChannelEmbed(commandTried)],
});
};

export const noMusicCurrentlyPlaying = (textChannel: TextBasedChannel) => {
if (textChannel instanceof PartialGroupDMChannel) return;
return textChannel.send({
embeds: [noMusicCurrentlyPlayingEmbed()],
});
Expand Down
4 changes: 2 additions & 2 deletions src/core/messages.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import ytpl from '@distube/ytpl';
import { TextChannel, User } from 'discord.js';
import { YouTubePlayList } from 'play-dl';
import { client } from 'src/Bot';
import { getFirstSong, getNextSongs } from 'src/database/queries/guilds/get';
import { playlistEmbed } from 'src/utils/embeds/listSongEmbed';
Expand Down Expand Up @@ -49,7 +49,7 @@ export const sendEmbed = async (

export const sendQueueEmbed = async (
guildId: string,
playlistData: YouTubePlayList,
playlistData: ytpl.result,
textChannelId: string,
author: User
) => {
Expand Down
21 changes: 16 additions & 5 deletions src/core/player.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import { createAudioPlayer, createAudioResource, getVoiceConnection, joinVoiceChannel } from '@discordjs/voice';
import { stream } from 'play-dl';
import {
createAudioPlayer,
createAudioResource,
getVoiceConnection,
joinVoiceChannel,
StreamType,
} from '@discordjs/voice';
import ytdl from '@distube/ytdl-core';
import { activePlayers, client } from 'src/Bot';
import { emptyNextSongs, removeCurrentPlayingSong } from 'src/database/queries/guilds/delete';
import { getCurrentVoiceChannel, getFirstSong, getNextSongs } from 'src/database/queries/guilds/get';
Expand Down Expand Up @@ -33,9 +39,14 @@ export const songPlayer = async (guildId: string) => {
createAudioPlayerListener(audioPlayer, guildId);
}

const source = await stream(nextSong.url);
const audioStream = createAudioResource(source.stream, {
inputType: source.type,
const stream = ytdl(nextSong.url, {
quality: 'highestaudio',
filter: 'audioonly',
highWaterMark: 1 << 25,
});

const audioStream = createAudioResource(stream, {
inputType: StreamType.Arbitrary,
});

audioPlayer.play(audioStream);
Expand Down
9 changes: 3 additions & 6 deletions src/core/requestHandlers/playlistRequest.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import ytpl from '@distube/ytpl';
import { User, VoiceBasedChannel } from 'discord.js';
import { playlist_info } from 'play-dl';

import { sendQueueEmbed } from '../messages';
import { songRequest } from './songRequest';
Expand All @@ -11,11 +11,8 @@ export const playlistHandler = async (
textChannelId: string,
voiceChannel?: VoiceBasedChannel | null
) => {
const playlistData = await playlist_info(url, {
incomplete: true,
});

const playlistVideos = await playlistData.all_videos();
const playlistData = await ytpl(url);
const playlistVideos = playlistData.items;

for (let i = 0; i < playlistVideos.length; i++) {
const video = playlistVideos[i];
Expand Down
7 changes: 4 additions & 3 deletions src/core/requestHandlers/searchRequest.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import ytsr from '@distube/ytsr';
import { User, VoiceBasedChannel } from 'discord.js';
import { search } from 'play-dl';

import { songRequest } from './songRequest';

Expand All @@ -10,7 +10,8 @@ export const searchSong = async (
textChannelId: string,
voiceChannel?: VoiceBasedChannel | null
) => {
const searchedVideo = await search(searchInput, { limit: 1 });
const searchedVideo = await ytsr(searchInput, { limit: 1 });
const video = searchedVideo.items[0];

songRequest(searchedVideo[0].url, guildId, requestAuthor, textChannelId, voiceChannel);
songRequest(video.url, guildId, requestAuthor, textChannelId, voiceChannel);
};
4 changes: 2 additions & 2 deletions src/database/queries/guilds/get.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ export const fetchGuild = async (guildId: string) => {
return query;
};

export const getSongs = async (guildId: string) => {
/* export const getSongs = async (guildId: string) => {
const guild = await fetchGuild(guildId);
return guild.nextSongs;
};
}; */

export const getFirstSong = async (guildId: string) => {
const guild = await fetchGuild(guildId);
Expand Down
3 changes: 1 addition & 2 deletions src/listeners/messageListener.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Client, Events } from 'discord.js';
import { yt_validate } from 'play-dl';
import { commands } from 'src/commands/slashCommands';
import {
currentCommand,
Expand All @@ -14,7 +13,7 @@ import {
import { secrets } from 'src/config/secrets';
import { fetchGuild } from 'src/database/queries/guilds/get';
import { unknownRequestEmbed } from 'src/utils/embeds/errorsEmbed';
import { messageFormater } from 'src/utils/utils';
import { messageFormater, yt_validate } from 'src/utils/utils';

export default (client: Client): void => {
client.on(Events.MessageCreate, async (message) => {
Expand Down
12 changes: 7 additions & 5 deletions src/utils/embeds/listSongEmbed.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import ytpl from '@distube/ytpl';
import { EmbedBuilder, User } from 'discord.js';
import { YouTubePlayList } from 'play-dl';
import i18n from 'src/config/i18n';
import { secrets } from 'src/config/secrets';

Expand Down Expand Up @@ -56,7 +56,7 @@ export const queueEmbed = async (nextSongs: songInterface[]): Promise<EmbedBuild
.addFields(tabEmbeds);
};

export const playlistEmbed = async (author: User, playlistData: YouTubePlayList): Promise<EmbedBuilder> => {
export const playlistEmbed = async (author: User, playlistData: ytpl.result): Promise<EmbedBuilder> => {
const msg = new EmbedBuilder()
.setTitle(`${author.username} ${i18n.t('embedsText.lists.playlist.title')} !`)
.setAuthor({
Expand All @@ -82,17 +82,19 @@ export const playlistEmbed = async (author: User, playlistData: YouTubePlayList)
},
{
name: `${i18n.t('embedsText.lists.playlist.fields.playlistCreatedBy')} :`,
value: (playlistData.channel && playlistData.channel.name) || 'No name',
value: (playlistData.author && playlistData.author.name) || 'No name',
inline: true,
},
{
name: `${i18n.t('embedsText.lists.playlist.fields.putInQueue')} :`,
value:
(playlistData.videos.length > 30 ? 30 : playlistData.videos.length) + `${i18n.t('embedsText.global.musics')}`,
(playlistData.items.length > 30 ? 30 : playlistData.items.length) + `${i18n.t('embedsText.global.musics')}`,
}
);

if (playlistData.thumbnail !== undefined) msg.setThumbnail(playlistData.thumbnail.url);
if (playlistData.items.length > 0 && playlistData.items[0].thumbnail) {
msg.setThumbnail(playlistData.items[0].thumbnail);
}

return msg;
};
Loading

0 comments on commit 1a4f00e

Please sign in to comment.