feat: 쿠키 파일 기반 설정(YOUTUBE_COOKIE_PATH)+리졸버 URL 설정화, 다음곡 프리페치로 재생지연 단축, ytresolver 리포 포함, 쿠키 재발급 스크립트, README 상세화
This commit is contained in:
@@ -8,6 +8,7 @@ import { DB } from "../utils/Database";
|
||||
import { shuffle } from "../utils/Shuffle";
|
||||
import { checkTextChannelAndMsg } from "../utils/music/Channel";
|
||||
import { Logger } from "../utils/Logger";
|
||||
import { Config } from "../utils/Config";
|
||||
|
||||
const DelayAfterErrMs = 1000 * 5;
|
||||
const idleEndTime = 1000 * 60 * 10;
|
||||
@@ -17,7 +18,9 @@ const StallThresholdMs = 1000 * 20;
|
||||
const MaxLoadFailRetry = 3;
|
||||
const LoadFailRetryDelayMs = 2500;
|
||||
|
||||
type QueueTrack = Track & { userId: string; };
|
||||
// _resolved: yt-dlp 리졸브 결과(http 소스 encoded)를 미리 받아두는 프리페치 캐시.
|
||||
// 다음 곡을 재생 전에 미리 리졸브해 두어 곡 전환 시 대기시간을 없앤다.
|
||||
type QueueTrack = Track & { userId: string; _resolved?: Promise<string>; };
|
||||
|
||||
export class GuildPlayer {
|
||||
private isDead = false;
|
||||
@@ -53,6 +56,8 @@ export class GuildPlayer {
|
||||
}
|
||||
// 새 곡이 시작되면 재생 위치 멈춤 감시 시작
|
||||
this.startStallWatchdog();
|
||||
// 현재 곡이 재생되는 동안 다음 곡을 미리 리졸브(곡 전환 지연 제거)
|
||||
this.prefetch(this.queue[1]);
|
||||
Redis?.publishState("player_update", {
|
||||
guildId: this.guild.id,
|
||||
});
|
||||
@@ -232,7 +237,8 @@ export class GuildPlayer {
|
||||
// 큐가 비어있으면 바로 재생
|
||||
await this.playNext();
|
||||
} else {
|
||||
// 재생목록 추가되었을때
|
||||
// 재생목록 추가되었을때: 다음 재생될 곡을 미리 리졸브
|
||||
this.prefetch(this.queue[1]);
|
||||
this.setMsg({ player: false, queue: true });
|
||||
}
|
||||
}
|
||||
@@ -241,10 +247,12 @@ export class GuildPlayer {
|
||||
const first = this.queue.length === 0;
|
||||
for (const track of tracks) this.queue.push({ ...track, userId });
|
||||
if (first) {
|
||||
// 큐가 비어있으면 바로 재생
|
||||
// 큐가 비어있으면 바로 재생 (+ 다음 곡 미리 리졸브)
|
||||
this.prefetch(this.queue[1]);
|
||||
await this.playNext();
|
||||
} else {
|
||||
// 재생목록 추가되었을때
|
||||
// 재생목록 추가되었을때: 다음 재생될 곡을 미리 리졸브
|
||||
this.prefetch(this.queue[1]);
|
||||
this.setMsg({ player: false, queue: true });
|
||||
}
|
||||
}
|
||||
@@ -258,8 +266,7 @@ export class GuildPlayer {
|
||||
if (src !== "youtube" && src !== "spotify" && src !== "youtubemusic") return track.encoded;
|
||||
const target = (src === "youtube" && info.uri) ? info.uri : `${info.author ?? ""} ${info.title ?? ""}`.trim();
|
||||
if (!target) return track.encoded;
|
||||
const host = process.env.LAVALINK_HOST || "192.168.10.6";
|
||||
const r = await fetch(`http://${host}:8779/resolve?url=${encodeURIComponent(target)}`, { signal: AbortSignal.timeout(45000) });
|
||||
const r = await fetch(`${Config.ytresolverUrl}/resolve?url=${encodeURIComponent(target)}`, { signal: AbortSignal.timeout(45000) });
|
||||
if (!r.ok) return track.encoded;
|
||||
const data = (await r.json()) as { url?: string };
|
||||
if (!data.url) return track.encoded;
|
||||
@@ -274,10 +281,28 @@ export class GuildPlayer {
|
||||
}
|
||||
}
|
||||
|
||||
/** 트랙의 재생용 encoded를 미리(백그라운드) 리졸브해 캐시한다. 이미 진행 중이면 무시. */
|
||||
private prefetch(track?: QueueTrack) {
|
||||
if (!track || track._resolved) return;
|
||||
track._resolved = this.resolvePlayable(track).catch((e) => {
|
||||
Logger.warn(`[GuildPlayer] 프리페치 실패(재생 시 재시도): ${String(e)}`);
|
||||
track._resolved = undefined; // 실패 시 캐시 비워 재생 시점에 다시 시도
|
||||
return track.encoded;
|
||||
});
|
||||
}
|
||||
|
||||
/** 재생용 encoded를 가져온다. 프리페치되어 있으면 즉시, 아니면 지금 리졸브한다. */
|
||||
private getResolved(track: QueueTrack): Promise<string> {
|
||||
if (!track._resolved) this.prefetch(track);
|
||||
return track._resolved!;
|
||||
}
|
||||
|
||||
public async playNext() {
|
||||
const track = this.queue[0];
|
||||
if (!track) return this.end();
|
||||
const encoded = await this.resolvePlayable(track);
|
||||
const encoded = await this.getResolved(track);
|
||||
// 다음 곡을 미리 리졸브해 곡 전환 지연 제거
|
||||
this.prefetch(this.queue[1]);
|
||||
await this.player.playTrack({ track: { encoded } });
|
||||
// 재생관련
|
||||
this.setMsg();
|
||||
|
||||
Reference in New Issue
Block a user