Skip to main content

py-media-server

Disclaimer. This software is provided AS IS, without warranty of any kind, express or implied. Use it entirely at your own risk. By default it listens on every network interface without a password: anyone who can reach the computer can browse and stream every video in the folder you serve. It also writes thumbnails and a playback cache into a .thumbs folder there. The author accepts no liability for data loss, data corruption, exposure of files, hardware damage, business interruption or any consequential damages arising from its use. You are responsible for choosing what you serve and on which network, for being authorised to share it, and for keeping tested backups. It is not certified for regulated, forensic, safety-critical or high-assurance use. The LICENSE is the governing text and prevails wherever it and this notice differ. See also DISCLAIMER.md.

A simple, lightweight media server for streaming video files over HTTP with a modern web interface.

Features

  • 🎬 Stream video files (MP4, MKV, AVI, MOV, M4V, WebM) from any directory
  • 📂 Recursive scanning: subfolders are browsed, filtered and searched
  • 🔍 Search, sort (name / newest / largest / longest) and folder filter, all preserved in the URL so a view can be bookmarked or shared
  • ⏯️ Resume where you left off, with a Continue watching row and progress bars on tiles
  • 💬 Sidecar subtitles (.srt / .vtt) discovered automatically and served as WebVTT, with language labels
  • ▶️ MKV and other non-native files play automatically: ffprobe inspects every audio track and the server remuxes, converts the audio, or converts the video, whichever is cheapest for the browser in front of it
  • ▶️ Full custom player: play/pause, ±10s skip, next/previous, seek bar, volume, playback speed, fullscreen and keyboard shortcuts
  • ⏭️ Auto-advance to the next video when one finishes
  • 🖼️ Automatic thumbnail generation using FFmpeg
  • 📱 Responsive web interface that works on mobile and desktop
  • ⚡ Range request support for efficient video streaming
  • 🔒 Threaded TCP server for concurrent connections
  • 🎨 Modern, dark-themed UI

Installation

From PyPI (when published)

pip install py-media-server

From source

git clone https://github.com/pandiyarajk/py-media-server.git
cd py-media-server
pip install -e .

Requirements

  • Python 3.11+
  • FFmpeg and ffprobe (optional)

FFmpeg is optional but unlocks three features: thumbnail generation, codec inspection (duration, resolution, audio tracks) and on-the-fly remuxing and transcoding. Without it the server still browses and streams: the affordances that would not work are simply not rendered, and files the browser is unlikely to play are badged "may not play".

The server looks for ffmpeg/ffprobe on PATH first, then in common install locations (C:\ffmpeg\bin, Homebrew, /usr/local/bin, the winget shim directory, etc.), so a normal install is picked up even when it was never added to PATH. If it lives somewhere unusual, point at it directly:

py-media-server ~/Videos --ffmpeg /opt/custom/ffmpeg/bin
# or the binary itself:
py-media-server ~/Videos --ffmpeg /opt/custom/ffmpeg/bin/ffmpeg

PY_MEDIA_SERVER_FFMPEG (path to the binary or its directory) works the same way as an environment variable.

To install FFmpeg:

  • Windows: Download from ffmpeg.org or use choco install ffmpeg
  • macOS: brew install ffmpeg
  • Linux: sudo apt-get install ffmpeg or sudo yum install ffmpeg

Usage

Basic usage

Serve media files from the current directory:

py-media-server

Or specify a directory:

py-media-server /path/to/your/movies

Custom port

py-media-server /path/to/your/movies --port 9000

Custom host and port

py-media-server ~/Videos --host 192.168.1.100 --port 8080

Help

py-media-server --help

Command-line Options

positional arguments:
  directory             Directory containing media files to serve (default: current directory)

optional arguments:
  -h, --help            show this help message and exit
  -p PORT, --port PORT  Port to run the server on (default: 8000)
  --host HOST           Host to bind the server to (default: 0.0.0.0)
  --ffmpeg PATH         Path to ffmpeg, or the directory holding it, for
                        installs not on PATH or in a common location
  -v, --version         show program's version number and exit

How it Works

  1. Start the server: Point it to a directory containing video files
  2. Open your browser: Navigate to http://localhost:8000
  3. Browse and play: Click on any video thumbnail to start streaming

The server will:

  • Scan the directory and its subfolders for supported video formats (directories beginning with . are skipped)
  • Generate thumbnails automatically (stored in the .thumbs subdirectory, alongside cached probe metadata and watch progress)
  • Serve videos with range request support for smooth playback
  • Handle concurrent connections efficiently

