fix(youtube): 未啟用時不再輸出斷線收尾
ScriptLib/youtube/chat.mjsTest/youtube_stop.test.mjsTikTok.js
commit 930d3ecb28f028457fc773d8ef9e97dd6265836dAuthor: Nuclear0709 <agp05432@gmail.com>Date: Tue Nov 10 23:37:20 2026 +0800 fix(youtube): 未啟用時不再輸出斷線收尾 - 只有真正啟動 YouTube 才標記 active,收尾時才輸出停止訊息 - handleExit 加上冪等 guard,避免重複退出造成重複收尾 - 補未啟用 YouTube 的收尾測試 diff --git a/ScriptLib/youtube/chat.mjs b/ScriptLib/youtube/chat.mjs--- a/ScriptLib/youtube/chat.mjs+++ b/ScriptLib/youtube/chat.mjs@@ -38,6 +38,7 @@ let youtubeNextPageToken = null; let youtubeViewerInterval = null; let youtubeBrowserFallbackStarted = false; let youtubeSourceMode = 'auto';+let youtubeActive = false; function loadYoutubeCache() { try {@@ -199,6 +200,7 @@ function handleYoutubeText(payload, deps, transport = 'native') { /** 每 60 秒用 API Key 更新觀眾數;瀏覽器 fallback 與原生 API 共用。 */ function startYoutubeViewerPolling(videoId, deps) { clearInterval(youtubeViewerInterval);+ youtubeActive = true; youtubeVideoId = videoId; youtubeViewerInterval = setInterval(async () => { if (!youtubeVideoId) return;@@ -218,6 +220,7 @@ function startYoutubeViewerPolling(videoId, deps) { function connect(liveChatId, videoId, channelName, deps) { const { axios, apiKey, pollIntervalS, writeLog, notify, sendSystem, sendChat } = deps;+ youtubeActive = true; youtubeLiveChatId = liveChatId; youtubeNextPageToken = null; youtubeBrowserFallbackStarted = false;@@ -423,6 +426,7 @@ export async function startYoutubeChat(channelName, deps) { onText: payload => handleYoutubeText(payload, deps, 'browser') } });+ youtubeActive = true; sendSystem(`Youtube 已透過專用瀏覽器 discovery 連線 ${channelName}`); notify("Youtube 連線", `已透過專用瀏覽器 discovery 連線 ${channelName}`, ""); } catch (error) {@@ -508,6 +512,8 @@ export async function startYoutubeChat(channelName, deps) { * @returns {Promise<void>} */ export async function stopYoutubeChat() {+ const wasActive = youtubeActive+ || Boolean(youtubePollInterval || youtubeViewerInterval || youtubeLiveChatId || youtubeVideoId); clearTimeout(youtubePollInterval); clearInterval(youtubeViewerInterval); youtubePollInterval = null;@@ -516,10 +522,11 @@ export async function stopYoutubeChat() { youtubeVideoId = null; youtubeNextPageToken = null; youtubeBrowserFallbackStarted = false;+ youtubeActive = false; try { await stopYoutubeBrowser(); } catch (error) { console.error('❌ 關閉 Youtube 專用瀏覽器失敗:', error.message); }- console.log('❌ Youtube 聊天室已斷線');+ if (wasActive) console.log('⏹️ Youtube 聊天室已停止'); }diff --git a/Test/youtube_stop.test.mjs b/Test/youtube_stop.test.mjs--- /dev/null+++ b/Test/youtube_stop.test.mjs@@ -0,0 +1,24 @@+import test from 'node:test';+import assert from 'node:assert/strict';+import { startYoutubeChat, stopYoutubeChat } from '../ScriptLib/youtube/chat.mjs';++test('未啟動 YouTube 時,收尾不應輸出聊天室已斷線', async () => {+ const logs = [];+ const original = console.log;+ console.log = (...args) => logs.push(args.join(' '));+ try {+ await startYoutubeChat('', {+ writeLog: () => {}, sendSystem: () => {}, notify: () => {},+ axios: {}, apiKey: '', pollIntervalS: 5, sendChat: () => {},+ reportTraffic: () => {}, reportFiltered: () => {}, filter: () => ({ blocked: false }),+ recordHeat: () => true, replaceEmojis: value => value, translate: async value => value,+ onViewerCount: () => {}+ });+ await stopYoutubeChat();+ await stopYoutubeChat();+ } finally {+ console.log = original;+ }+ assert.equal(logs.some(line => line.includes('Youtube 聊天室已斷線')), false);+ assert.equal(logs.some(line => line.includes('Youtube 聊天室已停止')), false);+});diff --git a/TikTok.js b/TikTok.js--- a/TikTok.js+++ b/TikTok.js@@ -353,10 +353,13 @@ const tiktokConn = createTikTokConnectionManager({ var isEnd=false+let exitStarted=false const EXIT_TIMEOUT_MS = 8000; async function handleExit() {+ if (exitStarted) return;+ exitStarted = true; console.log("⏹️ 程式結束,開始收尾..."); // 收尾若卡住(網路 / Puppeteer),必須有硬性上限,否則行程永遠不退出,