mirror of
https://github.com/Shik3i/KoalaSync.git
synced 2026-07-26 12:08:15 +00:00
feat: v2.0.0 - enhanced debug reports, platform detection, copy report, mockup i18n, new icons
- Copy Logs button now generates full Markdown debug report (system, tab, connection, video, history, logs)
- Enhanced Dev tab with 20+ video state fields (network, dimensions, error, shadow DOM, platform detection)
- Platform auto-detection (YouTube, Netflix, Twitch, Prime Video, Disney+, Hulu, HBO, Vimeo, Dailymotion)
- 'No video found' diagnostic mode with hints
- Convert mockup from inline <span lang> to {{MOCK_*}} template vars (all 6 languages)
- New TwoPointZero branding: icons (16/32/48/96/128), favicons (PNG 16/32 + apple-touch + 192)
- Fix logo vertical centering via display:contents on picture wrappers
- Increase popup logo to 48px, website nav logo to 64px
- AVIF quality 70->80, speed 6->4, remove min 3KB threshold
- Add 32+96 icon sizes to manifest
- Remove amazon. from tab blacklist (unblocks Prime Video)
- Update TESTED_SERVICES.md: mark Prime Video as non-functional
- Update CONTRIBUTING.md with Copy Debug Report workflow
This commit is contained in:
+19
-6
@@ -112,16 +112,29 @@ KoalaSync uses a **single source of truth** for all protocol constants in `share
|
||||
|
||||
## Bug Report Template
|
||||
|
||||
When filing a bug, include as much of the following as possible:
|
||||
When filing a bug, the easiest way is to use the **Copy Logs** button in the extension's **Status** tab. It copies a fully formatted Markdown report to your clipboard containing:
|
||||
|
||||
- System info (version, protocol, peer ID, browser)
|
||||
- Connection status (server, room, peers, reconnect state)
|
||||
- Video debug info (playback state, readyState, network state, dimensions, error codes, shadow DOM detection, platform)
|
||||
- Action history (last 20 events)
|
||||
- Log entries (last 50)
|
||||
|
||||
Simply paste the clipboard contents into your GitHub issue and add:
|
||||
|
||||
| Field | Example |
|
||||
|-------|---------|
|
||||
| **Steps to Reproduce** | 1. Create room → 2. Join from second browser → 3. Play video |
|
||||
| **Expected Behavior** | Both peers play simultaneously |
|
||||
| **Actual Behavior** | Peer B remains paused |
|
||||
|
||||
If you cannot access the Status tab, include as much of the following manually:
|
||||
|
||||
| Field | Example |
|
||||
|-------|---------|
|
||||
| **Browser** | Chrome 125, Firefox 128 |
|
||||
| **Extension Version** | v1.9.3 (visible in Dev tab) |
|
||||
| **Dev Tab Output** | Connection status, video debug info, log entries |
|
||||
| **Steps to Reproduce** | 1. Create room → 2. Join from second browser → 3. Play video |
|
||||
| **Expected Behavior** | Both peers play simultaneously |
|
||||
| **Actual Behavior** | Peer B remains paused |
|
||||
| **Extension Version** | v1.9.3 (visible at bottom of Settings tab) |
|
||||
| **Website/Platform** | Netflix, YouTube, Twitch, Jellyfin, etc.
|
||||
|
||||
---
|
||||
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ This document tracks which streaming platforms and media servers have been teste
|
||||
| **Jellyfin** | ✅ Full | ✅ Full | ✅ Full | Self-hosted. Full HTML5 player access. |
|
||||
| **Plex** | Not tested | Not tested | Not tested | Community reports indicate compatibility via HTML5 player mode. |
|
||||
| **Disney+** | Not tested | Not tested | Not tested | Widevine DRM may restrict title detection similar to Netflix. |
|
||||
| **Prime Video** | Not tested | Not tested | Not tested | DRM-protected player may restrict script injection. |
|
||||
| **Prime Video** | ❌ None | ❌ None | ❌ | Extension cannot detect video elements. Play/Pause do not work. Media title detection fails. DRM and custom player prevent script access. |
|
||||
| **HBO Max / Max** | Not tested | Not tested | Not tested | — |
|
||||
| **Crunchyroll** | Not tested | Not tested | Not tested | — |
|
||||
| **Vimeo** | Not tested | Not tested | Not tested | — |
|
||||
|
||||
@@ -1273,7 +1273,12 @@ async function handleAsyncMessage(message, sender, sendResponse) {
|
||||
targetTabId: currentTabId,
|
||||
episodeLobby: episodeLobby,
|
||||
reconnectAttempts,
|
||||
reconnectSlowMode: reconnectFailed
|
||||
reconnectSlowMode: reconnectFailed,
|
||||
roomId: currentRoom ? currentRoom.roomId : null,
|
||||
serverUrl: currentServerUrl,
|
||||
version: APP_VERSION,
|
||||
protocolVersion: PROTOCOL_VERSION,
|
||||
roomPassword: currentRoom ? currentRoom.password : null
|
||||
});
|
||||
} else if (message.type === 'LEAVE_ROOM') {
|
||||
emit(EVENTS.LEAVE_ROOM, { peerId });
|
||||
|
||||
+72
-2
@@ -433,6 +433,41 @@
|
||||
|
||||
if (message.type === 'GET_VIDEO_STATE') {
|
||||
const video = findVideo();
|
||||
|
||||
const platform = (() => {
|
||||
const h = window.location.hostname.toLowerCase();
|
||||
if (h.includes('youtube.com')) return 'YouTube';
|
||||
if (h.includes('twitch.tv')) return 'Twitch';
|
||||
if (h.includes('netflix.com')) return 'Netflix';
|
||||
if (h.includes('primevideo.com') || h.includes('amazon.')) return 'Prime Video';
|
||||
if (h.includes('disneyplus.com')) return 'Disney+';
|
||||
if (h.includes('hulu.com')) return 'Hulu';
|
||||
if (h.includes('max.com') || h.includes('hbomax.com')) return 'Max/HBO';
|
||||
if (h.includes('vimeo.com')) return 'Vimeo';
|
||||
if (h.includes('dailymotion.com')) return 'Dailymotion';
|
||||
return 'Generic';
|
||||
})();
|
||||
|
||||
const networkStates = ['NETWORK_EMPTY', 'NETWORK_IDLE', 'NETWORK_LOADING', 'NETWORK_NO_SOURCE'];
|
||||
const readyStates = ['HAVE_NOTHING', 'HAVE_METADATA', 'HAVE_CURRENT_DATA', 'HAVE_FUTURE_DATA', 'HAVE_ENOUGH_DATA'];
|
||||
|
||||
const videoCount = document.querySelectorAll('video').length;
|
||||
const inShadowDom = (() => {
|
||||
let el = video;
|
||||
while (el) {
|
||||
if (el.toString() === '[object ShadowRoot]') return true;
|
||||
el = el.parentNode;
|
||||
}
|
||||
// Also check if any potential host has shadow root (even if no video found)
|
||||
if (!video) {
|
||||
const hosts = document.querySelectorAll('[id*="player" i], [class*="player" i], [id*="video" i], [class*="video" i]');
|
||||
for (const host of hosts) {
|
||||
if (host.shadowRoot) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
})();
|
||||
|
||||
if (video) {
|
||||
const dataAttributes = {};
|
||||
if (video.attributes) {
|
||||
@@ -450,24 +485,59 @@
|
||||
artwork: Array.from(navigator.mediaSession.metadata.artwork || []).map(a => a.src)
|
||||
} : null;
|
||||
|
||||
const errorInfo = video.error ? {
|
||||
code: video.error.code,
|
||||
message: video.error.message
|
||||
} : null;
|
||||
|
||||
sendResponse({
|
||||
found: true,
|
||||
paused: video.paused,
|
||||
currentTime: video.currentTime,
|
||||
duration: video.duration || 0,
|
||||
readyState: video.readyState,
|
||||
readyStateLabel: readyStates[video.readyState] || 'UNKNOWN',
|
||||
networkState: video.networkState,
|
||||
networkStateLabel: networkStates[video.networkState] || 'UNKNOWN',
|
||||
muted: video.muted,
|
||||
volume: video.volume,
|
||||
playbackRate: video.playbackRate,
|
||||
videoWidth: video.videoWidth,
|
||||
videoHeight: video.videoHeight,
|
||||
seeking: video.seeking,
|
||||
ended: video.ended,
|
||||
error: errorInfo,
|
||||
buffered: video.buffered && video.buffered.length > 0
|
||||
? Array.from({ length: video.buffered.length }, (_, i) =>
|
||||
`${video.buffered.start(i).toFixed(1)}-${video.buffered.end(i).toFixed(1)}s`).join(', ')
|
||||
: 'none',
|
||||
loop: video.loop,
|
||||
url: window.location.href,
|
||||
pageTitle: document.title,
|
||||
id: video.id || 'none',
|
||||
className: video.className || 'none',
|
||||
src: video.src || 'none',
|
||||
currentSrc: video.currentSrc || 'none',
|
||||
dataAttributes,
|
||||
metadata
|
||||
metadata,
|
||||
videoCount,
|
||||
inShadowDom,
|
||||
platform
|
||||
});
|
||||
} else {
|
||||
sendResponse({ error: 'No video found' });
|
||||
sendResponse({
|
||||
found: false,
|
||||
videoCount,
|
||||
inShadowDom,
|
||||
platform,
|
||||
url: window.location.href,
|
||||
pageTitle: document.title,
|
||||
metadata: (navigator.mediaSession && navigator.mediaSession.metadata) ? {
|
||||
title: navigator.mediaSession.metadata.title,
|
||||
artist: navigator.mediaSession.metadata.artist,
|
||||
album: navigator.mediaSession.metadata.album
|
||||
} : null
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
+230
-21
@@ -1419,17 +1419,163 @@ chrome.runtime.onMessage.addListener((msg) => {
|
||||
});
|
||||
|
||||
elements.copyLogs.addEventListener('click', () => {
|
||||
chrome.runtime.sendMessage({ type: 'GET_LOGS' }, (logs) => {
|
||||
if (!logs || logs.length === 0) return;
|
||||
const text = logs.map(l => `[${l.timestamp}] [${l.type}] ${l.message}`).join('\n');
|
||||
const utcNow = new Date().toISOString().replace('T', ' ').slice(0, 19) + ' UTC';
|
||||
const userAgent = String(navigator.userAgent || '');
|
||||
|
||||
const safe = (val, fb) => (val != null ? val : fb);
|
||||
|
||||
Promise.all([
|
||||
new Promise(resolve => chrome.runtime.sendMessage({ type: 'GET_STATUS' }, resolve)),
|
||||
new Promise(resolve => chrome.runtime.sendMessage({ type: 'GET_LOGS' }, resolve)),
|
||||
new Promise(resolve => chrome.runtime.sendMessage({ type: 'GET_HISTORY' }, resolve))
|
||||
]).then(([status, logs, history]) => {
|
||||
status = status || {};
|
||||
logs = logs || [];
|
||||
history = history || [];
|
||||
|
||||
const videoPromise = (status && status.targetTabId)
|
||||
? new Promise(resolve => chrome.runtime.sendMessage({ type: 'GET_VIDEO_STATE', tabId: status.targetTabId }, resolve))
|
||||
: Promise.resolve(null);
|
||||
|
||||
videoPromise.then(rawVideo => {
|
||||
const vs = rawVideo || {};
|
||||
const lines = [];
|
||||
|
||||
// ── Header ──
|
||||
const verStr = safe(status.version, '?');
|
||||
lines.push('# KoalaSync Debug Report');
|
||||
lines.push(`> **v${verStr}** | ${utcNow}`);
|
||||
lines.push('');
|
||||
|
||||
// ── System ──
|
||||
lines.push('## System');
|
||||
lines.push(`- **Protocol:** ${safe(status.protocolVersion, '?')}`);
|
||||
lines.push(`- **Peer ID:** \`${safe(status.peerId, '?')}\``);
|
||||
lines.push(`- **User Agent:** ${userAgent}`);
|
||||
lines.push('');
|
||||
|
||||
// ── Tab ──
|
||||
if (rawVideo) {
|
||||
lines.push('## Tab');
|
||||
if (vs.pageTitle) lines.push(`- **Title:** ${vs.pageTitle}`);
|
||||
if (vs.url) lines.push(`- **URL:** ${vs.url}`);
|
||||
if (vs.platform) lines.push(`- **Platform:** ${safe(vs.platform, '?')}`);
|
||||
lines.push(`- **Video Count:** ${safe(vs.videoCount, 0)} | **Shadow DOM:** ${vs.inShadowDom ? 'YES' : 'NO'}`);
|
||||
lines.push('');
|
||||
}
|
||||
|
||||
// ── Connection ──
|
||||
lines.push('## Connection');
|
||||
const st = safe(status.status, 'disconnected');
|
||||
const emoji = st === 'connected' ? '\uD83D\uDFE2'
|
||||
: st === 'connecting' ? '\uD83D\uDFE1'
|
||||
: st === 'reconnecting' ? '\uD83D\uDFE0'
|
||||
: '\uD83D\uDD34';
|
||||
lines.push(`- **Status:** ${emoji} ${st}`);
|
||||
lines.push(`- **Server:** \`${safe(status.serverUrl, '?')}\``);
|
||||
if (status.roomId) {
|
||||
lines.push(`- **Room:** \`${status.roomId}\``);
|
||||
const peers = Array.isArray(status.peers) ? status.peers : [];
|
||||
const peerNames = peers.map(p => (p && (p.username || p.peerId)) || '?').join(', ');
|
||||
lines.push(`- **Peers (${peers.length}):** ${peers.length > 0 ? peerNames : 'none'}`);
|
||||
} else {
|
||||
lines.push('- **Room:** none');
|
||||
}
|
||||
if (safe(status.reconnectAttempts, 0) > 0) lines.push(`- **Reconnect Attempts:** ${status.reconnectAttempts}`);
|
||||
lines.push('');
|
||||
|
||||
// ── Video ──
|
||||
lines.push('## Video');
|
||||
if (!rawVideo) {
|
||||
lines.push('- *No tab selected / communication failed*');
|
||||
} else if (!vs.found) {
|
||||
lines.push('- **Found:** \u274C NO VIDEO ELEMENT');
|
||||
if (vs.videoCount != null) lines.push(`- **Video Tags:** ${vs.videoCount}`);
|
||||
if (vs.inShadowDom != null) lines.push(`- **Shadow DOM:** ${vs.inShadowDom ? 'YES (checked)' : 'NO'}`);
|
||||
if (vs.metadata) {
|
||||
if (vs.metadata.title) lines.push(`- **MediaSession Title:** "${vs.metadata.title}"`);
|
||||
if (vs.metadata.artist) lines.push(`- **MediaSession Artist:** "${vs.metadata.artist}"`);
|
||||
if (vs.metadata.album) lines.push(`- **MediaSession Album:** "${vs.metadata.album}"`);
|
||||
}
|
||||
} else {
|
||||
const timeStr = (typeof vs.currentTime === 'number' ? vs.currentTime.toFixed(2) : '?') + 's / ' +
|
||||
(typeof vs.duration === 'number' ? vs.duration.toFixed(2) : '?') + 's';
|
||||
const readyLabel = safe(vs.readyStateLabel, '?');
|
||||
const readyOk = safe(vs.readyState, -1) >= 3;
|
||||
const netLabel = safe(vs.networkStateLabel, '?');
|
||||
const dimOk = safe(vs.videoWidth, 0) > 0 && safe(vs.videoHeight, 0) > 0;
|
||||
|
||||
lines.push(`- **State:** ${vs.paused ? 'PAUSED' : 'PLAYING'}`);
|
||||
lines.push(`- **Time:** ${timeStr}`);
|
||||
lines.push(`- **ReadyState:** ${readyOk ? '\u2705' : '\u26A0\uFE0F'} ${safe(vs.readyState, '?')} (${readyLabel})`);
|
||||
lines.push(`- **Network:** ${safe(vs.networkState, '?')} (${netLabel})`);
|
||||
lines.push(`- **Buffered:** ${safe(vs.buffered, '?')}`);
|
||||
lines.push(`- **Dimensions:** ${safe(vs.videoWidth, '?')}x${safe(vs.videoHeight, '?')}${dimOk ? '' : ' \u26A0\uFE0F 0x0'}`);
|
||||
lines.push(`- **Muted:** ${safe(vs.muted, '?')} | **Volume:** ${safe(vs.volume, '?')} | **Speed:** ${safe(vs.playbackRate, '?')}x`);
|
||||
lines.push(`- **Seeking:** ${safe(vs.seeking, '?')} | **Ended:** ${safe(vs.ended, '?')} | **Loop:** ${safe(vs.loop, '?')}`);
|
||||
if (vs.error && vs.error.code != null) {
|
||||
lines.push(`- **Error:** code=${vs.error.code}, msg="${safe(vs.error.message, 'none')}"`);
|
||||
}
|
||||
lines.push(`- **ID:** \`${safe(vs.id, '')}\` | **Class:** \`${safe(vs.className, '')}\``);
|
||||
lines.push(`- **Source:** \`${safe(vs.currentSrc, safe(vs.src, 'none'))}\``);
|
||||
if (vs.metadata) {
|
||||
if (vs.metadata.title) lines.push(`- **MediaSession Title:** "${vs.metadata.title}"`);
|
||||
if (vs.metadata.artist) lines.push(`- **MediaSession Artist:** "${vs.metadata.artist}"`);
|
||||
if (vs.metadata.album) lines.push(`- **MediaSession Album:** "${vs.metadata.album}"`);
|
||||
}
|
||||
if (vs.dataAttributes) {
|
||||
const keys = Object.keys(vs.dataAttributes);
|
||||
if (keys.length > 0) {
|
||||
lines.push('- **Data Attributes:**');
|
||||
for (const k of keys) lines.push(` - \`${k}\` = "${safe(vs.dataAttributes[k], '')}"`);
|
||||
}
|
||||
}
|
||||
}
|
||||
lines.push('');
|
||||
|
||||
// ── Action History (last 20) ──
|
||||
lines.push('## Action History (last 20)');
|
||||
if (history && history.length > 0) {
|
||||
const recent = history.slice(-20);
|
||||
lines.push('```');
|
||||
for (const h of recent) {
|
||||
if (!h) continue;
|
||||
const ts = safe(h.timestamp, '');
|
||||
const evt = safe(h.event, safe(h.type, '?'));
|
||||
const from = h.senderId ? ` (${h.senderId})` : (h.peerId ? ` (${h.peerId})` : '');
|
||||
const extra = h.detail ? ` \u2192 ${h.detail}` : '';
|
||||
lines.push(`[${ts}] ${evt}${from}${extra}`);
|
||||
}
|
||||
lines.push('```');
|
||||
} else {
|
||||
lines.push('*No history entries*');
|
||||
}
|
||||
lines.push('');
|
||||
|
||||
// ── Logs (last 50) ──
|
||||
lines.push('## Logs (last 50)');
|
||||
if (logs && logs.length > 0) {
|
||||
const recent = logs.slice(-50);
|
||||
lines.push('```');
|
||||
for (const l of recent) {
|
||||
if (!l) continue;
|
||||
lines.push(`[${safe(l.timestamp, '')}] [${safe(l.type, '?')}] ${safe(l.message, '')}`);
|
||||
}
|
||||
lines.push('```');
|
||||
} else {
|
||||
lines.push('*No log entries*');
|
||||
}
|
||||
|
||||
const text = lines.join('\n');
|
||||
navigator.clipboard.writeText(text).then(() => {
|
||||
const original = elements.copyLogs.textContent;
|
||||
elements.copyLogs.textContent = getMessage('TOAST_LOGS_COPIED');
|
||||
setTimeout(() => elements.copyLogs.textContent = original, 2000);
|
||||
setTimeout(() => { elements.copyLogs.textContent = original; }, 2000);
|
||||
}).catch(() => {
|
||||
showToast(getMessage('TOAST_COPY_FAILED'), 'error');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function refreshDebugInfo() {
|
||||
@@ -1445,27 +1591,27 @@ function refreshDebugInfo() {
|
||||
|
||||
// Request direct state from the content script via background
|
||||
chrome.runtime.sendMessage({ type: 'GET_VIDEO_STATE', tabId: res.targetTabId }, (state) => {
|
||||
if (!state || state.error) {
|
||||
if (!state || (!state.found && state.error)) {
|
||||
if (elements.videoDebug) elements.videoDebug.textContent = getMessage('DEBUG_COMM_FAIL');
|
||||
return;
|
||||
}
|
||||
|
||||
if (elements.videoDebug) {
|
||||
elements.videoDebug.innerHTML = '';
|
||||
|
||||
|
||||
const addField = (label, value, color = null) => {
|
||||
const row = document.createElement('div');
|
||||
row.style.marginBottom = '4px';
|
||||
if (color) row.style.color = color;
|
||||
|
||||
|
||||
const b = document.createElement('b');
|
||||
b.textContent = `${label}: `;
|
||||
b.style.color = 'var(--text-muted)';
|
||||
|
||||
|
||||
const span = document.createElement('span');
|
||||
span.textContent = value;
|
||||
span.style.wordBreak = 'break-all';
|
||||
|
||||
|
||||
row.appendChild(b);
|
||||
row.appendChild(span);
|
||||
elements.videoDebug.appendChild(row);
|
||||
@@ -1473,29 +1619,92 @@ function refreshDebugInfo() {
|
||||
|
||||
const addSection = (title) => {
|
||||
const div = document.createElement('div');
|
||||
div.style.cssText = 'margin: 8px 0 4px 0; border-bottom: 1px solid #334155; padding-bottom: 2px; color: var(--accent); font-weight: bold; font-size: 9px;';
|
||||
div.textContent = title.toUpperCase();
|
||||
div.style.cssText = 'margin: 8px 0 4px 0; border-bottom: 1px solid #334155; padding-bottom: 2px; color: var(--accent); font-weight: bold; font-size: 10px; text-transform: uppercase;';
|
||||
div.textContent = title;
|
||||
elements.videoDebug.appendChild(div);
|
||||
};
|
||||
|
||||
addField('STATE', state.paused ? 'PAUSED' : 'PLAYING', 'var(--accent)');
|
||||
addField('TIME', `${state.currentTime.toFixed(2)}s / ${(state.duration || 0).toFixed(2)}s`);
|
||||
addField('READY', state.readyState);
|
||||
|
||||
if (!state.found) {
|
||||
// No video found — show diagnostic info
|
||||
addSection('Video Detection');
|
||||
const notFound = document.createElement('div');
|
||||
notFound.style.cssText = 'color: #ef4444; font-weight: 700; margin-bottom: 8px;';
|
||||
notFound.textContent = 'NO VIDEO ELEMENT FOUND';
|
||||
elements.videoDebug.appendChild(notFound);
|
||||
|
||||
addField('Platform', state.platform || '?', 'var(--accent)');
|
||||
addField('Page Title', state.pageTitle || '?');
|
||||
addField('URL', state.url || '?');
|
||||
addField('Video Tags', String(state.videoCount || 0));
|
||||
addField('Shadow DOM', state.inShadowDom ? 'YES (checked)' : 'NO');
|
||||
|
||||
if (state.metadata) {
|
||||
addSection('Media Session API');
|
||||
addField('Title', state.metadata.title || 'n/a');
|
||||
addField('Artist', state.metadata.artist || 'n/a');
|
||||
addField('Album', state.metadata.album || 'n/a');
|
||||
}
|
||||
|
||||
const hint = document.createElement('div');
|
||||
hint.style.cssText = 'margin-top: 12px; padding: 8px; background: rgba(251,191,36,0.1); border-left: 3px solid #fbbf24; border-radius: 4px; font-size: 10px; color: var(--text-muted);';
|
||||
hint.textContent = 'Is a video currently playing? The extension only detects <video> elements. Ensure media is actively loaded on the page.';
|
||||
elements.videoDebug.appendChild(hint);
|
||||
return;
|
||||
}
|
||||
|
||||
// Video found — full debug
|
||||
addSection('Playback');
|
||||
addField('State', state.paused ? 'PAUSED' : 'PLAYING', state.paused ? 'var(--text-muted)' : '#22c55e');
|
||||
addField('Time', `${state.currentTime.toFixed(2)}s / ${(state.duration || 0).toFixed(2)}s`);
|
||||
addField('ReadyState', `${state.readyState} (${state.readyStateLabel || '?'})`,
|
||||
state.readyState >= 3 ? '#22c55e' : '#fbbf24');
|
||||
addField('Network', `${state.networkState} (${state.networkStateLabel || '?'})`,
|
||||
state.networkState === 1 ? '#22c55e' : state.networkState === 3 ? '#ef4444' : 'var(--text-muted)');
|
||||
addField('Buffered', state.buffered || '?');
|
||||
|
||||
addSection('Properties');
|
||||
addField('Seeking', String(state.seeking));
|
||||
addField('Ended', String(state.ended));
|
||||
addField('Loop', String(state.loop));
|
||||
addField('Muted', String(state.muted));
|
||||
addField('Volume', String(state.volume));
|
||||
addField('Speed', `${state.playbackRate}x`);
|
||||
|
||||
addSection('Dimensions');
|
||||
const dimsOk = state.videoWidth > 0 && state.videoHeight > 0;
|
||||
addField('Resolution', `${state.videoWidth}x${state.videoHeight}`, dimsOk ? '#22c55e' : '#ef4444');
|
||||
if (!dimsOk) {
|
||||
const dimHint = document.createElement('div');
|
||||
dimHint.style.cssText = 'color: #fbbf24; font-size: 9px; margin: 2px 0 6px 12px;';
|
||||
dimHint.textContent = '0x0 = video element hidden or not yet loaded';
|
||||
elements.videoDebug.appendChild(dimHint);
|
||||
}
|
||||
|
||||
if (state.error) {
|
||||
addSection('Media Error');
|
||||
addField('Code', String(state.error.code), '#ef4444');
|
||||
addField('Message', state.error.message || '?', '#ef4444');
|
||||
}
|
||||
|
||||
addSection('Detection');
|
||||
addField('Platform', state.platform || '?', 'var(--accent)');
|
||||
addField('Video Count', String(state.videoCount || 0));
|
||||
addField('Shadow DOM', state.inShadowDom ? 'YES' : 'NO');
|
||||
|
||||
addSection('Identification');
|
||||
addField('URL', state.url);
|
||||
addField('ID', state.id);
|
||||
addField('CLASS', state.className);
|
||||
|
||||
|
||||
addSection('Media Source');
|
||||
addField('CURRENT_SRC', state.currentSrc);
|
||||
addField('SRC', state.src);
|
||||
addField('CurrentSrc', state.currentSrc);
|
||||
addField('Src', state.src);
|
||||
|
||||
if (state.metadata) {
|
||||
addSection('Media Session API');
|
||||
addField('TITLE', state.metadata.title || 'n/a');
|
||||
addField('ARTIST', state.metadata.artist || 'n/a');
|
||||
addField('ALBUM', state.metadata.album || 'n/a');
|
||||
addField('Title', state.metadata.title || 'n/a');
|
||||
addField('Artist', state.metadata.artist || 'n/a');
|
||||
addField('Album', state.metadata.album || 'n/a');
|
||||
}
|
||||
|
||||
if (state.dataAttributes && Object.keys(state.dataAttributes).length > 0) {
|
||||
|
||||
@@ -94,7 +94,6 @@ export const BLACKLIST_DOMAINS = [
|
||||
'imgur.com',
|
||||
|
||||
// E-Commerce
|
||||
'amazon.',
|
||||
'ebay.com',
|
||||
'aliexpress.com',
|
||||
'etsy.com',
|
||||
|
||||
Reference in New Issue
Block a user