TAUTWEEKLY FOR PLEXNAS Portable v1.1.0 · Unraid Apps, QNAP & Docker
Guide0%
UNRAID APPS · QNAP · DOCKER

Your Plex newsletter.One-click on Unraid.

A richer walkthrough for installing from Unraid Apps or QNAP Container Station, testing, scheduling, backing up, and safely operating TautWeekly for Plex beside Tautulli—without installing anything inside Tautulli.

tautweekly / ssh verification
$ ./tautweekly.sh verify

[OK] Docker daemon available
[OK] Compose command detected
[OK] Tautulli API connected
[OK] Persistent data writable
[OK] Six preview states supported
[OK] Weekly schedule syntax valid

NEXT: ./tautweekly.sh preview-all
No real Plex user email will be sent.

$ |
2equivalent archive formats
6deterministic preview states
1dedicated TautWeekly for Plex service
0credentials included in package
No guide sections matched. Try “Tautulli,” “schedule,” “SMTP,” “backup,” or “preview.”
Before installation

Choose an archive format

ZIP and TAR.GZ contain the same application.

Both files are functionally identical.

Keep both as backups if useful, but install only one into the final QNAP folder. They are two archive formats—not two editions.

ZIP

ZIP archive

Best when extracting on Windows or with a graphical archive utility. After copying to QNAP, mark the shell scripts executable.

Windows-first file
TautWeekly-nas-docker.zip
TGZ

TAR.GZ archive

Best when extracting directly on QNAP/Linux over SSH. It is the more native Unix format and better preserves executable permissions.

QNAP-first file
TautWeekly-nas-docker.tar.gz
WorkflowUseExtra step
Extract on Windows, then copy to QNAPZIPchmod +x qnap-install.sh tautweekly.sh
Extract directly on QNAP over SSHTAR.GZPermission repair usually unnecessary, but safe to run
TAR.GZ extraction
mkdir -p /share/Container/tautweekly
tar -xzf TautWeekly-nas-docker.tar.gz \
  --strip-components=1 \
  -C /share/Container/tautweekly
Deployment model

One dedicated service beside Tautulli

TautWeekly for Plex communicates through APIs and network access.

Separate service

Tautulli upgrades cannot remove TautWeekly for Plex files.

Persistent data

./data keeps configuration, state, logs, and previews.

PS

PowerShell 7

Microsoft’s cross-platform runtime powers the newsletter engine.

Internal scheduler

No Windows Task Scheduler or permanent QNAP cron edits.

QNAP Container Station ├── existing Tautulli container │ └── published host port 8181 │ └── tautweekly container ├── production newsletter engine ├── guarded weekly scheduler ├── ImageMagick artwork processing ├── persistent volume → ./data └── preview web server → NAS port 8787
Keep the services separate.

Installing TautWeekly for Plex inside Tautulli couples upgrades, backups, and troubleshooting. A dedicated container is safer and easier to restore.

Preflight

What the NAS needs

The installer checks most requirements automatically.

  • QNAP Container Station installed and running
  • 64-bit Intel/AMD x86-64 or ARM64 CPU
  • SSH access for the recommended workflow
  • Persistent share such as /share/Container/tautweekly
  • Network access to Tautulli’s published port
  • SMTP provider supporting STARTTLS, normally port 587
  • Tautulli API key and a TestEmail address

Ports used

PortPurpose
8181Tautulli host port
8787LAN preview server
587Typical SMTP STARTTLS
QNAP launch sequence

QNAP install, step by step

Every risky action stays behind preview and TestEmail checkpoints.

1

Install and start Container Station

Confirm Docker is available before beginning.

2

Extract one archive into persistent storage

Recommended folder
/share/Container/tautweekly
3

Connect over SSH

Prepare scripts
cd /share/Container/tautweekly
chmod +x qnap-install.sh tautweekly.sh
4

Run the guided installer

Pull, configure, and verify
./qnap-install.sh

The installer creates .env, pulls the published image, starts the service, runs setup, restarts, and verifies.

5

Review timezone and preview URL

For Arizona, use TZ=America/Phoenix. Confirm PREVIEW_BASE_URL contains the NAS LAN IP.

6

Run safe acceptance testing

No production users yet
./tautweekly.sh verify
./tautweekly.sh list-users
./tautweekly.sh exclude-users
./tautweekly.sh preview-all
./tautweekly.sh send-test-all
./tautweekly.sh schedule-status
7

Enable automatic delivery only after approval

Explicit activation
./tautweekly.sh schedule-enable
Automatic delivery starts disabled.

The container can run, preview, and send to TestEmail without being authorized to email all Plex users.

Unraid Apps deployment

Install directly from the Apps tab

The official v2 template pulls the published amd64/arm64 image and applies safe Unraid appdata, identity, port, and timezone defaults.

Community AppsGHCR imagePersistent appdataSix-state testing
Search Apps for TautWeekly for Plex and select Install.

Review /mnt/user/appdata/tautweekly, port 8787, timezone, PUID 99, and PGID 100. The template pulls ghcr.io/sparkmoxie/tautweekly:latest.

Complete setup from the container Console.

Run pwsh -NoLogo -NoProfile -File /opt/tautweekly/Setup-First.ps1, then run Verify-Setup.ps1. Setup offers the live exclusion roster; later, run Manage-User-Exclusions.ps1 to revise it independently. Scheduling remains disabled until explicitly enabled. The manual Compose steps below remain available for custom stacks.

1

Create the Unraid appdata project folder

Unraid terminal
mkdir -p /mnt/user/appdata/tautweekly
cd /mnt/user/appdata/tautweekly

Extract either archive here so compose.yaml, Dockerfile, tautweekly.sh, app/, and data/ are all in this directory.

2

Prepare Unraid ownership and environment defaults

Create .env
cp .env.example .env
chmod +x tautweekly.sh
nano .env
Recommended Unraid .env
COMPOSE_PROJECT_NAME=tautweekly
TZ=America/Phoenix
PUID=99
PGID=100
UMASK=077
PREVIEW_BIND=0.0.0.0
PREVIEW_PORT=8787
PREVIEW_BASE_URL=http://UNRAID_SERVER_IP:8787

America/Phoenix is an example. Replace it with the correct timezone and replace UNRAID_SERVER_IP with the LAN address of the Unraid host.

3

Deploy through Compose Manager or the CLI

Compose Manager Point the stack/project at /mnt/user/appdata/tautweekly/compose.yaml, review the environment values, then bring the stack up.

CLI equivalent
docker compose pull
docker compose up -d
4

Run the application setup inside the new service

Setup and verification
./tautweekly.sh setup
./tautweekly.sh verify
./tautweekly.sh status
5

Connect TautWeekly for Plex to Tautulli

The simplest route is the Unraid host LAN IP and Tautulli’s published host port:

Recommended URL
"TautulliUrl": "http://UNRAID_SERVER_IP:8181"

When both services share the same custom Docker network, the Tautulli container name can be used instead:

Shared custom network
"TautulliUrl": "http://tautulli:8181"
6

Run the same safe acceptance sequence

No production users yet
./tautweekly.sh list-users
./tautweekly.sh exclude-users
./tautweekly.sh preview-all
./tautweekly.sh send-test-all
./tautweekly.sh schedule-status

Open the preview index at http://UNRAID_SERVER_IP:8787/preview-all/preview-all-00-INDEX.html.

7

Enable the internal weekly scheduler after approval

Explicit activation
./tautweekly.sh schedule-enable
/data

Persistent mapping

The included relative volume ./data:/data resolves to /mnt/user/appdata/tautweekly/data when the Compose project lives in the recommended folder.

8787

Unraid WebUI target

Use http://[IP]:[PORT:8787]/preview-all/preview-all-00-INDEX.html as the preview index URL after the official template is installed.

The Community Applications metadata is public and maintainable.

ca_profile.xml and templates/tautweekly.xml live at the repository root and are validated in CI before release.

API connection

Connect to containerized Tautulli correctly

Avoid the most common container networking mistake.

Do not use http://127.0.0.1:8181.

Inside TautWeekly for Plex, localhost means TautWeekly for Plex—not the QNAP host or the separate Tautulli container.

Recommended: QNAP LAN IP

Use the same host address another LAN device uses to open Tautulli.

config.json
"TautulliUrl": "http://media.example.test:8181"

Advanced: shared Docker network

Attach both containers to a user-defined network and resolve Tautulli by service name.

Shared-network example
"TautulliUrl": "http://tautulli:8181"
Direct Plex metadata is optional.

