Conversation
Records the valid range, integer requirement and provider-scale notes for every external rating and vote-count field. No behaviour change yet.
Rating fields accepted any number, so a threshold on the wrong scale saved without complaint and then matched nothing. Reject out-of-range values, decimals on integer-backed fields, lists, booleans and non-finite values at save time, naming the field by its display label and explaining the two fields whose stored scale differs from the provider's published one.
A value can sit inside the valid range and still be unsatisfiable: nothing exceeds the ceiling, and nothing falls below the floor. Reject those two combinations and point at the inclusive operator that does what the user meant. The inclusive variants stay valid, since items do sit on both bounds.
Move the ten range/type validation tests back into NumericBoundsValidationTests where they belong, leaving NumericBoundaryImpossibilityTests with its six boundary tests. Both classes define identical _validate helpers, so no functional change — purely structural reorganization.
The numeric condition input carried no min, max or step, so the editor gave no hint that Tomatometer runs 0-100 while IMDb rating runs 0-10.
Metacritic user score and Letterboxd score are stored as percentages while their sites publish 0-10 and 0-5, so a user copying the published number enters a value that can never match. Say so in the field itself.
The editor keeps its own copy of the numeric bounds, matching how it already duplicates field labels and operators. This fails if the two drift.
- Document that Metacritic user score and Letterboxd score are stored as 0-100 even though the providers publish them on different scales, since the docs were repeating the exact trap the branch closes in code - Point FIELD_NUMERIC_BOUNDS at its frontend mirror and the parity test that enforces the two stay in sync - Rename the local bounds tuple in _validate_numeric_bounds from _minimum/_maximum to minimum/maximum; the underscore prefix wrongly signalled "unused" for values used throughout the function - Raise the field's full numeric expectation (range and, where applicable, the provider-scale note) instead of a bare "expects a number" when a value is a bool or fails numeric coercion - Give the bounded rating placeholder the same trailing ellipsis as its sibling placeholders
No job currently runs pytest on pull requests. This suite includes a test that pins the frontend rating bounds to the backend table; the test only protects against drift if it runs automatically on each pull request.
Validate rating field values in the advanced rule engine
Condition values were harvested as literal tag labels whatever the operator, so regex and substring patterns were treated as tag names. Skip them during collection and report their presence separately, ready for the refresh to widen its scope when one is present.
A regex or substring condition names no specific tag, so the per-scan top-up matched nothing and left tag data as the last media sync wrote it. A tag removed in Radarr could therefore keep a movie flagged for up to a full sync cycle. Refresh the whole catalog for that media type instead. Exact-match rules are unchanged: they still refresh only the labels they name and leave every other label on the row alone.
…fresh A strip set built from the arr's current tag catalog can't name a label whose tag was deleted there, and can't distinguish a catalog that came back genuinely empty from one that was never fetched. Both left stale labels on movie rows after a full-refresh pass. When a rule needs the whole catalog, replace each row with the confirmed labels instead of stripping and re-adding. Exact-match rules keep the narrower patch behaviour unchanged.
Mirrors the Radarr change on the Sonarr branch so both media types behave the same way, and a tag removed in Sonarr takes effect at the next cleanup scan rather than the next media sync.
Add two CleanupScanIntegrationTests cases for _refresh_arr_tags_for_rules in full-refresh mode: one confirming label additions union across all successful arr configs before the write loop runs, and one confirming a row reachable from any failed config is excluded from the write even when another config for that row succeeded. Neither property was previously tested, so a refactor that moved the write inside the per-config loop, or that used a single config's effective_labels, could silently wipe a second arr's tags while the suite stayed green. Also correct the function's docstring, which still described the write as a strip-then-readd patch in every case; that is only true in exact-match mode, since full-refresh mode replaces the row outright. Note at the movie_refreshed_labels/series_refreshed_labels declarations that they drive the exact-match strip and the debug log only, replace a debug log line that printed the whole catalog on every scan with a count in full-refresh mode, and simplify a condition that was already guaranteed true by its enclosing branch.
…operators Refresh arr tags for rules that match tags by pattern
fix: treat zero-vote TMDB titles as unrated
Selecting several users reduced their totals with max() before applying the operator, so `less than 5 minutes by alice or bob` only matched when both were under the threshold, and `equals` compared against the highest-watching user alone. Each selected user is now compared on their own and the condition matches as soon as one of them satisfies it, which is what the field labels and the docs describe. Per-user totals also read as a concrete zero for every target, including targets no imported history covers, so a "watched less than" rule matched media the playback source cannot see. The resolver now carries the same targets the aggregate duration fields are available for and reports the rest as unknown; the preview target count follows the same rule. Loading the totals is now gated on a rule actually using the fields, rather than on any playback field, so aggregate-only rules no longer pay for the per-user query.
The editor's value check stringified the condition value, and a
{usernames, amount} object stringifies to "[object Object]", so a
condition with no user picked and no amount passed as filled in and was
only rejected by the API on save. The check moves to a module beside the
tree helpers, where the compound shape is recognised by structure and
both halves have to be present, and where node --test can cover it.
The minimums are applied while history is imported, so changing them only affects events imported afterwards; neither the settings panel nor the rules page said so. Also spells out that percent counts time watched rather than furthest position, that it is measured against the runtime of the file being evaluated, and that episode runtimes arrive with a media-server sync, so percent rules on an existing install stay unknown until one has run.
dev's c3f7b1d09a24 and this branch's a3f6b8d1c4e2 both chain off d7f9a2c4e6b8, so combining them leaves alembic with two heads and `upgrade head` refuses to run. The playback migrations now chain onto c3f7b1d09a24 instead. That is a straight re-point rather than a merge revision because neither of this branch's migrations has shipped, so no database has applied them from the old parent.
pyproject.toml has been at 0.3.4 while uv.lock still recorded the reclaimerr package as 0.3.3, so every `uv run` or `uv sync` rewrote the lock and left a stray modification in the working tree.
chore: sync uv.lock with the project version
Seconds of playback
…arded-for spoofing
Harden trusted proxy authentication before merge
Forward auth
feat: updated dependencies
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.