< Summary - Jellyfin

Information
Class: MediaBrowser.Controller.SyncPlay.Queue.PlayQueueManager
Assembly: MediaBrowser.Controller
File(s): /srv/git/jellyfin/MediaBrowser.Controller/SyncPlay/Queue/PlayQueueManager.cs
Line coverage
43%
Covered lines: 79
Uncovered lines: 104
Coverable lines: 183
Total lines: 569
Line coverage: 43.1%
Branch coverage
34%
Covered branches: 26
Total branches: 76
Branch coverage: 34.2%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100 4/15/2026 - 12:14:34 AM Line coverage: 0% (0/174) Branch coverage: 0% (0/70) Total lines: 5506/19/2026 - 12:16:12 AM Line coverage: 14.9% (26/174) Branch coverage: 5.7% (4/70) Total lines: 5507/3/2026 - 12:15:32 AM Line coverage: 17.2% (30/174) Branch coverage: 8.5% (6/70) Total lines: 5507/26/2026 - 12:17:51 AM Line coverage: 43.1% (79/183) Branch coverage: 34.2% (26/76) Total lines: 569 4/15/2026 - 12:14:34 AM Line coverage: 0% (0/174) Branch coverage: 0% (0/70) Total lines: 5506/19/2026 - 12:16:12 AM Line coverage: 14.9% (26/174) Branch coverage: 5.7% (4/70) Total lines: 5507/3/2026 - 12:15:32 AM Line coverage: 17.2% (30/174) Branch coverage: 8.5% (6/70) Total lines: 5507/26/2026 - 12:17:51 AM Line coverage: 43.1% (79/183) Branch coverage: 34.2% (26/76) Total lines: 569

Coverage delta

Coverage delta 26 -26

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor()100%11100%
IsItemPlaying()100%11100%
GetPlaylist()100%11100%
SetPlaylist(...)50%2277.77%
Queue(...)0%620%
ShufflePlaylist()25%9433.33%
RestoreSortedPlaylist()0%620%
ClearPlaylist(...)0%4260%
QueueNext(...)0%620%
GetPlayingItemPlaylistId()50%22100%
GetPlayingItemId()50%22100%
SetPlayingItemById(...)100%210%
SetPlayingItemByPlaylistId(...)100%11100%
SetPlayingItemByIndex(...)100%44100%
RemoveFromPlaylist(...)70%101094.44%
MovePlaylistItem(...)0%620%
Reset()100%11100%
SetRepeatMode(...)100%11100%
SetShuffleMode(...)50%3250%
ToggleShuffleMode()0%620%
GetNextItemPlaylistId()0%110100%
Next()12.5%47815.38%
Previous()12.5%47815.38%
CreateQueueItemsFromArray(...)100%22100%
GetPlaylistInternal()100%22100%
GetPlayingItem()100%44100%

File(s)

/srv/git/jellyfin/MediaBrowser.Controller/SyncPlay/Queue/PlayQueueManager.cs

