Open source · MIT · Python 3.9+ · Windows, macOS, Linux, Docker

Play video inside remote ZIPs. Download nothing.

ZipStreamHub is a zero-download streaming server for remote ZIP and ZIP64 archives. It reads the ZIP index with HTTP range requests and serves seekable HTTP 206 streams to PotPlayer, MPV, VLC, IINA, your browser, M3U playlists, WebDAV and media servers.

$git clone https://github.com/MHJoy99/ZipStream-Hub.git
Season01.zip remote file · HTTP/HTTPS · ZIP / ZIP64 Episode01.mkvSTORE Episode02.mkvSTORE Episode03.mkvSTORE archive bytes file data, never fully fetched tail: ZIP index only the bytes you watch are read GET Range 206 GET Range 206 ZipStreamHub 127.0.0.1:8787 S + data_offset sliding-window buffer 206 Partial Content Accept-Ranges: bytes > Range: bytes=-1048576< 206 · EOCD + central directory > Range: bytes=0-< 206 · 2 MB blocks, 128 KB slices player: Range: bytes=2920577761-remote: Range: bytes=2920577985- player: Range: bytes=1460288880-remote: Range: bytes=1460289104- HTTP 206 SEEK → new range player seeks, socket closes, old buffer drains, a new Range request opens Any player PotPlayer · MPV · VLC · IINA · browser Season01.zip remote file · HTTP/HTTPS · ZIP / ZIP64 archive bytes file data, never fully fetched tail: ZIP index only the bytes you watch are read GET Range 206 ZipStreamHub 127.0.0.1:8787 S + data_offset sliding-window buffer · 206 Partial Content > Range: bytes=-1048576< 206 · EOCD + central directory > Range: bytes=0-< 206 · 2 MB blocks, 128 KB slices player: Range: bytes=2920577761-remote: Range: bytes=2920577985- player: Range: bytes=1460288880-remote: Range: bytes=1460289104- HTTP 206 SEEK → new range
Illustrative animation of the mechanism (the numbers are an example). A seek becomes a new Range request shifted by the entry's data offset.
~0.6 stail parse to build the index
0 bytesof video written to disk
ZIP64archives up to 16 EB
< 5 msseek translation

Figures are as stated in the project README, not independently measured here.

Key features

Everything between a cloud ZIP and your player

Ten capabilities straight from the README, from the byte-range engine to one-click player launch, playlists and a dark-mode dashboard.

Zero-download streaming

Streams media directly from remote HTTP/HTTPS ZIP archives over standard HTTP 206 byte-range requests.

ZIP, ZIP64 and Store

Handles archives larger than 4 GB (up to 16 exabytes by the ZIP64 format) with STORE and DEFLATE methods.

Sliding-window prefetcher

Multi-threaded read-ahead buffer with 128 KB chunk slicing for smooth playback and fast seeks. Size is tunable up to 5 GB of RAM.

1-click native players

Auto-detects PotPlayer, MPV, VLC, MPC-HC, MPC-BE and IINA via the Windows Registry and your system PATH.

Web player and dashboard

Dark-mode control panel with a built-in HTML5 player, fullscreen, playback speed and live stream telemetry.

M3U / M3U8 playlists

Export a playlist of every video in the archive for Kodi, Infuse (Apple TV and iOS) and VLC.

Subtitle auto-conversion

Detects subtitle tracks and converts .srt to WebVTT on the fly for in-browser viewing.

History and bookmarks

Quick-access storage for frequently inspected archives, with one-click reload and saved seek positions.

Windows 1-click launcher

Background startup via launch_zipstream.vbs, desktop shortcuts and automatic browser launch.

Interactive terminal player

A colourful ANSI CLI (zipstream) for keyboard-driven episode selection or direct launch into a player.

How it works

The ZIP index lives at the end of the file

A ZIP is built for random access. So ZipStreamHub never needs the whole archive: it reads the tail, builds an index in memory, then turns every player seek into one precise range request.

  1. Tail-parse the archive

    Request the final 1 MB with an HTTP Range request. Locate the End of Central Directory (0x06054b50) and the ZIP64 Locator (0x07064b50).

  2. Resolve the Central Directory

    Read the file headers (0x02014b50) and build the in-memory archive index. The README states this takes about 0.6 seconds.

  3. Translate ranges

    A player request for bytes [S, E] is offset against the file's data_offset and served through the asynchronous sliding-window prefetcher.

