diff options
| author | dkanada <dkanada@users.noreply.github.com> | 2020-06-04 03:20:48 +0900 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-06-04 03:20:48 +0900 |
| commit | fe16c3fad4b3ea2cab0ec4a4008e31be626b5eee (patch) | |
| tree | 3cd2fd175c174030e0f72335fea3c1582ac44f56 /MediaBrowser.Controller | |
| parent | 7176a9a30a7c9c925e04529f3ba7feac022f9248 (diff) | |
| parent | f0d182b8fe2a2ff2c893c3973b99cda85c30ca70 (diff) | |
Merge branch 'master' into install-plugin
Diffstat (limited to 'MediaBrowser.Controller')
18 files changed, 485 insertions, 52 deletions
diff --git a/MediaBrowser.Controller/Devices/CameraImageUploadInfo.cs b/MediaBrowser.Controller/Devices/CameraImageUploadInfo.cs deleted file mode 100644 index 89d0be58f..000000000 --- a/MediaBrowser.Controller/Devices/CameraImageUploadInfo.cs +++ /dev/null @@ -1,10 +0,0 @@ -using MediaBrowser.Model.Devices; - -namespace MediaBrowser.Controller.Devices -{ - public class CameraImageUploadInfo - { - public LocalFileInfo FileInfo { get; set; } - public DeviceInfo Device { get; set; } - } -} diff --git a/MediaBrowser.Controller/Drawing/IImageEncoder.cs b/MediaBrowser.Controller/Drawing/IImageEncoder.cs index 88e67b648..e09ccd204 100644 --- a/MediaBrowser.Controller/Drawing/IImageEncoder.cs +++ b/MediaBrowser.Controller/Drawing/IImageEncoder.cs @@ -44,6 +44,15 @@ namespace MediaBrowser.Controller.Drawing ImageDimensions GetImageSize(string path); /// <summary> + /// Gets the blurhash of an image. + /// </summary> + /// <param name="xComp">Amount of X components of DCT to take.</param> + /// <param name="yComp">Amount of Y components of DCT to take.</param> + /// <param name="path">The filepath of the image.</param> + /// <returns>The blurhash.</returns> + string GetImageBlurHash(int xComp, int yComp, string path); + + /// <summary> /// Encode an image. /// </summary> string EncodeImage(string inputPath, DateTime dateModified, string outputPath, bool autoOrient, ImageOrientation? orientation, int quality, ImageProcessingOptions options, ImageFormat outputFormat); diff --git a/MediaBrowser.Controller/Drawing/IImageProcessor.cs b/MediaBrowser.Controller/Drawing/IImageProcessor.cs index 36c746624..8800fdf99 100644 --- a/MediaBrowser.Controller/Drawing/IImageProcessor.cs +++ b/MediaBrowser.Controller/Drawing/IImageProcessor.cs @@ -41,12 +41,20 @@ namespace MediaBrowser.Controller.Drawing ImageDimensions GetImageDimensions(BaseItem item, ItemImageInfo info); /// <summary> + /// Gets the blurhash of the image. + /// </summary> + /// <param name="path">Path to the image file.</param> + /// <returns>BlurHash</returns> + string GetImageBlurHash(string path); + + /// <summary> /// Gets the image cache tag. /// </summary> /// <param name="item">The item.</param> /// <param name="image">The image.</param> /// <returns>Guid.</returns> string GetImageCacheTag(BaseItem item, ItemImageInfo image); + string GetImageCacheTag(BaseItem item, ChapterInfo info); /// <summary> diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 7ed8fa767..f4b71d8bf 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -1374,6 +1374,7 @@ namespace MediaBrowser.Controller.Entities new List<FileSystemMetadata>(); var ownedItemsChanged = await RefreshedOwnedItems(options, files, cancellationToken).ConfigureAwait(false); + LibraryManager.UpdateImages(this); // ensure all image properties in DB are fresh if (ownedItemsChanged) { @@ -2222,6 +2223,7 @@ namespace MediaBrowser.Controller.Entities existingImage.DateModified = image.DateModified; existingImage.Width = image.Width; existingImage.Height = image.Height; + existingImage.BlurHash = image.BlurHash; } else { @@ -2373,6 +2375,46 @@ namespace MediaBrowser.Controller.Entities .ElementAtOrDefault(imageIndex); } + /// <summary> + /// Computes image index for given image or raises if no matching image found. + /// </summary> + /// <param name="image">Image to compute index for.</param> + /// <exception cref="ArgumentException">Image index cannot be computed as no matching image found. + /// </exception> + /// <returns>Image index.</returns> + public int GetImageIndex(ItemImageInfo image) + { + if (image == null) + { + throw new ArgumentNullException(nameof(image)); + } + + if (image.Type == ImageType.Chapter) + { + var chapters = ItemRepository.GetChapters(this); + for (var i = 0; i < chapters.Count; i++) + { + if (chapters[i].ImagePath == image.Path) + { + return i; + } + } + + throw new ArgumentException("No chapter index found for image path", image.Path); + } + + var images = GetImages(image.Type).ToArray(); + for (var i = 0; i < images.Length; i++) + { + if (images[i].Path == image.Path) + { + return i; + } + } + + throw new ArgumentException("No image index found for image path", image.Path); + } + public IEnumerable<ItemImageInfo> GetImages(ImageType imageType) { if (imageType == ImageType.Chapter) diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index a468e0c35..29040f92e 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -341,6 +341,11 @@ namespace MediaBrowser.Controller.Entities { currentChild.UpdateToRepository(ItemUpdateType.MetadataImport, cancellationToken); } + else + { + // metadata is up-to-date; make sure DB has correct images dimensions and hash + LibraryManager.UpdateImages(currentChild); + } continue; } diff --git a/MediaBrowser.Controller/Entities/ItemImageInfo.cs b/MediaBrowser.Controller/Entities/ItemImageInfo.cs index fc46dec2e..12f5db2e0 100644 --- a/MediaBrowser.Controller/Entities/ItemImageInfo.cs +++ b/MediaBrowser.Controller/Entities/ItemImageInfo.cs @@ -28,6 +28,12 @@ namespace MediaBrowser.Controller.Entities public int Height { get; set; } + /// <summary> + /// Gets or sets the blurhash. + /// </summary> + /// <value>The blurhash.</value> + public string BlurHash { get; set; } + [JsonIgnore] public bool IsLocalFile => Path == null || !Path.StartsWith("http", StringComparison.OrdinalIgnoreCase); } diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs index 2e1c97f67..916e4fda7 100644 --- a/MediaBrowser.Controller/Library/ILibraryManager.cs +++ b/MediaBrowser.Controller/Library/ILibraryManager.cs @@ -118,7 +118,7 @@ namespace MediaBrowser.Controller.Library /// </summary> void QueueLibraryScan(); - void UpdateImages(BaseItem item); + void UpdateImages(BaseItem item, bool forceUpdate = false); /// <summary> /// Gets the default view. @@ -195,6 +195,7 @@ namespace MediaBrowser.Controller.Library /// Updates the item. /// </summary> void UpdateItems(IEnumerable<BaseItem> items, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken); + void UpdateItem(BaseItem item, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken); /// <summary> diff --git a/MediaBrowser.Controller/Library/IUserManager.cs b/MediaBrowser.Controller/Library/IUserManager.cs index ec6cb35eb..be7b4ce59 100644 --- a/MediaBrowser.Controller/Library/IUserManager.cs +++ b/MediaBrowser.Controller/Library/IUserManager.cs @@ -144,14 +144,6 @@ namespace MediaBrowser.Controller.Library UserDto GetUserDto(User user, string remoteEndPoint = null); /// <summary> - /// Gets the user public dto. - /// </summary> - /// <param name="user">Ther user.</param>\ - /// <param name="remoteEndPoint">The remote end point.</param> - /// <returns>A public UserDto, aka a UserDto stripped of personal data.</returns> - PublicUserDto GetPublicUserDto(User user, string remoteEndPoint = null); - - /// <summary> /// Authenticates the user. /// </summary> Task<User> AuthenticateUser(string username, string password, string passwordSha1, string remoteEndPoint, bool isUserSession); diff --git a/MediaBrowser.Controller/LiveTv/TimerEventInfo.cs b/MediaBrowser.Controller/LiveTv/TimerEventInfo.cs index cfec39b4e..1b8f41db6 100644 --- a/MediaBrowser.Controller/LiveTv/TimerEventInfo.cs +++ b/MediaBrowser.Controller/LiveTv/TimerEventInfo.cs @@ -1,10 +1,19 @@ +#nullable enable +#pragma warning disable CS1591 + using System; namespace MediaBrowser.Controller.LiveTv { public class TimerEventInfo { - public string Id { get; set; } - public Guid ProgramId { get; set; } + public TimerEventInfo(string id) + { + Id = id; + } + + public string Id { get; } + + public Guid? ProgramId { get; set; } } } diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 61a330675..2d2f73148 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -1338,7 +1338,7 @@ namespace MediaBrowser.Controller.MediaEncoding transcoderChannelLimit = 6; } - var isTranscodingAudio = !string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase); + var isTranscodingAudio = !EncodingHelper.IsCopyCodec(codec); int? resultChannels = state.GetRequestedAudioChannels(codec); if (isTranscodingAudio) @@ -1734,7 +1734,8 @@ namespace MediaBrowser.Controller.MediaEncoding var hasTextSubs = state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode; - if (string.Equals(videoEncoder, "h264_vaapi", StringComparison.OrdinalIgnoreCase) || (string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase) && !hasTextSubs) + if ((string.Equals(videoEncoder, "h264_vaapi", StringComparison.OrdinalIgnoreCase) + || (string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase) && !hasTextSubs)) && width.HasValue && height.HasValue) { @@ -1991,7 +1992,7 @@ namespace MediaBrowser.Controller.MediaEncoding filters.Add("hwupload"); } - // When the input may or may not be hardware QSV decodable + // When the input may or may not be hardware QSV decodable else if (string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase)) { if (!hasTextSubs) @@ -2248,7 +2249,7 @@ namespace MediaBrowser.Controller.MediaEncoding flags.Add("+ignidx"); } - if (state.GenPtsInput || string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase)) + if (state.GenPtsInput || EncodingHelper.IsCopyCodec(state.OutputVideoCodec)) { flags.Add("+genpts"); } @@ -2511,7 +2512,7 @@ namespace MediaBrowser.Controller.MediaEncoding /// </summary> protected string GetHardwareAcceleratedVideoDecoder(EncodingJobInfo state, EncodingOptions encodingOptions) { - if (string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase)) + if (EncodingHelper.IsCopyCodec(state.OutputVideoCodec)) { return null; } @@ -2799,7 +2800,7 @@ namespace MediaBrowser.Controller.MediaEncoding args += " -mpegts_m2ts_mode 1"; } - if (string.Equals(videoCodec, "copy", StringComparison.OrdinalIgnoreCase)) + if (EncodingHelper.IsCopyCodec(videoCodec)) { if (state.VideoStream != null && string.Equals(state.OutputContainer, "ts", StringComparison.OrdinalIgnoreCase) @@ -2901,7 +2902,7 @@ namespace MediaBrowser.Controller.MediaEncoding var args = "-codec:a:0 " + codec; - if (string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase)) + if (EncodingHelper.IsCopyCodec(codec)) { return args; } @@ -2973,5 +2974,10 @@ namespace MediaBrowser.Controller.MediaEncoding string.Empty, string.Empty).Trim(); } + + public static bool IsCopyCodec(string codec) + { + return string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase); + } } } diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs index 1127a08de..7cd783595 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs @@ -302,7 +302,7 @@ namespace MediaBrowser.Controller.MediaEncoding return false; } - return BaseRequest.BreakOnNonKeyFrames && string.Equals(videoCodec, "copy", StringComparison.OrdinalIgnoreCase); + return BaseRequest.BreakOnNonKeyFrames && EncodingHelper.IsCopyCodec(videoCodec); } return false; @@ -367,7 +367,7 @@ namespace MediaBrowser.Controller.MediaEncoding get { if (BaseRequest.Static - || string.Equals(OutputAudioCodec, "copy", StringComparison.OrdinalIgnoreCase)) + || EncodingHelper.IsCopyCodec(OutputAudioCodec)) { if (AudioStream != null) { @@ -390,7 +390,7 @@ namespace MediaBrowser.Controller.MediaEncoding get { if (BaseRequest.Static - || string.Equals(OutputAudioCodec, "copy", StringComparison.OrdinalIgnoreCase)) + || EncodingHelper.IsCopyCodec(OutputAudioCodec)) { if (AudioStream != null) { @@ -409,7 +409,7 @@ namespace MediaBrowser.Controller.MediaEncoding { get { - if (BaseRequest.Static || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase)) + if (BaseRequest.Static || EncodingHelper.IsCopyCodec(OutputVideoCodec)) { return VideoStream?.Level; } @@ -433,7 +433,7 @@ namespace MediaBrowser.Controller.MediaEncoding get { if (BaseRequest.Static - || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase)) + || EncodingHelper.IsCopyCodec(OutputVideoCodec)) { return VideoStream?.BitDepth; } @@ -451,7 +451,7 @@ namespace MediaBrowser.Controller.MediaEncoding get { if (BaseRequest.Static - || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase)) + || EncodingHelper.IsCopyCodec(OutputVideoCodec)) { return VideoStream?.RefFrames; } @@ -468,7 +468,7 @@ namespace MediaBrowser.Controller.MediaEncoding get { if (BaseRequest.Static - || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase)) + || EncodingHelper.IsCopyCodec(OutputVideoCodec)) { return VideoStream == null ? null : (VideoStream.AverageFrameRate ?? VideoStream.RealFrameRate); } @@ -499,7 +499,7 @@ namespace MediaBrowser.Controller.MediaEncoding { get { - if (BaseRequest.Static || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase)) + if (BaseRequest.Static || EncodingHelper.IsCopyCodec(OutputVideoCodec)) { return VideoStream?.PacketLength; } @@ -515,7 +515,7 @@ namespace MediaBrowser.Controller.MediaEncoding { get { - if (BaseRequest.Static || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase)) + if (BaseRequest.Static || EncodingHelper.IsCopyCodec(OutputVideoCodec)) { return VideoStream?.Profile; } @@ -535,7 +535,7 @@ namespace MediaBrowser.Controller.MediaEncoding get { if (BaseRequest.Static - || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase)) + || EncodingHelper.IsCopyCodec(OutputVideoCodec)) { return VideoStream?.CodecTag; } @@ -549,7 +549,7 @@ namespace MediaBrowser.Controller.MediaEncoding get { if (BaseRequest.Static - || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase)) + || EncodingHelper.IsCopyCodec(OutputVideoCodec)) { return VideoStream?.IsAnamorphic; } @@ -562,7 +562,7 @@ namespace MediaBrowser.Controller.MediaEncoding { get { - if (string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase)) + if (EncodingHelper.IsCopyCodec(OutputVideoCodec)) { return VideoStream?.Codec; } @@ -575,7 +575,7 @@ namespace MediaBrowser.Controller.MediaEncoding { get { - if (string.Equals(OutputAudioCodec, "copy", StringComparison.OrdinalIgnoreCase)) + if (EncodingHelper.IsCopyCodec(OutputAudioCodec)) { return AudioStream?.Codec; } @@ -589,7 +589,7 @@ namespace MediaBrowser.Controller.MediaEncoding get { if (BaseRequest.Static - || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase)) + || EncodingHelper.IsCopyCodec(OutputVideoCodec)) { return VideoStream?.IsInterlaced; } @@ -607,7 +607,7 @@ namespace MediaBrowser.Controller.MediaEncoding { get { - if (BaseRequest.Static || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase)) + if (BaseRequest.Static || EncodingHelper.IsCopyCodec(OutputVideoCodec)) { return VideoStream?.IsAVC; } diff --git a/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs b/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs index 1162bff13..5be656bdb 100644 --- a/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs +++ b/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs @@ -104,7 +104,7 @@ namespace MediaBrowser.Controller.Net } } - protected void SendData(bool force) + protected async Task SendData(bool force) { Tuple<IWebSocketConnection, CancellationTokenSource, TStateType>[] tuples; @@ -128,13 +128,18 @@ namespace MediaBrowser.Controller.Net .ToArray(); } - foreach (var tuple in tuples) + IEnumerable<Task> GetTasks() { - SendData(tuple); + foreach (var tuple in tuples) + { + yield return SendData(tuple); + } } + + await Task.WhenAll(GetTasks()).ConfigureAwait(false); } - private async void SendData(Tuple<IWebSocketConnection, CancellationTokenSource, TStateType> tuple) + private async Task SendData(Tuple<IWebSocketConnection, CancellationTokenSource, TStateType> tuple) { var connection = tuple.Item1; @@ -148,11 +153,13 @@ namespace MediaBrowser.Controller.Net if (data != null) { - await connection.SendAsync(new WebSocketMessage<TReturnDataType> - { - MessageType = Name, - Data = data - }, cancellationToken).ConfigureAwait(false); + await connection.SendAsync( + new WebSocketMessage<TReturnDataType> + { + MessageType = Name, + Data = data + }, + cancellationToken).ConfigureAwait(false); state.DateLastSendUtc = DateTime.UtcNow; } diff --git a/MediaBrowser.Controller/Net/IWebSocketConnection.cs b/MediaBrowser.Controller/Net/IWebSocketConnection.cs index 09e43c683..3ef8e5f6d 100644 --- a/MediaBrowser.Controller/Net/IWebSocketConnection.cs +++ b/MediaBrowser.Controller/Net/IWebSocketConnection.cs @@ -24,6 +24,12 @@ namespace MediaBrowser.Controller.Net DateTime LastActivityDate { get; } /// <summary> + /// Gets or sets the date of last Keeplive received. + /// </summary> + /// <value>The date of last Keeplive received.</value> + DateTime LastKeepAliveDate { get; set; } + + /// <summary> /// Gets or sets the query string. /// </summary> /// <value>The query string.</value> diff --git a/MediaBrowser.Controller/Session/ISessionManager.cs b/MediaBrowser.Controller/Session/ISessionManager.cs index 771027103..4c2f834cb 100644 --- a/MediaBrowser.Controller/Session/ISessionManager.cs +++ b/MediaBrowser.Controller/Session/ISessionManager.cs @@ -9,6 +9,7 @@ using MediaBrowser.Controller.Security; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Events; using MediaBrowser.Model.Session; +using MediaBrowser.Model.SyncPlay; namespace MediaBrowser.Controller.Session { @@ -141,6 +142,24 @@ namespace MediaBrowser.Controller.Session Task SendPlayCommand(string controllingSessionId, string sessionId, PlayRequest command, CancellationToken cancellationToken); /// <summary> + /// Sends the SyncPlayCommand. + /// </summary> + /// <param name="sessionId">The session id.</param> + /// <param name="command">The command.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task.</returns> + Task SendSyncPlayCommand(string sessionId, SendCommand command, CancellationToken cancellationToken); + + /// <summary> + /// Sends the SyncPlayGroupUpdate. + /// </summary> + /// <param name="sessionId">The session id.</param> + /// <param name="command">The group update.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task.</returns> + Task SendSyncPlayGroupUpdate<T>(string sessionId, GroupUpdate<T> command, CancellationToken cancellationToken); + + /// <summary> /// Sends the browse command. /// </summary> /// <param name="controllingSessionId">The controlling session identifier.</param> diff --git a/MediaBrowser.Controller/SyncPlay/GroupInfo.cs b/MediaBrowser.Controller/SyncPlay/GroupInfo.cs new file mode 100644 index 000000000..28a3ac505 --- /dev/null +++ b/MediaBrowser.Controller/SyncPlay/GroupInfo.cs @@ -0,0 +1,169 @@ +using System; +using System.Collections.Generic; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Session; + +namespace MediaBrowser.Controller.SyncPlay +{ + /// <summary> + /// Class GroupInfo. + /// </summary> + /// <remarks> + /// Class is not thread-safe, external locking is required when accessing methods. + /// </remarks> + public class GroupInfo + { + /// <summary> + /// Default ping value used for sessions. + /// </summary> + public long DefaulPing { get; } = 500; + + /// <summary> + /// Gets or sets the group identifier. + /// </summary> + /// <value>The group identifier.</value> + public Guid GroupId { get; } = Guid.NewGuid(); + + /// <summary> + /// Gets or sets the playing item. + /// </summary> + /// <value>The playing item.</value> + public BaseItem PlayingItem { get; set; } + + /// <summary> + /// Gets or sets whether playback is paused. + /// </summary> + /// <value>Playback is paused.</value> + public bool IsPaused { get; set; } + + /// <summary> + /// Gets or sets the position ticks. + /// </summary> + /// <value>The position ticks.</value> + public long PositionTicks { get; set; } + + /// <summary> + /// Gets or sets the last activity. + /// </summary> + /// <value>The last activity.</value> + public DateTime LastActivity { get; set; } + + /// <summary> + /// Gets the participants. + /// </summary> + /// <value>The participants, or members of the group.</value> + public Dictionary<string, GroupMember> Participants { get; } = + new Dictionary<string, GroupMember>(StringComparer.OrdinalIgnoreCase); + + /// <summary> + /// Checks if a session is in this group. + /// </summary> + /// <value><c>true</c> if the session is in this group; <c>false</c> otherwise.</value> + public bool ContainsSession(string sessionId) + { + return Participants.ContainsKey(sessionId); + } + + /// <summary> + /// Adds the session to the group. + /// </summary> + /// <param name="session">The session.</param> + public void AddSession(SessionInfo session) + { + if (ContainsSession(session.Id.ToString())) + { + return; + } + + var member = new GroupMember(); + member.Session = session; + member.Ping = DefaulPing; + member.IsBuffering = false; + Participants[session.Id.ToString()] = member; + } + + /// <summary> + /// Removes the session from the group. + /// </summary> + /// <param name="session">The session.</param> + public void RemoveSession(SessionInfo session) + { + if (!ContainsSession(session.Id.ToString())) + { + return; + } + + Participants.Remove(session.Id.ToString(), out _); + } + + /// <summary> + /// Updates the ping of a session. + /// </summary> + /// <param name="session">The session.</param> + /// <param name="ping">The ping.</param> + public void UpdatePing(SessionInfo session, long ping) + { + if (!ContainsSession(session.Id.ToString())) + { + return; + } + + Participants[session.Id.ToString()].Ping = ping; + } + + /// <summary> + /// Gets the highest ping in the group. + /// </summary> + /// <value name="session">The highest ping in the group.</value> + public long GetHighestPing() + { + long max = Int64.MinValue; + foreach (var session in Participants.Values) + { + max = Math.Max(max, session.Ping); + } + return max; + } + + /// <summary> + /// Sets the session's buffering state. + /// </summary> + /// <param name="session">The session.</param> + /// <param name="isBuffering">The state.</param> + public void SetBuffering(SessionInfo session, bool isBuffering) + { + if (!ContainsSession(session.Id.ToString())) + { + return; + } + + Participants[session.Id.ToString()].IsBuffering = isBuffering; + } + + /// <summary> + /// Gets the group buffering state. + /// </summary> + /// <value><c>true</c> if there is a session buffering in the group; <c>false</c> otherwise.</value> + public bool IsBuffering() + { + foreach (var session in Participants.Values) + { + if (session.IsBuffering) + { + return true; + } + } + + return false; + } + + /// <summary> + /// Checks if the group is empty. + /// </summary> + /// <value><c>true</c> if the group is empty; <c>false</c> otherwise.</value> + public bool IsEmpty() + { + return Participants.Count == 0; + } + } +} diff --git a/MediaBrowser.Controller/SyncPlay/GroupMember.cs b/MediaBrowser.Controller/SyncPlay/GroupMember.cs new file mode 100644 index 000000000..a3975c334 --- /dev/null +++ b/MediaBrowser.Controller/SyncPlay/GroupMember.cs @@ -0,0 +1,28 @@ +using MediaBrowser.Controller.Session; + +namespace MediaBrowser.Controller.SyncPlay +{ + /// <summary> + /// Class GroupMember. + /// </summary> + public class GroupMember + { + /// <summary> + /// Gets or sets whether this member is buffering. + /// </summary> + /// <value><c>true</c> if member is buffering; <c>false</c> otherwise.</value> + public bool IsBuffering { get; set; } + + /// <summary> + /// Gets or sets the session. + /// </summary> + /// <value>The session.</value> + public SessionInfo Session { get; set; } + + /// <summary> + /// Gets or sets the ping. + /// </summary> + /// <value>The ping.</value> + public long Ping { get; set; } + } +} diff --git a/MediaBrowser.Controller/SyncPlay/ISyncPlayController.cs b/MediaBrowser.Controller/SyncPlay/ISyncPlayController.cs new file mode 100644 index 000000000..de1fcd259 --- /dev/null +++ b/MediaBrowser.Controller/SyncPlay/ISyncPlayController.cs @@ -0,0 +1,67 @@ +using System; +using System.Threading; +using MediaBrowser.Controller.Session; +using MediaBrowser.Model.SyncPlay; + +namespace MediaBrowser.Controller.SyncPlay +{ + /// <summary> + /// Interface ISyncPlayController. + /// </summary> + public interface ISyncPlayController + { + /// <summary> + /// Gets the group id. + /// </summary> + /// <value>The group id.</value> + Guid GetGroupId(); + + /// <summary> + /// Gets the playing item id. + /// </summary> + /// <value>The playing item id.</value> + Guid GetPlayingItemId(); + + /// <summary> + /// Checks if the group is empty. + /// </summary> + /// <value>If the group is empty.</value> + bool IsGroupEmpty(); + + /// <summary> + /// Initializes the group with the session's info. + /// </summary> + /// <param name="session">The session.</param> + /// <param name="cancellationToken">The cancellation token.</param> + void InitGroup(SessionInfo session, CancellationToken cancellationToken); + + /// <summary> + /// Adds the session to the group. + /// </summary> + /// <param name="session">The session.</param> + /// <param name="request">The request.</param> + /// <param name="cancellationToken">The cancellation token.</param> + void SessionJoin(SessionInfo session, JoinGroupRequest request, CancellationToken cancellationToken); + + /// <summary> + /// Removes the session from the group. + /// </summary> + /// <param name="session">The session.</param> + /// <param name="cancellationToken">The cancellation token.</param> + void SessionLeave(SessionInfo session, CancellationToken cancellationToken); + + /// <summary> + /// Handles the requested action by the session. + /// </summary> + /// <param name="session">The session.</param> + /// <param name="request">The requested action.</param> + /// <param name="cancellationToken">The cancellation token.</param> + void HandleRequest(SessionInfo session, PlaybackRequest request, CancellationToken cancellationToken); + + /// <summary> + /// Gets the info about the group for the clients. + /// </summary> + /// <value>The group info for the clients.</value> + GroupInfoView GetInfo(); + } +}
\ No newline at end of file diff --git a/MediaBrowser.Controller/SyncPlay/ISyncPlayManager.cs b/MediaBrowser.Controller/SyncPlay/ISyncPlayManager.cs new file mode 100644 index 000000000..006fb687b --- /dev/null +++ b/MediaBrowser.Controller/SyncPlay/ISyncPlayManager.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using MediaBrowser.Controller.Session; +using MediaBrowser.Model.SyncPlay; + +namespace MediaBrowser.Controller.SyncPlay +{ + /// <summary> + /// Interface ISyncPlayManager. + /// </summary> + public interface ISyncPlayManager + { + /// <summary> + /// Creates a new group. + /// </summary> + /// <param name="session">The session that's creating the group.</param> + /// <param name="cancellationToken">The cancellation token.</param> + void NewGroup(SessionInfo session, CancellationToken cancellationToken); + + /// <summary> + /// Adds the session to a group. + /// </summary> + /// <param name="session">The session.</param> + /// <param name="groupId">The group id.</param> + /// <param name="request">The request.</param> + /// <param name="cancellationToken">The cancellation token.</param> + void JoinGroup(SessionInfo session, Guid groupId, JoinGroupRequest request, CancellationToken cancellationToken); + + /// <summary> + /// Removes the session from a group. + /// </summary> + /// <param name="session">The session.</param> + /// <param name="cancellationToken">The cancellation token.</param> + void LeaveGroup(SessionInfo session, CancellationToken cancellationToken); + + /// <summary> + /// Gets list of available groups for a session. + /// </summary> + /// <param name="session">The session.</param> + /// <param name="filterItemId">The item id to filter by.</param> + /// <value>The list of available groups.</value> + List<GroupInfoView> ListGroups(SessionInfo session, Guid filterItemId); + + /// <summary> + /// Handle a request by a session in a group. + /// </summary> + /// <param name="session">The session.</param> + /// <param name="request">The request.</param> + /// <param name="cancellationToken">The cancellation token.</param> + void HandleRequest(SessionInfo session, PlaybackRequest request, CancellationToken cancellationToken); + + /// <summary> + /// Maps a session to a group. + /// </summary> + /// <param name="session">The session.</param> + /// <param name="group">The group.</param> + /// <exception cref="InvalidOperationException"></exception> + void AddSessionToGroup(SessionInfo session, ISyncPlayController group); + + /// <summary> + /// Unmaps a session from a group. + /// </summary> + /// <param name="session">The session.</param> + /// <param name="group">The group.</param> + /// <exception cref="InvalidOperationException"></exception> + void RemoveSessionFromGroup(SessionInfo session, ISyncPlayController group); + } +} |
