From 7f057d818c91518f59908b700f7887a66db89d1c Mon Sep 17 00:00:00 2001 From: tkrmagid Date: Thu, 17 Sep 2026 15:08:47 +0900 Subject: [PATCH] =?UTF-8?q?experiment:=20SABR=20=EB=B9=8C=EB=93=9C=20?= =?UTF-8?q?=ED=99=95=EB=8C=80=20=EA=B2=80=EC=A6=9D=20=E2=80=94=20=EC=B2=AB?= =?UTF-8?q?=20=EC=9E=AC=EC=83=9D=EC=9D=80=20=EB=B9=A0=EB=A5=B4=EB=82=98=20?= =?UTF-8?q?~55=EC=B4=88=20=EB=92=A4=20403=EC=9C=BC=EB=A1=9C=20=EB=81=8A?= =?UTF-8?q?=EA=B9=80(=EC=A0=81=EC=9A=A9=20=EB=B6=88=EA=B0=80)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SelfTest 확장: 관찰시간(SELFTEST_WATCH_SEC), 곡전환(SELFTEST_SECOND_QUERY) 지원. position 샘플링 주기를 Lavalink 보고 주기(5초)에 맞춰 멈춤 오탐 제거. 격리 Lavalink(.9:2334, SABR 빌드) + ytresolver OFF 결과: 일반곡(K-pop) 성공 첫소리 1455ms 20초 관찰 끊김 0 해외곡 성공 첫소리 1696ms 20초 관찰 끊김 0 직접 URL 성공 첫소리 932ms 20초 관찰 끊김 0 긴 곡(1시간) 성공 첫소리 3865ms 20초 관찰 끊김 0 플레이리스트(120곡) 성공 첫소리 5656ms 20초 관찰 끊김 0 곡 전환 성공 (Ditto → HEYA, 6.0s) 라이브 스트림 실패 ※ 현행 방식(1.18.2+ytresolver)도 동일 실패 → 기존 한계 장시간 90초 실패 50초 재생 후 정지, 2곡 재현 장시간 실패 원인: 재생 중 googlevideo 403 (PersistentHttpStream.connect → "Not success status code: 403") 대조군(현행 방식) 동일 90초 테스트: 85초 진행, 끊김 0 → 현행이 안정적. 결론: SABR 빌드는 첫 재생이 약 3초 빠르지만 곡 길이(3~4분)를 못 버티므로 적용 불가. 프로덕션(Lavalink .6, 리졸버, master)은 변경하지 않았다. --- bot/src/utils/SelfTest.ts | 44 +++++++++++++++++++++++++++++++++------ docker-compose.exp.yml | 9 ++++---- 2 files changed, 43 insertions(+), 10 deletions(-) diff --git a/bot/src/utils/SelfTest.ts b/bot/src/utils/SelfTest.ts index d9208ba..9ce7763 100644 --- a/bot/src/utils/SelfTest.ts +++ b/bot/src/utils/SelfTest.ts @@ -45,17 +45,49 @@ export async function runSelfTest() { Logger.info(`[SelfTest] 재생 요청까지 ${tPlay - t0}ms (검색+해석+리졸브 ${tPlay - tJoin}ms)`); // 실제 오디오가 흐르는지 확인: position 이 시간에 따라 증가해야 한다. + // SELFTEST_WATCH_SEC 로 관찰 시간을 늘리면 장시간 재생 안정성(중간 끊김)을 검증할 수 있다. + const watchSec = Number(process.env.SELFTEST_WATCH_SEC?.trim() || "9"); + // Lavalink 는 position 을 약 5초 주기로 보고하므로 그 주기에 맞춰 샘플링해야 오탐이 없다. + const stepMs = 5000; + const steps = Math.max(3, Math.round((watchSec * 1000) / stepMs)); const samples: number[] = []; - for (let i = 0; i < 6; i++) { - await new Promise((r) => setTimeout(r, 1500)); - samples.push(player.position); - Logger.log(`[SelfTest] t+${((i + 1) * 1.5).toFixed(1)}s position=${player.position} isPlaying=${player.isPlaying} track="${player.nowTrack?.info?.title ?? "-"}"`); + let stallCount = 0; + for (let i = 0; i < steps; i++) { + await new Promise((r) => setTimeout(r, stepMs)); + const pos = player.position; + if (samples.length > 0 && pos <= samples[samples.length - 1]) stallCount++; + samples.push(pos); + if (i % 3 === 0 || i === steps - 1) { + Logger.log(`[SelfTest] t+${((i + 1) * stepMs / 1000).toFixed(0)}s position=${pos} isPlaying=${player.isPlaying} track="${player.nowTrack?.info?.title ?? "-"}"`); + } } const advanced = samples[samples.length - 1] > samples[0] && samples[samples.length - 1] > 0; if (advanced) { - Logger.ready(`[SelfTest] 결과: 재생 성공 ✅ (position ${samples[0]} → ${samples[samples.length - 1]}, 첫 소리까지 약 ${tPlay - t0}ms)`); + Logger.ready(`[SelfTest] 결과: 재생 성공 ✅ (${(watchSec).toFixed(0)}초 관찰, position ${samples[0]} → ${samples[samples.length - 1]}, 멈춤구간 ${stallCount}/${steps - 1}, 첫 소리까지 약 ${tPlay - t0}ms)`); } else { - Logger.error(`[SelfTest] 결과: 재생 실패 ❌ (position 이 진행하지 않음: ${samples.join(", ")})`); + Logger.error(`[SelfTest] 결과: 재생 실패 ❌ (position 이 진행하지 않음: ${samples.slice(0, 6).join(", ")})`); + return; + } + + // 곡 전환 검증: 두 번째 곡을 큐에 넣고 스킵해서 실제로 다음 곡이 재생되는지 본다. + const secondQuery = process.env.SELFTEST_SECOND_QUERY?.trim(); + if (secondQuery) { + const firstTitle = player.nowTrack?.info?.title ?? ""; + await lavalinkManager.search(guild.id, secondQuery, client.user?.id ?? "selftest", player); + await new Promise((r) => setTimeout(r, 1500)); + const tSkip = Date.now(); + player.skip(); + let switched = false; + for (let i = 0; i < 10; i++) { + await new Promise((r) => setTimeout(r, 1500)); + const cur = player.nowTrack?.info?.title ?? ""; + if (cur && cur !== firstTitle && player.position > 0) { switched = true; break; } + } + if (switched) { + Logger.ready(`[SelfTest] 곡전환: 성공 ✅ ("${firstTitle.slice(0, 25)}" → "${(player.nowTrack?.info?.title ?? "").slice(0, 25)}", ${Date.now() - tSkip}ms)`); + } else { + Logger.error(`[SelfTest] 곡전환: 실패 ❌ (다음 곡이 재생되지 않음)`); + } } } catch (err) { Logger.error(`[SelfTest] 예외: ${String(err)}`); diff --git a/docker-compose.exp.yml b/docker-compose.exp.yml index 55731f8..94ca61b 100644 --- a/docker-compose.exp.yml +++ b/docker-compose.exp.yml @@ -8,12 +8,13 @@ services: - ./bot/.env environment: - REDIS=false - - LAVALINK_HOST=192.168.10.6 - - LAVALINK_PORT=2333 + - LAVALINK_HOST=127.0.0.1 + - LAVALINK_PORT=2334 - SELFTEST_GUILD_ID=1352269198297923648 - SELFTEST_VOICE_CHANNEL_ID=1352269198914621465 - SELFTEST_TEXT_CHANNEL_ID=1421342055774289980 - - SELFTEST_QUERY=LE SSERAFIM CRAZY - - YTRESOLVER_ENABLED=true + - SELFTEST_QUERY=IVE HEYA + - SELFTEST_WATCH_SEC=90 + - YTRESOLVER_ENABLED=false volumes: - ./testdb:/db