Schematic, not to scale. The highlight sweeps in the order ZipStreamHub reads: tail first, then the index, then only the file data you play.

Range translation

The Central Directory points to the local file header, so ZipStreamHub reads those 30 bytes once and caches the real payload offset. With the STORE method, byte X of the video is byte data_offset + X of the remote file.

# per entry (cached after the first lookup)
data_offset = local_header_offset + 30 + name_len + extra_len

# player asks for [S_client, E_client]
S_remote = data_offset + S_client
E_remote = data_offset + E_client

Store vs. Deflate

STORE (method 0, no compression) gives 1:1 byte offsets and instant seeking. DEFLATE (method 8) compresses the data, so, per the troubleshooting guide, seeking to an arbitrary byte can stutter or fail.

Videos are already compressed, so a Store ZIP is about the same size. To re-pack with 7-Zip:

# 0% compression = instant streaming
7z a -mx0 output.zip "path/to/videos/*"

Architecture

A byte-range proxy with many front doors

One engine reads the remote archive; one multi-threaded server exposes it as streams, playlists, WebDAV and STRM bundles. No FUSE or kernel driver involved.

remote server

Remote ZIP / ZIP64

Any HTTP/HTTPS direct link that honours Range. Only the tail, the index and the played ranges are read.

engine.py

RemoteZipReader

Probes size with Range: bytes=0-0, tail-parses EOCD / ZIP64, builds the index, caches each data_offset.

engine.py

StreamPrefetcher

Worker thread pulls 2 MB blocks over a keep-alive urllib3 pool into a bounded queue; the consumer slices them into 128 KB writes.

server.py

HTTP / WebDAV server

Answers 206 Partial Content at /stream/<id>/<filename>, plus the REST API, M3U, STRM and WebDAV.

Sliding-window buffer, with instant seek drain

Data is buffered ahead of the player in RAM only. When the buffer is full, upstream fetching pauses (backpressure). On a seek the socket closes, an abort event stops the worker, queued blocks are drained, and prefetching restarts at the new offset.

prefetched in RAMplayback cursornot fetched

Memory presets

Set ZIPSTREAM_PREFETCH_MB (README presets):

SystemValueForward buffer
4 GB RAM (Raspberry Pi, small VPS)64~30-60 sec
8-16 GB RAM (desktop, laptop)1024~2-5 min
32 GB+ and gigabit fiber5120~10-25 min
engine.py

Byte-range proxy

ZIP/ZIP64 tail parser, RemoteZipReader, StreamPrefetcher and stream metrics.

server.py

Streaming server

Threaded HTTP server on port 8787 with CORS, the REST API and the web GUI at /.

player_detector.py

Players

Detects installed players (Windows Registry and PATH) and launches them with the stream URL.

/api/playlist.m3u

M3U / M3U8 export

An #EXTM3U playlist of the archive's video entries, each pointing at a /stream/ URL.

webdav_bridge.py

WebDAV bridge

RFC 4918 virtual filesystem at /webdav/ for Infuse, Kodi and Windows Explorer.

strm_generator.py

STRM bundles

Virtual .strm library ZIPs so Jellyfin, Emby and Kodi can index the archive.

static/web_gui.html

Web GUI

Dashboard, settings, history, live console and an HTML5 player with a codec/resolution badge.

subtitle_parser.py, media_inspector.py

Subtitles and inspection

.srt to WebVTT on the fly, plus binary media-header inspection (codecs, size, duration).

history.py, config.py, cli.py

State and CLI

SQLite history and bookmarks, JSON/env configuration, and the interactive terminal player.

Web GUI

Paste a link. Inspect. Watch.

Open http://127.0.0.1:8787, paste a direct ZIP URL and the dashboard lists every video with its compression method and offset. Play it in the browser or send it to your desktop player.

127.0.0.1:8787
ZipStreamHub web dashboard after inspecting a remote ZIP: archive size, video count, subtitle track count, Instant Seek mode, buttons for STRM library, WebDAV link and M3U playlist, and a list of three episodes with Watch in Browser and PotPlayer buttons.
The real dashboard inspecting a small demo ZIP (fictional test clips). Each entry shows STORE (0ms Seek) and its offset.
127.0.0.1:8787 · in-browser player
ZipStreamHub in-browser video player playing a test pattern with H.264, 1280x720 and duration badges, speed controls and a subtitle selector.
Built-in HTML5 player: codec and resolution badge, speed control, subtitles, keyboard shortcuts and a direct-link copy.

