Repository navigation
Scheduled Jobs
Everything Streamline does on its own is a named job on a fixed interval. All of them are visible, retimeable, pausable and runnable on demand at Settings → Schedules or via /api/v1/schedules.
| Job | Default | Config key | What it does | Trigger |
|---|---|---|---|---|
download-monitor |
30s |
schedules.download_monitor |
Polls download clients for progress; hands finished torrents to the importer; adopts manually-added streamline-tagged torrents |
UI / API |
import-scan |
60s |
schedules.import_scan |
Recovery sweep: re-queues any download record stuck in importing state, so work isn't lost across a restart or transient failure |
UI / API |
movie-rss-sync |
15m |
schedules.movie_rss_sync |
Reads indexer RSS feeds, grabs matching wanted movies | UI / API |
tv-rss-sync |
15m |
schedules.tv_rss_sync |
Same, for episodes | UI / API |
movie-missing-search |
12h |
schedules.movie_missing_search |
Actively searches indexers for every still-wanted movie | UI / API |
tv-missing-search |
12h |
schedules.tv_missing_search |
Same, for episodes | UI / API |
movie-metadata-refresh |
24h |
schedules.movie_metadata_refresh |
Re-pulls TMDB metadata, posters, release dates | UI / API |
tv-metadata-refresh |
24h |
schedules.tv_metadata_refresh |
Re-pulls TVDB metadata; discovers new seasons and episodes | UI / API |
movie-orphan-scan |
6h |
schedules.movie_orphan_scan |
Finds untracked video files under movie_path and queues them for review |
UI / API |
tv-orphan-scan |
6h |
schedules.tv_orphan_scan |
Same, under series_path
|
UI / API |
drift-check |
15m |
schedules.drift_check |
Detects tracked files that have vanished from disk | UI / API |
media-probe |
15m |
schedules.media_probe |
Backfills ffprobe technical info (media_info) onto MediaFile rows the importer didn't probe inline — adoption, orphan scan, bulk import. 25 rows/tick, oldest first. No-ops when ffmpeg.enabled is false or ffprobe isn't found |
UI / API |
file-selection |
30s |
schedules.file_selection |
Resolves magnet-sourced selective file downloads still waiting to learn what's inside the torrent. While records are actually pending the job re-checks every 5 seconds from inside its own run, so a magnet resolves at that cadence rather than this one; the interval here is only how often it looks for work when there is none. It keeps draining pending records regardless of download.selective_files — turning the setting off stops new pending records being created, but records already waiting still have to be resolved |
UI / API |
cleanup |
24h |
schedules.cleanup |
Prunes completed download records and aged-out events | UI / API |
purge-sessions |
1h |
— | Deletes expired sessions | System job — not controllable |
These are complementary, not redundant, and the distinction explains most latency questions.
RSS sync is reactive: it reads the newest items from each indexer's feed and checks them against what you want. Cheap, frequent, catches new releases within minutes. But a feed only carries recent items — an RSS sync will never find a film from 2011.
Missing search is active: it walks everything still marked Wanted and issues a real search query per item. Expensive, infrequent, and it's what eventually finds back-catalogue titles.
So a new release typically lands in minutes; something old can take up to 12 hours unless you hit Search now.
Two halves of keeping the database honest against the filesystem.
Orphan scan finds files on disk that Streamline doesn't know about. A confident match for a movie already in the library with no file is adopted where it lies — no copy, no link, no rename — and everything else is queued for review rather than imported blind, with the same accept/match/exclude flow as an import scan.
Drift check finds records whose files have gone. It doesn't act immediately: a file must be missing for library.drift_grace_ticks consecutive checks (default 3) before its row is deleted. At the default 15-minute interval that's 45 minutes of tolerance, which is what stops a brief NFS outage from erasing your library metadata.
If your storage is flaky, raise drift_grace_ticks (max 20) rather than lengthening the interval — you keep prompt detection while widening the tolerance.
The busiest job, and the one that does the most. Every 30 seconds it polls each enabled client for the state of streamline-tagged torrents, updates the queue, hands anything complete to the importer, and notices torrents you added into the streamline category — producing the proposals described in Activity and Calendar.
Raising the interval above 30s makes imports feel sluggish. Lowering it mostly generates API traffic against your download client.
In the UI: Settings → Schedules.
Via the API (all admin-only):
# List every job with its state
curl -H "X-API-Key: $KEY" https://streamline.example.com/api/v1/schedules
# One job
curl -H "X-API-Key: $KEY" https://streamline.example.com/api/v1/schedules/movie-rss-sync
# Retime it — minimum 10s
curl -X PATCH -H "X-API-Key: $KEY" -H 'Content-Type: application/json' \
-d '{"interval":"5m"}' \
https://streamline.example.com/api/v1/schedules/movie-rss-sync
# Pause / resume
curl -X POST -H "X-API-Key: $KEY" .../api/v1/schedules/movie-rss-sync/pause
curl -X POST -H "X-API-Key: $KEY" .../api/v1/schedules/movie-rss-sync/resume
# Run once, right now
curl -X POST -H "X-API-Key: $KEY" .../api/v1/schedules/movie-missing-search/runA manual run does the work the ticker would defer: the missing searches ignore library.no_match_cooldown and the grab-failure cap, and the metadata refreshes revisit every title instead of only those not refreshed in the last 24 hours.
Intervals have a 10 second floor. Pause state is persisted to the database, so a paused job stays paused across restarts.
purge-sessions is flagged system: true and rejects PATCH, pause, resume and run.
Each job reports:
| Field | Meaning |
|---|---|
name |
Job identifier |
interval |
Current interval |
paused |
Whether it's suspended |
system |
Read-only system job |
running |
Currently executing |
status |
never | success | error | skipped
|
last_started_at, last_finished_at
|
Timestamps |
next_run_at |
Next scheduled fire |
last_duration_ms |
How long the last run took |
last_error |
Failure message, when status is error
|
progress |
{done, total} while a run is in flight and the job counts its work; null otherwise |
progress comes from the jobs that loop over a known list — both metadata refreshes, both missing searches, the series orphan scan and the media probe report done out of total. The movie orphan scan and drift-check walk without knowing the size up front, so they report done with total: 0. The Schedules page shows it as "Running 12/240" with a bar under the status, or a bare count where there is no total.
GET /api/v1/schedules/events is a text/event-stream that sends the full schedule list on connect and again whenever anything changes — a run starting, reporting progress or finishing, a pause, resume or interval change. Frames are coalesced to at most four a second and a comment line every 15 seconds keeps idle connections open. The Schedules page uses it instead of polling; it falls back to a 10-second poll while the stream is down.
Important
skipped is the status worth watching for. It means the job ran but bailed out because a precondition wasn't met — most often no enabled download client, which causes all three acquisition jobs (*-rss-sync, *-missing-search) to give up immediately. A library that never grabs anything, with jobs reporting skipped, is almost always this.
A rising last_duration_ms on *-missing-search is normal as your wanted list grows; on drift-check it usually means slow storage.
Sensible directions, if the defaults don't suit:
| Situation | Change |
|---|---|
| Want new releases faster |
movie_rss_sync / tv_rss_sync → 5m. Check your indexers' rate limits first |
| Large library, missing-search takes too long | Lengthen to 24h; RSS still catches new releases |
| Aggressive indexer rate limits | Lengthen RSS sync; consider pausing missing-search and running it manually |
| Slow or networked storage | Lengthen drift_check, or raise library.drift_grace_ticks
|
| Library never changes outside Streamline | Pause both orphan scans entirely |
| Metadata churn matters (new seasons) | Shorten tv_metadata_refresh to 12h
|
Tip
Pausing a job is a legitimate long-term configuration, not just a debugging step. If nothing ever touches your library outside Streamline, the orphan scans are pure I/O for no benefit.
Four older keys, still honoured with a warning logged at boot
| Old key | Now |
|---|---|
schedules.rss_sync |
schedules.movie_rss_sync |
schedules.missing_search |
Applied to both movie_missing_search and tv_missing_search
|
schedules.metadata_refresh |
Applied to both movie_metadata_refresh and tv_metadata_refresh
|
schedules.orphan_scan |
Applied to both movie_orphan_scan and tv_orphan_scan
|
Migrate to the split keys — you almost certainly want TV metadata refreshed more often than movie metadata, and the merged keys can't express that.
🎬 Operating Streamline
- Installation
- First-Run Setup
- Adding Movies and TV
- Importing an Existing Library
- Activity and Calendar
- Requests and Users
- NixOS and Nix
- Troubleshooting
- Roadmap
⚙️ Advanced
- Configuration Reference
- Authentication and SSO
- Quality Profiles and Naming
- Quality Profiles and Custom Formats
- Scheduled Jobs
- REST API
- Observability and Logging
- GitOps and Kubernetes