Skip to main content

sestudio

Watch, cast and download series and films from several streaming sites — from one library, on your own machine.

License: MIT Python Run with uvx


Quick start

uvx sestudio

That is the whole install — uv brings its own Python, and ffmpeg ships in the wheel.

It prints two URLs:

URL Use it for
http://<host>:8080 Browsing, watching, downloading, DLNA
https://<host>:8443 Chromecast and AirPlay (they require HTTPS)

The server keeps running in the terminal — downloads continue even after you close the tab. Stop it with Ctrl-C.

[!WARNING] There is no authentication. The default bind (0.0.0.0) makes the UI reachable from every device on your network, which is what casting needs — run it only on a network you trust, or pass --host 127.0.0.1.


Contents


Why

Every streaming site carries a different slice of a title: one has the VF, one has the VOSTFR that came out yesterday, one is simply up today. sestudio treats them as one catalogue — it asks all of them, shows you what each has, and switches between them for you when the version you want lives somewhere else.


Features

Search across sites

One query hits every enabled site, and listings of the same title from different sites merge into one card. Sites can be turned off individually, and one can be preferred so its listing wins a merge.

Seasons fold into a single result carrying the season count — a long-running series takes one card, not twelve. The switch sits in the search toolbar and in Settings.

Every version, from every source

VF / VOSTFR / VO availability is collected from all the sites carrying a title, not just the one you happen to be on — so a VOSTFR only one site has is visible without going looking for it.

  • The playlist marks each episode with the versions that exist, struck through where none does, and lists episodes your language lacks instead of hiding them.
  • The sources panel shows every site as a row, with its versions and its hosts.
  • Clicking a version plays it, moving to the site that has it if needed.
  • When the open episode has nothing in the chosen language and another site does, it switches by itself — language first.

Watching