What the dashboard gives you

  • Live bandwidth, active streams and total transferred
  • Buffer and slice size settings you can change live
  • Live console of range requests and stream events
  • Recent archives with favourites
  • Filter by name, extension, STORE/DEFLATE or subtitled
  • WebDAV link, M3U download and STRM bundle buttons

Players and export

Your player, your playlist, your library

Every file in an inspected archive gets a stable stream URL: http://<host>:<port>/stream/<entry_id>/<url_encoded_filename>. Anything that can open an HTTP URL can play it.

  • PotPlayerWindows
  • MPVall
  • VLCall
  • MPC-HCWindows
  • MPC-BEWindows
  • IINAmacOS
  • Web browserbuilt-in
  • KodiM3U, WebDAV, STRM
  • InfuseM3U, WebDAV
  • Jellyfin / EmbySTRM

M3U / M3U8 export

/api/playlist.m3u
#EXTM3U
#EXT-X-VERSION:3
# PLAYLIST GENERATED BY ZIPSTREAM HUB (2 items)
#EXTINF:-1 tvg-name="Episode 01 - Pilot.mkv" group-title="ZipStream Hub",Episode 01 - Pilot.mkv
http://127.0.0.1:8787/stream/1/Episode%2001%20-%20Pilot.mkv
#EXTINF:-1 tvg-name="Episode 02 - The Journey.mkv" group-title="ZipStream Hub",Episode 02 - The Journey.mkv
http://127.0.0.1:8787/stream/2/Episode%2002%20-%20The%20Journey.mkv

Drop the file into a player, or serve the URL to IPTV-style apps. From the CLI: --export-m3u and --export-strm.

WebDAV and STRM

The WebDAV bridge at http://127.0.0.1:8787/webdav/ answers PROPFIND, GET, HEAD and OPTIONS, so the archive can be mounted as a network drive in Windows Explorer and used as a source in Infuse and Kodi, with transparent range streaming.

The /api/strm.zip endpoint generates a bundle of tiny .strm files (each holds one stream URL). Jellyfin, Emby and Kodi then scrape and play the videos from the remote archive with no video stored locally.

Subtitles: .srt tracks found in the archive are converted to WebVTT on the fly via /api/subtitle.

Quick start

From clone to first frame in three commands

Requires Python 3.9 or newer. A desktop player such as PotPlayer, MPV or VLC is optional (the browser player is built in).

1. Install

bash
git clone https://github.com/MHJoy99/ZipStream-Hub.git
cd ZipStream-Hub
pip install -e .
# or for development:
# pip install -e ".[dev]"

2. Web GUI dashboard

bash
zipstream-server
# or
python server.py

Open http://127.0.0.1:8787. On Windows you can also double-click launch_zipstream.vbs.

3. Or use the terminal player

bash
# Interactive episode browser
zipstream "https://example.com/anime_season1.zip"

# Non-interactive direct launch into PotPlayer
zipstream "https://example.com/anime_season1.zip" --ep 1 --player potplayer

Docker

bash
docker compose up -d

Tip: for instant seeking, the video ZIP must be packed with Store (no compression). See "Store vs. Deflate" above.

Configuration and API

Tune it live, or script it

Precedence is Environment variables > config.json > defaults. Everything is also editable from the Settings modal in the web UI.

Environment (.env.example)

.env
# high-speed 5 GB buffering on 32 GB+ RAM systems
export ZIPSTREAM_PREFETCH_MB=5120
export ZIPSTREAM_SLICE_KB=128
export ZIPSTREAM_HOST=0.0.0.0
export ZIPSTREAM_PORT=8787
export ZIPSTREAM_DEFAULT_PLAYER=potplayer
config.json
{
  "server":    { "host": "0.0.0.0", "port": 8787, "debug": false },
  "streaming": { "prefetch_buffer_size_mb": 1024, "slice_size_kb": 128,
                 "max_concurrent_streams": 8, "chunk_timeout_seconds": 30 },
  "players":   { "default_player": "potplayer" },
  "ui":        { "theme": "dark", "accent_color": "#3b82f6", "page_size": 50 }
}

Excerpt of the README example. Port conflict? Run python server.py --port 9090.

REST API

