github JFLXCLOUD/NeXroll v1.13.17
v1.13.17 — v2.0.0 prep audit

4 hours ago

This release is a full code audit ahead of v2.0.0 — 11 subsystems reviewed, 22 fixes shipped, every finding tracked in AUDIT.md with severity and status (FIXED / OPEN / ACKNOWLEDGED / DEFERRED). Rolls up v1.13.3 through v1.13.17 into one shipping build.

What was the audit looking for?

Disconnects, half-finished features, places where the same concept was handled three different ways. User-reported bugs ("the trailer is supposed to be disabled but it's still playing on Jellyfin", "the preview shows a Toy Story file that doesn't exist on my Windows install", "the countdown finished but the schedule didn't apply") consistently traced back to parallel implementations drifting apart.

Headline fixes

Trailer "disable" now actually disables (NEXUP-1, PLUGIN-2, SEQUENCING-1)

Toggling a NeX-Up trailer off used to run preroll.enabled = trailer.is_enabled against a column that didn't exist on the Preroll model. SQLAlchemy silently accepted it as a transient attribute and never persisted. Disabled trailers correctly disappeared from nexup_trailers sequence blocks but kept playing through random blocks, category schedules, and the Jellyfin/Emby plugin. Added the enabled column with migration, now respected by every scheduler path AND the manual sequence-apply endpoint AND the plugin resolver.

Backup/restore now lossless (BACKUP-1)

The JSON backup payload was missing many fields. A round-trip silently dropped:

  • Categories: plex_mode, apply_to_plex, is_system (the last protecting NeX-Up system categories from being deleted post-restore)
  • Prerolls: duration, file_size, enabled, community_preroll_id, exclude_from_matching, file_hash
  • Schedules: fallback_category_id, sequence, color, blend_enabled, priority, exclusive, holiday_name, holiday_country — sequence schedules came back as plain category schedules; blend/exclusive/priority all reverted to defaults
  • Holiday presets: start_month / start_day / end_month / end_day, is_recurring — date-range holidays collapsed to single point

Now lossless. Backup payload carries schema_version: 2; older v1 backups still restore (defaults for new fields).

Scheduler honors intent over Plex reply (SCHEDULER-6)

All three scheduler apply branches used to only update setting.active_category / active_schedule_id / last_run if Plex apply returned True. So an empty category, a brief Plex outage, broken paths, or any other apply failure left the dashboard tile permanently stale. Now state reflects intent (which schedule is the winner) regardless of Plex result. The 5-minute periodic verifier still retries Plex sync.

Dashboard preview shows reality, not Plex-stored ghosts (PREVIEW-1, PREVIEW-3)

  • The dashboard preview button used to query Plex for "what's currently set" and try to resolve those paths locally. If Plex held a leftover path from a different host (e.g. a Docker container's /data/prerolls/... after switching to a Windows install), preview rendered "Toy Story" instead of what NeXroll thinks is active. Now resolves from NeXroll's intent first.
  • Per-preroll preview used to build static/prerolls/{category.name}/{filename} URLs and 404 for uncategorized prerolls. New GET /prerolls/{id}/video endpoint streams by ID — works regardless of category folder structure.

Primary-category UI retirement (CATEGORIES-7)

The v1.13.0 work fixed the Categories page but four primary-flavored UI surfaces lingered. All removed:

  • CategoryPicker no longer renders a "Primary" chip or "Make primary" star — all selected categories are equal chips
  • "Set as Primary (moves files)" checkbox in Add-Prerolls-to-Category — gone
  • Bulk "Apply to N Selected" no longer moves files; just adds the m2m tag
  • Internal: ?set_primary=true|false no longer sent

"Sea of thumbnails" → folder picker (IMPORT-1)

Adding prerolls to a category used to be a flat thumbnail grid with no way to grab a whole folder. New affordances:

  • Folder filter dropdown derived on the fly from each preroll's path (Christmas (24), Halloween (12), etc.)
  • Select All (N) button picks every preroll matching the current filter
  • Each thumbnail's caption shows source folder name

Pick "Holiday" → click Select All → click Add. Done.

Scheduler tile countdown clarity (SCHEDULER-7)

User reported: "the countdown finished for 1135 Test but it didn't apply, instead reset to 1h 40min." Root cause: when one schedule's window opens, the countdown's "next up" target jumps to the next schedule on the same React render — both label and time flip simultaneously, looking like a reset. The countdown was actually correct; the display was ambiguous. Now shows:

  • Now: <currently active schedule> line above the countdown
  • Next: <upcoming> @ <target time> (target timestamp on the same line)

When the target schedule flips, the timestamp visibly changes too.

Cross-cutting cleanup

  • Deleted _apply_schedule_win_lose_logic (~410 lines): a parallel reimplementation of scheduler logic that lingered after v1.13.5 rerouted its call site.
  • Consolidated 7 parallel _prerolls_for_category helpers into one canonical prerolls_for_category_query(db, category_id) in scheduler.py. This was the audit's most architecturally dangerous finding — the enabled filter had to be added to each duplicate independently, and the manual-apply and Jellyfin/Emby paths were each caught only after testing surfaced the regression. Future fixes here land once, not seven times.
  • _paths_equal + _find_preroll_for_trailer helpers for trailer↔preroll linkage. Tolerant of case and forward-vs-backslash separator drift on Windows.

Deferred (tracked in AUDIT.md for future passes)

  • SCHEDULER-2 — Apply-branch elif chain readability (no behavior change)
  • SCHEDULER-5 — No visible "why this schedule wins" badge in the UI
  • CATEGORIES-4 — Sequence-import dedupe edge case
  • BACKUP-2Schedule.preroll_ids ID remap on restore (rarely used field)
  • BACKUP-3 — File-bundle ZIP backup path is unaudited
  • NEXUP-3 — Sync in-progress detection uses module dict (TOCTOU; safe for single-process uvicorn)

API changes

  • New: GET /prerolls/{id}/video, POST /prerolls/rescan (already shipped), GET /categories/{id}/delete-impact (already shipped)
  • Changed shape: DELETE /categories/{id} response now returns removed_m2m / cleared_primary / disabled_schedules / cleared_fallback_schedules / removed_holiday_presets instead of the v1.12.20 reassignment counts
  • Backup payload: new schema_version: 2, additional fields per model (see BACKUP-1 above). v1 payloads still restore.
  • New schema column: prerolls.enabled (Boolean, default True). Idempotent SQLite migration on startup. Older builds reading this DB just see the new column as defaulted-true.

Install

Windows: download NeXroll_Installer.exe below.
Docker: rebuild your image from this tag.

Existing installs will see the prerolls.enabled column added automatically on first launch — no manual migration needed.

Don't miss a new NeXroll release

NewReleases is sending notifications on new releases.