Skip to content

fix(player): encrypted playback throws before the first segment - #237

Merged
johan-bell merged 1 commit into
mainfrom
fix-key-xhr-eventtarget
Sep 4, 2026
Merged

fix(player): encrypted playback throws before the first segment#237
johan-bell merged 1 commit into
mainfrom
fix-key-xhr-eventtarget

Conversation

@johan-bell

Copy link
Copy Markdown
Collaborator

Encrypted video fails with a black frame and this in the console:

Uncaught TypeError: D.addEventListener is not a function   video.es.js:43577

Why

media-segment-request.js collects every request in an activeXhrs array and then attaches a loadend listener to each one:

activeXhrs.push(keyXhr);

activeXhrs.forEach(activeXhr => {
    activeXhr.addEventListener('loadend', handleLoadEnd({ loadendState, abortFn }));
});

installMemoryKeyXhr answers the sentinel key URI with a plain object carrying response, status, aborted and abort. It had no addEventListener, so the forEach threw and took the whole segment load with it — before anything was decrypted.

The comment in the interceptor accounted for the abort list ("mediaSegmentRequest files whatever comes back in its abort list") but not for the listener attached to the same array a few lines later.

The fix

No-op addEventListener/removeEventListener on the response object. Nothing depends on the event arriving: VHS's handleLoadEnd only calls abortFn() when the request reports aborted, and a request answered from memory is never aborted. Those two plus abort() are the only methods VHS calls on these objects — checked against the whole segment path in the pinned build.

Tests

Two added to vhsKeyInterceptor.test.ts, driving what VHS actually does rather than what the interface says: file the returned object in an array, attach a loadend listener to it, then abort it. With the fix reverted the first fails with the same addEventListener is not a function seen in the browser. 95 pass across the package.

Not fixed here

VIDEOJS: ERROR: The \"youtube\" tech is undefined also appears on every player init: techOrder names youtube, and the tech is only imported when a YouTube URL is played. It is noise rather than breakage — video.js skips an unregistered entry, which is what the comment in playerOptions.ts relies on — but it logs at error level every time. Worth a separate decision about whether to build techOrder after the tech registers.

media-segment-request.js files every request in activeXhrs and then
attaches a `loadend` listener to each, so the plain object the key
interceptor handed back threw

  TypeError: xhr.addEventListener is not a function

before a single segment was decrypted — encrypted playback showed a
black frame, in the app and in the CMS preview alike.

No-ops are enough: VHS's handleLoadEnd only reads `aborted`, and a
request answered from memory is never aborted.
@johan-bell
johan-bell merged commit a9cae72 into main Sep 4, 2026
9 checks passed
@johan-bell
johan-bell deleted the fix-key-xhr-eventtarget branch September 4, 2026 13:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant