mirror of
https://github.com/Shik3i/KoalaSync.git
synced 2026-07-26 12:08:15 +00:00
Add hidden remote seek test controls
This commit is contained in:
@@ -1592,6 +1592,62 @@ async function routeToContent(action, payload) {
|
||||
_routeToContentInternal(tabId, action, payload, actionTimestamp, commandSenderId, 0);
|
||||
}
|
||||
|
||||
function getTabVideoState(tabId) {
|
||||
return new Promise((resolve) => {
|
||||
chrome.tabs.sendMessage(tabId, { type: 'GET_VIDEO_STATE' }, (res) => {
|
||||
if (chrome.runtime.lastError) {
|
||||
resolve({ error: chrome.runtime.lastError.message });
|
||||
return;
|
||||
}
|
||||
resolve(res);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function getReadyTabVideoState(tabId) {
|
||||
let state = await getTabVideoState(tabId);
|
||||
if (!state || state.error) {
|
||||
await injectContentScript(tabId);
|
||||
await new Promise(resolve => setTimeout(resolve, 250));
|
||||
state = await getTabVideoState(tabId);
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
async function simulateRemoteSeek(delta) {
|
||||
if (!currentTabId) return { status: 'no_tab' };
|
||||
const tabId = parseInt(currentTabId);
|
||||
if (isNaN(tabId)) return { status: 'no_tab' };
|
||||
|
||||
const state = await getReadyTabVideoState(tabId);
|
||||
if (!state || state.error) return { status: 'error', message: state?.error || 'No video state' };
|
||||
if (!state.found || !Number.isFinite(state.currentTime)) return { status: 'no_video' };
|
||||
|
||||
let targetTime = Math.max(0, state.currentTime + delta);
|
||||
if (Number.isFinite(state.duration) && state.duration > 0) {
|
||||
targetTime = Math.min(targetTime, Math.max(0, state.duration - 0.1));
|
||||
}
|
||||
|
||||
const senderId = 'KoalaDev';
|
||||
const timestamp = Date.now();
|
||||
const payload = {
|
||||
senderId,
|
||||
actionTimestamp: timestamp,
|
||||
currentTime: targetTime,
|
||||
targetTime
|
||||
};
|
||||
|
||||
addToHistory(EVENTS.SEEK, senderId);
|
||||
showNotification(senderId, EVENTS.SEEK);
|
||||
updateLastAction(EVENTS.SEEK, senderId, timestamp);
|
||||
lastActionState.targetTime = targetTime;
|
||||
if (storageInitialized) chrome.storage.session.set({ lastActionState });
|
||||
updateLocalPeerState(senderId, { currentTime: targetTime });
|
||||
routeToContent(EVENTS.SEEK, payload);
|
||||
|
||||
return { status: 'ok', targetTime };
|
||||
}
|
||||
|
||||
function isNetflixUrl(url) {
|
||||
try {
|
||||
const host = new URL(url).hostname.toLowerCase();
|
||||
@@ -2056,6 +2112,16 @@ async function handleAsyncMessage(message, sender, sendResponse) {
|
||||
sendResponse(res);
|
||||
}
|
||||
});
|
||||
} else if (message.type === 'DEV_SIMULATE_REMOTE_SEEK') {
|
||||
const delta = Number(message.delta);
|
||||
if (!Number.isFinite(delta)) {
|
||||
sendResponse({ status: 'invalid_delta' });
|
||||
return;
|
||||
}
|
||||
simulateRemoteSeek(delta).then(sendResponse).catch(err => {
|
||||
addLog(`Remote seek simulation failed: ${err.message}`, 'warn');
|
||||
sendResponse({ status: 'error', message: err.message });
|
||||
});
|
||||
} else if (message.type === 'CONTENT_EVENT') {
|
||||
const processEvent = async () => {
|
||||
// Host Control Mode (sender-side): a non-controller in host-only mode must
|
||||
|
||||
@@ -391,6 +391,7 @@
|
||||
<button class="tab-btn" data-tab="tab-sync" data-i18n="TAB_SYNC" data-i18n-title="TAB_SYNC_TOOLTIP" title="Video sync controls and remote actions">Sync</button>
|
||||
<button class="tab-btn" data-tab="tab-settings" data-i18n="TAB_SETTINGS" data-i18n-title="TAB_SETTINGS_TOOLTIP" title="Extension preferences">Settings</button>
|
||||
<button class="tab-btn" data-tab="tab-dev" data-i18n="TAB_STATUS" data-i18n-title="TAB_STATUS_TOOLTIP" title="Advanced Diagnostics & Logs">Status</button>
|
||||
<button id="devToolsTabBtn" class="tab-btn" data-tab="tab-devtools" style="display:none;">Dev</button>
|
||||
</div>
|
||||
|
||||
<!-- Room Tab -->
|
||||
@@ -692,6 +693,14 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="tab-devtools" class="tab-content">
|
||||
<label>Remote Seek</label>
|
||||
<div class="info-card" style="display:flex; gap:8px; margin-bottom:15px;">
|
||||
<button id="remoteSeekBack" class="secondary" style="flex:1; font-size:12px;">-30s</button>
|
||||
<button id="remoteSeekForward" class="secondary" style="flex:1; font-size:12px;">+30s</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="popup.js" type="module"></script>
|
||||
|
||||
<!-- Onboarding Overlay -->
|
||||
|
||||
+40
-3
@@ -71,7 +71,10 @@ const elements = {
|
||||
settingsVersion: document.getElementById('settingsVersion'),
|
||||
devSupportLink: document.getElementById('devSupportLink'),
|
||||
devReviewLink: document.getElementById('devReviewLink'),
|
||||
syncTabCopyInvite: document.getElementById('syncTabCopyInvite')
|
||||
syncTabCopyInvite: document.getElementById('syncTabCopyInvite'),
|
||||
devToolsTabBtn: document.getElementById('devToolsTabBtn'),
|
||||
remoteSeekBack: document.getElementById('remoteSeekBack'),
|
||||
remoteSeekForward: document.getElementById('remoteSeekForward')
|
||||
};
|
||||
|
||||
let localPeerId = null;
|
||||
@@ -88,6 +91,19 @@ let errorToken = 0;
|
||||
let forceSyncDone = false;
|
||||
let connectionErrorTimer = null;
|
||||
let pendingConnectionErrorMsg = null;
|
||||
|
||||
function devToolsEnabled() {
|
||||
return elements.username && elements.username.value.trim() === 'KoalaDev';
|
||||
}
|
||||
|
||||
function syncDevToolsVisibility() {
|
||||
if (!elements.devToolsTabBtn) return;
|
||||
const enabled = devToolsEnabled();
|
||||
elements.devToolsTabBtn.style.display = enabled ? '' : 'none';
|
||||
if (!enabled && document.getElementById('tab-devtools')?.classList.contains('active')) {
|
||||
document.querySelector('.tab-btn[data-tab="tab-settings"]')?.click();
|
||||
}
|
||||
}
|
||||
let roomListRefreshTimer = null;
|
||||
let roomListRefreshInterval = null;
|
||||
const ROOM_LIST_REFRESH_COOLDOWN_MS = 11000;
|
||||
@@ -210,6 +226,7 @@ async function init() {
|
||||
elements.roomId.value = localData.roomId || '';
|
||||
elements.password.value = localData.password || '';
|
||||
elements.username.value = username;
|
||||
syncDevToolsVisibility();
|
||||
if (elements.filterNoise) elements.filterNoise.checked = localData.filterNoise !== false;
|
||||
if (elements.autoSyncNextEpisode) elements.autoSyncNextEpisode.checked = localData.autoSyncNextEpisode !== false;
|
||||
const legacyTitlePrivacyMode = Object.values(TITLE_PRIVACY_MODES).includes(localData.titlePrivacyMode) ? localData.titlePrivacyMode : TITLE_PRIVACY_MODES.FULL;
|
||||
@@ -277,13 +294,13 @@ async function init() {
|
||||
const syncTabBtn = document.querySelector('.tab-btn[data-tab="tab-sync"]');
|
||||
if (syncTabBtn) syncTabBtn.click();
|
||||
showSelectVideoHint();
|
||||
} else if (localData.activeTab) {
|
||||
} else if (localData.activeTab && (localData.activeTab !== 'tab-devtools' || devToolsEnabled())) {
|
||||
const btn = document.querySelector(`.tab-btn[data-tab="${localData.activeTab}"]`);
|
||||
if (btn) btn.click();
|
||||
}
|
||||
} else {
|
||||
await populateTabs();
|
||||
if (localData.activeTab) {
|
||||
if (localData.activeTab && (localData.activeTab !== 'tab-devtools' || devToolsEnabled())) {
|
||||
const btn = document.querySelector(`.tab-btn[data-tab="${localData.activeTab}"]`);
|
||||
if (btn) btn.click();
|
||||
}
|
||||
@@ -1275,8 +1292,10 @@ elements.serverUrl.addEventListener('input', () => {
|
||||
chrome.storage.local.set({ serverUrl: elements.serverUrl.value });
|
||||
});
|
||||
|
||||
elements.username.addEventListener('input', syncDevToolsVisibility);
|
||||
elements.username.addEventListener('change', () => {
|
||||
chrome.storage.local.set({ username: elements.username.value });
|
||||
syncDevToolsVisibility();
|
||||
});
|
||||
|
||||
if (elements.langSelector) {
|
||||
@@ -1689,6 +1708,24 @@ elements.pauseBtn.addEventListener('click', () => {
|
||||
}, 2500);
|
||||
});
|
||||
|
||||
function simulateRemoteSeek(delta) {
|
||||
chrome.runtime.sendMessage({ type: 'DEV_SIMULATE_REMOTE_SEEK', delta }, (res) => {
|
||||
if (chrome.runtime.lastError || !res || res.status !== 'ok') {
|
||||
showToast(res?.message || 'Remote seek failed', 'error');
|
||||
return;
|
||||
}
|
||||
showToast(`Remote seek -> ${formatTime(res.targetTime)}`, 'success', 1200);
|
||||
refreshDebugInfo();
|
||||
});
|
||||
}
|
||||
|
||||
if (elements.remoteSeekBack) {
|
||||
elements.remoteSeekBack.addEventListener('click', () => simulateRemoteSeek(-30));
|
||||
}
|
||||
if (elements.remoteSeekForward) {
|
||||
elements.remoteSeekForward.addEventListener('click', () => simulateRemoteSeek(30));
|
||||
}
|
||||
|
||||
elements.clearLogs.addEventListener('click', () => {
|
||||
chrome.runtime.sendMessage({ type: 'CLEAR_LOGS' }, () => {
|
||||
elements.logList.innerHTML = '';
|
||||
|
||||
Reference in New Issue
Block a user