Streams play through the server, so host referer/TLS quirks are handled for you. Each episode is probed across every host it has, and a failing one falls back to the next.

  • The playlist shows the show's other seasons as a tree, loading only the one you open.
  • Watch state lives on the server — position, watched flags, next-up — so it follows you between devices.
  • Hosts carrying soft subtitles (vidzy, premium) hand them to the player as sidecar tracks. HLS alternate renditions (#EXT-X-MEDIA) are relayed untouched, so a multi-track stream exposes its menus by itself.

Downloading

To the server's disk, or straight to the device you are browsing from.

The order to try is yours. Rank sites and hosts by clicking them, in the download window or in Settings: ranked entries are tried first, unranked ones stay as fallback — an order changes what is used, never what is possible. The shipped order is:

senpai › premium › uqload › vidzy › netu › voe

Senpai leads because it serves its own files, so there is no third-party host to be down.

Files land in a per-language folder:

<output_root>/<Series Name>/Season 01/VOSTFR/S01E01 - Title.mp4
<output_root>/sestudio_films/VF/<Film Title>.mp4

The "already downloaded" check is per language, so the same episode in VF and in VOSTFR are tracked separately.

Your downloads, as a source

What is on disk is scanned into its own library and offered beside the streaming sites — it plays straight from the file, with no host to resolve. A file you placed by hand outside a language folder is listed under other rather than being ignored.

Casting

DLNA to any renderer on the network, plus Chromecast and AirPlay over HTTPS. The player hands off both ways: cast from the position you are at, or pull a cast back into the browser where the TV left off. See Casting setup.


Sources and hosts

Sites — where titles are found:

Site Id Notes
FStream fstream Live-action and anime
French-Manga french-manga Anime
Senpai Stream senpai Serves its own files — no third-party host to fail

Hosts — where a stream is actually resolved from:

Host Resolved by
uqload, vidzy, premium, netu, luluvid, filmoon, voe The shared provider registry
senpai The site itself, as pre-signed files

Running it

uvx sestudio                       # no install
uvx sestudio --https_port 9443     # same, on another port
uv sync && uv run sestudio         # from a source checkout

sestudio starts the web UI — it is the only command.

Flag Default Description
--host 0.0.0.0 Bind address (0.0.0.0 = reachable from your LAN)
--http_port 8080 HTTP port (browsing, DLNA, cast media fetch)
--https_port 8443 HTTPS port (Chromecast / AirPlay)
--no_http off Don't start the HTTP server
--no_https off Don't start the HTTPS server
--no_resolve off Skip live-domain auto-resolution
-v, --verbose off Debug logging

Where it keeps things

Everything lives in ~/.config/sestudio/:

File Contents
config.json Output folder, language, download destination, site and host ranking, TMDB key
library.db SQLite: watch state, watchlist and favourites, the downloaded-file manifest
tmdb_cache.json Metadata lookups, misses included
cert.pem The self-signed HTTPS certificate

[!NOTE] Metadata — posters, ratings, synopses, cast and a "Trending this week" row come from themoviedb.org. Released builds ship a default key, so uvx sestudio works with no setup. To use your own, set TMDB_API_KEY in the environment or save it in Settings; either takes precedence. Keys are free. The key is never sent to the browser, and builds from a source checkout have no default key — enrichment stays off there until you supply one.


Casting setup

Start the server so other devices can reach it (this is the default):

uvx sestudio --host 0.0.0.0
Target Needs Notes
DLNA Plain HTTP The ⧉ button runs a ~4 s SSDP scan, then pushes the stream to the renderer you pick. Works with no extra flags.
Chromecast / AirPlay HTTPS The Cast Web Sender SDK refuses to run outside a secure context. Trust the certificate once, below.

HTTPS and the certificate

sestudio terminates HTTPS itself, with a self-signed certificate covering your LAN IP — no reverse proxy needed.

uvx sestudio                # both: http://…:8080 and https://…:8443
uvx sestudio --no_https     # HTTP only
uvx sestudio --no_http      # HTTPS only

Trust ~/.config/sestudio/cert.pem on whichever device uses the HTTPS site:

  • Desktop browser — open the URL and accept the warning, or import the cert into the system trust store.
  • Android phone driving the cast — install it as a user CA (Settings → Security → Encryption & credentials → Install a certificate → CA certificate). Until you do, the Cast button lists no devices.

From a phone over Tailscale

Casting needs the TV to reach the sestudio server. If you are away from home and reach the server over Tailscale, forward a port on the phone to the server's Tailscale address and browse to the phone's LAN address: the TV connects to the phone, and the phone forwards to the server.

  1. Be connected to Tailscale.
  2. Forward port 8443 from the phone's LAN IP to the server's Tailscale address.
  3. Open https://<phone-lan-ip>:8443 on the phone.

On Android, the phone's IP is behind a long press on the wifi widget, then the "advanced info" icon next to the network name.

[!TIP] Funnel avoids all of this — no port forwarding, no Tailscale on the phone or TV, and no self-signed cert.

From outside your network (Funnel)

Tailscale Funnel publishes a port to the public internet with a real, publicly-trusted certificate. Exposing the HTTPS port is enough; the HTTP port need not be public.

# On the server, once: enable the funnel node attribute in the tailnet ACL
#   "nodeAttrs": [{ "target": ["autogroup:member"], "attr": ["funnel"] }]

tailscale funnel --bg --https=8443 https+insecure://localhost:8443
tailscale funnel status          # prints the public URL

Then open https://<host>.<tailnet>.ts.net:8443 on the phone and cast as usual.

https+insecure:// is required because sestudio's own certificate is self-signed — it tells Funnel not to validate the upstream. Funnel presents its own valid certificate outward, so clients see a clean HTTPS site.

[!WARNING] Anyone with the URL can reach the server. There is no auth in front of it.

Things worth knowing before you fight it
  • Funnel only accepts public ports 443, 8443 and 10000. Nothing else.
  • tailscale funnel 8443 means public 443 → local 8443, not 8443 → 8443. Pass --https=8443 to preserve the port.
  • listener already exists for port 443 means that port has a serve (tailnet-only) entry. Inspect with tailscale serve status, clear it with tailscale serve --https=443 off. A port left as serve accepts the TCP connection from outside and then drops the TLS handshake — it looks like a broken certificate, but it is a missing funnel.
  • Without --bg the proxy is foreground-only and dies with the command.
The public name does not resolve

Check it against more than one resolver before assuming the setup is wrong:

nslookup <host>.<tailnet>.ts.net 8.8.8.8
nslookup <host>.<tailnet>.ts.net 1.1.1.1

A resolver queried before the funnel existed can cache the NXDOMAIN for its negative TTL while others answer correctly — 1.1.1.1 has been observed doing this for well over an hour. Purge it at https://1.1.1.1/purge-cache/ (A and AAAA records), or use another resolver meanwhile.


Requirements

uv — that is the only thing you install. Everything else is bundled: uv provides Python (3.11 or 3.12), and yt-dlp plus a static ffmpeg ship as dependencies. A system ffmpeg on PATH is preferred when present, giving a real ffprobe and fuller codecs. Node 18+ is only needed to rebuild the frontend during development.

The wheel bundles a static ffmpeg, so it targets the platforms imageio-ffmpeg ships wheels for:

OS Arch
Linux x86_64, arm64
macOS arm64
Windows x86_64

Development

uv run pytest                      # backend tests
uv run sestudio --no_https         # backend for the dev proxy, HTTP on 8080

cd frontend
npm install
npm run dev                        # dev server, proxies /api to localhost:8080
npm run lint && npm test           # frontend checks
npm run build                      # commit the result — frontend/dist ships in the wheel

Adding a site means implementing ContentSite in src/sestudio/sites/ and registering it in build_sites(). Everything downstream — search, merging, language detection, playback, downloads — works off that interface.


MIT licensed · LICENSE

Metadata

Release files for sestudio 0.15.0

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

Source distribution (sdist)

Source distribution for sestudio 0.15.0
File Size Uploaded
sestudio-0.15.0.tar.gz 38.5 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for sestudio 0.15.0
File Interpreter ABI Platform
sestudio-0.15.0-py3-none-any.whl Python 3 none any Details

Total release size: 38.9 MB

Release files / sestudio-0.15.0.tar.gz

Download URL sestudio-0.15.0.tar.gz
Size 38.5 MB
Tags Source
SHA-256 checksum
How to use checksums
85f2de3ff29edce1fe451ee9c119168d11ee5eaf5d09fa00caced0252213efe0
BLAKE2b-256 checksum
How to use checksums
230c7e18f212c1e5f516c3c00f575dabe85b905cbe960ac5d0251fff410fe9e3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / sestudio-0.15.0-py3-none-any.whl

Download URL sestudio-0.15.0-py3-none-any.whl
Size 416.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
17a42d36358a493f060822ace34e2aaebfa4a653a02b03b35fdff05548bc2c6b
BLAKE2b-256 checksum
How to use checksums
8bf49bb087773844b01433f4e10ceee52b3e4786c3fe7868a5faeebb5ca846ee
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

0.16.3

2 release files

0.16.2

2 release files

0.16.1

2 release files

0.16.0

2 release files

This release

0.15.0 This release

2 release files

0.14.0

2 release files

0.13.1

2 release files

0.9.0

2 release files

0.8.0

2 release files

0.7.1

2 release files

0.7.0

2 release files

0.6.2

2 release files

0.6.1

2 release files

0.6.0

2 release files

0.5.0

2 release files

0.4.1

2 release files

0.4.0

2 release files

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