This repository is archived. mpv-launcher now lives in bingekit, rewritten in Rust with the same flags. Its releases are tagged
mpv-launcher/vX.Y.Zthere. With Nix:nix run github:benashby/bingekit#mpv-launcher.
A Go application that uses FFmpeg for video analysis and preprocessing, then launches MPV for playback.
- Video analysis using FFmpeg (via go-astiav CGO bindings)
- MPV playback control
- XDG-compatible installation to
~/.local/bin - Graceful shutdown handling
- Video file scanner - Recursively or non-recursively scan directories for video files
- Supports 40+ common and uncommon video file extensions
- Configurable recursive scanning
- Symlink handling options
- Episode ordering system - Parse and sort TV show episodes intelligently
- Supports all major naming conventions (SxxExx, xXX, absolute, date-based)
- Handles multi-episode files and split episodes
- Smart sorting by season, episode, and part
- Works with complex filenames containing quality tags and metadata
- Audio and subtitle track analysis - Analyze media file tracks using FFmpeg
- Extract audio and subtitle language information
- Identify tracks common to all files in a series
- Support for multiple track variants (CC, Signs, regional variants)
- Batch analysis across multiple files
- Go 1.21+ - For building the application
- FFmpeg development libraries - Required for go-astiav
- MPV media player - Required for playback
- CGO toolchain - Required for building (gcc/clang)
sudo pacman -S go ffmpeg mpv base-develsudo apt install golang ffmpeg mpv libavcodec-dev libavformat-dev libavutil-dev libswscale-dev build-essentialsudo dnf install golang ffmpeg mpv ffmpeg-devel gccbrew install go ffmpeg mpvmake buildThis creates the binary at build/mpv-launcher.
make installMake sure ~/.local/bin is in your PATH:
export PATH="$HOME/.local/bin:$PATH"Add this to your ~/.bashrc or ~/.zshrc to make it permanent.
Run mpv-launcher in a directory containing video files:
cd /path/to/tv/show
mpv-launcherThe launcher will:
- Scan the current directory for video files (non-recursively)
- Sort episodes in correct viewing order
- Analyze audio and subtitle tracks
- Present playback options based on available tracks
- Launch MPV with your selected configuration
Available options (based on detected tracks):
English - No Subtitles- If English audio is availableJapanese - Eng Subtitles- If Japanese audio and English subtitles are availableJapanese - No Subtitles- If Japanese audio is available but no English subtitles
# Scan a specific directory
mpv-launcher -dir /path/to/videos
# Recursively scan subdirectories (up to 100 files)
mpv-launcher -dir /path/to/videos -r
# Scan current directory recursively
mpv-launcher -r$ cd "/path/to/Anne of Green Gables/Season 1"
$ mpv-launcher
Scanning directory: .
Found 24 video files
Analyzing audio and subtitle tracks...
Playback options:
1. English - No Subtitles
2. Japanese - Eng Subtitles
Select option: 2
Selected: Japanese - Eng Subtitles
Launching MPV with 24 files...
[MPV plays the entire season in order with Japanese audio and English subtitles]
Press Ctrl+C to stop playback at any time.
The scanner package provides functionality to find video files in directories.
The scanner recognizes 40+ video file extensions including:
- Common:
.mp4,.mkv,.avi,.mov,.wmv,.flv,.webm,.m4v,.mpg,.mpeg - Uncommon:
.vob,.rmvb,.mxf,.yuv,.divx,.dv,.f4v,.roq - And many more...
package main
import (
"fmt"
"mpv-launcher/internal/scanner"
)
func main() {
// Configure scan options
opts := scanner.ScanOptions{
Recursive: true, // Scan subdirectories
FollowSymlinks: false, // Don't follow symbolic links
}
// Scan directory for video files
videos, err := scanner.ScanDirectory("/path/to/videos", opts)
if err != nil {
panic(err)
}
// Print found videos
for _, video := range videos {
fmt.Println(video)
}
}dirs := []string{"/movies", "/tv-shows", "/clips"}
opts := scanner.ScanOptions{Recursive: true}
videos, err := scanner.ScanMultipleDirectories(dirs, opts)if scanner.IsVideoFile("movie.mp4") {
fmt.Println("This is a video file!")
}See examples/scan-directory.go for a complete example:
go run examples/scan-directory.go ~/Videos
go run examples/scan-directory.go ~/Videos --recursiveThe examples/play-series.go demonstrates full integration of all modules:
# Play a TV series with auto-detected tracks
go run examples/play-series.go -dir "/path/to/series"
# Specify audio and subtitle languages
go run examples/play-series.go -dir "/path/to/series" -audio jpn -sub eng
# Disable subtitles
go run examples/play-series.go -dir "/path/to/series" -no-sub
# Start in fullscreen
go run examples/play-series.go -dir "/path/to/series" -fullscreenThis example:
- Scans the directory for video files
- Parses and sorts episodes in correct viewing order
- Analyzes audio/subtitle tracks (if languages not specified)
- Launches MPV with the complete playlist and selected tracks
The episode package provides intelligent parsing and sorting of TV show episodes.
- SxxExx:
S01E01,s02e15,S1E1(with/without leading zeros) - xXX:
1x01,2x15,1X01(compact format) - Absolute:
001,ep01,025(sequential numbering) - Date-based:
2024-01-15,2023.12.25,15-01-2024 - Multi-episode:
S01E01-E03,S01E01E02E03 - Split episodes:
S01E01 pt1,S01E01.1,S01E01a
package main
import (
"fmt"
"mpv-launcher/internal/episode"
"mpv-launcher/internal/scanner"
)
func main() {
// Scan for video files
opts := scanner.ScanOptions{Recursive: true}
videos, _ := scanner.ScanDirectory("/path/to/show", opts)
// Parse and sort episodes
episodes := episode.ParseBatch(videos)
episode.Sort(episodes)
// Play in correct order
for _, ep := range episodes {
fmt.Printf("S%02dE%02d: %s\n",
ep.Season, ep.Episodes[0], ep.Filename)
}
}info := episode.Parse("Show.S02E15.1080p.mkv")
fmt.Println(info.Season) // 2
fmt.Println(info.Episodes) // [15]
fmt.Println(info.Type) // TypeSeasonEpisodeSee EPISODE_ORDERING.md for comprehensive documentation including:
- All supported naming conventions
- Multi-episode and split episode handling
- Sorting logic and priorities
- Complex filename examples
- API reference
The player package supports launching MPV with playlists and audio/subtitle track selection.
package main
import (
"mpv-launcher/internal/player"
)
func main() {
files := []string{
"episode01.mkv",
"episode02.mkv",
"episode03.mkv",
}
opts := player.PlaylistOptions{
AudioLanguage: "eng", // Prefer English audio
SubtitleLanguage: "eng", // Prefer English subtitles
Fullscreen: true, // Start in fullscreen
}
mpvPlayer, _ := player.NewMPVPlayer()
mpvPlayer.PlayPlaylist(files, opts)
mpvPlayer.Wait() // Wait for playback to complete
}// Select by language preference
opts := player.PlaylistOptions{
AudioLanguage: "jpn", // Prefer Japanese audio
SubtitleLanguage: "eng", // Prefer English subtitles
}
// Select by specific track index (from tracks.Track.Index)
opts := player.PlaylistOptions{
AudioTrack: 2, // Use audio track index 2
SubtitleTrack: 4, // Use subtitle track index 4
}
// Disable subtitles
opts := player.PlaylistOptions{
SubtitleTrack: -1, // Disable subtitles
}The tracks package analyzes audio and subtitle tracks in media files.
package main
import (
"fmt"
"mpv-launcher/internal/tracks"
)
func main() {
info, err := tracks.ProbeFile("movie.mkv")
if err != nil {
panic(err)
}
fmt.Printf("Audio tracks: %d\n", len(info.AudioTracks))
for _, track := range info.AudioTracks {
fmt.Printf(" [%d] %s - %s\n", track.Index, track.Language, track.Title)
}
fmt.Printf("Subtitle tracks: %d\n", len(info.SubtitleTracks))
for _, track := range info.SubtitleTracks {
fmt.Printf(" [%d] %s - %s\n", track.Index, track.Language, track.Title)
}
}package main
import (
"fmt"
"mpv-launcher/internal/tracks"
"mpv-launcher/internal/scanner"
)
func main() {
// Scan for videos
opts := scanner.ScanOptions{Recursive: true}
videos, _ := scanner.ScanDirectory("/path/to/series", opts)
// Analyze all files
analysis := tracks.AnalyzeFiles(videos)
fmt.Println("Common audio languages:", analysis.CommonAudioLanguages)
fmt.Println("Common subtitle languages:", analysis.CommonSubtitleLanguages)
// Find files missing English audio
for _, fileInfo := range analysis.Files {
if !fileInfo.HasLanguage(tracks.TrackTypeAudio, "eng") {
fmt.Printf("Missing English: %s\n", fileInfo.Filename)
}
}
}See TRACKS.md for comprehensive documentation including:
- Data structures and API reference
- Language detection and aggregation
- Track variants (CC, Signs, regional)
- Integration examples
- Performance considerations
mpv-launcher/
├── cmd/
│ └── mpv-launcher/ # Main application entry point
│ └── main.go
├── internal/ # Internal packages
│ ├── ffmpeg/ # FFmpeg video probing
│ │ ├── probe.go
│ │ └── probe_test.go
│ ├── player/ # MPV player control
│ │ ├── mpv.go
│ │ └── mpv_test.go
│ ├── scanner/ # Video file scanner
│ │ ├── scanner.go
│ │ └── scanner_test.go
│ ├── episode/ # Episode parsing and ordering
│ │ ├── episode.go
│ │ └── episode_test.go
│ └── tracks/ # Audio/subtitle track analysis
│ ├── tracks.go
│ └── tracks_test.go
├── examples/ # Example programs
│ └── scan-directory.go
├── go.mod # Go module definition
├── Makefile # Build and installation tasks
├── README.md # This file
├── SCANNER.md # Scanner API documentation
├── EPISODE_ORDERING.md # Episode ordering documentation
└── TRACKS.md # Track analysis documentation
make testRun tests with coverage:
make test-coverageThis generates coverage.html for viewing coverage details.
Download dependencies:
make depsTidy dependencies:
make tidymake build- Build the binary tobuild/mpv-launchermake install- Install to~/.local/binmake uninstall- Remove from~/.local/binmake clean- Clean build artifactsmake test- Run all testsmake test-coverage- Run tests with coverage reportmake deps- Download and verify dependenciesmake tidy- Tidy up go.mod and go.sum
make uninstallThis is a personal project for direct installation and use.
- CGO is required for building due to go-astiav FFmpeg bindings
- MPV must be installed and available in PATH for playback
- The application uses XDG-compatible directories for runtime files