Skip to content

Feature/plex collection sort#2856

Merged
enoch85 merged 6 commits into
Maintainerr:developmentfrom
00Scooby:feature/plex-collection-sort
May 8, 2026
Merged

Feature/plex collection sort#2856
enoch85 merged 6 commits into
Maintainerr:developmentfrom
00Scooby:feature/plex-collection-sort

Conversation

@00Scooby

@00Scooby 00Scooby commented May 7, 2026

Copy link
Copy Markdown
Contributor

Note to contributors: > This template is part of our review process. Pull requests that do not meaningfully complete the sections below, or that do not demonstrate understanding of the change, may be closed without detailed review.

Description & Design

Please include:

  • What problem this change solves: Users currently cannot automatically sort the items inside a media server collection created by Maintainerr. This PR adds the ability to define a custom sort order (e.g., Release Date, Date Added, Alphabetical) directly from the Rule Group settings.
  • Why this approach was chosen: We leverage Plex's custom sort capabilities. By applying the sort directly after the batch add process during rule execution, we ensure that the collection immediately reflects the user's desired order on the media server.
  • How the solution works at a high level: 1. A mediaServerSort column is added to the Collection database entity.
    2. The UI (AddModal) is updated with a dropdown to select the sort criteria (conditionally rendered for Plex).
    3. The PlexApiService and PlexAdapterService are extended with setCollectionCustomSort and moveCollectionItem endpoints.
    4. In collections.service.ts, after adding new items to the collection, the backend retrieves the hydrated metadata, sorts the items locally according to the chosen criteria, and pushes the exact new order to the media server.
  • Any important trade-offs, edge cases, or limitations: This feature is currently limited to Plex, as Jellyfin handles custom collection sorting differently. The UI field is gated behind the isPlex check, and the backend logic is protected by mediaServer.supportsFeature(MediaServerFeature.COLLECTION_SORT).

Related issue

no related issue

AI-Assisted Development

If you used AI tools (e.g. ChatGPT, Copilot, Claude) while working on this PR, please describe:

  • Which parts were AI-assisted: Generating the boilerplate for the Plex API integration, adjusting the UI forms, and structuring the local sorting logic in collections.service.ts.
  • What you personally reviewed, changed, or validated: I manually verified all Plex API calls via network traces, ensured database migrations worked cleanly on a fresh setup, debugged and fixed caching/startup errors (ECONNREFUSED and empty DB states), and rigorously tested the end-to-end flow on my local instance.
  • Why this solution fits this project’s coding standards and design: The implementation cleanly follows the existing Adapter pattern (IMediaServerService), hides Plex-specific logic behind feature flags, and seamlessly integrates into the existing UI components and Rule Executor lifecycle.

Note: The author is fully responsible for the correctness, testing, and maintainability of this change, regardless of tooling used.

Checklist

  • I have read the CONTRIBUTING.md document.
  • I understand the code I am submitting and can explain how it works
  • I have performed a self-review of my code
  • I have linted and formatted my code
  • My changes generate no new warnings
  • New and existing unit tests pass locally with my changes

How to test

Please describe the steps to test your changes, including any setup required.

  1. Create a new Rule Group in Maintainerr and select a Plex library.
  2. In the Options section, locate the new Collection items sort dropdown and select a criteria (e.g., "Release Date (Newest first)").
  3. Save the rule and execute it manually.
  4. Check your Plex server: The created collection should have its sorting set to "Custom", and the items should be ordered exactly as configured.

Additional context

Screenshot of the new UI configuration:
image

@SmolSoftBoi SmolSoftBoi added the enhancement New feature or request label May 7, 2026
@enoch85

enoch85 commented May 7, 2026

Copy link
Copy Markdown
Collaborator

@00Scooby Thanks! Nice and tidy PR, appriciated!

I'm reviewing this now.

- 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.
@enoch85 enoch85 force-pushed the feature/plex-collection-sort branch from 8f74b00 to a1874b7 Compare May 7, 2026 17:30
@enoch85

enoch85 commented May 7, 2026

Copy link
Copy Markdown
Collaborator

Made some changes, please test that this still works. 🙏

@enoch85 enoch85 marked this pull request as ready for review May 7, 2026 17:35
@enoch85 enoch85 self-requested a review as a code owner May 7, 2026 17:35
@00Scooby

00Scooby commented May 8, 2026

Copy link
Copy Markdown
Contributor Author

Hey @enoch85,

I just pulled your latest changes and did a hard reset on my local environment to clear the Vite cache.

I ran a full end-to-end test again, and everything works flawlessly! The supportsFeature check executes perfectly, and the collections are sorted exactly as configured in Plex.

From my side, this is completely good to go and ready to be merged! 🚀 Thanks again for the review and the final tweaks!

@enoch85 enoch85 merged commit c6c720a into Maintainerr:development May 8, 2026
13 checks passed
@enoch85

enoch85 commented May 8, 2026

Copy link
Copy Markdown
Collaborator

Congrats on your first PR to Maintainerr @00Scooby 🎉

@timelordx

Copy link
Copy Markdown

This is great! I really like it. It would be awesome if, when day/date based sorting is selected, a secondary alphabetical sort was automatically applied as well.

For example, let’s say we have these movies that are going to be deleted in 3 days:

S, H, G, X

and these movies that are going to be deleted in 5 days:

C, Z, A

The resulting order would be:

G, H, S, X, A, C, Z

So the primary sort would be “days to delete”, and then alphabetical when multiple movies share the same “days to delete” value.

That’s how the current UI version of Maintainerr-Overlay-Helperr handles it.

Sometimes there are 20+ movies scheduled for deletion on the same day, so having them sorted alphabetically within that group makes things much easier to browse.

@enoch85

enoch85 commented May 8, 2026

Copy link
Copy Markdown
Collaborator

@MrLinford Are you up for the task?

@timelordx Please create a seperate issue for this.

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>
@timelordx

Copy link
Copy Markdown

@enoch85: https://github.com/Maintainerr/Maintainerr/issues/2861

Sorry about using the bug template; that seems to be the only one available.

@maintainerr-automation

Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 3.11.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

@enoch85 enoch85 added this to the 3.11.0 milestone May 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request released

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants