Introduction

Jellything is a free server software to serve media like movies, videos and music through a website.

Source Code

Getting Started

Installation

From the AUR

This is the recommended option. It will also install a systemd service running in its own user.

git clone https://aur.archlinux.org/jellything-git.git
cd jellything-git
makepkg -si

From source

Requirements:

  • rustup
  • esbuild
  • nasm
  • meson
  • ninja
  • cmake
  • dav1d
  • ffmpeg (only if you use transcoding)

Jellything was only tested on x86_64-unknown-linux-gnu and aarch64-unknown-linux-gnu. Others might work.

git clone --recursive https://codeberg.org/metamuffin/jellything.git
cd jellything
cargo build --release
# Global installation
cp target/release/{jellything,jellytool} /usr/local/bin
# User installation
cp target/release/{jellything,jellytool} ~/.local/bin

Setup

1. Writing configurations

First write your configuration files whereever you want. The AUR package uses /etc/jellything.yaml.

# This hostname must be identical to how other instances reach you.
hostname: example.org
brand: "Jellything"
slogan: ""
admin_username: admin

# All of these paths can be customized. See "Paths"
media_path: "/srv/media"
asset_path: "/var/lib/jellything/assets"
database_path: "/var/lib/jellything/db"
library_path: "/var/lib/jellything/library"
temp_path: "/tmp/jellything"
cache_path: "/var/cache/jellything"
secrets_path: "/etc/jellysecrets.yaml" # points to the file below
# jellysecrets.yaml; filled with placeholders
admin_password: "xxxxxx"

# Both these keys should be initialized randomly.
# Use `head -c 32 /dev/random | base64`
cookie_key: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx="
session_key: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx="

# Credentials for remote instances. Keep this empty if you are just starting.
federation:
    "example.org": { username: "examplefed", password: "xxxxxxx" }

api:
    fanart_tv: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    tmdb: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    omdb: xxxxxxxx
    # This is the Trakt Application `client_id`
    trakt: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    tvdb: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

2. Creating required directories

Next create directories in place of cache_path, temp_path, media_path, library_path if jellything is not permitted to do so itself. Also obtain the default assets from git.

mkdir -p $cache_path $temp_path $media_path $library_path
git clone https://codeberg.org/metamuffin/jellything-assets.git $asset_path

3. Preparing your first import

In library_path, create files like below. This file provides an entry point to your library. Its exact meaning is described in The Import Guide.

# root.yaml
id: library
sources:
  - !override
    public:
      kind: !collection
      title: "My Library"
      children:
        - movies
# movies/directory.yaml
id: movies
sources:
  - !override
    public:
      kind: !collection
      title: "Movies"
  - !auto_children

Next place your favorite movies in media_path and use the import helper to quickly generate import instructions for it. This requires at least Trakt and TMDB keys to work.

# This should be run from media_path since the path in -m is relative to that no matter PWD.
# Jellytool will show an interactive wizard to select the correct metadata.
jellytool add -m big-buck-bunny.mkv
jellytool add -m agent-327-operation-barbershop.mkv
jellytool add -m spring.mkv

Launch your Platform

[!TIP] If you used the AUR package, this should be as simple as systemctl start jellything.

For other installations, run the jellything binary with the configuration file's path as its first argument or set it in JELLYTHING_CONFIG. Jellything will start and serve your library at https://127.0.0.1:8080/

jellything /etc/jellything.yaml
JELLYTHING_CONFIG=/etc/jellything.yaml jellything

It is also advised to use jellything with a reverse proxy. Configure the network interface with the environment variables BIND_ADDR and PORT. Stderr logging can be configured with LOG.

Migrating the Database

With some updates the database serialization for changes. This requires a migration process. You need to follow this procedure either with every update.

1. Export the database

Use the jellytool of the previous version to export the database to JSON.

mv /path/to/db /path/to/db.old # Rename the DB to avoid conflict later
jellytool.old migrate export /path/to/db.old export /tmp/jdb

2. Run migrations on the JSON dump

This is not implemented yet. It usually just works without anyway.

3. Import the database

Now import your library back to where it usually lives using the jellytool of the current version.

jellytool migrate export /path/to/db import /tmp/jdb

4. Delete old Databases

Delete the old database and the JSON dump, they are not required anymore.

[!CAUTION] Confirm that everything still works and no data ist lost

rm /path/to/db.old
rm -r /tmp/jdb

Paths

  • media_path: All your media (videos, movies, etc.) lives here. Jellything will only require read-only access to this directory.
  • asset_path: Static assets for the page. This includes fallback images, fonts and the front page. Used read-only.
    • error.avif: Used when images cant be displayed because of an error. This image is not required to be AVIF despite the name extension.
    • fallback-<kind>.avif: Fallback image when there is no asset available. AVIF also not required. <kind> is a node kind or "Person".
    • front.htm: Contents of the front page. The typical jellything page scaffold is placed around it.
    • logo.svg: Logo of the platform to replace the text in the top left of every page.
    • fonts/material-icons.woff2 Material Icons Font. Get this from online.
  • database_path: The database stores all imported nodes, user accound and such. Write access required.
  • library_path: This directory structure contains instructions for importing your media.
  • temp_path: Where to place short-lived files. Write access required. Can be in volatile memory (e.g. /tmp).
  • cache_path: In here jellything will place dozens of transcoded imagery, saved api responses and media metadata. This folder will likely be the biggest. Write access required.
  • secrets_path: Path to the secrets YAML file. Read access required. Should ideally not be readable by anybody else than jellything.

Jellything's Import System

In normal operation, jellything serves all metadata from only the database.

Whenever you want to change this metadata, you initiate a Reimport from the admin panel. This will follow the import instructions in library_path and regenerate that part of the database from scratch.

The Import Procedure

library_path is scanned recursively to locate all files that match *.yaml (YAML import options) or *.jelly (JSON import options). Each of these files contains a key id that specifies which node is primarily affected and an array sources which lists metadata providers. When all these files have been evaluated, each node's children are traversed recursively to generate their paths. The imported libraries structure is a directed acyclic graph (DAG) - nodes can have multiple parents.

The Sources are applied in-order to that node with decreasing priority. This process merges all aquired metadata into single nodes.

Metadata Providers

override

Allows for manual hardcoded metadata. This an instance of Node.

Special to this option are the poster and backdrop properties in the private section. These are reference images local to library_path and directly set the counterparts in public.

Example

id: library
sources:
  - !override
    public:
      kind: !collection
      title: "My Library"
      children:
        - movies
        - stuff
    private:
      poster: library.png

auto_children

Scans the directory of the file currently processed for *.yaml and .jelly, parses their ID and adds them as children.

media

This references a media file on disk and links it to this node. Additionally it extracts matroska metadata aswell.

[!NOTE] If path is a directory, then these options are applied recursively all files inside, automatically creating new nodes based on inferred IDs and linking them up.

Example

id: decay-2012
sources:
- !media
  path: /movies/decay.webm
  ignore_metadata: true
  ignore_attachments: false
  ignore_chapters: false

trakt

The recommended provider for any movie, show or season. It automatically queries other linked APIs.

Example

id: decay-2012
sources:
- !trakt
  kind: movie
  id: 98052

tmdb

Just like trakt but uses only TMDB instead.

federated

Recursively federates a node with the same ID from another instance. Requires valid credentials in the seecrets file.

id: movies
sources:
- !federated
  host: jellything.example.org

Jellything Rust API

For making your own applications that implement client functionality, use the jellyclient crate. The jellycommon crate exposes commonly used structs like those used in the library and for jhls.

Generated Documentation

Jellything HTTP API

Most endpoints require the Accept header to be present and set to application/json and image/avif respectively. Any endpoint returning JSON, will report errors with an object containing error string in the error key. Routes marked with * require authentification.

The jellyclient crate already implements most API functionality. The jellycommon crate provides useful structs for deserializing data (also reexported in jellyclient).

# Cargo.toml
[depedencies]
jellyclient = { git = "https://codeberg.org/metamuffin/jellything.git" }

General

GET /api/version

Returns API version number.

POST /api/create_session

Request body contains JSON with keys username, password, expire (in seconds) and drop_permissions (a list of permissions, that this session cannot use). The Response contains the session cookie as a string in JSON.

GET* /n/<id>

Request a library node (either a directory or item). Returns it as NodePublic.

GET* /n/<id>/extended

Request extended informationf for library node. Returns it as ExtendedNode.

Assets

All asset endpoints redirect to the asset that you need. Returned images are coded with AVIF. The width parameter is the width of the resolution you want to image to be.

[!WARNING] The actual returned resolution must not be exactly what you requested.

GET* /n/<id>/asset?<role>&<width>

Where role is one of backdrop or poster and

GET* /n/<id>/thumbnail?<t>&<width>

Returns a single frame from some track video of the media at a given time t.

GET* /n/<id>/person/<index>/asset?<group>&<width>

Returns headshot of a person from that node.

Stream

GET* /stream/<id>?<format>&<index>&<profile>&<index>&<tracks>&<webm>

Responds with the stream directly or a redirect to the actual source in case of federation.