No description
  • Dockerfile 50.9%
  • Shell 49.1%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-10-14 14:30:27 -07:00
backend docs[backend]: update latest tag to 25.9.26 2025-09-26 12:01:32 -07:00
frontend docs[frontend]: add readme for docker image 2025-09-18 09:49:39 -07:00
secrets.example initial commit, with some examples 2025-09-15 13:00:32 -07:00
tools docs[tools]: add standard oci labels to dockerfile 2025-09-18 09:36:38 -07:00
.env.example docs[example]: change .env.example domain to localhost, simplify library path 2025-09-17 13:56:36 -07:00
.gitignore initial commit, with some examples 2025-09-15 13:00:32 -07:00
docker-compose.yaml tweak: use simplified image refs for docker-compose 2025-09-18 09:53:29 -07:00
LICENSE rebrand Methadox as CompuVision 2026-10-13 14:51:22 -07:00
README.md update private hostname with public one 2026-10-14 14:30:27 -07:00

Quickstart

stars open issues open pull requests

CompuVision is a private, self-hosted video streaming system for your movies and shows.

Features

  • responsive design suitable for both mobile devices and tv screens
  • install as an app for platform-native feel
  • use your mobile device as a tv remote control
  • minimal and lightweight software that runs nicely on cheap low-end hardware
  • designed with accessibility in mind
  • ready for localization, with externalized locale strings and rtl-compatible styles
  • fully private and self-hosted, with no runtime dependencies on third-party services
  • automatic metadata enrichment from imdb, thetvdb, and themoviedb
  • your user data (saved items, watch history) is never stored on the server
  • backup your user data
  • restore from a backup of your user data
  • sync your user data across all of your online devices
  • integrated authentication to protect library from unauthorized access
  • integrated rate limiting to defend from bots and other malicious actors
  • server has limited api with read-only library access

Installation

This repo helps you get up and running quickly using Docker Compose.

Have a Kubernetes cluster? You may want to check out the official Helm chart instead.

Prerequisites

  • Docker
  • At least one video file that you can play directly in a web browser
    • Recommended format:
      • Video Track: AV1 codec, WebM container
      • Audio Track: Opus codec, OGG container
      • Text Track: WebVTT (chapters, subtitles)

Ripping

MHES is designed for physical media (DVD, Blu-ray) collectors who want to make digital backups of their physical discs for their own personal convenience and enjoyment. We recommend MakeMKV for the initial task of extracting video files from each disc in your collection.

Disclaimer: We do not condone unauthorized redistribution of licensed content. The advice and instructions in this guide are intended for personal use only, and in no way intended to encourage piracy or theft of any kind. Please support the artists who create the content you enjoy and appreciate. We have the privilege -- not an entitlement -- of having access to quality entertainment.

MakeMKV is a GUI app, but it also has a CLI, makemkvcon, which can be used to automate the process of ripping a disc.

For example:

# Extract all titles from the disc in optical disc drive /dev/sr0 into directory ~/Videos
makemkvcon mkv dev:/dev/sr0 all ~/Videos

Ripping is tedious work for many reasons:

  • Most discs do not clearly label playlists, so identifying which playlist corresponds to which content involves some guesswork and manual validation.

  • Many discs use "copy protection" techniques such as fake playlists to foil automation attempts.

  • The slightest imperfection in a disc can cause an entire playlist to become unreadable, so the "defect rate" in discs, for ripping purposes, seems to be somewhere around 2-5%.

  • Only a few optical disc drives available on the market support ripping at speeds over 2x. The highest speeds for DVD seem to be around 12-16x, and for Blu-Ray around 8x.

  • Support for 4k UHD is rare.

Despite the tedium, the work can be highly rewarding, as your digital library grows over time and you hone your process.

Encoding

MHES does not support transcoding, so all media in your library needs to be encoded in advance. This is a feature, not a bug! It is what allows MHES to be so lightweight and to run on cheap hardware without a high-end GPU.