PlexServerUrl and PlexToken improve clearLogo support. Tautulli and text-title fallbacks remain available.

Setup wizard

Configuration that matters

The wizard writes live settings into persistent storage.

Tautulli

URL, API key, history window, watched thresholds, and exclusions.

Email

Sender, Reply-To, SMTP host/port, authentication, and TestEmail.

Branding

Server label, footer name, Plex web link, and optional direct Plex metadata.

Schedule

Day, local time, grace period, polling, and enabled state.

Safety

Ten-second production recipient delay and shorter test delay.

Eligibility

Setup selects excluded users by stable Tautulli ID; the standalone command revises them without changing SMTP or scheduling.

Selected config fields
{
  "TautulliUrl": "http://media.example.test:8181",
  "ApiKey": "PASTE_YOUR_TAUTULLI_API_KEY_HERE",
  "FooterServerName": "My Plex",
  "FromName": "My Plex Newsletter",
  "SmtpHost": "smtp.example.com",
  "SmtpPort": 587,
  "SmtpEnableSsl": true,
  "TestEmail": "you@example.com",
  "ScheduleDay": "Friday",
  "ScheduleTime": "09:30",
  "SendDelaySeconds": 10,
  "ScheduleEnabled": false
}
Credentials stay outside the image.

The live config is stored under data/, receives restrictive permissions, and is not included in either distributed archive.

Recipient changes do not require full setup.

Run ./tautweekly.sh exclude-users, choose comma-separated rows or ranges, press Enter to keep the current list, or type none to clear it. Treat displayed names and emails as private.

Operational controls

Command center

Every routine action is exposed through ./tautweekly.sh.

CommandWhat it doesRisk
verifyChecks runtime, assets, config, Tautulli, SMTP reachability, and schedule syntax.Safe
list-usersLists selectable Plex/Tautulli users.Safe
exclude-usersUpdates stable user exclusions without rerunning setup.Config only
preview [user]Creates one browser preview.No email
preview-all [user]Creates six HTML states and an index.No email
send-test [user]Sends one message only to TestEmail.TestEmail
send-test-all [user]Sends all six only to TestEmail.TestEmail
welcome [user]Sends a real welcome and records state.Real recipient
send-allSends to every eligible Plex user after confirmation.Bulk send
rosterDisplays access/welcome state.Safe
repair-assetsRepairs email-safe assets.Safe
logs / statusShows logs or service state.Safe
restart / updateRestarts or pulls the published image without deleting data.Service change
backupArchives persistent data.Safe
shellOpens a container shell.Advanced
Visual acceptance

Six-state regression suite

All states use the production renderer and MIME/CID pipeline.

01

Manual welcome

One-off onboarding without weekly stats, quiet copy, or warm-up copy.

WELCOMEONLY = TRUE
02

New user — no history

First scheduled delivery with onboarding replacing empty statistics.

RECENTACCESS + ZERO STATS
03

New user — with history

First scheduled delivery with populated statistics.

RECENTACCESS + ACTIVITY
04

Established normal

Populated established newsletter; sample stats only when needed for layout testing.

NORMAL ACTIVITY
05

Established quiet

Zero activity after warm-up, showing QUIET IN THIS SECTOR.

WARMINGUP = FALSE
06

Established warm-up

Zero activity during the initial window, showing STATS ARE WARMING UP.

WARMINGUP = TRUE
Create all previews
./tautweekly.sh preview-all
Preview index
http://QNAP_LAN_IP:8787/preview-all/preview-all-00-INDEX.html
Test delivery is isolated.

send-test-all sends six real SMTP messages, but every message goes exclusively to TestEmail.

Card presentation

Movie genres and TV overflow counts

The same card renderer is used by browser previews, TestEmail, welcomes, normal newsletters, and quiet-week Latest Releases.

TV

Three visible episode rows

TV cards show the three newest episode rows. Totals above three add a gold footer at the bottom: 1 additional episode recently added for four total, or X additional episodes recently added for five or more. Totals from one through three show no footer.

MOV

Genre metadata under the title

Movie cards show up to two genres directly below the title. More than two becomes Genre One, Genre Two, and more. The 13px / 500-weight line uses the same muted color as the description and disappears entirely when no genre metadata is available.

Spacing stays deterministic.

Movie content height was expanded for the genre row, while TV details reserve room for three episode lines and a bottom-aligned overflow footer.

