localstream

package
v0.97.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 16, 2026 License: MIT Imports: 4 Imported by: 0

Documentation

Overview

Package localstream wraps the io.ReadSeeker of a local mounted file so the server can do two things at once while a video plays:

  • measure how fast bytes are actually pulled from the underlying mount (a throughput indicator for the UI — crucial on rclone/Google-Drive mounts where a play silently fetches over the network), and
  • read ahead in large, aligned blocks so ffmpeg/ServeContent don't issue many tiny Range reads that an rclone FUSE mount penalises.

A single Session covers both the direct-play path (it is an io.ReadSeeker, fed to http.ServeContent) and the HLS path (the transcode package wraps it in its own atomic readSeekerContent). The Registry keys sessions so the transfer-status endpoint can find the live throughput for a given file.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type ReadSeekCloser

type ReadSeekCloser interface {
	io.ReadSeeker
	io.Closer
}

ReadSeekCloser is the underlying file handle a Session owns (an *os.File satisfies it). The Session closes it when the Registry reaps the session.

type Registry

type Registry struct {
	// contains filtered or unexported fields
}

Registry tracks live read sessions keyed by a caller-chosen string (built from mount/path/user). It hands out Sessions, refcounts the direct-play path, and reaps idle sessions so a closed player doesn't leak file handles.

func NewRegistry

func NewRegistry(readaheadMB int) *Registry

NewRegistry builds a Registry whose sessions read ahead readaheadMB at a time (0 → 16 MiB default) and starts the idle reaper.

func (*Registry) Close

func (r *Registry) Close()

Close stops the reaper and closes every live session.

func (*Registry) Get

func (r *Registry) Get(key string) (*Session, bool)

Get returns the live session for key, if any.

func (*Registry) OpenShared

func (r *Registry) OpenShared(key string, f ReadSeekCloser, size int64) *Session

OpenShared returns the Session for key, creating it from f if absent and reusing (and closing the duplicate f) if present. Use this for sessions that outlive a single request (the HLS transcode source, deduped by key): the caller does NOT Release — the reaper closes it when ffmpeg stops pulling.

func (*Registry) OpenSolo

func (r *Registry) OpenSolo(key string, f ReadSeekCloser, size int64) *Session

OpenSolo returns a fresh Session bound to its own f and registers it under key for status lookup. Use this for the direct-play path, where concurrent http.ServeContent requests must NOT share a single cursor. Pair every OpenSolo with a deferred Release(key, sess).

func (*Registry) Release

func (r *Registry) Release(key string, sess *Session)

Release drops one reference from a solo session and closes it when the last one goes. It only unmaps key if the slot still points at sess, so it never evicts a newer session that replaced this one.

type Session

type Session struct {
	// contains filtered or unexported fields
}

Session wraps one open file. Read/Seek are mutex-guarded so it is safe to share across the (already serialised) HLS reader and concurrent status reads. Only bytes fetched from the underlying file are metered — buffer replays are not, so the rate reflects real mount I/O, not cache hits.

func (*Session) Read

func (s *Session) Read(p []byte) (int, error)

Read serves from the read-ahead buffer when possible; otherwise it refills the buffer with one large aligned read at the current position. Implements io.Reader.

func (*Session) Seek

func (s *Session) Seek(offset int64, whence int) (int64, error)

Seek moves the logical cursor. A seek that lands outside the current read-ahead buffer invalidates it (the next Read refills). SeekEnd is used by http.ServeContent for sizing and never meters or refills. Implements io.Seeker.

func (*Session) Snapshot

func (s *Session) Snapshot() Snapshot

Snapshot returns the current throughput view. Safe to call concurrently.

type Snapshot

type Snapshot struct {
	Key        string `json:"key"`
	BytesRead  int64  `json:"bytesRead"`
	RatePerSec int64  `json:"ratePerSec"`
	Size       int64  `json:"size"`
	Active     bool   `json:"active"`
	Stalled    bool   `json:"stalled"`
}

Snapshot is the immutable view the transfer-status endpoint serialises.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL