using MediaBrowser.Common.Events; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Kernel; using MediaBrowser.Model.DTO; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Net; using MediaBrowser.UI.Configuration; using MediaBrowser.UI.Controller; using MediaBrowser.UI.Playback.InternalPlayer; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; namespace MediaBrowser.UI.Playback { /// /// Class PlaybackManager /// public class PlaybackManager : BaseManager { /// /// Initializes a new instance of the class. /// /// The kernel. public PlaybackManager(UIKernel kernel) : base(kernel) { } #region PlaybackStarted Event /// /// Occurs when [playback started]. /// public event EventHandler PlaybackStarted; /// /// Called when [playback started]. /// /// The player. /// The options. /// The player configuration. private void OnPlaybackStarted(BaseMediaPlayer player, PlayOptions options, PlayerConfiguration playerConfiguration) { EventHelper.QueueEventIfNotNull(PlaybackStarted, this, new PlaybackEventArgs { Options = options, Player = player, PlayerConfiguration = playerConfiguration }); } #endregion #region PlaybackCompleted Event /// /// Occurs when [playback completed]. /// public event EventHandler PlaybackCompleted; /// /// Called when [playback completed]. /// /// The player. /// The items. internal void OnPlaybackCompleted(BaseMediaPlayer player, List items) { EventHelper.QueueEventIfNotNull(PlaybackCompleted, this, new PlaybackStopEventArgs { Items = items, Player = player }); } #endregion /// /// Gets the active players. /// /// The active players. public IEnumerable ActivePlayers { get { return Kernel.MediaPlayers.Where(m => m.PlayState != PlayState.Idle); } } /// /// Gets the active internal players. /// /// The active internal players. public IEnumerable ActiveInternalPlayers { get { return ActivePlayers.Where(p => p is BaseInternalMediaPlayer); } } /// /// Plays the specified items. /// /// The options. /// Task. /// /// public async Task Play(PlayOptions options) { if (options == null) { throw new ArgumentNullException("options"); } if (options.Items == null || options.Items.Count == 0) { throw new ArgumentNullException("options"); } var player = GetPlayer(options.Items); if (player != null) { await StopAllPlayback(); await Play(player.Item1, options, player.Item2); } else { throw new InvalidOperationException(); } } /// /// Plays the specified player. /// /// The player. /// The options. /// The player configuration. /// Task. private async Task Play(BaseMediaPlayer player, PlayOptions options, PlayerConfiguration playerConfiguration) { if (options.Shuffle) { options.Items = options.Items.Shuffle().ToList(); } var firstItem = options.Items[0]; if (options.StartPositionTicks == 0 && player.SupportsMultiFilePlayback && firstItem.IsVideo && firstItem.LocationType == LocationType.FileSystem) { try { var intros = await UIKernel.Instance.ApiClient.GetIntrosAsync(firstItem.Id, App.Instance.CurrentUser.Id); options.Items.InsertRange(0, intros.Select(GetPlayableItem)); } catch (HttpException ex) { Logger.ErrorException("Error retrieving intros", ex); } } await player.Play(options, playerConfiguration); OnPlaybackStarted(player, options, playerConfiguration); } /// /// Gets the playable item. /// /// The path. /// DtoBaseItem. public DtoBaseItem GetPlayableItem(string path) { return new DtoBaseItem { Path = path, Name = Path.GetFileName(path), Type = "Video", VideoType = VideoType.VideoFile, IsFolder = false }; } /// /// Gets the playable item. /// /// The URI. /// The name. /// DtoBaseItem. public DtoBaseItem GetPlayableItem(Uri uri, string name) { return new DtoBaseItem { Path = uri.ToString(), Name = name, Type = "Video", VideoType = VideoType.VideoFile, IsFolder = false, LocationType = LocationType.Remote }; } /// /// Stops all playback. /// /// Task. public async Task StopAllPlayback() { var tasks = Kernel.MediaPlayers.Where(p => p.PlayState == PlayState.Playing || p.PlayState == PlayState.Paused).Select(p => p.Stop()); await Task.WhenAll(tasks); } /// /// Gets the player. /// /// The items. /// BaseMediaPlayer. private Tuple GetPlayer(List items) { var player = GetConfiguredPlayer(items); if (player != null) { return player; } // If there's no explicit configuration just find the first matching player var mediaPlayer = Kernel.MediaPlayers.OfType().FirstOrDefault(p => items.All(p.CanPlay)); if (mediaPlayer != null) { return new Tuple(mediaPlayer, null); } return null; } /// /// Gets the configured player. /// /// The items. /// BaseMediaPlayer. private Tuple GetConfiguredPlayer(List items) { if (UIKernel.Instance.Configuration.MediaPlayers == null) { return null; } return UIKernel.Instance.Configuration.MediaPlayers.Where(p => IsConfiguredToPlay(p, items)) .Select(p => new Tuple(UIKernel.Instance.MediaPlayers.FirstOrDefault(m => m.Name.Equals(p.PlayerName, StringComparison.OrdinalIgnoreCase)), p)) .FirstOrDefault(p => p.Item1 != null); } /// /// Determines whether [is configured to play] [the specified configuration]. /// /// The configuration. /// The items. /// true if [is configured to play] [the specified configuration]; otherwise, false. private bool IsConfiguredToPlay(PlayerConfiguration configuration, List items) { if (configuration.ItemTypes != null && configuration.ItemTypes.Length > 0) { if (items.Any(i => !configuration.ItemTypes.Contains(i.Type, StringComparer.OrdinalIgnoreCase))) { return false; } } if (configuration.FileExtensions != null && configuration.FileExtensions.Length > 0) { if (items.Any(i => !configuration.FileExtensions.Select(ext => ext.TrimStart('.')).Contains((Path.GetExtension(i.Path) ?? string.Empty).TrimStart('.'), StringComparer.OrdinalIgnoreCase))) { return false; } } if (configuration.VideoTypes != null && configuration.VideoTypes.Length > 0) { if (items.Any(i => i.VideoType.HasValue && !configuration.VideoTypes.Contains(i.VideoType.Value))) { return false; } } if (configuration.VideoFormats != null && configuration.VideoFormats.Length > 0) { if (items.Any(i => i.VideoFormat.HasValue && !configuration.VideoFormats.Contains(i.VideoFormat.Value))) { return false; } } return true; } } }