Library Usage Guide
This guide provides comprehensive documentation for using mkv2cast as a Python library in your applications.
Overview
mkv2cast exposes a clean Python API that allows you to:
Convert video files programmatically
Monitor conversion progress via callbacks
Process multiple files in parallel
Integrate with your existing applications
Architecture
The mkv2cast API is organized into several modules:
mkv2cast/
├── Config # Configuration dataclass
├── convert_file() # Single file conversion
├── convert_batch() # Parallel batch conversion
├── decide_for() # Codec analysis
├── pick_backend() # Backend selection
└── HistoryDB # Conversion history
Data Flow
The conversion process follows this flow:
Input File
│
▼
┌─────────────────┐
│ decide_for() │ ─── Analyze codecs, determine if transcoding needed
└────────┬────────┘
│
▼
┌─────────────────┐
│ pick_backend() │ ─── Select best encoder (NVENC, QSV, VAAPI, CPU)
└────────┬────────┘
│
▼
┌─────────────────┐
│ convert_file() │ ─── Build FFmpeg command, run encoding
└────────┬────────┘
│
▼
Output File
Quick Start
Minimal Example
from mkv2cast import convert_file, Config
from pathlib import Path
config = Config.for_library()
success, output, msg = convert_file(Path("movie.mkv"), cfg=config)
With Progress Tracking
from mkv2cast import convert_file, Config
from pathlib import Path
def on_progress(filepath, progress):
print(f"{progress['stage']}: {progress['progress_percent']:.1f}%")
config = Config.for_library(hw="vaapi")
convert_file(Path("movie.mkv"), cfg=config, progress_callback=on_progress)
Configuration
Using Config.for_library()
The recommended way to create a configuration for library usage:
from mkv2cast import Config
# Basic usage - automatically disables UI features
config = Config.for_library()
# With custom options
config = Config.for_library(
hw="vaapi", # Hardware acceleration
crf=20, # Quality (lower = better)
preset="slow", # Encoding preset
container="mkv", # Output format
encode_workers=2, # Parallel encoders for batch
)
What Config.for_library() Does
Automatically sets:
progress=False- No progress barsnotify=False- No desktop notificationspipeline=False- No Rich UI
This ensures clean operation when running as a library.
Manual Configuration
You can also configure manually:
from mkv2cast import Config
config = Config(
# Output
suffix=".cast",
container="mkv",
# Hardware
hw="auto", # "auto", "nvenc", "qsv", "vaapi", "cpu"
vaapi_device="/dev/dri/renderD128",
vaapi_qp=23,
qsv_quality=23,
nvenc_cq=23,
# Encoding
crf=20, # CPU CRF (18-28)
preset="slow", # "ultrafast" to "veryslow"
abr="192k", # Audio bitrate
# Behavior
skip_when_ok=True, # Skip compatible files
force_h264=False, # Force H264 transcoding
allow_hevc=False, # Allow HEVC passthrough
# UI (disable for library)
progress=False,
notify=False,
pipeline=False,
# Workers
encode_workers=2,
)
Progress Callbacks
Callback Function Signature
def progress_callback(filepath: Path, progress: dict) -> None:
"""
Called during conversion with progress updates.
Args:
filepath: Path to the file being processed
progress: Dictionary with progress information
"""
pass
Progress Dictionary
The progress dictionary contains:
{
"stage": "encoding", # "checking", "encoding", "done", "skipped", "failed"
"progress_percent": 45.2, # 0.0 to 100.0
"fps": 120.5, # Current FPS
"eta_seconds": 30.5, # Estimated time remaining
"bitrate": "2500kbits/s", # Current bitrate
"speed": "2.5x", # Encoding speed
"current_time_ms": 1620000, # Current position
"duration_ms": 3600000, # Total duration
"error": None, # Error message if failed
}
Stage Transitions
checking ──► encoding ──► done
│ │
│ └──► failed
│
└──► skipped (if compatible)
│
└──► failed (if integrity check fails)
Example: GUI Integration
import tkinter as tk
from tkinter import ttk
from mkv2cast import convert_file, Config
from pathlib import Path
import threading
class ConversionGUI:
def __init__(self):
self.root = tk.Tk()
self.progress = ttk.Progressbar(self.root, length=300)
self.progress.pack(pady=20)
self.label = tk.Label(self.root, text="Ready")
self.label.pack()
def on_progress(self, filepath, progress):
# Update GUI from main thread
self.root.after(0, self._update_gui, progress)
def _update_gui(self, progress):
self.progress["value"] = progress["progress_percent"]
self.label["text"] = f"{progress['stage']}: {progress['progress_percent']:.1f}%"
def convert(self, filepath):
config = Config.for_library()
threading.Thread(
target=convert_file,
args=(Path(filepath),),
kwargs={"cfg": config, "progress_callback": self.on_progress}
).start()
Batch Processing
Using convert_batch()
Process multiple files in parallel:
from mkv2cast import convert_batch, Config
from pathlib import Path
config = Config.for_library(encode_workers=2)
files = [
Path("movie1.mkv"),
Path("movie2.mkv"),
Path("movie3.mkv"),
]
results = convert_batch(files, cfg=config)
for filepath, (success, output, msg) in results.items():
print(f"{filepath.name}: {msg}")
Thread-Safe Callbacks
When using convert_batch(), callbacks may be called from multiple threads.
The library handles thread safety internally, but your callback code should also be safe:
import threading
from mkv2cast import convert_batch, Config
progress_lock = threading.Lock()
all_progress = {}
def thread_safe_callback(filepath, progress):
with progress_lock:
all_progress[str(filepath)] = progress
# Safe to access/modify shared state here
config = Config.for_library(encode_workers=4)
convert_batch(files, cfg=config, progress_callback=thread_safe_callback)
Controlling Parallelism
config = Config.for_library(
encode_workers=4, # 4 parallel encoding jobs
)
encode_workers=0: Auto-detect (usually 1)encode_workers=1: Sequential processingencode_workers=N: N parallel jobs
Error Handling
Convert File Errors
from mkv2cast import convert_file, Config
from pathlib import Path
config = Config.for_library()
try:
success, output, msg = convert_file(Path("movie.mkv"), cfg=config)
if not success:
print(f"Conversion failed: {msg}")
elif output is None:
print(f"File skipped: {msg}")
else:
print(f"Converted to: {output}")
except Exception as e:
print(f"Unexpected error: {e}")
Batch Error Handling
from mkv2cast import convert_batch, Config
results = convert_batch(files, cfg=config)
failed_files = [
(path, msg)
for path, (success, _, msg) in results.items()
if not success
]
if failed_files:
print("Failed files:")
for path, msg in failed_files:
print(f" {path}: {msg}")
Callback Errors
Callback errors are caught and ignored to prevent affecting the conversion:
def buggy_callback(filepath, progress):
raise Exception("Bug!") # Won't stop conversion
# Conversion continues despite callback error
convert_file(path, cfg=config, progress_callback=buggy_callback)
Best Practices
1. Always Use Config.for_library()
This ensures UI features are disabled:
# Good
config = Config.for_library(hw="vaapi")
# Avoid (may produce unwanted output)
config = Config(hw="vaapi")
2. Handle All Result Cases
success, output, msg = convert_file(path, cfg=config)
if success and output:
# File was converted
pass
elif success and not output:
# File was skipped (already compatible or dry run)
pass
else:
# Conversion failed
pass
3. Use Thread-Safe Callbacks for Batch
import threading
lock = threading.Lock()
def callback(filepath, progress):
with lock:
# Safe operations here
pass
4. Set Appropriate Worker Count
import os
# Don't use more workers than CPU cores
config = Config.for_library(
encode_workers=min(4, os.cpu_count() or 1)
)
Troubleshooting
No Output When Expected
Check if file is already compatible:
from mkv2cast import decide_for
decision = decide_for(path)
print(f"Needs video transcode: {decision.need_v}")
print(f"Needs audio transcode: {decision.need_a}")
print(f"Reason: {decision.reason_v}")
Progress Callback Not Called
Ensure you’re passing the callback correctly:
# Correct
convert_file(path, cfg=config, progress_callback=my_callback)
# Wrong (callback as positional arg)
convert_file(path, config, my_callback)
Backend Not Detected
Test backend availability:
from mkv2cast import pick_backend, Config
config = Config(hw="auto")
backend = pick_backend(config)
print(f"Selected backend: {backend}")
API Reference
For complete API documentation, see API Reference.