mpv

package
v0.0.2 Latest Latest
Warning

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

Go to latest
Published: Feb 26, 2026 License: MIT Imports: 16 Imported by: 0

README

#+TITLE: README
#+FILETAGS: :documentation

* MPV Player Implementation

This package provides an mpv-based implementation of the =player.Player= interface using IPC (Inter-Process Communication) via Unix sockets.

** Features

- Full playback control (play, pause, resume, stop, seek)
- Real-time progress monitoring with callbacks
- Support for all mpv options (subtitles, audio tracks, custom arguments)
- HTTP header support for streaming (referer, user-agent, custom headers)
- Thread-safe operations
- Automatic resource cleanup

** Requirements

- mpv binary installed and available in PATH
- Unix-like system with Unix socket support

** Usage

*** Basic Playback

#+BEGIN_SRC go
package main

import (
    "context"
    "fmt"
    "time"

    "github.com/justchokingaround/greg/internal/player"
    "github.com/justchokingaround/greg/internal/player/mpv"
)

func main() {
    // Create player instance
    p, err := mpv.NewMPVPlayer()
    if err != nil {
        panic(err)
    }

    // Play a video
    ctx := context.Background()
    err = p.Play(ctx, "https://example.com/video.mp4", player.PlayOptions{
        Volume: 80,
    })
    if err != nil {
        panic(err)
    }

    // Wait a bit then stop
    time.Sleep(10 * time.Second)
    p.Stop(ctx)
}
#+END_SRC


*** With Progress Monitoring

#+BEGIN_SRC go
p, _ := mpv.NewMPVPlayer()

// Set up progress callback (called every second)
p.OnProgressUpdate(func(progress player.PlaybackProgress) {
    fmt.Printf("Progress: %.2f%% (%.0f/%.0f seconds)\n",
        progress.Percentage,
        progress.CurrentTime.Seconds(),
        progress.Duration.Seconds())
})

// Set up playback end callback
p.OnPlaybackEnd(func() {
    fmt.Println("Playback finished!")
})

// Set up error callback
p.OnError(func(err error) {
    fmt.Printf("Player error: %v\n", err)
})

// Start playback
ctx := context.Background()
p.Play(ctx, url, player.PlayOptions{})
#+END_SRC


*** With Streaming Headers

#+BEGIN_SRC go
// Play with custom headers for streaming sites
p.Play(ctx, streamURL, player.PlayOptions{
    Referer:   "https://streaming-site.com",
    UserAgent: "Mozilla/5.0 ...",
    Headers: map[string]string{
        "X-Custom-Header": "value",
    },
})
#+END_SRC


*** With Subtitles

#+BEGIN_SRC go
p.Play(ctx, videoURL, player.PlayOptions{
    SubtitleURL:   "https://example.com/subs.srt",
    SubtitleLang:  "eng",
    SubtitleDelay: 500 * time.Millisecond,
})
#+END_SRC


*** Pause, Resume, and Seek

#+BEGIN_SRC go
// Pause playback
p.Pause(ctx)

// Resume playback
p.Resume(ctx)

// Seek to 5 minutes
p.Seek(ctx, 5*time.Minute)

// Get current progress
progress, _ := p.GetProgress(ctx)
fmt.Printf("Current position: %v\n", progress.CurrentTime)
#+END_SRC


*** Advanced Options

#+BEGIN_SRC go
p.Play(ctx, videoURL, player.PlayOptions{
    StartTime:  30 * time.Second,      // Start from 30s
    Volume:     75,                     // Set volume to 75%
    Speed:      1.5,                    // 1.5x playback speed
    Fullscreen: true,                   // Start in fullscreen
    AudioTrack: 2,                      // Select audio track 2
    Title:      "My Video",             // Window title
    MPVArgs:    []string{"--hwdec=auto"}, // Custom mpv args
})
#+END_SRC


** Architecture

*** IPC Communication

The player communicates with mpv using JSON-RPC over Unix sockets:

1. /Socket Creation/: A unique socket path is generated in =/tmp/greg-mpv-{random}.sock=
2. /Process Spawning/: mpv is launched with =--input-ipc-server={socket}=
3. /Connection/: gopv library connects to the socket
4. /Commands/: JSON-RPC commands are sent via =Request()= method
   - Get property: =Request("get_property", "time-pos")=
   - Set property: =Request("set_property", "pause", true)=
   - Execute command: =Request("quit")=

*** Progress Monitoring

Two goroutines run during playback:

1. /Progress Monitor/: Polls mpv properties every second
   - Queries: time-pos, duration, pause, volume, speed, eof-reached
   - Triggers =OnProgressUpdate= callback
   - Triggers =OnPlaybackEnd= callback when EOF is reached

2. /Process Monitor/: Watches the mpv process
   - Detects unexpected exits
   - Triggers =OnError= callback on process failure
   - Cleans up resources

*** Resource Management

Cleanup happens automatically:
- Socket file is removed on stop
- mpv process is killed if still running
- Goroutines are stopped via context cancellation
- All resources are cleaned up even if mpv crashes

** Testing