Depending on the size of your library, encoding can take up to several days or weeks to complete. However, the process can be completely automated and parallelized, and can run on virtually any hardware.

  • For speed, use a desktop PC with a GPU having hardware support for AV1 encoding and use hardware-accelerated AV1 codec (e.g: av1_nvenc). Typical encoding speed for an HD video is around 13x-20x.

  • For quality, use a desktop PC with a CPU having many cores and use an AV1 codec implemented in software (e.g: libaom-av1). Typical encoding speed for an HD video is around 3x-5x.

Note: A speed of 3x is based on the duration of the video. For example, a 30-minute video at 3x speed means it takes 10 minutes to encode.

We strongly encourage using ffmpeg directly. While there can be a learning curve for learning ffmpeg in general, it can help to have constraints.

Here's a sample command for encoding a video on a machine with an NVIDIA GPU that supports AV1 encoding via NVENC:

# Encode "input.mkv" to "output.webm"
ffmpeg -y -i "input.mkv" \
  -c:a libopus -vbr 1 -b:a 320000 \
  -c:v av1_nvenc -preset p7 -tune hq \
  "output.webm"

Environment

This repo contains a .env.example file containing some environment variables expected by the Docker Compose configuration.

Create a .env file and set the following environment variables:

  • DOMAIN - the domain name at which you want to access this media server
  • LIBRARY_DIR - the base directory where all your media library's video files are stored

Organizing your library

This app expects your library to be organized in a specific manner. The convention is loosely based on Jellyfin's recommendations for Movies and Shows.

  • /path/to/library
    • /Movies
      • /{ALIAS} [imdbid-{TCONST}] - where {ALIAS} is typically something close to {TITLE} ({YEAR}), and {TCONST} is the movie's IMDb ID. Typically used if the title in IMDb contains special characters that cannot be used in a directory name (e.g: "Self/less (2015)").
      • /{TITLE}} ({YEAR}) - where {TITLE} is the exact movie title as listed on IMDb and {YEAR} is the movie's year listed on IMDb.
        • /{TITLE} ({YEAR}).webm - the default edition to play for this movie.
        • /{TITLE} ({YEAR}).part{PART}.webm - where {PART} is a positive integer indicating which part of a multi-part movie this source file represents.
        • /{TITLE} ({YEAR}) - {EDITION}.webm - an alternative {EDITION} of the movie (e.g: "Extended Cut").
        • /{TITLE} ({YEAR}).chapters.{LANGUAGE}.vtt - chapter markers for this movie, where {LANGUAGE} is the two-character ISO 639 language code. Optional.
        • /{TITLE} ({YEAR}).subtitles.{LANGUAGE}.vtt - subtitles for this movie, where {LANGUAGE} is the two-character ISO 639 language code. Optional.
    • /Shows
      • /{ALIAS} [imdbid-{TCONST}] - where {ALIAS} is typically something close to {TITLE} ({YEAR}), and {TCONST} is the show's IMDb ID. Typically used if the title in IMDb contains special characters that cannot be used in a directory name (e.g: "20/20 (1978)")
      • /{TITLE} ({YEAR}) - where {TITLE} is the exact show title as listed on IMDb and {YEAR} is the show's year listed on IMDb.
        • /Season {SEASON} - where {SEASON} is the two-digit season number (e.g: "01").
          • {TITLE} S{SEASON}E{EPISODE}.webm - the default edition to play for this episode, where {EPISODE} is the two-digit episode number (e.g: "01").
          • {TITLE} S{SEASON}E{EPISODE}.part{PART}.webm - where {PART} is a positive integer indicating which part of a multi-part episode this source file represents.
          • {TITLE} S{SEASON}E{EPISODE} - {EDITION}.webm - an alternative {EDITION} of the episode (e.g: "Extended Cut").

Secrets

This repo contains a secrets.example directory containing several empty files. This directory is a reference for what your secrets directory should look like.