Automatic delivery

Scheduling with guardrails

The scheduler records attempts to avoid duplicates.

Disabled initially

ScheduleEnabled=false until explicitly enabled.

Timezone-aware

Day and time follow TZ in .env.

Grace window

A short restart after send time can still be handled.

Duplicate guard

The attempt is recorded before delivery starts.

Schedule controls
./tautweekly.sh schedule-status
./tautweekly.sh schedule-enable
./tautweekly.sh schedule-disable
./tautweekly.sh schedule-reset
schedule-reset is deliberately dangerous.

It clears today’s attempt guard. A later scheduler poll may permit another real SendAll on the same day.

Persistence

Data, backups, and updates

The data folder is the installation’s durable identity.

data/ ├── config.json ├── state.json ├── access-state.json ├── scheduler-state.json ├── assets/ ├── output/ └── logs/

State protection

  • config.json: API, SMTP, branding, schedule
  • state.json: warm-up tracking
  • access-state.json: new-user and welcome history
  • scheduler-state.json: same-day send guard
Create a dated backup
./tautweekly.sh backup
Pull an update without deleting data
./tautweekly.sh update
Backups contain credentials.

The backup includes config.json. Treat the generated archive as a private credential-bearing file.

Hardening

Security boundaries

The package minimizes exposure; the administrator controls LAN and credentials.

Built-in boundaries

  • Runtime drops to a configurable non-root UID/GID
  • Root UID/GID values are refused
  • UMASK=077 restricts new files
  • Credentials live in persistent storage, not the image
  • Welcome and SendAll require confirmation

Administrator responsibilities

  • Never port-forward preview port 8787 publicly
  • Do not commit .env or config.json
  • Use STARTTLS; implicit SMTPS 465 is unsupported
  • Keep backups private
  • Review all six TestEmail messages before scheduling
Diagnostics

Common problems

Start with verification and logs.

Tautulli verification fails
Use the QNAP LAN IP and Tautulli’s published port, not 127.0.0.1. Recheck the API key and verify Tautulli opens from another LAN device.
The preview page does not open
Run ./tautweekly.sh status and ./tautweekly.sh logs. Confirm port 8787 and PREVIEW_BASE_URL use the current NAS LAN IP.
Shell scripts say permission denied
Run chmod +x qnap-install.sh tautweekly.sh. This is common after Windows ZIP extraction.
SMTP cannot authenticate
Confirm STARTTLS, normally port 587, username, and app password/provider credentials. Port 465 implicit SMTPS is unsupported.
The service is healthy but no scheduled email arrives
Check schedule-status, ScheduleEnabled, TZ, day/time, grace window, and the same-day attempt guard.
Container Station recreates the container
That is safe while the project folder and data/ bind mount remain intact. The container layer is disposable.
Complete reference

Recommended operating sequence

The shortest safe path from extraction to scheduled delivery.

Full safe workflow
cd /share/Container/tautweekly
chmod +x qnap-install.sh tautweekly.sh

./qnap-install.sh

./tautweekly.sh verify
./tautweekly.sh list-users
./tautweekly.sh exclude-users
./tautweekly.sh preview-all
./tautweekly.sh send-test-all
./tautweekly.sh schedule-status

# Enable only after approving all six TestEmail messages:
./tautweekly.sh schedule-enable

Statically validated

Archive integrity, shell syntax, Compose structure, Linux-safe paths, non-root startup, and six-state wiring.

Environment validation

Your QNAP, Docker daemon, Tautulli, Plex metadata, LAN, SMTP provider, and mail clients still require acceptance testing.

Schedule authorization

Enabling the schedule authorizes confirmation-free weekly SendAll at the configured local time.

Final acceptance gate.

Do not enable the schedule until the browser preview index and all six TestEmail messages are approved on desktop and mobile clients.

v1.0.7 correction

TV overflow counts now survive Tautulli aggregation

When Tautulli reports one show or season row, TautWeekly for Plex inspects child episode timestamps inside the newsletter window and calculates the true remainder beyond the three visible rows.

Display remains capped

Only the three newest episode rows appear on the card.

Footer uses the real total

Four total produces 1 additional episode recently added; five or more uses the exact remaining count.

Reserved footer space

A wrapped title or IMDb row no longer pushes the gold overflow line outside the visible card.

Movie genre formatting is retained.