*** Unit Tests

#+BEGIN_SRC bash
go test ./internal/player/mpv
#+END_SRC


Tests socket generation, argument building, state management, and callbacks.

*** Integration Tests

#+BEGIN_SRC bash
go test -tags=integration ./internal/player/mpv
#+END_SRC


Requires mpv to be installed. Tests actual playback with mpv test sources.

** Implementation Details

*** Thread Safety

All public methods use mutex locks to ensure thread-safe access:
- =mu sync.RWMutex= protects all state
- Read operations use =RLock()=
- Write operations use =Lock()=

*** State Management

Player maintains state:
- =StateStopped=: No playback
- =StatePlaying=: Actively playing
- =StatePaused=: Playback paused
- =StateLoading=: Buffering (not currently used)
- =StateError=: Error state (not currently used)

*** Error Handling

- All errors are wrapped with context
- IPC errors trigger the error callback
- Process failures are handled gracefully
- Failed operations don't leave orphaned processes

** Limitations

- Unix sockets only (Linux, macOS, BSD)
- No Windows support (would need named pipes)
- EOF detection may not work with some synthetic sources (lavfi)
- Progress polling is 1-second intervals (not configurable yet)

** Future Enhancements

- [ ] Windows support via named pipes
- [ ] Configurable progress polling interval
- [ ] Property observation using =ObserveProperty=
- [ ] Event listeners using =RegisterListener=
- [ ] Playlist support
- [ ] Audio-only mode
- [ ] Screenshot functionality
- [ ] Chapter navigation

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func FindMPVExecutable

func FindMPVExecutable(platform Platform) (string, error)

FindMPVExecutable attempts to find the mpv executable path

func GetGopvConnectionString

func GetGopvConnectionString(config *IPCConfig) string

GetGopvConnectionString returns the connection string for gopv library

func GetMPVExecutable

func GetMPVExecutable(platform Platform) string

GetMPVExecutable returns the mpv executable name for the platform

func GetMPVIPCArgument

func GetMPVIPCArgument(config *IPCConfig) string

GetMPVIPCArgument returns the mpv command-line argument for IPC

Types

type IPCConfig

type IPCConfig struct {
	Type     IPCType
	Address  string
	IsSocket bool // true for Unix sockets, false for TCP
}

IPCConfig holds IPC connection configuration

func GetIPCConfig

func GetIPCConfig(platform Platform) (*IPCConfig, error)

GetIPCConfig generates an IPC configuration for the platform

type IPCType

type IPCType int

IPCType represents the IPC connection type

const (
	IPCUnixSocket IPCType = iota
	IPCNamedPipe
	IPCTCP
)

type MPVPlayer

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

MPVPlayer implements the Player interface using mpv with IPC

func NewMPVPlayer

func NewMPVPlayer() (*MPVPlayer, error)

NewMPVPlayer creates a new mpv player instance

func NewMPVPlayerWithConfig

func NewMPVPlayerWithConfig(cfg *config.Config, debug bool) (*MPVPlayer, error)

NewMPVPlayerWithConfig creates a new mpv player instance with configuration

func NewMPVPlayerWithDebug

func NewMPVPlayerWithDebug(debug bool) (*MPVPlayer, error)

NewMPVPlayerWithDebug creates a new mpv player instance with debug flag

func (*MPVPlayer) GetProgress

func (p *MPVPlayer) GetProgress(ctx context.Context) (*player.PlaybackProgress, error)

GetProgress returns the current playback progress

func (*MPVPlayer) IsPaused

func (p *MPVPlayer) IsPaused() bool

IsPaused returns true if the player is currently paused

func (*MPVPlayer) IsPlaying

func (p *MPVPlayer) IsPlaying() bool

IsPlaying returns true if the player is currently playing

func (*MPVPlayer) OnError

func (p *MPVPlayer) OnError(callback func(err error))

OnError sets the error callback

func (*MPVPlayer) OnPlaybackEnd

func (p *MPVPlayer) OnPlaybackEnd(callback func())

OnPlaybackEnd sets the playback end callback

func (*MPVPlayer) OnProgressUpdate

func (p *MPVPlayer) OnProgressUpdate(callback func(progress player.PlaybackProgress))

OnProgressUpdate sets the progress update callback

func (*MPVPlayer) Play

func (p *MPVPlayer) Play(ctx context.Context, url string, options player.PlayOptions) error

Play starts playback of the given URL with options Returns immediately after validating prerequisites and starting the launch process Launch failures will be reported via the OnError callback

func (*MPVPlayer) Seek

func (p *MPVPlayer) Seek(ctx context.Context, position time.Duration) error

Seek seeks to the specified position

func (*MPVPlayer) Stop

func (p *MPVPlayer) Stop(ctx context.Context) error

Stop stops playback and cleans up resources

type Platform

type Platform int

Platform represents the operating system platform

const (
	PlatformLinux Platform = iota
	PlatformWindows
	PlatformWSL
	PlatformMac
)

func DetectPlatform

func DetectPlatform() Platform

DetectPlatform detects the current platform

Jump to

Keyboard shortcuts

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