Obzorarr

Obzorarr

Docker app from grtgbln's Repository

Overview

Obzorarr is a "Wrapped for Plex" application that syncs viewing history from your Plex Media Server and generates yearly statistics with an animated slideshow presentation - similar to Spotify Wrapped. It doesn't require Tautulli; it only relies on the Plex API.

Obzorarr Logo

Obzorarr

Year in Review for Plex Media Server

License Bun SvelteKit TypeScript SQLite Ask DeepWiki


What is Obzorarr?

Obzorarr is a "Wrapped for Plex" application that syncs viewing history from your Plex Media Server and generates yearly statistics with an animated slideshow presentation - similar to Spotify Wrapped. It doesn't require Tautulli; it only relies on the Plex API.

Features

Obzorarr Wrapped story mode

  • Yearly Statistics — Total watch time, top movies, shows, and genres
  • 19 Slide Types — From watch streaks and binge sessions to decade breakdowns and series completion
  • Two Ways to Watch — An animated story-mode slideshow, or a scrollable single-page recap
  • Watch Patterns — Monthly, hourly, and weekday distribution charts
  • Percentile Rankings — See how you compare to other users on your server
  • Server Wrapped — A server-wide recap with a top-viewers leaderboard
  • Privacy Modes — Real, hybrid, or fully anonymous names, with one-click privacy presets
  • Five Themes — UI and Wrapped themes are chosen independently
  • Slide Editor — Reorder, enable, or disable slides and add your own custom ones
  • Plex OAuth — Secure authentication with your Plex account
  • Automatic Sync — Scheduled background sync of viewing history, with live progress
  • Reverse-Proxy Diagnostic — Compares what your browser sees, what the proxy forwards, and what Obzorarr uses
  • AI Fun Facts — Optional AI-written fun facts, with the built-in templates as the fallback

Issues & Support

Found a bug or have a feature request? Please submit issues and feature requests to the obzorarr-docker repository rather than this repository. This ensures your report reaches the maintainers monitoring issue tracking across the project.

Screenshots

Every screenshot below is captured from a running instance. Usernames are rendered by Obzorarr's own anonymisation mode, and server addresses are demo values.

Onboarding

First run walks through seven steps: Claim → Security → Reverse proxy → Connect → Sync → Configure → Done.

Obzorarr onboarding wizard

Claim setup CSRF origin
Claim setup step CSRF origin step
Reverse-proxy trust Proxy diagnostic
Reverse proxy trust step Proxy diagnostic evidence
Server picker Connection choice
Plex server picker Plex connection choice
Connected Sync in progress
Plex server connected First sync running
Sync complete Choose slides
First sync complete Slide selection
Pick a theme Setup complete
Theme selection Setup complete
Admin

Obzorarr admin panel

Dashboard Wrapped overview Slide editor
Admin dashboard Wrapped overview Slide order editor
Sync (idle) Sync running Users
Sync command centre Sync running with live progress User management
Live logs Settings Connections
Live log stream Settings index Plex connection settings
Appearance Privacy Security
Appearance settings Privacy settings Security settings
Data System
Data settings System settings
Your Wrapped

Story mode plays the slides one at a time; scroll mode puts the whole recap on a single page.

Total time Top movies Top shows
Total watch time Top movies Top shows
Genres Viewing patterns Weekday patterns
Favourite genres Monthly and hourly distribution Weekday patterns
Movies vs shows By decade Series completion
Content type split Release decade breakdown Series completion
Rewatches Marathon day Longest streak
Most rewatched Biggest marathon day Longest watch streak
Year comparison Percentile Binge sessions
Year-over-year comparison Percentile ranking Binge sessions
First and last Fun fact Summary
First and last watch of the year Fun fact slide Wrapped summary

Scroll mode — the same recap as one continuous page:

Scroll mode (top) Scroll mode (further down)
Scroll mode top Scroll mode further down

On a phone — the Wrapped experience is built portrait-first:

Mobile Wrapped slide Mobile Wrapped slide Mobile Wrapped slide

Themes

Five presets ship with Obzorarr. The admin UI theme and the Wrapped theme are set independently under Admin → Settings → Appearance, so the panel you work in and the recap your users see do not have to match.

UI themes — the admin dashboard in each preset:

Admin UI in all five themes

Wrapped themes — the same slide in each preset:

Wrapped slide in all five themes

Sharing & privacy

