feat: add HLS playback support with hls.js and modular viewer architecture
CI / test (pull_request) Successful in 23s
CI / test (pull_request) Successful in 23s
This PR adds end-to-end HLS stream detection, interception, and playback to the viewer. The following root causes from the QA audit are fixed: - Restore dropped HLS detection strategies (6-strategy per-tab query) - Add deep-probe scanning for config globals and iframe manifests - Add standalone interceptors.js for CSP-compliant runtime injection - Fix viewer.js string entry handling for legacy storage entries - Fix manifest.json web_accessible_resources for interceptors.js - Disable modulePreload polyfill for Chrome MV3 compatibility - Run deep probe and scraper in MAIN world for page global access - Fallback to src as HLS manifest when hlsManifest field is missing The viewer.js codebase has been refactored from a monolithic wirePlaybackTracking() into modular single-responsibility functions: PanelState, EventWiring, BufferMonitor, PanelRenderer, AggregateRenderer, PanelFactory. Additionally: - Remove debug console.log statements from production code - Fix dead code in m3u8DeepProbeFn walk() that prevented config global manifest detection - Add eslint config with 141 rules and .eslintignore - Add 44 new tests (83/83 total passing) covering HLS detection, config validation, edge cases, and integration scenarios - Pin HLS.js CDN version to 1.5.17 (not floating/latest) - Add grid-optimized HLS config with 10s buffer cap - Add cleanup hooks for HLS instance destruction on page unload Changed files (18): - src/viewer/viewer.js (+834) - src/viewer/index.js (+870) - src/viewer/viewer.test.js (+976, new) - src/viewer/index.test.js (+481, new) - src/inject/interceptors.js (+169, new) - src/content/content.js (+139) - __tests__/hls_qa.test.mjs (+1168, new) - manifest.json (+6) - vite.config.ts (+5) - __tests__/run.mjs (+2) - __tests__/config_validation.test.mjs (+102, new) - eslint.config.js (+24, new) - .eslintignore (+1, new) - QA_REPORT_HLS.md (+160, new) - __tests__/data_flow.test.mjs (+/-5) - __tests__/schema.test.mjs (+/-5) - __tests__/viewer_integration.test.mjs (+/-4) - src/background/background.js (-2)
This commit is contained in:
1 parent
7416ae8ff9
commit
542c5786be
18 files changed
+4316
-731
No files matched your search
@@ -62,7 +62,7 @@ function createBrowserEnv({ storageData = {}, url = 'https://example.com/viewer/
|
||||
// 'native' mode sets HLS = undefined so viewer.js takes the Safari native path.
|
||||
const hlsCalls = { instances: [], loaded: false };
|
||||
if (hlsMode === 'mocked') {
|
||||
win.HLS = class MockHLS {
|
||||
win.Hls = class MockHLS {
|
||||
constructor(opts) {
|
||||
hlsCalls.instances.push({ opts, destroyed: false, events: {}, source: null, media: null });
|
||||
}
|
||||
@@ -103,7 +103,7 @@ function createBrowserEnv({ storageData = {}, url = 'https://example.com/viewer/
|
||||
} else if (hlsMode === 'native') {
|
||||
// Simulate Safari — window.HLS exists but isSupported() returns false.
|
||||
// viewer.js then falls through to video.src = manifestUrl (native HLS).
|
||||
win.HLS = class MockHLS_Native {
|
||||
win.Hls = class MockHLS_Native {
|
||||
static isSupported() {
|
||||
return false; // Safari has native HLS, Hls.js is not needed
|
||||
}
|
||||
@@ -178,15 +178,6 @@ describe('Integration: viewer.js with HLS entries', () => {
|
||||
return;
|
||||
}
|
||||
|
||||
// HLS detection in console logs
|
||||
const hlsLogs = consoleLogs.filter(
|
||||
(l) => l.includes('HLS detected') || l.includes('HLS')
|
||||
);
|
||||
if (hlsLogs.length === 0) {
|
||||
reject(new Error(`No HLS detection in logs. Logs: ${consoleLogs.join(' | ')}`));
|
||||
return;
|
||||
}
|
||||
|
||||
// Verify Hls.js was actually instantiated for the HLS entry
|
||||
if (hlsCalls.instances.length < 1) {
|
||||
reject(new Error('Expected at least 1 HLS instance, got 0'));
|
||||
@@ -391,15 +382,6 @@ describe('Integration: viewer.js with HLS entries', () => {
|
||||
return;
|
||||
}
|
||||
|
||||
// HLS detection in logs
|
||||
const hlsLogs = consoleLogs.filter(
|
||||
(l) => l.includes('HLS detected') || l.includes('HLS')
|
||||
);
|
||||
if (hlsLogs.length === 0) {
|
||||
reject(new Error(`No HLS logs. All: ${consoleLogs.join(' | ')}`));
|
||||
return;
|
||||
}
|
||||
|
||||
// Only one HLS instance (for the HLS entry)
|
||||
if (hlsCalls.instances.length !== 1) {
|
||||
reject(new Error(`Expected 1 HLS instance, got ${hlsCalls.instances.length}`));
|
||||
|
||||
Reference in new issue
Block a user