refactor: audit fixes — clean up bugs, dead code, and type mismatches
CI / test (pull_request) Successful in 23s

- viewer.js: remove undefined state.segmentsBuffered assignment in BUFFER_RESET
- viewer.js: add missing 'switching' and 'ended' phases to PHASE_ICONS
- index.js: remove redundant Strategy B3 (duplicated B1 video-element walk)
- index.js: filter standard <video> DOM attributes from custom-property scans
  (m3u8DeepProbeFn B1 and scrapeFn Priority 3) to avoid iterating built-in
  properties that can never contain HLS data
- background.js: remove unconditional return true (handlers are synchronous)
  and remove dead error-response for unknown message types
- types.ts: align MediaType union ('direct' | 'hls') with actual runtime
  value used in code ('video' | 'hls')
- package.json: remove hls.js from dependencies (loaded from CDN at runtime,
  never imported as an npm module)
This commit is contained in:
robot committed 2026-08-13 04:59:59 +00:00
1 parent ae8608148d
commit 8e9f7f96f1
5 files changed
+36 -31

No files matched your search

-3
View File
@@ -13,9 +13,6 @@
"test:watch": "vitest",
"check": "tsc --noEmit"
},
"dependencies": {
"hls.js": "^1.5.17"
},
"devDependencies": {
"ajv": "^8.17.1",
"ajv-formats": "^3.0.1",
+1 -5
View File
@@ -18,13 +18,9 @@ chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
switch (message.type) {
case 'PING':
sendResponse({ pong: true, timestamp: Date.now() });
break;
return;
default:
console.warn('[goon-plugin] Unknown message type:', message.type);
sendResponse({ error: `Unknown type: ${message.type}` });
break;
}
return true; // keep sendResponse channel open for async work
});
+2 -2
View File
@@ -14,10 +14,10 @@
/**
* Media delivery format of the video source.
* - 'direct' : plain MP4/WebM blob served via HTTP(S)
* - 'video' : plain MP4/WebM blob served via HTTP(S)
* - 'hls' : HTTP Live Streaming manifest (.m3u8)
*/
export type MediaType = 'direct' | 'hls';
export type MediaType = 'video' | 'hls';
/**
* A single video entry discovered by a tab scan.
+31 -20
View File
@@ -171,8 +171,23 @@ function m3u8DeepProbeFn() {
// NOT a reverse-lookup. It returns its input unchanged — Hls.cast(video) just
// returns the <video> element with no .url property. Real players store the
// HLS instance directly on the video element as a custom property.
// Only check non-standard properties (skip built-in <video> attributes).
const STANDARD_VIDEO_ATTRS = new Set([
'src', 'srcObject', 'crossOrigin', 'preload', 'autoplay', 'muted',
'defaultMuted', 'loop', 'controls', 'controlsList', 'disableRemotePlayback',
'volume', 'playbackRate', 'defaultPlaybackRate', 'playbackState',
'currentTime', 'startTime', 'duration', 'buffered', 'seekable',
'played', 'seeking', 'ended', 'readyState', 'networkState',
'videoWidth', 'videoHeight', 'error', 'textTracks', 'videoTracks',
'audioTracks', 'loadingBeganAt', 'label', 'id', 'className',
'classList', 'style', 'attributes', 'dataset', 'innerHTML', 'textContent',
'outerHTML', 'outerText', 'slot', 'accessKey', 'draggable', 'hidden',
'lang', 'spellcheck', 'tabIndex', 'title', 'translate',
'width', 'height', 'poster', 'preload', 'referrerPolicy',
]);
for (const v of document.querySelectorAll('video')) {
for (const key of Object.keys(v)) {
if (STANDARD_VIDEO_ATTRS.has(key)) continue;
const obj = v[key];
if (!obj || typeof obj !== 'object') continue;
// Check for common HLS instance patterns on video elements
@@ -193,23 +208,6 @@ function m3u8DeepProbeFn() {
}
}
// B3: Fallback — walk video element keys looking for HLS-related objects
for (const v of document.querySelectorAll('video')) {
for (const key of Object.keys(v)) {
if (key.toLowerCase().includes('hls') || key === 'player') {
const hlsObj = v[key];
if (hlsObj && typeof hlsObj === 'object') {
for (const subKey of Object.keys(hlsObj)) {
if (typeof hlsObj[subKey] === 'string' && hlsObj[subKey].includes('.m3u8')) {
if (!seen.has(hlsObj[subKey])) { seen.add(hlsObj[subKey]); manifests.push({ url: hlsObj[subKey], player: `video.${key}.${subKey}` }); }
}
}
walk(hlsObj, `video.${key}`, 0);
}
}
}
}
// Strategy C: Inline scripts
for (const script of document.querySelectorAll('script:not([src])')) {
const text = script.textContent || '';
@@ -281,11 +279,24 @@ function scrapeFn(hlsManifests) {
mediaType = 'hls';
src = hlsManifest;
}
// Priority 3: Scan video element properties for HLS instance references.
// Hls.cast() is a TypeScript identity cast in hls.js — NOT a reverse-lookup.
// Real players store the HLS instance on the video element as custom properties.
// Priority 3: Scan video element custom properties for HLS instance references.
// Only check non-standard (custom) properties — skip built-in <video> attrs.
if (!hlsManifest) {
const stdAttrs = new Set([
'src', 'srcObject', 'crossOrigin', 'preload', 'autoplay', 'muted',
'defaultMuted', 'loop', 'controls', 'controlsList', 'disableRemotePlayback',
'volume', 'playbackRate', 'defaultPlaybackRate', 'playbackState',
'currentTime', 'startTime', 'duration', 'buffered', 'seekable',
'played', 'seeking', 'ended', 'readyState', 'networkState',
'videoWidth', 'videoHeight', 'error', 'textTracks', 'videoTracks',
'audioTracks', 'loadingBeganAt', 'label', 'id', 'className',
'classList', 'style', 'attributes', 'dataset', 'innerHTML', 'textContent',
'outerHTML', 'outerText', 'slot', 'accessKey', 'draggable', 'hidden',
'lang', 'spellcheck', 'tabIndex', 'title', 'translate',
'width', 'height', 'poster', 'referrerPolicy',
]);
for (const key of Object.keys(v)) {
if (stdAttrs.has(key)) continue;
const obj = v[key];
if (obj && typeof obj === 'object' && typeof obj.url === 'string') {
const u = obj.url.toLowerCase();
+2 -1
View File
@@ -170,7 +170,9 @@ const PHASE_ICONS = {
loading: '\u2B07', // down arrow
buffering: '\u27F7', // spinning
playing: '\u25B6', // play
switching: '\u21BB', // right arrow corner
error: '\u2717', // X
ended: '\u23F9', // stop
};
/**
@@ -413,7 +415,6 @@ function wireHlsEvents(hls, video, panel) {
state.lastActivity = Date.now();
state.bufferedAhead = 0;
state.segmentsLoaded = 0;
state.segmentsBuffered = 0;
renderPanelState(video, panel);
});