# `Laev.Playback`
[🔗](https://github.com/alexdont/laev/blob/main/lib/laev/playback.ex#L1)

Where a stream is played, and how its progress gets back.

laev grew up launching a player on the machine it runs on, which is the only
thing a terminal can do. A web interface can do that too — the server and the
browser are the same computer, for somebody running this at home — but the
obvious thing for it to do is play the film in the page, and that is what it
does unless told otherwise.

So there are three targets and one setting between them:

  * `browser` — the stream goes to a `<video>` in the page. The default
    anywhere but the terminal.
  * `mpv` / `vlc` — the player on the machine, launched as the CLI launches
    it, for anyone who would rather watch in their own player.

What the browser gets back is a *source*, not a file. Most releases are h265
in Matroska, which no browser opens, so they are remuxed into HLS on the way
through — `Laev.Remux` stream-copies them with ffmpeg, which is lossless and
near-instant for anything already h264, and segments land on disk fast enough
that seeking works almost at once. Where a release is something a browser can
read already, the direct link is handed over instead, because remuxing what
does not need it costs time for nothing.

Real-Debrid's own transcoder is deliberately not used: it still answers, but
the URLs it returns have no host in them and its media-info endpoint replies
503, so what it offers cannot be played. Remuxing locally is both working and
ours.

Progress comes back through `progress/3`, which works out what was watched
and what was skipped the same way the mpv script and the VLC poller do —
except from the file rather than from memory, since a browser posting every
few seconds is a different request each time and remembers nothing.

# `available`

The players this user could choose between.

The browser is offered to a web interface and withheld from the terminal,
which has no page to play anything in; a local player is offered only where
it is actually installed, since a list of things that will not work is worse
than a short list.

# `browser_source`

What the page needs to play this stream.

`{:ok, %{kind: :hls | :direct, url:, resume_at:, …}}`. HLS where the release
is not something a browser can read, which is most of them.

# `elapsed`

How long this user has been watching the thing in front of them.

# `playlist`

Which playlist to serve for a remux, asked when serving rather than when
starting.

ffmpeg decides between a single playlist and a master with one variant per
audio track, and it decides it after it has looked at the file — so a name
captured at the moment the remux was started is a guess, and was the wrong
one for every release with more than one audio track.

# `progress`

Record where the browser's player has got to.

Stateless on purpose: a browser posts a position every few seconds and each
post is its own request, so what was watched since last time is worked out
from what is already written down rather than from anything held in memory.
The rules are the ones every other player here follows — a position under
half a minute is the beginning, past 85% is watched, and a jump is skipped
rather than watched.

# `ready`

Wait until there is enough on disk to start playing — `:fast` for the first
few segments, `:complete` for the whole thing.

# `start`

Start playing, wherever this user's playback goes.

For a local player this launches it and says so. For the browser it returns
what the page needs to play the thing: a URL, how it should be read, where to
start, and any subtitle laev fetched.

# `subtitles`

Any subtitle tracks pulled out of the release, as WebVTT next to the
segments — a browser needs them as separate files, which is the one thing a
`<video>` cannot take from the container itself.

# `target`

Where playback should go for whoever is asking: `:browser`, `:mpv` or `:vlc`.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
