< 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
0%
Covered lines: 0
Uncovered lines: 174
Coverable lines: 174
Total lines: 550
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 70
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100

Metrics

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>
 026        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>
 032        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        {
 039            Reset();
 040        }
 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        {
 072            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        {
 081            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        {
 090            _sortedPlaylist.Clear();
 091            _shuffledPlaylist.Clear();
 92
 093            _sortedPlaylist = CreateQueueItemsFromArray(items);
 094            if (ShuffleMode.Equals(GroupShuffleMode.Shuffle))
 95            {
 096                _shuffledPlaylist = new List<SyncPlayQueueItem>(_sortedPlaylist);
 097                _shuffledPlaylist.Shuffle();
 98            }
 99
 0100            PlayingItemIndex = NoPlayingItemIndex;
 0101            LastChange = DateTime.UtcNow;
 0102        }
 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        {
 0126            if (PlayingItemIndex == NoPlayingItemIndex)
 127            {
 0128                _shuffledPlaylist = new List<SyncPlayQueueItem>(_sortedPlaylist);
 0129                _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
 0151            ShuffleMode = GroupShuffleMode.Shuffle;
 0152            LastChange = DateTime.UtcNow;
 0153        }
 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        {
 0229            var playingItem = GetPlayingItem();
 0230            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        {
 0239            var playingItem = GetPlayingItem();
 0240            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        {
 0261            var playlist = GetPlaylistInternal();
 0262            PlayingItemIndex = playlist.FindIndex(item => item.PlaylistItemId.Equals(playlistItemId));
 0263            LastChange = DateTime.UtcNow;
 264
 0265            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        {
 0274            var playlist = GetPlaylistInternal();
 0275            if (playlistIndex < 0 || playlistIndex > playlist.Count)
 276            {
 0277                PlayingItemIndex = NoPlayingItemIndex;
 278            }
 279            else
 280            {
 0281                PlayingItemIndex = playlistIndex;
 282            }
 283
 0284            LastChange = DateTime.UtcNow;
 0285        }
 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        {
 0294            var playingItem = GetPlayingItem();
 295
 0296            _sortedPlaylist.RemoveAll(item => playlistItemIds.Contains(item.PlaylistItemId));
 0297            _shuffledPlaylist.RemoveAll(item => playlistItemIds.Contains(item.PlaylistItemId));
 298
 0299            LastChange = DateTime.UtcNow;
 300
 0301            if (playingItem is not null)
 302            {
 0303                if (playlistItemIds.Contains(playingItem.PlaylistItemId))
 304                {
 305                    // Playing item has been removed, picking previous item.
 0306                    PlayingItemIndex--;
 0307                    if (PlayingItemIndex < 0)
 308                    {
 309                        // Was first element, picking next if available.
 310                        // Default to no playing item otherwise.
 0311                        PlayingItemIndex = _sortedPlaylist.Count > 0 ? 0 : NoPlayingItemIndex;
 312                    }
 313
 0314                    return true;
 315                }
 316
 317                // Restoring playing item.
 0318                SetPlayingItemByPlaylistId(playingItem.PlaylistItemId);
 0319                return false;
 320            }
 321
 0322            return false;
 323        }
 324
 325        /// <summary>
 326        /// Moves an item in the playlist to another position.
 327        /// </summary>
 328        /// <param name="playlistItemId">The item to move.</param>
 329        /// <param name="newIndex">The new position.</param>
 330        /// <returns><c>true</c> if the item has been moved; <c>false</c> otherwise.</returns>
 331        public bool MovePlaylistItem(Guid playlistItemId, int newIndex)
 332        {
 0333            var playlist = GetPlaylistInternal();
 0334            var playingItem = GetPlayingItem();
 335
 0336            var oldIndex = playlist.FindIndex(item => item.PlaylistItemId.Equals(playlistItemId));
 0337            if (oldIndex < 0)
 338            {
 0339                return false;
 340            }
 341
 0342            var queueItem = playlist[oldIndex];
 0343            playlist.RemoveAt(oldIndex);
 0344            newIndex = Math.Clamp(newIndex, 0, playlist.Count);
 0345            playlist.Insert(newIndex, queueItem);
 346
 0347            LastChange = DateTime.UtcNow;
 0348            PlayingItemIndex = playlist.IndexOf(playingItem);
 0349            return true;
 350        }
 351
 352        /// <summary>
 353        /// Resets the playlist to its initial state.
 354        /// </summary>
 355        public void Reset()
 356        {
 0357            _sortedPlaylist.Clear();
 0358            _shuffledPlaylist.Clear();
 0359            PlayingItemIndex = NoPlayingItemIndex;
 0360            ShuffleMode = GroupShuffleMode.Sorted;
 0361            RepeatMode = GroupRepeatMode.RepeatNone;
 0362            LastChange = DateTime.UtcNow;
 0363        }
 364
 365        /// <summary>
 366        /// Sets the repeat mode.
 367        /// </summary>
 368        /// <param name="mode">The new mode.</param>
 369        public void SetRepeatMode(GroupRepeatMode mode)
 370        {
 0371            RepeatMode = mode;
 0372            LastChange = DateTime.UtcNow;
 0373        }
 374
 375        /// <summary>
 376        /// Sets the shuffle mode.
 377        /// </summary>
 378        /// <param name="mode">The new mode.</param>
 379        public void SetShuffleMode(GroupShuffleMode mode)
 380        {
 0381            if (mode.Equals(GroupShuffleMode.Shuffle))
 382            {
 0383                ShufflePlaylist();
 384            }
 385            else
 386            {
 0387                RestoreSortedPlaylist();
 388            }
 0389        }
 390
 391        /// <summary>
 392        /// Toggles the shuffle mode between sorted and shuffled.
 393        /// </summary>
 394        public void ToggleShuffleMode()
 395        {
 0396            if (ShuffleMode.Equals(GroupShuffleMode.Sorted))
 397            {
 0398                ShufflePlaylist();
 399            }
 400            else
 401            {
 0402                RestoreSortedPlaylist();
 403            }
 0404        }
 405
 406        /// <summary>
 407        /// Gets the next item in the playlist considering repeat mode and shuffle mode.
 408        /// </summary>
 409        /// <returns>The next item in the playlist.</returns>
 410        public SyncPlayQueueItem GetNextItemPlaylistId()
 411        {
 412            int newIndex;
 0413            var playlist = GetPlaylistInternal();
 414
 0415            switch (RepeatMode)
 416            {
 417                case GroupRepeatMode.RepeatOne:
 0418                    newIndex = PlayingItemIndex;
 0419                    break;
 420                case GroupRepeatMode.RepeatAll:
 0421                    newIndex = PlayingItemIndex + 1;
 0422                    if (newIndex >= playlist.Count)
 423                    {
 0424                        newIndex = 0;
 425                    }
 426
 0427                    break;
 428                default:
 0429                    newIndex = PlayingItemIndex + 1;
 430                    break;
 431            }
 432
 0433            if (newIndex < 0 || newIndex >= playlist.Count)
 434            {
 0435                return null;
 436            }
 437
 0438            return playlist[newIndex];
 439        }
 440
 441        /// <summary>
 442        /// Sets the next item in the queue as playing item.
 443        /// </summary>
 444        /// <returns><c>true</c> if the playing item changed; <c>false</c> otherwise.</returns>
 445        public bool Next()
 446        {
 0447            if (RepeatMode.Equals(GroupRepeatMode.RepeatOne))
 448            {
 0449                LastChange = DateTime.UtcNow;
 0450                return true;
 451            }
 452
 0453            PlayingItemIndex++;
 0454            if (PlayingItemIndex >= _sortedPlaylist.Count)
 455            {
 0456                if (RepeatMode.Equals(GroupRepeatMode.RepeatAll))
 457                {
 0458                    PlayingItemIndex = 0;
 459                }
 460                else
 461                {
 0462                    PlayingItemIndex = _sortedPlaylist.Count - 1;
 0463                    return false;
 464                }
 465            }
 466
 0467            LastChange = DateTime.UtcNow;
 0468            return true;
 469        }
 470
 471        /// <summary>
 472        /// Sets the previous item in the queue as playing item.
 473        /// </summary>
 474        /// <returns><c>true</c> if the playing item changed; <c>false</c> otherwise.</returns>
 475        public bool Previous()
 476        {
 0477            if (RepeatMode.Equals(GroupRepeatMode.RepeatOne))
 478            {
 0479                LastChange = DateTime.UtcNow;
 0480                return true;
 481            }
 482
 0483            PlayingItemIndex--;
 0484            if (PlayingItemIndex < 0)
 485            {
 0486                if (RepeatMode.Equals(GroupRepeatMode.RepeatAll))
 487                {
 0488                    PlayingItemIndex = _sortedPlaylist.Count - 1;
 489                }
 490                else
 491                {
 0492                    PlayingItemIndex = 0;
 0493                    return false;
 494                }
 495            }
 496
 0497            LastChange = DateTime.UtcNow;
 0498            return true;
 499        }
 500
 501        /// <summary>
 502        /// Creates a list from the array of items. Each item is given an unique playlist identifier.
 503        /// </summary>
 504        /// <returns>The list of queue items.</returns>
 505        private List<SyncPlayQueueItem> CreateQueueItemsFromArray(IReadOnlyList<Guid> items)
 506        {
 0507            var list = new List<SyncPlayQueueItem>();
 0508            foreach (var item in items)
 509            {
 0510                var queueItem = new SyncPlayQueueItem(item);
 0511                list.Add(queueItem);
 512            }
 513
 0514            return list;
 515        }
 516
 517        /// <summary>
 518        /// Gets the current playlist considering the shuffle mode.
 519        /// </summary>
 520        /// <returns>The playlist.</returns>
 521        private List<SyncPlayQueueItem> GetPlaylistInternal()
 522        {
 0523            if (ShuffleMode.Equals(GroupShuffleMode.Shuffle))
 524            {
 0525                return _shuffledPlaylist;
 526            }
 527
 0528            return _sortedPlaylist;
 529        }
 530
 531        /// <summary>
 532        /// Gets the current playing item, depending on the shuffle mode.
 533        /// </summary>
 534        /// <returns>The playing item.</returns>
 535        private SyncPlayQueueItem GetPlayingItem()
 536        {
 0537            if (PlayingItemIndex == NoPlayingItemIndex)
 538            {
 0539                return null;
 540            }
 541
 0542            if (ShuffleMode.Equals(GroupShuffleMode.Shuffle))
 543            {
 0544                return _shuffledPlaylist[PlayingItemIndex];
 545            }
 546
 0547            return _sortedPlaylist[PlayingItemIndex];
 548        }
 549    }
 550}