feat(storage-metrics): split potential reclaim into movie/show/season/episode panels#2854
Merged
enoch85 merged 2 commits intoMay 6, 2026
Conversation
…ype breakdown The Potential reclaim by type breakdown only bucketed 'movie' and 'show' collections; 'season' and 'episode' fell through and were silently dropped. The header total counted them via reclaimableCount and activeSizeBytes, producing a contradiction inside the same view (e.g. '1 of 1 reclaimable collections sized — 232 GB' next to 'Movies 0 B / 0 collections, Shows 0 B / 0 collections'). Roll show-derived types (show / season / episode) into the existing Shows bucket so the per-type panel matches the header total. No contract change, no UI change. Splitting Seasons/Episodes into their own panels — to mirror the cleanup-totals layout — can be a separate follow-up.
…/episode panels The Potential reclaim by type panel previously bucketed only movie and show, so season and episode collections were silently dropped from the breakdown even though the header total counted them. The previous fix in this PR rolled them into the Shows bucket as a stop-gap. This widens the contract and the UI to four distinct buckets, mirroring the Cleanup totals layout (Movies / Shows / Seasons / Episodes). Service splits the count loop, the per-item DB-grouped sum, and the fallback per-collection sum into their own type branches; UI grid switches to sm:grid-cols-2 lg:grid-cols-4 with Seasons (CollectionIcon) and Episodes (PlayIcon) panels matching the cleanup totals icons. Validated end-to-end with seeded data covering all four types: header total equals the sum of the four panel buckets in both per-item and fallback modes; the user's reproducer (an episode-only collection showing 232 GB in the header but 0 B in both Movies and Shows) now correctly attributes the bytes to the Episodes panel.
maintainerr-automation Bot
added a commit
that referenced
this pull request
May 8, 2026
* feat(storage-metrics): split potential reclaim into movie/show/season/episode panels (#2854) * fix(collections): credit reclaimed bytes when sizeBytes is not yet cached (#2855) * Feature/plex collection sort (#2856) * feat: implement plex adapter collection sorting logic * feat: apply collection sort orchestration in collections service * feat: add collection items sort dropdown to rule form * feat: complete UI and full stack implementation for collection sorting * fix(collections): address review feedback for collection sort - Generate proper TypeORM migration for mediaServerSort column (previous PR shipped the entity change without one — upgrades would fail on the missing column). - Extend collection sort comparator for addDate and releaseDate; the UI offered seven sort options but four (addDate.{asc,desc}, releaseDate.{asc,desc}) fell through to the default branch and silently preserved input order. - Replace fake MediaServerCollectionSort union with a real template literal (\${CollectionMediaSortField}.\${MediaSortOrder}); add parseCollectionSortKey helper and remove \`as any\` casts. - Add smart-collection guard before reorder (Plex rejects move on smart) and membership-changed gate so we only push order to Plex when newMedia.length > 0. - Filter items with missing metadata before sorting; replace the \`mediaData!\` non-null assertion. - Move MEDIA_SERVER_FEATURES + supportsFeature into contracts and gate the UI dropdown via supportsFeature(COLLECTION_SORT) so the feature becomes available automatically if Jellyfin ever gains a boxset reorder API. - Add ICollection.mediaServerSort field; tighten zod-to-payload parsing in AddModal. - Revert unrelated edits introduced by this PR (rules.service return values, scheduler defensive guards, cosmetic setContext changes). - Tests: setCollectionCustomSort and moveCollectionItem URL shapes, PlexAdapter.reorderCollectionItems orchestration, JellyfinAdapter feature flag and reorder rejection. --------- Co-authored-by: Steven Seitz <steven.seitz@omron.com> Co-authored-by: enoch85 <mailto@danielhansson.nu> * fix(collections): apply collection sort on save, harden Plex reorder (#2860) - Apply the collection sort immediately when a rule group is saved with a newly enabled or changed sort value, so users no longer see a configured sort that hasn't been pushed to Plex until a later add cycle. Previous behaviour gated the push on `newMedia.length > 0`, which left save-time changes unapplied indefinitely. - Continue past per-item move failures in the Plex adapter reorderCollectionItems loop. A single rejected move no longer aborts subsequent moves; failures are accumulated and logged in a summary warning so the rest of the collection still gets ordered. - Short-circuit the Plex reorder when the collection is already in the requested order. The adapter reads current child IDs first via PlexApiService.getCollectionChildren and returns early when they match `orderedItemIds`, skipping both the prefs PUT and every move PUT. The membership-changed gate in addToCollectionInternal is kept as a coarse pre-filter; the adapter check is the safety net. - Reuse the existing `case 'title':` branch in compareMediaItemsBySort as a tiebreaker for airDate / rating / watchCount / deleteSoonest, so items sharing the same primary value fall back to alphabetical ordering. Status sorts (manual / excluded) intentionally skip the fallback to preserve their partition contract. Affects every consumer of the comparator (Maintainerr UI overview/library/ collection display and the Plex collection-sort push) so on-disk Plex order matches what users see in Maintainerr for the same key. Tests: new sort-utils.spec covering the timelordx tiebreaker scenario, updated plex-adapter.service.spec for short-circuit and per-item failure paths, and rules.service.updateRules.spec covering the save-time apply transition. * fix(metadata): align Jellyfin retry id check with pre-filter (#2853) The metadata refresh retry path validated the verified id with isBlankMediaServerId, while the pre-filter used shouldRefreshMetadataItemId. When getMetadata returned an item with the Jellyfin empty GUID (00000000-0000-0000-0000-000000000000), the asymmetric check let the retry call refreshItemMetadata with that id, producing the "Guid can't be empty" spam in Jellyfin's ProviderManager queue. Use the same predicate in both places so foreign-shaped and empty-GUID ids are rejected consistently before any refresh request is issued. * Add Maintainerr architecture overview (#2817) --------- Co-authored-by: enoch85 <mailto@danielhansson.nu> Co-authored-by: 00Scooby <72069678+00Scooby@users.noreply.github.com> Co-authored-by: Steven Seitz <steven.seitz@omron.com> Co-authored-by: maintainerr-automation[bot] <261505141+maintainerr-automation[bot]@users.noreply.github.com> Co-authored-by: Kristian Matthews-Kennington <kristian@matthews-kennington.com>
Contributor
|
🎉 This PR is included in version 3.11.0 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
doonga
pushed a commit
to greyrock-labs/home-ops
that referenced
this pull request
May 13, 2026
…11.1 ) (#29) This PR contains the following updates: | Package | Update | Change | |---|---|---| | [ghcr.io/maintainerr/maintainerr](https://github.com/Maintainerr/Maintainerr) | minor | `3.10.1` → `3.11.1` | --- ### Release Notes <details> <summary>Maintainerr/Maintainerr (ghcr.io/maintainerr/maintainerr)</summary> ### [`v3.11.1`](https://github.com/Maintainerr/Maintainerr/blob/HEAD/CHANGELOG.md#3111-2026-05-12) [Compare Source](Maintainerr/Maintainerr@v3.11.0...v3.11.1) #### Highlights - Fixed an issue where Jellyfin libraries with "Group films into collections" enabled caused BoxSet members to incorrectly toggle in and out of rule results ([#​2870](Maintainerr/Maintainerr#2870)). - Improved error message for invalid Plex library section IDs to better guide users in resolving configuration issues ([#​2883](Maintainerr/Maintainerr#2883)). - Enhanced custom collection UX by renaming tags for clarity and adding tooltips to explain collection handling options ([#​2882](Maintainerr/Maintainerr#2882)). #### Fixes - Validated Jellyfin IDs before refresh to prevent errors ([#​2853](Maintainerr/Maintainerr#2853)). - Improved error message when Plex library section ID is invalid ([#​2883](Maintainerr/Maintainerr#2883)). - Resolved Jellyfin collection add/remove loop for BoxSet items ([#​2870](Maintainerr/Maintainerr#2870)). - Clarified custom collection terminology and added warnings for disabling collection handling ([#​2882](Maintainerr/Maintainerr#2882)). - Added explicit token permissions to the Fider move job to address CodeQL findings. - Added environment gate to mitigate TOCTOU vulnerability in the release\_pr workflow ([#​2879](Maintainerr/Maintainerr#2879)). - Addressed transitive dependency vulnerabilities by adding Yarn resolutions for specific packages ([#​2881](Maintainerr/Maintainerr#2881)). #### Dependencies - Updated 10 dependencies, including notable packages: vite, typeorm, and [@​typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/eslint-plugin). #### New Contributors - [@​blixten85](https://github.com/blixten85) made their first contribution in [#​2881](Maintainerr/Maintainerr#2881) ### [`v3.11.0`](https://github.com/Maintainerr/Maintainerr/blob/HEAD/CHANGELOG.md#3110-2026-05-11) [Compare Source](Maintainerr/Maintainerr@v3.10.2...v3.11.0) #### Highlights - Added support for force-processing overlays and gated reset operations against concurrent processing runs ([#​2827](Maintainerr/Maintainerr#2827)). - Improved collection sorting: collections now apply sort order on save and synchronize with the media server ([#​2860](Maintainerr/Maintainerr#2860)). - Enhanced storage metrics: potential reclaimable storage is now split into movie, show, season, and episode panels ([#​2854](Maintainerr/Maintainerr#2854)). #### Breaking Changes - None. #### Features - Added force-processing support for overlay operations and gated reset operations against concurrent processing runs ([#​2827](Maintainerr/Maintainerr#2827)). - Improved storage metrics by splitting potential reclaimable storage into movie, show, season, and episode panels ([#​2854](Maintainerr/Maintainerr#2854)). #### Fixes - Fixed collection sorting to apply on save and synchronize with the media server ([#​2860](Maintainerr/Maintainerr#2860)). - Resolved issue where excluding a single episode incorrectly excluded all episodes of the same show ([#​2867](Maintainerr/Maintainerr#2867)). - Fixed storage metrics to merge shared volumes across hosts when byte-exact ([#​2852](Maintainerr/Maintainerr#2852)). - Fixed Jellyfin retry ID check to align with pre-filter logic ([#​2853](Maintainerr/Maintainerr#2853)). - Addressed issue where reclaimed bytes were not credited when `sizeBytes` was not yet cached ([#​2855](Maintainerr/Maintainerr#2855)). #### Performance - None. #### Database migrations - Added a new `mediaServerSort` column to the `collection` table to support media server-specific sorting. #### Internal - Added typed TanStack Query test helpers and migrated UI spec mocks to use typed builders ([#​2863](Maintainerr/Maintainerr#2863)). - Added an architecture overview document detailing the monorepo structure, runtime flow, and core components ([#​2817](Maintainerr/Maintainerr#2817)). #### Dependencies - Updated 5 dependencies, including notable updates to TypeScript and typescript-eslint. ### [`v3.10.2`](https://github.com/Maintainerr/Maintainerr/blob/HEAD/CHANGELOG.md#3102-2026-05-08) [Compare Source](Maintainerr/Maintainerr@v3.10.1...v3.10.2) #### Highlights - Fixed incorrect version comparison logic that caused update notifications to fail for multi-digit version segments ([#​2838](Maintainerr/Maintainerr#2838)). - Improved storage metrics by deduplicating reclaimable bytes and adding per-type cleanup byte counters ([#​2833](Maintainerr/Maintainerr#2833)). </details> --- ### Configuration 📅 **Schedule**: (in timezone America/New_York) - Branch creation - At any time (no schedule defined) - Automerge - At any time (no schedule defined) 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about these updates again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xNjAuNyIsInVwZGF0ZWRJblZlciI6IjQzLjE2MC43IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJyZW5vdmF0ZS9jb250YWluZXIiLCJ0eXBlL21pbm9yIl19--> Reviewed-on: https://git.greyrock.io/greyrock-labs/home-ops/pulls/29
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The Storage Metrics page's Potential reclaim by type panel only had Movies and Shows buckets. Season and episode collections fell through both the count and bytes branches and were silently dropped, while the header card directly above counted them — so the same view could show
1 of 1 reclaimable collections sized — 232 GBimmediately aboveMovies 0 B / 0 collections, Shows 0 B / 0 collectionsfor an episode-typed collection.This adds Seasons and Episodes panels to mirror the Cleanup totals layout (which already breaks out all four types). Now the per-type breakdown matches the header total and matches the Cleanup totals icons + structure.
Changes
StorageCollectionSummary): addsseasonSizeBytes,episodeSizeBytes,reclaimableSeasonCount,reclaimableEpisodeCount.buildCollectionSummary): splits the count loop, the per-item DB-grouped sum, and the fallback per-collection-total sum into four explicit type branches.sm:grid-cols-2tosm:grid-cols-2 lg:grid-cols-4; Seasons usesCollectionIcon, Episodes usesPlayIcon— matching the Cleanup totals row.Validation
End-to-end checked with the dev server + Playwright + a seeded SQLite DB:
collection_media.sizeBytespopulated. UI shows Movies 953.67 MB / Shows 1.86 GB / Seasons 2.79 GB / Episodes 476.84 MB; header total 6.05 GB matches the sum of the four buckets.collection_mediarows so per-item sizes are missing; service falls back tocollection.totalSizeBytes. All four panels still display correctly with the fallback subtitle copy active.handledMediaAmountandhandledMediaSizeBytesper type — all five summary cards (Items / Movies / Shows / Seasons / Episodes) display correct counts and reclaimed bytes.