src/functions/myqueue.ts (view raw)
1import { createAudioResource, createAudioPlayer, NoSubscriberBehavior, AudioPlayerStatus, VoiceConnection, AudioPlayer, AudioResource, joinVoiceChannel } from '@discordjs/voice';
2import { VoiceBasedChannel } from 'discord.js';
3import play, { YouTubeVideo } from 'play-dl';
4
5export function getChannelConnection(channel: VoiceBasedChannel) {
6 const guild = channel.guild;
7 return joinVoiceChannel({
8 channelId: channel.id,
9 guildId: guild.id,
10 adapterCreator: guild.voiceAdapterCreator
11 });
12}
13
14async function resourceFromYTUrl(url: string): Promise<AudioResource<null>> {
15 try {
16 const stream = await play.stream(url);
17 return createAudioResource(stream.stream, { inputType: stream.type })
18 } catch (error) {
19 return null;
20 }
21
22}
23
24export default class MyQueue {
25 #nowPlaying: YouTubeVideo;
26 #queue: Array<YouTubeVideo>;
27 connection: VoiceConnection;
28 player: AudioPlayer;
29
30 get queue() {
31 if (this.#nowPlaying)
32 return [this.#nowPlaying].concat(this.#queue);
33 return null
34 }
35
36 constructor() {
37 this.#nowPlaying = null;
38 this.connection = null;
39 this.#queue = Array<YouTubeVideo>();
40 this.player = createAudioPlayer({ behaviors: { noSubscriber: NoSubscriberBehavior.Stop } });
41 this.player.on(AudioPlayerStatus.Idle, () => {
42 if (this.#queue.length > 0)
43 return this.next();
44 this.connection.disconnect();
45 });
46 }
47
48 clear() {
49 this.#queue = Array<YouTubeVideo>();
50 }
51
52 stop(): boolean {
53 this.clear();
54 this.#nowPlaying = null;
55 if (this.player) {
56 const p = this.player.stop();
57 const c = this.connection.disconnect();
58 return p && c;
59 }
60 return false;
61 }
62
63 async next() {
64 if (this.#queue.length == 0)
65 return this.stop();
66 this.#nowPlaying = this.#queue.shift();
67 const resource = await resourceFromYTUrl(this.#nowPlaying.url);
68 if (!resource) {
69 return await this.next();
70 }
71 this.player.play(resource);
72 this.connection.subscribe(this.player);
73 }
74
75 add(video: YouTubeVideo, position=this.#queue.length) {
76 const l = this.#queue.length;
77 const normalizedPosition = position % (l + 1);
78 this.#queue.splice(normalizedPosition, 0, video);
79 if (l == 0) this.next();
80 }
81
82 async addArray(videos: YouTubeVideo[], channel: VoiceBasedChannel) {
83 if (!channel) {
84 console.log('Channel error:', channel);
85 return;
86 }
87 if (videos.length == 0) return [];
88 this.connection = getChannelConnection(channel);
89
90 const l = this.#queue.length;
91 this.#queue.push(...videos);
92 if (l == 0 && this.player.state.status == AudioPlayerStatus.Idle) this.next();
93 return videos;
94 }
95
96 pause() {
97 this.player.pause();
98 }
99
100 resume() {
101 this.player.unpause();
102 this.connection.subscribe(this.player);
103 }
104
105 async outro(url: string, channel: VoiceBasedChannel) {
106 if (!channel) {
107 console.log('Channel error:', channel);
108 return;
109 }
110 this.connection = getChannelConnection(channel);
111
112 this.player.pause();
113 const resource = await resourceFromYTUrl(url);
114
115 const p = createAudioPlayer();
116 p.on(AudioPlayerStatus.Idle, () => {
117 if (this.player.state.status == AudioPlayerStatus.Paused) {
118 this.resume();
119 return
120 }
121 this.connection.disconnect();
122 });
123
124 p.play(resource);
125 this.connection.subscribe(p);
126 }
127}
128
129(module).exports = MyQueue;