Skip to content

Three small fixes: EventEmitter listeners, the MP4 append queue, Panopto's label - #578

Merged
Andrews54757 merged 3 commits into
Andrews54757:mainfrom
Nawid3333:upstream-pr/player-fixes
Oct 9, 2026
Merged

Andrews54757 merged 3 commits into
Andrews54757:mainfrom
Nawid3333:upstream-pr/player-fixes

Conversation

@Nawid3333

Copy link
Copy Markdown
Contributor

Three independent fixes, one commit each. Each commit applies to main on its own.

1. EventEmitter: an event reaches every listener when one removes itself

emit() walks the listener list with every() while listeners change it. The emitter also walks its context list that way. A once() listener removes itself at the index the walk is on, so the next listener moves into that index and is skipped. A handler that destroys its own context does the same to the next context. VideoAnalyzer, AudioAnalyzer and PreviewFrameExtractor destroy their contexts like this on the player's events.

Both walks now go over a copy, as Node's EventEmitter does. Two consequences:

  • A listener added during an emit() is not called for that event. That was already true, because every() stops at the length it started with.
  • A listener that another listener removes during an emit() still hears that event. This matches Node.

2. MP4: a SourceBuffer call that throws no longer stops the queue

SourceBufferWrapper runs MP4Player's appends and removals one at a time, each after the previous updateend. There were two problems:

  • A remove() that threw (InvalidStateError once the MediaSource closed, for example) still set updating. No update had started, so no updateend came, and every later append and removal stayed queued for good.
  • An appendBuffer() that threw (QuotaExceededError) left the operation at the head of the queue. Every later call ran it again and was rejected with its error.

A throw from either call now rejects that operation, drops it and runs the next one. Nothing changes when nothing throws.

3. Locales: translate "Accelerated Panopto"

player_source_accelpanopto exists only in en. The other 15 locales show the English label in the Sources browser's mode menu, the build prints "Missing keys" for each of them, and combined-locales.json lacks the key. Each locale's label now follows its own "Accelerated Vimeo", with the name swapped. This commit also fixes a typo in the German Vimeo label ("Beschleunigtest" → "Beschleunigtes").

Verification

Commits 1 and 2: the real modules from main and from this branch, run in Node. SourceBufferWrapper gets a stand-in SourceBuffer that throws on request.

Case main this branch
two once() listeners on one event only the first is called both are called
a context destroys itself in its handler, another context after it the second is skipped both are called
remove() throws, then an append is queued the append never settles the append resolves
append A throws, then B and C are queued A, B and C are rejected; A is run 3 times A is rejected; B and C resolve

🤖 Generated with Claude Code

Nawid3333 and others added 3 commits October 7, 2026 16:21
emit() walked the listener list, and the emitter its context list, while
they changed under it. A once() listener, or a handler that destroys its own
context (VideoAnalyzer, AudioAnalyzer and PreviewFrameExtractor do on the
player's events), removed itself at the place the walk was on; the next one
moved into that place and never heard that event. Both walks now go over a
copy, as Node's EventEmitter does.

Tested with vitest against this module: two once() listeners on one event,
and a context that destroys itself in its handler; before the change the
second listener and the second context were skipped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
player_source_accelpanopto was only in en, so the other 15 locales showed
the English label in the Sources browser's mode menu, every build printed
"Missing keys" for each of them, and combined-locales.json did not have the
key. Each locale's label follows its own "Accelerated Vimeo" with the name
swapped. Also fixes the German Vimeo label's typo ("Beschleunigtest" ->
"Beschleunigtes"). `node localescript.mjs` reports no missing keys now.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SourceBufferWrapper runs MP4Player's appends and removals one at a time,
each after the previous updateend. A remove() that threw (InvalidStateError
once the MediaSource closed, say) still set `updating`: no update had
started, so no updateend came, and every later append and removal stayed
queued for good. An appendBuffer() that threw (QuotaExceededError) left the
operation at the head of the queue, so every later call ran it again and
was rejected with its error.

A throw from either call now rejects that operation, drops it and runs the
next one. Nothing changes when nothing throws.

Tested with vitest against a stand-in SourceBuffer: a remove that throws, an
append that throws, and operations queued behind two throws in a row; all
three failed before. Accelerated MP4 playback and a seek far ahead play the
same as before in Firefox 157.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

2 participants