The muted 13px, weight-500 line stays directly under the movie title with the first two genres and “, and more” when applicable.

v1.0.7 layout correction

Global movie genre placement

Movie genres now appear immediately beneath the title identity on regular cards and every movie-based hero layout.

Regular cards

The title and genre use normal flow, eliminating the large blank gap caused by fixed-height table distribution.

Desktop hero

The genre appears below either the clearLogo or the normal fallback title.

Mobile hero

The genre appears below the normal title while the mobile banner remains clearLogo-free.

One approved style everywhere.

13px, weight 500, muted description color, first two genres plus “, and more,” and no blank row when genre metadata is missing.

Windows Docker host

Docker Desktop installation

The container runs in Linux-container mode; the included PowerShell wrapper removes the need for Git Bash or WSL.

1. Prepare .env

Copy .env.example to .env. Use PREVIEW_BASE_URL=http://localhost:8787 and an IANA timezone such as America/Phoenix.

2. Build and configure

Run docker compose up -d --build, then .\tautweekly-docker.ps1 setup.

3. Accept safely

Run verify, preview-all, and send-test-all before enabling the schedule.

Docker Desktop acceptance
Copy-Item .env.example .env
docker compose pull
docker compose up -d
.\tautweekly-docker.ps1 setup
.\tautweekly-docker.ps1 verify
.\tautweekly-docker.ps1 preview-all
.\tautweekly-docker.ps1 send-test-all
Connecting to host-published Tautulli.

Use http://host.docker.internal:8181 on Docker Desktop, or use http://tautulli:8181 when both services share a custom Docker network.

v1.0.7 corrections

Setup and preview assets repaired

The reported symptoms came from three independent defects, all corrected in this release.

Blank optional token

Pressing Enter now safely stores an empty Plex token and continues setup.

GIF verification

Frame counts are newline-delimited before parsing, so valid animated files are no longer mislabeled as unreadable.

Browser asset root

Preview HTML uses assets/..., backed by a real /data/output/assets mirror inside the Python web root.

No manual symlink is required.

Container startup removes the old workaround symlink when present, creates a real directory, and refreshes it from /data/assets.

Repair an existing installation after updating
docker compose up -d --build
./tautweekly.sh repair-assets
./tautweekly.sh verify
./tautweekly.sh preview-all
Current email behavior

Adaptive stats and the Binge Champion award

Light viewing weeks reveal more detail without making heavy viewing weeks excessively tall.

1–3

Itemized movies

Mini poster, movie title, Rotten Tomatoes critic score, and audience score.

1–3

Itemized episodes

Mini show poster, show title, Sxx EPxx episode title, and IMDb score.

4+

Compact counts

At four or more, the card returns to the large-number format to protect email length.

Binge Champion is a privacy-preserving server-wide award.

The user with the most qualifying watch time wins; total plays break an exact-time tie. Every scheduled weekly email shows the same anonymous movie-play, TV-play, and total-time aggregate. Only the winner’s card receives the gold border, larger trophy, and “YOU WON” treatment.

Trending is not duplicated.

Trending ranks a media title and displays its server-wide play count once. Binge Champion ranks a person by qualifying watch time, but never reveals that person’s name, user ID, or watched titles.

Global movie formatting

Genre now appears in watched-movie statistics

The compact 1–3 movie recap now matches the rest of the newsletter’s movie hierarchy.

1–3

Adaptive movie rows

Each row includes a mini poster, title, genre, and Rotten Tomatoes critic and audience scores.

ALL

One genre rule

Show the first two genres and append “, and more” when additional genres exist.

No empty genre row

When metadata has no genre, the rating line moves up automatically with no blank space.

Applied everywhere the adaptive movie-stat card can render.

This includes production and portable previews, individual tests, six-state test suites, scheduled weekly newsletters, and all applicable recipient states.

PowerShell 5.1 hotfix

Single-item adaptive cards now retain array semantics

Exactly one watched movie or one streamed episode no longer becomes a scalar under strict mode.

Resolved: “The property ‘Count’ cannot be found on this object.”

MovieItems and EpisodeItems are initialized and assigned explicitly as arrays before the adaptive renderer checks their counts.

0

Empty collection

Retains a valid zero-length array.

1

Single item

Remains an array instead of being unwrapped into a scalar.

2–3

Multiple items

Continues to render the adaptive poster rows normally.