Release v1.1.0-rc.7: backfill, scheduler hardening, and Docker sync#38
Conversation
|
@codex please review this v1.1.0-rc.1 release branch. Focus on provider import, force/safe web flow, multi-version linking, deferred media-server refresh, Docker/runtime defaults, and docs readability. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f60fc8fe6c
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| ImportContentType::Movie | ImportContentType::Auto => { | ||
| let Some(tmdb) = tmdb else { | ||
| return Ok(RemoteLookupOutcome::NoMatch); | ||
| }; | ||
| let matches = tmdb.search_movie(query, candidate.year_hint).await?; |
There was a problem hiding this comment.
Handle auto import lookups with TV-aware providers
When content_type is auto, remote resolution currently goes straight to TMDB movie search, so TV folders/files without explicit IDs are either left unresolved or assigned a movie ID. In mixed provider roots (--content-type auto --lookup-mode remote), this can produce wrong {tmdb-...} tags and downstream placement decisions for TV content because no TVDB/TMDB-TV path is attempted in this branch.
Useful? React with 👍 / 👎.
| if id.starts_with("tmdb-") || id.starts_with("imdb-") { | ||
| return rules_plan | ||
| .and_then(|rules| rules.rules.destinations.movies.destination_for(candidate)) | ||
| .or_else(|| options.movie_destination.clone()); |
There was a problem hiding this comment.
Route tmdb-tagged auto imports by actual media type
Auto-destination routing treats every tmdb-* ID as a movie, but TMDB IDs are used for both movies and TV entries. A TV source already tagged like {tmdb-12345} will be forced into the movie destination path, which can misplace imports and poison later link/backfill state for mixed libraries.
Useful? React with 👍 / 👎.
| let filename = if self.multi_version { | ||
| append_version_label( | ||
| &filename, | ||
| &m.source_item.extension, | ||
| &version_label(&m.source_item), |
There was a problem hiding this comment.
Restrict multi-version suffixing to movie targets
The multi_version toggle is applied to episode filename generation as well, even though the shipped config/docs describe this as movie-only behavior. Enabling it will append per-version suffixes to TV/anime episode links too, changing established episode naming and creating extra per-episode variants that operators likely did not opt into.
Useful? React with 👍 / 👎.
|
Addressed the Codex review findings in a95ee7c:
Validation:
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a95ee7c551
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| || (id.starts_with("tmdb-") | ||
| && infer_auto_content_type(candidate) == ImportContentReport::Tv) | ||
| { |
There was a problem hiding this comment.
Handle inferred-anime TMDB imports as TV records
destination_for_auto_candidate now routes inferred anime paths to the anime destination, but media_type_for_import_candidate only reclassifies tmdb-* IDs when inference is Tv. For --content-type auto paths like /.../anime/... {tmdb-123}, the import can be written under anime while backfill still stores MediaType::Movie, which sends later repair/scan logic down movie-specific paths for episodic content. This mismatch is introduced by the new auto-routing behavior and should also treat inferred Anime as TV-family media when deriving LinkRecord.media_type.
Useful? React with 👍 / 👎.
| ImportContentType::Movie => tmdb.is_some(), | ||
| ImportContentType::Auto => tmdb.is_some() || tvdb.is_some(), | ||
| ImportContentType::Tv | ImportContentType::Anime => tmdb.is_some() || tvdb.is_some(), |
There was a problem hiding this comment.
Avoid spending lookup budget on unqueryable auto candidates
In resolve_candidates_from_remote, auto mode is now considered usable with only TVDB credentials, but the lookup loop still increments the max_lookups counter before checking whether a candidate can actually be queried with available providers. When TMDB is missing, movie-inferred auto candidates immediately return NoMatch yet still consume the cap, so later TV candidates may never be queried against TVDB in the same run.
Useful? React with 👍 / 👎.
846d516 to
b2e45d4
Compare
… and collapsible sections
…t, and refactor status, scan, and cleanup pages
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 62745a074c
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| let key = VersionedDestinationKey { | ||
| destination, | ||
| version_slot: version_slot(&candidate.source_item), | ||
| }; | ||
| insert_or_replace(&mut by_destination, key, candidate); |
There was a problem hiding this comment.
Preserve each source in multi-version reduction
When symlink.multi_version is enabled, two movie sources with the same parsed quality/edition/HDR/codec—or any two untagged sources, which both use "version"—produce the same VersionedDestinationKey, so insert_or_replace silently discards one. The linker's path-hash suffix cannot distinguish these files because the discarded candidate never reaches it, preventing common same-resolution encodes from coexisting despite multi-version mode.
Useful? React with 👍 / 👎.
| let permit = match state.scheduler_jobs().try_acquire_owned() { | ||
| Ok(permit) => permit, | ||
| Err(_) => { | ||
| return api_error( | ||
| StatusCode::CONFLICT, | ||
| "Another scheduler job is already running", | ||
| ) | ||
| } | ||
| }; |
There was a problem hiding this comment.
Share scheduler exclusion with all background jobs
When the daemon and web UI run together, this semaphore serializes only API run-now requests: the daemon's run_scheduler_loop executes due rules without acquiring it, while ordinary web scans use a separate background_jobs guard. A scheduled scan can therefore overlap either a manual scan or a run-now scan, and the UI will still accept the second job; both then reconcile the same links and scan records concurrently, allowing one run's dead-link sweep or updates to interfere with the other.
Useful? React with 👍 / 👎.
| if cfg.daemon.vacuum_enabled { | ||
| rules.push(ScheduleRule::new_bootstrap( | ||
| "Legacy VACUUM housekeeping", | ||
| ScheduledEvent::HousekeepingVacuum, | ||
| ScheduleTrigger::Daily { | ||
| times: vec![format!("{:02}:00", cfg.daemon.vacuum_hour_local)], | ||
| }, | ||
| json!({ "vacuum": true }), |
There was a problem hiding this comment.
Schedule retention housekeeping even when VACUUM is disabled
With the default daemon.vacuum_enabled: false, no housekeeping rule is created at all. The previous daemon loop always called housekeeping_with_vacuum(false) and used this flag only to decide whether to run the expensive VACUUM step, so after this scheduler migration default installations stop deleting expired API cache rows, old scan runs, link events, and completed acquisition jobs and let the database grow indefinitely.
Useful? React with 👍 / 👎.
| fn has_media(&self, media_id: &str) -> bool { | ||
| self.targets_by_media_id.contains_key(media_id) | ||
| } |
There was a problem hiding this comment.
Scope active-link suppression to the candidate destination
When a media ID has any active DB link, this snapshot suppresses backfill without checking that the target belongs to the candidate's Arr path or selected library. For example, after moving an item to a new library root—or when another library already contains the same title—the old/other target makes has_media true, so an empty current Radarr destination is counted as already satisfied and --search-missing never submits it even though the folder being backfilled remains empty.
Useful? React with 👍 / 👎.
| let next_due = rule | ||
| .next_after(now) | ||
| .map(|dt| dt.format("%Y-%m-%d %H:%M:%S %Z").to_string()); |
There was a problem hiding this comment.
Derive displayed due times from each rule's persisted cursor
For a startless interval rule, next_after(now) always returns now + interval, while the daemon schedules from the last persisted planned_at and treats a never-run rule as immediately due at created_at. Consequently the scheduler page's countdown resets whenever it is loaded—for example, a 15-minute rule that last ran 10 minutes ago is shown as due in 15 minutes although it will run in 5—and a newly created rule can execute immediately while the UI says its first run is one interval away.
Useful? React with 👍 / 👎.
Summary
Verification
Release
Version: 1.1.0-rc.7. Prerelease images publish to the rc tag; stable releases publish to latest. No Rust toolchain pin was added.