Compare commits

...

108 Commits

Author SHA1 Message Date
Miguel Palhas 5d80177622 Merge #227: say what a pack was abandoned for
Closes #227
2026-08-25 12:12:34 +01:00
Miguel Palhas 591cf27dc5 feat(web): say what a pack was abandoned for
A pack that hard-failed at import blacklisted its release, put every
episode back to missing and left the season reading 0/10, with nothing
on screen joining the two. Every fact was already recorded.

The blacklist now carries its reason out of the database: deck rows read
`blacklisted · size` instead of a bare `blacklisted`, and say whether the
policy turned the file down — relaxable for this title — or the release
itself failed, which a retry only repeats. A season whose pack was
abandoned says so on its row and above its deck, with the release name,
when it failed, and what it failed on. A row the blacklist no longer
answers for keeps rendering and claims no reason.

Two defects from the integration review of #211 sit in the same code and
are fixed here: a waived row threw away the rule it now carries and read
a bare `below policy`, and the empty-eligible count called every waived
row force-grabbable, since #211 gave those rows the rule `overridable`
reads.

Verified against a real browser: series detail, both season decks and
their buckets, at 1280 and 390 px.

Refs #227, #211
2026-08-25 12:11:46 +01:00
Miguel Palhas 9e445d0398 Merge #240: reconcile design coherence in 5.7 and 9.5
Closes #240
2026-08-25 11:50:56 +01:00
Miguel Palhas 1e30c49a72 Merge #245: pack backoff runs from the failure
Closes #245
2026-08-25 11:50:39 +01:00
Miguel Palhas 55373d228c fix: pack backoff runs from the failure
#239 moved §5.7's attention window to `failed_at` and left §6.2's pack
ladder on `grabbed_at`. A torrent that stalls for weeks before ffprobe
condemns it at import has elapsed the whole ladder the moment it fails,
so the pack lane retried a source that had just failed — the one thing
the backoff exists to prevent.

The ladder now measures from the failure, the same anchor and the same
column §5.7 reads, with `grabbed_at` as the fallback for rows written
before the column existed. All three sites read
`max(coalesce(failed_at, grabbed_at))`, so the `last_failed_at` alias
holds what its name says — including the one the season deck feeds into
`reopens_at` and `pack_retry_at`, which was showing a grab time under a
name §5.7 had redefined.

DESIGN.md §6.2 states the anchor the way §5.7 states its own.

Tests cover a pack grabbed 35 days ago and failed 10 minutes ago on the
targeted lane, the RSS lane and the season deck.

`just ci` through the gate: 519/519 tests pass, web checks clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 11:49:44 +01:00
Miguel Palhas c78c73ec6c docs: reconcile design coherence issues in §5.7 and §9.5
§9.5 restated the pre-#226 decision-bar rule without the 30-day window,
giving the document two versions of the same rule. Make it defer to
§5.7 instead.

§5.7 used two phrasings for the season-queuing predicate in one
paragraph (existence-of-file vs. state check) though both readers
implement the state check; picked the state phrasing throughout. Also
fixed the arithmetically confusing "seen twice ... third face"
sentence, and rewrapped the single unwrapped ~450-character line in
§7.4 to the document's ~78-column width.

Ref #240. Ran full `just ci` through the gate (517 tests, exit 0).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-25 11:49:43 +01:00
Miguel Palhas 58a45fc98e Merge #211: let a waiver name the rule it relaxed
Closes #211
2026-08-25 11:47:25 +01:00
Miguel Palhas a0dc07f085 feat(db): let a waiver name the rule it relaxed
`releases` forbade a rule name on anything but a rejection, so §9.3's
deck showed a bare `waived` beside rejections that each named their own,
and §5.7's "watchable but not what was asked" lost the half that says
what was not asked for. Since #210 that is the ordinary outcome of
waiving a size rejection, not a rare one.

0032 rebuilds the table with `CHECK (verdict != 'rejected' OR
rejected_rule IS NOT NULL)`, and the daemon and arr-api's
reclassification both store the waived rule. Existing rows keep NULL and
read as they do today.

`releases` is a parent — `grabs`, `movie_releases`, `episode_releases`
and `season_releases` point at it, three ON DELETE CASCADE — so the
rebuild runs `-- no-transaction` with foreign keys off around one
explicit transaction, per SQLite's own procedure. Verified against a
real database: the pre-0032 binary created and populated it, this build
migrated a copy, and every release row, child row and created_at came
through byte-identical with `PRAGMA foreign_key_check` clean.

Refs #211
2026-08-25 11:44:05 +01:00
Miguel Palhas de6c35cce3 Merge #232: plainer words for grab, blocked, deck
Closes #232
2026-08-25 11:43:55 +01:00
Miguel Palhas a6720f4c06 feat(web): plainer words for grab, blocked, deck
Three words the operator reads change; nothing underneath does. Waivers
are still written, recorded and served under their existing names, the
`blocked` column and flag keep theirs, and the release deck keeps its
name in DESIGN.md §9.3 and in the code.

- `waive + grab` reads `force grab`, and its accessible name says which
  rule the click relaxes. The `waived` bucket reads `below policy`, and
  so does the verdict chip on its rows — an operator can act on "below
  policy" and cannot act on the name the record keeps.
- A waived import already said what was relaxed for two rules; `size`
  joins them and the fallback names the rule rather than badging it
  `waived`.
- `blocked` reads `no targeted search`, everywhere the flag surfaces.
  §6.3 gives it one effect and a bare toggle hid it: the accessible name
  carries the RSS half at rest, and a note under the controls spells it
  out while the flag is on.
- The season and episode controls are already the #230 search icon; only
  their labels still said "deck". They now say what the click does.

`just ci` passes through the gate: 509 tests, biome, tsc, tokens.
Verified in a real browser (agent-browser) on the library, movie detail,
series detail, the episode release view, queues and settings, at 1440
and 390 wide — no horizontal scroll at either.

Refs #232
2026-08-25 11:43:29 +01:00
Miguel Palhas 9a413b10ed Merge #246: re-derive verdicts on a policy edit
Closes #246
2026-08-25 11:42:10 +01:00
Miguel Palhas bbb6d2f4a4 feat(api): re-derive verdicts on a policy edit
PUT /api/policies/{id} changed the rule every title under every root
pointing at the policy is judged by, and re-derived nothing, so §9.3's
deck and the daemon's grab gate kept reading verdicts computed under
rules that no longer existed.

Drives #241's walker from a policy id: root by root through
reclassify::root, so the skip rules and the leave-unchanged-rows-alone
rule stay in one place. A rename touches no rule and walks nothing.

Inline still holds at this width. Measured on a release build over 2000
titles and 10 000 stored releases across two roots sharing one policy:
0.36 s when no verdict moves, 2.7 s when all 10 000 do. DESIGN.md §5.1
now names four actions and carries those numbers.

just ci passed through the gate.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 11:40:32 +01:00
Miguel Palhas c454b3ec11 Merge #244: normalise stored root paths
Closes #244
2026-08-25 11:29:13 +01:00
Miguel Palhas c4d4ade4da fix(api): normalise stored root paths
#243 normalised the incoming path but compared it against the value read
raw from the database, so a root stored with a trailing separator never
compared equal. Every edit of it -- a policy change included -- took the
relocation branch, where each planned destination is its own source and
the pre-check refuses. That root could not be edited at all.

`update` now normalises both sides, and hands `relocate_root` the
normalised stored value. `path_is_free` normalises the stored side in SQL
and `create` goes through it too, so `/mnt/x` and `/mnt/x/` cannot be two
roots for one directory -- the unique index compares raw strings and
cannot see that.

Migration 0031 strips the separator from rows already written. It skips
any row whose stripped form another row would also hold, rather than
tripping the unique index: a migration that cannot apply stops the daemon
booting, which is worse than two roots naming one directory.

Also from the same review: `undo` recorded only the leaf directory, so a
failed move into `/mnt/media-v2/tv/kids` left `tv` behind. It now records
every level `create_dir_all` materialised, deepest first, and still never
touches one that was already on disk.

Refs #244.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 11:28:35 +01:00
Miguel Palhas cfa0eaa77d Merge #231: settings rows on one line with icon actions
Closes #231
2026-08-25 11:21:35 +01:00
Miguel Palhas 955a0f309c feat(web): settings rows on one line with icon actions
Each root and policy row is one line: identity left, chips at the right
edge, then pencil/trash icon controls — the episode-row idiom instead of
a two-line group. Every icon action's aria-label names the action and
the row (edit root /mnt/media/tv/main). Delete keeps the arm-then-confirm
behaviour via the shared armedDeleteIcon; the local text-button
armedDelete is gone with it. Under 46rem the row wraps like an episode
row: identity, chips full-width, actions keeping the right edge.