MethodEndpointWhat it does
POST/api/inspectInspect a remote ZIP URL, return the archive structure as JSON
GET/stream/<id>/<file>HTTP 206 byte-range stream
HEAD/stream/<id>/<file>Content-Length and MIME type
GET/api/playlist.m3uExport an #EXTM3U playlist
GET/api/strm.zipVirtual .strm library bundle
GET/api/subtitleSubtitle to WebVTT on the fly
GET/api/media_inspectCodec, size and duration from media headers
GET/api/statsBitrate, active streams, buffer, latency
POST/api/playLaunch the host media player with a stream URL
PROPFIND/webdav/RFC 4918 directory tree for Infuse, Kodi, Explorer

More endpoints (config, history, players) are in docs/API.md.

FAQ

Questions, answered from the docs

What is ZipStreamHub?

ZipStreamHub is an open-source streaming server and virtual archive extractor for remote ZIP and ZIP64 files. It lets media players such as PotPlayer, MPV, VLC and IINA, or a web browser, play and seek through video stored inside a ZIP archive on a web server, without downloading or unpacking the archive first.

How can it play a video without downloading the whole ZIP?

A ZIP keeps its index (the Central Directory) at the end of the file. ZipStreamHub uses HTTP Range requests to read only the last 1 MB, finds the End of Central Directory record and the ZIP64 locator, then reads the Central Directory to build an in-memory index. When a player asks for a byte range of a video, ZipStreamHub adds the entry's data offset and fetches just that range from the remote server, so only the parts you watch cross the network.

Why does seeking only work well on ZIPs packed with Store?

With the STORE method (method 0, no compression) byte X of the video corresponds 1:1 to byte data_offset + X in the ZIP, so a seek is a simple offset calculation. With DEFLATE the data is compressed, and the project's troubleshooting guide says seeking to an arbitrary byte can stutter or fail. Re-pack the archive with Store, for example 7z a -mx0 output.zip with your videos, and seeking becomes instant. Video files are usually already compressed, so a Store ZIP is about the same size.

Does it write the video to my disk?

No video data is written to disk. Streamed data passes through an in-memory sliding-window prefetch buffer (StreamPrefetcher) whose size you set with ZIPSTREAM_PREFETCH_MB. When you seek or stop, the old buffer is drained. The app does keep small local files of its own, such as config.json and an SQLite database for archive history and bookmarks.

What does the remote server need to support?

The URL must be a direct link to the ZIP file, and the server or CDN must honour HTTP Range requests (it must answer with 206 Partial Content and Accept-Ranges: bytes). HTML landing pages, confirmation pages and servers without Range support will fail with errors such as "Valid ZIP End of Central Directory record not found" or "Server does not support Range requests".

Which players and apps does it work with?

It auto-detects and launches PotPlayer, MPV, VLC, MPC-HC, MPC-BE and IINA, and it has a built-in HTML5 browser player. Any app that can open an HTTP URL works with the /stream endpoint. Playlists exported as M3U/M3U8 work in players such as Kodi, Infuse and VLC, the WebDAV bridge serves Infuse, Kodi and Windows Explorer, and STRM bundles let Jellyfin, Emby and Kodi index the archive as a virtual library.

How do I install and run it?

You need Python 3.9 or newer. Clone https://github.com/MHJoy99/ZipStream-Hub.git, run pip install -e . inside the folder, then start the server with zipstream-server (or python server.py) and open http://127.0.0.1:8787. From the terminal you can also run zipstream followed by the ZIP URL, or start it with docker compose up -d.

How much RAM should I give the buffer?

Set ZIPSTREAM_PREFETCH_MB. The README suggests 64 MB for 4 GB RAM systems (about 30 to 60 seconds of forward buffer), 1024 MB for 8 to 16 GB systems, and 5120 MB for 32 GB and gigabit-fiber machines. The precedence is environment variables, then config.json, then defaults.

Is ZipStreamHub free, and which platforms does it run on?

Yes. It is free and released under the MIT License. It is a Python 3.9+ package with urllib3 as its only dependency, and it runs on Windows, macOS and Linux, or as a Docker container. Use it only with archives you are entitled to access and respect the terms of the hosts and services involved.

Use responsibly. ZipStreamHub is a neutral streaming tool. Only stream archives you have the right to access, and respect the terms of service and rate limits of the hosts and CDNs you connect to.