Library view

The index page offers a search box, a sort selector and a folder filter. The active view is written to the URL fragment, so a filtered listing can be bookmarked or shared. Files you have started appear in a Continue watching row, and every tile shows a progress bar plus badges for resolution, duration, subtitles and how the file will be played (see below).

Subtitles

Sidecar files next to the video are picked up automatically:

Shows/S01/ep01.mkv
Shows/S01/ep01.srt          -> "Subtitles"
Shows/S01/ep01.en.srt       -> "EN"
Shows/S01/ep01.English.vtt  -> "English"

SRT is converted to WebVTT on the fly, so no pre-processing is needed. Encodings other than UTF-8 (including BOM-prefixed and CP1252 files) are decoded rather than rejected.

Compatibility and transcoding

When ffprobe is available the server records the video codec and every audio track of each file (codec, channels, language, default flag), probing the library in the background and caching the result in .thumbs. Each time a file is opened it picks a playback plan, cheapest first:

Plan When What ffmpeg does
direct MP4/MOV/WebM, decodable video, first audio track decodable nothing; the file is served with range requests
remux decodable video and some decodable audio track, but an MKV/AVI container or the good track is not first copies one video and one audio stream into fragmented MP4 (no re-encode, almost no CPU)
audio decodable video, no decodable audio track (EAC3/AC3/DTS only) copies the video, converts the main audio track to stereo AAC
full the browser cannot decode the video (for example HEVC on a browser without HEVC support) re-encodes the video to H.264 and copies or converts the audio