#LineLine coverage
 1#nullable disable
 2
 3using System;
 4using System.Collections.Generic;
 5using System.Linq;
 6using Jellyfin.Extensions;
 7using MediaBrowser.Model.SyncPlay;
 8
 9namespace MediaBrowser.Controller.SyncPlay.Queue
 10{
 11    /// <summary>
 12    /// Class PlayQueueManager.
 13    /// </summary>
 14    public class PlayQueueManager
 15    {
 16        /// <summary>
 17        /// Placeholder index for when no item is playing.
 18        /// </summary>
 19        /// <value>The no-playing item index.</value>
 20        private const int NoPlayingItemIndex = -1;
 21
 22        /// <summary>
 23        /// The sorted playlist.
 24        /// </summary>
 25        /// <value>The sorted playlist, or play queue of the group.</value>
 1826        private List<SyncPlayQueueItem> _sortedPlaylist = new List<SyncPlayQueueItem>();
 27
 28        /// <summary>
 29        /// The shuffled playlist.
 30        /// </summary>
 31        /// <value>The shuffled playlist, or play queue of the group.</value>
 1832        private List<SyncPlayQueueItem> _shuffledPlaylist = new List<SyncPlayQueueItem>();
 33
 34        /// <summary>
 35        /// Initializes a new instance of the <see cref="PlayQueueManager" /> class.
 36        /// </summary>
 37        public PlayQueueManager()
 38        {
 1839            Reset();
 1840        }
 41
 42        /// <summary>
 43        /// Gets the playing item index.
 44        /// </summary>
 45        /// <value>The playing item index.</value>
 46        public int PlayingItemIndex { get; private set; }
 47
 48        /// <summary>
 49        /// Gets the last time the queue has been changed.
 50        /// </summary>
 51        /// <value>The last time the queue has been changed.</value>
 52        public DateTime LastChange { get; private set; }
 53
 54        /// <summary>
 55        /// Gets the shuffle mode.
 56        /// </summary>
 57        /// <value>The shuffle mode.</value>
 58        public GroupShuffleMode ShuffleMode { get; private set; } = GroupShuffleMode.Sorted;
 59
 60        /// <summary>
 61        /// Gets the repeat mode.
 62        /// </summary>
 63        /// <value>The repeat mode.</value>
 64        public GroupRepeatMode RepeatMode { get; private set; } = GroupRepeatMode.RepeatNone;
 65
 66        /// <summary>
 67        /// Checks if an item is playing.
 68        /// </summary>
 69        /// <returns><c>true</c> if an item is playing; <c>false</c> otherwise.</returns>
 70        public bool IsItemPlaying()
 71        {
 1172            return PlayingItemIndex != NoPlayingItemIndex;
 73        }
 74
 75        /// <summary>
 76        /// Gets the current playlist considering the shuffle mode.
 77        /// </summary>
 78        /// <returns>The playlist.</returns>
 79        public IReadOnlyList<SyncPlayQueueItem> GetPlaylist()
 80        {
 3781            return GetPlaylistInternal();
 82        }
 83
 84        /// <summary>
 85        /// Sets a new playlist. Playing item is reset.
 86        /// </summary>
 87        /// <param name="items">The new items of the playlist.</param>
 88        public void SetPlaylist(IReadOnlyList<Guid> items)
 89        {
 1190            _sortedPlaylist.Clear();
 1191            _shuffledPlaylist.Clear();
 92
 1193            _sortedPlaylist = CreateQueueItemsFromArray(items);
 1194            if (ShuffleMode.Equals(GroupShuffleMode.Shuffle))
 95            {
 096                _shuffledPlaylist = new List<SyncPlayQueueItem>(_sortedPlaylist);
 097                _shuffledPlaylist.Shuffle();
 98            }
 99
 11100            PlayingItemIndex = NoPlayingItemIndex;
 11101            LastChange = DateTime.UtcNow;
 11102        }
 103
 104        /// <summary>
 105        /// Appends new items to the playlist. The specified order is maintained.
 106        /// </summary>
 107        /// <param name="items">The items to add to the playlist.</param>
 108        public void Queue(IReadOnlyList<Guid> items)
 109        {
 0110            var newItems = CreateQueueItemsFromArray(items);
 111
 0112            _sortedPlaylist.AddRange(newItems);
 0113            if (ShuffleMode.Equals(GroupShuffleMode.Shuffle))
 114            {
 0115                _shuffledPlaylist.AddRange(newItems);
 116            }
 117
 0118            LastChange = DateTime.UtcNow;
 0119        }
 120
 121        /// <summary>
 122        /// Shuffles the playlist. Shuffle mode is changed. The playlist gets re-shuffled if already shuffled.
 123        /// </summary>
 124        public void ShufflePlaylist()
 125        {
 1126            if (PlayingItemIndex == NoPlayingItemIndex)
 127            {
 1128                _shuffledPlaylist = new List<SyncPlayQueueItem>(_sortedPlaylist);
 1129                _shuffledPlaylist.Shuffle();
 130            }
 0131            else if (ShuffleMode.Equals(GroupShuffleMode.Sorted))
 132            {
 133                // First time shuffle.
 0134                var playingItem = _sortedPlaylist[PlayingItemIndex];
 0135                _shuffledPlaylist = new List<SyncPlayQueueItem>(_sortedPlaylist);
 0136                _shuffledPlaylist.RemoveAt(PlayingItemIndex);
 0137                _shuffledPlaylist.Shuffle();
 0138                _shuffledPlaylist.Insert(0, playingItem);
 0139                PlayingItemIndex = 0;
 140            }
 141            else
 142            {
 143                // Re-shuffle playlist.
 0144                var playingItem = _shuffledPlaylist[PlayingItemIndex];
 0145                _shuffledPlaylist.RemoveAt(PlayingItemIndex);
 0146                _shuffledPlaylist.Shuffle();
 0147                _shuffledPlaylist.Insert(0, playingItem);
 0148                PlayingItemIndex = 0;
 149            }
 150
 1151            ShuffleMode = GroupShuffleMode.Shuffle;
 1152            LastChange = DateTime.UtcNow;
 1153        }
 154
 155        /// <summary>
 156        /// Resets the playlist to sorted mode. Shuffle mode is changed.
 157        /// </summary>
 158        public void RestoreSortedPlaylist()
 159        {
 0160            if (PlayingItemIndex != NoPlayingItemIndex)
 161            {
 0162                var playingItem = _shuffledPlaylist[PlayingItemIndex];
 0163                PlayingItemIndex = _sortedPlaylist.IndexOf(playingItem);
 164            }
 165
 0166            _shuffledPlaylist.Clear();
 167
 0168            ShuffleMode = GroupShuffleMode.Sorted;
 0169            LastChange = DateTime.UtcNow;
 0170        }
 171
 172        /// <summary>
 173        /// Clears the playlist. Shuffle mode is preserved.
 174        /// </summary>
 175        /// <param name="clearPlayingItem">Whether to remove the playing item as well.</param>
 176        public void ClearPlaylist(bool clearPlayingItem)
 177        {
 0178            var playingItem = GetPlayingItem();
 0179            _sortedPlaylist.Clear();
 0180            _shuffledPlaylist.Clear();
 0181            LastChange = DateTime.UtcNow;
 182
 0183            if (!clearPlayingItem && playingItem is not null)
 184            {
 0185                _sortedPlaylist.Add(playingItem);
 0186                if (ShuffleMode.Equals(GroupShuffleMode.Shuffle))
 187                {
 0188                    _shuffledPlaylist.Add(playingItem);
 189                }
 190
 0191                PlayingItemIndex = 0;
 192            }
 193            else
 194            {
 0195                PlayingItemIndex = NoPlayingItemIndex;
 196            }
 0197        }
 198
 199        /// <summary>
 200        /// Adds new items to the playlist right after the playing item. The specified order is maintained.
 201        /// </summary>
 202        /// <param name="items">The items to add to the playlist.</param>
 203        public void QueueNext(IReadOnlyList<Guid> items)
 204        {
 0205            var newItems = CreateQueueItemsFromArray(items);
 206
 0207            if (ShuffleMode.Equals(GroupShuffleMode.Shuffle))
 208            {
 0209                var playingItem = GetPlayingItem();
 0210                var sortedPlayingItemIndex = _sortedPlaylist.IndexOf(playingItem);
 211                // Append items to sorted and shuffled playlist as they are.
 0212                _sortedPlaylist.InsertRange(sortedPlayingItemIndex + 1, newItems);
 0213                _shuffledPlaylist.InsertRange(PlayingItemIndex + 1, newItems);
 214            }
 215            else
 216            {
 0217                _sortedPlaylist.InsertRange(PlayingItemIndex + 1, newItems);
 218            }
 219
 0220            LastChange = DateTime.UtcNow;
 0221        }
 222
 223        /// <summary>
 224        /// Gets playlist identifier of the playing item, if any.
 225        /// </summary>
 226        /// <returns>The playlist identifier of the playing item.</returns>
 227        public Guid GetPlayingItemPlaylistId()
 228        {
 11229            var playingItem = GetPlayingItem();
 11230            return playingItem?.PlaylistItemId ?? Guid.Empty;
 231        }
 232
 233        /// <summary>
 234        /// Gets the playing item identifier, if any.
 235        /// </summary>
 236        /// <returns>The playing item identifier.</returns>
 237        public Guid GetPlayingItemId()
 238        {
 5239            var playingItem = GetPlayingItem();
 5240            return playingItem?.ItemId ?? Guid.Empty;
 241        }
 242
 243        /// <summary>
 244        /// Sets the playing item using its identifier. If not in the playlist, the playing item is reset.
 245        /// </summary>
 246        /// <param name="itemId">The new playing item identifier.</param>
 247        public void SetPlayingItemById(Guid itemId)
 248        {
 0249            var playlist = GetPlaylistInternal();
 0250            PlayingItemIndex = playlist.FindIndex(item => item.ItemId.Equals(itemId));
 0251            LastChange = DateTime.UtcNow;
 0252        }
 253
 254        /// <summary>
 255        /// Sets the playing item using its playlist identifier. If not in the playlist, the playing item is reset.
 256        /// </summary>
 257        /// <param name="playlistItemId">The new playing item identifier.</param>
 258        /// <returns><c>true</c> if playing item has been set; <c>false</c> if item is not in the playlist.</returns>
 259        public bool SetPlayingItemByPlaylistId(Guid playlistItemId)
 260        {
 1261            var playlist = GetPlaylistInternal();
 1262            PlayingItemIndex = playlist.FindIndex(item => item.PlaylistItemId.Equals(playlistItemId));
 1263            LastChange = DateTime.UtcNow;
 264
 1265            return PlayingItemIndex != NoPlayingItemIndex;
 266        }
 267
 268        /// <summary>
 269        /// Sets the playing item using its position. If not in range, the playing item is reset.
 270        /// </summary>
 271        /// <param name="playlistIndex">The new playing item index.</param>
 272        public void SetPlayingItemByIndex(int playlistIndex)
 273        {
 9274            var playlist = GetPlaylistInternal();
 9275            if (playlistIndex < 0 || playlistIndex >= playlist.Count)
 276            {
 3277                PlayingItemIndex = NoPlayingItemIndex;
 278            }
 279            else
 280            {
 6281                PlayingItemIndex = playlistIndex;
 282            }
 283
 9284            LastChange = DateTime.UtcNow;
 9285        }
 286
 287        /// <summary>
 288        /// Removes items from the playlist. If not removed, the playing item is preserved.
 289        /// </summary>
 290        /// <param name="playlistItemIds">The items to remove.</param>
 291        /// <returns><c>true</c> if playing item got removed; <c>false</c> otherwise.</returns>
 292        public bool RemoveFromPlaylist(IReadOnlyList<Guid> playlistItemIds)
 293        {
 5294            var playingItem = GetPlayingItem();
 295
 296            // Removed items that precede the playing item shift its index as well.
 5297            var removedBeforePlayingItem = 0;
 5298            if (playingItem is not null)
 299            {
 5300                removedBeforePlayingItem = GetPlaylistInternal()
 5301                    .Take(PlayingItemIndex)
 5302                    .Count(item => playlistItemIds.Contains(item.PlaylistItemId));
 303            }
 304
 5305            _sortedPlaylist.RemoveAll(item => playlistItemIds.Contains(item.PlaylistItemId));
 5306            _shuffledPlaylist.RemoveAll(item => playlistItemIds.Contains(item.PlaylistItemId));
 307
 5308            LastChange = DateTime.UtcNow;
 309
 5310            if (playingItem is not null)
 311            {
 5312                if (playlistItemIds.Contains(playingItem.PlaylistItemId))
 313                {
 314                    // Playing item has been removed, picking previous item.
 4315                    PlayingItemIndex -= removedBeforePlayingItem + 1;
 4316                    if (PlayingItemIndex < 0)
 317                    {
 318                        // Was first element, picking next if available.
 319                        // Default to no playing item otherwise.
 1320                        PlayingItemIndex = GetPlaylistInternal().Count > 0 ? 0 : NoPlayingItemIndex;
 321                    }
 322
 4323                    return true;
 324                }
 325
 326                // Restoring playing item.
 1327                SetPlayingItemByPlaylistId(playingItem.PlaylistItemId);
 1328                return false;
 329            }
 330
 0331            return false;
 332        }
 333
 334        /// <summary>
 335        /// Moves an item in the playlist to another position.
 336        /// </summary>
 337        /// <param name="playlistItemId">The item to move.</param>
 338        /// <param name="newIndex">The new position.</param>
 339        /// <returns><c>true</c> if the item has been moved; <c>false</c> otherwise.</returns>
 340        public bool MovePlaylistItem(Guid playlistItemId, int newIndex)
 341        {
 0342            var playlist = GetPlaylistInternal();
 0343            var playingItem = GetPlayingItem();
 344
 0345            var oldIndex = playlist.FindIndex(item => item.PlaylistItemId.Equals(playlistItemId));
 0346            if (oldIndex < 0)
 347            {
 0348                return false;
 349            }
 350
 0351            var queueItem = playlist[oldIndex];
 0352            playlist.RemoveAt(oldIndex);
 0353            newIndex = Math.Clamp(newIndex, 0, playlist.Count);
 0354            playlist.Insert(newIndex, queueItem);
 355
 0356            LastChange = DateTime.UtcNow;
 0357            PlayingItemIndex = playlist.IndexOf(playingItem);
 0358            return true;
 359        }
 360
 361        /// <summary>
 362        /// Resets the playlist to its initial state.
 363        /// </summary>
 364        public void Reset()
 365        {
 20366            _sortedPlaylist.Clear();
 20367            _shuffledPlaylist.Clear();
 20368            PlayingItemIndex = NoPlayingItemIndex;
 20369            ShuffleMode = GroupShuffleMode.Sorted;
 20370            RepeatMode = GroupRepeatMode.RepeatNone;
 20371            LastChange = DateTime.UtcNow;
 20372        }
 373
 374        /// <summary>
 375        /// Sets the repeat mode.
 376        /// </summary>
 377        /// <param name="mode">The new mode.</param>
 378        public void SetRepeatMode(GroupRepeatMode mode)
 379        {
 6380            RepeatMode = mode;
 6381            LastChange = DateTime.UtcNow;
 6382        }
 383
 384        /// <summary>
 385        /// Sets the shuffle mode.
 386        /// </summary>
 387        /// <param name="mode">The new mode.</param>
 388        public void SetShuffleMode(GroupShuffleMode mode)
 389        {
 1390            if (mode.Equals(GroupShuffleMode.Shuffle))
 391            {
 1392                ShufflePlaylist();
 393            }
 394            else
 395            {
 0396                RestoreSortedPlaylist();
 397            }
 0398        }
 399
 400        /// <summary>
 401        /// Toggles the shuffle mode between sorted and shuffled.
 402        /// </summary>
 403        public void ToggleShuffleMode()
 404        {
 0405            if (ShuffleMode.Equals(GroupShuffleMode.Sorted))
 406            {
 0407                ShufflePlaylist();
 408            }
 409            else
 410            {
 0411                RestoreSortedPlaylist();
 412            }
 0413        }
 414
 415        /// <summary>
 416        /// Gets the next item in the playlist considering repeat mode and shuffle mode.
 417        /// </summary>
 418        /// <returns>The next item in the playlist.</returns>
 419        public SyncPlayQueueItem GetNextItemPlaylistId()
 420        {
 421            int newIndex;
 0422            var playlist = GetPlaylistInternal();
 423
 0424            switch (RepeatMode)
 425            {
 426                case GroupRepeatMode.RepeatOne:
 0427                    newIndex = PlayingItemIndex;
 0428                    break;
 429                case GroupRepeatMode.RepeatAll:
 0430                    newIndex = PlayingItemIndex + 1;
 0431                    if (newIndex >= playlist.Count)
 432                    {
 0433                        newIndex = 0;
 434                    }
 435
 0436                    break;
 437                default:
 0438                    newIndex = PlayingItemIndex + 1;
 439                    break;
 440            }
 441
 0442            if (newIndex < 0 || newIndex >= playlist.Count)
 443            {
 0444                return null;
 445            }
 446
 0447            return playlist[newIndex];
 448        }
 449
 450        /// <summary>
 451        /// Sets the next item in the queue as playing item.
 452        /// </summary>
 453        /// <returns><c>true</c> if the playing item changed; <c>false</c> otherwise.</returns>
 454        public bool Next()
 455        {
 3456            if (GetPlaylistInternal().Count == 0)
 457            {
 3458                return false;
 459            }
 460
 0461            if (RepeatMode.Equals(GroupRepeatMode.RepeatOne))
 462            {
 0463                LastChange = DateTime.UtcNow;
 0464                return true;
 465            }
 466
 0467            PlayingItemIndex++;
 0468            if (PlayingItemIndex >= _sortedPlaylist.Count)
 469            {
 0470                if (RepeatMode.Equals(GroupRepeatMode.RepeatAll))
 471                {
 0472                    PlayingItemIndex = 0;
 473                }
 474                else
 475                {
 0476                    PlayingItemIndex = _sortedPlaylist.Count - 1;
 0477                    return false;
 478                }
 479            }
 480
 0481            LastChange = DateTime.UtcNow;
 0482            return true;
 483        }
 484
 485        /// <summary>
 486        /// Sets the previous item in the queue as playing item.
 487        /// </summary>
 488        /// <returns><c>true</c> if the playing item changed; <c>false</c> otherwise.</returns>
 489        public bool Previous()
 490        {
 3491            if (GetPlaylistInternal().Count == 0)
 492            {
 3493                return false;
 494            }
 495
 0496            if (RepeatMode.Equals(GroupRepeatMode.RepeatOne))
 497            {
 0498                LastChange = DateTime.UtcNow;
 0499                return true;
 500            }
 501
 0502            PlayingItemIndex--;
 0503            if (PlayingItemIndex < 0)
 504            {
 0505                if (RepeatMode.Equals(GroupRepeatMode.RepeatAll))
 506                {
 0507                    PlayingItemIndex = _sortedPlaylist.Count - 1;
 508                }
 509                else
 510                {
 0511                    PlayingItemIndex = 0;
 0512                    return false;
 513                }
 514            }
 515
 0516            LastChange = DateTime.UtcNow;
 0517            return true;
 518        }
 519
 520        /// <summary>
 521        /// Creates a list from the array of items. Each item is given an unique playlist identifier.
 522        /// </summary>
 523        /// <returns>The list of queue items.</returns>
 524        private List<SyncPlayQueueItem> CreateQueueItemsFromArray(IReadOnlyList<Guid> items)
 525        {
 11526            var list = new List<SyncPlayQueueItem>();
 78527            foreach (var item in items)
 528            {
 28529                var queueItem = new SyncPlayQueueItem(item);
 28530                list.Add(queueItem);
 531            }
 532
 11533            return list;
 534        }
 535
 536        /// <summary>
 537        /// Gets the current playlist considering the shuffle mode.
 538        /// </summary>
 539        /// <returns>The playlist.</returns>
 540        private List<SyncPlayQueueItem> GetPlaylistInternal()
 541        {
 59542            if (ShuffleMode.Equals(GroupShuffleMode.Shuffle))
 543            {
 4544                return _shuffledPlaylist;
 545            }
 546
 55547            return _sortedPlaylist;
 548        }
 549
 550        /// <summary>
 551        /// Gets the current playing item, depending on the shuffle mode.
 552        /// </summary>
 553        /// <returns>The playing item.</returns>
 554        private SyncPlayQueueItem GetPlayingItem()
 555        {
 21556            if (PlayingItemIndex == NoPlayingItemIndex)
 557            {
 11558                return null;
 559            }
 560
 10561            if (ShuffleMode.Equals(GroupShuffleMode.Shuffle))
 562            {
 2563                return _shuffledPlaylist[PlayingItemIndex];
 564            }
 565
 8566            return _sortedPlaylist[PlayingItemIndex];
 567        }
 568    }
 569}