Closes #231

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-25 11:19:05 +01:00
Miguel Palhas df1af383a3 refactor(web): extract shared icon controls to module
The #230 icon block (glyph set, icon(), armedDeleteIcon()) moves from
main.ts to icons.ts so the settings rows (#231) can consume it without
an import cycle. The shared set is extended with one pencil glyph for
the rows' edit action — extension of the shared block, not a fork.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-25 11:19:05 +01:00
Miguel Palhas 7df83133b3 Merge #241: reclassify on root and policy changes
Closes #241
2026-08-25 11:18:51 +01:00
Miguel Palhas 5974d43785 Merge remote-tracking branch 'origin/blitz/feedback-2' into issue/241-reclassify-on-root-change 2026-08-25 11:17:47 +01:00
Miguel Palhas 43e65514ed fix(api): reclassify on root and policy changes
Moving a title to a root with a different policy, and pointing a root
at a different policy via PUT /api/roots/{id}, both changed the
effective policy without re-deriving stored verdicts — which §9.3's
deck and the daemon's manual-grab gate read. Both now run the same
reclassify the overrides path uses, inline in the request; §5.1 states
the contract, and relocate.rs no longer claims the move alone makes
the policy apply.

PUT /api/policies/{id} has the same gap one level up; noted on #241
for its own issue.

Closes #241

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-25 11:17:33 +01:00
Miguel Palhas a42f3970d2 Merge #230: icon-only controls and readouts
Closes #230
2026-08-25 11:10:04 +01:00
Miguel Palhas 2a58a103d3 Merge #239: measure attention window from failure
Closes #239
2026-08-25 11:06:42 +01:00
Miguel Palhas 528aadf59c fix(daemon): measure attention window from failure
Closes the gap #239 describes: §5.7's 30-day window was filtered on
grabbed_at, so a torrent stalling past the window before hard-failing
at import never surfaced in the needs-a-decision queue. grabs gains
failed_at (migration 0030, backfilled from grabbed_at for existing
failed rows), the import tick stamps it on hard fail, and every window
query in the daemon notifier and the attention endpoint reads it.
§5.7 now states the anchor explicitly. §6.2's pack backoff stays on
grabbed_at deliberately; noted on the issue.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-25 11:05:59 +01:00
Miguel Palhas 070fd3d7ba Merge blitz/feedback-2: fmt fix and attention queue 2026-08-25 10:58:07 +01:00
Miguel Palhas 0a0c6359a4 Merge #243: trailing-slash self-conflict and stray relocation dir
Closes #243
2026-08-25 10:58:07 +01:00
Miguel Palhas 0b4ded3fe9 feat(web): icon-only controls and readouts (#230)
Trash replaces the remove labels on title pages and season and
episode rows, a magnifier replaces the deck control, the season
on-disk readout carries a drive glyph before its 0/10, and the
TMDB, TVDB, IMDb and Rotten Tomatoes links carry drawn marks
shipped inline (wordmark badges plus a tomato), never remote
images. Every icon-only control keeps an aria-label naming the
action and the row or title it acts on, and the arm-then-confirm
delete speaks a visible amber "confirm" while armed, so the first
hit never destroys.

Shared CSS lives in one block in style.css headed
"icon-only controls (#230)" — .icon, .control-icon, .icon-mark —
for #231 to consume for the settings rows.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-25 10:58:07 +01:00
Miguel Palhas 6fa88b8f1f fix(api): trailing-slash self-conflict and stray relocation dir
A root path differing only by a trailing separator now normalises to
the same value on create and update, so PUT no longer treats a no-op
edit as a relocation whose destinations conflict with their own
sources.

A failed root-path move now removes the new root directory it created
for that move, but only when it created it — a directory that already
existed at the destination is left alone, matching the retry
guarantee relocate.rs documents.

Refs #243
2026-08-25 10:56:13 +01:00
Miguel Palhas afc17ca34a Merge #238: queue only targets still waiting for a file
Closes #238
2026-08-25 10:50:56 +01:00
Miguel Palhas 6847d25cf5 feat: queue only targets still waiting for a file
The needs-a-decision queue had no liveness condition on the season lane
and none at all in the API reader, so a season pack that hard-failed
twice, fell back to per-episode grabbing exactly as §6.2 intends, and was
then fully acquired kept notifying for 30 days, and
`GET /api/queues/attention` listed titles the daemon never notified on.

DESIGN.md §5.7 now states the third face of the same rule alongside the
count and the window: a movie or an episode is queued while `wanted` and
not `available`; a season, holding no intent of its own (§4.1), while at
least one of its episodes is. Both readers apply it on all three lanes.

`just ci` passed through the gate.
2026-08-25 10:49:02 +01:00
Miguel Palhas 50056a2bd9 style(api): reformat two error arms
Pre-existing rustfmt drift on the branch base; `just ci` fails on it
before reaching anything else.
2026-08-25 10:48:56 +01:00
Miguel Palhas c962998a2b Merge #229: drop the back button, banner meets the rail
Closes #229
2026-08-25 10:42:05 +01:00
Miguel Palhas 815a072ef0 fix(web): drop back button from movie and series pages (#229)
The rail, browser back, and Esc already cover navigation — the back
button was a third way to do what two other things already do, and it
cost the page its first line.

- Remove #movie-back and #series-back buttons and their event listeners
- Focus lands on the title element (tabindex=-1) instead of the removed
  button on open
- TV releases back button is untouched
- Hero banner now meets the rail with zero top padding on movie/series
- Esc and parent-route behaviour unchanged
- Deep link fallback for TV releases uses #nav-library instead of the
  removed #series-back
2026-08-25 10:40:37 +01:00
Miguel Palhas 690eaeda5c fix(api): let a stranded folder be retried, not refused
Two findings from the integration review of this milestone, both caused
by two sessions editing the same code without seeing each other.

The retry that relocate.rs documents did not converge. The conflict
pre-check ran over every planned rename, including renames whose source
was already gone, and the skip for a missing source came after it. An
undo is best-effort, so a failed move can leave one folder at the
destination with its row still naming the source; every later attempt
then 409'd against the operator's own half-moved library and the only
way out was moving the folder back by hand. The pre-check now skips a
rename whose source is absent, which is what the perform loop already
did. Verified: the new test returns 409 without the change and 200 with.

ApiError::Filesystem rendered as "files not removed: {error}". That was
written for the delete lane; #228 and #236 then returned the same
variant for move failures, so a root path change with one unwritable
folder reported "files not removed" after an operation that removed
nothing. The variant now renders the caller's message and the two
delete lanes carry their own context.
2026-08-25 10:29:38 +01:00
Miguel Palhas d23ae0ebcf Merge #236: move title folders on root path change
Closes #236
2026-08-25 10:21:01 +01:00
Miguel Palhas efb47d64e7 feat(api): move title folders on root path change
Changing a root's path rewrote the row and moved nothing, so every title
under it was mislocated at once. It now reuses the #228 mover: plan every
rename, refuse a destination that already exists, rewrite the media_files
rows in the same transaction as the row change.

The move is all or nothing. A root row carries one path, so a half-moved
library would have to describe both places; instead one folder that
cannot move puts back the ones that already did and leaves the root's
path alone, and the same request is the retry.

just ci ran clean through the gate: 498 tests passed.
2026-08-25 10:20:37 +01:00
Miguel Palhas 442ee3b022 Merge #226: require two recent failures to queue a season
Closes #226
2026-08-25 10:10:50 +01:00
Miguel Palhas ac0e80c044 feat: require two recent failures to queue a season
The season branch of the attention queue listed a season on one failed
grab of any age, so `GET /api/queues/attention` returned Rick and Morty
with every season it has and buried the one that needed attention.

Two changes, both stated in DESIGN.md §5.7:

- The season branch now enforces the same bar the episode branch does:
  two grabs that hard-failed on *different* releases.
- A failed grab counts toward the queue for 30 days
  (`arr_db::ATTENTION_WINDOW`). Nothing clears a `grabs` row, so without
  a window the queue only grows and can never be emptied. #181 gave the
  pack guard a backoff curve for the same reason; this is the queue's
  version of §6.2's "it never gives up entirely, it goes quiet". A
  season the operator dealt with stops failing and drops out; one still
  breaking keeps failing (the pack guard retries at worst weekly) and
  stays.

The window applies to all three hard-fail lanes — movie, episode and
season — because DESIGN.md states one rule for the queue, and to the
daemon's needs-a-decision notifier as well as the API, since both read
the same queue and a season-per-failure notification is the same noise
on a different channel. No schema change: `grabs.grabbed_at` already
carries the timestamp.

Gate: `just ci` green (486 tests).
2026-08-25 10:09:07 +01:00
Miguel Palhas 9fc7272765 Merge #228: move title files on root change
Closes #228
2026-08-25 10:02:00 +01:00
Miguel Palhas bce3d3823d feat(api): move title files on root change
Changing a movie's or series' root previously rewrote root_id and left
the files behind, so the §7.4 layout stopped describing the disk and
the root's policy applied to a library the files were not in. Series
had no root control at all.

All roots share one ZFS dataset, so the move is a rename of the title
folder into the new root, never a copy — hardlinks and the seeding
torrent survive it (§7.3). Disk first, row second: a destination that
already holds the folder is a 409, a failed rename leaves the row
unchanged, and a title with nothing on disk moves with no filesystem
work. media_files rows are rewritten in the same transaction as the
root_id, and a successful move triggers the §7.5 Jellyfin refresh.

Issue #228

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-25 09:55:18 +01:00
Miguel Palhas c64c572781 refactor(arr): give arr-api its own jellyfin client
arr-daemon depends on arr-api, so a handler in arr-api can never
reach the daemon's private JellyfinClient. Move it into arr-api and
attach an instance to AppState, so a manual subtitle write can ask
for the same refresh import already does (#195).
2026-08-25 09:47:53 +01:00
Miguel Palhas 8c3e1c4a92 Merge milestone 'Size bands and waivers'
ci / web (push) Successful in 45s
e2e / e2e (push) Successful in 1m30s
ci / rust (push) Successful in 1m38s
A size band is now a rate: floor and target scale by the series'
minutes per episode against a 45-minute reference, so a short-form
show is no longer judged against an hour of video. Shipped band
values are unchanged — the reference runtime is chosen so they keep
their meaning. A missing runtime scales by one, and movies are never
scaled.

A size rejection can also be waived. The override relaxes the floor
for one title into a waiver rather than lifting it, so the release
stays out of automatic grabbing and imports on the record (§5.7).

The runtime migration is numbered 0026 to leave 0024 and 0025 to the
concurrent subtitles milestone; two files claiming one version do not
conflict in git and would have reached main unnoticed.

Closes #208, #209, #210
2026-08-24 22:51:55 +01:00
Miguel Palhas bb2708f7ee chore(db): renumber the runtime migration to 0026
The concurrent subtitles milestone carries 0024_subtitles.sql and
0025_subtitle_settings.sql. Two migrations claiming version 24 do not
conflict in git — the filenames differ — so both would land on main and
sqlx would see a duplicate version. Renumbering here is the half that
does not depend on the other milestone acting.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-24 22:47:18 +01:00
Miguel Palhas 5c5b1e234c Merge #209: scale size bands by episode runtime
Closes #209
2026-08-24 22:47:11 +01:00
Miguel Palhas 917aa4fa76 feat: scale size bands by episode runtime
Implements #209 per §5.5 as amended by #208: a band's floor and target
are rates against a 45-minute reference runtime, scaled by the series'
minutes per episode. A missing or zero runtime applies the bands
unscaled, and movies are never scaled. The runtime is stored on the
series row (new migration), filled on add and by the metadata refresh,
which never blanks a known value against TMDB's frequently-empty
episode_run_time. Composes with #210: allow_below_floor waives against
the scaled floor.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-24 22:45:55 +01:00
Miguel Palhas 024786f356 Merge #210: let a size rejection be waived
Closes #210
2026-08-24 22:16:01 +01:00
Miguel Palhas f0d45996a0 Merge #208: size bands scale with episode runtime
Closes #208
2026-08-24 22:16:01 +01:00
Miguel Palhas 9fee07f080 feat: let a size rejection be waived
A release below §5.5's floor was rejected with no way through, so a
policy wrong about one title left three Rick and Morty S09 packs
visible and none grabbable.

`allow_below_floor` relaxes the floor for one title into a soft fail,
never a pass: the release is waived, so automatic grabbing still skips
it and the import records a §5.7 waiver. The deck offers the one click
on a rejected row where the rule has an override, which is exactly what
§9.3's override is for.

Stored verdicts are re-derived when a title's overrides change — the
deck and the daemon's grab gate both read that column, so without it
the row the operator just acted on would keep reading `rejected`.

Closes #210

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-24 22:15:14 +01:00
Miguel Palhas bd52941a6d docs: size bands scale with episode runtime
A band's floor and target now read as a rate against a 45-minute
reference runtime, scaled by the series' per-episode runtime from
TMDB. Missing or zero runtime falls back to the reference, keeping
today's behaviour. Movies are explicitly unscaled. Closes the axis
question in #208; #209 implements it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-24 21:53:32 +01:00
Miguel Palhas 405358bf24 Merge milestone 'Season packs'
e2e / e2e (push) Successful in 1m16s
ci / web (push) Successful in 1m41s
ci / rust (push) Successful in 1m48s
Size bands describe one episode, so a pack's size is divided by the
episodes it covers before the floor and target are applied — a 2160p
pack no longer loses to a 1080p one on a penalty that scaled with
season length.

A failed pack grab now quiets the pack lane on the shared §6.2 backoff
curve instead of disabling it forever, and a manual season search
waives the window. The season deck names which state it is actually in
rather than blaming backoff for a sweep that was never going to run.

Closes #179, #180, #181, #182
2026-08-24 21:31:38 +01:00
Miguel Palhas 93c064701c Merge #182: name the season deck's real state
Closes #182
2026-08-24 21:27:42 +01:00
Miguel Palhas d474909ab5 Merge #180: score season packs per episode
Closes #180
2026-08-24 21:27:42 +01:00
Miguel Palhas 1e03873209 feat(web): name the season deck's real state
An empty season deck was three truths wearing one message, and the one
it chose to blame was wrong: a season on the per-episode lane sat on
"sweeping indexers…" for the full wait and then blamed a backoff for a
pack search that was never going to run.

`GET /api/series/{id}/seasons/{n}/pack-state` says which lane the
season takes and why, from `season_grab_reason` in arr-core, plus the
failed-pack tally and when #181's window reopens. Seasons gain
`last_pack_search_at`, written only by a season-scoped sweep, so a
pack search that ran and found nothing is a settled answer rather than
a pending one.

The deck then says the true thing in each case, and a season held off
the pack lane by a failure offers the retry that waives its window.

Refs #182
2026-08-24 20:14:48 +01:00
Miguel Palhas 2286cba862 feat(core): score season packs per episode
A size band describes one episode (DESIGN.md §5.5), so both the target
penalty and the floor now compare a release's size divided by the number
of episodes it covers. The caller supplies the count — arr-core has no
IO. Movies and unknown counts divide by one, so movie scoring is
unchanged and pinned by test.

Closes #180

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-24 20:10:23 +01:00
Miguel Palhas 577eef3f31 Merge #181: retry season packs after backoff
Closes #181
2026-08-24 19:52:40 +01:00
Miguel Palhas a5fd543561 Merge #179: size bands are per episode
Closes #179
2026-08-24 19:52:40 +01:00
Miguel Palhas dc6c25f582 fix(daemon): retry season packs after backoff
A failed season-pack grab held the season off the pack lane forever:
pack_hard_failed was a bare EXISTS over failed grabs, so one bad
torrent disabled pack search for good, against §6.2's "it never gives
up entirely, it goes quiet".

The guard now rides the shared backoff curve (backoff_elapsed, 1h → 6h
→ 1d → 3d, capped 7d), counting failed pack grabs as attempts and
anchoring on the latest one's grabbed_at. Both the targeted and RSS
lanes agree. A manual season search waives the window outright — the
season deck is §6.2's escape hatch.

Closes #181.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-24 19:51:28 +01:00
Miguel Palhas 0c2ba6ff15 docs(design): size bands are per episode
A release covering several episodes is measured by size divided by the
episode count, for the target penalty and the floor alike. An unknown
count divides by one, which fails toward rejecting rather than grabbing.

Refs #179
2026-08-24 19:42:04 +01:00
Miguel Palhas a3897df0ab fix(db): keep migration checksums stable
ci / web (push) Successful in 38s
ci / rust (push) Successful in 1m4s
e2e / e2e (push) Successful in 1m14s
Issue #155 removed the sqlx 0.8 rebuild workarounds by editing migrations
0007, 0014 and 0021 in place. Editing an applied migration changes its
checksum, and `migrate()` refuses to run when one no longer matches what
`_sqlx_migrations` recorded, so the daemon exited on startup against any
database that had already applied them — production included.

The sqlx 0.9 bump is the fix and survives: a new migration can carry
`-- no-transaction` so `PRAGMA foreign_keys = OFF` holds and a table
rebuild stops cascade-deleting its children. Only the retroactive cleanup
of migrations that already ran is reverted, along with #153's
`CHECK (title <> '')`, which rode on the 0021 edit and needs a migration
of its own rather than a rewrite of history.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-24 19:14:42 +01:00
Miguel Palhas 83a0b247c9 Merge milestone 'Reliability and first run'
ci / web (push) Successful in 38s
e2e / e2e (push) Successful in 1m30s
ci / rust (push) Successful in 1m45s
Closes #155, #158, #168, #176, #177
2026-08-24 19:05:27 +01:00
Miguel Palhas 74cf82489b Merge #177: distinguish pending refresh from empty
Closes #177
2026-08-24 19:00:29 +01:00
Miguel Palhas b2147fe7b9 feat(web): tell pending seasons from settled empty 2026-08-24 18:59:15 +01:00
Miguel Palhas dc145ae2c1 Merge #158: bound the TMDB response cache
Closes #158
2026-08-24 18:59:01 +01:00
Miguel Palhas 3261741415 fix(meta): bound the TMDB response cache on disk
Closes #158
2026-08-24 18:56:59 +01:00
Miguel Palhas 998717b578 Merge #168: search survives a down TMDB
Closes #168
2026-08-24 18:53:21 +01:00
Miguel Palhas ddf1c5fd93 fix(api): search survives a down TMDB and errors name the upstream 2026-08-24 18:52:15 +01:00
Miguel Palhas 88a353dc68 Merge #176: refresh metadata on add
Closes #176
2026-08-24 18:41:48 +01:00
Miguel Palhas f954db9830 Merge #155: bump sqlx to 0.9, honour no-transaction
Closes #155
2026-08-24 18:41:48 +01:00
Miguel Palhas 93a3485c50 fix(db): drop 0.8 rebuild workarounds, enforce title
With sqlx 0.9 honouring `-- no-transaction`, 0007 loses its
movie_releases_backup stash like 0014 did, and 0021 gains
the CHECK (title <> '') that #153 abandoned because the 0.8
migrator could not run a rebuild with foreign keys off. The
rebuild test now enters at migration 6 so the child links
ride through all three rebuilds, and a new test proves the
'' -> 'TBA' backfill and the rejection of new empty titles.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-24 18:39:22 +01:00
Miguel Palhas d8797e1996 feat(daemon): refresh metadata on add
The metadata lane runs daily, so a series added a moment ago showed no
seasons for up to 24 hours and a movie had no digital release date —
the field §6.2 gates targeted search on.

AppState now carries a MetadataCommand channel alongside the movie,
episode and season ones. Both create handlers send on it after the row
is committed, and a new daemon lane drains it. Its own task rather than
an arm of manual::run: a refresh against TMDB can take a while and must
not sit in front of an operator's manual search.

The add never waits on TMDB and never fails because of it. A refresh
that fails leaves metadata_refreshed_at NULL, which is what the daily
sweep already treats as due, so the title is retried rather than lost.
A command naming a title deleted in between finds no row and does
nothing. METADATA_INTERVAL is unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-24 18:12:10 +01:00
Miguel Palhas ef4722e1dd fix(db): bump sqlx to 0.9, honour no-transaction
sqlx-sqlite 0.8 ignored Migration.no_tx and wrapped every
migration in a transaction, where PRAGMA foreign_keys = OFF
is a no-op — so any table rebuild cascade-deleted children.
0.9 honours `-- no-transaction`, so 0014 drops its
movie_releases_backup / episode_releases_backup workaround
and runs the plain rebuild recipe with foreign keys off.
A migration test rebuilds both parents from a pre-0014
database and asserts the child link rows survive.

Closes #155

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-24 18:10:53 +01:00
Miguel Palhas e9806d7a84 Merge origin/main
ci / web (push) Successful in 1m9s
e2e / e2e (push) Successful in 2m3s
ci / rust (push) Successful in 2m12s
2026-08-24 17:21:54 +01:00
Miguel Palhas 70e9e17136 Merge milestone 'Removal and navigation'
Closes #169, #170, #171, #172, #173, #174, #175
2026-08-24 17:21:45 +01:00
Miguel Palhas a6af920e39 Merge #175: removal controls for series, seasons, episodes
Closes #175
2026-08-24 17:14:00 +01:00
Miguel Palhas 7c30928907 feat(web): series, season and episode removal
Series detail exposes DELETE /api/series/{id} through the movie
removePanel, generalised over endpoints and file rollup instead of
copied. Season headers and on-disk episode rows get the settings
armed-delete control wired to #174's file endpoints, worded for what
they do: files go, wanted clears, the row stays listed.

Closes #175

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-24 17:09:46 +01:00
Miguel Palhas d5fc790839 docs: drop area/web Fable routing rule
ci / web (push) Successful in 48s
e2e / e2e (push) Successful in 1m36s
ci / rust (push) Successful in 1m42s
The rule justified pinning area/web issues to Fable 5 by saying they go
through the impeccable skill. That skill is symlinked into codex as well,
so it says nothing about which model to use. area/web now routes by
difficulty label like every other area; line 47 already requires
impeccable for anything that decides how something looks.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-24 16:55:23 +01:00
Miguel Palhas 3dda666273 Merge #174: delete files and untrack a season or episode
Closes #174
2026-08-24 16:45:21 +01:00
Miguel Palhas 64e28e5930 feat: remove files for one season or episode
DELETE /api/series/{id}/seasons/{n}/files and
DELETE /api/episodes/{id}/files unlink what the scope covers, drop the
matching media_files rows and clear wanted, in one action. 204 on
success, 404 for an unknown season or episode, and a scope with nothing
on disk still clears intent.

Season and episode rows stay: TMDB owns that metadata and the next
refresh would recreate them.

The three scopes share one unlink path. A whole series still resolves to
its title folder (§7.4, atomic); a season or episode resolves to the
recorded file and nothing else, so a narrow call cannot reach a sibling.
The intent clear goes through arr_core::tracking::apply_tracked(false),
the same §4.1 rule #171 landed.

An episode whose file just went is set back to 'missing' when it was
'available', matching what a failed import already does. Closes #174.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-24 16:43:53 +01:00
Miguel Palhas f3336d066e Merge #172: search returns titles only
Closes #172
2026-08-24 16:40:44 +01:00
Miguel Palhas 27525302b2 Merge #173: library is the homepage
Closes #173
2026-08-24 16:40:38 +01:00
Miguel Palhas 4b89245f00 fix(api): drop episode rows from unified search
Search returns titles only (§9.2, amended): the episode branch of the
library query, its json_each token machinery, and the SPA's episode
row rendering are removed. Deep links to episode releases stay.

Fixes #172
2026-08-24 16:39:48 +01:00
Miguel Palhas 7ef24088a5 feat(web): library homepage, chain in settings
Implements the §9.7 shell rulings (issue #173): "/" renders the library
and the board route is gone — every former board target (Esc, cleared
search, detail parent routes, the not-found fallback) lands on the
library. The signal chain moves into /settings as its first section,
lamps still driven by the health poll; the master lamp stays on the
rail. The wordmark is now a real anchor that navigates home.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-24 16:39:07 +01:00
Miguel Palhas 4a611a6794 Merge #171: untracking a season clears wanted
Closes #171
2026-08-24 16:32:02 +01:00
Miguel Palhas 9e003823c6 feat: untracking a season clears its wanted 2026-08-24 16:31:09 +01:00
Miguel Palhas 205855fa7f Merge #170: centre deck column chips in their box
Closes #170
2026-08-24 16:29:25 +01:00
Miguel Palhas fada2ff89a fix(web): centre deck chips in their boxes 2026-08-24 16:28:42 +01:00
Miguel Palhas 60642d8709 Merge #169: amend DESIGN.md for four rulings
Closes #169
2026-08-24 16:21:03 +01:00
Miguel Palhas c37031207e docs: amend DESIGN.md for four rulings
Untracking a season now clears its episodes' wanted flag, unified search
returns titles only, the library is the homepage and the signal chain moves
into settings, and the wordmark links home.

Refs #169
2026-08-24 16:20:13 +01:00
Miguel Palhas 869e3af9c8 Merge milestone 'Feedback pass'
ci / web (push) Successful in 36s
e2e / e2e (push) Successful in 1m10s
ci / rust (push) Successful in 1m19s
Nine issues from a hands-on pass over the deployed build.

Behaviour: adding a series no longer tracks its whole back catalogue
(auto_track applies from the second refresh onward); a series-title
query no longer returns every episode of that series; seasons and
episodes come back newest-first.

UI: cast comes off title detail; buttons and chips share one 2rem
geometry; state chips carry the ramp (green satisfied, amber gap,
violet active, neutral idle); the header drops its verdict line and
sits on the content width; season and episode rows lose their search
button and the deck sweeps itself when opened empty.

DESIGN.md 4.1, 4.2, 9.2 and 9.6 amended first; every other issue
implements against the amended text.

Closes #159, #160, #161, #162, #163, #164, #165, #166, #167
2026-08-24 15:26:12 +01:00
Miguel Palhas e509c37649 Merge #167: tv deck sweeps on open when empty 2026-08-24 15:14:53 +01:00
Miguel Palhas 8a672c1dcf feat(web): tv deck sweeps on open when empty
Drop the standalone search button from season and episode rows; the
deck is the one control. An empty deck fires a targeted sweep on
open and reports progress in place; a deck with results never sweeps
on open. re-search inside the deck forces a fresh sweep.
2026-08-24 14:42:29 +01:00
Miguel Palhas fb03fc3808 Merge #166: lamp-only header on the content width 2026-08-24 14:38:03 +01:00
Miguel Palhas 1add26b7f7 Merge #165: state ramp on status and counts chips 2026-08-24 14:38:03 +01:00
Miguel Palhas fa5d876c09 chore(web): sync design.json to the state ramp
The token system still mirrored the pre-amendment palette and carried
the rule 'Don't colour complete green or incomplete amber', which now
contradicts DESIGN.md 4.2 and would have had the next session revert
the ramp. Drops the four --status-* tokens no rule consumes any more;
satisfaction reuses --signal-ok and a gap --signal-warn.

Committed by the blitz orchestrator: the authoring session wedged.
2026-08-24 14:37:52 +01:00
Miguel Palhas 0cb8111678 fix(web): pin the version readout to the rail edge 2026-08-24 14:36:18 +01:00
Miguel Palhas d1d6b4f9a0 feat(web): lamp-only header on the content width
Drops #master-verdict entirely - element, .rail-verdict rules and every
line that wrote into it. The lamp is the only header signal; the
unreachable detail moves onto the board's arr module so nothing is lost.
Rail contents move into .rail-inner capped at the deck's 68rem and
centred, leaving the panel full-bleed.

Committed by the blitz orchestrator: the authoring session wedged with
the work complete on disk and stopped accepting input.
2026-08-24 14:36:18 +01:00
Miguel Palhas 2dbb0f48f7 feat(web): state ramp on status and counts chips
Per amended DESIGN.md §4.2: green --signal-ok for on disk/complete,
amber --signal-warn for wanted-but-missing, violet --status-airing for
downloading, neutral for untracked and waiting. Stars go amber. Words
stay in every chip; verdict chips untouched.
2026-08-24 13:57:17 +01:00
Miguel Palhas 8221ea633c Merge #163: drop cast from title detail 2026-08-24 12:34:34 +01:00
Miguel Palhas 7d4ddb7331 refactor(web): drop cast from title detail
DESIGN.md §9.6 no longer carries cast (#163). Removes the cast
sections, the MetadataCastMember schema and field from both metadata
responses, and every .cast-* rule. arr-meta keeps CastMember and the
credits append; the TMDB call is unchanged.
2026-08-24 12:34:00 +01:00
Miguel Palhas ed39b1ca49 Merge #164: one control geometry for buttons and chips 2026-08-24 12:12:08 +01:00
Miguel Palhas 1a4394cf0d feat(web): one control geometry for buttons and chips
Buttons, chips, toggles and the search input converge on --control-h
2rem with --text-xs shared, and .control becomes an inline-flex so
anchor text centres in the title-detail action bar. Chip colour is
untouched (#165 owns it). Verified in agent-browser at 1440px, 46rem
and 40rem; the release deck keeps its column widths and does not
scroll horizontally.
2026-08-24 12:11:08 +01:00
Miguel Palhas 4d5cc120d1 fix(api): newest-first season and episode order
DESIGN.md §9.6 now puts the current season at the top: seasons
descend by number within a series (season 0 lands last under plain
numeric descending) and episodes descend within each season. Changed
the two queries in load_seasons; the SPA renders this order as given.

arr-compat checked and untouched: it reads seasons with its own
ORDER BY number query straight from the database, never through
arr-api, and SeasonResource carries season_number, which Jellyseerr
matches on rather than position. Sonarr's real API also returns
seasons ascending, so the shim keeps the contract it emulates.

The existing vanished-flag test indexed seasons positionally; it now
looks them up by number so it tests the flag, not the order. New test
asserts [2, 1, 0] for seasons and descending episodes within each.
.sqlx regenerated via just db-prepare. just ci green locally.
2026-08-24 12:09:52 +01:00
Miguel Palhas 2c293e7f60 fix(api): episode rows need a token beyond series title
Per amended DESIGN.md 9.2, an episode row now also requires at
least one query token to match the episode title on its own, so
a series-title-only query lists the series and no episodes.
2026-08-24 11:59:18 +01:00
Miguel Palhas 8f7373d73d fix(core): auto-track skips the seeding refresh
A series' first metadata refresh reveals its whole back catalogue, and
apply_auto_track flagged every season as new — adding Rick and Morty
tracked S01-S09 and wanted 91 episodes (#160).

Per DESIGN.md 4.1 the rule applies from the second refresh onward.
The caller passes whether metadata_refreshed_at is set; arr-core stays
IO-free and decides. Wired through the daemon's daily refresh and the
API's create-season endpoint.
2026-08-24 11:40:44 +01:00
Miguel Palhas 2495895717 docs: record five rulings from the deployed build 2026-08-24 11:28:05 +01:00
Miguel Palhas 40620d93b4 fix(web): series page loads; cast art falls back
ci / web (push) Successful in 34s
e2e / e2e (push) Successful in 57s
ci / rust (push) Successful in 1m9s
2026-08-24 03:21:42 +01:00
Miguel Palhas 1c351eb867 Merge #151: library poster grid with list toggle
Closes #151
2026-08-24 03:05:59 +01:00
Miguel Palhas a6cdd0ab0b feat(web): library poster grid with list toggle 2026-08-24 03:04:06 +01:00
Miguel Palhas 365c2a7587 Merge #148: rich search result rows
Closes #148
2026-08-24 01:57:25 +01:00
233 changed files with 18600 additions and 3726 deletions
+8 -8
View File
@@ -123,7 +123,7 @@
},
"scale": {
"displayName": "Type scale",
"purpose": "Seven fixed steps as tokens: --text-2xs 0.6875rem (micro-labels), --text-xs 0.75rem (details, chips), --text-sm 0.8125rem (readouts, controls), --text-base 1rem (body), --text-md 1.0625rem (module names), --text-lg 1.3125rem (master module), --text-xl 1.625rem (wordmark). Components never carry a literal font-size."
"purpose": "Seven fixed steps as tokens: --text-2xs 0.6875rem (micro-labels), --text-xs 0.75rem (details, chips, controls), --text-sm 0.8125rem (readouts), --text-base 1rem (body), --text-md 1.0625rem (module names), --text-lg 1.3125rem (master module), --text-xl 1.625rem (wordmark). Components never carry a literal font-size. One control geometry: buttons and chips share --control-h 2rem and --text-xs, so a season line reads as one system."
}
},
"shadows": [
@@ -189,7 +189,7 @@
"refersTo": "ground",
"description": "The §9.3 column unit: a small machined panel holding one mono value (score, resolution, source, HDR, audio, size, seeders). Fixed rhythm so columns align; never a soft pill.",
"html": "<span class=\"chip readout\">2160p</span>",
"css": ".chip { display: inline-flex; align-items: center; gap: var(--space-1); min-height: 1.375rem; padding: 0 var(--space-2); background: var(--panel-raised); border: 1px solid var(--line); border-radius: var(--radius); font-family: var(--font-readout); font-size: var(--text-xs); color: var(--ink-muted); }"
"css": ".chip { display: inline-flex; align-items: center; gap: var(--space-1); min-height: var(--control-h); padding: 0 var(--space-2); background: var(--panel-raised); border: 1px solid var(--line); border-radius: var(--radius); font-family: var(--font-readout); font-size: var(--text-xs); color: var(--ink-muted); }"
},
{
"name": "Verdict chip",
@@ -205,7 +205,7 @@
"refersTo": "status-channel",
"description": "Library status (§4.2): airing / incomplete / waiting / complete / ended. Informational, not severity — colour marks activity in one channel-violet family (airing brightest, incomplete, waiting dimmest), satisfaction is neutral (complete bright ink, ended faint). The mono word is always present.",
"html": "<span class=\"chip readout\" data-status=\"airing\">airing</span>",
"css": ".chip[data-status=\"airing\"] { color: var(--status-airing); border-color: oklch(from var(--status-airing) l c h / 45%); } .chip[data-status=\"incomplete\"] { color: var(--status-incomplete); } .chip[data-status=\"waiting\"] { color: var(--status-waiting); } .chip[data-status=\"complete\"] { color: var(--status-complete); } .chip[data-status=\"ended\"] { color: var(--status-ended); }"
"css": ".chip[data-status=\"airing\"] { color: var(--status-airing); border-color: oklch(from var(--status-airing) l c h / 45%); } .chip[data-status=\"incomplete\"] { color: var(--signal-warn); border-color: oklch(from var(--signal-warn) l c h / 55%); } .chip[data-status=\"complete\"], .chip[data-status=\"ended\"] { color: var(--signal-ok); border-color: oklch(from var(--signal-ok) l c h / 45%); } .chip[data-counts=\"complete\"] { color: var(--signal-ok); } .chip[data-counts=\"partial\"] { color: var(--signal-warn); }"
},
{
"name": "Module (channel strip)",
@@ -229,15 +229,15 @@
"refersTo": "accent",
"description": "The interactive element: mono uppercase, phosphor-cyan text, panel ground, ≥44px hit target. Cyan belongs to interaction only — never to status.",
"html": "<button type=\"button\" class=\"control\">probe</button>",
"css": ".control { min-height: 2.75rem; padding: 0 var(--space-4); font-family: var(--font-readout); text-transform: uppercase; letter-spacing: 0.08em; color: var(--accent); background: var(--panel-raised); border: 1px solid var(--line-strong); border-radius: var(--radius); }"
"css": ".control { display: inline-flex; align-items: center; justify-content: center; min-height: var(--control-h); padding: 0 var(--space-4); font-family: var(--font-readout); font-size: var(--text-xs); text-transform: uppercase; letter-spacing: 0.08em; color: var(--accent); background: var(--panel-raised); border: 1px solid var(--line-strong); border-radius: var(--radius); }"
},
{
"name": "Rail",
"kind": "nav",
"refersTo": "ground",
"description": "Full-width panel strip framing the board top (identity + verdict + controls) and bottom (readouts). Hairline-ruled, panel ground, inset highlight.",
"html": "<header class=\"rail\"><div class=\"rail-id\"><span class=\"lamp\" data-state=\"ok\" aria-hidden=\"true\"></span><h1 class=\"wordmark\">arr</h1></div><p class=\"rail-verdict readout\" data-tone=\"ok\">all signals nominal</p></header>",
"css": ".rail { display: flex; align-items: center; gap: var(--space-6); padding: var(--space-3) var(--space-6); border-bottom: 1px solid var(--line); background: var(--panel); box-shadow: inset 0 1px var(--highlight); }"
"description": "Full-width panel strip framing the board top (identity + controls) and bottom (readouts). Hairline-ruled, panel ground, inset highlight. Contents sit in a .rail-inner column capped at 68rem, centred — the panel stays full-bleed. The lamp beside the wordmark is the only header status signal.",
"html": "<header class=\"rail\"><div class=\"rail-inner\"><div class=\"rail-id\"><span class=\"lamp\" data-state=\"ok\" aria-hidden=\"true\"></span><h1 class=\"wordmark\">arr</h1></div></div></header>",
"css": ".rail { padding: var(--space-3) var(--space-6); border-bottom: 1px solid var(--line); background: var(--panel); box-shadow: inset 0 1px var(--highlight); } .rail-inner { display: flex; align-items: center; gap: var(--space-6); width: 100%; max-width: 68rem; margin-inline: auto; }"
}
],
"narrative": {
@@ -300,7 +300,7 @@
"Do take every colour, spacing and font-size from the :root tokens; the token check (web/scripts/check-tokens.mjs) fails CI on literals."
],
"donts": [
"Don't colour `complete` green or `incomplete` amber — library status is informational, not a severity ramp.",
"Colour state chips on the ramp — `complete` and `ended` green, `incomplete` and a partial count amber, `airing` and `downloading` violet, `waiting` and `untracked` neutral. The word stays in every chip, so state survives with colour removed.",
"Don't use fault red for a rejected release; red means the daemon itself is broken.",
"Don't introduce a second interactive hue or use cyan for a status.",
"Don't box content in soft-shadowed rounded cards or add glass/blur.",
+9 -3
View File
@@ -29,9 +29,15 @@ reads the library as derived status, not flags.
counts behind cyan show/hide toggles. Release name is a secondary
truncated line, full string plus indexer/date/bytes on row expand. Every
waived/rejected row carries a verdict chip naming the rule. Grab is its
own control per row; on a waived row it is "waive + grab" and writes the
per-title override (rule→override mapping in releases.ts — bookkeeping,
never policy; verdicts always come from the API). Esc steps back one
own control per row; where the rule that failed has an override it reads
"waive + grab" and writes the per-title override (rule→override mapping in
releases.ts — bookkeeping, never policy; verdicts always come from the
API). A rejected row offers it too — §9.3's one click exists precisely for
the rule the operator disagrees with, and a size rejection (#210) is only
ever rejected. What the click produces stays a waiver: the row re-reads as
a dashed WAIVED, never eligible. When ELIGIBLE is empty and something is
waivable, its "none" line carries the count, so the way out is not folded
inside a collapsed bucket. Esc steps back one
layer: deck → search results → board.
- Removal (§7.4, issue 104): REMOVE is a quiet control in the deck head,
never a row affordance — a dense list is the wrong place for a delete.
@@ -1,6 +1,6 @@
{
"db_name": "SQLite",
"query": "UPDATE movies SET metadata_refreshed_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')\n WHERE id = ?",
"query": "UPDATE movies SET metadata_refreshed_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')\n WHERE id = ?",
"describe": {
"columns": [],
"parameters": {
@@ -8,5 +8,5 @@
},
"nullable": []
},
"hash": "de2153eefd6ec03ceeaf640d599271834cb06c94eda7a9956c5bd7399593c0ae"
"hash": "0516e88e07d3708ba4ccc52d7ba635d45e9d1b2c639b71102e241f50982502eb"
}
@@ -0,0 +1,33 @@
{
"db_name": "SQLite",
"query": "SELECT se.number AS \"number!: i64\", COUNT(e.id) AS \"episodes!: i64\"\n FROM seasons se LEFT JOIN episodes e ON e.season_id = se.id\n WHERE se.series_id = ?\n GROUP BY se.number",
"describe": {
"columns": [
{
"name": "number!: i64",
"ordinal": 0,
"type_info": "Integer",
"origin": {
"Table": {
"table": "seasons",
"name": "number"
}
}
},
{
"name": "episodes!: i64",
"ordinal": 1,
"type_info": "Integer",
"origin": "Expression"
}
],
"parameters": {
"Right": 1
},
"nullable": [
false,
false
]
},
"hash": "05b52c731b198cc338c15afff3b0c22ee77649a368081580a1f13ff29288325d"
}
@@ -0,0 +1,86 @@
{
"db_name": "SQLite",
"query": "SELECT s.id AS \"series_id!: i64\", s.title AS \"series_title!: String\", s.tvdb_id AS series_tvdb_id, s.original_language,\n se.number AS \"season_number!: i64\", e.number AS \"episode_number!: i64\"\n FROM episodes e\n JOIN seasons se ON se.id = e.season_id\n JOIN series s ON s.id = se.series_id\n WHERE e.id = ?",
"describe": {
"columns": [
{
"name": "series_id!: i64",
"ordinal": 0,
"type_info": "Integer",
"origin": {
"Table": {
"table": "series",
"name": "id"
}
}
},
{
"name": "series_title!: String",
"ordinal": 1,
"type_info": "Text",
"origin": {
"Table": {
"table": "series",
"name": "title"
}
}
},
{
"name": "series_tvdb_id",
"ordinal": 2,
"type_info": "Integer",
"origin": {
"Table": {
"table": "series",
"name": "tvdb_id"
}
}
},
{
"name": "original_language",
"ordinal": 3,
"type_info": "Text",
"origin": {
"Table": {
"table": "series",
"name": "original_language"
}
}
},
{
"name": "season_number!: i64",
"ordinal": 4,
"type_info": "Integer",
"origin": {
"Table": {
"table": "seasons",
"name": "number"
}
}
},
{
"name": "episode_number!: i64",
"ordinal": 5,
"type_info": "Integer",
"origin": {
"Table": {
"table": "episodes",
"name": "number"
}
}
}
],
"parameters": {
"Right": 1
},
"nullable": [
false,
false,
true,
true,
false,
false
]
},
"hash": "0618d006da5cdf0d86d851cce7ba1fcd279d4542e6aa1f8d8d80bf96746e1f33"
}
@@ -6,34 +6,59 @@
{
"name": "id!: i64",
"ordinal": 0,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "media_files",
"name": "id"
}
}
},
{
"name": "path!: String",
"ordinal": 1,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "media_files",
"name": "path"
}
}
},
{
"name": "size!: i64",
"ordinal": 2,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "media_files",
"name": "size"
}
}
},
{
"name": "probed?: serde_json::Value",
"ordinal": 3,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "media_files",
"name": "probed"
}
}
},
{
"name": "waiver?: String",
"ordinal": 4,
"type_info": "Null"
"type_info": "Null",
"origin": "Expression"
}
],
"parameters": {
"Right": 1
},
"nullable": [
true,
false,
false,
false,
true,
@@ -0,0 +1,98 @@
{
"db_name": "SQLite",
"query": "\n SELECT s.id AS \"series_id!: i64\", s.tmdb_id AS \"tmdb_id!: i64\",\n s.title AS \"title!: String\", s.year,\n e.id AS \"episode_id!: i64\",\n se.number AS \"season_number!: i64\", e.number AS \"episode_number!: i64\"\n FROM episodes e\n JOIN seasons se ON se.id = e.season_id\n JOIN series s ON s.id = se.series_id\n JOIN roots root ON root.id = s.root_id\n WHERE root.audience = 'kids'\n AND s.blocked = 0\n AND e.wanted = 1 AND e.state = 'missing' AND e.search_attempts > 0\n AND NOT EXISTS (\n SELECT 1 FROM episode_releases er\n JOIN releases r ON r.id = er.release_id\n WHERE er.episode_id = e.id AND r.verdict IN ('eligible', 'waived')\n )\n ORDER BY se.number, e.number\n ",
"describe": {
"columns": [
{
"name": "series_id!: i64",
"ordinal": 0,
"type_info": "Integer",
"origin": {
"Table": {
"table": "series",
"name": "id"
}
}
},
{
"name": "tmdb_id!: i64",
"ordinal": 1,
"type_info": "Integer",
"origin": {
"Table": {
"table": "series",
"name": "tmdb_id"
}
}
},
{
"name": "title!: String",
"ordinal": 2,
"type_info": "Text",
"origin": {
"Table": {
"table": "series",
"name": "title"
}
}
},
{
"name": "year",
"ordinal": 3,
"type_info": "Integer",
"origin": {
"Table": {
"table": "series",
"name": "year"
}
}
},
{
"name": "episode_id!: i64",
"ordinal": 4,
"type_info": "Integer",
"origin": {
"Table": {
"table": "episodes",
"name": "id"
}
}
},
{
"name": "season_number!: i64",
"ordinal": 5,
"type_info": "Integer",
"origin": {
"Table": {
"table": "seasons",
"name": "number"
}
}
},
{
"name": "episode_number!: i64",
"ordinal": 6,
"type_info": "Integer",
"origin": {
"Table": {
"table": "episodes",
"name": "number"
}
}
}
],
"parameters": {
"Right": 0
},
"nullable": [
false,
false,
false,
true,
false,
false,
false
]
},
"hash": "06eca0d86be94dc4615cfaa1f75f630b89ab665e0c4323d8c1aa6521c200b86d"
}
@@ -1,26 +0,0 @@
{
"db_name": "SQLite",
"query": "SELECT normalised_name AS \"normalised_name!: String\", infohash FROM blacklist",
"describe": {
"columns": [
{
"name": "normalised_name!: String",
"ordinal": 0,
"type_info": "Text"
},
{
"name": "infohash",
"ordinal": 1,
"type_info": "Text"
}
],
"parameters": {
"Right": 0
},
"nullable": [
false,
true
]
},
"hash": "071c14544e225001d31c5da60c90d3144fc5a071893c74b2c1eea51bfe00ac97"
}
@@ -6,14 +6,20 @@
{
"name": "id",
"ordinal": 0,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "seasons",
"name": "id"
}
}
}
],
"parameters": {
"Right": 3
},
"nullable": [
true
null
]
},
"hash": "0aea0f15e8a03a37b5d9d32d3c7caffa70f9ec44f5a5da1dc0560f29f445ceab"
@@ -6,7 +6,8 @@
{
"name": "count!: i64",
"ordinal": 0,
"type_info": "Integer"
"type_info": "Integer",
"origin": "Expression"
}
],
"parameters": {
@@ -0,0 +1,62 @@
{
"db_name": "SQLite",
"query": "\n SELECT s.id AS \"series_id!: i64\", s.title AS \"title!: String\", s.year,\n g.target_id AS \"season_id!: i64\"\n FROM grabs g\n JOIN seasons se ON g.target_kind = 'season' AND se.id = g.target_id\n JOIN series s ON s.id = se.series_id\n WHERE g.state = 'failed'\n AND g.failed_at >= strftime('%Y-%m-%dT%H:%M:%fZ', 'now', ?)\n AND EXISTS (\n SELECT 1 FROM episodes e\n WHERE e.season_id = se.id\n AND e.wanted = 1 AND e.state != 'available'\n )\n GROUP BY s.id, s.title, s.year, se.id\n HAVING count(DISTINCT g.release_id) >= 2\n ",
"describe": {
"columns": [
{
"name": "series_id!: i64",
"ordinal": 0,
"type_info": "Integer",
"origin": {
"Table": {
"table": "series",
"name": "id"
}
}
},
{
"name": "title!: String",
"ordinal": 1,
"type_info": "Text",
"origin": {
"Table": {
"table": "series",
"name": "title"
}
}
},
{
"name": "year",
"ordinal": 2,
"type_info": "Integer",
"origin": {
"Table": {
"table": "series",
"name": "year"
}
}
},
{
"name": "season_id!: i64",
"ordinal": 3,
"type_info": "Integer",
"origin": {
"Table": {
"table": "grabs",
"name": "target_id"
}
}
}
],
"parameters": {
"Right": 1
},
"nullable": [
false,
false,
true,
false
]
},
"hash": "0c97b43e22b1c83c5a7131ce9a07fa96cc595454d8803b3c4307074ab273eb1d"
}
@@ -6,54 +6,108 @@
{
"name": "id!: i64",
"ordinal": 0,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "movies",
"name": "id"
}
}
},
{
"name": "tmdb_id!: i64",
"ordinal": 1,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "movies",
"name": "tmdb_id"
}
}
},
{
"name": "title!: String",
"ordinal": 2,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "movies",
"name": "title"
}
}
},
{
"name": "year",
"ordinal": 3,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "movies",
"name": "year"
}
}
},
{
"name": "original_language",
"ordinal": 4,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "movies",
"name": "original_language"
}
}
},
{
"name": "search_attempts!: i64",
"ordinal": 5,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "movies",
"name": "search_attempts"
}
}
},
{
"name": "last_searched_at",
"ordinal": 6,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "movies",
"name": "last_searched_at"
}
}
},
{
"name": "digital_release",
"ordinal": 7,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "movies",
"name": "digital_release"
}
}
},
{
"name": "metadata_refreshed_at",
"ordinal": 8,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "movies",
"name": "metadata_refreshed_at"
}
}
}
],
"parameters": {
"Right": 1
},
"nullable": [
true,
false,
false,
false,
true,
@@ -6,24 +6,42 @@
{
"name": "id!: i64",
"ordinal": 0,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "owners",
"name": "id"
}
}
},
{
"name": "name!: String",
"ordinal": 1,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "owners",
"name": "name"
}
}
},
{
"name": "ntfy_topic!: String",
"ordinal": 2,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "owners",
"name": "ntfy_topic"
}
}
}
],
"parameters": {
"Right": 0
},
"nullable": [
true,
false,
false,
false
]
@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "UPDATE movies\n SET title = ?, year = ?, original_language = ?, digital_release = ?,\n imdb_id = ?, poster_path = ?, backdrop_path = ?, vote_average = ?,\n metadata_refreshed_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now'),\n search_attempts = 0, last_searched_at = NULL,\n updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')\n WHERE id = ? AND (\n title IS NOT ? OR year IS NOT ? OR original_language IS NOT ?\n OR digital_release IS NOT ? OR imdb_id IS NOT ?\n OR poster_path IS NOT ? OR backdrop_path IS NOT ?\n OR vote_average IS NOT ?\n )",
"describe": {
"columns": [],
"parameters": {
"Right": 17
},
"nullable": []
},
"hash": "0f0966881a69a9f4009df7b27ebeadce81719e044c8fb65ba404b45b70bf8d8c"
}
@@ -1,12 +0,0 @@
{
"db_name": "SQLite",
"query": "DELETE FROM media_files WHERE owner_kind = 'episode' AND owner_id IN (\n SELECT e.id FROM episodes e JOIN seasons se ON se.id = e.season_id\n WHERE se.series_id = ?)",
"describe": {
"columns": [],
"parameters": {
"Right": 1
},
"nullable": []
},
"hash": "0ff61f3af9e7185dcd1d506027381c591fab8dcf3e2c223e8cbb82db00e2f02b"
}
@@ -0,0 +1,98 @@
{
"db_name": "SQLite",
"query": "SELECT required_audio AS \"required_audio!: String\",\n dub_blacklist AS \"dub_blacklist!: String\",\n hdr_rules AS \"hdr_rules!: String\",\n size_bands AS \"size_bands!: String\",\n resolution_pref AS \"resolution_pref!: String\",\n source_weights AS \"source_weights!: String\",\n score_weights AS \"score_weights!: String\"\n FROM policies WHERE id = ?",
"describe": {
"columns": [
{
"name": "required_audio!: String",
"ordinal": 0,
"type_info": "Text",
"origin": {
"Table": {
"table": "policies",
"name": "required_audio"
}
}
},
{
"name": "dub_blacklist!: String",
"ordinal": 1,
"type_info": "Text",
"origin": {
"Table": {
"table": "policies",
"name": "dub_blacklist"
}
}
},
{
"name": "hdr_rules!: String",
"ordinal": 2,
"type_info": "Text",
"origin": {
"Table": {
"table": "policies",
"name": "hdr_rules"
}
}
},
{
"name": "size_bands!: String",
"ordinal": 3,
"type_info": "Text",
"origin": {
"Table": {
"table": "policies",
"name": "size_bands"
}
}
},
{
"name": "resolution_pref!: String",
"ordinal": 4,
"type_info": "Text",
"origin": {
"Table": {
"table": "policies",
"name": "resolution_pref"
}
}
},
{
"name": "source_weights!: String",
"ordinal": 5,
"type_info": "Text",
"origin": {
"Table": {
"table": "policies",
"name": "source_weights"
}
}
},
{
"name": "score_weights!: String",
"ordinal": 6,
"type_info": "Text",
"origin": {
"Table": {
"table": "policies",
"name": "score_weights"
}
}
}
],
"parameters": {
"Right": 1
},
"nullable": [
false,
false,
false,
false,
false,
false,
false
]
},
"hash": "100e5e9297a9a917eb107c673ac492efe0df0123d9def212d84b88e57aaa0484"
}
@@ -6,29 +6,53 @@
{
"name": "id!: i64",
"ordinal": 0,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "grabs",
"name": "id"
}
}
},
{
"name": "infohash!: String",
"ordinal": 1,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "grabs",
"name": "infohash"
}
}
},
{
"name": "target_kind!: String",
"ordinal": 2,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "grabs",
"name": "target_kind"
}
}
},
{
"name": "target_id!: i64",
"ordinal": 3,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "grabs",
"name": "target_id"
}
}
}
],
"parameters": {
"Right": 0
},
"nullable": [
true,
false,
false,
false,
false
@@ -6,32 +6,68 @@
{
"name": "id!: i64",
"ordinal": 0,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "roots",
"name": "id"
}
}
},
{
"name": "kind!: String",
"ordinal": 1,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "roots",
"name": "kind"
}
}
},
{
"name": "audience!: String",
"ordinal": 2,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "roots",
"name": "audience"
}
}
},
{
"name": "path!: String",
"ordinal": 3,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "roots",
"name": "path"
}
}
},
{
"name": "policy_id!: i64",
"ordinal": 4,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "policies",
"name": "id"
}
}
},
{
"name": "policy_name!: String",
"ordinal": 5,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "policies",
"name": "name"
}
}
}
],
"parameters": {
@@ -6,7 +6,13 @@
{
"name": "id!: i64",
"ordinal": 0,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "releases",
"name": "id"
}
}
}
],
"parameters": {
@@ -6,7 +6,8 @@
{
"name": "exists!: bool",
"ordinal": 0,
"type_info": "Integer"
"type_info": "Integer",
"origin": "Expression"
}
],
"parameters": {
@@ -6,7 +6,13 @@
{
"name": "tmdb_id",
"ordinal": 0,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "movies",
"name": "tmdb_id"
}
}
}
],
"parameters": {
@@ -6,84 +6,174 @@
{
"name": "id!: i64",
"ordinal": 0,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "movies",
"name": "id"
}
}
},
{
"name": "tmdb_id!: i64",
"ordinal": 1,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "movies",
"name": "tmdb_id"
}
}
},
{
"name": "title!: String",
"ordinal": 2,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "movies",
"name": "title"
}
}
},
{
"name": "year",
"ordinal": 3,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "movies",
"name": "year"
}
}
},
{
"name": "original_language",
"ordinal": 4,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "movies",
"name": "original_language"
}
}
},
{
"name": "root_id!: i64",
"ordinal": 5,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "movies",
"name": "root_id"
}
}
},
{
"name": "wanted!: bool",
"ordinal": 6,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "movies",
"name": "wanted"
}
}
},
{
"name": "overrides!: serde_json::Value",
"ordinal": 7,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "movies",
"name": "overrides"
}
}
},
{
"name": "state!: String",
"ordinal": 8,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "movies",
"name": "state"
}
}
},
{
"name": "blocked!: bool",
"ordinal": 9,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "movies",
"name": "blocked"
}
}
},
{
"name": "search_attempts!: i64",
"ordinal": 10,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "movies",
"name": "search_attempts"
}
}
},
{
"name": "last_searched_at",
"ordinal": 11,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "movies",
"name": "last_searched_at"
}
}
},
{
"name": "poster_path",
"ordinal": 12,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "movies",
"name": "poster_path"
}
}
},
{
"name": "vote_average",
"ordinal": 13,
"type_info": "Float"
"type_info": "Float",
"origin": {
"Table": {
"table": "movies",
"name": "vote_average"
}
}
},
{
"name": "waiver?: serde_json::Value",
"ordinal": 14,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "media_files",
"name": "waiver"
}
}
}
],
"parameters": {
"Right": 1
},
"nullable": [
true,
false,
false,
false,
true,
@@ -6,27 +6,52 @@
{
"name": "episode_id!: i64",
"ordinal": 0,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "episodes",
"name": "id"
}
}
},
{
"name": "path!: String",
"ordinal": 1,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "media_files",
"name": "path"
}
}
},
{
"name": "size!: i64",
"ordinal": 2,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "media_files",
"name": "size"
}
}
},
{
"name": "probed?: serde_json::Value",
"ordinal": 3,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "media_files",
"name": "probed"
}
}
},
{
"name": "waiver?: String",
"ordinal": 4,
"type_info": "Null"
"type_info": "Null",
"origin": "Expression"
}
],
"parameters": {
@@ -1,38 +0,0 @@
{
"db_name": "SQLite",
"query": "\n SELECT s.id AS \"series_id!: i64\", s.title AS \"title!: String\", s.year,\n g.target_id AS \"episode_id!: i64\"\n FROM grabs g\n JOIN episodes e ON g.target_kind = 'episode' AND e.id = g.target_id\n JOIN seasons se ON se.id = e.season_id\n JOIN series s ON s.id = se.series_id\n WHERE g.state = 'failed'\n AND e.wanted = 1 AND e.state != 'available'\n GROUP BY s.id, s.title, s.year, e.id\n HAVING count(DISTINCT g.release_id) >= 2\n ",
"describe": {
"columns": [
{
"name": "series_id!: i64",
"ordinal": 0,
"type_info": "Integer"
},
{
"name": "title!: String",
"ordinal": 1,
"type_info": "Text"
},
{
"name": "year",
"ordinal": 2,
"type_info": "Integer"
},
{
"name": "episode_id!: i64",
"ordinal": 3,
"type_info": "Integer"
}
],
"parameters": {
"Right": 0
},
"nullable": [
false,
false,
true,
false
]
},
"hash": "1878841679d1664139dfedffae9d97ed1764321d76022ff55684969be0171cb6"
}
@@ -6,7 +6,8 @@
{
"name": "exists!: bool",
"ordinal": 0,
"type_info": "Integer"
"type_info": "Integer",
"origin": "Expression"
}
],
"parameters": {
@@ -0,0 +1,182 @@
{
"db_name": "SQLite",
"query": "\n SELECT s.overrides AS \"overrides!: String\",\n r.id AS \"root_id!: i64\",\n r.kind AS \"root_kind!: String\",\n r.audience AS \"root_audience!: String\",\n r.path AS \"root_path!: String\",\n p.id AS \"policy_id!: i64\",\n p.name AS \"policy_name!: String\",\n p.required_audio AS \"required_audio!: String\",\n p.dub_blacklist AS \"dub_blacklist!: String\",\n p.hdr_rules AS \"hdr_rules!: String\",\n p.size_bands AS \"size_bands!: String\",\n p.resolution_pref AS \"resolution_pref!: String\",\n p.source_weights AS \"source_weights!: String\",\n p.score_weights AS \"score_weights!: String\"\n FROM series s\n JOIN roots r ON r.id = s.root_id\n JOIN policies p ON p.id = r.policy_id\n WHERE s.id = ?\n ",
"describe": {
"columns": [
{
"name": "overrides!: String",
"ordinal": 0,
"type_info": "Text",
"origin": {
"Table": {
"table": "series",
"name": "overrides"
}
}
},
{
"name": "root_id!: i64",
"ordinal": 1,
"type_info": "Integer",
"origin": {
"Table": {
"table": "roots",
"name": "id"
}
}
},
{
"name": "root_kind!: String",
"ordinal": 2,
"type_info": "Text",
"origin": {
"Table": {
"table": "roots",
"name": "kind"
}
}
},
{
"name": "root_audience!: String",
"ordinal": 3,
"type_info": "Text",
"origin": {
"Table": {
"table": "roots",
"name": "audience"
}
}
},
{
"name": "root_path!: String",
"ordinal": 4,
"type_info": "Text",
"origin": {
"Table": {
"table": "roots",
"name": "path"
}
}
},
{
"name": "policy_id!: i64",
"ordinal": 5,
"type_info": "Integer",
"origin": {
"Table": {
"table": "policies",
"name": "id"
}
}
},
{
"name": "policy_name!: String",
"ordinal": 6,
"type_info": "Text",
"origin": {
"Table": {
"table": "policies",
"name": "name"
}
}
},
{
"name": "required_audio!: String",
"ordinal": 7,
"type_info": "Text",
"origin": {
"Table": {
"table": "policies",
"name": "required_audio"
}
}
},
{
"name": "dub_blacklist!: String",
"ordinal": 8,
"type_info": "Text",
"origin": {
"Table": {
"table": "policies",
"name": "dub_blacklist"
}
}
},
{
"name": "hdr_rules!: String",
"ordinal": 9,
"type_info": "Text",
"origin": {
"Table": {
"table": "policies",
"name": "hdr_rules"
}
}
},
{
"name": "size_bands!: String",
"ordinal": 10,
"type_info": "Text",
"origin": {
"Table": {
"table": "policies",
"name": "size_bands"
}
}
},
{
"name": "resolution_pref!: String",
"ordinal": 11,
"type_info": "Text",
"origin": {
"Table": {
"table": "policies",
"name": "resolution_pref"
}
}
},
{
"name": "source_weights!: String",
"ordinal": 12,
"type_info": "Text",
"origin": {
"Table": {
"table": "policies",
"name": "source_weights"
}
}
},
{
"name": "score_weights!: String",
"ordinal": 13,
"type_info": "Text",
"origin": {
"Table": {
"table": "policies",
"name": "score_weights"
}
}
}
],
"parameters": {
"Right": 1
},
"nullable": [
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false
]
},
"hash": "1f67ae5af28115d22ad5e2fde1df8ee0f4aeb615e1d29c9cae03a589c7a6bdd4"
}
@@ -0,0 +1,146 @@
{
"db_name": "SQLite",
"query": "\n SELECT g.id AS \"grab_id!: i64\",\n g.infohash AS \"infohash!: String\",\n se.id AS \"season_id!: i64\",\n se.number AS \"season_number!: i64\",\n s.id AS \"series_id!: i64\",\n s.tmdb_id AS \"series_tmdb_id!: i64\",\n s.title AS \"series_title!: String\",\n s.year AS \"series_year\",\n s.original_language,\n s.runtime_minutes,\n r.name AS \"release_name!: String\"\n FROM grabs g\n JOIN seasons se ON se.id = g.target_id\n JOIN series s ON s.id = se.series_id\n JOIN releases r ON r.id = g.release_id\n WHERE g.state = 'downloaded' AND g.target_kind = 'season'\n ORDER BY g.id\n ",
"describe": {
"columns": [
{
"name": "grab_id!: i64",
"ordinal": 0,
"type_info": "Integer",
"origin": {
"Table": {
"table": "grabs",
"name": "id"
}
}
},
{
"name": "infohash!: String",
"ordinal": 1,
"type_info": "Text",
"origin": {
"Table": {
"table": "grabs",
"name": "infohash"
}
}
},
{
"name": "season_id!: i64",
"ordinal": 2,
"type_info": "Integer",
"origin": {
"Table": {
"table": "seasons",
"name": "id"
}
}
},
{
"name": "season_number!: i64",
"ordinal": 3,
"type_info": "Integer",
"origin": {
"Table": {
"table": "seasons",
"name": "number"
}
}
},
{
"name": "series_id!: i64",
"ordinal": 4,
"type_info": "Integer",
"origin": {
"Table": {
"table": "series",
"name": "id"
}
}
},
{
"name": "series_tmdb_id!: i64",
"ordinal": 5,
"type_info": "Integer",
"origin": {
"Table": {
"table": "series",
"name": "tmdb_id"
}
}
},
{
"name": "series_title!: String",
"ordinal": 6,
"type_info": "Text",
"origin": {
"Table": {
"table": "series",
"name": "title"
}
}
},
{
"name": "series_year",
"ordinal": 7,
"type_info": "Integer",
"origin": {
"Table": {
"table": "series",
"name": "year"
}
}
},
{
"name": "original_language",
"ordinal": 8,
"type_info": "Text",
"origin": {
"Table": {
"table": "series",
"name": "original_language"
}
}
},
{
"name": "runtime_minutes",
"ordinal": 9,
"type_info": "Integer",
"origin": {
"Table": {
"table": "series",
"name": "runtime_minutes"
}
}
},
{
"name": "release_name!: String",
"ordinal": 10,
"type_info": "Text",
"origin": {
"Table": {
"table": "releases",
"name": "name"
}
}
}
],
"parameters": {
"Right": 0
},
"nullable": [
false,
false,
false,
false,
false,
false,
false,
true,
true,
true,
false
]
},
"hash": "1fa97a49b40502e95b7618ac8fed38ad03e58f95ec9636a79ef0c06b24635a67"
}
@@ -0,0 +1,33 @@
{
"db_name": "SQLite",
"query": "SELECT e.air_date,\n EXISTS (\n SELECT 1 FROM media_files f\n WHERE f.owner_kind = 'episode' AND f.owner_id = e.id\n ) AS \"has_file!: bool\"\n FROM episodes e\n WHERE e.season_id = ?",
"describe": {
"columns": [
{
"name": "air_date",
"ordinal": 0,
"type_info": "Text",
"origin": {
"Table": {
"table": "episodes",
"name": "air_date"
}
}
},
{
"name": "has_file!: bool",
"ordinal": 1,
"type_info": "Integer",
"origin": "Expression"
}
],
"parameters": {
"Right": 1
},
"nullable": [
true,
false
]
},
"hash": "20c1cbcd9cc317b148357ff64e967e30032a438c4815816323cf62896398d2a2"
}
@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "UPDATE media_files SET path = ?, updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now') WHERE id = ?",
"describe": {
"columns": [],
"parameters": {
"Right": 2
},
"nullable": []
},
"hash": "20dd51ed5a7e54bb156d9a5eaf83621c69971e6dc0784f3d2c011c8e90a001d6"
}
@@ -1,6 +1,6 @@
{
"db_name": "SQLite",
"query": "UPDATE roots SET kind = ?, audience = ?, path = ?, policy_id = ?,\n updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')\n WHERE id = ?",
"query": "UPDATE roots SET kind = ?, audience = ?, path = ?, policy_id = ?,\n updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')\n WHERE id = ?",
"describe": {
"columns": [],
"parameters": {
@@ -8,5 +8,5 @@
},
"nullable": []
},
"hash": "0a27365a669aa217ae7dae675ebf54c4e4a343c4b56c4a44eb56d1a26c2aea4b"
"hash": "2231fa4a963a5f1a15dcdb835fb7c2d970ad55a8fd62b12aadd68ee6a261ed41"
}
@@ -6,72 +6,156 @@
{
"name": "overrides!: String",
"ordinal": 0,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "series",
"name": "overrides"
}
}
},
{
"name": "root_id!: i64",
"ordinal": 1,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "roots",
"name": "id"
}
}
},
{
"name": "root_kind!: String",
"ordinal": 2,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "roots",
"name": "kind"
}
}
},
{
"name": "root_audience!: String",
"ordinal": 3,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "roots",
"name": "audience"
}
}
},
{
"name": "root_path!: String",
"ordinal": 4,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "roots",
"name": "path"
}
}
},
{
"name": "policy_id!: i64",
"ordinal": 5,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "policies",
"name": "id"
}
}
},
{
"name": "policy_name!: String",
"ordinal": 6,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "policies",
"name": "name"
}
}
},
{
"name": "required_audio!: String",
"ordinal": 7,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "policies",
"name": "required_audio"
}
}
},
{
"name": "dub_blacklist!: String",
"ordinal": 8,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "policies",
"name": "dub_blacklist"
}
}
},
{
"name": "hdr_rules!: String",
"ordinal": 9,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "policies",
"name": "hdr_rules"
}
}
},
{
"name": "size_bands!: String",
"ordinal": 10,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "policies",
"name": "size_bands"
}
}
},
{
"name": "resolution_pref!: String",
"ordinal": 11,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "policies",
"name": "resolution_pref"
}
}
},
{
"name": "source_weights!: String",
"ordinal": 12,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "policies",
"name": "source_weights"
}
}
},
{
"name": "score_weights!: String",
"ordinal": 13,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "policies",
"name": "score_weights"
}
}
}
],
"parameters": {
@@ -6,39 +6,75 @@
{
"name": "id!: i64",
"ordinal": 0,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "roots",
"name": "id"
}
}
},
{
"name": "kind!: String",
"ordinal": 1,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "roots",
"name": "kind"
}
}
},
{
"name": "audience!: String",
"ordinal": 2,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "roots",
"name": "audience"
}
}
},
{
"name": "path!: String",
"ordinal": 3,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "roots",
"name": "path"
}
}
},
{
"name": "policy_id",
"ordinal": 4,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "roots",
"name": "policy_id"
}
}
},
{
"name": "policy_name!: String",
"ordinal": 5,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "policies",
"name": "name"
}
}
}
],
"parameters": {
"Right": 0
},
"nullable": [
true,
false,
false,
false,
false,
@@ -6,47 +6,101 @@
{
"name": "id!: i64",
"ordinal": 0,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "series",
"name": "id"
}
}
},
{
"name": "tmdb_id!: i64",
"ordinal": 1,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "series",
"name": "tmdb_id"
}
}
},
{
"name": "title!: String",
"ordinal": 2,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "series",
"name": "title"
}
}
},
{
"name": "year",
"ordinal": 3,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "series",
"name": "year"
}
}
},
{
"name": "original_language",
"ordinal": 4,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "series",
"name": "original_language"
}
}
},
{
"name": "root_id!: i64",
"ordinal": 5,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "series",
"name": "root_id"
}
}
},
{
"name": "blocked!: bool",
"ordinal": 6,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "series",
"name": "blocked"
}
}
},
{
"name": "poster_path",
"ordinal": 7,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "series",
"name": "poster_path"
}
}
},
{
"name": "vote_average",
"ordinal": 8,
"type_info": "Float"
"type_info": "Float",
"origin": {
"Table": {
"table": "series",
"name": "vote_average"
}
}
}
],
"parameters": {
@@ -0,0 +1,26 @@
{
"db_name": "SQLite",
"query": "SELECT last_pack_search_at FROM seasons WHERE id = ?",
"describe": {
"columns": [
{
"name": "last_pack_search_at",
"ordinal": 0,
"type_info": "Text",
"origin": {
"Table": {
"table": "seasons",
"name": "last_pack_search_at"
}
}
}
],
"parameters": {
"Right": 1
},
"nullable": [
true
]
},
"hash": "2759a3a5fb0ca9d37560b8d89922c7c5618b25843192ffa28b9c13eacd0b9c26"
}
@@ -1,56 +0,0 @@
{
"db_name": "SQLite",
"query": "\n SELECT s.id AS \"series_id!: i64\", s.tmdb_id AS \"tmdb_id!: i64\",\n s.title AS \"title!: String\", s.year,\n e.id AS \"episode_id!: i64\",\n se.number AS \"season_number!: i64\", e.number AS \"episode_number!: i64\"\n FROM episodes e\n JOIN seasons se ON se.id = e.season_id\n JOIN series s ON s.id = se.series_id\n JOIN roots root ON root.id = s.root_id\n WHERE root.audience = 'kids'\n AND s.blocked = 0\n AND e.wanted = 1 AND e.state = 'missing' AND e.search_attempts > 0\n AND NOT EXISTS (\n SELECT 1 FROM episode_releases er\n JOIN releases r ON r.id = er.release_id\n WHERE er.episode_id = e.id AND r.verdict IN ('eligible', 'waived')\n )\n ORDER BY se.number, e.number\n ",
"describe": {
"columns": [
{
"name": "series_id!: i64",
"ordinal": 0,
"type_info": "Integer"
},
{
"name": "tmdb_id!: i64",
"ordinal": 1,
"type_info": "Integer"
},
{
"name": "title!: String",
"ordinal": 2,
"type_info": "Text"
},
{
"name": "year",
"ordinal": 3,
"type_info": "Integer"
},
{
"name": "episode_id!: i64",
"ordinal": 4,
"type_info": "Integer"
},
{
"name": "season_number!: i64",
"ordinal": 5,
"type_info": "Integer"
},
{
"name": "episode_number!: i64",
"ordinal": 6,
"type_info": "Integer"
}
],
"parameters": {
"Right": 0
},
"nullable": [
false,
false,
false,
true,
true,
false,
false
]
},
"hash": "29d6fdda533e0552f5da24a8e5180e5935b3550ee2c024e09aef9b76fd47a708"
}
@@ -0,0 +1,26 @@
{
"db_name": "SQLite",
"query": "SELECT id AS \"id!: i64\" FROM movies WHERE root_id = ?",
"describe": {
"columns": [
{
"name": "id!: i64",
"ordinal": 0,
"type_info": "Integer",
"origin": {
"Table": {
"table": "movies",
"name": "id"
}
}
}
],
"parameters": {
"Right": 1
},
"nullable": [
false
]
},
"hash": "2bb8e5739bd8d16bc7c5a08f5530839c26fbbea836aca6a55a43d75210ae4368"
}
@@ -1,62 +0,0 @@
{
"db_name": "SQLite",
"query": "SELECT e.id AS \"episode_id!: i64\", s.id AS \"series_id!: i64\", s.title AS \"series_title!: String\", printf('S%02dE%02d', se.number, e.number) AS \"tag!: String\", e.title AS \"title!: String\", s.poster_path, s.vote_average, s.tmdb_id AS \"series_tmdb_id!: i64\" FROM episodes e JOIN seasons se ON se.id = e.season_id JOIN series s ON s.id = se.series_id WHERE NOT EXISTS (SELECT 1 FROM json_each(?) token WHERE (s.title || ' ' || e.title) NOT LIKE '%' || token.value || '%' ESCAPE '\\') ORDER BY s.title, se.number, e.number, e.id",
"describe": {
"columns": [
{
"name": "episode_id!: i64",
"ordinal": 0,
"type_info": "Integer"
},
{
"name": "series_id!: i64",
"ordinal": 1,
"type_info": "Integer"
},
{
"name": "series_title!: String",
"ordinal": 2,
"type_info": "Text"
},
{
"name": "tag!: String",
"ordinal": 3,
"type_info": "Null"
},
{
"name": "title!: String",
"ordinal": 4,
"type_info": "Text"
},
{
"name": "poster_path",
"ordinal": 5,
"type_info": "Text"
},
{
"name": "vote_average",
"ordinal": 6,
"type_info": "Float"
},
{
"name": "series_tmdb_id!: i64",
"ordinal": 7,
"type_info": "Integer"
}
],
"parameters": {
"Right": 1
},
"nullable": [
true,
false,
false,
null,
false,
true,
true,
false
]
},
"hash": "2bc048c0fca4822eb38befbe3091f44f961fafb79432d4771aa48e36d32bf2a2"
}
@@ -6,14 +6,20 @@
{
"name": "id!: i64",
"ordinal": 0,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "grabs",
"name": "id"
}
}
}
],
"parameters": {
"Right": 4
},
"nullable": [
true
null
]
},
"hash": "2d0b92ba9aa4bc257286a67f0d37e2182c4478153c9096c1522c5ab23ad472e9"
@@ -6,7 +6,13 @@
{
"name": "title!: String",
"ordinal": 0,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "series",
"name": "title"
}
}
}
],
"parameters": {
@@ -0,0 +1,26 @@
{
"db_name": "SQLite",
"query": "SELECT path AS \"path!: String\"\n FROM media_files WHERE owner_kind = 'episode' AND owner_id = ?",
"describe": {
"columns": [
{
"name": "path!: String",
"ordinal": 0,
"type_info": "Text",
"origin": {
"Table": {
"table": "media_files",
"name": "path"
}
}
}
],
"parameters": {
"Right": 1
},
"nullable": [
false
]
},
"hash": "32c562fdb70cabb3265e351583b6ac1579490b29da670fe9062117451911576a"
}
@@ -6,56 +6,94 @@
{
"name": "id!: i64",
"ordinal": 0,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "episodes",
"name": "id"
}
}
},
{
"name": "number!: i64",
"ordinal": 1,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "episodes",
"name": "number"
}
}
},
{
"name": "air_date",
"ordinal": 2,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "episodes",
"name": "air_date"
}
}
},
{
"name": "wanted!: bool",
"ordinal": 3,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "episodes",
"name": "wanted"
}
}
},
{
"name": "search_attempts!: i64",
"ordinal": 4,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "episodes",
"name": "search_attempts"
}
}
},
{
"name": "last_searched_at",
"ordinal": 5,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "episodes",
"name": "last_searched_at"
}
}
},
{
"name": "has_file!: bool",
"ordinal": 6,
"type_info": "Null"
"type_info": "Integer",
"origin": "Expression"
},
{
"name": "in_flight!: bool",
"ordinal": 7,
"type_info": "Null"
"type_info": "Integer",
"origin": "Expression"
}
],
"parameters": {
"Right": 1
},
"nullable": [
true,
false,
false,
true,
false,
false,
true,
null,
null
false,
false
]
},
"hash": "33de7e17f5defa761de0d6e174dca40e2d883b9990bec497355422f5b5e6dfe5"
@@ -6,47 +6,101 @@
{
"name": "id!: i64",
"ordinal": 0,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "policies",
"name": "id"
}
}
},
{
"name": "name!: String",
"ordinal": 1,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "policies",
"name": "name"
}
}
},
{
"name": "required_audio!: String",
"ordinal": 2,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "policies",
"name": "required_audio"
}
}
},
{
"name": "dub_blacklist!: String",
"ordinal": 3,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "policies",
"name": "dub_blacklist"
}
}
},
{
"name": "hdr_rules!: String",
"ordinal": 4,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "policies",
"name": "hdr_rules"
}
}
},
{
"name": "size_bands!: String",
"ordinal": 5,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "policies",
"name": "size_bands"
}
}
},
{
"name": "resolution_pref!: String",
"ordinal": 6,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "policies",
"name": "resolution_pref"
}
}
},
{
"name": "source_weights!: String",
"ordinal": 7,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "policies",
"name": "source_weights"
}
}
},
{
"name": "score_weights!: String",
"ordinal": 8,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "policies",
"name": "score_weights"
}
}
}
],
"parameters": {
@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "DELETE FROM media_files WHERE owner_kind = 'episode' AND owner_id IN (\n SELECT e.id FROM episodes e JOIN seasons se ON se.id = e.season_id\n WHERE se.series_id = ?)",
"describe": {
"columns": [],
"parameters": {
"Right": 1
},
"nullable": []
},
"hash": "3531a16bd038c149465397a70c24c14ed005683c68f06d322409c5c2b39b84d1"
}
@@ -1,26 +1,44 @@
{
"db_name": "SQLite",
"query": "\n SELECT id AS \"id!: i64\", title AS \"title!: String\", year\n FROM movies\n WHERE movies.wanted = 1 AND movies.state != 'available'\n AND (SELECT count(DISTINCT g.release_id)\n FROM grabs g\n WHERE g.target_kind = 'movie' AND g.target_id = movies.id\n AND g.state = 'failed') >= 2\n ",
"query": "\n SELECT id AS \"id!: i64\", title AS \"title!: String\", year\n FROM movies\n WHERE movies.wanted = 1 AND movies.state != 'available'\n AND (SELECT count(DISTINCT g.release_id)\n FROM grabs g\n WHERE g.target_kind = 'movie' AND g.target_id = movies.id\n AND g.state = 'failed'\n AND g.failed_at >= strftime('%Y-%m-%dT%H:%M:%fZ', 'now', ?)) >= 2\n ",
"describe": {
"columns": [
{
"name": "id!: i64",
"ordinal": 0,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "movies",
"name": "id"
}
}
},
{
"name": "title!: String",
"ordinal": 1,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "movies",
"name": "title"
}
}
},
{
"name": "year",
"ordinal": 2,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "movies",
"name": "year"
}
}
}
],
"parameters": {
"Right": 0
"Right": 1
},
"nullable": [
false,
@@ -28,5 +46,5 @@
true
]
},
"hash": "91d1ee1e8e206569b57d2a699228139d45cb658b94103f677dfa49dcd9f0e07d"
"hash": "35d381e3acde2f3e49f8559da15d03e9414398ce9d0b716e5c8e367003625a46"
}
@@ -0,0 +1,158 @@
{
"db_name": "SQLite",
"query": "SELECT se.series_id AS \"series_id!: i64\", e.id AS \"id!: i64\", e.season_id AS \"season_id!: i64\", se.number AS \"season_number!: i64\", e.number AS \"number!: i64\", e.title AS \"title!: String\", e.air_date, e.wanted AS \"wanted!: bool\", e.state AS \"state!: String\", e.vanished AS \"vanished!: bool\", e.search_attempts AS \"search_attempts!: i64\", e.last_searched_at\n FROM episodes e JOIN seasons se ON se.id = e.season_id WHERE e.season_id = ?",
"describe": {
"columns": [
{
"name": "series_id!: i64",
"ordinal": 0,
"type_info": "Integer",
"origin": {
"Table": {
"table": "seasons",
"name": "series_id"
}
}
},
{
"name": "id!: i64",
"ordinal": 1,
"type_info": "Integer",
"origin": {
"Table": {
"table": "episodes",
"name": "id"
}
}
},
{
"name": "season_id!: i64",
"ordinal": 2,
"type_info": "Integer",
"origin": {
"Table": {
"table": "episodes",
"name": "season_id"
}
}
},
{
"name": "season_number!: i64",
"ordinal": 3,
"type_info": "Integer",
"origin": {
"Table": {
"table": "seasons",
"name": "number"
}
}
},
{
"name": "number!: i64",
"ordinal": 4,
"type_info": "Integer",
"origin": {
"Table": {
"table": "episodes",
"name": "number"
}
}
},
{
"name": "title!: String",
"ordinal": 5,
"type_info": "Text",
"origin": {
"Table": {
"table": "episodes",
"name": "title"
}
}
},
{
"name": "air_date",
"ordinal": 6,
"type_info": "Text",
"origin": {
"Table": {
"table": "episodes",
"name": "air_date"
}
}
},
{
"name": "wanted!: bool",
"ordinal": 7,
"type_info": "Integer",
"origin": {
"Table": {
"table": "episodes",
"name": "wanted"
}
}
},
{
"name": "state!: String",
"ordinal": 8,
"type_info": "Text",
"origin": {
"Table": {
"table": "episodes",
"name": "state"
}
}
},
{
"name": "vanished!: bool",
"ordinal": 9,
"type_info": "Integer",
"origin": {
"Table": {
"table": "episodes",
"name": "vanished"
}
}
},
{
"name": "search_attempts!: i64",
"ordinal": 10,
"type_info": "Integer",
"origin": {
"Table": {
"table": "episodes",
"name": "search_attempts"
}
}
},
{
"name": "last_searched_at",
"ordinal": 11,
"type_info": "Text",
"origin": {
"Table": {
"table": "episodes",
"name": "last_searched_at"
}
}
}
],
"parameters": {
"Right": 1
},
"nullable": [
false,
false,
false,
false,
false,
false,
true,
false,
false,
false,
false,
true
]
},
"hash": "3a02f30ced2765dcdbf7575d680c1b4ab1db895065f65db4a99bb0a4dc6e71ed"
}
@@ -6,52 +6,102 @@
{
"name": "id!: i64",
"ordinal": 0,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "movies",
"name": "id"
}
}
},
{
"name": "tmdb_id!: i64",
"ordinal": 1,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "movies",
"name": "tmdb_id"
}
}
},
{
"name": "title!: String",
"ordinal": 2,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "movies",
"name": "title"
}
}
},
{
"name": "year",
"ordinal": 3,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "movies",
"name": "year"
}
}
},
{
"name": "wanted!: bool",
"ordinal": 4,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "movies",
"name": "wanted"
}
}
},
{
"name": "root_id!: i64",
"ordinal": 5,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "movies",
"name": "root_id"
}
}
},
{
"name": "root_path!: String",
"ordinal": 6,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "roots",
"name": "path"
}
}
},
{
"name": "created_at!: String",
"ordinal": 7,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "movies",
"name": "created_at"
}
}
},
{
"name": "file_count!: i64",
"ordinal": 8,
"type_info": "Integer"
"type_info": "Integer",
"origin": "Expression"
},
{
"name": "size_on_disk!: i64",
"ordinal": 9,
"type_info": "Integer"
"type_info": "Integer",
"origin": "Expression"
}
],
"parameters": {
@@ -0,0 +1,182 @@
{
"db_name": "SQLite",
"query": "SELECT id AS \"id!: i64\", tmdb_id AS \"tmdb_id!: i64\", tvdb_id, title AS \"title!: String\", year, original_language, root_id AS \"root_id!: i64\", auto_track AS \"auto_track!: bool\", overrides AS \"overrides!: serde_json::Value\", upstream_ended AS \"upstream_ended!: bool\", blocked AS \"blocked!: bool\", poster_path, vote_average, metadata_refreshed_at FROM series ORDER BY title, year, id",
"describe": {
"columns": [
{
"name": "id!: i64",
"ordinal": 0,
"type_info": "Integer",
"origin": {
"Table": {
"table": "series",
"name": "id"
}
}
},
{
"name": "tmdb_id!: i64",
"ordinal": 1,
"type_info": "Integer",
"origin": {
"Table": {
"table": "series",
"name": "tmdb_id"
}
}
},
{
"name": "tvdb_id",
"ordinal": 2,
"type_info": "Integer",
"origin": {
"Table": {
"table": "series",
"name": "tvdb_id"
}
}
},
{
"name": "title!: String",
"ordinal": 3,
"type_info": "Text",
"origin": {
"Table": {
"table": "series",
"name": "title"
}
}
},
{
"name": "year",
"ordinal": 4,
"type_info": "Integer",
"origin": {
"Table": {
"table": "series",
"name": "year"
}
}
},
{
"name": "original_language",
"ordinal": 5,
"type_info": "Text",
"origin": {
"Table": {
"table": "series",
"name": "original_language"
}
}
},
{
"name": "root_id!: i64",
"ordinal": 6,
"type_info": "Integer",
"origin": {
"Table": {
"table": "series",
"name": "root_id"
}
}
},
{
"name": "auto_track!: bool",
"ordinal": 7,
"type_info": "Integer",
"origin": {
"Table": {
"table": "series",
"name": "auto_track"
}
}
},
{
"name": "overrides!: serde_json::Value",
"ordinal": 8,
"type_info": "Text",
"origin": {
"Table": {
"table": "series",
"name": "overrides"
}
}
},
{
"name": "upstream_ended!: bool",
"ordinal": 9,
"type_info": "Integer",
"origin": {
"Table": {
"table": "series",
"name": "upstream_ended"
}
}
},
{
"name": "blocked!: bool",
"ordinal": 10,
"type_info": "Integer",
"origin": {
"Table": {
"table": "series",
"name": "blocked"
}
}
},
{
"name": "poster_path",
"ordinal": 11,
"type_info": "Text",
"origin": {
"Table": {
"table": "series",
"name": "poster_path"
}
}
},
{
"name": "vote_average",
"ordinal": 12,
"type_info": "Float",
"origin": {
"Table": {
"table": "series",
"name": "vote_average"
}
}
},
{
"name": "metadata_refreshed_at",
"ordinal": 13,
"type_info": "Text",
"origin": {
"Table": {
"table": "series",
"name": "metadata_refreshed_at"
}
}
}
],
"parameters": {
"Right": 0
},
"nullable": [
false,
false,
true,
false,
true,
true,
false,
false,
false,
false,
false,
true,
true,
true
]
},
"hash": "3b518839d694b872f83005ca059d45d048a8c44efd78b75e08e74299fed072ea"
}
@@ -6,19 +6,31 @@
{
"name": "id!: i64",
"ordinal": 0,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "seasons",
"name": "id"
}
}
},
{
"name": "tracked!: bool",
"ordinal": 1,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "seasons",
"name": "tracked"
}
}
}
],
"parameters": {
"Right": 2
},
"nullable": [
true,
false,
false
]
},
@@ -0,0 +1,38 @@
{
"db_name": "SQLite",
"query": "SELECT id AS \"id!: i64\", path AS \"path!: String\"\n FROM media_files WHERE owner_kind = 'movie' AND owner_id = ?",
"describe": {
"columns": [
{
"name": "id!: i64",
"ordinal": 0,
"type_info": "Integer",
"origin": {
"Table": {
"table": "media_files",
"name": "id"
}
}
},
{
"name": "path!: String",
"ordinal": 1,
"type_info": "Text",
"origin": {
"Table": {
"table": "media_files",
"name": "path"
}
}
}
],
"parameters": {
"Right": 1
},
"nullable": [
false,
false
]
},
"hash": "3be9c350ef24a69490540379225d95d2949fe2a0f1f59d55d6272cca1bd431aa"
}
@@ -6,62 +6,134 @@
{
"name": "series_id!: i64",
"ordinal": 0,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "seasons",
"name": "series_id"
}
}
},
{
"name": "id!: i64",
"ordinal": 1,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "episodes",
"name": "id"
}
}
},
{
"name": "season_id!: i64",
"ordinal": 2,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "episodes",
"name": "season_id"
}
}
},
{
"name": "season_number!: i64",
"ordinal": 3,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "seasons",
"name": "number"
}
}
},
{
"name": "number!: i64",
"ordinal": 4,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "episodes",
"name": "number"
}
}
},
{
"name": "title!: String",
"ordinal": 5,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "episodes",
"name": "title"
}
}
},
{
"name": "air_date",
"ordinal": 6,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "episodes",
"name": "air_date"
}
}
},
{
"name": "wanted!: bool",
"ordinal": 7,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "episodes",
"name": "wanted"
}
}
},
{
"name": "state!: String",
"ordinal": 8,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "episodes",
"name": "state"
}
}
},
{
"name": "vanished!: bool",
"ordinal": 9,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "episodes",
"name": "vanished"
}
}
},
{
"name": "search_attempts!: i64",
"ordinal": 10,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "episodes",
"name": "search_attempts"
}
}
},
{
"name": "last_searched_at",
"ordinal": 11,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "episodes",
"name": "last_searched_at"
}
}
}
],
"parameters": {
@@ -1,12 +1,12 @@
{
"db_name": "SQLite",
"query": "INSERT INTO series (tmdb_id, tvdb_id, title, year, original_language, root_id, auto_track, upstream_ended, blocked, overrides, poster_path, backdrop_path, vote_average) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
"query": "INSERT INTO series (tmdb_id, tvdb_id, title, year, original_language, root_id, auto_track, upstream_ended, blocked, overrides, poster_path, backdrop_path, vote_average, runtime_minutes) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
"describe": {
"columns": [],
"parameters": {
"Right": 13
"Right": 14
},
"nullable": []
},
"hash": "9ce66f0bdb64b26ffad51d5e908f58ffe11054b836ca53ee31b185b522749331"
"hash": "3cc5524f3ac253e86de8618970a5413135a87ccbd8ec3f73e21ef9c73e8ba80c"
}
@@ -0,0 +1,28 @@
{
"db_name": "SQLite",
"query": "SELECT count(*) AS \"failures!: i64\",\n max(coalesce(failed_at, grabbed_at)) AS \"last_failed_at?: String\"\n FROM grabs\n WHERE target_kind = 'season' AND target_id = ? AND state = 'failed'",
"describe": {
"columns": [
{
"name": "failures!: i64",
"ordinal": 0,
"type_info": "Integer",
"origin": "Expression"
},
{
"name": "last_failed_at?: String",
"ordinal": 1,
"type_info": "Text",
"origin": "Expression"
}
],
"parameters": {
"Right": 1
},
"nullable": [
false,
true
]
},
"hash": "3e8fdbb8d28441b429d2ef011f206c4fad280f56905b6a85035a142dd592dbec"
}
@@ -0,0 +1,194 @@
{
"db_name": "SQLite",
"query": "SELECT id AS \"id!: i64\", tmdb_id AS \"tmdb_id!: i64\", title AS \"title!: String\", year, original_language, root_id AS \"root_id!: i64\", wanted AS \"wanted!: bool\", overrides AS \"overrides!: serde_json::Value\", state AS \"state!: String\", blocked AS \"blocked!: bool\", search_attempts AS \"search_attempts!: i64\", last_searched_at, poster_path, vote_average, (SELECT f.waiver FROM media_files f WHERE f.owner_kind = 'movie' AND f.owner_id = movies.id AND f.waiver IS NOT NULL ORDER BY f.id LIMIT 1) AS \"waiver?: serde_json::Value\" FROM movies WHERE movies.wanted = 1 AND movies.state != 'available' AND (SELECT count(DISTINCT g.release_id) FROM grabs g WHERE g.target_kind = 'movie' AND g.target_id = movies.id AND g.state = 'failed' AND g.failed_at >= strftime('%Y-%m-%dT%H:%M:%fZ', 'now', ?)) >= 2 ORDER BY title",
"describe": {
"columns": [
{
"name": "id!: i64",
"ordinal": 0,
"type_info": "Integer",
"origin": {
"Table": {
"table": "movies",
"name": "id"
}
}
},
{
"name": "tmdb_id!: i64",
"ordinal": 1,
"type_info": "Integer",
"origin": {
"Table": {
"table": "movies",
"name": "tmdb_id"
}
}
},
{
"name": "title!: String",
"ordinal": 2,
"type_info": "Text",
"origin": {
"Table": {
"table": "movies",
"name": "title"
}
}
},
{
"name": "year",
"ordinal": 3,
"type_info": "Integer",
"origin": {
"Table": {
"table": "movies",
"name": "year"
}
}
},
{
"name": "original_language",
"ordinal": 4,
"type_info": "Text",
"origin": {
"Table": {
"table": "movies",
"name": "original_language"
}
}
},
{
"name": "root_id!: i64",
"ordinal": 5,
"type_info": "Integer",
"origin": {
"Table": {
"table": "movies",
"name": "root_id"
}
}
},
{
"name": "wanted!: bool",
"ordinal": 6,
"type_info": "Integer",
"origin": {
"Table": {
"table": "movies",
"name": "wanted"
}
}
},
{
"name": "overrides!: serde_json::Value",
"ordinal": 7,
"type_info": "Text",
"origin": {
"Table": {
"table": "movies",
"name": "overrides"
}
}
},
{
"name": "state!: String",
"ordinal": 8,
"type_info": "Text",
"origin": {
"Table": {
"table": "movies",
"name": "state"
}
}
},
{
"name": "blocked!: bool",
"ordinal": 9,
"type_info": "Integer",
"origin": {
"Table": {
"table": "movies",
"name": "blocked"
}
}
},
{
"name": "search_attempts!: i64",
"ordinal": 10,
"type_info": "Integer",
"origin": {
"Table": {
"table": "movies",
"name": "search_attempts"
}
}
},
{
"name": "last_searched_at",
"ordinal": 11,
"type_info": "Text",
"origin": {
"Table": {
"table": "movies",
"name": "last_searched_at"
}
}
},
{
"name": "poster_path",
"ordinal": 12,
"type_info": "Text",
"origin": {
"Table": {
"table": "movies",
"name": "poster_path"
}
}
},
{
"name": "vote_average",
"ordinal": 13,
"type_info": "Float",
"origin": {
"Table": {
"table": "movies",
"name": "vote_average"
}
}
},
{
"name": "waiver?: serde_json::Value",
"ordinal": 14,
"type_info": "Text",
"origin": {
"Table": {
"table": "media_files",
"name": "waiver"
}
}
}
],
"parameters": {
"Right": 1
},
"nullable": [
false,
false,
false,
true,
true,
false,
false,
false,
false,
false,
false,
true,
true,
true,
true
]
},
"hash": "3ebb77103e3b3ec01b3de47c35ef613790d8c88ed4cabd46b33f67eddc47f9c0"
}
@@ -6,62 +6,134 @@
{
"name": "series_id!: i64",
"ordinal": 0,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "seasons",
"name": "series_id"
}
}
},
{
"name": "id!: i64",
"ordinal": 1,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "episodes",
"name": "id"
}
}
},
{
"name": "season_id!: i64",
"ordinal": 2,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "episodes",
"name": "season_id"
}
}
},
{
"name": "season_number!: i64",
"ordinal": 3,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "seasons",
"name": "number"
}
}
},
{
"name": "number!: i64",
"ordinal": 4,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "episodes",
"name": "number"
}
}
},
{
"name": "title!: String",
"ordinal": 5,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "episodes",
"name": "title"
}
}
},
{
"name": "air_date",
"ordinal": 6,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "episodes",
"name": "air_date"
}
}
},
{
"name": "wanted!: bool",
"ordinal": 7,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "episodes",
"name": "wanted"
}
}
},
{
"name": "state!: String",
"ordinal": 8,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "episodes",
"name": "state"
}
}
},
{
"name": "vanished!: bool",
"ordinal": 9,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "episodes",
"name": "vanished"
}
}
},
{
"name": "search_attempts!: i64",
"ordinal": 10,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "episodes",
"name": "search_attempts"
}
}
},
{
"name": "last_searched_at",
"ordinal": 11,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "episodes",
"name": "last_searched_at"
}
}
}
],
"parameters": {
@@ -69,7 +141,7 @@
},
"nullable": [
false,
true,
false,
false,
false,
false,
@@ -0,0 +1,74 @@
{
"db_name": "SQLite",
"query": "SELECT g.target_id AS \"season_id!: i64\",\n r.name AS \"name!: String\",\n g.infohash AS \"infohash!: String\",\n g.failed_at,\n g.grabbed_at AS \"grabbed_at!: String\"\n FROM grabs g\n JOIN releases r ON r.id = g.release_id\n JOIN seasons s ON s.id = g.target_id\n WHERE g.target_kind = 'season'\n AND g.state = 'failed'\n AND s.series_id = ?\n AND EXISTS (\n SELECT 1 FROM episodes e\n WHERE e.season_id = s.id AND e.wanted\n AND NOT EXISTS (\n SELECT 1 FROM media_files f\n WHERE f.owner_kind = 'episode' AND f.owner_id = e.id\n )\n )\n ORDER BY coalesce(g.failed_at, g.grabbed_at), g.id",
"describe": {
"columns": [
{
"name": "season_id!: i64",
"ordinal": 0,
"type_info": "Integer",
"origin": {
"Table": {
"table": "grabs",
"name": "target_id"
}
}
},
{
"name": "name!: String",
"ordinal": 1,
"type_info": "Text",
"origin": {
"Table": {
"table": "releases",
"name": "name"
}
}
},
{
"name": "infohash!: String",
"ordinal": 2,
"type_info": "Text",
"origin": {
"Table": {
"table": "grabs",
"name": "infohash"
}
}
},
{
"name": "failed_at",
"ordinal": 3,
"type_info": "Text",
"origin": {
"Table": {
"table": "grabs",
"name": "failed_at"
}
}
},
{
"name": "grabbed_at!: String",
"ordinal": 4,
"type_info": "Text",
"origin": {
"Table": {
"table": "grabs",
"name": "grabbed_at"
}
}
}
],
"parameters": {
"Right": 1
},
"nullable": [
false,
false,
false,
true,
false
]
},
"hash": "3f252a992c1bc5b18bb4467d1c4fd4137966a469b134fbeb51fb91f67951cbbe"
}
@@ -6,12 +6,19 @@
{
"name": "air_date",
"ordinal": 0,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "episodes",
"name": "air_date"
}
}
},
{
"name": "on_disk!: bool",
"ordinal": 1,
"type_info": "Null"
"type_info": "Integer",
"origin": "Expression"
}
],
"parameters": {
@@ -19,7 +26,7 @@
},
"nullable": [
true,
null
false
]
},
"hash": "3fc7d7e39520dc60fa81da21a4cae0c4a718e95f9718629f0aba9f3d00d179e1"
@@ -6,7 +6,8 @@
{
"name": "exists!: bool",
"ordinal": 0,
"type_info": "Integer"
"type_info": "Integer",
"origin": "Expression"
}
],
"parameters": {
@@ -6,7 +6,13 @@
{
"name": "title!: String",
"ordinal": 0,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "series",
"name": "title"
}
}
}
],
"parameters": {
@@ -6,7 +6,8 @@
{
"name": "count!: i64",
"ordinal": 0,
"type_info": "Integer"
"type_info": "Integer",
"origin": "Expression"
}
],
"parameters": {
@@ -1,12 +0,0 @@
{
"db_name": "SQLite",
"query": "UPDATE movies\n SET title = ?, year = ?, original_language = ?, digital_release = ?,\n imdb_id = ?, poster_path = ?, backdrop_path = ?, vote_average = ?,\n metadata_refreshed_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now'),\n search_attempts = 0, last_searched_at = NULL,\n updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')\n WHERE id = ? AND (\n title IS NOT ? OR year IS NOT ? OR original_language IS NOT ?\n OR digital_release IS NOT ? OR imdb_id IS NOT ?\n OR poster_path IS NOT ? OR backdrop_path IS NOT ?\n OR vote_average IS NOT ?\n )",
"describe": {
"columns": [],
"parameters": {
"Right": 17
},
"nullable": []
},
"hash": "462039959030ee881bcce2daf9d7c74f232ec6543787f64efeb53bffa8170113"
}
@@ -0,0 +1,182 @@
{
"db_name": "SQLite",
"query": "SELECT id AS \"id!: i64\", tmdb_id AS \"tmdb_id!: i64\", tvdb_id,\n title AS \"title!: String\", year, original_language,\n root_id AS \"root_id!: i64\", auto_track AS \"auto_track!: bool\",\n upstream_ended AS \"upstream_ended!: bool\", metadata_refreshed_at,\n poster_path, backdrop_path, vote_average, runtime_minutes\n FROM series WHERE id = ?",
"describe": {
"columns": [
{
"name": "id!: i64",
"ordinal": 0,
"type_info": "Integer",
"origin": {
"Table": {
"table": "series",
"name": "id"
}
}
},
{
"name": "tmdb_id!: i64",
"ordinal": 1,
"type_info": "Integer",
"origin": {
"Table": {
"table": "series",
"name": "tmdb_id"
}
}
},
{
"name": "tvdb_id",
"ordinal": 2,
"type_info": "Integer",
"origin": {
"Table": {
"table": "series",
"name": "tvdb_id"
}
}
},
{
"name": "title!: String",
"ordinal": 3,
"type_info": "Text",
"origin": {
"Table": {
"table": "series",
"name": "title"
}
}
},
{
"name": "year",
"ordinal": 4,
"type_info": "Integer",
"origin": {
"Table": {
"table": "series",
"name": "year"
}
}
},
{
"name": "original_language",
"ordinal": 5,
"type_info": "Text",
"origin": {
"Table": {
"table": "series",
"name": "original_language"
}
}
},
{
"name": "root_id!: i64",
"ordinal": 6,
"type_info": "Integer",
"origin": {
"Table": {
"table": "series",
"name": "root_id"
}
}
},
{
"name": "auto_track!: bool",
"ordinal": 7,
"type_info": "Integer",
"origin": {
"Table": {
"table": "series",
"name": "auto_track"
}
}
},
{
"name": "upstream_ended!: bool",
"ordinal": 8,
"type_info": "Integer",
"origin": {
"Table": {
"table": "series",
"name": "upstream_ended"
}
}
},
{
"name": "metadata_refreshed_at",
"ordinal": 9,
"type_info": "Text",
"origin": {
"Table": {
"table": "series",
"name": "metadata_refreshed_at"
}
}
},
{
"name": "poster_path",
"ordinal": 10,
"type_info": "Text",
"origin": {
"Table": {
"table": "series",
"name": "poster_path"
}
}
},
{
"name": "backdrop_path",
"ordinal": 11,
"type_info": "Text",
"origin": {
"Table": {
"table": "series",
"name": "backdrop_path"
}
}
},
{
"name": "vote_average",
"ordinal": 12,
"type_info": "Float",
"origin": {
"Table": {
"table": "series",
"name": "vote_average"
}
}
},
{
"name": "runtime_minutes",
"ordinal": 13,
"type_info": "Integer",
"origin": {
"Table": {
"table": "series",
"name": "runtime_minutes"
}
}
}
],
"parameters": {
"Right": 1
},
"nullable": [
false,
false,
true,
false,
true,
true,
false,
false,
false,
true,
true,
true,
true,
true
]
},
"hash": "4af1ce70996a3dc496b0ff507b8c70a43cde5f8b79ea02483047626fc11bc377"
}
@@ -1,38 +0,0 @@
{
"db_name": "SQLite",
"query": "\n SELECT s.id AS \"series_id!: i64\", s.title AS \"title!: String\", s.year,\n g.target_id AS \"season_id!: i64\"\n FROM grabs g\n JOIN seasons se ON g.target_kind = 'season' AND se.id = g.target_id\n JOIN series s ON s.id = se.series_id\n WHERE g.state = 'failed'\n GROUP BY s.id, s.title, s.year, se.id\n ",
"describe": {
"columns": [
{
"name": "series_id!: i64",
"ordinal": 0,
"type_info": "Integer"
},
{
"name": "title!: String",
"ordinal": 1,
"type_info": "Text"
},
{
"name": "year",
"ordinal": 2,
"type_info": "Integer"
},
{
"name": "season_id!: i64",
"ordinal": 3,
"type_info": "Integer"
}
],
"parameters": {
"Right": 0
},
"nullable": [
false,
false,
true,
false
]
},
"hash": "4ddb143ab51ca61ac782f22cff84f01f7d58d8a424310ae0743fb0c91577665e"
}
@@ -0,0 +1,98 @@
{
"db_name": "SQLite",
"query": "\n SELECT s.id AS \"series_id!: i64\", s.tmdb_id AS \"tmdb_id!: i64\",\n s.title AS \"title!: String\", s.year,\n g.target_id AS \"episode_id!: i64\",\n se.number AS \"season_number!: i64\", e.number AS \"episode_number!: i64\"\n FROM grabs g\n JOIN episodes e ON g.target_kind = 'episode' AND e.id = g.target_id\n JOIN seasons se ON se.id = e.season_id\n JOIN series s ON s.id = se.series_id\n WHERE g.state = 'failed'\n AND g.failed_at >= strftime('%Y-%m-%dT%H:%M:%fZ', 'now', ?)\n AND e.wanted = 1 AND e.state != 'available'\n GROUP BY s.id, s.tmdb_id, s.title, s.year, e.id, se.number, e.number\n HAVING count(DISTINCT g.release_id) >= 2\n ",
"describe": {
"columns": [
{
"name": "series_id!: i64",
"ordinal": 0,
"type_info": "Integer",
"origin": {
"Table": {
"table": "series",
"name": "id"
}
}
},
{
"name": "tmdb_id!: i64",
"ordinal": 1,
"type_info": "Integer",
"origin": {
"Table": {
"table": "series",
"name": "tmdb_id"
}
}
},
{
"name": "title!: String",
"ordinal": 2,
"type_info": "Text",
"origin": {
"Table": {
"table": "series",
"name": "title"
}
}
},
{
"name": "year",
"ordinal": 3,
"type_info": "Integer",
"origin": {
"Table": {
"table": "series",
"name": "year"
}
}
},
{
"name": "episode_id!: i64",
"ordinal": 4,
"type_info": "Integer",
"origin": {
"Table": {
"table": "grabs",
"name": "target_id"
}
}
},
{
"name": "season_number!: i64",
"ordinal": 5,
"type_info": "Integer",
"origin": {
"Table": {
"table": "seasons",
"name": "number"
}
}
},
{
"name": "episode_number!: i64",
"ordinal": 6,
"type_info": "Integer",
"origin": {
"Table": {
"table": "episodes",
"name": "number"
}
}
}
],
"parameters": {
"Right": 1
},
"nullable": [
false,
false,
false,
true,
false,
false,
false
]
},
"hash": "4f9e8728ca5f0bd70c40155b05a857365f0ced5fbf77c49f25049492658998db"
}
@@ -6,12 +6,24 @@
{
"name": "title_id!: i64",
"ordinal": 0,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "title_owners",
"name": "title_id"
}
}
},
{
"name": "owner_id!: i64",
"ordinal": 1,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "title_owners",
"name": "owner_id"
}
}
}
],
"parameters": {
@@ -6,32 +6,63 @@
{
"name": "season_id!: i64",
"ordinal": 0,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "seasons",
"name": "id"
}
}
},
{
"name": "season_number!: i64",
"ordinal": 1,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "seasons",
"name": "number"
}
}
},
{
"name": "series_title!: String",
"ordinal": 2,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "series",
"name": "title"
}
}
},
{
"name": "series_tvdb_id",
"ordinal": 3,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "series",
"name": "tvdb_id"
}
}
},
{
"name": "original_language",
"ordinal": 4,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "series",
"name": "original_language"
}
}
},
{
"name": "is_gap!: bool",
"ordinal": 5,
"type_info": "Integer"
"type_info": "Integer",
"origin": "Expression"
}
],
"parameters": {
@@ -6,77 +6,167 @@
{
"name": "id!: i64",
"ordinal": 0,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "movies",
"name": "id"
}
}
},
{
"name": "tmdb_id!: i64",
"ordinal": 1,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "movies",
"name": "tmdb_id"
}
}
},
{
"name": "title!: String",
"ordinal": 2,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "movies",
"name": "title"
}
}
},
{
"name": "year",
"ordinal": 3,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "movies",
"name": "year"
}
}
},
{
"name": "original_language",
"ordinal": 4,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "movies",
"name": "original_language"
}
}
},
{
"name": "root_id!: i64",
"ordinal": 5,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "movies",
"name": "root_id"
}
}
},
{
"name": "wanted!: bool",
"ordinal": 6,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "movies",
"name": "wanted"
}
}
},
{
"name": "overrides!: serde_json::Value",
"ordinal": 7,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "movies",
"name": "overrides"
}
}
},
{
"name": "state!: String",
"ordinal": 8,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "movies",
"name": "state"
}
}
},
{
"name": "blocked!: bool",
"ordinal": 9,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "movies",
"name": "blocked"
}
}
},
{
"name": "search_attempts!: i64",
"ordinal": 10,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "movies",
"name": "search_attempts"
}
}
},
{
"name": "last_searched_at",
"ordinal": 11,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "movies",
"name": "last_searched_at"
}
}
},
{
"name": "poster_path",
"ordinal": 12,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "movies",
"name": "poster_path"
}
}
},
{
"name": "vote_average",
"ordinal": 13,
"type_info": "Float"
"type_info": "Float",
"origin": {
"Table": {
"table": "movies",
"name": "vote_average"
}
}
},
{
"name": "waiver?: serde_json::Value",
"ordinal": 14,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "media_files",
"name": "waiver"
}
}
}
],
"parameters": {
@@ -6,7 +6,13 @@
{
"name": "path!: String",
"ordinal": 0,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "media_files",
"name": "path"
}
}
}
],
"parameters": {
@@ -0,0 +1,158 @@
{
"db_name": "SQLite",
"query": "SELECT se.series_id AS \"series_id!: i64\", e.id AS \"id!: i64\", e.season_id AS \"season_id!: i64\", se.number AS \"season_number!: i64\", e.number AS \"number!: i64\", e.title AS \"title!: String\", e.air_date, e.wanted AS \"wanted!: bool\", e.state AS \"state!: String\", e.vanished AS \"vanished!: bool\", e.search_attempts AS \"search_attempts!: i64\", e.last_searched_at\n FROM episodes e JOIN seasons se ON se.id = e.season_id WHERE se.series_id = ? ORDER BY se.number DESC, e.number DESC",
"describe": {
"columns": [
{
"name": "series_id!: i64",
"ordinal": 0,
"type_info": "Integer",
"origin": {
"Table": {
"table": "seasons",
"name": "series_id"
}
}
},
{
"name": "id!: i64",
"ordinal": 1,
"type_info": "Integer",
"origin": {
"Table": {
"table": "episodes",
"name": "id"
}
}
},
{
"name": "season_id!: i64",
"ordinal": 2,
"type_info": "Integer",
"origin": {
"Table": {
"table": "episodes",
"name": "season_id"
}
}
},
{
"name": "season_number!: i64",
"ordinal": 3,
"type_info": "Integer",
"origin": {
"Table": {
"table": "seasons",
"name": "number"
}
}
},
{
"name": "number!: i64",
"ordinal": 4,
"type_info": "Integer",
"origin": {
"Table": {
"table": "episodes",
"name": "number"
}
}
},
{
"name": "title!: String",
"ordinal": 5,
"type_info": "Text",
"origin": {
"Table": {
"table": "episodes",
"name": "title"
}
}
},
{
"name": "air_date",
"ordinal": 6,
"type_info": "Text",
"origin": {
"Table": {
"table": "episodes",
"name": "air_date"
}
}
},
{
"name": "wanted!: bool",
"ordinal": 7,
"type_info": "Integer",
"origin": {
"Table": {
"table": "episodes",
"name": "wanted"
}
}
},
{
"name": "state!: String",
"ordinal": 8,
"type_info": "Text",
"origin": {
"Table": {
"table": "episodes",
"name": "state"
}
}
},
{
"name": "vanished!: bool",
"ordinal": 9,
"type_info": "Integer",
"origin": {
"Table": {
"table": "episodes",
"name": "vanished"
}
}
},
{
"name": "search_attempts!: i64",
"ordinal": 10,
"type_info": "Integer",
"origin": {
"Table": {
"table": "episodes",
"name": "search_attempts"
}
}
},
{
"name": "last_searched_at",
"ordinal": 11,
"type_info": "Text",
"origin": {
"Table": {
"table": "episodes",
"name": "last_searched_at"
}
}
}
],
"parameters": {
"Right": 1
},
"nullable": [
false,
false,
false,
false,
false,
false,
true,
false,
false,
false,
false,
true
]
},
"hash": "580e3bf81062d253fd08c9a4e1a879a0e57d6357ab9b278503cdf8fb5252886a"
}
@@ -1,44 +0,0 @@
{
"db_name": "SQLite",
"query": "SELECT id AS \"id!: i64\", series_id AS \"series_id!: i64\", number AS \"number!: i64\", tracked AS \"tracked!: bool\", vanished AS \"vanished!: bool\" FROM seasons WHERE series_id = ? ORDER BY number",
"describe": {
"columns": [
{
"name": "id!: i64",
"ordinal": 0,
"type_info": "Integer"
},
{
"name": "series_id!: i64",
"ordinal": 1,
"type_info": "Integer"
},
{
"name": "number!: i64",
"ordinal": 2,
"type_info": "Integer"
},
{
"name": "tracked!: bool",
"ordinal": 3,
"type_info": "Integer"
},
{
"name": "vanished!: bool",
"ordinal": 4,
"type_info": "Integer"
}
],
"parameters": {
"Right": 1
},
"nullable": [
true,
false,
false,
false,
false
]
},
"hash": "583b48cc8a0c618294361f606a9c8a4b3a42000c96343ff9a5490bb518700f81"
}
@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "UPDATE episodes\n SET wanted = ?,\n state = CASE WHEN state = 'available' THEN 'missing' ELSE state END,\n updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')\n WHERE id = ?",
"describe": {
"columns": [],
"parameters": {
"Right": 2
},
"nullable": []
},
"hash": "58fe68be59f015fd8214306483c0d45fcc4b2ac595d3b04efb224b3e426cc1cf"
}
@@ -0,0 +1,26 @@
{
"db_name": "SQLite",
"query": "SELECT r.path AS \"path!: String\"\n FROM roots r\n JOIN series s ON s.root_id = r.id\n JOIN seasons se ON se.series_id = s.id\n JOIN episodes e ON e.season_id = se.id\n WHERE e.id = ?",
"describe": {
"columns": [
{
"name": "path!: String",
"ordinal": 0,
"type_info": "Text",
"origin": {
"Table": {
"table": "roots",
"name": "path"
}
}
}
],
"parameters": {
"Right": 1
},
"nullable": [
false
]
},
"hash": "5b059a2f93a3fb46b6c55e0b665aa55931045af290894e396b3b410123bfd539"
}
@@ -6,77 +6,167 @@
{
"name": "id!: i64",
"ordinal": 0,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "movies",
"name": "id"
}
}
},
{
"name": "tmdb_id!: i64",
"ordinal": 1,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "movies",
"name": "tmdb_id"
}
}
},
{
"name": "title!: String",
"ordinal": 2,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "movies",
"name": "title"
}
}
},
{
"name": "year",
"ordinal": 3,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "movies",
"name": "year"
}
}
},
{
"name": "original_language",
"ordinal": 4,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "movies",
"name": "original_language"
}
}
},
{
"name": "root_id!: i64",
"ordinal": 5,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "movies",
"name": "root_id"
}
}
},
{
"name": "wanted!: bool",
"ordinal": 6,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "movies",
"name": "wanted"
}
}
},
{
"name": "overrides!: serde_json::Value",
"ordinal": 7,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "movies",
"name": "overrides"
}
}
},
{
"name": "state!: String",
"ordinal": 8,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "movies",
"name": "state"
}
}
},
{
"name": "blocked!: bool",
"ordinal": 9,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "movies",
"name": "blocked"
}
}
},
{
"name": "search_attempts!: i64",
"ordinal": 10,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "movies",
"name": "search_attempts"
}
}
},
{
"name": "last_searched_at",
"ordinal": 11,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "movies",
"name": "last_searched_at"
}
}
},
{
"name": "poster_path",
"ordinal": 12,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "movies",
"name": "poster_path"
}
}
},
{
"name": "vote_average",
"ordinal": 13,
"type_info": "Float"
"type_info": "Float",
"origin": {
"Table": {
"table": "movies",
"name": "vote_average"
}
}
},
{
"name": "waiver?: serde_json::Value",
"ordinal": 14,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "media_files",
"name": "waiver"
}
}
}
],
"parameters": {
@@ -6,24 +6,42 @@
{
"name": "id!: i64",
"ordinal": 0,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "owners",
"name": "id"
}
}
},
{
"name": "name!: String",
"ordinal": 1,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "owners",
"name": "name"
}
}
},
{
"name": "ntfy_topic!: String",
"ordinal": 2,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "owners",
"name": "ntfy_topic"
}
}
}
],
"parameters": {
"Right": 1
},
"nullable": [
true,
false,
false,
false
]
@@ -6,42 +6,90 @@
{
"name": "name!: String",
"ordinal": 0,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "policies",
"name": "name"
}
}
},
{
"name": "required_audio!: String",
"ordinal": 1,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "policies",
"name": "required_audio"
}
}
},
{
"name": "dub_blacklist!: String",
"ordinal": 2,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "policies",
"name": "dub_blacklist"
}
}
},
{
"name": "hdr_rules!: String",
"ordinal": 3,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "policies",
"name": "hdr_rules"
}
}
},
{
"name": "size_bands!: String",
"ordinal": 4,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "policies",
"name": "size_bands"
}
}
},
{
"name": "resolution_pref!: String",
"ordinal": 5,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "policies",
"name": "resolution_pref"
}
}
},
{
"name": "source_weights!: String",
"ordinal": 6,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "policies",
"name": "source_weights"
}
}
},
{
"name": "score_weights!: String",
"ordinal": 7,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "policies",
"name": "score_weights"
}
}
}
],
"parameters": {
@@ -6,62 +6,134 @@
{
"name": "series_id!: i64",
"ordinal": 0,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "seasons",
"name": "series_id"
}
}
},
{
"name": "id!: i64",
"ordinal": 1,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "episodes",
"name": "id"
}
}
},
{
"name": "season_id!: i64",
"ordinal": 2,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "episodes",
"name": "season_id"
}
}
},
{
"name": "season_number!: i64",
"ordinal": 3,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "seasons",
"name": "number"
}
}
},
{
"name": "number!: i64",
"ordinal": 4,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "episodes",
"name": "number"
}
}
},
{
"name": "title!: String",
"ordinal": 5,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "episodes",
"name": "title"
}
}
},
{
"name": "air_date",
"ordinal": 6,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "episodes",
"name": "air_date"
}
}
},
{
"name": "wanted!: bool",
"ordinal": 7,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "episodes",
"name": "wanted"
}
}
},
{
"name": "state!: String",
"ordinal": 8,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "episodes",
"name": "state"
}
}
},
{
"name": "vanished!: bool",
"ordinal": 9,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "episodes",
"name": "vanished"
}
}
},
{
"name": "search_attempts!: i64",
"ordinal": 10,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "episodes",
"name": "search_attempts"
}
}
},
{
"name": "last_searched_at",
"ordinal": 11,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "episodes",
"name": "last_searched_at"
}
}
}
],
"parameters": {
@@ -0,0 +1,26 @@
{
"db_name": "SQLite",
"query": "SELECT path AS \"path!: String\" FROM roots WHERE id = ?",
"describe": {
"columns": [
{
"name": "path!: String",
"ordinal": 0,
"type_info": "Text",
"origin": {
"Table": {
"table": "roots",
"name": "path"
}
}
}
],
"parameters": {
"Right": 1
},
"nullable": [
false
]
},
"hash": "6ef45d7e6fc113b578fab91148f9ab5455e7a2d06fc86291e2c6c9696fd08953"
}
@@ -6,47 +6,101 @@
{
"name": "id!: i64",
"ordinal": 0,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "series",
"name": "id"
}
}
},
{
"name": "tmdb_id!: i64",
"ordinal": 1,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "series",
"name": "tmdb_id"
}
}
},
{
"name": "title!: String",
"ordinal": 2,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "series",
"name": "title"
}
}
},
{
"name": "year",
"ordinal": 3,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "series",
"name": "year"
}
}
},
{
"name": "original_language",
"ordinal": 4,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "series",
"name": "original_language"
}
}
},
{
"name": "root_id!: i64",
"ordinal": 5,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "series",
"name": "root_id"
}
}
},
{
"name": "blocked!: bool",
"ordinal": 6,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "series",
"name": "blocked"
}
}
},
{
"name": "poster_path",
"ordinal": 7,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "series",
"name": "poster_path"
}
}
},
{
"name": "vote_average",
"ordinal": 8,
"type_info": "Float"
"type_info": "Float",
"origin": {
"Table": {
"table": "series",
"name": "vote_average"
}
}
}
],
"parameters": {
@@ -0,0 +1,62 @@
{
"db_name": "SQLite",
"query": "\n SELECT s.id AS \"series_id!: i64\", s.title AS \"title!: String\", s.year,\n g.target_id AS \"episode_id!: i64\"\n FROM grabs g\n JOIN episodes e ON g.target_kind = 'episode' AND e.id = g.target_id\n JOIN seasons se ON se.id = e.season_id\n JOIN series s ON s.id = se.series_id\n WHERE g.state = 'failed'\n AND g.failed_at >= strftime('%Y-%m-%dT%H:%M:%fZ', 'now', ?)\n AND e.wanted = 1 AND e.state != 'available'\n GROUP BY s.id, s.title, s.year, e.id\n HAVING count(DISTINCT g.release_id) >= 2\n ",
"describe": {
"columns": [
{
"name": "series_id!: i64",
"ordinal": 0,
"type_info": "Integer",
"origin": {
"Table": {
"table": "series",
"name": "id"
}
}
},
{
"name": "title!: String",
"ordinal": 1,
"type_info": "Text",
"origin": {
"Table": {
"table": "series",
"name": "title"
}
}
},
{
"name": "year",
"ordinal": 2,
"type_info": "Integer",
"origin": {
"Table": {
"table": "series",
"name": "year"
}
}
},
{
"name": "episode_id!: i64",
"ordinal": 3,
"type_info": "Integer",
"origin": {
"Table": {
"table": "grabs",
"name": "target_id"
}
}
}
],
"parameters": {
"Right": 1
},
"nullable": [
false,
false,
true,
false
]
},
"hash": "6faa1fc2c2015becebb83843b031b071e9720132a42e837a0e9bb8b2d955a9e8"
}
@@ -1,92 +0,0 @@
{
"db_name": "SQLite",
"query": "SELECT id AS \"id!: i64\", tmdb_id AS \"tmdb_id!: i64\", tvdb_id,\n title AS \"title!: String\", year, original_language,\n root_id AS \"root_id!: i64\", auto_track AS \"auto_track!: bool\",\n upstream_ended AS \"upstream_ended!: bool\", metadata_refreshed_at,\n poster_path, backdrop_path, vote_average\n FROM series\n ORDER BY metadata_refreshed_at IS NOT NULL, metadata_refreshed_at, id",
"describe": {
"columns": [
{
"name": "id!: i64",
"ordinal": 0,
"type_info": "Integer"
},
{
"name": "tmdb_id!: i64",
"ordinal": 1,
"type_info": "Integer"
},
{
"name": "tvdb_id",
"ordinal": 2,
"type_info": "Integer"
},
{
"name": "title!: String",
"ordinal": 3,
"type_info": "Text"
},
{
"name": "year",
"ordinal": 4,
"type_info": "Integer"
},
{
"name": "original_language",
"ordinal": 5,
"type_info": "Text"
},
{
"name": "root_id!: i64",
"ordinal": 6,
"type_info": "Integer"
},
{
"name": "auto_track!: bool",
"ordinal": 7,
"type_info": "Integer"
},
{
"name": "upstream_ended!: bool",
"ordinal": 8,
"type_info": "Integer"
},
{
"name": "metadata_refreshed_at",
"ordinal": 9,
"type_info": "Text"
},
{
"name": "poster_path",
"ordinal": 10,
"type_info": "Text"
},
{
"name": "backdrop_path",
"ordinal": 11,
"type_info": "Text"
},
{
"name": "vote_average",
"ordinal": 12,
"type_info": "Float"
}
],
"parameters": {
"Right": 0
},
"nullable": [
false,
false,
true,
false,
true,
true,
false,
false,
false,
true,
true,
true,
true
]
},
"hash": "6fb25e97d46957c92475679190d71f115f9bf97edbc5ff6621bcbe90d60f5644"
}
@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "DELETE FROM media_files WHERE owner_kind = 'episode' AND owner_id IN (\n SELECT e.id FROM episodes e WHERE e.season_id = ?)",
"describe": {
"columns": [],
"parameters": {
"Right": 1
},
"nullable": []
},
"hash": "6feaa1abb79ce2a0298bef052d3b731e8ccec88228f7a81b2e8ff5cee76f3fa4"
}
@@ -6,72 +6,156 @@
{
"name": "overrides!: String",
"ordinal": 0,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "series",
"name": "overrides"
}
}
},
{
"name": "root_id!: i64",
"ordinal": 1,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "roots",
"name": "id"
}
}
},
{
"name": "root_kind!: String",
"ordinal": 2,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "roots",
"name": "kind"
}
}
},
{
"name": "root_audience!: String",
"ordinal": 3,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "roots",
"name": "audience"
}
}
},
{
"name": "root_path!: String",
"ordinal": 4,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "roots",
"name": "path"
}
}
},
{
"name": "policy_id!: i64",
"ordinal": 5,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "policies",
"name": "id"
}
}
},
{
"name": "policy_name!: String",
"ordinal": 6,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "policies",
"name": "name"
}
}
},
{
"name": "required_audio!: String",
"ordinal": 7,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "policies",
"name": "required_audio"
}
}
},
{
"name": "dub_blacklist!: String",
"ordinal": 8,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "policies",
"name": "dub_blacklist"
}
}
},
{
"name": "hdr_rules!: String",
"ordinal": 9,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "policies",
"name": "hdr_rules"
}
}
},
{
"name": "size_bands!: String",
"ordinal": 10,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "policies",
"name": "size_bands"
}
}
},
{
"name": "resolution_pref!: String",
"ordinal": 11,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "policies",
"name": "resolution_pref"
}
}
},
{
"name": "source_weights!: String",
"ordinal": 12,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "policies",
"name": "source_weights"
}
}
},
{
"name": "score_weights!: String",
"ordinal": 13,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "policies",
"name": "score_weights"
}
}
}
],
"parameters": {
@@ -0,0 +1,38 @@
{
"db_name": "SQLite",
"query": "SELECT mf.id AS \"id!: i64\", mf.path AS \"path!: String\"\n FROM media_files mf\n JOIN episodes e ON mf.owner_kind = 'episode' AND e.id = mf.owner_id\n JOIN seasons se ON se.id = e.season_id\n JOIN series s ON s.id = se.series_id\n WHERE s.root_id = ?\n ORDER BY mf.id",
"describe": {
"columns": [
{
"name": "id!: i64",
"ordinal": 0,
"type_info": "Integer",
"origin": {
"Table": {
"table": "media_files",
"name": "id"
}
}
},
{
"name": "path!: String",
"ordinal": 1,
"type_info": "Text",
"origin": {
"Table": {
"table": "media_files",
"name": "path"
}
}
}
],
"parameters": {
"Right": 1
},
"nullable": [
false,
false
]
},
"hash": "73f978ab92e16183828ed15674f69f08914d6faf42d21c1689d7e2872c7fdbaa"
}
@@ -6,7 +6,8 @@
{
"name": "count!: i64",
"ordinal": 0,
"type_info": "Integer"
"type_info": "Integer",
"origin": "Expression"
}
],
"parameters": {
@@ -1,50 +0,0 @@
{
"db_name": "SQLite",
"query": "\n SELECT s.id AS \"series_id!: i64\", s.tmdb_id AS \"tmdb_id!: i64\",\n s.title AS \"title!: String\", s.year,\n g.target_id AS \"season_id!: i64\", se.number AS \"season_number!: i64\"\n FROM grabs g\n JOIN seasons se ON g.target_kind = 'season' AND se.id = g.target_id\n JOIN series s ON s.id = se.series_id\n WHERE g.state = 'failed'\n GROUP BY s.id, s.tmdb_id, s.title, s.year, se.id, se.number\n ",
"describe": {
"columns": [
{
"name": "series_id!: i64",
"ordinal": 0,
"type_info": "Integer"
},
{
"name": "tmdb_id!: i64",
"ordinal": 1,
"type_info": "Integer"
},
{
"name": "title!: String",
"ordinal": 2,
"type_info": "Text"
},
{
"name": "year",
"ordinal": 3,
"type_info": "Integer"
},
{
"name": "season_id!: i64",
"ordinal": 4,
"type_info": "Integer"
},
{
"name": "season_number!: i64",
"ordinal": 5,
"type_info": "Integer"
}
],
"parameters": {
"Right": 0
},
"nullable": [
false,
false,
false,
true,
false,
false
]
},
"hash": "7930e2d10b25627dcbf81f60a5ac077c27b647a6f0b411e13105398a2963cd51"
}
@@ -6,17 +6,35 @@
{
"name": "series_id!: i64",
"ordinal": 0,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "seasons",
"name": "series_id"
}
}
},
{
"name": "number!: i64",
"ordinal": 1,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "seasons",
"name": "number"
}
}
},
{
"name": "tracked!: bool",
"ordinal": 2,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "seasons",
"name": "tracked"
}
}
}
],
"parameters": {
@@ -1,74 +0,0 @@
{
"db_name": "SQLite",
"query": "\n SELECT g.id AS \"grab_id!: i64\",\n g.infohash AS \"infohash!: String\",\n se.id AS \"season_id!: i64\",\n se.number AS \"season_number!: i64\",\n s.id AS \"series_id!: i64\",\n s.tmdb_id AS \"series_tmdb_id!: i64\",\n s.title AS \"series_title!: String\",\n s.year AS \"series_year\",\n s.original_language,\n r.name AS \"release_name!: String\"\n FROM grabs g\n JOIN seasons se ON se.id = g.target_id\n JOIN series s ON s.id = se.series_id\n JOIN releases r ON r.id = g.release_id\n WHERE g.state = 'downloaded' AND g.target_kind = 'season'\n ORDER BY g.id\n ",
"describe": {
"columns": [
{
"name": "grab_id!: i64",
"ordinal": 0,
"type_info": "Integer"
},
{
"name": "infohash!: String",
"ordinal": 1,
"type_info": "Text"
},
{
"name": "season_id!: i64",
"ordinal": 2,
"type_info": "Integer"
},
{
"name": "season_number!: i64",
"ordinal": 3,
"type_info": "Integer"
},
{
"name": "series_id!: i64",
"ordinal": 4,
"type_info": "Integer"
},
{
"name": "series_tmdb_id!: i64",
"ordinal": 5,
"type_info": "Integer"
},
{
"name": "series_title!: String",
"ordinal": 6,
"type_info": "Text"
},
{
"name": "series_year",
"ordinal": 7,
"type_info": "Integer"
},
{
"name": "original_language",
"ordinal": 8,
"type_info": "Text"
},
{
"name": "release_name!: String",
"ordinal": 9,
"type_info": "Text"
}
],
"parameters": {
"Right": 0
},
"nullable": [
true,
false,
false,
false,
false,
false,
false,
true,
true,
false
]
},
"hash": "7963cce11a588f8b8697e596da24802e4a5556c388b84fe7a6102444a887c2cc"
}
@@ -0,0 +1,165 @@
{
"db_name": "SQLite",
"query": "SELECT r.id AS \"id!: i64\", r.indexer_id AS \"indexer_id!: i64\", r.guid AS \"guid!: String\", r.name AS \"name!: String\", r.size AS \"size!: i64\", r.seeders, r.publish_date, r.download_url AS \"download_url!: String\", r.parsed AS \"parsed!: serde_json::Value\", r.score, r.verdict, r.rejected_rule, NULL AS \"blacklist_reason?: String\" FROM releases r JOIN movie_releases mr ON mr.release_id = r.id WHERE mr.movie_id = ? ORDER BY CASE r.verdict WHEN 'eligible' THEN 0 WHEN 'waived' THEN 1 ELSE 2 END, r.score DESC, r.id",
"describe": {
"columns": [
{
"name": "id!: i64",
"ordinal": 0,
"type_info": "Integer",
"origin": {
"Table": {
"table": "releases",
"name": "id"
}
}
},
{
"name": "indexer_id!: i64",
"ordinal": 1,
"type_info": "Integer",
"origin": {
"Table": {
"table": "releases",
"name": "indexer_id"
}
}
},
{
"name": "guid!: String",
"ordinal": 2,
"type_info": "Text",
"origin": {
"Table": {
"table": "releases",
"name": "guid"
}
}
},
{
"name": "name!: String",
"ordinal": 3,
"type_info": "Text",
"origin": {
"Table": {
"table": "releases",
"name": "name"
}
}
},
{
"name": "size!: i64",
"ordinal": 4,
"type_info": "Integer",
"origin": {
"Table": {
"table": "releases",
"name": "size"
}
}
},
{
"name": "seeders",
"ordinal": 5,
"type_info": "Integer",
"origin": {
"Table": {
"table": "releases",
"name": "seeders"
}
}
},
{
"name": "publish_date",
"ordinal": 6,
"type_info": "Text",
"origin": {
"Table": {
"table": "releases",
"name": "publish_date"
}
}
},
{
"name": "download_url!: String",
"ordinal": 7,
"type_info": "Text",
"origin": {
"Table": {
"table": "releases",
"name": "download_url"
}
}
},
{
"name": "parsed!: serde_json::Value",
"ordinal": 8,
"type_info": "Text",
"origin": {
"Table": {
"table": "releases",
"name": "parsed"
}
}
},
{
"name": "score",
"ordinal": 9,
"type_info": "Float",
"origin": {
"Table": {
"table": "releases",
"name": "score"
}
}
},
{
"name": "verdict",
"ordinal": 10,
"type_info": "Text",
"origin": {
"Table": {
"table": "releases",
"name": "verdict"
}
}
},
{
"name": "rejected_rule",
"ordinal": 11,
"type_info": "Text",
"origin": {
"Table": {
"table": "releases",
"name": "rejected_rule"
}
}
},
{
"name": "blacklist_reason?: String",
"ordinal": 12,
"type_info": "Null",
"origin": "Expression"
}
],
"parameters": {
"Right": 1
},
"nullable": [
false,
false,
false,
false,
false,
true,
true,
false,
false,
true,
true,
true,
true
]
},
"hash": "7aa154a1bd54ec84412a1be6d7db51bb65ce9702e3bb1d26b51b93c5fdefa79f"
}
@@ -0,0 +1,158 @@
{
"db_name": "SQLite",
"query": "SELECT se.series_id AS \"series_id!: i64\", e.id AS \"id!: i64\", e.season_id AS \"season_id!: i64\", se.number AS \"season_number!: i64\", e.number AS \"number!: i64\", e.title AS \"title!: String\", e.air_date, e.wanted AS \"wanted!: bool\", e.state AS \"state!: String\", e.vanished AS \"vanished!: bool\", e.search_attempts AS \"search_attempts!: i64\", e.last_searched_at\n FROM episodes e JOIN seasons se ON se.id = e.season_id WHERE e.season_id = ?",
"describe": {
"columns": [
{
"name": "series_id!: i64",
"ordinal": 0,
"type_info": "Integer",
"origin": {
"Table": {
"table": "seasons",
"name": "series_id"
}
}
},
{
"name": "id!: i64",
"ordinal": 1,
"type_info": "Integer",
"origin": {
"Table": {
"table": "episodes",
"name": "id"
}
}
},
{
"name": "season_id!: i64",
"ordinal": 2,
"type_info": "Integer",
"origin": {
"Table": {
"table": "episodes",
"name": "season_id"
}
}
},
{
"name": "season_number!: i64",
"ordinal": 3,
"type_info": "Integer",
"origin": {
"Table": {
"table": "seasons",
"name": "number"
}
}
},
{
"name": "number!: i64",
"ordinal": 4,
"type_info": "Integer",
"origin": {
"Table": {
"table": "episodes",
"name": "number"
}
}
},
{
"name": "title!: String",
"ordinal": 5,
"type_info": "Text",
"origin": {
"Table": {
"table": "episodes",
"name": "title"
}
}
},
{
"name": "air_date",
"ordinal": 6,
"type_info": "Text",
"origin": {
"Table": {
"table": "episodes",
"name": "air_date"
}
}
},
{
"name": "wanted!: bool",
"ordinal": 7,
"type_info": "Integer",
"origin": {
"Table": {
"table": "episodes",
"name": "wanted"
}
}
},
{
"name": "state!: String",
"ordinal": 8,
"type_info": "Text",
"origin": {
"Table": {
"table": "episodes",
"name": "state"
}
}
},
{
"name": "vanished!: bool",
"ordinal": 9,
"type_info": "Integer",
"origin": {
"Table": {
"table": "episodes",
"name": "vanished"
}
}
},
{
"name": "search_attempts!: i64",
"ordinal": 10,
"type_info": "Integer",
"origin": {
"Table": {
"table": "episodes",
"name": "search_attempts"
}
}
},
{
"name": "last_searched_at",
"ordinal": 11,
"type_info": "Text",
"origin": {
"Table": {
"table": "episodes",
"name": "last_searched_at"
}
}
}
],
"parameters": {
"Right": 1
},
"nullable": [
false,
false,
false,
false,
false,
false,
true,
false,
false,
false,
false,
true
]
},
"hash": "7c638b148b697645d250fe43cf7fcc049e3754e5b1837ff54289e8908523d40c"
}
@@ -6,52 +6,107 @@
{
"name": "id!: i64",
"ordinal": 0,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "movies",
"name": "id"
}
}
},
{
"name": "tmdb_id!: i64",
"ordinal": 1,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "movies",
"name": "tmdb_id"
}
}
},
{
"name": "title!: String",
"ordinal": 2,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "movies",
"name": "title"
}
}
},
{
"name": "year",
"ordinal": 3,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "movies",
"name": "year"
}
}
},
{
"name": "original_language",
"ordinal": 4,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "movies",
"name": "original_language"
}
}
},
{
"name": "search_attempts!: i64",
"ordinal": 5,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "movies",
"name": "search_attempts"
}
}
},
{
"name": "last_searched_at",
"ordinal": 6,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "movies",
"name": "last_searched_at"
}
}
},
{
"name": "digital_release",
"ordinal": 7,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "movies",
"name": "digital_release"
}
}
},
{
"name": "metadata_refreshed_at",
"ordinal": 8,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "movies",
"name": "metadata_refreshed_at"
}
}
},
{
"name": "is_gap!: i64",
"ordinal": 9,
"type_info": "Integer"
"type_info": "Integer",
"origin": "Expression"
}
],
"parameters": {
@@ -1,80 +0,0 @@
{
"db_name": "SQLite",
"query": "\n SELECT g.id AS \"grab_id!: i64\",\n g.infohash AS \"infohash!: String\",\n e.id AS \"episode_id!: i64\",\n se.id AS \"season_id!: i64\",\n se.number AS \"season_number!: i64\",\n s.id AS \"series_id!: i64\",\n s.tmdb_id AS \"series_tmdb_id!: i64\",\n s.title AS \"series_title!: String\",\n s.year AS \"series_year\",\n s.original_language,\n r.name AS \"release_name!: String\"\n FROM grabs g\n JOIN episodes e ON e.id = g.target_id\n JOIN seasons se ON se.id = e.season_id\n JOIN series s ON s.id = se.series_id\n JOIN releases r ON r.id = g.release_id\n WHERE g.state = 'downloaded' AND g.target_kind = 'episode'\n ORDER BY g.id\n ",
"describe": {
"columns": [
{
"name": "grab_id!: i64",
"ordinal": 0,
"type_info": "Integer"
},
{
"name": "infohash!: String",
"ordinal": 1,
"type_info": "Text"
},
{
"name": "episode_id!: i64",
"ordinal": 2,
"type_info": "Integer"
},
{
"name": "season_id!: i64",
"ordinal": 3,
"type_info": "Integer"
},
{
"name": "season_number!: i64",
"ordinal": 4,
"type_info": "Integer"
},
{
"name": "series_id!: i64",
"ordinal": 5,
"type_info": "Integer"
},
{
"name": "series_tmdb_id!: i64",
"ordinal": 6,
"type_info": "Integer"
},
{
"name": "series_title!: String",
"ordinal": 7,
"type_info": "Text"
},
{
"name": "series_year",
"ordinal": 8,
"type_info": "Integer"
},
{
"name": "original_language",
"ordinal": 9,
"type_info": "Text"
},
{
"name": "release_name!: String",
"ordinal": 10,
"type_info": "Text"
}
],
"parameters": {
"Right": 0
},
"nullable": [
true,
false,
false,
false,
false,
false,
false,
false,
true,
true,
false
]
},
"hash": "7f94d0bad8dd346c5e930c2606c8643e732850b0a1ec331e216c3aedf6987ad4"
}
@@ -6,7 +6,13 @@
{
"name": "original_language",
"ordinal": 0,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "movies",
"name": "original_language"
}
}
}
],
"parameters": {
@@ -0,0 +1,74 @@
{
"db_name": "SQLite",
"query": "\n SELECT r.id AS \"id!: i64\",\n r.size AS \"size!: i64\",\n r.parsed AS \"parsed!: serde_json::Value\",\n r.verdict,\n r.rejected_rule\n FROM releases r\n WHERE r.id IN (\n SELECT er.release_id\n FROM episode_releases er\n JOIN episodes e ON e.id = er.episode_id\n JOIN seasons se ON se.id = e.season_id\n WHERE se.series_id = ?\n UNION\n SELECT sr.release_id\n FROM season_releases sr\n JOIN seasons se ON se.id = sr.season_id\n WHERE se.series_id = ?\n )\n ",
"describe": {
"columns": [
{
"name": "id!: i64",
"ordinal": 0,
"type_info": "Integer",
"origin": {
"Table": {
"table": "releases",
"name": "id"
}
}
},
{
"name": "size!: i64",
"ordinal": 1,
"type_info": "Integer",
"origin": {
"Table": {
"table": "releases",
"name": "size"
}
}
},
{
"name": "parsed!: serde_json::Value",
"ordinal": 2,
"type_info": "Text",
"origin": {
"Table": {
"table": "releases",
"name": "parsed"
}
}
},
{
"name": "verdict",
"ordinal": 3,
"type_info": "Text",
"origin": {
"Table": {
"table": "releases",
"name": "verdict"
}
}
},
{
"name": "rejected_rule",
"ordinal": 4,
"type_info": "Text",
"origin": {
"Table": {
"table": "releases",
"name": "rejected_rule"
}
}
}
],
"parameters": {
"Right": 2
},
"nullable": [
false,
false,
false,
true,
true
]
},
"hash": "7fe60916664f3552cd111e45586c58997079821e2521a196174f4182e41531ad"
}
@@ -6,57 +6,113 @@
{
"name": "id!: i64",
"ordinal": 0,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "episodes",
"name": "id"
}
}
},
{
"name": "number!: i64",
"ordinal": 1,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "episodes",
"name": "number"
}
}
},
{
"name": "air_date",
"ordinal": 2,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "episodes",
"name": "air_date"
}
}
},
{
"name": "wanted!: bool",
"ordinal": 3,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "episodes",
"name": "wanted"
}
}
},
{
"name": "has_file!: bool",
"ordinal": 4,
"type_info": "Integer"
"type_info": "Integer",
"origin": "Expression"
},
{
"name": "in_flight!: bool",
"ordinal": 5,
"type_info": "Integer"
"type_info": "Integer",
"origin": "Expression"
},
{
"name": "season_id!: i64",
"ordinal": 6,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "seasons",
"name": "id"
}
}
},
{
"name": "season_number!: i64",
"ordinal": 7,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "seasons",
"name": "number"
}
}
},
{
"name": "series_title!: String",
"ordinal": 8,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "series",
"name": "title"
}
}
},
{
"name": "series_tvdb_id",
"ordinal": 9,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "series",
"name": "tvdb_id"
}
}
},
{
"name": "original_language",
"ordinal": 10,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "series",
"name": "original_language"
}
}
}
],
"parameters": {
@@ -0,0 +1,158 @@
{
"db_name": "SQLite",
"query": "\n SELECT g.id AS \"grab_id!: i64\",\n g.infohash AS \"infohash!: String\",\n e.id AS \"episode_id!: i64\",\n se.id AS \"season_id!: i64\",\n se.number AS \"season_number!: i64\",\n s.id AS \"series_id!: i64\",\n s.tmdb_id AS \"series_tmdb_id!: i64\",\n s.title AS \"series_title!: String\",\n s.year AS \"series_year\",\n s.original_language,\n s.runtime_minutes,\n r.name AS \"release_name!: String\"\n FROM grabs g\n JOIN episodes e ON e.id = g.target_id\n JOIN seasons se ON se.id = e.season_id\n JOIN series s ON s.id = se.series_id\n JOIN releases r ON r.id = g.release_id\n WHERE g.state = 'downloaded' AND g.target_kind = 'episode'\n ORDER BY g.id\n ",
"describe": {
"columns": [
{
"name": "grab_id!: i64",
"ordinal": 0,
"type_info": "Integer",
"origin": {
"Table": {
"table": "grabs",
"name": "id"
}
}
},
{
"name": "infohash!: String",
"ordinal": 1,
"type_info": "Text",
"origin": {
"Table": {
"table": "grabs",
"name": "infohash"
}
}
},
{
"name": "episode_id!: i64",
"ordinal": 2,
"type_info": "Integer",
"origin": {
"Table": {
"table": "episodes",
"name": "id"
}
}
},
{
"name": "season_id!: i64",
"ordinal": 3,
"type_info": "Integer",
"origin": {
"Table": {
"table": "seasons",
"name": "id"
}
}
},
{
"name": "season_number!: i64",
"ordinal": 4,
"type_info": "Integer",
"origin": {
"Table": {
"table": "seasons",
"name": "number"
}
}
},
{
"name": "series_id!: i64",
"ordinal": 5,
"type_info": "Integer",
"origin": {
"Table": {
"table": "series",
"name": "id"
}
}
},
{
"name": "series_tmdb_id!: i64",
"ordinal": 6,
"type_info": "Integer",
"origin": {
"Table": {
"table": "series",
"name": "tmdb_id"
}
}
},
{
"name": "series_title!: String",
"ordinal": 7,
"type_info": "Text",
"origin": {
"Table": {
"table": "series",
"name": "title"
}
}
},
{
"name": "series_year",
"ordinal": 8,
"type_info": "Integer",
"origin": {
"Table": {
"table": "series",
"name": "year"
}
}
},
{
"name": "original_language",
"ordinal": 9,
"type_info": "Text",
"origin": {
"Table": {
"table": "series",
"name": "original_language"
}
}
},
{
"name": "runtime_minutes",
"ordinal": 10,
"type_info": "Integer",
"origin": {
"Table": {
"table": "series",
"name": "runtime_minutes"
}
}
},
{
"name": "release_name!: String",
"ordinal": 11,
"type_info": "Text",
"origin": {
"Table": {
"table": "releases",
"name": "name"
}
}
}
],
"parameters": {
"Right": 0
},
"nullable": [
false,
false,
false,
false,
false,
false,
false,
false,
true,
true,
true,
false
]
},
"hash": "81361374c5c84d4c12ac33fc103ab96ce79164dc551f388180c554034dec007b"
}
@@ -0,0 +1,26 @@
{
"db_name": "SQLite",
"query": "SELECT original_language FROM series WHERE id = ?",
"describe": {
"columns": [
{
"name": "original_language",
"ordinal": 0,
"type_info": "Text",
"origin": {
"Table": {
"table": "series",
"name": "original_language"
}
}
}
],
"parameters": {
"Right": 1
},
"nullable": [
true
]
},
"hash": "869e17a01bcf8710bb533dbe428af062e73659ee89ab4387e0a28b7d546f5be4"
}
@@ -1,20 +0,0 @@
{
"db_name": "SQLite",
"query": "SELECT mf.path AS \"path!: String\"\n FROM media_files mf\n JOIN episodes e ON mf.owner_kind = 'episode' AND e.id = mf.owner_id\n JOIN seasons se ON se.id = e.season_id\n WHERE se.series_id = ?",
"describe": {
"columns": [
{
"name": "path!: String",
"ordinal": 0,
"type_info": "Text"
}
],
"parameters": {
"Right": 1
},
"nullable": [
false
]
},
"hash": "87b2e62f51149472db07f32fbc2a548afa40d46b938df101bf3d3b42b01791b4"
}
@@ -0,0 +1,38 @@
{
"db_name": "SQLite",
"query": "SELECT mf.id AS \"id!: i64\", mf.path AS \"path!: String\"\n FROM media_files mf\n JOIN episodes e ON mf.owner_kind = 'episode' AND e.id = mf.owner_id\n JOIN seasons se ON se.id = e.season_id\n WHERE se.series_id = ?",
"describe": {
"columns": [
{
"name": "id!: i64",
"ordinal": 0,
"type_info": "Integer",
"origin": {
"Table": {
"table": "media_files",
"name": "id"
}
}
},
{
"name": "path!: String",
"ordinal": 1,
"type_info": "Text",
"origin": {
"Table": {
"table": "media_files",
"name": "path"
}
}
}
],
"parameters": {
"Right": 1
},
"nullable": [
false,
false
]
},
"hash": "89fb1aa140a11bcd9bb082b2dcba50df414a77bc191755d515e5d5905a651adc"
}
@@ -0,0 +1,165 @@
{
"db_name": "SQLite",
"query": "SELECT r.id AS \"id!: i64\", r.indexer_id AS \"indexer_id!: i64\", r.guid AS \"guid!: String\", r.name AS \"name!: String\", r.size AS \"size!: i64\", r.seeders, r.publish_date, r.download_url AS \"download_url!: String\", r.parsed AS \"parsed!: serde_json::Value\", r.score, r.verdict, r.rejected_rule, NULL AS \"blacklist_reason?: String\" FROM releases r JOIN episode_releases er ON er.release_id = r.id WHERE er.episode_id = ? ORDER BY CASE r.verdict WHEN 'eligible' THEN 0 WHEN 'waived' THEN 1 ELSE 2 END, r.score DESC, r.id",
"describe": {
"columns": [
{
"name": "id!: i64",
"ordinal": 0,
"type_info": "Integer",
"origin": {
"Table": {
"table": "releases",
"name": "id"
}
}
},
{
"name": "indexer_id!: i64",
"ordinal": 1,
"type_info": "Integer",
"origin": {
"Table": {
"table": "releases",
"name": "indexer_id"
}
}
},
{
"name": "guid!: String",
"ordinal": 2,
"type_info": "Text",
"origin": {
"Table": {
"table": "releases",
"name": "guid"
}
}
},
{
"name": "name!: String",
"ordinal": 3,
"type_info": "Text",
"origin": {
"Table": {
"table": "releases",
"name": "name"
}
}
},
{
"name": "size!: i64",
"ordinal": 4,
"type_info": "Integer",
"origin": {
"Table": {
"table": "releases",
"name": "size"
}
}
},
{
"name": "seeders",
"ordinal": 5,
"type_info": "Integer",
"origin": {
"Table": {
"table": "releases",
"name": "seeders"
}
}
},
{
"name": "publish_date",
"ordinal": 6,
"type_info": "Text",
"origin": {
"Table": {
"table": "releases",
"name": "publish_date"
}
}
},
{
"name": "download_url!: String",
"ordinal": 7,
"type_info": "Text",
"origin": {
"Table": {
"table": "releases",
"name": "download_url"
}
}
},
{
"name": "parsed!: serde_json::Value",
"ordinal": 8,
"type_info": "Text",
"origin": {
"Table": {
"table": "releases",
"name": "parsed"
}
}
},
{
"name": "score",
"ordinal": 9,
"type_info": "Float",
"origin": {
"Table": {
"table": "releases",
"name": "score"
}
}
},
{
"name": "verdict",
"ordinal": 10,
"type_info": "Text",
"origin": {
"Table": {
"table": "releases",
"name": "verdict"
}
}
},
{
"name": "rejected_rule",
"ordinal": 11,
"type_info": "Text",
"origin": {
"Table": {
"table": "releases",
"name": "rejected_rule"
}
}
},
{
"name": "blacklist_reason?: String",
"ordinal": 12,
"type_info": "Null",
"origin": "Expression"
}
],
"parameters": {
"Right": 1
},
"nullable": [
false,
false,
false,
false,
false,
true,
true,
false,
false,
true,
true,
true,
true
]
},
"hash": "8b2aa810e679ddde423a5dc5a1a89e0967206e8186b2157bb4b4d11e9a136759"
}
@@ -6,77 +6,167 @@
{
"name": "id!: i64",
"ordinal": 0,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "movies",
"name": "id"
}
}
},
{
"name": "tmdb_id!: i64",
"ordinal": 1,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "movies",
"name": "tmdb_id"
}
}
},
{
"name": "title!: String",
"ordinal": 2,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "movies",
"name": "title"
}
}
},
{
"name": "year",
"ordinal": 3,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "movies",
"name": "year"
}
}
},
{
"name": "original_language",
"ordinal": 4,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "movies",
"name": "original_language"
}
}
},
{
"name": "root_id!: i64",
"ordinal": 5,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "movies",
"name": "root_id"
}
}
},
{
"name": "wanted!: bool",
"ordinal": 6,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "movies",
"name": "wanted"
}
}
},
{
"name": "overrides!: serde_json::Value",
"ordinal": 7,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "movies",
"name": "overrides"
}
}
},
{
"name": "state!: String",
"ordinal": 8,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "movies",
"name": "state"
}
}
},
{
"name": "blocked!: bool",
"ordinal": 9,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "movies",
"name": "blocked"
}
}
},
{
"name": "search_attempts!: i64",
"ordinal": 10,
"type_info": "Integer"
"type_info": "Integer",
"origin": {
"Table": {
"table": "movies",
"name": "search_attempts"
}
}
},
{
"name": "last_searched_at",
"ordinal": 11,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "movies",
"name": "last_searched_at"
}
}
},
{
"name": "poster_path",
"ordinal": 12,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "movies",
"name": "poster_path"
}
}
},
{
"name": "vote_average",
"ordinal": 13,
"type_info": "Float"
"type_info": "Float",
"origin": {
"Table": {
"table": "movies",
"name": "vote_average"
}
}
},
{
"name": "waiver?: serde_json::Value",
"ordinal": 14,
"type_info": "Text"
"type_info": "Text",
"origin": {
"Table": {
"table": "media_files",
"name": "waiver"
}
}
}
],
"parameters": {
@@ -1,20 +0,0 @@
{
"db_name": "SQLite",
"query": "SELECT EXISTS (\n SELECT 1 FROM grabs\n WHERE target_kind = 'season' AND target_id = ? AND state = 'failed'\n ) AS \"failed!: bool\"",
"describe": {
"columns": [
{
"name": "failed!: bool",
"ordinal": 0,
"type_info": "Integer"
}
],
"parameters": {
"Right": 1
},
"nullable": [
false
]
},
"hash": "8cdf7339d395e21af86bd71e011e5681e0c00b1f9692a7142043067ff64a00a1"
}

Some files were not shown because too many files have changed in this diff Show More