-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathqueue.ts
132 lines (108 loc) · 2.93 KB
/
queue.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
import { AudioPlayer, AudioPlayerStatus, AudioResource, VoiceConnectionStatus, createAudioPlayer, createAudioResource, getVoiceConnection, joinVoiceChannel } from "@discordjs/voice";
import { Guild } from "discord.js";
import ytdl from "ytdl-core";
import { randomUUID } from "crypto"
export type Song = {
info: ytdl.videoInfo,
/// This ID is unique to every song added, even if they are from the same music
unique_id: string
}
export class Queue {
guild: Guild;
player: AudioPlayer;
resource: AudioResource | null;
current_song: Song | null;
songs: Song[];
destroyed: boolean;
constructor(guild: Guild, voice_channel: string) {
this.guild = guild;
this.player = createAudioPlayer();
this.resource = null;
this.current_song = null;
this.songs = [];
this.destroyed = false;
const connection = joinVoiceChannel({
adapterCreator: guild.voiceAdapterCreator,
guildId: guild.id,
channelId: voice_channel
});
connection.on(VoiceConnectionStatus.Ready, (_old_state) => {
connection.subscribe(this.player);
});
this.player.on(AudioPlayerStatus.Idle, (old_state) => {
if (old_state.status === AudioPlayerStatus.Playing) {
this.play();
}
});
this.player.on("error", (error) => {
console.log(error);
})
}
play() {
this.current_song = this.get_next();
if (this.current_song === null) { // stop player
queue_manager.remove(this.guild.id);
return;
}
const stream = ytdl.downloadFromInfo(this.current_song.info, {
dlChunkSize: 100000,
filter: 'audioonly', quality: "highestaudio",
});
this.resource = createAudioResource(stream);
this.player.play(this.resource);
}
pause() {
}
unpause() {
}
/**
* @param url - Valid url of a youtube video
* @returns The video info if it is an existing youtube video, otherwise null
*/
async add_song(url: string): Promise<ytdl.videoInfo | null> {
try {
let info = await ytdl.getInfo(url);
this.songs.push({
info: info,
unique_id: randomUUID()
});
return info;
} catch (e) {
return null;
}
}
/// Returns the next song to play (first in queue) or null if the queue is empty
get_next(): Song | null {
return this.songs.shift() ?? null;
}
is_empty(): boolean {
return this.songs.length === 0;
}
destroy() {
getVoiceConnection(this.guild.id)?.destroy();
this.player.stop();
this.destroyed = true;
}
}
export class QueueManager {
map: Map<string, Queue>;
constructor() {
this.map = new Map();
}
get_or_create(guild: Guild, voice_channel: string): Queue {
let queue = this.map.get(guild.id);
if (queue !== undefined) return queue;
let new_queue = new Queue(guild, voice_channel);
this.map.set(guild.id, new_queue);
return new_queue;
}
get(guild: Guild): Queue | null {
return this.map.get(guild.id) ?? null;
}
remove(guild_id: string) {
this.map.get(guild_id)?.destroy();
this.map.delete(guild_id);
}
}
/// Maps a guild's id to its queue
export let queue_manager = new QueueManager();