ticketarr

ticketarr

Docker app from jivandabeast's Repository

Overview

Watches an email inbox for AMC A-List and Regal Unlimited movie ticket confirmations, resolves the movie on TMDB, then scrobbles it to your tracker (Trakt, Ryot, or Yamtrack) with the correct showtime and (optionally) requests it in Jellyseerr/Overseerr or Ombi. Headless - no WebUI, only a private /healthz endpoint for Docker healthchecks.

ticketarr

ticketarr

A tiny, headless Python service that watches an email inbox for AMC A-List and Regal Unlimited reservation / cancellation emails and mirrors them to your movie tracker (Trakt, Ryot, or Yamtrack). Optionally submits a request for the same movie to Jellyseerr/Overseerr ("Seerr") or Ombi.

Support for additional theater chains is designed to drop in as a single new parser module — see AGENTS.md.

No UI. No public endpoints. Just a docker-native background worker.

How it works

  1. Log into your IMAP inbox and look for messages from any registered parser's canonical sender addresses. Out of the box:
    • AMC A-ListAMCTheatres@amctheatres.com
    • Regal Unlimitedtickets@regaltickets.com (reservations), noreply@regaltickets.com (refunds)
  2. Classify each message (reservation / cancellation / other).
  3. Parse the movie title, theater, showtime, and order/booking number. Regal reservation emails encode the real theater + showtime inside an inline JPEG, so ticketarr OCRs the ticket with Tesseract (bundled in the Docker image) and falls back to email-received time only when OCR fails.
  4. Resolve the movie to a TMDB id.
  5. Report the movie to the configured tracker with the showtime as the watched-at timestamp. On a cancellation, remove the corresponding history entry.
  6. If a requester is configured, also submit a movie request.

Order numbers are persisted (/config/state.json) so cancellations can undo their matching reservation.

Quick start

git clone <this-repo> ticketarr && cd ticketarr

# Option 1: YAML config
cp config.example.yml config/config.yml
$EDITOR config/config.yml

# Option 2: environment variables
cp .env.example .env
$EDITOR .env
# then uncomment `env_file: - .env` in docker-compose.yml

docker compose up -d
docker compose logs -f ticketarr

The healthcheck endpoint listens on http://127.0.0.1:8765/healthz.

Unraid (Community Apps)

ticketarr ships with a Community Apps template (templates/ticketarr.xml) and a matching ca_profile.xml so it can be installed directly from the Apps tab.

Two ways to configure — pick one

  1. Environment variables. Fill in the IMAP + TMDB + tracker/requester fields on the template and click Apply. Every knob has a matching env var; see the template descriptions.
  2. YAML file. Leave every field on the template blank, click Apply, then look in /mnt/user/appdata/ticketarr/ for the config.yml.sample that ticketarr drops on first run. Copy it to config.yml, edit it, and restart the container.

If both are used, environment variables win. Empty env-var fields are treated as "not set" (they do not clobber a real value in config.yml), so you can safely leave anything you don't care about blank on the template.

None of the template fields are marked strictly required because either path is valid — but the container will refuse to start and print a clear error listing the missing keys if IMAP host/username/password or a TMDB credential is missing from both sources.