Here's a checklist of all the secrets you will need to collect:

  • thetvdb-api-key - required during library scans to import some metadata and images
  • tmdb-access-token - required during library scans to import some metadata and images
  • tls.crt/tls.key - required to access the web app
  • jwt-signing-key.json - required for authentication
  • credentials.json - required for authentication

Create a secrets directory, and follow the instructions below for each file:

TheTVDB API Key (secrets/thetvdb-api-key)

TheTVDB provides metadata and images for movies, shows, and episodes in your library. An API key is necessary in order to pull these details while performing a library scan. This API key is free, but requires the creation of a TheTVDB account (also free).

TheMovieDB Access Token (secrets/tmdb-access-token)

TheMovieDB provides metadata and images for movies, shows, and people. An access token is necessary in order to pull these details while performing a library scan. This access token is free, but requires the creation of a TheMovieDB account (also free).

TLS Certificate & Key (secrets/tls.crt and secrets/tls.key)

This app must be served via HTTPS, with a TLS certificate trusted by the browser.

The general process of getting a TLS certificate goes something like this:

  • Register the domain name you'd like to use for this app.
  • Add a DNS record to route that domain name to the public-facing IP address of the server on which you'll be hosting this app.
  • Use a service like Let's Encrypt to verify ownership of the domain and obtain a TLS certificate.
  • Occasionally repeat the previous step to renew the certificate (typically every 3-4 months).

Advanced: You may use a self-signed certificate or a certificate issued by a private authority, as long as you are able to install the signing CA certificate as a trust anchor in each browser through which you intend to access this app.

Here's one way to generate a self-signed certificate for local testing:

docker run -it --rm \
  -v "${PWD}/secrets":/secrets:rw \
  -u "$(id -u):$(id -g)" \
  CompuVision/tools \
  generate tlsCertificate /secrets localhost

JWT Signing Key (secrets/jwt-signing-key.json)

Authentication involves the exchange of a username/passphrase pair for a cryptographically signed access token. A signing key is required in order to issue this token.

If you do not provide your own signing key, then the app will generate one for you. However, because this happens each time the app launches, users will need to re-authenticate any time you restart the app.

To generate a new signing key, run the following command:

docker run --rm \
  CompuVision/tools \
  generate jwtSigningKey

Credentials (secrets/credentials.json)

Authentication involves the exchange of a username/passphrase pair for a cryptographically signed access token. A set of username/passphrase mappings is necessary in order to limit access only to pre-authorized users.

This file is a simple JSON object whose keys are usernames and whose values are Argon2id hashes of the corresponding passphrase.

For example:

{
  "alice@example.com": "$argon2id$v=19$m=65536,t=3,p=..."
}

To generate a Argon2id hash for a given passphrase (in this example, the passphrase is hello world), run the following command:

docker run --rm \
  CompuVision/tools \
  generate passphraseHash "hello world"

Running

With all prerequisites met, you can start the server with a simple Docker Compose command:

docker compose up --build -d

Troubleshooting

If you run into permissions issues for accessing secrets, then you may need to change the ownership to uid 902:

chown 902 secrets/*

Contributing

There are several ways you can contribute to improving this project:

  • Report an issue - if you're having trouble, or see something that could be improved, writing up an issue can help us know what may need to be fixed or changed
  • Improve the documentation - is there something missing in the docs? find a typo? need clarification?

This project is just getting started, so there are still some details to iron out before we begin accepting code contributions.

Security

Security researchers are welcome. Of course, please abide by standard etiquette and request permission prior to engaging in potentially disruptive activities like penetration tests and active scans.

While this is a non-commercial project and there is no monetary reward for reporting issues, non-monetary rewards may include gratitude, acknowledgment in this README, and general goodwill.

To report a security issue or vulnerability, email methadox.sh66t@passmail.net to coordinate on establishing a secure channel for communication.

License

CompuVision © 2025 by Devin Canterberry is licensed under CC BY-NC-SA 4.0.

This project may not be used, in whole or in part, for AI training.