Skip to content

Commit

Permalink
music-bot: 2.1.0
Browse files Browse the repository at this point in the history
* Update deps
* Add spotify playlist support
* Fully typed.
  • Loading branch information
nikosszzz committed Oct 7, 2022
1 parent a3f6ecd commit eee453d
Show file tree
Hide file tree
Showing 10 changed files with 94 additions and 42 deletions.
3 changes: 2 additions & 1 deletion commands/info/changelogs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ export default {
`;

const whatsnew = `- Move to TypeScript and Discord.JS 14.5!
- Rewrote the entire Music system!`;
- Rewrote the entire Music system!
- Added Spotify Playlist support!`;

const UpdateEmbed = new EmbedBuilder()
.setColor("#000000")
Expand Down
8 changes: 4 additions & 4 deletions commands/music/play.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { ChatInputCommandInteraction, GuildMember, EmbedBuilder, SlashCommandBui
import { bot } from "@bot";
import { MusicQueue } from "@utils/MusicQueue";
import { Song } from "@utils/Song";
import { playlistCheck } from "@utils/patterns";
import { playlistCheck, spotifyPlaylistPattern } from "@utils/patterns";
import { Logger } from "@utils/Logger";

export default {
Expand Down Expand Up @@ -42,13 +42,13 @@ export default {

if (!permissions?.has([PermissionFlagsBits.Connect, PermissionFlagsBits.Speak])) return interaction.reply({ embeds: [botNoPermissions], ephemeral: true });

if (playlistCheck.test(url)) {
if (playlistCheck.test(url) || spotifyPlaylistPattern.test(url)) {
return bot.commands.get("playlist")?.execute(interaction, url);
}

interaction.reply({ content: "⏳ Loading..." });

let song: any;
let song!: Song;
try {
song = await Song.from({ url, search: url, interaction });
} catch (err: any) {
Expand Down Expand Up @@ -83,6 +83,6 @@ export default {
});

bot.queues.set(interaction.guild?.id as string, newQueue);
newQueue.enqueue({ songs: [song] });
return newQueue.enqueue({ songs: [song] });
}
};
11 changes: 5 additions & 6 deletions commands/music/playlist.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ export default {
const url: string = interaction.options.getString("query") as string;

/* Embeds for music */

const notInVC = new EmbedBuilder()
.setColor("#000000")
.setTitle("Track Player")
Expand All @@ -42,8 +41,8 @@ export default {

if (!permissions?.has([PermissionFlagsBits.Connect, PermissionFlagsBits.Speak])) return interaction.reply({ embeds: [botNoPermissions], ephemeral: true });

let playlist: any;

interaction.deferReply();
let playlist!: Playlist;
try {
playlist = await Playlist.from({ url, search: url, interaction });
} catch (err: any) {
Expand Down Expand Up @@ -78,14 +77,14 @@ export default {
.setDescription("The following playlist has been added to the queue:")
.addFields(
{
name: playlist.data?.title, value: "** **"
name: playlist.data.title ? playlist.data.title : "Spotify Playlist", value: "** **"
})
.setURL(playlist.data?.url);
.setURL(playlist.data.url as string);

if ((playlistEmbed.data.description?.length as number) >= 2048)
playlistEmbed.data.description =
playlistEmbed.data.description?.substr(0, 2007) as string;

return interaction.reply({ embeds: [playlistEmbed] }).catch((err: any) => Logger.error({ type: "MUSICCMDS", err }));
return interaction.editReply({ embeds: [playlistEmbed] }).catch((err: any) => Logger.error({ type: "MUSICCMDS", err }));
},
};
2 changes: 1 addition & 1 deletion manager/modules/importer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export async function importer({ client }: { client: Client; }): Promise<void> {
const commands: string[] = [];

const commandCategories = readdirSync(dir);
commandCategories.forEach(async (cat) => {
commandCategories.forEach(async (cat): Promise<void> => {
const commandFiles = readdirSync(join(dir, cat)).filter(files => files.endsWith(".ts"));

commandFiles.forEach(async (file): Promise<void> => {
Expand Down
2 changes: 1 addition & 1 deletion manager/modules/presence.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export async function presence({ client }: { client: Client; }): Promise<void> {
/* Automatic presence */
let state = Number(0);

client.on("ready", async () => {
client.on("ready", async (): Promise<void> => {
Logger.log({ type: "STARTUP", msg: "Music Bot is online." });
setInterval(() => {
const presences: [{ type: ActivityType.Watching, message: string }, { type: ActivityType.Playing, message: string }] = [
Expand Down
61 changes: 47 additions & 14 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,25 +21,27 @@
"@typescript-eslint/eslint-plugin": "^5.39.0",
"array-move": "^3.0.1",
"chalk": "^4.1.2",
"discord-api-types": "^0.37.11",
"discord-api-types": "^0.37.12",
"discord.js": "^14.5.0",
"ffmpeg-static": "^5.1.0",
"lyrics-finder": "^21.7.0",
"node-spotify-api": "^1.1.1",
"simple-youtube-api": "^5.2.1",
"soundcloud-downloader": "^1.0.0",
"spotify-uri": "^3.0.3",
"spotify-url-info": "^3.1.9",
"string-progressbar": "^1.0.4",
"ts-node": "^10.9.1",
"tsconfig-paths": "^4.1.0",
"undici": "^5.11.0",
"youtube-sr": "^4.3.4",
"ytdl-core": "^4.11.2",
"ytdl-core-discord": "^1.3.1",
"ytsr": "^3.8.0"
},
"devDependencies": {
"@swc/core": "^1.3.4",
"@types/node": "^18.8.2"
"@types/node": "^18.8.3"
},
"optionalDependencies": {
"@discordjs/opus": "^0.8.0",
Expand Down
2 changes: 1 addition & 1 deletion utils/Bot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export class Bot {
public readonly devtoken: string = config.DEVTOKEN;
public readonly token: string = config.TOKEN;
public readonly debug = new Boolean;
public readonly version: string = "2.0.0";
public readonly version: string = "2.1.0";
public readonly branch = new String;

public constructor(private client: Client) {
Expand Down
29 changes: 20 additions & 9 deletions utils/Playlist.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
import youtube, { Playlist as youtubePlaylist, Thumbnail } from "youtube-sr";
import youtube, { Playlist as youtubePlaylist, Thumbnail, Video } from "youtube-sr";
import { config } from "@utils/config";
import { Song } from "@utils/Song";
import { playlistPattern } from "@utils/patterns";
import { playlistPattern, spotifyPlaylistPattern } from "@utils/patterns";
import { ChatInputCommandInteraction, CommandInteraction } from "discord.js";
import SpotifyUrlInfo, { Tracks } from "spotify-url-info";
import { fetch } from "undici";

export class Playlist {
public data: youtubePlaylist;
public req: string;
public videos: Song[];
public readonly data: youtubePlaylist;
public readonly req: string;
public readonly videos: Song[];

public constructor({ playlist, interaction }: { playlist: youtubePlaylist; interaction: CommandInteraction | ChatInputCommandInteraction; }) {
this.data = playlist;

this.videos = this.data.videos
.filter((video) => video.title != "Private video" && video.title != "Deleted video")
.filter((video: Video) => video.title != "Private video" && video.title != "Deleted video")
.slice(0, config.MAX_PLAYLIST_SIZE)
.map((video): Song => new Song({
.map((video: Video): Song => new Song({
title: video?.title as string,
url: video.url,
duration: video.duration / 1000,
Expand All @@ -26,11 +28,20 @@ export class Playlist {

public static async from({ url = "", search = "", interaction }: { url: string; search: string; interaction: CommandInteraction | ChatInputCommandInteraction; }): Promise<Playlist> {
const isYoutubeUrl = playlistPattern.test(url);
//const isSpotifyUrl = spotifyPlaylistPattern.test(url);
const isSpotifyUrl = spotifyPlaylistPattern.test(url);

let playlist: youtubePlaylist;
if (isSpotifyUrl) {
const playlistTrack = await SpotifyUrlInfo(fetch).getTracks(url);
if (playlistTrack.length > config.MAX_PLAYLIST_SIZE) {
playlistTrack.length = config.MAX_PLAYLIST_SIZE;
}

if (isYoutubeUrl) {
const spotifyPl = Promise.all(playlistTrack.map(async (track: Tracks): Promise<Video> => {
return await youtube.searchOne(`${track.name} - ${track.artists ? track.artists[0].name : ""}`, "video");
}));
playlist = new youtubePlaylist({ videos: await Promise.all((await spotifyPl).filter((song: Video): boolean => song.title != undefined || song.duration != undefined)) });
} else if (isYoutubeUrl) {
playlist = await youtube.getPlaylist(url);
} else {
const result = await youtube.searchOne(search, "playlist");
Expand Down
12 changes: 9 additions & 3 deletions utils/Song.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { AudioResource, createAudioResource, StreamType } from "@discordjs/voice";
import youtube from "youtube-sr";
import { getInfo } from "ytdl-core";
import { getInfo, videoInfo } from "ytdl-core";
import { parse, Track } from "spotify-uri";
//@ts-ignore
import Spotify from "node-spotify-api";
import ytdl from "ytdl-core-discord";
import { ChatInputCommandInteraction, CommandInteraction, EmbedBuilder } from "discord.js";
import { videoPattern, spotifyPattern } from "@utils/patterns";
import { config } from "@utils/config";
import internal from "stream";
import { Logger } from "./Logger";

const spotify = new Spotify({
id: config.SPOTIFY_CLIENT_ID,
Expand Down Expand Up @@ -39,19 +41,23 @@ export class Song {
public static async from({ url = "", search = "", interaction }: { url: string; search: string; interaction: CommandInteraction | ChatInputCommandInteraction; }): Promise<Song> {
const isYoutubeUrl = videoPattern.test(url);
const isSpotifyUrl = spotifyPattern.test(url);
//const isSoundCloudUrl = scRegex.test(url);
//const isMobileSCUrl = mobileScRegex.test(url);

let songInfo;
let songInfo: videoInfo;
let spotifyTitle: string, spotifyArtist: string;
if (isSpotifyUrl) {
const spotifyTrackID = (parse(url) as Track).id;
const spotifyInfo = await spotify.request(`https://api.spotify.com/v1/tracks/${spotifyTrackID}`).catch((err: any) => {
Logger.error({ type: "MUSIC/SPOTIFYSTACK", err: err });
return interaction.reply("There was a error with the Spotify Stack: ```" + err + "```");
});
spotifyTitle = spotifyInfo.name;
spotifyArtist = spotifyInfo.artists[0].name;

const spotifyresult = await youtube.searchOne(`${spotifyArtist} - ${spotifyTitle}`);
songInfo = await getInfo(spotifyresult.url);

return new this({
title: songInfo.videoDetails.title,
url: songInfo.videoDetails.video_url,
Expand Down Expand Up @@ -84,7 +90,7 @@ export class Song {
}

public async makeResource(): Promise<AudioResource<Song> | void> {
let stream;
let stream!: internal.Readable;

const type = this.url.includes("youtube.com") ? StreamType.Opus : StreamType.OggOpus;

Expand Down

0 comments on commit eee453d

Please sign in to comment.