Streams are always mapped explicitly. A typical MKV carries a 5.1 EAC3 track first and a stereo AAC track second; the AAC track is chosen (preferring the film's own language, the default flag, then stereo) and copied, so the video neither plays silently nor needs a re-encode.

The player asks the browser what it can decode (MediaSource.isTypeSupported, falling back to canPlayType) and tells the server, for example ?hevc=1. Chrome and Edge on Windows with hardware HEVC decoding then get HEVC copied into MP4 tagged hvc1; other browsers get it converted. AC3 and EAC3 support is asked the same way. A browser that reports nothing gets the conservative plan.

The player starts the chosen plan by itself. If playback fails, or a few seconds in the browser has decoded no video or no audio, it moves on to the next plan automatically. A Direct / Remux / Convert audio / Convert video selector beside the subtitles menu switches by hand. Seeking a converted stream restarts ffmpeg at the new point, and the time display, resume and sidecar subtitles all stay on the file's own clock.

Library badges say what will happen: nothing for direct play, a small container chip (such as MKV) for a remux, converts audio, or converts video (CPU heavy). HEVC files are labelled for the browser viewing the page. may not play appears only when ffmpeg is unavailable and the file is not browser-native.

Up to six remuxes and two conversions run at once; a request waits up to ten seconds for a slot before getting a 503. ffmpeg is stopped as soon as the viewer seeks, switches mode or closes the tab.

Player

Clicking a tile opens the player at /play/<filename>. The page is served with the whole library as its playlist, so next/previous move through the same sorted listing shown on the home page (disabled at the ends).

Controls

Button Action
⏮ Previous video
⏪ Back 10 seconds
▶ / ⏸ Play / pause
⏩ Forward 10 seconds
⏭ Next video
🔊 Mute, with a volume slider
1x Playback speed (0.5x – 2x)
⛶ Fullscreen

A draggable seek bar shows elapsed and total time.

Keyboard shortcuts

Key Action
Space / K Play / pause
← / → Seek 10 seconds
↑ / ↓ Volume up / down
N Next video
P Previous video
M Mute
C Cycle subtitle track
F Fullscreen

Development

Setup development environment

git clone https://github.com/pandiyarajk/py-media-server.git
cd py-media-server
pip install -e ".[dev]"

Run tests

pytest

Run tests with coverage

pytest --cov=py_media_server --cov-report=html

Project Structure

py-media-server/
├── py_media_server/
│   ├── __init__.py
│   ├── server.py
│   └── templates.py
├── tests/
│   ├── __init__.py
│   └── test_server.py
├── setup.py
├── pyproject.toml
├── README.md
├── LICENSE
└── MANIFEST.in

Security Notice

⚠️ Warning: This server is intended for local network use only. It does not include authentication or encryption. Do not expose it to the public internet without proper security measures.

License

MIT License - see LICENSE file for details

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Changelog

0.4.1 (2026-10-19)

  • Removed the keyboard and touch help line under the player; the shortcuts still work and stay documented here

0.4.0 (2026-10-19)

  • Fullscreen shows the player controls (seek bar, back/forward, mute) as an overlay instead of a bare video, on desktop, phone and TV browsers
  • Controls fade out after 3 seconds of playback and return on mouse move, tap or key press
  • Touch: double-tap the left or right side of the video to skip back or forward, with a +/-10s flash; tap shows or hides the controls
  • One mute path for the button, the M key and the remote's mute key; media keys (rewind, fast-forward, play/pause) work, and the D-pad can move between buttons
  • Larger touch targets, visible focus outline, volume slider hidden on narrow screens (use the hardware keys)
  • The startup banner lists the LAN address to open from a TV or phone

0.3.1 (2026-10-18)

  • MKV files with browser-safe codecs play through a stream-copy remux instead of being badged "may not play"
  • ffprobe records every audio track; a file with EAC3 first and AAC second plays the AAC track instead of playing silently
  • ffmpeg streams are mapped explicitly (-map 0:v:N -map 0:a:N) instead of letting ffmpeg pick the 5.1 EAC3 track; cover-art streams are skipped
  • Playback plan (direct, remux, audio, full) chosen per file and per browser; the player starts it automatically and falls back on failure or silence
  • HEVC is copied as hvc1 for browsers that report HEVC support, converted for others; AC3/EAC3 support is detected the same way
  • Manual Direct / Remux / Convert selector in the player
  • Seeking a converted stream keeps the time display, resume position and sidecar subtitles in sync (/subs/...?offset=N)
  • Honest library badges: container chip, "converts audio", "converts video"; "may not play" only without ffmpeg
  • Metadata cache entries from 0.3.0 still load and are re-probed automatically
  • Remuxes and conversions use separate slot pools, and a seek waits for a slot instead of failing with "busy"

0.3.0 (2026-08-15)

  • ffmpeg/ffprobe are discovered in common install locations, and via --ffmpeg / PY_MEDIA_SERVER_FFMPEG, when absent from PATH
  • Recursive scanning of subfolders
  • Search, sort and folder filtering, with the view reflected in the URL
  • Resume playback, "Continue watching" row and per-tile progress bars
  • Sidecar subtitle discovery with SRT-to-WebVTT conversion
  • Codec compatibility warnings via ffprobe
  • On-demand transcoding for files the browser cannot play
  • Optional features are hidden when ffmpeg is unavailable rather than rendered broken
  • Page markup and scripts split out into templates.py
  • Minimum supported Python raised to 3.11

0.2.0 (2026-08-15)

  • Full custom player: play/pause, ±10s skip, next/previous, seek bar, volume, playback speed, fullscreen, keyboard shortcuts, auto-advance
  • Unmatched routes now return 404 instead of closing the connection with no response
  • Range requests are clamped to the file size
  • Filenames are escaped when embedded in page scripts

0.1.0 (2026-04-19)

  • Initial release
  • Basic video streaming functionality
  • Thumbnail generation
  • Web interface
  • Command-line interface

Metadata

Release files for py-media-server 0.4.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for py-media-server 0.4.1
File Size Uploaded Provenance
py_media_server-0.4.1.tar.gz 73.1 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for py-media-server 0.4.1
File Interpreter ABI Platform
py_media_server-0.4.1-py3-none-any.whl Python 3 none any Details

Total release size: 117.4 kB

Release files / py_media_server-0.4.1.tar.gz

Download URL py_media_server-0.4.1.tar.gz
Size 73.1 kB
Tags Source
SHA-256 checksum
How to use checksums
84f9a34f9deca8788d359db9a262c334669d1fc0989932dc2791d7a2a148f451
BLAKE2b-256 checksum
How to use checksums
df9e7ca188f883eede9275cef43b5b80b64a182a6e8cffdfdf48df4124bd22dc
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.5

Release files / py_media_server-0.4.1-py3-none-any.whl

Download URL py_media_server-0.4.1-py3-none-any.whl
Size 44.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
3d0a855f8d12c5ef4f0acd25c24f27b44e79d120aefc18488f715145b1f061df
BLAKE2b-256 checksum
How to use checksums
21069f52a439757ffa22707a6d08590acad083d51701c43394e97895586b2729
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.5

Security for py-media-server 0.4.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page