UNPKG

music-start-pro

Version:

Music Start Pro is a discord bot that can play YouTube music by slash command.

227 lines (200 loc) 6.95 kB
import { MusicInfo } from './musicInfo'; import { messages } from './language.json'; import { ButtonBuilder, ButtonStyle, MessageEditOptions, Options, ActionRowBuilder, } from 'discord.js'; const entriesOfOnePage = 30; interface langMap { [key: string]: string; } export class Queue { private _list: Array<MusicInfo>; private _index: number; private _searchResult: Array<number>; private _searchQuery: RegExp | null; constructor() { this._list = []; this._searchResult = []; this._index = 0; this._searchQuery = null; } get list(): Array<MusicInfo> { return this._list; } get len(): number { return this._list.length; } // return the current music info get current(): MusicInfo { return this._list[this._index]; } // return current page get page(): number { return Math.floor(this._index / entriesOfOnePage); } // return the number of pages get pages(): number { return Math.ceil(this.len / entriesOfOnePage); } private _genericIndex(index: number) { if (this.len === 0) return 0; index = index % this.len; return (index < 0) ? index + this.len : index; } genericPage(page: number) { if (this.pages === 0) return 0; page = page % this.pages; return (page < 0) ? page + this.pages : page; } isEmpty(): boolean { return this.len === 0; } add(info: MusicInfo) { this._list.push(info); } next(num: number) { return this.jump(this._index + num); } // @param index can be any integer. jump(index: number) { if (this.isEmpty()) return; this._index = this._genericIndex(index); } // @param index can be any integer. // @return true if success, vice versa. remove(index: number, isNowPlaying: boolean): boolean { index = this._genericIndex(index); if (index == this._index && isNowPlaying) return false; if (index <= this._index && this._index > 0) { this._index--; } this._list.splice(index, 1); return true; } swap(index1: number, index2: number) { index1 = this._genericIndex(index1); index2 = this._genericIndex(index2); if (index1 == index2) return; if (index1 == this._index) this._index = index2; if (index2 == this._index) this._index = index1; const tmp = this._list[index1]; this._list[index1] = this._list[index2]; this._list[index2] = tmp; } removeDuplicate() { const set = new Set<string>(); // set of urls const newList = new Array<MusicInfo>(); let newIndex = 0; for (let i = 0; i < this.len; i++) { if (!set.has(this._list[i].url)) { newList.push(this._list[i]); set.add(this._list[i].url); if (this._list[this._index].url == this._list[i].url) { newIndex = i; } } } this._list = newList; this._index = newIndex; } search(query: RegExp | null) { this._searchResult.length = 0; if (query !== null) { this._searchQuery = query; } else { query = this._searchQuery; } if (query === null) return; for (let i = 0; i < this.len; i++) { if (this._list[i].title.match(query)) { this._searchResult.push(i); } } } removeAll() { this._list = new Array<MusicInfo>(); this._index = 0; } // O(nLgN + n) sort() { const currentURL = this._list[this._index].url; this._list.sort((a, b) => { return a.title.localeCompare(b.title); }); // fix current index after sorting for (let i = 0; i < this.len; i++) { if (this._list[i].url == currentURL) { this._index = i; break; } } } // O(2n) shuffle() { const currentURL = this._list[this._index].url; // Knuth shuffle algorithm for (let i = 0; i < this.len; i++) { const j = ~~(Math.random() * i); // swap i and j const tmp = this._list[i]; this._list[i] = this._list[j]; this._list[j] = tmp; } // fix current index after shuffling for (let i = 0; i < this.len; i++) { if (this._list[i].url == currentURL) { this._index = i; break; } } } private showListByPage(lang: string, page: number, isShowSearchList: boolean = false): string { let content = '```yaml\n'; const pages = isShowSearchList ? Math.ceil(this._searchResult.length / entriesOfOnePage) : this.pages; // when showing the current page, increasing one for the number of current page. content += `page:\t${page + 1}/${Math.max(pages, 1)}\n`; if (this.isEmpty() || (isShowSearchList && this._searchResult.length === 0)) { content += (messages.playlist_is_empty as langMap)[lang]; } const len = isShowSearchList ? this._searchResult.length : this.len; for (let i = page * entriesOfOnePage; i < Math.min((page + 1) * entriesOfOnePage, len); i++) { const j = isShowSearchList ? this._searchResult[i] : i; if (j == this._index) { content += '>' + `${j}`.padStart(3, ' ') + `:\t${this._list[j].title}\n`; } else { content += `${j}`.padStart(4, ' ') + `:\t${this._list[j].title}\n`; } } return content + '\n```'; } showList(lang: string, page?: number | undefined, isShowSearchList: boolean = false): MessageEditOptions | Options { page = page ?? Math.floor(this._index / entriesOfOnePage); const btnNext = new ButtonBuilder() .setCustomId(!isShowSearchList ? `next-${page}` : `nextSearch-${page}`) .setLabel('Next') .setStyle(ButtonStyle.Secondary); const btnPre = new ButtonBuilder() .setCustomId(!isShowSearchList ? `pre-${page}` : `preSearch-${page}`) .setLabel('Previous') .setStyle(ButtonStyle.Secondary); const btnRefresh = new ButtonBuilder() .setCustomId(!isShowSearchList ? 'refresh' : 'refreshSearch') .setLabel('Refresh') .setStyle(ButtonStyle.Secondary); return { content: this.showListByPage(lang, page, isShowSearchList), components: [new ActionRowBuilder() .addComponents(btnPre) .addComponents(btnRefresh) .addComponents(btnNext) ] }; } toList(): MusicInfo[] { return this._list; } }