First-run checklist

  1. Install ticketarr from Community Apps (or point the CA "Install template from URL" dialog at https://raw.githubusercontent.com/jivandabeast/ticketarr/main/templates/ticketarr.xml).
  2. Set the Appdata path (default /mnt/user/appdata/ticketarr).
  3. Either fill in the env-var fields or start the container, then edit /mnt/user/appdata/ticketarr/config.yml (copied from .sample).
  4. Check the container log — you should see Loaded configuration (source=…) followed by each integration's startup ok line.
  5. If you selected Trakt as the tracker, the log will print a https://trakt.tv/activate URL and a one-time code — visit it in a browser to complete the OAuth device flow. The token is cached in /config/trakt_token.json and reused on every restart.

The healthcheck endpoint is on the container's private network at http://<container>:8765/healthz. There is no WebUI — clicking the container's icon in Unraid will not open anything useful.

Configuration

ticketarr loads config in this order, with later sources overriding earlier ones:

  1. TICKETARR_CONFIG env var → path to a YAML file
  2. First existing default: /config/config.yml, /config/config.yaml, ./config.yml, ./config.yaml
  3. Environment variables (override anything above)

See config.example.yml and .env.example for the full list of options.

Required

  • IMAP host + credentials for the inbox that receives ticket emails
  • TMDB API key (v3) or bearer token (v4 read access token)
  • One of the tracker providers (trakt, ryot, yamtrack) — or none if you only want to submit requests

Filtering senders

By default ticketarr polls for messages from every registered parser's canonical From-addresses (AMCTheatres@amctheatres.com, tickets@regaltickets.com, noreply@regaltickets.com). If you want to poll a different / additional set of senders — for example, a friend who forwards you tickets, or a SimpleLogin / 33mail alias — set an explicit list.

YAML (imap.sender_filters in config.yml):

imap:
  sender_filters:
    - AMCTheatres@amctheatres.com
    - tickets@regaltickets.com
    - noreply@regaltickets.com
    - friend@gmail.com

Environment variable (IMAP_SENDER_FILTERS, comma-separated):

IMAP_SENDER_FILTERS="AMCTheatres@amctheatres.com,tickets@regaltickets.com,noreply@regaltickets.com,friend@gmail.com"

Setting this explicitly replaces the default list — include every sender you want, not just the extras. Whitespace around commas is trimmed. An empty value means "fall back to the registered defaults".

Trakt bootstrap

On first run with tracker.provider: trakt, the app performs Trakt's OAuth Device Flow. It logs a message like:

Trakt authorization required: go to https://trakt.tv/activate and enter code XXXXXXXX

Visit that URL in a browser, sign in, and enter the code. The resulting tokens are written to trakt.token_path (default /config/trakt_token.json) and reused on subsequent runs.

Provider notes

Provider Auth Notes
Trakt.tv OAuth (device flow), needs client id + secret Full watched/unwatched support with showtime timestamp
Ryot Username + password (or session token) GraphQL against /backend/graphql. ticketarr runs the loginUser mutation at startup and uses the returned session token as Authorization: Bearer …. ryot.api_key remains as an advanced fallback for pre-issued session/access-link tokens. Scrobbles via deployBulkMetadataProgressUpdate.
Yamtrack Web-UI username + password Drives the internal /media_save Django form (Yamtrack has no REST API for setting a specific watched-at). end_date = start_date + TMDB runtime (fallback 120 min). Always-creates a new row per showing so rewatches are preserved. Contract is unofficial — see AGENTS.md.
Jellyseerr / Overseerr X-Api-Key header POST /api/v1/request with {"mediaType":"movie","mediaId":<tmdb>, "is4k": <bool>} (opt-in via seerr.request_4k)
Ombi ApiKey header POST /api/v1/Request/movie (Ombi has no V2 movie-request endpoint; V2 hits an SPA fallback and returns HTTP 500).

Repository layout

ticketarr/                    Python package
  ├─ __main__.py              entrypoint
  ├─ app.py                   orchestrator
  ├─ config.py                YAML + env config loader (pydantic)
  ├─ imap_monitor.py          IMAP polling / message decode (multi-sender)
  ├─ parsers/                 per-chain email parsers (registry-based)
  │    ├─ __init__.py         dispatcher + REGISTRY
  │    ├─ base.py             ParsedEmail + EmailParser protocol
  │    ├─ util.py             shared regex/date helpers
  │    ├─ amc.py              AMC A-List (ported from Marquee)
  │    └─ regal.py            Regal Unlimited
  ├─ state.py                 JSON state store (dedupe + order → tmdb map)
  ├─ tmdb.py                  TMDB search client
  └─ integrations/
       ├─ base.py             Tracker / Requester protocols
       ├─ trakt.py
       ├─ ryot.py
       ├─ yamtrack.py
       ├─ seerr.py
       └─ ombi.py
Dockerfile
docker-compose.yml
config.example.yml
.env.example
templates/ticketarr.xml         Unraid Community Apps template
ca_profile.xml                  Unraid CA repository profile
icon.svg                        used by CA and the README

Attribution

The AMC A-List email parsers in ticketarr/parsers/amc.py were ported from the JavaScript parsers in ijoshi129/Marquee by @ijoshi129. Thank you for the work.

Development

python -m venv .venv
source .venv/bin/activate
pip install -e '.[dev]'
python -m ticketarr

Running the tests

The parsers are the highest-risk surface, so the test suite is fixture-driven: drop sanitized real emails into tests/fixtures/<chain>/ as .eml files and pytest will parametrize the parser assertions over every one.

pytest

Filename conventions and optional <name>.expected.json sidecar assertions are documented in tests/fixtures/amc/README.md. The suite runs green even when the fixtures directory is empty, so you can commit the harness now and add real emails later.

See AGENTS.md for architecture notes and pointers relevant for future edits.

License

See LICENSE.

Install Ticketarr on Unraid in a few clicks.

Find Ticketarr 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 Ticketarr Review the template variables and paths Click Install

Requirements

An IMAP mailbox that receives your AMC A-List / Regal Unlimited confirmations, and a TMDB API key. See the README for tracker + requester setup.

Download Statistics

437
Total Downloads

Related apps

Details

Repository
jivandabeast/ticketarr:latest
Last Updated2026-07-30
First Seen2026-07-30

Runtime arguments

Network
bridge
Shell
bash
Privileged
false

Template configuration

AppdataPathrw

Persistent state (state.json), cached Trakt OAuth token, and optional config.yml. On first run ticketarr drops a `config.yml.sample` here - copy it to `config.yml` if you'd rather configure via YAML than environment variables. Must be writable by the container.

Target
/config
Default
/mnt/user/appdata/ticketarr
HealthcheckPorttcp

Private HTTP port that serves /healthz for Docker/Unraid healthchecks. Only exposed to the Docker network; no WebUI is served here.

Target
8765
Default
8765
IMAP_HOSTVariable

IMAP server hostname (e.g. imap.gmail.com, outlook.office365.com). Required unless set in /config/config.yml.

IMAP_PORTVariable

IMAP TLS port. Defaults to 993 (implicit TLS).

Default
993
IMAP_USERNAMEVariable

IMAP account username (usually your email address). Required unless set in /config/config.yml.

IMAP_PASSWORDVariable

IMAP account password. For Gmail/Google Workspace you MUST use an App Password, not your real password. Required unless set in /config/config.yml.

IMAP_MAILBOXVariable

Mailbox to poll.

Default
INBOX
IMAP_SSLVariable

Use implicit TLS on the IMAP connection.

Default
true
IMAP_SENDER_FILTERSVariable

Comma-separated list of From-addresses to poll. Leave empty to use every registered parser's default senders (AMC + Regal). Set to override, e.g. to add a friend who forwards you tickets.

IMAP_INITIAL_LOOKBACK_DAYSVariable

On first run, skip messages older than this many days. 0 = process every message in the inbox.

Default
7
IMAP_POLL_INTERVAL_SECONDSVariable

Fallback polling interval when the server doesn't support IMAP IDLE.

Default
120
IMAP_MARK_SEENVariable

Mark processed messages as seen. Set false if you want them to stay unread.

Default
true
TMDB_API_KEYVariable

TMDB v3 API key. Free - request at https://www.themoviedb.org/settings/api. Either this OR TMDB_BEARER_TOKEN is required (or set one of them in /config/config.yml).

TMDB_BEARER_TOKENVariable

TMDB v4 read access token (Bearer). Optional alternative to TMDB_API_KEY.

TRACKER_PROVIDERVariable

Which watch tracker to scrobble to. One of: none, trakt, ryot, yamtrack. Leave blank to inherit the value from /config/config.yml (defaults to `none` when unset). See the matching credential variables below.

REQUESTER_PROVIDERVariable

Where to request the movie so it's available in your library. One of: none, seerr, ombi. Leave blank to inherit from /config/config.yml.

TRAKT_CLIENT_IDVariable

Trakt application Client ID. Create a free app at https://trakt.tv/oauth/applications - Redirect URI must be `urn:ietf:wg:oauth:2.0:oob`. Required only when TRACKER_PROVIDER=trakt.

TRAKT_CLIENT_SECRETVariable

Trakt application Client Secret. Required only when TRACKER_PROVIDER=trakt.

RYOT_BASE_URLVariable

Base URL of your Ryot instance (e.g. http://ryot:8000). Required only when TRACKER_PROVIDER=ryot, and only if not set in /config/config.yml.

RYOT_USERNAMEVariable

Ryot username. ticketarr logs in via the loginUser GraphQL mutation at startup and uses the returned session token for all subsequent calls. Required only when TRACKER_PROVIDER=ryot (unless RYOT_API_KEY is set instead).

RYOT_PASSWORDVariable

Ryot password. Required only when TRACKER_PROVIDER=ryot (unless RYOT_API_KEY is set instead).

RYOT_API_KEYVariable

Advanced fallback: a pre-issued Ryot session token (e.g. from a processAccessLink access link). Use instead of RYOT_USERNAME/RYOT_PASSWORD. Most users should leave this blank.

YAMTRACK_BASE_URLVariable

Base URL of your Yamtrack instance (e.g. http://yamtrack:8000). Required only when TRACKER_PROVIDER=yamtrack, and only if not set in /config/config.yml.

YAMTRACK_USERNAMEVariable

Yamtrack web-UI username. ticketarr drives Yamtrack's internal /media_save form (no REST API supports backdated watched-at). Required only when TRACKER_PROVIDER=yamtrack.

YAMTRACK_PASSWORDVariable

Yamtrack web-UI password. Required only when TRACKER_PROVIDER=yamtrack.

SEERR_BASE_URLVariable

Base URL of your Jellyseerr or Overseerr instance (e.g. http://jellyseerr:5055). Required only when REQUESTER_PROVIDER=seerr, and only if not set in /config/config.yml.

SEERR_API_KEYVariable

Jellyseerr/Overseerr API key. Required only when REQUESTER_PROVIDER=seerr.

SEERR_REQUEST_4KVariable

Set true to file 4K requests instead of standard quality.

Default
false
OMBI_BASE_URLVariable

Base URL of your Ombi instance (e.g. http://ombi:3579). Required only when REQUESTER_PROVIDER=ombi, and only if not set in /config/config.yml.

OMBI_API_KEYVariable

Ombi API key. Required only when REQUESTER_PROVIDER=ombi.

OMBI_LANGUAGE_CODEVariable

ISO 639-1 language code sent with Ombi requests.

Default
en
TICKETARR_LOG_LEVELVariable

Python logging level (DEBUG, INFO, WARNING, ERROR).

Default
INFO