diff options
| author | Luke <luke.pulverenti@gmail.com> | 2015-01-04 09:27:54 -0500 |
|---|---|---|
| committer | Luke <luke.pulverenti@gmail.com> | 2015-01-04 09:27:54 -0500 |
| commit | c5ff30f66e368efc2ca7dea7813fba6d9f6a657c (patch) | |
| tree | c5552b898f66b7d510e9257eb8bbeafd6a003676 /MediaBrowser.Controller | |
| parent | 767590125b27c2498e3ad9544edbede30fb70f45 (diff) | |
| parent | 59b6bc28c332701d5e383fbf99170bdc740fb6cc (diff) | |
Merge pull request #965 from MediaBrowser/dev
3.0.5482.0
Diffstat (limited to 'MediaBrowser.Controller')
52 files changed, 1008 insertions, 358 deletions
diff --git a/MediaBrowser.Controller/Channels/Channel.cs b/MediaBrowser.Controller/Channels/Channel.cs index f618c8a25..5a9fc3322 100644 --- a/MediaBrowser.Controller/Channels/Channel.cs +++ b/MediaBrowser.Controller/Channels/Channel.cs @@ -14,7 +14,7 @@ namespace MediaBrowser.Controller.Channels public override bool IsVisible(User user) { - if (user.Configuration.BlockedChannels.Contains(Id.ToString("N"), StringComparer.OrdinalIgnoreCase)) + if (user.Policy.BlockedChannels.Contains(Id.ToString("N"), StringComparer.OrdinalIgnoreCase)) { return false; } diff --git a/MediaBrowser.Controller/Channels/ChannelAudioItem.cs b/MediaBrowser.Controller/Channels/ChannelAudioItem.cs index ede366dab..896d598bb 100644 --- a/MediaBrowser.Controller/Channels/ChannelAudioItem.cs +++ b/MediaBrowser.Controller/Channels/ChannelAudioItem.cs @@ -6,6 +6,7 @@ using MediaBrowser.Model.Entities; using System.Collections.Generic; using System.Linq; using System.Threading; +using MediaBrowser.Model.Users; namespace MediaBrowser.Controller.Channels { @@ -25,8 +26,8 @@ namespace MediaBrowser.Controller.Channels public string OriginalImageUrl { get; set; } public List<ChannelMediaInfo> ChannelMediaSources { get; set; } - - protected override bool GetBlockUnratedValue(UserConfiguration config) + + protected override bool GetBlockUnratedValue(UserPolicy config) { return config.BlockUnratedItems.Contains(UnratedItem.ChannelContent); } diff --git a/MediaBrowser.Controller/Channels/ChannelFolderItem.cs b/MediaBrowser.Controller/Channels/ChannelFolderItem.cs index 5362cc195..8482e38df 100644 --- a/MediaBrowser.Controller/Channels/ChannelFolderItem.cs +++ b/MediaBrowser.Controller/Channels/ChannelFolderItem.cs @@ -5,6 +5,7 @@ using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Querying; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Model.Users; namespace MediaBrowser.Controller.Channels { @@ -20,7 +21,7 @@ namespace MediaBrowser.Controller.Channels public string OriginalImageUrl { get; set; } - protected override bool GetBlockUnratedValue(UserConfiguration config) + protected override bool GetBlockUnratedValue(UserPolicy config) { // Don't block. return false; diff --git a/MediaBrowser.Controller/Channels/ChannelVideoItem.cs b/MediaBrowser.Controller/Channels/ChannelVideoItem.cs index 72e2b110a..f0eafcbdf 100644 --- a/MediaBrowser.Controller/Channels/ChannelVideoItem.cs +++ b/MediaBrowser.Controller/Channels/ChannelVideoItem.cs @@ -8,6 +8,7 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Threading; +using MediaBrowser.Model.Users; namespace MediaBrowser.Controller.Channels { @@ -51,7 +52,7 @@ namespace MediaBrowser.Controller.Channels return ExternalId; } - protected override bool GetBlockUnratedValue(UserConfiguration config) + protected override bool GetBlockUnratedValue(UserPolicy config) { return config.BlockUnratedItems.Contains(UnratedItem.ChannelContent); } diff --git a/MediaBrowser.Controller/Devices/IDeviceManager.cs b/MediaBrowser.Controller/Devices/IDeviceManager.cs index efd24336a..f5010bb45 100644 --- a/MediaBrowser.Controller/Devices/IDeviceManager.cs +++ b/MediaBrowser.Controller/Devices/IDeviceManager.cs @@ -85,5 +85,13 @@ namespace MediaBrowser.Controller.Devices /// <param name="file">The file.</param> /// <returns>Task.</returns> Task AcceptCameraUpload(string deviceId, Stream stream, LocalFileInfo file); + + /// <summary> + /// Determines whether this instance [can access device] the specified user identifier. + /// </summary> + /// <param name="userId">The user identifier.</param> + /// <param name="deviceId">The device identifier.</param> + /// <returns><c>true</c> if this instance [can access device] the specified user identifier; otherwise, <c>false</c>.</returns> + bool CanAccessDevice(string userId, string deviceId); } } diff --git a/MediaBrowser.Controller/Dto/DtoOptions.cs b/MediaBrowser.Controller/Dto/DtoOptions.cs new file mode 100644 index 000000000..eeb4fc114 --- /dev/null +++ b/MediaBrowser.Controller/Dto/DtoOptions.cs @@ -0,0 +1,47 @@ +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Querying; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace MediaBrowser.Controller.Dto +{ + public class DtoOptions + { + private static readonly List<ItemFields> DefaultExcludedFields = new List<ItemFields> + { + ItemFields.SeasonUserData + }; + + public List<ItemFields> Fields { get; set; } + public List<ImageType> ImageTypes { get; set; } + public int ImageTypeLimit { get; set; } + public bool EnableImages { get; set; } + + public DtoOptions() + { + Fields = new List<ItemFields>(); + ImageTypeLimit = int.MaxValue; + EnableImages = true; + + Fields = Enum.GetNames(typeof (ItemFields)) + .Select(i => (ItemFields) Enum.Parse(typeof (ItemFields), i, true)) + .Except(DefaultExcludedFields) + .ToList(); + + ImageTypes = Enum.GetNames(typeof(ImageType)) + .Select(i => (ImageType)Enum.Parse(typeof(ImageType), i, true)) + .ToList(); + } + + public int GetImageLimit(ImageType type) + { + if (EnableImages && ImageTypes.Contains(type)) + { + return ImageTypeLimit; + } + + return 0; + } + } +} diff --git a/MediaBrowser.Controller/Entities/Audio/Audio.cs b/MediaBrowser.Controller/Entities/Audio/Audio.cs index 447328ea1..c5ed09016 100644 --- a/MediaBrowser.Controller/Entities/Audio/Audio.cs +++ b/MediaBrowser.Controller/Entities/Audio/Audio.cs @@ -8,6 +8,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; +using MediaBrowser.Model.Users; namespace MediaBrowser.Controller.Entities.Audio { @@ -88,6 +89,21 @@ namespace MediaBrowser.Controller.Entities.Audio } } + [IgnoreDataMember] + public bool IsArchive + { + get + { + if (string.IsNullOrWhiteSpace(Path)) + { + return false; + } + var ext = System.IO.Path.GetExtension(Path) ?? string.Empty; + + return new[] { ".zip", ".rar", ".7z" }.Contains(ext, StringComparer.OrdinalIgnoreCase); + } + } + /// <summary> /// Gets or sets the artist. /// </summary> @@ -173,7 +189,7 @@ namespace MediaBrowser.Controller.Entities.Audio return base.GetUserDataKey(); } - protected override bool GetBlockUnratedValue(UserConfiguration config) + protected override bool GetBlockUnratedValue(UserPolicy config) { return config.BlockUnratedItems.Contains(UnratedItem.Music); } diff --git a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs index 1f7c62de0..90edfcce7 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; +using MediaBrowser.Model.Users; namespace MediaBrowser.Controller.Entities.Audio { @@ -154,7 +155,7 @@ namespace MediaBrowser.Controller.Entities.Audio return base.GetUserDataKey(); } - protected override bool GetBlockUnratedValue(UserConfiguration config) + protected override bool GetBlockUnratedValue(UserPolicy config) { return config.BlockUnratedItems.Contains(UnratedItem.Music); } diff --git a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs index 2d9e052b1..a60258d1a 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs @@ -8,6 +8,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Model.Users; namespace MediaBrowser.Controller.Entities.Audio { @@ -114,7 +115,7 @@ namespace MediaBrowser.Controller.Entities.Audio return "Artist-" + item.Name; } - protected override bool GetBlockUnratedValue(UserConfiguration config) + protected override bool GetBlockUnratedValue(UserPolicy config) { return config.BlockUnratedItems.Contains(UnratedItem.Music); } @@ -135,7 +136,7 @@ namespace MediaBrowser.Controller.Entities.Audio // Refresh songs foreach (var item in songs) { - if (tasks.Count >= 3) + if (tasks.Count >= 2) { await Task.WhenAll(tasks).ConfigureAwait(false); tasks.Clear(); @@ -172,37 +173,23 @@ namespace MediaBrowser.Controller.Entities.Audio // Refresh all non-songs foreach (var item in others) { - if (tasks.Count >= 3) - { - await Task.WhenAll(tasks).ConfigureAwait(false); - tasks.Clear(); - } - cancellationToken.ThrowIfCancellationRequested(); - var innerProgress = new ActionableProgress<double>(); // Avoid implicitly captured closure var currentChild = item; - innerProgress.RegisterAction(p => - { - lock (percentages) - { - percentages[currentChild.Id] = p / 100; - var percent = percentages.Values.Sum(); - percent /= totalItems; - percent *= 100; - progress.Report(percent); - } - }); + await item.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false); + lock (percentages) + { + percentages[currentChild.Id] = 1; - // Avoid implicitly captured closure - var taskChild = item; - tasks.Add(Task.Run(async () => await RefreshItem(taskChild, refreshOptions, innerProgress, cancellationToken).ConfigureAwait(false), cancellationToken)); + var percent = percentages.Values.Sum(); + percent /= totalItems; + percent *= 100; + progress.Report(percent); + } } - await Task.WhenAll(tasks).ConfigureAwait(false); - progress.Report(100); } diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index ed950b1c5..ee562d8b4 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -14,6 +14,7 @@ using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Library; using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Users; using System; using System.Collections.Generic; using System.IO; @@ -45,6 +46,8 @@ namespace MediaBrowser.Controller.Entities /// </summary> public static readonly string[] SupportedImageExtensions = { ".png", ".jpg", ".jpeg", ".tbn" }; + public static readonly List<string> SupportedImageExtensionsList = SupportedImageExtensions.ToList(); + /// <summary> /// The trailer folder name /// </summary> @@ -593,7 +596,7 @@ namespace MediaBrowser.Controller.Entities /// <returns>PlayAccess.</returns> public PlayAccess GetPlayAccess(User user) { - if (!user.Configuration.EnableMediaPlayback) + if (!user.Policy.EnableMediaPlayback) { return PlayAccess.None; } @@ -985,7 +988,7 @@ namespace MediaBrowser.Controller.Entities return false; } - var maxAllowedRating = user.Configuration.MaxParentalRating; + var maxAllowedRating = user.Policy.MaxParentalRating; if (maxAllowedRating == null) { @@ -1001,7 +1004,7 @@ namespace MediaBrowser.Controller.Entities if (string.IsNullOrWhiteSpace(rating)) { - return !GetBlockUnratedValue(user.Configuration); + return !GetBlockUnratedValue(user.Policy); } var value = LocalizationManager.GetRatingLevel(rating); @@ -1021,7 +1024,7 @@ namespace MediaBrowser.Controller.Entities if (hasTags != null) { - if (user.Configuration.BlockedTags.Any(i => hasTags.Tags.Contains(i, StringComparer.OrdinalIgnoreCase))) + if (user.Policy.BlockedTags.Any(i => hasTags.Tags.Contains(i, StringComparer.OrdinalIgnoreCase))) { return false; } @@ -1035,7 +1038,7 @@ namespace MediaBrowser.Controller.Entities /// </summary> /// <param name="config">The configuration.</param> /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns> - protected virtual bool GetBlockUnratedValue(UserConfiguration config) + protected virtual bool GetBlockUnratedValue(UserPolicy config) { return config.BlockUnratedItems.Contains(UnratedItem.Other); } @@ -1574,6 +1577,11 @@ namespace MediaBrowser.Controller.Entities foreach (var newImage in images) { + if (newImage == null) + { + throw new ArgumentException("null image found in list"); + } + var existing = existingImages .FirstOrDefault(i => string.Equals(i.Path, newImage.FullName, StringComparison.OrdinalIgnoreCase)); diff --git a/MediaBrowser.Controller/Entities/Book.cs b/MediaBrowser.Controller/Entities/Book.cs index ea7ecfb4a..381b2101d 100644 --- a/MediaBrowser.Controller/Entities/Book.cs +++ b/MediaBrowser.Controller/Entities/Book.cs @@ -2,6 +2,7 @@ using MediaBrowser.Model.Configuration; using System.Collections.Generic; using System.Linq; +using MediaBrowser.Model.Users; namespace MediaBrowser.Controller.Entities { @@ -36,7 +37,7 @@ namespace MediaBrowser.Controller.Entities Tags = new List<string>(); } - protected override bool GetBlockUnratedValue(UserConfiguration config) + protected override bool GetBlockUnratedValue(UserPolicy config) { return config.BlockUnratedItems.Contains(UnratedItem.Book); } diff --git a/MediaBrowser.Controller/Entities/CollectionFolder.cs b/MediaBrowser.Controller/Entities/CollectionFolder.cs index a10742f01..f47a439a7 100644 --- a/MediaBrowser.Controller/Entities/CollectionFolder.cs +++ b/MediaBrowser.Controller/Entities/CollectionFolder.cs @@ -66,6 +66,22 @@ namespace MediaBrowser.Controller.Entities return CreateResolveArgs(directoryService).FileSystemChildren; } + internal override bool IsValidFromResolver(BaseItem newItem) + { + var newCollectionFolder = newItem as CollectionFolder; + + if (newCollectionFolder != null) + { + if (!string.Equals(CollectionType, newCollectionFolder.CollectionType, StringComparison.OrdinalIgnoreCase)) + { + return false; + } + } + + + return base.IsValidFromResolver(newItem); + } + private ItemResolveArgs CreateResolveArgs(IDirectoryService directoryService) { var path = ContainingFolderPath; diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index 87ad9c380..2761aa5d7 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -303,10 +303,10 @@ namespace MediaBrowser.Controller.Entities { if (this is ICollectionFolder) { - if (user.Configuration.BlockedMediaFolders.Contains(Id.ToString("N"), StringComparer.OrdinalIgnoreCase) || + if (user.Policy.BlockedMediaFolders.Contains(Id.ToString("N"), StringComparer.OrdinalIgnoreCase) || // Backwards compatibility - user.Configuration.BlockedMediaFolders.Contains(Name, StringComparer.OrdinalIgnoreCase)) + user.Policy.BlockedMediaFolders.Contains(Name, StringComparer.OrdinalIgnoreCase)) { return false; } @@ -545,7 +545,7 @@ namespace MediaBrowser.Controller.Entities foreach (var child in children) { - if (tasks.Count >= 3) + if (tasks.Count >= 2) { await Task.WhenAll(tasks).ConfigureAwait(false); tasks.Clear(); @@ -708,7 +708,7 @@ namespace MediaBrowser.Controller.Entities /// <returns>IEnumerable{BaseItem}.</returns> protected virtual IEnumerable<BaseItem> GetNonCachedChildren(IDirectoryService directoryService) { - var collectionType = LibraryManager.FindCollectionType(this); + var collectionType = LibraryManager.GetContentType(this); return LibraryManager.ResolvePaths(GetFileSystemChildren(directoryService), directoryService, this, collectionType); } diff --git a/MediaBrowser.Controller/Entities/Game.cs b/MediaBrowser.Controller/Entities/Game.cs index e4d032359..bf32d3e63 100644 --- a/MediaBrowser.Controller/Entities/Game.cs +++ b/MediaBrowser.Controller/Entities/Game.cs @@ -4,6 +4,7 @@ using MediaBrowser.Model.Entities; using System; using System.Collections.Generic; using System.Linq; +using MediaBrowser.Model.Users; namespace MediaBrowser.Controller.Entities { @@ -108,7 +109,7 @@ namespace MediaBrowser.Controller.Entities return base.GetDeletePaths(); } - protected override bool GetBlockUnratedValue(UserConfiguration config) + protected override bool GetBlockUnratedValue(UserPolicy config) { return config.BlockUnratedItems.Contains(UnratedItem.Game); } diff --git a/MediaBrowser.Controller/Entities/GameSystem.cs b/MediaBrowser.Controller/Entities/GameSystem.cs index f2fec4397..758498977 100644 --- a/MediaBrowser.Controller/Entities/GameSystem.cs +++ b/MediaBrowser.Controller/Entities/GameSystem.cs @@ -2,6 +2,7 @@ using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Configuration; using System; +using MediaBrowser.Model.Users; namespace MediaBrowser.Controller.Entities { @@ -43,7 +44,7 @@ namespace MediaBrowser.Controller.Entities return base.GetUserDataKey(); } - protected override bool GetBlockUnratedValue(UserConfiguration config) + protected override bool GetBlockUnratedValue(UserPolicy config) { // Don't block. Determine by game return false; diff --git a/MediaBrowser.Controller/Entities/IHasMediaSources.cs b/MediaBrowser.Controller/Entities/IHasMediaSources.cs index d487362f5..98d268298 100644 --- a/MediaBrowser.Controller/Entities/IHasMediaSources.cs +++ b/MediaBrowser.Controller/Entities/IHasMediaSources.cs @@ -49,8 +49,8 @@ namespace MediaBrowser.Controller.Entities : new[] { user.Configuration.AudioLanguagePreference }; var preferredSubs = string.IsNullOrEmpty(user.Configuration.SubtitleLanguagePreference) - ? new string[] { } - : new[] { user.Configuration.SubtitleLanguagePreference }; + ? new List<string> { } + : new List<string> { user.Configuration.SubtitleLanguagePreference }; foreach (var source in sources) { diff --git a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs index 9dc600675..4483c7b0f 100644 --- a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs +++ b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs @@ -9,6 +9,7 @@ using System.Linq; using System.Runtime.Serialization; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Model.Users; namespace MediaBrowser.Controller.Entities.Movies { @@ -18,7 +19,7 @@ namespace MediaBrowser.Controller.Entities.Movies public class BoxSet : Folder, IHasTrailers, IHasKeywords, IHasPreferredMetadataLanguage, IHasDisplayOrder, IHasLookupInfo<BoxSetInfo>, IMetadataContainer, IHasShares { public List<Share> Shares { get; set; } - + public BoxSet() { RemoteTrailers = new List<MediaUrl>(); @@ -67,7 +68,7 @@ namespace MediaBrowser.Controller.Entities.Movies /// <value>The display order.</value> public string DisplayOrder { get; set; } - protected override bool GetBlockUnratedValue(UserConfiguration config) + protected override bool GetBlockUnratedValue(UserPolicy config) { return config.BlockUnratedItems.Contains(UnratedItem.Movie); } @@ -170,10 +171,13 @@ namespace MediaBrowser.Controller.Entities.Movies { var userId = user.Id.ToString("N"); - return Shares.Any(i => string.Equals(userId, i.UserId, StringComparison.OrdinalIgnoreCase)) || + // Need to check Count > 0 for boxsets created prior to the introduction of Shares + if (Shares.Count > 0 && !Shares.Any(i => string.Equals(userId, i.UserId, StringComparison.OrdinalIgnoreCase))) + { + //return false; + } - // Need to support this for boxsets created prior to the creation of Shares - Shares.Count == 0; + return true; } return false; diff --git a/MediaBrowser.Controller/Entities/Movies/Movie.cs b/MediaBrowser.Controller/Entities/Movies/Movie.cs index e749d89e4..b3774cfe0 100644 --- a/MediaBrowser.Controller/Entities/Movies/Movie.cs +++ b/MediaBrowser.Controller/Entities/Movies/Movie.cs @@ -1,6 +1,7 @@ using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Users; using System; using System.Collections.Generic; using System.IO; @@ -146,14 +147,21 @@ namespace MediaBrowser.Controller.Entities.Movies return itemsChanged; } - protected override bool GetBlockUnratedValue(UserConfiguration config) + protected override bool GetBlockUnratedValue(UserPolicy config) { return config.BlockUnratedItems.Contains(UnratedItem.Movie); } public MovieInfo GetLookupInfo() { - return GetItemLookupInfo<MovieInfo>(); + var info = GetItemLookupInfo<MovieInfo>(); + + if (!IsInMixedFolder) + { + info.Name = System.IO.Path.GetFileName(ContainingFolderPath); + } + + return info; } public override bool BeforeMetadataRefresh() diff --git a/MediaBrowser.Controller/Entities/MusicVideo.cs b/MediaBrowser.Controller/Entities/MusicVideo.cs index d7cd62aa6..4ca8cf1c5 100644 --- a/MediaBrowser.Controller/Entities/MusicVideo.cs +++ b/MediaBrowser.Controller/Entities/MusicVideo.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; +using MediaBrowser.Model.Users; namespace MediaBrowser.Controller.Entities { @@ -80,7 +81,7 @@ namespace MediaBrowser.Controller.Entities return this.GetProviderId(MetadataProviders.Tmdb) ?? this.GetProviderId(MetadataProviders.Imdb) ?? base.GetUserDataKey(); } - protected override bool GetBlockUnratedValue(UserConfiguration config) + protected override bool GetBlockUnratedValue(UserPolicy config) { return config.BlockUnratedItems.Contains(UnratedItem.Music); } diff --git a/MediaBrowser.Controller/Entities/Photo.cs b/MediaBrowser.Controller/Entities/Photo.cs index 367db5dcb..a3d892181 100644 --- a/MediaBrowser.Controller/Entities/Photo.cs +++ b/MediaBrowser.Controller/Entities/Photo.cs @@ -3,6 +3,7 @@ using MediaBrowser.Model.Drawing; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; +using MediaBrowser.Model.Users; namespace MediaBrowser.Controller.Entities { @@ -69,8 +70,8 @@ namespace MediaBrowser.Controller.Entities public double? Longitude { get; set; } public double? Altitude { get; set; } public int? IsoSpeedRating { get; set; } - - protected override bool GetBlockUnratedValue(UserConfiguration config) + + protected override bool GetBlockUnratedValue(UserPolicy config) { return config.BlockUnratedItems.Contains(UnratedItem.Other); } diff --git a/MediaBrowser.Controller/Entities/PhotoAlbum.cs b/MediaBrowser.Controller/Entities/PhotoAlbum.cs index 982b1ef17..24ebf8815 100644 --- a/MediaBrowser.Controller/Entities/PhotoAlbum.cs +++ b/MediaBrowser.Controller/Entities/PhotoAlbum.cs @@ -1,6 +1,7 @@ using MediaBrowser.Model.Configuration; using System.Linq; using System.Runtime.Serialization; +using MediaBrowser.Model.Users; namespace MediaBrowser.Controller.Entities { @@ -22,8 +23,8 @@ namespace MediaBrowser.Controller.Entities return true; } } - - protected override bool GetBlockUnratedValue(UserConfiguration config) + + protected override bool GetBlockUnratedValue(UserPolicy config) { return config.BlockUnratedItems.Contains(UnratedItem.Other); } diff --git a/MediaBrowser.Controller/Entities/TV/Episode.cs b/MediaBrowser.Controller/Entities/TV/Episode.cs index cc0fc6812..6b67cebc8 100644 --- a/MediaBrowser.Controller/Entities/TV/Episode.cs +++ b/MediaBrowser.Controller/Entities/TV/Episode.cs @@ -1,7 +1,7 @@ -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Providers; +using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Users; using System; using System.Collections.Generic; using System.Linq; @@ -179,6 +179,15 @@ namespace MediaBrowser.Controller.Entities.TV } [IgnoreDataMember] + public bool IsInSeasonFolder + { + get + { + return FindParent<Season>() != null; + } + } + + [IgnoreDataMember] public string SeriesName { get @@ -275,7 +284,7 @@ namespace MediaBrowser.Controller.Entities.TV return new[] { Path }; } - protected override bool GetBlockUnratedValue(UserConfiguration config) + protected override bool GetBlockUnratedValue(UserPolicy config) { return config.BlockUnratedItems.Contains(UnratedItem.Series); } @@ -301,51 +310,9 @@ namespace MediaBrowser.Controller.Entities.TV { var hasChanges = base.BeforeMetadataRefresh(); - var locationType = LocationType; - if (locationType == LocationType.FileSystem || locationType == LocationType.Offline) - { - if (!IndexNumber.HasValue && !string.IsNullOrEmpty(Path)) - { - IndexNumber = LibraryManager.GetEpisodeNumberFromFile(Path, true); - - // If a change was made record it - if (IndexNumber.HasValue) - { - hasChanges = true; - } - } - - if (!IndexNumberEnd.HasValue && !string.IsNullOrEmpty(Path)) - { - IndexNumberEnd = LibraryManager.GetEndingEpisodeNumberFromFile(Path); - - // If a change was made record it - if (IndexNumberEnd.HasValue) - { - hasChanges = true; - } - } - } - - if (!ParentIndexNumber.HasValue) + if (LibraryManager.FillMissingEpisodeNumbersFromPath(this)) { - var season = Season; - - if (season != null) - { - ParentIndexNumber = season.IndexNumber; - } - - if (!ParentIndexNumber.HasValue && !string.IsNullOrEmpty(Path)) - { - ParentIndexNumber = LibraryManager.GetSeasonNumberFromEpisodeFile(Path); - } - - // If a change was made record it - if (ParentIndexNumber.HasValue) - { - hasChanges = true; - } + hasChanges = true; } return hasChanges; diff --git a/MediaBrowser.Controller/Entities/TV/Season.cs b/MediaBrowser.Controller/Entities/TV/Season.cs index 2df90244c..54db12b6f 100644 --- a/MediaBrowser.Controller/Entities/TV/Season.cs +++ b/MediaBrowser.Controller/Entities/TV/Season.cs @@ -1,9 +1,9 @@ -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Localization; +using MediaBrowser.Controller.Localization; using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Querying; +using MediaBrowser.Model.Users; +using MoreLinq; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; @@ -155,24 +155,6 @@ namespace MediaBrowser.Controller.Entities.TV return IndexNumber != null ? IndexNumber.Value.ToString("0000") : Name; } - private IEnumerable<Episode> GetEpisodes() - { - var series = Series; - - if (series != null && series.ContainsEpisodesWithoutSeasonFolders) - { - var seasonNumber = IndexNumber; - - if (seasonNumber.HasValue) - { - return series.RecursiveChildren.OfType<Episode>() - .Where(i => i.ParentIndexNumber.HasValue && i.ParentIndexNumber.Value == seasonNumber.Value); - } - } - - return Children.OfType<Episode>(); - } - [IgnoreDataMember] public bool IsMissingSeason { @@ -220,16 +202,32 @@ namespace MediaBrowser.Controller.Entities.TV var episodes = GetRecursiveChildren(user) .OfType<Episode>(); - if (IndexNumber.HasValue) + var series = Series; + + if (IndexNumber.HasValue && series != null) { - var series = Series; + return series.GetEpisodes(user, IndexNumber.Value, includeMissingEpisodes, includeVirtualUnairedEpisodes, episodes); + } - if (series != null) + if (series != null && series.ContainsEpisodesWithoutSeasonFolders) + { + var seasonNumber = IndexNumber; + var list = episodes.ToList(); + + if (seasonNumber.HasValue) { - return series.GetEpisodes(user, IndexNumber.Value, includeMissingEpisodes, includeVirtualUnairedEpisodes, episodes); + list.AddRange(series.GetRecursiveChildren(user).OfType<Episode>() + .Where(i => i.ParentIndexNumber.HasValue && i.ParentIndexNumber.Value == seasonNumber.Value)); + } + else + { + list.AddRange(series.GetRecursiveChildren(user).OfType<Episode>() + .Where(i => !i.ParentIndexNumber.HasValue)); } - } + episodes = list.DistinctBy(i => i.Id); + } + if (!includeMissingEpisodes) { episodes = episodes.Where(i => !i.IsMissingEpisode); @@ -244,12 +242,39 @@ namespace MediaBrowser.Controller.Entities.TV .Cast<Episode>(); } + private IEnumerable<Episode> GetEpisodes() + { + var episodes = RecursiveChildren.OfType<Episode>(); + var series = Series; + + if (series != null && series.ContainsEpisodesWithoutSeasonFolders) + { + var seasonNumber = IndexNumber; + var list = episodes.ToList(); + + if (seasonNumber.HasValue) + { + list.AddRange(series.RecursiveChildren.OfType<Episode>() + .Where(i => i.ParentIndexNumber.HasValue && i.ParentIndexNumber.Value == seasonNumber.Value)); + } + else + { + list.AddRange(series.RecursiveChildren.OfType<Episode>() + .Where(i => !i.ParentIndexNumber.HasValue)); + } + + episodes = list.DistinctBy(i => i.Id); + } + + return episodes; + } + public override IEnumerable<BaseItem> GetChildren(User user, bool includeLinkedChildren) { return GetEpisodes(user); } - protected override bool GetBlockUnratedValue(UserConfiguration config) + protected override bool GetBlockUnratedValue(UserPolicy config) { // Don't block. Let either the entire series rating or episode rating determine it return false; diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs index 4c0d1fdfb..55cfffeb2 100644 --- a/MediaBrowser.Controller/Entities/TV/Series.cs +++ b/MediaBrowser.Controller/Entities/TV/Series.cs @@ -3,6 +3,7 @@ using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Querying; +using MediaBrowser.Model.Users; using System; using System.Collections.Generic; using System.Linq; @@ -87,7 +88,17 @@ namespace MediaBrowser.Controller.Entities.TV /// Gets or sets the date last episode added. /// </summary> /// <value>The date last episode added.</value> - public DateTime DateLastEpisodeAdded { get; set; } + [IgnoreDataMember] + public DateTime DateLastEpisodeAdded + { + get + { + return RecursiveChildren.OfType<Episode>() + .Select(i => i.DateCreated) + .OrderByDescending(i => i) + .FirstOrDefault(); + } + } /// <summary> /// Series aren't included directly in indices - Their Episodes will roll up to them @@ -246,7 +257,7 @@ namespace MediaBrowser.Controller.Entities.TV }); } - protected override bool GetBlockUnratedValue(UserConfiguration config) + protected override bool GetBlockUnratedValue(UserPolicy config) { return config.BlockUnratedItems.Contains(UnratedItem.Series); } diff --git a/MediaBrowser.Controller/Entities/Trailer.cs b/MediaBrowser.Controller/Entities/Trailer.cs index bb165d790..7a1eef8db 100644 --- a/MediaBrowser.Controller/Entities/Trailer.cs +++ b/MediaBrowser.Controller/Entities/Trailer.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Runtime.Serialization; +using MediaBrowser.Model.Users; namespace MediaBrowser.Controller.Entities { @@ -98,7 +99,7 @@ namespace MediaBrowser.Controller.Entities return base.GetUserDataKey(); } - protected override bool GetBlockUnratedValue(UserConfiguration config) + protected override bool GetBlockUnratedValue(UserPolicy config) { return config.BlockUnratedItems.Contains(UnratedItem.Trailer); } diff --git a/MediaBrowser.Controller/Entities/User.cs b/MediaBrowser.Controller/Entities/User.cs index 3dfc8cc7d..626afcfdf 100644 --- a/MediaBrowser.Controller/Entities/User.cs +++ b/MediaBrowser.Controller/Entities/User.cs @@ -1,5 +1,4 @@ -using MediaBrowser.Common.Configuration; -using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Connect; @@ -107,37 +106,27 @@ namespace MediaBrowser.Controller.Entities /// <value>The last activity date.</value> public DateTime? LastActivityDate { get; set; } - /// <summary> - /// The _configuration - /// </summary> - private UserConfiguration _configuration; - /// <summary> - /// The _configuration initialized - /// </summary> - private bool _configurationInitialized; - /// <summary> - /// The _configuration sync lock - /// </summary> - private object _configurationSyncLock = new object(); - /// <summary> - /// Gets the user's configuration - /// </summary> - /// <value>The configuration.</value> + private UserConfiguration _config; + private readonly object _configSyncLock = new object(); [IgnoreDataMember] public UserConfiguration Configuration { get { - // Lazy load - LazyInitializer.EnsureInitialized(ref _configuration, ref _configurationInitialized, ref _configurationSyncLock, () => (UserConfiguration)ConfigurationHelper.GetXmlConfiguration(typeof(UserConfiguration), ConfigurationFilePath, XmlSerializer)); - return _configuration; - } - private set - { - _configuration = value; + if (_config == null) + { + lock (_configSyncLock) + { + if (_config == null) + { + _config = UserManager.GetUserConfiguration(this); + } + } + } - _configurationInitialized = value != null; + return _config; } + set { _config = value; } } private UserPolicy _policy; @@ -256,35 +245,6 @@ namespace MediaBrowser.Controller.Entities return System.IO.Path.Combine(parentPath, Id.ToString("N")); } - /// <summary> - /// Gets the path to the user's configuration file - /// </summary> - /// <value>The configuration file path.</value> - [IgnoreDataMember] - public string ConfigurationFilePath - { - get - { - return System.IO.Path.Combine(ConfigurationDirectoryPath, "config.xml"); - } - } - - /// <summary> - /// Updates the configuration. - /// </summary> - /// <param name="config">The config.</param> - /// <exception cref="System.ArgumentNullException">config</exception> - public void UpdateConfiguration(UserConfiguration config) - { - if (config == null) - { - throw new ArgumentNullException("config"); - } - - Configuration = config; - UserManager.UpdateConfiguration(this, Configuration); - } - public bool IsParentalScheduleAllowed() { return IsParentalScheduleAllowed(DateTime.UtcNow); @@ -292,7 +252,7 @@ namespace MediaBrowser.Controller.Entities public bool IsParentalScheduleAllowed(DateTime date) { - var schedules = Configuration.AccessSchedules; + var schedules = Policy.AccessSchedules; if (schedules.Length == 0) { diff --git a/MediaBrowser.Controller/Entities/UserView.cs b/MediaBrowser.Controller/Entities/UserView.cs index 926ffa19c..0364ff678 100644 --- a/MediaBrowser.Controller/Entities/UserView.cs +++ b/MediaBrowser.Controller/Entities/UserView.cs @@ -63,7 +63,8 @@ namespace MediaBrowser.Controller.Entities { CollectionType.Books, CollectionType.HomeVideos, - CollectionType.Photos + CollectionType.Photos, + string.Empty }; var collectionFolder = folder as ICollectionFolder; diff --git a/MediaBrowser.Controller/Entities/Video.cs b/MediaBrowser.Controller/Entities/Video.cs index 0c6125dbe..3abaf095c 100644 --- a/MediaBrowser.Controller/Entities/Video.cs +++ b/MediaBrowser.Controller/Entities/Video.cs @@ -91,6 +91,21 @@ namespace MediaBrowser.Controller.Entities get { return LocalAlternateVersions.Count > 0; } } + [IgnoreDataMember] + public bool IsArchive + { + get + { + if (string.IsNullOrWhiteSpace(Path)) + { + return false; + } + var ext = System.IO.Path.GetExtension(Path) ?? string.Empty; + + return new[] { ".zip", ".rar", ".7z" }.Contains(ext, StringComparer.OrdinalIgnoreCase); + } + } + public IEnumerable<Guid> GetAdditionalPartIds() { return AdditionalParts.Select(i => LibraryManager.GetNewItemId(i, typeof(Video))); @@ -246,7 +261,7 @@ namespace MediaBrowser.Controller.Entities { return System.IO.Path.GetFileName(Path); } - + return System.IO.Path.GetFileNameWithoutExtension(Path); } diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs index 33dea4dca..8573f32e0 100644 --- a/MediaBrowser.Controller/Library/ILibraryManager.cs +++ b/MediaBrowser.Controller/Library/ILibraryManager.cs @@ -1,5 +1,6 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Resolvers; using MediaBrowser.Controller.Sorting; @@ -22,11 +23,9 @@ namespace MediaBrowser.Controller.Library /// </summary> /// <param name="fileInfo">The file information.</param> /// <param name="parent">The parent.</param> - /// <param name="collectionType">Type of the collection.</param> /// <returns>BaseItem.</returns> BaseItem ResolvePath(FileSystemInfo fileInfo, - Folder parent = null, - string collectionType = null); + Folder parent = null); /// <summary> /// Resolves a set of files into a list of BaseItem @@ -258,9 +257,16 @@ namespace MediaBrowser.Controller.Library /// </summary> /// <param name="item">The item.</param> /// <returns>System.String.</returns> - string FindCollectionType(BaseItem item); + string GetContentType(BaseItem item); /// <summary> + /// Gets the type of the inherited content. + /// </summary> + /// <param name="item">The item.</param> + /// <returns>System.String.</returns> + string GetInheritedContentType(BaseItem item); + + /// <summary> /// Normalizes the root path list. /// </summary> /// <param name="paths">The paths.</param> @@ -340,26 +346,11 @@ namespace MediaBrowser.Controller.Library int? GetSeasonNumberFromPath(string path); /// <summary> - /// Gets the season number from episode file. - /// </summary> - /// <param name="path">The path.</param> - /// <returns>System.Nullable<System.Int32>.</returns> - int? GetSeasonNumberFromEpisodeFile(string path); - - /// <summary> - /// Gets the ending episode number from file. + /// Fills the missing episode numbers from path. /// </summary> - /// <param name="path">The path.</param> - /// <returns>System.Nullable<System.Int32>.</returns> - int? GetEndingEpisodeNumberFromFile(string path); - - /// <summary> - /// Gets the episode number from file. - /// </summary> - /// <param name="path">The path.</param> - /// <param name="considerSeasonless">if set to <c>true</c> [consider seasonless].</param> - /// <returns>System.Nullable<System.Int32>.</returns> - int? GetEpisodeNumberFromFile(string path, bool considerSeasonless); + /// <param name="episode">The episode.</param> + /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns> + bool FillMissingEpisodeNumbersFromPath(Episode episode); /// <summary> /// Parses the name. diff --git a/MediaBrowser.Controller/Library/IUserDataManager.cs b/MediaBrowser.Controller/Library/IUserDataManager.cs index 226f77525..06e7d1763 100644 --- a/MediaBrowser.Controller/Library/IUserDataManager.cs +++ b/MediaBrowser.Controller/Library/IUserDataManager.cs @@ -61,5 +61,9 @@ namespace MediaBrowser.Controller.Library /// <returns></returns> Task SaveAllUserData(Guid userId, IEnumerable<UserItemData> userData, CancellationToken cancellationToken); + /// <summary> + /// Updates playstate for an item and returns true or false indicating if it was played to completion + /// </summary> + bool UpdatePlayState(BaseItem item, UserItemData data, long positionTicks); } } diff --git a/MediaBrowser.Controller/Library/IUserManager.cs b/MediaBrowser.Controller/Library/IUserManager.cs index 9dc16ba4d..f5846973e 100644 --- a/MediaBrowser.Controller/Library/IUserManager.cs +++ b/MediaBrowser.Controller/Library/IUserManager.cs @@ -36,13 +36,6 @@ namespace MediaBrowser.Controller.Library event EventHandler<GenericEventArgs<User>> UserPasswordChanged; /// <summary> - /// Updates the configuration. - /// </summary> - /// <param name="user">The user.</param> - /// <param name="newConfiguration">The new configuration.</param> - void UpdateConfiguration(User user, UserConfiguration newConfiguration); - - /// <summary> /// Gets a User by Id /// </summary> /// <param name="id">The id.</param> @@ -173,10 +166,32 @@ namespace MediaBrowser.Controller.Library UserPolicy GetUserPolicy(User user); /// <summary> + /// Gets the user configuration. + /// </summary> + /// <param name="user">The user.</param> + /// <returns>UserConfiguration.</returns> + UserConfiguration GetUserConfiguration(User user); + + /// <summary> + /// Updates the configuration. + /// </summary> + /// <param name="userId">The user identifier.</param> + /// <param name="newConfiguration">The new configuration.</param> + /// <returns>Task.</returns> + Task UpdateConfiguration(string userId, UserConfiguration newConfiguration); + + /// <summary> /// Updates the user policy. /// </summary> /// <param name="userId">The user identifier.</param> /// <param name="userPolicy">The user policy.</param> Task UpdateUserPolicy(string userId, UserPolicy userPolicy); + + /// <summary> + /// Makes the valid username. + /// </summary> + /// <param name="username">The username.</param> + /// <returns>System.String.</returns> + string MakeValidUsername(string username); } } diff --git a/MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs b/MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs index 9f8d67a48..b95d67ad8 100644 --- a/MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs +++ b/MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs @@ -2,6 +2,7 @@ using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Entities; using System.Linq; +using MediaBrowser.Model.Users; namespace MediaBrowser.Controller.LiveTv { @@ -78,7 +79,7 @@ namespace MediaBrowser.Controller.LiveTv } } - protected override bool GetBlockUnratedValue(UserConfiguration config) + protected override bool GetBlockUnratedValue(UserPolicy config) { return config.BlockUnratedItems.Contains(UnratedItem.LiveTvProgram); } diff --git a/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs b/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs index df118b25f..de72accff 100644 --- a/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs +++ b/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs @@ -6,6 +6,7 @@ using MediaBrowser.Model.LiveTv; using MediaBrowser.Model.MediaInfo; using System.Collections.Generic; using System.Linq; +using MediaBrowser.Model.Users; namespace MediaBrowser.Controller.LiveTv { @@ -33,7 +34,7 @@ namespace MediaBrowser.Controller.LiveTv } } - protected override bool GetBlockUnratedValue(UserConfiguration config) + protected override bool GetBlockUnratedValue(UserPolicy config) { return config.BlockUnratedItems.Contains(UnratedItem.LiveTvChannel); } diff --git a/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs b/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs index 266eaabee..29b23a551 100644 --- a/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs +++ b/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs @@ -6,6 +6,7 @@ using System; using System.Threading; using System.Threading.Tasks; using System.Linq; +using MediaBrowser.Model.Users; namespace MediaBrowser.Controller.LiveTv { @@ -199,7 +200,7 @@ namespace MediaBrowser.Controller.LiveTv return ItemRepository.SaveItem(this, cancellationToken); } - protected override bool GetBlockUnratedValue(UserConfiguration config) + protected override bool GetBlockUnratedValue(UserPolicy config) { return config.BlockUnratedItems.Contains(UnratedItem.LiveTvProgram); } diff --git a/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs b/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs index 66de81213..6fc985643 100644 --- a/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs +++ b/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs @@ -2,6 +2,7 @@ using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Entities; using System.Linq; +using MediaBrowser.Model.Users; namespace MediaBrowser.Controller.LiveTv { @@ -78,7 +79,7 @@ namespace MediaBrowser.Controller.LiveTv } } - protected override bool GetBlockUnratedValue(UserConfiguration config) + protected override bool GetBlockUnratedValue(UserPolicy config) { return config.BlockUnratedItems.Contains(UnratedItem.LiveTvProgram); } diff --git a/MediaBrowser.Controller/LiveTv/RecordingGroup.cs b/MediaBrowser.Controller/LiveTv/RecordingGroup.cs index 7bd810b8d..d7250d9d2 100644 --- a/MediaBrowser.Controller/LiveTv/RecordingGroup.cs +++ b/MediaBrowser.Controller/LiveTv/RecordingGroup.cs @@ -1,11 +1,12 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Model.Configuration; +using MediaBrowser.Model.Users; namespace MediaBrowser.Controller.LiveTv { public class RecordingGroup : Folder { - protected override bool GetBlockUnratedValue(UserConfiguration config) + protected override bool GetBlockUnratedValue(UserPolicy config) { // Don't block. return false; diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index c198a58d4..0667730fd 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -115,6 +115,7 @@ <Compile Include="Drawing\ImageProcessingOptions.cs" /> <Compile Include="Drawing\ImageProcessorExtensions.cs" /> <Compile Include="Drawing\ImageStream.cs" /> + <Compile Include="Dto\DtoOptions.cs" /> <Compile Include="Dto\IDtoService.cs" /> <Compile Include="Entities\AdultVideo.cs" /> <Compile Include="Entities\Audio\IHasAlbumArtist.cs" /> @@ -198,18 +199,17 @@ <Compile Include="LiveTv\SeriesTimerInfo.cs" /> <Compile Include="LiveTv\TimerInfo.cs" /> <Compile Include="Localization\ILocalizationManager.cs" /> - <Compile Include="MediaEncoding\EncodingOptions.cs" /> <Compile Include="MediaEncoding\ChapterImageRefreshOptions.cs" /> - <Compile Include="MediaEncoding\EncodingResult.cs" /> + <Compile Include="MediaEncoding\EncodingJobOptions.cs" /> <Compile Include="MediaEncoding\IEncodingManager.cs" /> <Compile Include="MediaEncoding\ImageEncodingOptions.cs" /> <Compile Include="MediaEncoding\IMediaEncoder.cs" /> <Compile Include="MediaEncoding\InternalMediaInfoResult.cs" /> <Compile Include="MediaEncoding\ISubtitleEncoder.cs" /> <Compile Include="MediaEncoding\MediaStreamSelector.cs" /> - <Compile Include="MediaEncoding\VideoEncodingOptions.cs" /> <Compile Include="Net\AuthenticatedAttribute.cs" /> <Compile Include="Net\AuthorizationInfo.cs" /> + <Compile Include="Net\BasePeriodicWebSocketListener.cs" /> <Compile Include="Net\IAuthorizationContext.cs" /> <Compile Include="Net\IAuthService.cs" /> <Compile Include="Net\IHasAuthorization.cs" /> @@ -221,10 +221,15 @@ <Compile Include="Net\IServerManager.cs" /> <Compile Include="Net\IServiceRequest.cs" /> <Compile Include="Net\ISessionContext.cs" /> + <Compile Include="Net\IWebSocket.cs" /> + <Compile Include="Net\IWebSocketConnection.cs" /> + <Compile Include="Net\IWebSocketListener.cs" /> <Compile Include="Net\LoggedAttribute.cs" /> <Compile Include="Net\SecurityException.cs" /> <Compile Include="Net\ServiceStackServiceRequest.cs" /> <Compile Include="Net\StaticResultOptions.cs" /> + <Compile Include="Net\WebSocketConnectEventArgs.cs" /> + <Compile Include="Net\WebSocketMessageInfo.cs" /> <Compile Include="News\INewsService.cs" /> <Compile Include="Notifications\INotificationManager.cs" /> <Compile Include="Notifications\INotificationService.cs" /> diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs b/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs new file mode 100644 index 000000000..a988c2f97 --- /dev/null +++ b/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs @@ -0,0 +1,91 @@ +using MediaBrowser.Model.Dlna; + +namespace MediaBrowser.Controller.MediaEncoding +{ + public class EncodingJobOptions + { + public string OutputContainer { get; set; } + + public long? StartTimeTicks { get; set; } + public int? Width { get; set; } + public int? Height { get; set; } + public int? MaxWidth { get; set; } + public int? MaxHeight { get; set; } + public bool Static = false; + public float? Framerate { get; set; } + public float? MaxFramerate { get; set; } + public string Profile { get; set; } + public int? Level { get; set; } + + public string DeviceId { get; set; } + public string ItemId { get; set; } + public string MediaSourceId { get; set; } + public string AudioCodec { get; set; } + + public bool EnableAutoStreamCopy { get; set; } + + public int? MaxAudioChannels { get; set; } + public int? AudioChannels { get; set; } + public int? AudioBitRate { get; set; } + public int? AudioSampleRate { get; set; } + + public DeviceProfile DeviceProfile { get; set; } + public EncodingContext Context { get; set; } + + public string VideoCodec { get; set; } + + public int? VideoBitRate { get; set; } + public int? AudioStreamIndex { get; set; } + public int? VideoStreamIndex { get; set; } + public int? SubtitleStreamIndex { get; set; } + public int? MaxRefFrames { get; set; } + public int? MaxVideoBitDepth { get; set; } + public SubtitleDeliveryMethod SubtitleMethod { get; set; } + + /// <summary> + /// Gets a value indicating whether this instance has fixed resolution. + /// </summary> + /// <value><c>true</c> if this instance has fixed resolution; otherwise, <c>false</c>.</value> + public bool HasFixedResolution + { + get + { + return Width.HasValue || Height.HasValue; + } + } + + public bool? Cabac { get; set; } + + public EncodingJobOptions() + { + + } + + public EncodingJobOptions(StreamInfo info, DeviceProfile deviceProfile) + { + OutputContainer = info.Container; + StartTimeTicks = info.StartPositionTicks; + MaxWidth = info.MaxWidth; + MaxHeight = info.MaxHeight; + MaxFramerate = info.MaxFramerate; + Profile = info.VideoProfile; + Level = info.VideoLevel; + ItemId = info.ItemId; + MediaSourceId = info.MediaSourceId; + AudioCodec = info.AudioCodec; + MaxAudioChannels = info.MaxAudioChannels; + AudioBitRate = info.AudioBitrate; + AudioSampleRate = info.TargetAudioSampleRate; + DeviceProfile = deviceProfile; + VideoCodec = info.VideoCodec; + VideoBitRate = info.VideoBitrate; + AudioStreamIndex = info.AudioStreamIndex; + SubtitleStreamIndex = info.SubtitleStreamIndex; + MaxRefFrames = info.MaxRefFrames; + MaxVideoBitDepth = info.MaxVideoBitDepth; + SubtitleMethod = info.SubtitleDeliveryMethod; + Cabac = info.Cabac; + Context = info.Context; + } + } +} diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingOptions.cs b/MediaBrowser.Controller/MediaEncoding/EncodingOptions.cs deleted file mode 100644 index 26182ebc4..000000000 --- a/MediaBrowser.Controller/MediaEncoding/EncodingOptions.cs +++ /dev/null @@ -1,80 +0,0 @@ -using MediaBrowser.Controller.Dlna; -using MediaBrowser.Model.Dlna; - -namespace MediaBrowser.Controller.MediaEncoding -{ - public class EncodingOptions - { - /// <summary> - /// Gets or sets the item identifier. - /// </summary> - /// <value>The item identifier.</value> - public string ItemId { get; set; } - - /// <summary> - /// Gets or sets the media source identifier. - /// </summary> - /// <value>The media source identifier.</value> - public string MediaSourceId { get; set; } - - /// <summary> - /// Gets or sets the device profile. - /// </summary> - /// <value>The device profile.</value> - public DeviceProfile DeviceProfile { get; set; } - - /// <summary> - /// Gets or sets the output path. - /// </summary> - /// <value>The output path.</value> - public string OutputPath { get; set; } - - /// <summary> - /// Gets or sets the container. - /// </summary> - /// <value>The container.</value> - public string Container { get; set; } - - /// <summary> - /// Gets or sets the audio codec. - /// </summary> - /// <value>The audio codec.</value> - public string AudioCodec { get; set; } - - /// <summary> - /// Gets or sets the start time ticks. - /// </summary> - /// <value>The start time ticks.</value> - public long? StartTimeTicks { get; set; } - - /// <summary> - /// Gets or sets the maximum channels. - /// </summary> - /// <value>The maximum channels.</value> - public int? MaxAudioChannels { get; set; } - - /// <summary> - /// Gets or sets the channels. - /// </summary> - /// <value>The channels.</value> - public int? AudioChannels { get; set; } - - /// <summary> - /// Gets or sets the sample rate. - /// </summary> - /// <value>The sample rate.</value> - public int? AudioSampleRate { get; set; } - - /// <summary> - /// Gets or sets the bit rate. - /// </summary> - /// <value>The bit rate.</value> - public int? AudioBitRate { get; set; } - - /// <summary> - /// Gets or sets the maximum audio bit rate. - /// </summary> - /// <value>The maximum audio bit rate.</value> - public int? MaxAudioBitRate { get; set; } - } -} diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingResult.cs b/MediaBrowser.Controller/MediaEncoding/EncodingResult.cs deleted file mode 100644 index 75ee90e42..000000000 --- a/MediaBrowser.Controller/MediaEncoding/EncodingResult.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace MediaBrowser.Controller.MediaEncoding -{ - public class EncodingResult - { - public string OutputPath { get; set; } - } -} diff --git a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs index 38c2c83c4..47544f972 100644 --- a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs +++ b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs @@ -96,5 +96,27 @@ namespace MediaBrowser.Controller.MediaEncoding /// <param name="ticks">The ticks.</param> /// <returns>System.String.</returns> string GetTimeParameter(long ticks); + + /// <summary> + /// Encodes the audio. + /// </summary> + /// <param name="options">The options.</param> + /// <param name="progress">The progress.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task.</returns> + Task<string> EncodeAudio(EncodingJobOptions options, + IProgress<double> progress, + CancellationToken cancellationToken); + + /// <summary> + /// Encodes the video. + /// </summary> + /// <param name="options">The options.</param> + /// <param name="progress">The progress.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task<System.String>.</returns> + Task<string> EncodeVideo(EncodingJobOptions options, + IProgress<double> progress, + CancellationToken cancellationToken); } } diff --git a/MediaBrowser.Controller/MediaEncoding/MediaStreamSelector.cs b/MediaBrowser.Controller/MediaEncoding/MediaStreamSelector.cs index 58a68c257..4a807df7a 100644 --- a/MediaBrowser.Controller/MediaEncoding/MediaStreamSelector.cs +++ b/MediaBrowser.Controller/MediaEncoding/MediaStreamSelector.cs @@ -34,15 +34,13 @@ namespace MediaBrowser.Controller.MediaEncoding } public static int? GetDefaultSubtitleStreamIndex(List<MediaStream> streams, - IEnumerable<string> preferredLanguages, + List<string> preferredLanguages, SubtitlePlaybackMode mode, string audioTrackLanguage) { - var languages = preferredLanguages.ToList(); - streams = GetSortedStreams(streams, MediaStreamType.Subtitle, languages).ToList(); + streams = GetSortedStreams(streams, MediaStreamType.Subtitle, preferredLanguages).ToList(); var full = streams.Where(s => !s.IsForced); - var forced = streams.Where(s => s.IsForced && string.Equals(s.Language, audioTrackLanguage, StringComparison.OrdinalIgnoreCase)); MediaStream stream = null; @@ -54,9 +52,9 @@ namespace MediaBrowser.Controller.MediaEncoding if (mode == SubtitlePlaybackMode.Default) { // if the audio language is not understood by the user, load their preferred subs, if there are any - if (!ContainsOrdinal(languages, audioTrackLanguage)) + if (!ContainsOrdinal(preferredLanguages, audioTrackLanguage)) { - stream = full.FirstOrDefault(s => ContainsOrdinal(languages, s.Language)); + stream = full.FirstOrDefault(s => ContainsOrdinal(preferredLanguages, s.Language)); } } else if (mode == SubtitlePlaybackMode.Always) @@ -66,7 +64,7 @@ namespace MediaBrowser.Controller.MediaEncoding } // load forced subs if we have found no suitable full subtitles - stream = stream ?? forced.FirstOrDefault(); + stream = stream ?? streams.FirstOrDefault(s => s.IsForced && string.Equals(s.Language, audioTrackLanguage, StringComparison.OrdinalIgnoreCase)); if (stream != null) { diff --git a/MediaBrowser.Controller/MediaEncoding/VideoEncodingOptions.cs b/MediaBrowser.Controller/MediaEncoding/VideoEncodingOptions.cs deleted file mode 100644 index 773f0ea46..000000000 --- a/MediaBrowser.Controller/MediaEncoding/VideoEncodingOptions.cs +++ /dev/null @@ -1,26 +0,0 @@ - -namespace MediaBrowser.Controller.MediaEncoding -{ - public class VideoEncodingOptions : EncodingOptions - { - public string VideoCodec { get; set; } - - public string VideoProfile { get; set; } - - public double? VideoLevel { get; set; } - - public int? VideoStreamIndex { get; set; } - - public int? AudioStreamIndex { get; set; } - - public int? SubtitleStreamIndex { get; set; } - - public int? MaxWidth { get; set; } - - public int? MaxHeight { get; set; } - - public int? Height { get; set; } - - public int? Width { get; set; } - } -} diff --git a/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs b/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs new file mode 100644 index 000000000..f1e371c1a --- /dev/null +++ b/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs @@ -0,0 +1,327 @@ +using MediaBrowser.Common.Net; +using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Net; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Controller.Net +{ + /// <summary> + /// Starts sending data over a web socket periodically when a message is received, and then stops when a corresponding stop message is received + /// </summary> + /// <typeparam name="TReturnDataType">The type of the T return data type.</typeparam> + /// <typeparam name="TStateType">The type of the T state type.</typeparam> + public abstract class BasePeriodicWebSocketListener<TReturnDataType, TStateType> : IWebSocketListener, IDisposable + where TStateType : WebSocketListenerState, new() + where TReturnDataType : class + { + /// <summary> + /// The _active connections + /// </summary> + protected readonly List<Tuple<IWebSocketConnection, CancellationTokenSource, Timer, TStateType, SemaphoreSlim>> ActiveConnections = + new List<Tuple<IWebSocketConnection, CancellationTokenSource, Timer, TStateType, SemaphoreSlim>>(); + + /// <summary> + /// Gets the name. + /// </summary> + /// <value>The name.</value> + protected abstract string Name { get; } + + /// <summary> + /// Gets the data to send. + /// </summary> + /// <param name="state">The state.</param> + /// <returns>Task{`1}.</returns> + protected abstract Task<TReturnDataType> GetDataToSend(TStateType state); + + /// <summary> + /// The logger + /// </summary> + protected ILogger Logger; + + /// <summary> + /// Initializes a new instance of the <see cref="BasePeriodicWebSocketListener{TStateType}" /> class. + /// </summary> + /// <param name="logger">The logger.</param> + /// <exception cref="System.ArgumentNullException">logger</exception> + protected BasePeriodicWebSocketListener(ILogger logger) + { + if (logger == null) + { + throw new ArgumentNullException("logger"); + } + + Logger = logger; + } + + /// <summary> + /// The null task result + /// </summary> + protected Task NullTaskResult = Task.FromResult(true); + + /// <summary> + /// Processes the message. + /// </summary> + /// <param name="message">The message.</param> + /// <returns>Task.</returns> + public Task ProcessMessage(WebSocketMessageInfo message) + { + if (message.MessageType.Equals(Name + "Start", StringComparison.OrdinalIgnoreCase)) + { + Start(message); + } + + if (message.MessageType.Equals(Name + "Stop", StringComparison.OrdinalIgnoreCase)) + { + Stop(message); + } + + return NullTaskResult; + } + + protected readonly CultureInfo UsCulture = new CultureInfo("en-US"); + + protected virtual bool SendOnTimer + { + get + { + return true; + } + } + + /// <summary> + /// Starts sending messages over a web socket + /// </summary> + /// <param name="message">The message.</param> + private void Start(WebSocketMessageInfo message) + { + var vals = message.Data.Split(','); + + var dueTimeMs = long.Parse(vals[0], UsCulture); + var periodMs = long.Parse(vals[1], UsCulture); + + var cancellationTokenSource = new CancellationTokenSource(); + + Logger.Info("{1} Begin transmitting over websocket to {0}", message.Connection.RemoteEndPoint, GetType().Name); + + var timer = SendOnTimer ? + new Timer(TimerCallback, message.Connection, Timeout.Infinite, Timeout.Infinite) : + null; + + var state = new TStateType + { + IntervalMs = periodMs, + InitialDelayMs = dueTimeMs + }; + + var semaphore = new SemaphoreSlim(1, 1); + + lock (ActiveConnections) + { + ActiveConnections.Add(new Tuple<IWebSocketConnection, CancellationTokenSource, Timer, TStateType, SemaphoreSlim>(message.Connection, cancellationTokenSource, timer, state, semaphore)); + } + + if (timer != null) + { + timer.Change(TimeSpan.FromMilliseconds(dueTimeMs), TimeSpan.FromMilliseconds(periodMs)); + } + } + + /// <summary> + /// Timers the callback. + /// </summary> + /// <param name="state">The state.</param> + private void TimerCallback(object state) + { + var connection = (IWebSocketConnection)state; + + Tuple<IWebSocketConnection, CancellationTokenSource, Timer, TStateType, SemaphoreSlim> tuple; + + lock (ActiveConnections) + { + tuple = ActiveConnections.FirstOrDefault(c => c.Item1 == connection); + } + + if (tuple == null) + { + return; + } + + if (connection.State != WebSocketState.Open || tuple.Item2.IsCancellationRequested) + { + DisposeConnection(tuple); + return; + } + + SendData(tuple); + } + + protected void SendData(bool force) + { + List<Tuple<IWebSocketConnection, CancellationTokenSource, Timer, TStateType, SemaphoreSlim>> tuples; + + lock (ActiveConnections) + { + tuples = ActiveConnections + .Where(c => + { + if (c.Item1.State == WebSocketState.Open && !c.Item2.IsCancellationRequested) + { + var state = c.Item4; + + if (force || (DateTime.UtcNow - state.DateLastSendUtc).TotalMilliseconds >= state.IntervalMs) + { + return true; + } + } + + return false; + }) + .ToList(); + } + + foreach (var tuple in tuples) + { + SendData(tuple); + } + } + + private async void SendData(Tuple<IWebSocketConnection, CancellationTokenSource, Timer, TStateType, SemaphoreSlim> tuple) + { + var connection = tuple.Item1; + + try + { + await tuple.Item5.WaitAsync(tuple.Item2.Token).ConfigureAwait(false); + + var state = tuple.Item4; + + var data = await GetDataToSend(state).ConfigureAwait(false); + + if (data != null) + { + await connection.SendAsync(new WebSocketMessage<TReturnDataType> + { + MessageType = Name, + Data = data + + }, tuple.Item2.Token).ConfigureAwait(false); + + state.DateLastSendUtc = DateTime.UtcNow; + } + + tuple.Item5.Release(); + } + catch (OperationCanceledException) + { + if (tuple.Item2.IsCancellationRequested) + { + DisposeConnection(tuple); + } + } + catch (Exception ex) + { + Logger.ErrorException("Error sending web socket message {0}", ex, Name); + DisposeConnection(tuple); + } + } + + /// <summary> + /// Stops sending messages over a web socket + /// </summary> + /// <param name="message">The message.</param> + private void Stop(WebSocketMessageInfo message) + { + lock (ActiveConnections) + { + var connection = ActiveConnections.FirstOrDefault(c => c.Item1 == message.Connection); + + if (connection != null) + { + DisposeConnection(connection); + } + } + } + + /// <summary> + /// Disposes the connection. + /// </summary> + /// <param name="connection">The connection.</param> + private void DisposeConnection(Tuple<IWebSocketConnection, CancellationTokenSource, Timer, TStateType, SemaphoreSlim> connection) + { + Logger.Info("{1} stop transmitting over websocket to {0}", connection.Item1.RemoteEndPoint, GetType().Name); + + var timer = connection.Item3; + + if (timer != null) + { + try + { + timer.Dispose(); + } + catch (ObjectDisposedException) + { + + } + } + + try + { + connection.Item2.Cancel(); + connection.Item2.Dispose(); + } + catch (ObjectDisposedException) + { + + } + + try + { + connection.Item5.Dispose(); + } + catch (ObjectDisposedException) + { + + } + + ActiveConnections.Remove(connection); + } + + /// <summary> + /// Releases unmanaged and - optionally - managed resources. + /// </summary> + /// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param> + protected virtual void Dispose(bool dispose) + { + if (dispose) + { + lock (ActiveConnections) + { + foreach (var connection in ActiveConnections.ToList()) + { + DisposeConnection(connection); + } + } + } + } + + /// <summary> + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// </summary> + public void Dispose() + { + Dispose(true); + } + } + + public class WebSocketListenerState + { + public DateTime DateLastSendUtc { get; set; } + public long InitialDelayMs { get; set; } + public long IntervalMs { get; set; } + } +} diff --git a/MediaBrowser.Controller/Net/IWebSocket.cs b/MediaBrowser.Controller/Net/IWebSocket.cs new file mode 100644 index 000000000..b88f2c389 --- /dev/null +++ b/MediaBrowser.Controller/Net/IWebSocket.cs @@ -0,0 +1,54 @@ +using MediaBrowser.Model.Net; +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Controller.Net +{ + /// <summary> + /// Interface IWebSocket + /// </summary> + public interface IWebSocket : IDisposable + { + /// <summary> + /// Occurs when [closed]. + /// </summary> + event EventHandler<EventArgs> Closed; + + /// <summary> + /// Gets or sets the state. + /// </summary> + /// <value>The state.</value> + WebSocketState State { get; } + + /// <summary> + /// Gets or sets the receive action. + /// </summary> + /// <value>The receive action.</value> + Action<byte[]> OnReceiveBytes { get; set; } + + /// <summary> + /// Gets or sets the on receive. + /// </summary> + /// <value>The on receive.</value> + Action<string> OnReceive { get; set; } + + /// <summary> + /// Sends the async. + /// </summary> + /// <param name="bytes">The bytes.</param> + /// <param name="endOfMessage">if set to <c>true</c> [end of message].</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task.</returns> + Task SendAsync(byte[] bytes, bool endOfMessage, CancellationToken cancellationToken); + + /// <summary> + /// Sends the asynchronous. + /// </summary> + /// <param name="text">The text.</param> + /// <param name="endOfMessage">if set to <c>true</c> [end of message].</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task.</returns> + Task SendAsync(string text, bool endOfMessage, CancellationToken cancellationToken); + } +} diff --git a/MediaBrowser.Controller/Net/IWebSocketConnection.cs b/MediaBrowser.Controller/Net/IWebSocketConnection.cs new file mode 100644 index 000000000..37fd6708d --- /dev/null +++ b/MediaBrowser.Controller/Net/IWebSocketConnection.cs @@ -0,0 +1,72 @@ +using MediaBrowser.Model.Net; +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Controller.Net +{ + public interface IWebSocketConnection : IDisposable + { + /// <summary> + /// Occurs when [closed]. + /// </summary> + event EventHandler<EventArgs> Closed; + + /// <summary> + /// Gets the id. + /// </summary> + /// <value>The id.</value> + Guid Id { get; } + + /// <summary> + /// Gets the last activity date. + /// </summary> + /// <value>The last activity date.</value> + DateTime LastActivityDate { get; } + + /// <summary> + /// Gets or sets the receive action. + /// </summary> + /// <value>The receive action.</value> + Action<WebSocketMessageInfo> OnReceive { get; set; } + + /// <summary> + /// Gets the state. + /// </summary> + /// <value>The state.</value> + WebSocketState State { get; } + + /// <summary> + /// Gets the remote end point. + /// </summary> + /// <value>The remote end point.</value> + string RemoteEndPoint { get; } + + /// <summary> + /// Sends a message asynchronously. + /// </summary> + /// <typeparam name="T"></typeparam> + /// <param name="message">The message.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task.</returns> + /// <exception cref="System.ArgumentNullException">message</exception> + Task SendAsync<T>(WebSocketMessage<T> message, CancellationToken cancellationToken); + + /// <summary> + /// Sends a message asynchronously. + /// </summary> + /// <param name="buffer">The buffer.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task.</returns> + Task SendAsync(byte[] buffer, CancellationToken cancellationToken); + + /// <summary> + /// Sends a message asynchronously. + /// </summary> + /// <param name="text">The text.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task.</returns> + /// <exception cref="System.ArgumentNullException">buffer</exception> + Task SendAsync(string text, CancellationToken cancellationToken); + } +}
\ No newline at end of file diff --git a/MediaBrowser.Controller/Net/IWebSocketListener.cs b/MediaBrowser.Controller/Net/IWebSocketListener.cs new file mode 100644 index 000000000..2b4fc7676 --- /dev/null +++ b/MediaBrowser.Controller/Net/IWebSocketListener.cs @@ -0,0 +1,18 @@ +using MediaBrowser.Common.Net; +using System.Threading.Tasks; + +namespace MediaBrowser.Controller.Net +{ + /// <summary> + ///This is an interface for listening to messages coming through a web socket connection + /// </summary> + public interface IWebSocketListener + { + /// <summary> + /// Processes the message. + /// </summary> + /// <param name="message">The message.</param> + /// <returns>Task.</returns> + Task ProcessMessage(WebSocketMessageInfo message); + } +} diff --git a/MediaBrowser.Controller/Net/WebSocketConnectEventArgs.cs b/MediaBrowser.Controller/Net/WebSocketConnectEventArgs.cs new file mode 100644 index 000000000..394fcd92f --- /dev/null +++ b/MediaBrowser.Controller/Net/WebSocketConnectEventArgs.cs @@ -0,0 +1,21 @@ +using System; + +namespace MediaBrowser.Controller.Net +{ + /// <summary> + /// Class WebSocketConnectEventArgs + /// </summary> + public class WebSocketConnectEventArgs : EventArgs + { + /// <summary> + /// Gets or sets the web socket. + /// </summary> + /// <value>The web socket.</value> + public IWebSocket WebSocket { get; set; } + /// <summary> + /// Gets or sets the endpoint. + /// </summary> + /// <value>The endpoint.</value> + public string Endpoint { get; set; } + } +} diff --git a/MediaBrowser.Controller/Net/WebSocketMessageInfo.cs b/MediaBrowser.Controller/Net/WebSocketMessageInfo.cs new file mode 100644 index 000000000..332f16420 --- /dev/null +++ b/MediaBrowser.Controller/Net/WebSocketMessageInfo.cs @@ -0,0 +1,16 @@ +using MediaBrowser.Model.Net; + +namespace MediaBrowser.Controller.Net +{ + /// <summary> + /// Class WebSocketMessageInfo + /// </summary> + public class WebSocketMessageInfo : WebSocketMessage<string> + { + /// <summary> + /// Gets or sets the connection. + /// </summary> + /// <value>The connection.</value> + public IWebSocketConnection Connection { get; set; } + } +}
\ No newline at end of file diff --git a/MediaBrowser.Controller/Providers/DirectoryService.cs b/MediaBrowser.Controller/Providers/DirectoryService.cs index 74a9c6606..06ea7be02 100644 --- a/MediaBrowser.Controller/Providers/DirectoryService.cs +++ b/MediaBrowser.Controller/Providers/DirectoryService.cs @@ -46,6 +46,11 @@ namespace MediaBrowser.Controller.Providers private Dictionary<string, FileSystemInfo> GetFileSystemDictionary(string path, bool clearCache) { + if (string.IsNullOrWhiteSpace(path)) + { + throw new ArgumentNullException("path"); + } + Dictionary<string, FileSystemInfo> entries; if (clearCache) diff --git a/MediaBrowser.Controller/Sync/ISyncManager.cs b/MediaBrowser.Controller/Sync/ISyncManager.cs index 47339f677..59136c0e6 100644 --- a/MediaBrowser.Controller/Sync/ISyncManager.cs +++ b/MediaBrowser.Controller/Sync/ISyncManager.cs @@ -2,6 +2,7 @@ using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Querying; using MediaBrowser.Model.Sync; +using MediaBrowser.Model.Users; using System.Collections.Generic; using System.Threading.Tasks; @@ -20,7 +21,7 @@ namespace MediaBrowser.Controller.Sync /// Gets the jobs. /// </summary> /// <returns>QueryResult<SyncJob>.</returns> - QueryResult<SyncJob> GetJobs(SyncJobQuery query); + Task<QueryResult<SyncJob>> GetJobs(SyncJobQuery query); /// <summary> /// Gets the job items. @@ -37,6 +38,13 @@ namespace MediaBrowser.Controller.Sync SyncJob GetJob(string id); /// <summary> + /// Updates the job. + /// </summary> + /// <param name="job">The job.</param> + /// <returns>Task.</returns> + Task UpdateJob(SyncJob job); + + /// <summary> /// Cancels the job. /// </summary> /// <param name="id">The identifier.</param> @@ -80,5 +88,26 @@ namespace MediaBrowser.Controller.Sync /// <param name="id">The identifier.</param> /// <returns>SyncJobItem.</returns> SyncJobItem GetJobItem(string id); + + /// <summary> + /// Reports the offline action. + /// </summary> + /// <param name="action">The action.</param> + /// <returns>Task.</returns> + Task ReportOfflineAction(UserAction action); + + /// <summary> + /// Gets the ready synchronize items. + /// </summary> + /// <param name="targetId">The target identifier.</param> + /// <returns>List<SyncedItem>.</returns> + List<SyncedItem> GetReadySyncItems(string targetId); + + /// <summary> + /// Synchronizes the data. + /// </summary> + /// <param name="request">The request.</param> + /// <returns>Task<SyncDataResponse>.</returns> + Task<SyncDataResponse> SyncData(SyncDataRequest request); } } diff --git a/MediaBrowser.Controller/Sync/ISyncProvider.cs b/MediaBrowser.Controller/Sync/ISyncProvider.cs index 89f61b80e..af08edb5e 100644 --- a/MediaBrowser.Controller/Sync/ISyncProvider.cs +++ b/MediaBrowser.Controller/Sync/ISyncProvider.cs @@ -19,10 +19,22 @@ namespace MediaBrowser.Controller.Sync IEnumerable<SyncTarget> GetSyncTargets(); /// <summary> + /// Gets the synchronize targets. + /// </summary> + /// <param name="userId">The user identifier.</param> + /// <returns>IEnumerable<SyncTarget>.</returns> + IEnumerable<SyncTarget> GetSyncTargets(string userId); + + /// <summary> /// Gets the device profile. /// </summary> /// <param name="target">The target.</param> /// <returns>DeviceProfile.</returns> DeviceProfile GetDeviceProfile(SyncTarget target); } + + public interface IHasUniqueTargetIds + { + + } } |