Names shown below come from Obzorarr's anonymous privacy mode, which renders every user as User #1, User #2, and so on. Real and hybrid (you see your own name, everyone else is anonymised) are the other options — see Admin → Settings → Privacy.

Share modal Public Wrapped
Share modal with share link Public Wrapped seen by a visitor
Server Wrapped Top viewers
Server-wide Wrapped Top viewers leaderboard, anonymised
For your users
Landing page Dashboard Sharing preferences
Landing page User dashboard User sharing preferences

Tech Stack

Component Technology
Runtime Bun
Framework SvelteKit + Svelte 5
Database SQLite (Drizzle ORM)
Styling UnoCSS + shadcn-svelte
Animation GSAP + Motion

Quick Start

Docker (Recommended) — Image Repo

services:
  obzorarr:
    container_name: obzorarr
    image: ghcr.io/engels74/obzorarr-docker
    ports:
      - 3000:3000
    environment:
      - PUID=1000
      - PGID=1000
      - UMASK=002
      - TZ=Etc/UTC
      # Optional: lock Plex connection at the env layer. You can also leave
      # these unset and configure the server from the admin UI after onboarding.
      # - PLEX_SERVER_URL=http://plex-url-here:32400
      # - PLEX_TOKEN=your-plex-token-here
    volumes:
      - /<host_folder_config>:/config

Replace /<host_folder_config> with your desired config path. Access the web UI at http://localhost:3000 to complete setup.

From Source

git clone https://github.com/engels74/obzorarr.git
cd obzorarr
cp .env.example .env
bun install
bun run dev

Note on .env in local dev. bun run dev does not auto-load .env, so any PLEX_SERVER_URL / OPENAI_* values you put there are ignored — local dev configures the server through onboarding and the admin UI (values stored in the SQLite DB). Environment-variable precedence (and the "Locked by environment variable" UI) applies to Docker/production, where the container passes the vars into the process. To exercise env-precedence locally (e.g. to see an env-locked field render its ENV badge), run bun run dev:env, which loads .env via --env-file.

When PLEX_SERVER_URL and PLEX_TOKEN come from the environment, onboarding and Admin → Settings → Connections show them as read-only with an ENV badge — the value is owned by your container config, not the database:

Connect step with server URL and token locked by environment variables

First-Time Setup

The first time you open the web UI, Obzorarr runs a short onboarding wizard: Claim → Security → Reverse proxy → Connect → Sync → Configure.

The claim token

So nobody can grab your fresh install before you do, the first step asks for a one-time bootstrap token. Obzorarr prints it to the server console — on a fresh install it is never shown in the browser:

Obzorarr initial setup requires a bootstrap claim.
Setup URL: http://localhost:3000/onboarding/claim
Bootstrap token: xxxx-xxxx-xxxx

With Docker, read it from the container logs:

docker logs obzorarr

The token expires after 15 minutes and only one browser can hold the claim at a time. If it lapses, restart Obzorarr to print a new one.

Claim setup step asking for the bootstrap token

The remaining steps connect your Plex server (or confirm the values you set via PLEX_SERVER_URL / PLEX_TOKEN), run the first history sync, and let you choose which slides users see. Anything set here can be changed later under Admin → Settings.

Starting over

Admin → Settings → Data has a Danger zone with Reset instance, which deletes everything Obzorarr has stored and drops you back at the claim screen, signed out. Before wiping, it shows you a fresh claim token to paste on the next screen — that one lasts 60 minutes, since you have to sign in to Plex, reconfigure, and sync again. It is also printed to the console as usual, so losing the tab is recoverable.

Your watch statistics come back: they re-sync from Plex. Everything else does not. That covers all settings, every per-user share setting, and every share link you have already handed out stops working, along with any manual curation and the log history. Anything configured through environment variables (Plex, OpenAI, ORIGIN, TRUST_PROXY, TZ) is not in the database, so it survives and the new setup arrives partly pre-filled. Obzorarr refuses to reset while a sync is running.

Scheduled Syncs and Time Zones

Admin → Sync holds the automatic sync schedule as a cron expression. The schedule survives restarts: Obzorarr stores the expression and whether you left the scheduler running, paused, or stopped, and rebuilds the job on the next boot.

Cron expressions are interpreted in the configured timezone, which also drives the nightly log retention cleanup. Obzorarr resolves it in this order:

  1. the TZ environment variable, when it names a zone the runtime knows (TZ=Europe/Copenhagen);
  2. the timezone saved under Admin → Settings → System;
  3. UTC.

As with every other environment-backed setting, TZ wins: the field renders read-only with an ENV badge, and a database value it shadows is dropped at startup. A TZ the runtime cannot resolve is ignored rather than applied, so a typo leaves the admin field editable instead of scheduling syncs in an unknown zone. Fixed offsets such as +02:00 are rejected for the same reason a DST-aware zone is wanted here: 0 0 * * * should mean local midnight all year.

Running Behind a Reverse Proxy

Obzorarr needs to know the address your browser uses, not the internal one it listens on. Otherwise login redirects, share links, and CSRF checks get built from the wrong hostname.

Set ORIGIN to your public URL, including the port if it isn't 80 or 443:

ORIGIN=https://obzorarr.example.com

That covers most setups. TRUST_PROXY is a separate, optional switch: when enabled, Obzorarr takes the hostname and protocol from the X-Forwarded-Host and X-Forwarded-Proto headers your proxy sends instead. Only turn it on when both of these are true:

  • Obzorarr can only be reached through the proxy — nothing can hit it directly.
  • Your proxy sets both headers itself, overwriting whatever a visitor sends.

If either is false, a visitor can forge those headers and make Obzorarr build links pointing at a domain they control. When in doubt, leave TRUST_PROXY off and rely on ORIGIN.

Onboarding and Admin → Settings → Security include a diagnostic that compares what your browser sees, what the proxy forwards, and what Obzorarr actually uses, with hints for Caddy, Nginx, Nginx Proxy Manager, and Apache. Changing either variable through the environment requires a restart. (Client-IP detection is configured separately, via the Bun adapter's ADDRESS_HEADER and XFF_DEPTH.)

Reverse-proxy step Technical evidence
Reverse proxy trust step Reverse proxy diagnostic evidence and repair guides

How Plex Users Are Matched

Plex watch history records a server-local account ID rather than a global Plex identity, so on every sync Obzorarr rebuilds the mapping between those local IDs and real Plex users by comparing your server's account list with the users you've shared the server with. In practice:

  • You, the server owner, are always local account 1. Never edit account IDs by hand.
  • A user only gets a Wrapped once their share is confirmed. If the check comes back incomplete (Plex unreachable, partial response), Obzorarr keeps the previous mapping instead of guessing.
  • Un-sharing removes access — that user's public Wrapped link stops resolving. Re-share and run a sync to restore it.
  • Mappings go stale after 24 hours and are re-proved by the next sync. They also reset whenever the Plex URL or token changes — back up your database before changing either, then restart and run a normal sync.

Public Wrapped links deliberately return the same "not found" response for an unknown user, a private profile, and a stale mapping, so the page can't be used to discover who has an account on your server.

Whether real usernames appear at all is a separate setting. Admin → Settings → Privacy offers five presets — from Maximum Privacy (members-only, anonymous names) to Public Showcase (public recap, real names) — plus a Custom card that lights up once you change anything underneath, and a Names in stats control with Real, Anonymous (User #1, User #2, …), and Hybrid (you see your own name, everyone else is anonymised).

Privacy settings with presets and a before/after preview

License

This project is licensed under the GNU Affero General Public License v3.0.

Media gallery

1 / 13

Install Obzorarr on Unraid in a few clicks.

Find Obzorarr in Community Apps on your Unraid server, review the template, and click Install. Unraid handles the Docker app or plugin setup from the published template.

Open the Apps tab on your Unraid server Search Community Apps for Obzorarr Review the template variables and paths Click Install

Requirements


        Requires a separate Plex instance.
    

Details

Repository
ghcr.io/engels74/obzorarr-docker:latest
Last Updated2026-08-30
First Seen2026-03-01

Runtime arguments

Web UI
http://[IP]:[PORT:3000]/
Network
bridge
Privileged
false

Template configuration

Web UI PortPorttcp

Container Port: 3000

Target
3000
Default
3000
Value
3000
Plex Server URLVariable

URL of your Plex Server (e.g., http://localhost:32400)

Target
PLEX_SERVER_URL
Plex TokenVariable

Plex token

Target
PLEX_TOKEN
Config DataPath

Path to the config data

Target
/config
Default
/mnt/user/appdata/obzorarr/config
Value
/mnt/user/appdata/obzorarr/config
PUIDVariable
Default
1000
Value
1000
PGIDVariable
Default
1000
Value
1000
UMASKVariable
Default
002
Value
002