Skip to content

Add shuffle playback support#1771

Open
vikgor wants to merge 5 commits intojellyfin:mainfrom
vikgor:feature/shuffle
Open

Add shuffle playback support#1771
vikgor wants to merge 5 commits intojellyfin:mainfrom
vikgor:feature/shuffle

Conversation

@vikgor
Copy link

@vikgor vikgor commented Oct 20, 2025

Add Shuffle Playback Support

Summary

Implements shuffle playback for shows, collections, and library views. Users can shuffle from item detail pages, from library navigation menus, and view/navigate the shuffled queue on iOS.

Related Issues

Closes #673

New Files

  • ShuffleMediaPlayerQueue.swift - Queue management with iOS visual display
  • ShuffleActionHelper.swift - Centralized shuffle logic for all item types
  • ItemViewModel+Shuffle.swift - Unified shuffle playback method for ItemViewModel

Features

Shuffle Entry Points

tvOS - Advanced Options Menu

Shuffle is accessible through the existing Advanced Options menu:

tvOS action buttons tvOS options selected tvOS options tapped
tvOS - Action Button Stack tvOS - Options Button Selected tvOS - Advanced Options Menu

iOS - Play Button Context Menu

Shuffle is accessible via long-press on the Play button, alongside "Play from Beginning":

iOS Play button iPadOS Play button (long press)
iOS - Play Button (long-press for shuffle) iPadOS - Single Movie (No Shuffle Available)

Library View Menu

Users can shuffle directly from library views (e.g., TV Shows, Collections):

Library view menu iOS Library view menu iPadOS
iOS - Library View Menu iPadOS - Library View Menu

Visual Queue Display (iOS/iPadOS Only)

On iOS and iPadOS, users can view and navigate the shuffled queue with a visual episode list:

Episode row on iPadOS

iPadOS shuffle queue with episode list for quick navigation

Note: Visual queue display is only available on iOS/iPadOS, not tvOS.

Player Integration

  • Auto-advance: Automatically plays the next item in the shuffled queue
  • Next/Previous controls: Navigate forward/backward through the shuffle queue (Swiftfin only)
  • Siri support: Siri commands for playback control (Swiftfin only)

Notes

  • Previous shuffle implementation (#816) was removed in #905 refactor; this is a fresh implementation
  • Queue display reuses EpisodeMediaPlayerQueue components for consistency
  • Season shuffle support was intentionally excluded as seasons don't have dedicated UI representation
  • Error Handling TODOs: Added TODOs in PagingLibraryViewModel.swift, PagingLibraryView.swift (iOS), and PagingLibraryView.swift (tvOS) for implementing user-visible error messages. Currently, shuffle errors fail silently. A centralized error display mechanism is needed for background task errors across both platforms.

Implements shuffle for series, collections, and libraries with queue management & visual display on iOS;
Introduces an Options menu in ActionButtonHStack that contains shuffle functionality and is designed to accommodate future actions.
@alexphanna
Copy link

alexphanna commented Oct 20, 2025

Very cool, your implementation looks much more complete than mine. The one small suggestion I have is to move the shuffle button on the item details page. I don't think it should be behind an options menu, since it is just one option.

An alternate idea I have is putting the shuffle button to the right of the play button, either taking a half, a third, or a square of the horizontal space. My reasoning for this is that in my opinion it is more similar to the play action than the actions in the HStack. I do understand why the play button should remain the main focus though. That is why I would personally prefer a smaller button to the right of the play button with just the shuffle symbol and no text.

I will create a few visual examples using your repository in a few hours to make it more clear.

Copy link
Member

@JPKribs JPKribs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for your contribution!

I haven't had a chance to test this out but just 2 UI changes that stand out to me. Please let me know if you have any questions!

I also would ask you clean up some of the comments to mirror other Swiftfin classes. We've historically only left comments for items that require explanation or break standard practice. The code itself should be self explanatory (which is appears to currently be). Primarily, I just mean the comments over func.

Since this is a playback change, I'm going to lean on @LePips since he is a lot more familiar with this. He can help provide a more technical/in-depth review for your changes.


// MARK: - Options Menu

if viewModel.item.canShuffle {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to tvOS. I'm not sure this belongs in the toolbar menu on iOS (the advanced settings counterpart vs tvOS) but I'm not sure if it goes here either. I almost wonder if this is a contextMenu on the play button instead like how resume/start from beginning works?

Copy link
Author

@vikgor vikgor Oct 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've moved the Shuffle option to a long press on the Play button:

Play button Play button (long press)
Simulator Screenshot - iPhone 17 Pro Max - 2025-10-21 at 01 25 45 Simulator Screenshot - iPhone 17 Pro Max - 2025-10-21 at 01 25 48

Although I had some concerns when I started working on it - the context menu might not be obvious enough w/o any display that a long press has some extra options.
One more thing I noticed is that Collections (boxSet) don't display a Play button on iOS (by design in presentPlayButton property) - and even though it could be shuffled, there's no actual way of doing so. Unlike tvOS which shows the Advanced Options button and allows us to shuffle a boxSet:

tvOS iOS
Simulator Screenshot - Apple TV 4K (3rd generation) (at 1080p) - 2025-10-21 at 12 18 01 Simulator Screenshot - iPhone 17 Pro Max - 2025-10-21 at 12 31 26

I'd consider either adding the Advanced Options to the iOS view or keeping the long press but bringing it to tvOS (which is in the progress afaik).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The toolbar button is available in the Media tab, in case users wanted to shuffle Movies, for example.
It's also right next to "Random" which seems appropriate.

Simulator Screenshot - iPhone 17 Pro Max - 2025-10-21 at 12 48 32

This is iOS only at the moment since tvOS doesn't have a toolbar.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm... You are completely right. Part of where I am getting stuck is that the play button row is for playback, and the action button row are for actions that are not directly related to playback. Favoriting, marking has complete completed, trailers, etc.

If we keep that pattern, the shuffle definitely belongs on the play button row, but I agree the context menu isn't readily apparent. Pulling from other examples, the context menu seems to be used in other similar iOS-centric apps (example: Infuse)

image

Apple TV + doesn't even seem to have a shuffle so unfortunately, we don't have a direct Apple example.

I remember the original reason that we got rid of the play button on collections was because the steps that it took to go from the collection to an individual piece of content could often times be several jumps. For example, if I had a television show in a collection, I would need to go from collection to show to season to episode. This also meant that the logic for the play button was costly as well because we would want to find the next available piece of content that was unplayed potentially requiring us to dig into items.

Full transparency, I've been busy since the playback changes so I haven't had a great opportunity to look into what all is entailed for starting playback anymore. I know the information required is a lot different so maybe we need to look at revisiting the play button on collections? But, my guess is that we're still in the same spot for that. Which might also be a hangup for shuffling on collections as well? But, play button on collections would enable this in both locations and might be more consistent to do it this way on both iOS and tvOS so long as playback on collections is doable where we don't need a dozen server calls to calculate the correct playback item.

Sorry, I'm just thinking "out loud". I should hopefully have some time this weekend where I can look at this more in-depth and hopefully we can figure out a good place to put this! I'll reach out when I've had a chance to look at this more

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually like the long button press, and maybe it doesn't need to be obvious, or maybe it's obvious enough idk.

Shuffle on collections isn't top priority I think, we could leave that as a todo and whenever collection playback is improved shuffle would be pretty easy to enable.

No problem, I'm cool with either honestly

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am so sorry for the wait on this! I am pulling this down and taking a look at everything now. There were a couple changes on main updating the SDK to 10.11 so you will need to rebase to make sure you get everything but I think the localizations are the only part that isn't an auto-merge.

I will let you know as I wrap up testing this but please feel free to reach out if you have any trouble merging back with Main!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some notes:

Works well for a show but I think we'd want to make sure we can limit this to specific groupings. Something similar to what I started (and never finished) here: #1647. Specifically, we should have an option in the SeasonItemView to either shuffle a full series or just that specific season. I'm not sure how that would work menu wise but I think the ViewModel we can just add the ParentID as a parameter for input.

Movie shuffling, I am having some issues but these are purely related to the SDK changes that are now in Main. These seem to be resolved by bumping this version and getting this on 0.6.0. Since 10.11 added some HDR variations, if one of those HDR shows up in the shuffle everything crashes. Main resolves this.

The larger issue is the performance to start shuffling can be poor for larger shows or libraries. I think we need to think about a way to paginate the shuffle so we grab ~10 items, then grab more items later on. I'm not quite sure what that would look like. The issue I am thinking about is there are some shows with ~1,000 episodes. So shuffling those would take a decent chunk on this version.

Last UI item, I am actually a fan of the context menu that makes sense to me. I have family that still uses Infuse so that could be part of it that mirrors something I am familiar with but I think it makes sense. Only change I think would be valuable is add a loading spinner or something when the shuffle is selected. Since, now, we can have a few hundred items queue for shuffling, I found I could select shuffle then move like 5 views away before the player popped up. Adding a spinner will keep the user in place and also provide some feedback that the shuffle worked especially on slower networks where that call might take a second.

I think we want to extend the BaseItemDto and add a canShuffle function that we can use to toggle shuffle on and off. Additionally, we can put the shuffle logic as an override on the ItemView as a whole and cascade that down since we will want that for Music eventually as well.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, I am a bit confused why we use the local .shuffled() function, when there is a random itemSortBy option using in the api. Sorry to repeat myself, but can you take a look at my last comment on this pr. It's very possible I am overlooking something, but I made a pr on the fork just in case. Thanks.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am a bit confused why we use the local .shuffled() function

Sorry, yes I agree that we should use the API random sort as well. Using the API random plus pagination, we should be able to get the paginated results I was mentioning.

Apologies, I didn't mean to skip over your suggestion. I do agree that is the best way to go about this!

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All good. I feel like the Episodes button behavior is confusing as well. When I play an episode normally, the button shows the other episodes in the season. When I shuffle a show, the button shows the entire queue of episodes. Is the behavior of this button intended to be a queue?

If it is intended to be a queue then I think it should probably normally go beyond the season of the current episode. For example, if you play the last episode of a season, maybe the next season should show in this menu. This could work well with pagination, if it was just a queue of the next 10 episodes both ways.

@alexphanna
Copy link

I never realized there was a context menu on the play button. That may be a better idea than mine.

@JPKribs JPKribs added the enhancement New feature or request label Oct 20, 2025
@alexphanna
Copy link

One of these options is how I imagined the shuffle button on iOS:

1/2 1/3 1/4 Square

@JPKribs
Copy link
Member

JPKribs commented Oct 21, 2025

One of these options is how I imagined the shuffle button on iOS:

| | | | |

| ----------- | ----------- | ----------- | ----------- |

| 1/2 | 1/3 | 1/4 | Square |

Please make sure anything inline with the play button also accounts for the version selector. iOS it's in the action bar but tvOS it takes the same placement as your shuffle button.

From an older PR, this is where that goes now:

Simulator.Screen.Recording.-.Apple.TV.4K.3rd.generation.-.2025-03-20.at.12.49.44.mp4

The context menu, to me, feels like our best option. Mirrors our resume/play from beginning while still being intuitive (I think). That being said, I'm not the most UI-minded guy in the world so there definitely could be a better, more intuitive solution. I'd prefer if we could keep it consistent between iOS and tvOS if possible.

I'm a broken record but I'd be interested in @LePips's insight as he's a lot more involved in SwiftUI standard practice than I am.

@alexphanna
Copy link

I'm sorry, I was not aware of the version selector's position on tvOS. Ironically that is inconsistent across platforms.

I'll try my best to succinctly explain why I don't think the shuffle button should be in the context menu. Currently the play button has two actions as you mentioned, resume and play from the beginning, both of these actions pertain to a single episode. The shuffle action on the other hand is unrelated to the episode displayed on the play button as it shuffles episodes from the entire show.

As a side note, I mentioned this before, but the context menu as a whole was not intuitive in my experience.

Move Shuffle to Play Button Context Menu (iOS) & Advanced Options Menu (tvOS);
alexphanna

This comment was marked as outdated.

@alexphanna
Copy link

In my opinion, ItemSortBy.random should be used as the sortBy parameter to shuffle content instead of sorting by name and using the .shuffled() function. This makes use of the API instead of performing the shuffling locally. I opted for making these changes in a pull request on the fork instead of my previous suggestion to reduce spam.

@JPKribs JPKribs added iOS Impacts iOS or iPadOS tvOS Impacts tvOS labels Oct 28, 2025
Copy link
Member

@LePips LePips left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apologies, as I do change how libraries are made in #1752 - which afterward would make the implementation of this feature easier.

Based on the discussion above:

  • This would be accessible from the play button via the context menu
  • We can't retrieve all items from a parent at once, which is what the calls currently do, and only have a look-ahead of ~20 items. We should be retrieving the items from the server using the sort parameter rather than shuffling locally.

Based on general review:

  • We should find a way to consolidate the views used between the episode queue supplement and this supplement, since they are exactly the same.
  • Retrieving the child items from a parent doesn't need to be split between series/boxset. After #1752, we should be able to use an existing library and just pass in the filters. Additionally, we wouldn't specify the item types but use the .video media type.

@vikgor
Copy link
Author

vikgor commented Nov 3, 2025

Hello there, sorry for the late reply, I'll be updating this comment along with commits with fixes. I'll address all of them in the coming days. Gonna keep this checklist here for now:

  • Use ItemSortBy.random for API-based shuffling
  • Consolidate queue supplement views (EpisodeRow/EpisodeButton)
  • Add a loading spinner
  • Add acanShuffle property on BaseItemDto

Since #1752 is pretty big but can make this implementation easier, I can either branch from LePips:poster-library-home or just wait until it's merged, do you have an approximate timeline on that PR?

  • Use library approach with .video media type instead of splitting series/boxset logic (this one is linked to Posters, Libraries, Home #1752)
  • Implement pagination for large shows/libraries (~20 items)
  • Add season shuffle support (shuffle full series vs specific season) - although I'm not sure what that would look like.

I'm not in a rush, if we can keep this PR here until #1752 is merged I'm cool with that, gonna work on something else in the meantime.

@vikgor
Copy link
Author

vikgor commented Nov 17, 2025

Hello there, I've updated the branch following the latest comments. In short:

  • Unified shuffle logic into ItemViewModel,
  • Added dynamic queue fetching to ShuffleMediaPlayerQueue
  • Extracted shared queue UI components into MediaPlayerQueueItemViews to eliminate duplicate code between ShuffleMediaPlayerQueue and EpisodeMediaPlayerQueue.

Shuffle flow

  1. User triggers shuffle -> ItemViewModel.playShuffle() is called
  2. Initial fetch -> getShuffledItems() returns first batch (20 items, keeping that '20' in ShuffleQueueConstants)
  3. Queue creation: ShuffleMediaPlayerQueue initialized with items + fetchMoreItems closure
  4. Playback starts -> First item plays, queue tracks current index
  5. When <= 10 items remain, fetchMoreItems() is automatically called. FetchState tracks excluded items to prevent duplicates
  6. Queue overlay shows items using MediaPlayerQueueItemViews components

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request iOS Impacts iOS or iPadOS tvOS Impacts tvOS

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Random order play

4 participants