player

package
v0.5.5 Latest Latest
Warning

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

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

Documentation

Overview

Package player is pixeltui's headless audio engine: it resolves a track to a playable stream and drives mpv (with ffplay/afplay fallbacks) over IPC. It has no dependency on the terminal UI, so any front-end — the TUI, the pocket hardware client, or the server — can reuse it. The TUI wraps these calls in tea.Cmds; pocket consumes Watch()/Media() directly.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func CleanupCovers

func CleanupCovers()

CleanupCovers removes all generated cover PNGs (call on exit).

func CoverFor

func CoverFor(artURL string) string

CoverFor returns a cached pixelated cover PNG for artURL, generating it on first use. Returns "" if unavailable. Safe for concurrent calls.

func EnsureVideoID

func EnsureVideoID(c engine.Candidate) engine.Candidate

EnsureVideoID enriches a candidate with a YouTube Music video id (+ duration and art) if it doesn't already have one. Recommender candidates arrive bare; ytmusic search results already carry these.

func MPVAvailable

func MPVAvailable() bool

MPVAvailable reports whether mpv is installed (gates playback controls).

func Offline

func Offline() bool

Offline reports whether network resolution is currently gated off.

func OutputRegistry

func OutputRegistry() *output.Registry

OutputRegistry returns the active sink registry, creating a default one if needed. Useful for menus that list available sinks.

func Resolve

func Resolve(videoID string) (string, error)

Resolve turns a video id into a direct CDN audio URL (InnerTube first, yt-dlp fallback), caching the result by video id. Convenience wrapper that resolves the yt-dlp path itself.

func SetAudioDevice

func SetAudioDevice(dev string)

SetAudioDevice pins the mpv --audio-device for subsequently started streams. Empty (the default) uses mpv's default output device. It also updates the "mpv-device" sink in the output registry, so capability-driven menus see it.

func SetCache

func SetCache(c Cache)

SetCache installs the resolved-URL disk cache (nil disables caching).

func SetOffline

func SetOffline(b bool)

SetOffline toggles the connectivity gate on the resolver.

func SetOutputRegistry

func SetOutputRegistry(r *output.Registry)

SetOutputRegistry replaces the active sink registry. Callers that want a custom sink list (e.g. a Bluetooth or AirPlay renderer) use this; the registry is still consulted in mpvBaseArgs.

func YtdlpPath

func YtdlpPath() string

YtdlpPath returns the preferred yt-dlp, in priority order:

  1. $PIXELTUI_YTDLP (explicit override)
  2. ~/.pixeltui/ytdlp-venv/bin/yt-dlp (pip install)
  3. ~/.pixeltui/bin/yt-dlp (standalone, doctor --fix)
  4. yt-dlp on PATH

Types

type Cache

type Cache interface {
	GetStreamURL(videoID string) (string, bool)
	PutStreamURL(videoID, url string, expire int64)
}

Cache caches resolved CDN URLs to disk (implemented by store.Cache).

type MediaCmd

type MediaCmd int

MediaCmd is an OS / hardware transport command observed from mpv. On the desktop these come from OS media keys / the Now Playing widget; on pocket hardware the GPIO buttons feed this same channel.

const (
	MediaNext MediaCmd = iota + 1
	MediaPrev
	MediaPlayPause
)

type State

type State struct {
	Pos     float64
	Dur     float64
	Paused  bool
	Vol     int
	EntryID int
	Ended   bool
}

State is a snapshot of playback for headless consumers (pocket, serve) that can't use the TUI's tea-driven poll loop.

type Stream

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

Stream holds one active audio stream.

func Attach

func Attach(socket string, ended <-chan struct{}) *Stream

Attach wraps an already-running mpv IPC socket as a controllable Stream without spawning a process — for front-ends that drive a player they didn't start (and for tests). ended is closed when the underlying player exits; an open channel means "still playing".

func Start

func Start(c engine.Candidate, preloadedURL string) (*Stream, engine.Candidate, error)

Start begins streaming a candidate. We ALWAYS resolve the direct CDN URL ourselves (fast android_vr client) and hand it to the player — mpv's internal ytdl hook is slow/fragile (it hangs on music.youtube URLs), so we never rely on it. Resolution order:

  1. resolve CDN URL (preloaded if available, else InnerTube/yt-dlp)
  2. play it: mpv (IPC controls) → ffplay (opus-capable)
  3. fallback: yt-dlp | ffplay pipe, then afplay proxy (m4a) — for odd cases

Returns the (possibly enriched) candidate so the UI gets duration/art.

func (*Stream) CanControl

func (s *Stream) CanControl() bool

CanControl reports whether IPC control (pause/seek/volume) is available.

func (*Stream) CurrentEntryID

func (s *Stream) CurrentEntryID() int

CurrentEntryID reports the playlist entry id mpv is currently on (0 if unknown) — how a poll loop notices a gapless auto-advance.

func (*Stream) Duration

func (s *Stream) Duration() float64

func (*Stream) Ended

func (s *Stream) Ended() bool

Ended reports whether the underlying player process has exited.

func (*Stream) Gapless

func (s *Stream) Gapless(removeID int, url, title, cover string) (int, error)

Gapless reconciles mpv's playlist with the queue head: it drops a stale previously-appended entry (removeID; 0 = none) and, if url != "", appends the next track right after the current one so the natural end-of-track boundary plays on inside the running mpv (no respawn). Returns the new entry id.

func (*Stream) IsPaused

func (s *Stream) IsPaused() bool

func (*Stream) Media

func (s *Stream) Media() <-chan MediaCmd

Media returns the channel of OS/hardware transport commands (nil for a non-mpv fallback player). On pocket hardware the GPIO buttons feed this same channel; re-read it after each command to keep listening.

func (*Stream) Pause

func (s *Stream) Pause()

func (*Stream) Position

func (s *Stream) Position() float64

func (*Stream) Restart

func (s *Stream) Restart()

Restart seeks the current track back to the beginning (OS "previous" → restart).

func (*Stream) Seek

func (s *Stream) Seek(sec float64)

func (*Stream) SetTitle

func (s *Stream) SetTitle(title string)

SetTitle updates the OS Now Playing title of the current entry over IPC.

func (*Stream) SetVolume

func (s *Stream) SetVolume(v int)

func (*Stream) Stop

func (s *Stream) Stop()

Stop kills the player (and any yt-dlp feeder) and cleans up the IPC socket.

func (*Stream) Volume

func (s *Stream) Volume() int

func (*Stream) Watch

func (s *Stream) Watch(ctx context.Context, every time.Duration) <-chan State

Watch polls the stream every `every` and emits a State snapshot on the returned channel until the stream ends or ctx is cancelled; the channel is closed on exit. Intended for headless front-ends — the TUI keeps its own tea-driven poll.

Jump to

Keyboard shortcuts

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