Skip to content

Keep the encoder's availability true, and stop printing raw floats at the editor - #1985

Merged
johan-bell merged 5 commits into
1878-api-cms-hls-media-data-modelfrom
1878-encoder-availability-poll
Sep 4, 2026
Merged

Keep the encoder's availability true, and stop printing raw floats at the editor#1985
johan-bell merged 5 commits into
1878-api-cms-hls-media-data-modelfrom
1878-encoder-availability-poll

Conversation

@johan-bell

@johan-bell johan-bell commented Sep 2, 2026

Copy link
Copy Markdown
Collaborator

The notice told an editor to open Luminary Media Convert and try again, and it meant it — nothing on the page would ever find out on its own.

Luminary Media Convert is not running. Open it, then try again.

Why it never updated

Following the link scheduled a single re-check:

const recheckAfterLaunch = () => setTimeout(() => emit("recheck"), 2000);

Two seconds is shorter than the app takes to start — it boots Nest and probes the machine's encoders first — so the usual outcome was one failed check and a notice still claiming the app was not running when it was. And an editor who opened the app from the Dock, or who already had it starting, re-checked nothing at all.

What it does now

useMediaEncoder polls while the encoder is missing and stops the moment it answers.

  • Only while the tab is visible. A background tab is not somebody waiting for a window to appear, and this is a request per interval to a port that may have nothing listening on it.
  • Immediately on becoming visible again. Coming back to the tab is the likeliest moment for the app to have been started in the meantime.
  • Stops on unmount, and removes its listener.

The single delayed re-check goes, and EncodeStatus loses its only emit — one mechanism rather than two doing the same thing badly.

Both notices say so rather than asking for another attempt:

Luminary Media Convert is not running. Open it — this updates on its own once it is.

Both directions

Watching one way was not enough: an editor who quits the encoder was left with an Encode button that still looked usable on an app that was gone. Clicking it did report the truth — start() re-checks before opening a session — but only after they had clicked something that looked ready.

The first attempt at this checked on visibilitychange, reasoning that quitting the encoder means leaving the browser and coming back. That is wrong about the event. visibilitychange fires when a tab is hidden — switched away from, or the window minimised — and not when another application takes focus. Quitting the encoder from its own menu, with the CMS tab still the visible one, fired nothing at all: exactly the case it was added for. Reported, and fixed properly.

Polling now runs the whole time the section is mounted rather than only while the encoder is missing, which makes the answer true in both directions without depending on what the browser counts as a visibility change:

  • 3s while it is missing, 10s once it has answered — waiting versus confirming. Either way a loopback request that fails immediately when nothing is listening on the port.
  • A hidden tab is skipped. Nobody there is waiting for an answer.
  • focus on the window beside visibilitychange, because that one does fire on an application switch — so coming back from the encoder asks at once rather than at the next interval.

The percentage, while in here

The encoder reports progress as a raw float, so the bar read "Encoding 1.7666666666666668%". Rounded to one decimal, through Number() so a whole percentage still reads as one rather than "23.0".

The preview's waiting panel loses its second line entirely. "Encoding is at 23.6%. Checking again automatically." put the same number a second time, unrounded, across the middle of a frame the editor is trying to judge — and progress belongs in the Media section, where there is a bar for it. What is left is what the panel is for: "Nothing at this URL yet."

The two props that fed it, and their drilling down from EditContentMedia through EditContentVideo, go with it.

Tests

Six on the watcher: that it keeps looking until the encoder answers, that it notices the encoder going away with nobody touching the browser, that it comes back on its own when the encoder is reopened, that it asks sooner while missing than once answered, that a hidden tab is left alone, and that a window focus asks immediately. The obsolete "re-checks shortly after the launch link is followed" is replaced by one asserting the copy no longer asks for a retry.

One note on the tests: they stub the user agent as Chromium, because jsdom's is not and every failure would otherwise land on browser-unsupported. The polling is the same either way; only the label differs.

Verified: 129 files, 1169 passed, vue-tsc and eslint clean.

… to retry

The notice told an editor to open Luminary Media Convert and try again, and
meant it: nothing on the page would ever find out on its own. The launch
link scheduled a single re-check two seconds later, which is shorter than
the app takes to boot Nest and probe the machine's encoders — so the usual
outcome was a notice that still said the app was not running when it was.
Opening it from the Dock instead of the link re-checked nothing at all.

The section now polls while the encoder is missing and stops the moment it
answers. Only while the tab is visible: a background tab is not somebody
waiting for a window to appear, and this is a request per interval to a
port that may have nothing on it. Coming back to the tab checks
immediately, which is the likeliest moment for the app to have been started
in the meantime.

The single delayed re-check goes with it, and EncodeStatus loses its one
emit — one mechanism rather than two doing the same thing badly.

Both notices say so instead of asking for another attempt: "Open it — this
updates on its own once it is."
The watch was one-directional. Polling stopped the moment the encoder
answered and the visibility check bailed out while it was available, so an
editor who quit the encoder was left with an Encode button that still
looked usable on an app that was no longer there. Clicking it did report
the truth — start() re-checks before opening a session — but only after
they had clicked something that looked ready.

Returning to the tab now checks whichever way the answer goes. Polling
still only runs while the encoder is missing; a request every few seconds
for the life of an open document is not worth keeping a button greyed out,
and quitting the encoder means leaving the browser and coming back, which
is the moment that catches it for nothing.

A check that finds it gone starts the watch again, so the notice recovers
on its own when the app is reopened.
…ot a tab change

Reported: closing the encoder still left the CMS thinking it was there.

The previous commit claimed the reverse direction was covered by checking
on `visibilitychange`, reasoning that quitting the encoder means leaving
the browser and coming back. That is wrong about the event.
`visibilitychange` fires when a *tab* is hidden — switched away from, or
the window minimised — and not when another application takes focus. So
quitting the encoder from its own menu, with the CMS tab still the visible
one, fired nothing at all: exactly the case it was added for.

Polling now runs the whole time the section is mounted rather than only
while the encoder is missing, which makes the answer true in both
directions without depending on what the browser considers a visibility
change. Slower once it has answered — ten seconds against three — because
then it is confirming rather than waiting, and either way it is a loopback
request that fails immediately when nothing is listening on the port. A
hidden tab is still skipped; nobody there is waiting for an answer.

`focus` on the window is added beside `visibilitychange`. That one does
fire on an application switch, so coming back from the encoder asks at once
rather than at the next interval.
The encoder reports progress as a raw float, so the bar was labelled
"Encoding 1.7666666666666668%" — arithmetic rather than progress. Rounded
to one decimal, through Number() so a whole percentage still reads as one
rather than "23.0".

The preview's waiting panel loses its second line entirely. "Encoding is at
23.6%. Checking again automatically." put the same number a second time,
unrounded, across the middle of a frame the editor is trying to judge —
and progress belongs in the Media section, where there is a bar for it.
What is left is the thing the panel is for: "Nothing at this URL yet."

The two props that fed it, and their drilling down from EditContentMedia
through EditContentVideo, go with it.
@johan-bell johan-bell changed the title Notice when the encoder appears, without asking the editor to retry Keep the encoder's availability true, and stop printing raw floats at the editor Sep 2, 2026
Two files conflicted, both because the same sentences changed for
different reasons.

EncodeStatus: this branch dropped "then try again" — polling makes it
untrue, the notice clears itself — while the epic added the download link
for editors who never installed the app (#2002). Both are right, so both
survive: "Open it — this updates on its own once it is — or download it
if it is not installed yet."

EditContentMedia: each side added one field to the same destructure,
`outdated` from the epic and `watchForEncoder` from here. Kept both.

Also fixed while merging: the watcher was started only when the encoder
was missing at mount, which contradicts the comment on it — polling is
meant to run for as long as the section is on screen, so that quitting
the encoder is noticed too, not only starting it. It now does. The test
mock predates both features and gained the two members it was missing.

cms: 1187 tests pass, vue-tsc clean.
@johan-bell

Copy link
Copy Markdown
Collaborator Author

Rebased onto the epic; MERGEABLE / CLEAN again.

Two files conflicted, both because the same sentences had changed for different reasons.

EncodeStatus.vue — this branch removed "then try again" (polling makes it untrue: the notice clears itself), while the epic added the download link for editors who never installed the app (#2002). Both are right, so both survive:

Luminary Media Convert is not running. Open it — this updates on its own once it is — or download it if it is not installed yet.

EditContentMedia.vue — each side added one field to the same destructure: outdated from the epic, watchForEncoder from here. Kept both.

Fixed while merging

The watcher was started only when the encoder was missing at mount:

if (!(await refreshAvailability())) watchForEncoder();

which contradicts the comment on it — polling is meant to run for as long as the section is on screen, so that quitting the encoder is noticed too, not only starting it. With that wiring, an editor who had the encoder open and then closed it would keep a notice claiming it was still there until the window regained focus. Now started unconditionally.

The test mock predated both features and was missing watchForEncoder and outdated; calling the first unconditionally threw before resume ran, which is what surfaced it.

cms: 1187 tests pass, vue-tsc 0 errors.

@johan-bell
johan-bell merged commit 5979002 into 1878-api-cms-hls-media-data-model Sep 4, 2026
@johan-bell
johan-bell deleted the 1878-encoder-availability-poll branch September 4, 2026 06:58
johan-bell added a commit that referenced this pull request Sep 4, 2026
One file conflicted, and both hunks were the same shape: each side had
removed something the other still carried.

The help text — this branch drops "and audio" with the feature; the epic
added the download link for editors who never installed the app (#2002).
Both removals and both additions stand.

The video block — #1985 moved the encode status out of EditContentVideo,
so its props go; MediaAudioList goes with the audio feature, and its
component file is deleted on this branch, so keeping the tag would have
broken the build.

cms: 1179 tests, api: 981, both type-checks clean.
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