import { Connectors, LoadType, Shoukaku, Track } from "shoukaku"; import { Client } from "discord.js"; import { GuildPlayer } from "./GuildPlayer"; import { Config } from "../utils/Config"; import { Logger } from "../utils/Logger"; import { parseLink } from "../utils/music/Url"; import { shuffle } from "../utils/Shuffle"; import { Spotify } from "../utils/api/Spotify"; import { YoutubeMusic } from "../utils/api/YoutubeMusic"; export class LavalinkManager { public shoukaku: Shoukaku; private players: Map = new Map(); constructor(readonly client: Client) { this.shoukaku = new Shoukaku(new Connectors.DiscordJS(client), [{ name: "tkrmagid-Lavalink-Server", url: `${Config.lavalink.host}:${Config.lavalink.port}`, auth: Config.lavalink.pw, secure: false, }], { moveOnDisconnect: true, reconnectTries: 3, }); this.shoukaku.on("ready", (name) => { Logger.ready(`[LavalinkManager] 노드 [${name}] 연결 완료!`); }); this.shoukaku.on("error", (name, err) => { Logger.error(`[LavalinkManager] 노드 [${name}] 에러: ${String(err)}`); }); } getPlayer(guildId: string) { return this.players.get(guildId); } addPlayer(guildId: string, player: GuildPlayer) { this.players.set(guildId, player); } delPlayer(guildId: string) { this.players.delete(guildId); } async search(guildId: string, query: string, userId: string, player?: GuildPlayer) { const node = this.shoukaku.options.nodeResolver(this.shoukaku.nodes); if (!node) { this.delPlayer(guildId); throw new ReferenceError(`[LavalinkManager] lavalink node is missing`); } player = player ?? this.getPlayer(guildId); if (!player) return; const { isUrl, text, flags } = parseLink(query.trim()); let searchText = isUrl ? text : (await Spotify.getSearchUrl(text)) ?? (await YoutubeMusic.getSearchUrl(text)) ?? `ytsearch:${text}`; if (flags.has('y')) searchText = (await YoutubeMusic.getSearchUrl(text)) ?? `ytsearch:${text}`; if (searchText.startsWith("ytsearch") && !flags.has('o')) searchText += " Topic"; const result = await node.rest.resolve(searchText); if (!result || result.loadType === LoadType.EMPTY || result.loadType === LoadType.ERROR) { if (result?.loadType === LoadType.ERROR) Logger.error(`[LavalinkManager] loadtype ERROR: ${result.data.message}`); // 노래 못찾았을때 player.errMsg(`노래를 찾을수 없습니다.`); return; } if (result.loadType === LoadType.PLAYLIST) { player.addTracks( flags?.has("s") ? shuffle(result.data.tracks) : result.data.tracks, userId ); return; } if (result.loadType === LoadType.TRACK) { player.addTrack(result.data, userId); return; } if (result.loadType === LoadType.SEARCH) { if (result.data.length === 0) { player.errMsg(`노래를 찾을수 없습니다.`); return; } player.addTrack(result.data[0], userId); return; } } public async youtubeSearch(query: string): Promise { const node = this.shoukaku.options.nodeResolver(this.shoukaku.nodes); if (!node) throw new ReferenceError(`[LavalinkManager] lavalink node is missing`); const result = await node.rest.resolve(`ytsearch:${query.trim()}`); // if (!result || result.loadType === LoadType.EMPTY || result.loadType === LoadType.ERROR) return []; if (result?.loadType === LoadType.TRACK) return [ result.data ]; if (result?.loadType === LoadType.SEARCH) return result.data; return []; } }