Skip to content

0.3.0: Movies & Shows tab (films and series, with on-the-fly audio conversion) - #23

Merged
ralyodio merged 1 commit into
mainfrom
feat/movies-and-shows
Oct 14, 2026
Merged

ralyodio merged 1 commit into
mainfrom
feat/movies-and-shows

Conversation

@ralyodio

Copy link
Copy Markdown
Contributor

Adds a Movies & Shows tab next to Live TV.

Why

Anthony's line had 53,531 live channels, 213,348 movies and 46,051 series (1.19M episodes). hdtilt only showed the live channels. Its films are MKV with AC-3 / E-AC-3 audio. The provider returns 500 for .ts or .mp4, so a browser can't play them as served.

What

  • Browse: categories → titles (newest first) → a show's seasons and episodes, via POST /api/vod. Nothing loads the whole catalogue.
  • Search: covers every title. The server builds a compact index once per line (typed arrays, one build at a time, kept for an hour). The first search on this line took ~6 s; later ones ~12 ms.
  • Playback (src/server/remux.js vod mode): ffprobe reads length and codecs (/hls/<b64>/info.json). Video is copied and audio becomes stereo AAC, delivered as fMP4 HLS (HEVC tagged hvc1 for Safari). #EXT-X-START makes a growing playlist start at 0. A seek past what's converted starts a new session there (?vod=1&t=). An .mp4 with ordinary codecs plays directly.
  • Continue watching: resumes where you stopped, next episode auto-plays, ◀/▶ seek 10 s/30 s, n jumps to the next episode.
  • Plain M3U: /movie/ and /series/ entries appear in the same tab.
  • MCP: a movies_shows tool.
  • Fix: a play() rejected by a newer source (AbortError) was treated as an autoplay block. That muted the video after a seek or a fast zap.

Tested

  • bun test: 78 pass with ffmpeg. New test/vod.test.js includes an end-to-end MKV + AC-3 → HLS + AAC conversion.
  • Headless Chrome against the real line, 19/19:
    • 368 categories load; a 1080p MKV movie plays with audio decoded.
    • +30 s and a seek to 1h03m both work; Continue watching keeps the position.
    • Search "matrix" → 94 results.
    • Lost in Space → 28 episodes; an episode plays with audio.
    • Back works; on a phone-width screen the categories open from ☰.

Notes

  • Memory: in a fresh process, building both search indexes peaked at ~800 MB RSS. Each index holds ~50 MB, and builds are serialised. The container limit is 2 GB.
  • HEVC (the 4K titles) is copied, not transcoded, so it plays only where the browser decodes HEVC (Safari, and Chrome/Edge with hardware support).

🤖 Generated with Claude Code

An Xtream line's films and series get their own tab. A big line has
hundreds of thousands of titles (one measured 213,348 films, 108 MB of
JSON), so the screen asks for categories, then one category, then one
show's episodes (POST /api/vod). Search builds a compact server-side
index once per line (typed arrays, one build at a time) and answers in
milliseconds after.

Providers serve films as ripped, mostly MKV with AC-3 / E-AC-3 / DTS
sound, and refuse .ts / .mp4 (500). No browser decodes that audio, so
the remuxer gains a vod mode: ffprobe for length and codecs, the
picture copied, the sound made AAC, fMP4 HLS (HEVC tagged hvc1 for
Safari), #EXT-X-START so a growing playlist starts at 0. Seeking past
what is converted starts a session at that point. A plain .mp4 with
ordinary codecs still plays directly.

Continue watching resumes where you stopped; the next episode follows.
Arrows seek 10 s / 30 s over a film. A plain M3U's /movie/ and /series/
entries show up too. MCP gets movies_shows.

Also: a play() rejected by a newer source (AbortError) no longer counts
as the browser refusing sound, which muted the video after a seek or a
fast zap.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ralyodio
ralyodio merged commit 4a98a3b into main Oct 14, 2026
3 checks passed
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