All posts
ProductStreamingHLS14 Sep 2026 · 3 min read

Five courses, two kinds of work

JPJean Perez

Three days before this, the learn catalog had one course with content behind it. The roadmap page still claimed five, which was the subject of an earlier fix. These are the three days that made the claim true, and they were two different jobs.

DASH and WebRTC were already written, just not in the repo. Ten full lessons for each sat inside the design archive's shell/lessons-dash.js and shell/lessons-webrtc.js, evaluated in a sandbox and mapped into the app's content-block format rather than retyped by hand. A block-count check against the prototype's own data matched 51 out of 51 blocks for each course. Prose is word for word. The two ports ran in parallel with no files in common. The DASH port owned the one shared piece (Lesson.labHref); the WebRTC port touched only its own module directory.

HLS and Media Pipelines didn't exist anywhere, not in the repo and not in the archive. Six modules and twelve lessons for HLS, seven modules and fourteen for Pipelines, written from nothing, using the DASH course as a template for shape and density rather than content.

The LL-HLS lesson says what FFmpeg can't do

Low-latency HLS needed the most care. FFmpeg's own hls muxer never emits EXT-X-PART or EXT-X-PRELOAD-HINT, the pieces that make LL-HLS actually low-latency. It produces the live segment source that a packager or origin server slices into parts; it doesn't slice them itself. Both LL-HLS lessons say that plainly and show only FFmpeg's real half of the pipeline, rather than inventing a command that doesn't exist. Neither claims a hands-on lab step for the part FFmpeg can't do.

A parser bug three new courses finally woke up

parseMarkdown ran its inline-code regex before its fenced-code regex, so any triple-backtick block landed inside a text block and got chopped into inline fragments instead of rendering as a code block. That had been true for months. It just never mattered, because no FFMPEG lesson had ever used a fenced block inside prose. DASH, WebRTC and HLS all did. Fixed by reordering the two regexes and moving the parser into its own dependency-free module, with a test that fails on the old order.

A second bug surfaced in the same pass: bullet text was bolding everything up to the first colon, which is fine for a real lead-in and wrong for a literal string. It was mangling "ftyp / styp" and "availabilityStartTime" in the DASH lessons into bolded fragments that didn't mean anything. Fixed by bolding only an actual **lead** marker.

The FFMPEG course that quietly grew four courses

The sharpest bug came out of the Media Pipelines commit, the last of the five. lib/learn/courses.ts had been building the FFMPEG course's module list from the entire module registry, not an explicit list of its own modules. That was invisible while FFMPEG was the only course in the registry. The moment the DASH port landed, the FFMPEG course silently started claiming DASH's modules as its own, and every course added after that made it worse: by the time WebRTC and HLS shipped, the FFMPEG course's reader navigation, module quiz and JSON-LD were all quietly listing lessons from three courses that weren't FFMPEG. Fixed by exporting an explicit ffmpegModules list instead of the full registry, with a comment on the list itself now explaining why it can never go back to modules.

Final counts

CourseModulesLessons
FFMPEG9101
HLS612
MPEG-DASH110
WebRTC110
Media Pipelines714

24 modules and 147 lessons, so the roadmap could say five. Whether those 147 lessons are right is a separate question. A fact-check against the specs nine days later corrected 98 claims in the FFMPEG course alone.