diff options
Diffstat (limited to 'MediaBrowser.Controller')
36 files changed, 808 insertions, 807 deletions
diff --git a/MediaBrowser.Controller/Channels/Channel.cs b/MediaBrowser.Controller/Channels/Channel.cs index b6514ca0a6..f746d87fff 100644 --- a/MediaBrowser.Controller/Channels/Channel.cs +++ b/MediaBrowser.Controller/Channels/Channel.cs @@ -5,7 +5,6 @@ using System; using System.Linq; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Model.Users; namespace MediaBrowser.Controller.Channels { @@ -15,19 +14,9 @@ namespace MediaBrowser.Controller.Channels public override bool IsVisible(User user) { - if (user.Policy.BlockedChannels != null) + if (!user.Policy.EnableAllChannels && !user.Policy.EnabledChannels.Contains(Id.ToString("N"), StringComparer.OrdinalIgnoreCase)) { - if (user.Policy.BlockedChannels.Contains(Id.ToString("N"), StringComparer.OrdinalIgnoreCase)) - { - return false; - } - } - else - { - if (!user.Policy.EnableAllChannels && !user.Policy.EnabledChannels.Contains(Id.ToString("N"), StringComparer.OrdinalIgnoreCase)) - { - return false; - } + return false; } return base.IsVisible(user); diff --git a/MediaBrowser.Controller/Channels/ChannelAudioItem.cs b/MediaBrowser.Controller/Channels/ChannelAudioItem.cs index 8d90246765..82fe66c7ba 100644 --- a/MediaBrowser.Controller/Channels/ChannelAudioItem.cs +++ b/MediaBrowser.Controller/Channels/ChannelAudioItem.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Model.Channels; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Dto; @@ -100,5 +101,10 @@ namespace MediaBrowser.Controller.Channels { return false; } + + public override bool IsVisibleStandalone(User user) + { + return IsVisibleStandaloneInternal(user, false) && ChannelVideoItem.IsChannelVisible(this, user); + } } } diff --git a/MediaBrowser.Controller/Channels/ChannelFolderItem.cs b/MediaBrowser.Controller/Channels/ChannelFolderItem.cs index 7ba73d126c..641d37161a 100644 --- a/MediaBrowser.Controller/Channels/ChannelFolderItem.cs +++ b/MediaBrowser.Controller/Channels/ChannelFolderItem.cs @@ -80,5 +80,10 @@ namespace MediaBrowser.Controller.Channels { return false; } + + public override bool IsVisibleStandalone(User user) + { + return IsVisibleStandaloneInternal(user, false) && ChannelVideoItem.IsChannelVisible(this, user); + } } } diff --git a/MediaBrowser.Controller/Channels/ChannelVideoItem.cs b/MediaBrowser.Controller/Channels/ChannelVideoItem.cs index 8eec2021b5..ef3cc7cbab 100644 --- a/MediaBrowser.Controller/Channels/ChannelVideoItem.cs +++ b/MediaBrowser.Controller/Channels/ChannelVideoItem.cs @@ -130,5 +130,17 @@ namespace MediaBrowser.Controller.Channels { return false; } + + public override bool IsVisibleStandalone(User user) + { + return IsVisibleStandaloneInternal(user, false) && IsChannelVisible(this, user); + } + + internal static bool IsChannelVisible(IChannelItem item, User user) + { + var channel = ChannelManager.GetChannel(item.ChannelId); + + return channel.IsVisible(user); + } } } diff --git a/MediaBrowser.Controller/Drawing/IImageProcessor.cs b/MediaBrowser.Controller/Drawing/IImageProcessor.cs index 6fafc2b464..685d2706da 100644 --- a/MediaBrowser.Controller/Drawing/IImageProcessor.cs +++ b/MediaBrowser.Controller/Drawing/IImageProcessor.cs @@ -14,6 +14,12 @@ namespace MediaBrowser.Controller.Drawing public interface IImageProcessor { /// <summary> + /// Gets the supported input formats. + /// </summary> + /// <value>The supported input formats.</value> + string[] SupportedInputFormats { get; } + + /// <summary> /// Gets the image enhancers. /// </summary> /// <value>The image enhancers.</value> @@ -93,5 +99,11 @@ namespace MediaBrowser.Controller.Drawing /// </summary> /// <returns>ImageOutputFormat[].</returns> ImageFormat[] GetSupportedImageOutputFormats(); + + /// <summary> + /// Creates the image collage. + /// </summary> + /// <param name="options">The options.</param> + void CreateImageCollage(ImageCollageOptions options); } } diff --git a/MediaBrowser.Controller/Drawing/ImageCollageOptions.cs b/MediaBrowser.Controller/Drawing/ImageCollageOptions.cs new file mode 100644 index 0000000000..edc4f85586 --- /dev/null +++ b/MediaBrowser.Controller/Drawing/ImageCollageOptions.cs @@ -0,0 +1,32 @@ + +namespace MediaBrowser.Controller.Drawing +{ + public class ImageCollageOptions + { + /// <summary> + /// Gets or sets the input paths. + /// </summary> + /// <value>The input paths.</value> + public string[] InputPaths { 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 width. + /// </summary> + /// <value>The width.</value> + public int Width { get; set; } + /// <summary> + /// Gets or sets the height. + /// </summary> + /// <value>The height.</value> + public int Height { get; set; } + /// <summary> + /// Gets or sets the text. + /// </summary> + /// <value>The text.</value> + public string Text { get; set; } + } +} diff --git a/MediaBrowser.Controller/Dto/IDtoService.cs b/MediaBrowser.Controller/Dto/IDtoService.cs index ea311d9937..5ec8f274be 100644 --- a/MediaBrowser.Controller/Dto/IDtoService.cs +++ b/MediaBrowser.Controller/Dto/IDtoService.cs @@ -36,6 +36,14 @@ namespace MediaBrowser.Controller.Dto BaseItemDto GetBaseItemDto(BaseItem item, List<ItemFields> fields, User user = null, BaseItem owner = null); /// <summary> + /// Fills the synchronize information. + /// </summary> + /// <param name="dtos">The dtos.</param> + /// <param name="options">The options.</param> + /// <param name="user">The user.</param> + void FillSyncInfo(IEnumerable<IHasSyncInfo> dtos, DtoOptions options, User user); + + /// <summary> /// Gets the base item dto. /// </summary> /// <param name="item">The item.</param> diff --git a/MediaBrowser.Controller/Entities/Audio/IHasAlbumArtist.cs b/MediaBrowser.Controller/Entities/Audio/IHasAlbumArtist.cs index 56921409ae..254f90376d 100644 --- a/MediaBrowser.Controller/Entities/Audio/IHasAlbumArtist.cs +++ b/MediaBrowser.Controller/Entities/Audio/IHasAlbumArtist.cs @@ -1,6 +1,5 @@ -using System; +using MediaBrowser.Controller.Library; using System.Collections.Generic; -using System.Linq; namespace MediaBrowser.Controller.Entities.Audio { @@ -20,11 +19,11 @@ namespace MediaBrowser.Controller.Entities.Audio { public static bool HasArtist(this IHasArtist hasArtist, string artist) { - return hasArtist.Artists.Contains(artist, StringComparer.OrdinalIgnoreCase); + return NameExtensions.EqualsAny(hasArtist.Artists, artist); } public static bool HasAnyArtist(this IHasArtist hasArtist, string artist) { - return hasArtist.AllArtists.Contains(artist, StringComparer.OrdinalIgnoreCase); + return NameExtensions.EqualsAny(hasArtist.AllArtists, artist); } } } diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index cdb52ec668..b7322494d6 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Common.Extensions; +using System.Globalization; +using MediaBrowser.Common.Extensions; using MediaBrowser.Common.IO; using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Collections; @@ -44,7 +45,7 @@ namespace MediaBrowser.Controller.Entities /// <summary> /// The supported image extensions /// </summary> - public static readonly string[] SupportedImageExtensions = { ".png", ".jpg", ".jpeg", ".tbn" }; + public static readonly string[] SupportedImageExtensions = { ".png", ".jpg", ".jpeg" }; public static readonly List<string> SupportedImageExtensionsList = SupportedImageExtensions.ToList(); @@ -1144,6 +1145,11 @@ namespace MediaBrowser.Controller.Entities public virtual bool IsVisibleStandalone(User user) { + return IsVisibleStandaloneInternal(user, true); + } + + protected bool IsVisibleStandaloneInternal(User user, bool checkFolders) + { if (!IsVisible(user)) { return false; @@ -1154,7 +1160,23 @@ namespace MediaBrowser.Controller.Entities return false; } - // TODO: Need some work here, e.g. is in user library, for channels, can user access channel, etc. + if (checkFolders) + { + var topParent = Parents.LastOrDefault() ?? this; + + if (string.IsNullOrWhiteSpace(topParent.Path)) + { + return true; + } + + var userCollectionFolders = user.RootFolder.GetChildren(user, true).Select(i => i.Id).ToList(); + var itemCollectionFolders = LibraryManager.GetCollectionFolders(this).Select(i => i.Id); + + if (!itemCollectionFolders.Any(userCollectionFolders.Contains)) + { + return false; + } + } return true; } @@ -1219,18 +1241,6 @@ namespace MediaBrowser.Controller.Entities private BaseItem FindLinkedChild(LinkedChild info) { - if (!string.IsNullOrWhiteSpace(info.ItemName)) - { - if (string.Equals(info.ItemType, "musicgenre", StringComparison.OrdinalIgnoreCase)) - { - return LibraryManager.GetMusicGenre(info.ItemName); - } - if (string.Equals(info.ItemType, "musicartist", StringComparison.OrdinalIgnoreCase)) - { - return LibraryManager.GetArtist(info.ItemName); - } - } - if (!string.IsNullOrEmpty(info.Path)) { var itemByPath = LibraryManager.RootFolder.FindByPath(info.Path); @@ -1243,23 +1253,6 @@ namespace MediaBrowser.Controller.Entities return itemByPath; } - if (!string.IsNullOrWhiteSpace(info.ItemName) && !string.IsNullOrWhiteSpace(info.ItemType)) - { - return LibraryManager.RootFolder.GetRecursiveChildren(i => - { - if (string.Equals(i.Name, info.ItemName, StringComparison.OrdinalIgnoreCase)) - { - if (string.Equals(i.GetType().Name, info.ItemType, StringComparison.OrdinalIgnoreCase)) - { - return true; - } - } - - return false; - - }).FirstOrDefault(); - } - return null; } @@ -1540,7 +1533,7 @@ namespace MediaBrowser.Controller.Entities } // Remove it from the item - ImageInfos.Remove(info); + RemoveImage(info); // Delete the source file var currentFile = new FileInfo(info.Path); @@ -1559,6 +1552,11 @@ namespace MediaBrowser.Controller.Entities return UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None); } + public void RemoveImage(ItemImageInfo image) + { + ImageInfos.Remove(image); + } + public virtual Task UpdateToRepository(ItemUpdateType updateReason, CancellationToken cancellationToken) { return LibraryManager.UpdateItem(this, updateReason, cancellationToken); @@ -1651,7 +1649,7 @@ namespace MediaBrowser.Controller.Entities public bool AddImages(ImageType imageType, IEnumerable<FileInfo> images) { - return AddImages(imageType, images.Cast<FileSystemInfo>()); + return AddImages(imageType, images.Cast<FileSystemInfo>().ToList()); } /// <summary> @@ -1661,7 +1659,7 @@ namespace MediaBrowser.Controller.Entities /// <param name="images">The images.</param> /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns> /// <exception cref="System.ArgumentException">Cannot call AddImages with chapter images</exception> - public bool AddImages(ImageType imageType, IEnumerable<FileSystemInfo> images) + public bool AddImages(ImageType imageType, List<FileSystemInfo> images) { if (imageType == ImageType.Chapter) { @@ -1672,6 +1670,7 @@ namespace MediaBrowser.Controller.Entities .ToList(); var newImageList = new List<FileSystemInfo>(); + var imageAdded = false; foreach (var newImage in images) { @@ -1686,14 +1685,26 @@ namespace MediaBrowser.Controller.Entities if (existing == null) { newImageList.Add(newImage); + imageAdded = true; } else { existing.DateModified = FileSystem.GetLastWriteTimeUtc(newImage); - existing.Length = ((FileInfo) newImage).Length; + existing.Length = ((FileInfo)newImage).Length; } } + if (imageAdded || images.Count != existingImages.Count) + { + var newImagePaths = images.Select(i => i.FullName).ToList(); + + var deleted = existingImages + .Where(i => !newImagePaths.Contains(i.Path, StringComparer.OrdinalIgnoreCase) && !File.Exists(i.Path)) + .ToList(); + + ImageInfos = ImageInfos.Except(deleted).ToList(); + } + ImageInfos.AddRange(newImageList.Select(i => GetImageInfo(i, imageType))); return newImageList.Count > 0; @@ -1882,5 +1893,18 @@ namespace MediaBrowser.Controller.Entities return video.RefreshMetadata(newOptions, cancellationToken); } + + public string GetEtag() + { + return string.Join("|", GetEtagValues().ToArray()).GetMD5().ToString("N"); + } + + protected virtual List<string> GetEtagValues() + { + return new List<string> + { + DateLastSaved.Ticks.ToString(CultureInfo.InvariantCulture) + }; + } } } diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index cffc0989a5..61e5acdb3f 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -334,22 +334,9 @@ namespace MediaBrowser.Controller.Entities { if (this is ICollectionFolder && !(this is BasePluginFolder)) { - if (user.Policy.BlockedMediaFolders != null) + if (!user.Policy.EnableAllFolders && !user.Policy.EnabledFolders.Contains(Id.ToString("N"), StringComparer.OrdinalIgnoreCase)) { - if (user.Policy.BlockedMediaFolders.Contains(Id.ToString("N"), StringComparer.OrdinalIgnoreCase) || - - // Backwards compatibility - user.Policy.BlockedMediaFolders.Contains(Name, StringComparer.OrdinalIgnoreCase)) - { - return false; - } - } - else - { - if (!user.Policy.EnableAllFolders && !user.Policy.EnabledFolders.Contains(Id.ToString("N"), StringComparer.OrdinalIgnoreCase)) - { - return false; - } + return false; } } @@ -1004,8 +991,9 @@ namespace MediaBrowser.Controller.Entities } var locations = user.RootFolder - .GetChildren(user, true) + .Children .OfType<CollectionFolder>() + .Where(i => i.IsVisible(user)) .SelectMany(i => i.PhysicalLocations) .ToList(); diff --git a/MediaBrowser.Controller/Entities/IHasImages.cs b/MediaBrowser.Controller/Entities/IHasImages.cs index 00a42271b5..1871d7b68a 100644 --- a/MediaBrowser.Controller/Entities/IHasImages.cs +++ b/MediaBrowser.Controller/Entities/IHasImages.cs @@ -141,7 +141,7 @@ namespace MediaBrowser.Controller.Entities /// <param name="imageType">Type of the image.</param> /// <param name="images">The images.</param> /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns> - bool AddImages(ImageType imageType, IEnumerable<FileSystemInfo> images); + bool AddImages(ImageType imageType, List<FileSystemInfo> images); /// <summary> /// Determines whether [is save local metadata enabled]. @@ -190,6 +190,12 @@ namespace MediaBrowser.Controller.Entities /// </summary> /// <returns><c>true</c> if [is internet metadata enabled]; otherwise, <c>false</c>.</returns> bool IsInternetMetadataEnabled(); + + /// <summary> + /// Removes the image. + /// </summary> + /// <param name="image">The image.</param> + void RemoveImage(ItemImageInfo image); } public static class HasImagesExtensions diff --git a/MediaBrowser.Controller/Entities/LinkedChild.cs b/MediaBrowser.Controller/Entities/LinkedChild.cs index 949c9741b1..ac13657b94 100644 --- a/MediaBrowser.Controller/Entities/LinkedChild.cs +++ b/MediaBrowser.Controller/Entities/LinkedChild.cs @@ -9,9 +9,6 @@ namespace MediaBrowser.Controller.Entities public string Path { get; set; } public LinkedChildType Type { get; set; } - public string ItemName { get; set; } - public string ItemType { get; set; } - [IgnoreDataMember] public string Id { get; set; } diff --git a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs index 0778643da5..02e9d4cf9e 100644 --- a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs +++ b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs @@ -175,19 +175,19 @@ namespace MediaBrowser.Controller.Entities.Movies public override bool IsVisible(User user) { - if (base.IsVisible(user)) - { - var userId = user.Id.ToString("N"); - - // 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; - } + var userId = user.Id.ToString("N"); + // 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 true; } + if (base.IsVisible(user)) + { + return GetChildren(user, true).Any(); + } + return false; } } diff --git a/MediaBrowser.Controller/Entities/PhotoAlbum.cs b/MediaBrowser.Controller/Entities/PhotoAlbum.cs index 24ebf88153..5b48a70e9c 100644 --- a/MediaBrowser.Controller/Entities/PhotoAlbum.cs +++ b/MediaBrowser.Controller/Entities/PhotoAlbum.cs @@ -1,11 +1,15 @@ -using MediaBrowser.Model.Configuration; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Configuration; +using MediaBrowser.Model.Users; +using System; using System.Linq; using System.Runtime.Serialization; -using MediaBrowser.Model.Users; +using System.Threading; +using System.Threading.Tasks; namespace MediaBrowser.Controller.Entities { - public class PhotoAlbum : Folder + public class PhotoAlbum : Folder, IMetadataContainer { public override bool SupportsLocalMetadata { @@ -28,5 +32,31 @@ namespace MediaBrowser.Controller.Entities { return config.BlockUnratedItems.Contains(UnratedItem.Other); } + + public async Task RefreshAllMetadata(MetadataRefreshOptions refreshOptions, IProgress<double> progress, CancellationToken cancellationToken) + { + var items = GetRecursiveChildren().ToList(); + + var totalItems = items.Count; + var numComplete = 0; + + // Refresh songs + foreach (var item in items) + { + cancellationToken.ThrowIfCancellationRequested(); + + await item.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false); + + numComplete++; + double percent = numComplete; + percent /= totalItems; + progress.Report(percent * 100); + } + + // Refresh current item + await RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false); + + progress.Report(100); + } } } diff --git a/MediaBrowser.Controller/Entities/UserViewBuilder.cs b/MediaBrowser.Controller/Entities/UserViewBuilder.cs index 0e602dabe2..63ce223afe 100644 --- a/MediaBrowser.Controller/Entities/UserViewBuilder.cs +++ b/MediaBrowser.Controller/Entities/UserViewBuilder.cs @@ -50,6 +50,16 @@ namespace MediaBrowser.Controller.Entities { var user = query.User; + if (query.IncludeItemTypes != null && + query.IncludeItemTypes.Length == 1 && + string.Equals(query.IncludeItemTypes[0], "Playlist", StringComparison.OrdinalIgnoreCase)) + { + if (!string.Equals(viewType, CollectionType.Playlists, StringComparison.OrdinalIgnoreCase)) + { + return await FindPlaylists(queryParent, user, query).ConfigureAwait(false); + } + } + switch (viewType) { case CollectionType.Channels: @@ -107,9 +117,7 @@ namespace MediaBrowser.Controller.Entities case CollectionType.LiveTv: { - var result = await GetLiveTvFolders(user).ConfigureAwait(false); - - return GetResult(result, queryParent, query); + return await GetLiveTvView(queryParent, user, query).ConfigureAwait(false); } case CollectionType.Books: @@ -205,6 +213,9 @@ namespace MediaBrowser.Controller.Entities case SpecialFolder.MusicLatest: return GetMusicLatest(queryParent, user, query); + case SpecialFolder.MusicPlaylists: + return await GetMusicPlaylists(queryParent, user, query).ConfigureAwait(false); + case SpecialFolder.MusicAlbums: return GetMusicAlbums(queryParent, user, query); @@ -240,6 +251,16 @@ namespace MediaBrowser.Controller.Entities } } + private async Task<QueryResult<BaseItem>> FindPlaylists(Folder parent, User user, InternalItemsQuery query) + { + var collectionFolders = user.RootFolder.GetChildren(user, true).Select(i => i.Id).ToList(); + + var list = _playlistManager.GetPlaylists(user.Id.ToString("N")) + .Where(i => i.GetChildren(user, true).Any(media => _libraryManager.GetCollectionFolders(media).Select(c => c.Id).Any(collectionFolders.Contains))); + + return GetResult(list, parent, query); + } + private int GetSpecialItemsLimit() { return 50; @@ -257,12 +278,13 @@ namespace MediaBrowser.Controller.Entities var list = new List<BaseItem>(); list.Add(await GetUserView(SpecialFolder.MusicLatest, user, "0", parent).ConfigureAwait(false)); - list.Add(await GetUserView(SpecialFolder.MusicAlbums, user, "1", parent).ConfigureAwait(false)); - list.Add(await GetUserView(SpecialFolder.MusicAlbumArtists, user, "2", parent).ConfigureAwait(false)); - list.Add(await GetUserView(SpecialFolder.MusicArtists, user, "3", parent).ConfigureAwait(false)); - list.Add(await GetUserView(SpecialFolder.MusicSongs, user, "4", parent).ConfigureAwait(false)); - list.Add(await GetUserView(SpecialFolder.MusicGenres, user, "5", parent).ConfigureAwait(false)); - list.Add(await GetUserView(SpecialFolder.MusicFavorites, user, "6", parent).ConfigureAwait(false)); + list.Add(await GetUserView(SpecialFolder.MusicPlaylists, user, "1", parent).ConfigureAwait(false)); + list.Add(await GetUserView(SpecialFolder.MusicAlbums, user, "2", parent).ConfigureAwait(false)); + list.Add(await GetUserView(SpecialFolder.MusicAlbumArtists, user, "3", parent).ConfigureAwait(false)); + //list.Add(await GetUserView(SpecialFolder.MusicArtists, user, "4", parent).ConfigureAwait(false)); + list.Add(await GetUserView(SpecialFolder.MusicSongs, user, "5", parent).ConfigureAwait(false)); + list.Add(await GetUserView(SpecialFolder.MusicGenres, user, "6", parent).ConfigureAwait(false)); + list.Add(await GetUserView(SpecialFolder.MusicFavorites, user, "7", parent).ConfigureAwait(false)); return GetResult(list, parent, query); } @@ -283,7 +305,7 @@ namespace MediaBrowser.Controller.Entities var tasks = GetRecursiveChildren(parent, user, new[] { CollectionType.Music, CollectionType.MusicVideos }) .Where(i => !i.IsFolder) .SelectMany(i => i.Genres) - .Distinct(StringComparer.OrdinalIgnoreCase) + .DistinctNames() .Select(i => { try @@ -313,7 +335,7 @@ namespace MediaBrowser.Controller.Entities .Where(i => i.Genres.Contains(displayParent.Name, StringComparer.OrdinalIgnoreCase)) .OfType<IHasAlbumArtist>() .SelectMany(i => i.AlbumArtists) - .Distinct(StringComparer.OrdinalIgnoreCase) + .DistinctNames() .Select(i => { try @@ -337,7 +359,7 @@ namespace MediaBrowser.Controller.Entities .Where(i => !i.IsFolder) .OfType<IHasAlbumArtist>() .SelectMany(i => i.AlbumArtists) - .Distinct(StringComparer.OrdinalIgnoreCase) + .DistinctNames() .Select(i => { try @@ -361,7 +383,7 @@ namespace MediaBrowser.Controller.Entities .Where(i => !i.IsFolder) .OfType<IHasArtist>() .SelectMany(i => i.Artists) - .Distinct(StringComparer.OrdinalIgnoreCase) + .DistinctNames() .Select(i => { try @@ -385,7 +407,7 @@ namespace MediaBrowser.Controller.Entities .Where(i => !i.IsFolder) .OfType<IHasAlbumArtist>() .SelectMany(i => i.AlbumArtists) - .Distinct(StringComparer.OrdinalIgnoreCase) + .DistinctNames() .Select(i => { try @@ -403,6 +425,14 @@ namespace MediaBrowser.Controller.Entities return GetResult(artists, parent, query); } + private Task<QueryResult<BaseItem>> GetMusicPlaylists(Folder parent, User user, InternalItemsQuery query) + { + query.IncludeItemTypes = new[] { "Playlist" }; + query.Recursive = true; + + return parent.GetItems(query); + } + private QueryResult<BaseItem> GetMusicAlbums(Folder parent, User user, InternalItemsQuery query) { var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Music, CollectionType.MusicVideos }, i => (i is MusicAlbum) && FilterItem(i, query)); @@ -552,7 +582,7 @@ namespace MediaBrowser.Controller.Entities var tasks = GetRecursiveChildren(parent, user, new[] { CollectionType.Movies, CollectionType.BoxSets, string.Empty }) .Where(i => i is Movie) .SelectMany(i => i.Genres) - .Distinct(StringComparer.OrdinalIgnoreCase) + .DistinctNames() .Select(i => { try @@ -724,7 +754,7 @@ namespace MediaBrowser.Controller.Entities var tasks = GetRecursiveChildren(parent, user, new[] { CollectionType.TvShows, string.Empty }) .OfType<Series>() .SelectMany(i => i.Genres) - .Distinct(StringComparer.OrdinalIgnoreCase) + .DistinctNames() .Select(i => { try @@ -776,7 +806,7 @@ namespace MediaBrowser.Controller.Entities var tasks = GetRecursiveChildren(parent, user, new[] { CollectionType.Games }) .OfType<Game>() .SelectMany(i => i.Genres) - .Distinct(StringComparer.OrdinalIgnoreCase) + .DistinctNames() .Select(i => { try @@ -1749,17 +1779,26 @@ namespace MediaBrowser.Controller.Entities return parent.GetRecursiveChildren(user, filter); } - private async Task<IEnumerable<BaseItem>> GetLiveTvFolders(User user) + private async Task<QueryResult<BaseItem>> GetLiveTvView(Folder queryParent, User user, InternalItemsQuery query) { - var list = new List<BaseItem>(); + if (query.Recursive) + { + return await _liveTvManager.GetInternalRecordings(new RecordingQuery + { + IsInProgress = false, + Status = RecordingStatus.Completed, + UserId = user.Id.ToString("N") - var parent = user.RootFolder; + }, CancellationToken.None).ConfigureAwait(false); + } + + var list = new List<BaseItem>(); //list.Add(await GetUserSubView(SpecialFolder.LiveTvNowPlaying, user, "0", parent).ConfigureAwait(false)); - list.Add(await GetUserView(SpecialFolder.LiveTvChannels, user, string.Empty, parent).ConfigureAwait(false)); - list.Add(await GetUserView(SpecialFolder.LiveTvRecordingGroups, user, string.Empty, parent).ConfigureAwait(false)); + list.Add(await GetUserView(SpecialFolder.LiveTvChannels, user, string.Empty, user.RootFolder).ConfigureAwait(false)); + list.Add(await GetUserView(SpecialFolder.LiveTvRecordingGroups, user, string.Empty, user.RootFolder).ConfigureAwait(false)); - return list; + return GetResult(list, queryParent, query); } private async Task<UserView> GetUserView(string name, string type, User user, string sortName, BaseItem parent) diff --git a/MediaBrowser.Controller/IO/ThrottledStream.cs b/MediaBrowser.Controller/IO/ThrottledStream.cs new file mode 100644 index 0000000000..1df00b45a2 --- /dev/null +++ b/MediaBrowser.Controller/IO/ThrottledStream.cs @@ -0,0 +1,393 @@ +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Controller.IO +{ + /// <summary> + /// Class for streaming data with throttling support. + /// </summary> + public class ThrottledStream : Stream + { + /// <summary> + /// A constant used to specify an infinite number of bytes that can be transferred per second. + /// </summary> + public const long Infinite = 0; + + #region Private members + /// <summary> + /// The base stream. + /// </summary> + private readonly Stream _baseStream; + + /// <summary> + /// The maximum bytes per second that can be transferred through the base stream. + /// </summary> + private long _maximumBytesPerSecond; + + /// <summary> + /// The number of bytes that has been transferred since the last throttle. + /// </summary> + private long _byteCount; + + /// <summary> + /// The start time in milliseconds of the last throttle. + /// </summary> + private long _start; + #endregion + + #region Properties + /// <summary> + /// Gets the current milliseconds. + /// </summary> + /// <value>The current milliseconds.</value> + protected long CurrentMilliseconds + { + get + { + return Environment.TickCount; + } + } + + /// <summary> + /// Gets or sets the maximum bytes per second that can be transferred through the base stream. + /// </summary> + /// <value>The maximum bytes per second.</value> + public long MaximumBytesPerSecond + { + get + { + return _maximumBytesPerSecond; + } + set + { + if (MaximumBytesPerSecond != value) + { + _maximumBytesPerSecond = value; + Reset(); + } + } + } + + /// <summary> + /// Gets a value indicating whether the current stream supports reading. + /// </summary> + /// <returns>true if the stream supports reading; otherwise, false.</returns> + public override bool CanRead + { + get + { + return _baseStream.CanRead; + } + } + + /// <summary> + /// Gets a value indicating whether the current stream supports seeking. + /// </summary> + /// <value></value> + /// <returns>true if the stream supports seeking; otherwise, false.</returns> + public override bool CanSeek + { + get + { + return _baseStream.CanSeek; + } + } + + /// <summary> + /// Gets a value indicating whether the current stream supports writing. + /// </summary> + /// <value></value> + /// <returns>true if the stream supports writing; otherwise, false.</returns> + public override bool CanWrite + { + get + { + return _baseStream.CanWrite; + } + } + + /// <summary> + /// Gets the length in bytes of the stream. + /// </summary> + /// <value></value> + /// <returns>A long value representing the length of the stream in bytes.</returns> + /// <exception cref="T:System.NotSupportedException">The base stream does not support seeking. </exception> + /// <exception cref="T:System.ObjectDisposedException">Methods were called after the stream was closed. </exception> + public override long Length + { + get + { + return _baseStream.Length; + } + } + + /// <summary> + /// Gets or sets the position within the current stream. + /// </summary> + /// <value></value> + /// <returns>The current position within the stream.</returns> + /// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception> + /// <exception cref="T:System.NotSupportedException">The base stream does not support seeking. </exception> + /// <exception cref="T:System.ObjectDisposedException">Methods were called after the stream was closed. </exception> + public override long Position + { + get + { + return _baseStream.Position; + } + set + { + _baseStream.Position = value; + } + } + #endregion + + public long MinThrottlePosition; + + #region Ctor + /// <summary> + /// Initializes a new instance of the <see cref="T:ThrottledStream"/> class. + /// </summary> + /// <param name="baseStream">The base stream.</param> + /// <param name="maximumBytesPerSecond">The maximum bytes per second that can be transferred through the base stream.</param> + /// <exception cref="ArgumentNullException">Thrown when <see cref="baseStream"/> is a null reference.</exception> + /// <exception cref="ArgumentOutOfRangeException">Thrown when <see cref="maximumBytesPerSecond"/> is a negative value.</exception> + public ThrottledStream(Stream baseStream, long maximumBytesPerSecond) + { + if (baseStream == null) + { + throw new ArgumentNullException("baseStream"); + } + + if (maximumBytesPerSecond < 0) + { + throw new ArgumentOutOfRangeException("maximumBytesPerSecond", + maximumBytesPerSecond, "The maximum number of bytes per second can't be negative."); + } + + _baseStream = baseStream; + _maximumBytesPerSecond = maximumBytesPerSecond; + _start = CurrentMilliseconds; + _byteCount = 0; + } + #endregion + + #region Public methods + /// <summary> + /// Clears all buffers for this stream and causes any buffered data to be written to the underlying device. + /// </summary> + /// <exception cref="T:System.IO.IOException">An I/O error occurs.</exception> + public override void Flush() + { + _baseStream.Flush(); + } + + /// <summary> + /// Reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read. + /// </summary> + /// <param name="buffer">An array of bytes. When this method returns, the buffer contains the specified byte array with the values between offset and (offset + count - 1) replaced by the bytes read from the current source.</param> + /// <param name="offset">The zero-based byte offset in buffer at which to begin storing the data read from the current stream.</param> + /// <param name="count">The maximum number of bytes to be read from the current stream.</param> + /// <returns> + /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached. + /// </returns> + /// <exception cref="T:System.ArgumentException">The sum of offset and count is larger than the buffer length. </exception> + /// <exception cref="T:System.ObjectDisposedException">Methods were called after the stream was closed. </exception> + /// <exception cref="T:System.NotSupportedException">The base stream does not support reading. </exception> + /// <exception cref="T:System.ArgumentNullException">buffer is null. </exception> + /// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception> + /// <exception cref="T:System.ArgumentOutOfRangeException">offset or count is negative. </exception> + public override int Read(byte[] buffer, int offset, int count) + { + Throttle(count); + + return _baseStream.Read(buffer, offset, count); + } + + /// <summary> + /// Sets the position within the current stream. + /// </summary> + /// <param name="offset">A byte offset relative to the origin parameter.</param> + /// <param name="origin">A value of type <see cref="T:System.IO.SeekOrigin"></see> indicating the reference point used to obtain the new position.</param> + /// <returns> + /// The new position within the current stream. + /// </returns> + /// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception> + /// <exception cref="T:System.NotSupportedException">The base stream does not support seeking, such as if the stream is constructed from a pipe or console output. </exception> + /// <exception cref="T:System.ObjectDisposedException">Methods were called after the stream was closed. </exception> + public override long Seek(long offset, SeekOrigin origin) + { + return _baseStream.Seek(offset, origin); + } + + /// <summary> + /// Sets the length of the current stream. + /// </summary> + /// <param name="value">The desired length of the current stream in bytes.</param> + /// <exception cref="T:System.NotSupportedException">The base stream does not support both writing and seeking, such as if the stream is constructed from a pipe or console output. </exception> + /// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception> + /// <exception cref="T:System.ObjectDisposedException">Methods were called after the stream was closed. </exception> + public override void SetLength(long value) + { + _baseStream.SetLength(value); + } + + private long _bytesWritten; + + /// <summary> + /// Writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written. + /// </summary> + /// <param name="buffer">An array of bytes. This method copies count bytes from buffer to the current stream.</param> + /// <param name="offset">The zero-based byte offset in buffer at which to begin copying bytes to the current stream.</param> + /// <param name="count">The number of bytes to be written to the current stream.</param> + /// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception> + /// <exception cref="T:System.NotSupportedException">The base stream does not support writing. </exception> + /// <exception cref="T:System.ObjectDisposedException">Methods were called after the stream was closed. </exception> + /// <exception cref="T:System.ArgumentNullException">buffer is null. </exception> + /// <exception cref="T:System.ArgumentException">The sum of offset and count is greater than the buffer length. </exception> + /// <exception cref="T:System.ArgumentOutOfRangeException">offset or count is negative. </exception> + public override void Write(byte[] buffer, int offset, int count) + { + Throttle(count); + + _baseStream.Write(buffer, offset, count); + + _bytesWritten += count; + } + + public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + await ThrottleAsync(count, cancellationToken).ConfigureAwait(false); + + await _baseStream.WriteAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false); + + _bytesWritten += count; + } + + /// <summary> + /// Returns a <see cref="T:System.String"></see> that represents the current <see cref="T:System.Object"></see>. + /// </summary> + /// <returns> + /// A <see cref="T:System.String"></see> that represents the current <see cref="T:System.Object"></see>. + /// </returns> + public override string ToString() + { + return _baseStream.ToString(); + } + #endregion + + private bool ThrottleCheck(int bufferSizeInBytes) + { + if (_bytesWritten < MinThrottlePosition) + { + return false; + } + + // Make sure the buffer isn't empty. + if (_maximumBytesPerSecond <= 0 || bufferSizeInBytes <= 0) + { + return false; + } + + return true; + } + + #region Protected methods + /// <summary> + /// Throttles for the specified buffer size in bytes. + /// </summary> + /// <param name="bufferSizeInBytes">The buffer size in bytes.</param> + protected void Throttle(int bufferSizeInBytes) + { + if (!ThrottleCheck(bufferSizeInBytes)) + { + return ; + } + + _byteCount += bufferSizeInBytes; + long elapsedMilliseconds = CurrentMilliseconds - _start; + + if (elapsedMilliseconds > 0) + { + // Calculate the current bps. + long bps = _byteCount * 1000L / elapsedMilliseconds; + + // If the bps are more then the maximum bps, try to throttle. + if (bps > _maximumBytesPerSecond) + { + // Calculate the time to sleep. + long wakeElapsed = _byteCount * 1000L / _maximumBytesPerSecond; + int toSleep = (int)(wakeElapsed - elapsedMilliseconds); + + if (toSleep > 1) + { + try + { + // The time to sleep is more then a millisecond, so sleep. + Thread.Sleep(toSleep); + } + catch (ThreadAbortException) + { + // Eatup ThreadAbortException. + } + + // A sleep has been done, reset. + Reset(); + } + } + } + } + + protected async Task ThrottleAsync(int bufferSizeInBytes, CancellationToken cancellationToken) + { + if (!ThrottleCheck(bufferSizeInBytes)) + { + return; + } + + _byteCount += bufferSizeInBytes; + long elapsedMilliseconds = CurrentMilliseconds - _start; + + if (elapsedMilliseconds > 0) + { + // Calculate the current bps. + long bps = _byteCount * 1000L / elapsedMilliseconds; + + // If the bps are more then the maximum bps, try to throttle. + if (bps > _maximumBytesPerSecond) + { + // Calculate the time to sleep. + long wakeElapsed = _byteCount * 1000L / _maximumBytesPerSecond; + int toSleep = (int)(wakeElapsed - elapsedMilliseconds); + + if (toSleep > 1) + { + // The time to sleep is more then a millisecond, so sleep. + await Task.Delay(toSleep, cancellationToken).ConfigureAwait(false); + + // A sleep has been done, reset. + Reset(); + } + } + } + } + + /// <summary> + /// Will reset the bytecount to 0 and reset the start time to the current time. + /// </summary> + protected void Reset() + { + long difference = CurrentMilliseconds - _start; + + // Only reset counters when a known history is available of more then 1 second. + if (difference > 1000) + { + _byteCount = 0; + _start = CurrentMilliseconds; + } + } + #endregion + } +}
\ No newline at end of file diff --git a/MediaBrowser.Controller/Library/IMediaSourceManager.cs b/MediaBrowser.Controller/Library/IMediaSourceManager.cs index 9cbbabc8db..a77d88049b 100644 --- a/MediaBrowser.Controller/Library/IMediaSourceManager.cs +++ b/MediaBrowser.Controller/Library/IMediaSourceManager.cs @@ -43,18 +43,10 @@ namespace MediaBrowser.Controller.Library /// <param name="id">The identifier.</param> /// <param name="userId">The user identifier.</param> /// <param name="enablePathSubstitution">if set to <c>true</c> [enable path substitution].</param> + /// <param name="supportedLiveMediaTypes">The supported live media types.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>IEnumerable<MediaSourceInfo>.</returns> - Task<IEnumerable<MediaSourceInfo>> GetPlayackMediaSources(string id, string userId, bool enablePathSubstitution, CancellationToken cancellationToken); - - /// <summary> - /// Gets the playack media sources. - /// </summary> - /// <param name="id">The identifier.</param> - /// <param name="enablePathSubstitution">if set to <c>true</c> [enable path substitution].</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task<IEnumerable<MediaSourceInfo>>.</returns> - Task<IEnumerable<MediaSourceInfo>> GetPlayackMediaSources(string id, bool enablePathSubstitution, CancellationToken cancellationToken); + Task<IEnumerable<MediaSourceInfo>> GetPlayackMediaSources(string id, string userId, bool enablePathSubstitution, string[] supportedLiveMediaTypes, CancellationToken cancellationToken); /// <summary> /// Gets the static media sources. @@ -63,24 +55,16 @@ namespace MediaBrowser.Controller.Library /// <param name="enablePathSubstitution">if set to <c>true</c> [enable path substitution].</param> /// <param name="user">The user.</param> /// <returns>IEnumerable<MediaSourceInfo>.</returns> - IEnumerable<MediaSourceInfo> GetStaticMediaSources(IHasMediaSources item, bool enablePathSubstitution, User user); + IEnumerable<MediaSourceInfo> GetStaticMediaSources(IHasMediaSources item, bool enablePathSubstitution, User user = null); /// <summary> - /// Gets the static media sources. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="enablePathSubstitution">if set to <c>true</c> [enable path substitution].</param> - /// <returns>IEnumerable<MediaSourceInfo>.</returns> - IEnumerable<MediaSourceInfo> GetStaticMediaSources(IHasMediaSources item, bool enablePathSubstitution); - - /// <summary> /// Gets the static media source. /// </summary> /// <param name="item">The item.</param> /// <param name="mediaSourceId">The media source identifier.</param> /// <param name="enablePathSubstitution">if set to <c>true</c> [enable path substitution].</param> /// <returns>MediaSourceInfo.</returns> - MediaSourceInfo GetStaticMediaSource(IHasMediaSources item, string mediaSourceId, bool enablePathSubstitution); + Task<MediaSourceInfo> GetMediaSource(IHasMediaSources item, string mediaSourceId, bool enablePathSubstitution); /// <summary> /// Opens the media source. diff --git a/MediaBrowser.Controller/Library/NameExtensions.cs b/MediaBrowser.Controller/Library/NameExtensions.cs new file mode 100644 index 0000000000..b2acdc7ea5 --- /dev/null +++ b/MediaBrowser.Controller/Library/NameExtensions.cs @@ -0,0 +1,41 @@ +using MediaBrowser.Common.Extensions; +using MoreLinq; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace MediaBrowser.Controller.Library +{ + public static class NameExtensions + { + public static bool AreEqual(string name1, string name2) + { + name1 = NormalizeForComparison(name1); + name2 = NormalizeForComparison(name2); + + return string.Equals(name1, name2, StringComparison.OrdinalIgnoreCase); + } + + public static bool EqualsAny(IEnumerable<string> names, string name) + { + name = NormalizeForComparison(name); + + return names.Any(i => string.Equals(NormalizeForComparison(i), name, StringComparison.OrdinalIgnoreCase)); + } + + private static string NormalizeForComparison(string name) + { + if (string.IsNullOrWhiteSpace(name)) + { + return string.Empty; + } + + return name.RemoveDiacritics(); + } + + public static IEnumerable<string> DistinctNames(this IEnumerable<string> names) + { + return names.DistinctBy(NormalizeForComparison, StringComparer.OrdinalIgnoreCase); + } + } +} diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs index d5b5d92a6e..4ee0565f9b 100644 --- a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs +++ b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs @@ -1,4 +1,5 @@ using MediaBrowser.Controller.Channels; +using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Model.Dto; using MediaBrowser.Model.LiveTv; @@ -74,10 +75,11 @@ namespace MediaBrowser.Controller.LiveTv /// Gets the recording. /// </summary> /// <param name="id">The identifier.</param> - /// <param name="user">The user.</param> + /// <param name="options">The options.</param> /// <param name="cancellationToken">The cancellation token.</param> + /// <param name="user">The user.</param> /// <returns>Task{RecordingInfoDto}.</returns> - Task<RecordingInfoDto> GetRecording(string id, CancellationToken cancellationToken, User user = null); + Task<RecordingInfoDto> GetRecording(string id, DtoOptions options, CancellationToken cancellationToken, User user = null); /// <summary> /// Gets the channel. @@ -103,14 +105,15 @@ namespace MediaBrowser.Controller.LiveTv /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task{TimerInfoDto}.</returns> Task<SeriesTimerInfoDto> GetSeriesTimer(string id, CancellationToken cancellationToken); - + /// <summary> /// Gets the recordings. /// </summary> /// <param name="query">The query.</param> + /// <param name="options">The options.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>QueryResult{RecordingInfoDto}.</returns> - Task<QueryResult<RecordingInfoDto>> GetRecordings(RecordingQuery query, CancellationToken cancellationToken); + Task<QueryResult<RecordingInfoDto>> GetRecordings(RecordingQuery query, DtoOptions options, CancellationToken cancellationToken); /// <summary> /// Gets the timers. diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvService.cs b/MediaBrowser.Controller/LiveTv/ILiveTvService.cs index d7e3df4e23..4ef4847a34 100644 --- a/MediaBrowser.Controller/LiveTv/ILiveTvService.cs +++ b/MediaBrowser.Controller/LiveTv/ILiveTvService.cs @@ -1,10 +1,9 @@ -using System; +using MediaBrowser.Controller.Drawing; +using MediaBrowser.Model.Dto; +using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Controller.Channels; -using MediaBrowser.Controller.Drawing; -using MediaBrowser.Model.Dto; namespace MediaBrowser.Controller.LiveTv { diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 2fa62f79f2..b9a161a9f2 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -52,6 +52,10 @@ <SpecificVersion>False</SpecificVersion> <HintPath>..\packages\morelinq.1.1.0\lib\net35\MoreLinq.dll</HintPath> </Reference> + <Reference Include="Patterns.IO, Version=1.0.5580.36861, Culture=neutral, processorArchitecture=MSIL"> + <SpecificVersion>False</SpecificVersion> + <HintPath>..\packages\Patterns.IO.1.0.0.3\lib\portable-net45+sl4+wp71+win8+wpa81\Patterns.IO.dll</HintPath> + </Reference> <Reference Include="System" /> <Reference Include="System.Core" /> <Reference Include="System.Data" /> @@ -115,6 +119,7 @@ <Compile Include="Dlna\IMediaReceiverRegistrar.cs" /> <Compile Include="Dlna\IUpnpService.cs" /> <Compile Include="Drawing\IImageProcessor.cs" /> + <Compile Include="Drawing\ImageCollageOptions.cs" /> <Compile Include="Drawing\ImageProcessingOptions.cs" /> <Compile Include="Drawing\ImageProcessorExtensions.cs" /> <Compile Include="Drawing\ImageStream.cs" /> @@ -171,6 +176,7 @@ <Compile Include="Entities\UserView.cs" /> <Compile Include="Entities\UserViewBuilder.cs" /> <Compile Include="FileOrganization\IFileOrganizationService.cs" /> + <Compile Include="IO\ThrottledStream.cs" /> <Compile Include="Library\DeleteOptions.cs" /> <Compile Include="Library\ILibraryPostScanTask.cs" /> <Compile Include="Library\IMediaSourceManager.cs" /> @@ -184,6 +190,7 @@ <Compile Include="Library\IUserViewManager.cs" /> <Compile Include="Library\LibraryManagerExtensions.cs" /> <Compile Include="Library\MetadataConfigurationStore.cs" /> + <Compile Include="Library\NameExtensions.cs" /> <Compile Include="Library\PlaybackStopEventArgs.cs" /> <Compile Include="Library\UserDataSaveEventArgs.cs" /> <Compile Include="LiveTv\ILiveTvItem.cs" /> @@ -211,8 +218,8 @@ <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\MediaInfoRequest.cs" /> <Compile Include="MediaEncoding\MediaStreamSelector.cs" /> <Compile Include="Net\AuthenticatedAttribute.cs" /> <Compile Include="Net\AuthorizationInfo.cs" /> @@ -394,6 +401,7 @@ <Compile Include="Subtitles\SubtitleResponse.cs" /> <Compile Include="Subtitles\SubtitleSearchRequest.cs" /> <Compile Include="Sync\IHasDynamicAccess.cs" /> + <Compile Include="Sync\IRemoteSyncProvider.cs" /> <Compile Include="Sync\IServerSyncProvider.cs" /> <Compile Include="Sync\ISyncDataProvider.cs" /> <Compile Include="Sync\ISyncManager.cs" /> diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs b/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs index fe0fb32955..bb8841222a 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs @@ -41,6 +41,8 @@ namespace MediaBrowser.Controller.MediaEncoding public int? SubtitleStreamIndex { get; set; } public int? MaxRefFrames { get; set; } public int? MaxVideoBitDepth { get; set; } + public int? CpuCoreLimit { get; set; } + public bool ReadInputAtNativeFramerate { get; set; } public SubtitleDeliveryMethod SubtitleMethod { get; set; } /// <summary> diff --git a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs index 47544f972b..5bec7980aa 100644 --- a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs +++ b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs @@ -63,16 +63,14 @@ namespace MediaBrowser.Controller.MediaEncoding string filenamePrefix, int? maxWidth, CancellationToken cancellationToken); - + /// <summary> /// Gets the media info. /// </summary> - /// <param name="inputFiles">The input files.</param> - /// <param name="protocol">The protocol.</param> - /// <param name="isAudio">if set to <c>true</c> [is audio].</param> + /// <param name="request">The request.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task.</returns> - Task<InternalMediaInfoResult> GetMediaInfo(string[] inputFiles, MediaProtocol protocol, bool isAudio, CancellationToken cancellationToken); + Task<MediaInfo> GetMediaInfo(MediaInfoRequest request, CancellationToken cancellationToken); /// <summary> /// Gets the probe size argument. diff --git a/MediaBrowser.Controller/MediaEncoding/InternalMediaInfoResult.cs b/MediaBrowser.Controller/MediaEncoding/InternalMediaInfoResult.cs deleted file mode 100644 index 796fdb723a..0000000000 --- a/MediaBrowser.Controller/MediaEncoding/InternalMediaInfoResult.cs +++ /dev/null @@ -1,327 +0,0 @@ -using System.Collections.Generic; - -namespace MediaBrowser.Controller.MediaEncoding -{ - /// <summary> - /// Class MediaInfoResult - /// </summary> - public class InternalMediaInfoResult - { - /// <summary> - /// Gets or sets the streams. - /// </summary> - /// <value>The streams.</value> - public MediaStreamInfo[] streams { get; set; } - - /// <summary> - /// Gets or sets the format. - /// </summary> - /// <value>The format.</value> - public MediaFormatInfo format { get; set; } - - /// <summary> - /// Gets or sets the chapters. - /// </summary> - /// <value>The chapters.</value> - public MediaChapter[] Chapters { get; set; } - } - - public class MediaChapter - { - public int id { get; set; } - public string time_base { get; set; } - public long start { get; set; } - public string start_time { get; set; } - public long end { get; set; } - public string end_time { get; set; } - public Dictionary<string, string> tags { get; set; } - } - - /// <summary> - /// Represents a stream within the output - /// </summary> - public class MediaStreamInfo - { - /// <summary> - /// Gets or sets the index. - /// </summary> - /// <value>The index.</value> - public int index { get; set; } - - /// <summary> - /// Gets or sets the profile. - /// </summary> - /// <value>The profile.</value> - public string profile { get; set; } - - /// <summary> - /// Gets or sets the codec_name. - /// </summary> - /// <value>The codec_name.</value> - public string codec_name { get; set; } - - /// <summary> - /// Gets or sets the codec_long_name. - /// </summary> - /// <value>The codec_long_name.</value> - public string codec_long_name { get; set; } - - /// <summary> - /// Gets or sets the codec_type. - /// </summary> - /// <value>The codec_type.</value> - public string codec_type { get; set; } - - /// <summary> - /// Gets or sets the sample_rate. - /// </summary> - /// <value>The sample_rate.</value> - public string sample_rate { get; set; } - - /// <summary> - /// Gets or sets the channels. - /// </summary> - /// <value>The channels.</value> - public int channels { get; set; } - - /// <summary> - /// Gets or sets the channel_layout. - /// </summary> - /// <value>The channel_layout.</value> - public string channel_layout { get; set; } - - /// <summary> - /// Gets or sets the avg_frame_rate. - /// </summary> - /// <value>The avg_frame_rate.</value> - public string avg_frame_rate { get; set; } - - /// <summary> - /// Gets or sets the duration. - /// </summary> - /// <value>The duration.</value> - public string duration { get; set; } - - /// <summary> - /// Gets or sets the bit_rate. - /// </summary> - /// <value>The bit_rate.</value> - public string bit_rate { get; set; } - - /// <summary> - /// Gets or sets the width. - /// </summary> - /// <value>The width.</value> - public int width { get; set; } - - /// <summary> - /// Gets or sets the height. - /// </summary> - /// <value>The height.</value> - public int height { get; set; } - - /// <summary> - /// Gets or sets the display_aspect_ratio. - /// </summary> - /// <value>The display_aspect_ratio.</value> - public string display_aspect_ratio { get; set; } - - /// <summary> - /// Gets or sets the tags. - /// </summary> - /// <value>The tags.</value> - public Dictionary<string, string> tags { get; set; } - - /// <summary> - /// Gets or sets the bits_per_sample. - /// </summary> - /// <value>The bits_per_sample.</value> - public int bits_per_sample { get; set; } - - /// <summary> - /// Gets or sets the r_frame_rate. - /// </summary> - /// <value>The r_frame_rate.</value> - public string r_frame_rate { get; set; } - - /// <summary> - /// Gets or sets the has_b_frames. - /// </summary> - /// <value>The has_b_frames.</value> - public int has_b_frames { get; set; } - - /// <summary> - /// Gets or sets the sample_aspect_ratio. - /// </summary> - /// <value>The sample_aspect_ratio.</value> - public string sample_aspect_ratio { get; set; } - - /// <summary> - /// Gets or sets the pix_fmt. - /// </summary> - /// <value>The pix_fmt.</value> - public string pix_fmt { get; set; } - - /// <summary> - /// Gets or sets the level. - /// </summary> - /// <value>The level.</value> - public int level { get; set; } - - /// <summary> - /// Gets or sets the time_base. - /// </summary> - /// <value>The time_base.</value> - public string time_base { get; set; } - - /// <summary> - /// Gets or sets the start_time. - /// </summary> - /// <value>The start_time.</value> - public string start_time { get; set; } - - /// <summary> - /// Gets or sets the codec_time_base. - /// </summary> - /// <value>The codec_time_base.</value> - public string codec_time_base { get; set; } - - /// <summary> - /// Gets or sets the codec_tag. - /// </summary> - /// <value>The codec_tag.</value> - public string codec_tag { get; set; } - - /// <summary> - /// Gets or sets the codec_tag_string. - /// </summary> - /// <value>The codec_tag_string.</value> - public string codec_tag_string { get; set; } - - /// <summary> - /// Gets or sets the sample_fmt. - /// </summary> - /// <value>The sample_fmt.</value> - public string sample_fmt { get; set; } - - /// <summary> - /// Gets or sets the dmix_mode. - /// </summary> - /// <value>The dmix_mode.</value> - public string dmix_mode { get; set; } - - /// <summary> - /// Gets or sets the start_pts. - /// </summary> - /// <value>The start_pts.</value> - public string start_pts { get; set; } - - /// <summary> - /// Gets or sets the is_avc. - /// </summary> - /// <value>The is_avc.</value> - public string is_avc { get; set; } - - /// <summary> - /// Gets or sets the nal_length_size. - /// </summary> - /// <value>The nal_length_size.</value> - public string nal_length_size { get; set; } - - /// <summary> - /// Gets or sets the ltrt_cmixlev. - /// </summary> - /// <value>The ltrt_cmixlev.</value> - public string ltrt_cmixlev { get; set; } - - /// <summary> - /// Gets or sets the ltrt_surmixlev. - /// </summary> - /// <value>The ltrt_surmixlev.</value> - public string ltrt_surmixlev { get; set; } - - /// <summary> - /// Gets or sets the loro_cmixlev. - /// </summary> - /// <value>The loro_cmixlev.</value> - public string loro_cmixlev { get; set; } - - /// <summary> - /// Gets or sets the loro_surmixlev. - /// </summary> - /// <value>The loro_surmixlev.</value> - public string loro_surmixlev { get; set; } - - /// <summary> - /// Gets or sets the disposition. - /// </summary> - /// <value>The disposition.</value> - public Dictionary<string, string> disposition { get; set; } - } - - /// <summary> - /// Class MediaFormat - /// </summary> - public class MediaFormatInfo - { - /// <summary> - /// Gets or sets the filename. - /// </summary> - /// <value>The filename.</value> - public string filename { get; set; } - - /// <summary> - /// Gets or sets the nb_streams. - /// </summary> - /// <value>The nb_streams.</value> - public int nb_streams { get; set; } - - /// <summary> - /// Gets or sets the format_name. - /// </summary> - /// <value>The format_name.</value> - public string format_name { get; set; } - - /// <summary> - /// Gets or sets the format_long_name. - /// </summary> - /// <value>The format_long_name.</value> - public string format_long_name { get; set; } - - /// <summary> - /// Gets or sets the start_time. - /// </summary> - /// <value>The start_time.</value> - public string start_time { get; set; } - - /// <summary> - /// Gets or sets the duration. - /// </summary> - /// <value>The duration.</value> - public string duration { get; set; } - - /// <summary> - /// Gets or sets the size. - /// </summary> - /// <value>The size.</value> - public string size { get; set; } - - /// <summary> - /// Gets or sets the bit_rate. - /// </summary> - /// <value>The bit_rate.</value> - public string bit_rate { get; set; } - - /// <summary> - /// Gets or sets the probe_score. - /// </summary> - /// <value>The probe_score.</value> - public int probe_score { get; set; } - - /// <summary> - /// Gets or sets the tags. - /// </summary> - /// <value>The tags.</value> - public Dictionary<string, string> tags { get; set; } - } -} diff --git a/MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs b/MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs index 530c127da7..da9dd4dfd2 100644 --- a/MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs +++ b/MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs @@ -1,9 +1,7 @@ -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.IO; +using MediaBrowser.Model.IO; using MediaBrowser.Model.MediaInfo; using System; using System.Collections.Generic; -using System.Globalization; using System.IO; using System.Linq; @@ -46,291 +44,5 @@ namespace MediaBrowser.Controller.MediaEncoding .Where(f => !string.IsNullOrEmpty(f)) .ToList(); } - - public static MediaInfo GetMediaInfo(InternalMediaInfoResult data) - { - var internalStreams = data.streams ?? new MediaStreamInfo[] { }; - - var info = new MediaInfo - { - MediaStreams = internalStreams.Select(s => GetMediaStream(s, data.format)) - .Where(i => i != null) - .ToList() - }; - - if (data.format != null) - { - info.Format = data.format.format_name; - - if (!string.IsNullOrEmpty(data.format.bit_rate)) - { - info.TotalBitrate = int.Parse(data.format.bit_rate, UsCulture); - } - } - - return info; - } - - private static readonly CultureInfo UsCulture = new CultureInfo("en-US"); - - /// <summary> - /// Converts ffprobe stream info to our MediaStream class - /// </summary> - /// <param name="streamInfo">The stream info.</param> - /// <param name="formatInfo">The format info.</param> - /// <returns>MediaStream.</returns> - private static MediaStream GetMediaStream(MediaStreamInfo streamInfo, MediaFormatInfo formatInfo) - { - var stream = new MediaStream - { - Codec = streamInfo.codec_name, - Profile = streamInfo.profile, - Level = streamInfo.level, - Index = streamInfo.index, - PixelFormat = streamInfo.pix_fmt - }; - - if (streamInfo.tags != null) - { - stream.Language = GetDictionaryValue(streamInfo.tags, "language"); - } - - if (string.Equals(streamInfo.codec_type, "audio", StringComparison.OrdinalIgnoreCase)) - { - stream.Type = MediaStreamType.Audio; - - stream.Channels = streamInfo.channels; - - if (!string.IsNullOrEmpty(streamInfo.sample_rate)) - { - stream.SampleRate = int.Parse(streamInfo.sample_rate, UsCulture); - } - - stream.ChannelLayout = ParseChannelLayout(streamInfo.channel_layout); - } - else if (string.Equals(streamInfo.codec_type, "subtitle", StringComparison.OrdinalIgnoreCase)) - { - stream.Type = MediaStreamType.Subtitle; - } - else if (string.Equals(streamInfo.codec_type, "video", StringComparison.OrdinalIgnoreCase)) - { - stream.Type = (streamInfo.codec_name ?? string.Empty).IndexOf("mjpeg", StringComparison.OrdinalIgnoreCase) != -1 - ? MediaStreamType.EmbeddedImage - : MediaStreamType.Video; - - stream.Width = streamInfo.width; - stream.Height = streamInfo.height; - stream.AspectRatio = GetAspectRatio(streamInfo); - - stream.AverageFrameRate = GetFrameRate(streamInfo.avg_frame_rate); - stream.RealFrameRate = GetFrameRate(streamInfo.r_frame_rate); - - stream.BitDepth = GetBitDepth(stream.PixelFormat); - - //stream.IsAnamorphic = string.Equals(streamInfo.sample_aspect_ratio, "0:1", StringComparison.OrdinalIgnoreCase) || - // string.Equals(stream.AspectRatio, "2.35:1", StringComparison.OrdinalIgnoreCase) || - // string.Equals(stream.AspectRatio, "2.40:1", StringComparison.OrdinalIgnoreCase); - - stream.IsAnamorphic = string.Equals(streamInfo.sample_aspect_ratio, "0:1", StringComparison.OrdinalIgnoreCase); - } - else - { - return null; - } - - // Get stream bitrate - var bitrate = 0; - - if (!string.IsNullOrEmpty(streamInfo.bit_rate)) - { - bitrate = int.Parse(streamInfo.bit_rate, UsCulture); - } - else if (formatInfo != null && !string.IsNullOrEmpty(formatInfo.bit_rate) && stream.Type == MediaStreamType.Video) - { - // If the stream info doesn't have a bitrate get the value from the media format info - bitrate = int.Parse(formatInfo.bit_rate, UsCulture); - } - - if (bitrate > 0) - { - stream.BitRate = bitrate; - } - - if (streamInfo.disposition != null) - { - var isDefault = GetDictionaryValue(streamInfo.disposition, "default"); - var isForced = GetDictionaryValue(streamInfo.disposition, "forced"); - - stream.IsDefault = string.Equals(isDefault, "1", StringComparison.OrdinalIgnoreCase); - - stream.IsForced = string.Equals(isForced, "1", StringComparison.OrdinalIgnoreCase); - } - - return stream; - } - - private static int? GetBitDepth(string pixelFormat) - { - var eightBit = new List<string> - { - "yuv420p", - "yuv411p", - "yuvj420p", - "uyyvyy411", - "nv12", - "nv21", - "rgb444le", - "rgb444be", - "bgr444le", - "bgr444be", - "yuvj411p" - }; - - if (!string.IsNullOrEmpty(pixelFormat)) - { - if (eightBit.Contains(pixelFormat, StringComparer.OrdinalIgnoreCase)) - { - return 8; - } - } - - return null; - } - - /// <summary> - /// Gets a string from an FFProbeResult tags dictionary - /// </summary> - /// <param name="tags">The tags.</param> - /// <param name="key">The key.</param> - /// <returns>System.String.</returns> - private static string GetDictionaryValue(Dictionary<string, string> tags, string key) - { - if (tags == null) - { - return null; - } - - string val; - - tags.TryGetValue(key, out val); - return val; - } - - private static string ParseChannelLayout(string input) - { - if (string.IsNullOrEmpty(input)) - { - return input; - } - - return input.Split('(').FirstOrDefault(); - } - - private static string GetAspectRatio(MediaStreamInfo info) - { - var original = info.display_aspect_ratio; - - int height; - int width; - - var parts = (original ?? string.Empty).Split(':'); - if (!(parts.Length == 2 && - int.TryParse(parts[0], NumberStyles.Any, UsCulture, out width) && - int.TryParse(parts[1], NumberStyles.Any, UsCulture, out height) && - width > 0 && - height > 0)) - { - width = info.width; - height = info.height; - } - - if (width > 0 && height > 0) - { - double ratio = width; - ratio /= height; - - if (IsClose(ratio, 1.777777778, .03)) - { - return "16:9"; - } - - if (IsClose(ratio, 1.3333333333, .05)) - { - return "4:3"; - } - - if (IsClose(ratio, 1.41)) - { - return "1.41:1"; - } - - if (IsClose(ratio, 1.5)) - { - return "1.5:1"; - } - - if (IsClose(ratio, 1.6)) - { - return "1.6:1"; - } - - if (IsClose(ratio, 1.66666666667)) - { - return "5:3"; - } - - if (IsClose(ratio, 1.85, .02)) - { - return "1.85:1"; - } - - if (IsClose(ratio, 2.35, .025)) - { - return "2.35:1"; - } - - if (IsClose(ratio, 2.4, .025)) - { - return "2.40:1"; - } - } - - return original; - } - - private static bool IsClose(double d1, double d2, double variance = .005) - { - return Math.Abs(d1 - d2) <= variance; - } - - /// <summary> - /// Gets a frame rate from a string value in ffprobe output - /// This could be a number or in the format of 2997/125. - /// </summary> - /// <param name="value">The value.</param> - /// <returns>System.Nullable{System.Single}.</returns> - private static float? GetFrameRate(string value) - { - if (!string.IsNullOrEmpty(value)) - { - var parts = value.Split('/'); - - float result; - - if (parts.Length == 2) - { - result = float.Parse(parts[0], UsCulture) / float.Parse(parts[1], UsCulture); - } - else - { - result = float.Parse(parts[0], UsCulture); - } - - return float.IsNaN(result) ? (float?)null : result; - } - - return null; - } - } } diff --git a/MediaBrowser.Controller/MediaEncoding/MediaInfoRequest.cs b/MediaBrowser.Controller/MediaEncoding/MediaInfoRequest.cs new file mode 100644 index 0000000000..24df7b8854 --- /dev/null +++ b/MediaBrowser.Controller/MediaEncoding/MediaInfoRequest.cs @@ -0,0 +1,25 @@ +using MediaBrowser.Model.Dlna; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.IO; +using MediaBrowser.Model.MediaInfo; +using System.Collections.Generic; + +namespace MediaBrowser.Controller.MediaEncoding +{ + public class MediaInfoRequest + { + public string InputPath { get; set; } + public MediaProtocol Protocol { get; set; } + public bool ExtractChapters { get; set; } + public DlnaProfileType MediaType { get; set; } + public IIsoMount MountedIso { get; set; } + public VideoType VideoType { get; set; } + public List<string> PlayableStreamFileNames { get; set; } + public bool ExtractKeyFrameInterval { get; set; } + + public MediaInfoRequest() + { + PlayableStreamFileNames = new List<string>(); + } + } +} diff --git a/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs b/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs index 6facc1074a..13f83c0fc9 100644 --- a/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs +++ b/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs @@ -1404,24 +1404,12 @@ namespace MediaBrowser.Controller.Providers { switch (reader.Name) { - case "Name": - { - linkedItem.ItemName = reader.ReadElementContentAsString(); - break; - } - case "Path": { linkedItem.Path = reader.ReadElementContentAsString(); break; } - case "Type": - { - linkedItem.ItemType = reader.ReadElementContentAsString(); - break; - } - default: reader.Skip(); break; @@ -1435,7 +1423,7 @@ namespace MediaBrowser.Controller.Providers return linkedItem; } - return string.IsNullOrWhiteSpace(linkedItem.ItemName) || string.IsNullOrWhiteSpace(linkedItem.ItemType) ? null : linkedItem; + return null; } diff --git a/MediaBrowser.Controller/Providers/IImageEnhancer.cs b/MediaBrowser.Controller/Providers/IImageEnhancer.cs index e5a51a56e0..a43941607e 100644 --- a/MediaBrowser.Controller/Providers/IImageEnhancer.cs +++ b/MediaBrowser.Controller/Providers/IImageEnhancer.cs @@ -1,5 +1,4 @@ -using MediaBrowser.Controller.Drawing; -using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities; using MediaBrowser.Model.Drawing; using MediaBrowser.Model.Entities; using System.Threading.Tasks; diff --git a/MediaBrowser.Controller/Providers/IProviderManager.cs b/MediaBrowser.Controller/Providers/IProviderManager.cs index d40fa835fc..d6fc39c5f5 100644 --- a/MediaBrowser.Controller/Providers/IProviderManager.cs +++ b/MediaBrowser.Controller/Providers/IProviderManager.cs @@ -78,6 +78,19 @@ namespace MediaBrowser.Controller.Providers /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task.</returns> Task SaveImage(IHasImages item, Stream source, string mimeType, ImageType type, int? imageIndex, string internalCacheKey, CancellationToken cancellationToken); + + /// <summary> + /// Saves the image. + /// </summary> + /// <param name="item">The item.</param> + /// <param name="source">The source.</param> + /// <param name="mimeType">Type of the MIME.</param> + /// <param name="type">The type.</param> + /// <param name="imageIndex">Index of the image.</param> + /// <param name="internalCacheKey">The internal cache key.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task.</returns> + Task SaveImage(IHasImages item, string source, string mimeType, ImageType type, int? imageIndex, string internalCacheKey, CancellationToken cancellationToken); /// <summary> /// Adds the metadata providers. diff --git a/MediaBrowser.Controller/Sync/IHasDynamicAccess.cs b/MediaBrowser.Controller/Sync/IHasDynamicAccess.cs index f907de7290..cf868a3812 100644 --- a/MediaBrowser.Controller/Sync/IHasDynamicAccess.cs +++ b/MediaBrowser.Controller/Sync/IHasDynamicAccess.cs @@ -9,10 +9,10 @@ namespace MediaBrowser.Controller.Sync /// <summary> /// Gets the synced file information. /// </summary> - /// <param name="remotePath">The remote path.</param> + /// <param name="id">The identifier.</param> /// <param name="target">The target.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task<SyncedFileInfo>.</returns> - Task<SyncedFileInfo> GetSyncedFileInfo(string remotePath, SyncTarget target, CancellationToken cancellationToken); + Task<SyncedFileInfo> GetSyncedFileInfo(string id, SyncTarget target, CancellationToken cancellationToken); } } diff --git a/MediaBrowser.Controller/Sync/IRemoteSyncProvider.cs b/MediaBrowser.Controller/Sync/IRemoteSyncProvider.cs new file mode 100644 index 0000000000..aeb7a3bff3 --- /dev/null +++ b/MediaBrowser.Controller/Sync/IRemoteSyncProvider.cs @@ -0,0 +1,10 @@ + +namespace MediaBrowser.Controller.Sync +{ + /// <summary> + /// A marker interface + /// </summary> + public interface IRemoteSyncProvider + { + } +} diff --git a/MediaBrowser.Controller/Sync/IServerSyncProvider.cs b/MediaBrowser.Controller/Sync/IServerSyncProvider.cs index 46bbbd3299..6b694d26d9 100644 --- a/MediaBrowser.Controller/Sync/IServerSyncProvider.cs +++ b/MediaBrowser.Controller/Sync/IServerSyncProvider.cs @@ -1,6 +1,7 @@ -using MediaBrowser.Model.Sync; +using MediaBrowser.Model.Querying; +using MediaBrowser.Model.Sync; +using Patterns.IO; using System; -using System.Collections.Generic; using System.IO; using System.Threading; using System.Threading.Tasks; @@ -13,46 +14,39 @@ namespace MediaBrowser.Controller.Sync /// Transfers the file. /// </summary> /// <param name="stream">The stream.</param> - /// <param name="remotePath">The remote path.</param> + /// <param name="pathParts">The path parts.</param> /// <param name="target">The target.</param> /// <param name="progress">The progress.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task.</returns> - Task<SyncedFileInfo> SendFile(Stream stream, string remotePath, SyncTarget target, IProgress<double> progress, CancellationToken cancellationToken); + Task<SyncedFileInfo> SendFile(Stream stream, string[] pathParts, SyncTarget target, IProgress<double> progress, CancellationToken cancellationToken); /// <summary> /// Deletes the file. /// </summary> - /// <param name="path">The path.</param> + /// <param name="id">The identifier.</param> /// <param name="target">The target.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task.</returns> - Task DeleteFile(string path, SyncTarget target, CancellationToken cancellationToken); + Task DeleteFile(string id, SyncTarget target, CancellationToken cancellationToken); /// <summary> /// Gets the file. /// </summary> - /// <param name="path">The path.</param> + /// <param name="id">The identifier.</param> /// <param name="target">The target.</param> /// <param name="progress">The progress.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task<Stream>.</returns> - Task<Stream> GetFile(string path, SyncTarget target, IProgress<double> progress, CancellationToken cancellationToken); + Task<Stream> GetFile(string id, SyncTarget target, IProgress<double> progress, CancellationToken cancellationToken); /// <summary> - /// Gets the full path. + /// Gets the files. /// </summary> - /// <param name="path">The path.</param> + /// <param name="query">The query.</param> /// <param name="target">The target.</param> - /// <returns>System.String.</returns> - string GetFullPath(IEnumerable<string> path, SyncTarget target); - - /// <summary> - /// Gets the parent directory path. - /// </summary> - /// <param name="path">The path.</param> - /// <param name="target">The target.</param> - /// <returns>System.String.</returns> - string GetParentDirectoryPath(string path, SyncTarget target); + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task<QueryResult<FileMetadata>>.</returns> + Task<QueryResult<FileMetadata>> GetFiles(FileQuery query, SyncTarget target, CancellationToken cancellationToken); } } diff --git a/MediaBrowser.Controller/Sync/ISyncDataProvider.cs b/MediaBrowser.Controller/Sync/ISyncDataProvider.cs index dc3edc4e40..ebff50cb02 100644 --- a/MediaBrowser.Controller/Sync/ISyncDataProvider.cs +++ b/MediaBrowser.Controller/Sync/ISyncDataProvider.cs @@ -7,20 +7,12 @@ namespace MediaBrowser.Controller.Sync public interface ISyncDataProvider { /// <summary> - /// Gets the server item ids. + /// Gets the local items. /// </summary> /// <param name="target">The target.</param> /// <param name="serverId">The server identifier.</param> - /// <returns>Task<List<System.String>>.</returns> - Task<List<string>> GetServerItemIds(SyncTarget target, string serverId); - - /// <summary> - /// Gets the synchronize job item ids. - /// </summary> - /// <param name="target">The target.</param> - /// <param name="serverId">The server identifier.</param> - /// <returns>Task<List<System.String>>.</returns> - Task<List<string>> GetSyncJobItemIds(SyncTarget target, string serverId); + /// <returns>Task<List<LocalItem>>.</returns> + Task<List<LocalItem>> GetLocalItems(SyncTarget target, string serverId); /// <summary> /// Adds the or update. diff --git a/MediaBrowser.Controller/Sync/ISyncManager.cs b/MediaBrowser.Controller/Sync/ISyncManager.cs index 3b6e20edc9..97591551c1 100644 --- a/MediaBrowser.Controller/Sync/ISyncManager.cs +++ b/MediaBrowser.Controller/Sync/ISyncManager.cs @@ -174,6 +174,13 @@ namespace MediaBrowser.Controller.Sync /// <param name="targetId">The target identifier.</param> /// <returns>IEnumerable<SyncQualityOption>.</returns> IEnumerable<SyncQualityOption> GetQualityOptions(string targetId); + /// <summary> + /// Gets the quality options. + /// </summary> + /// <param name="targetId">The target identifier.</param> + /// <param name="user">The user.</param> + /// <returns>IEnumerable<SyncQualityOption>.</returns> + IEnumerable<SyncQualityOption> GetQualityOptions(string targetId, User user); /// <summary> /// Gets the profile options. @@ -181,5 +188,12 @@ namespace MediaBrowser.Controller.Sync /// <param name="targetId">The target identifier.</param> /// <returns>IEnumerable<SyncQualityOption>.</returns> IEnumerable<SyncProfileOption> GetProfileOptions(string targetId); + /// <summary> + /// Gets the profile options. + /// </summary> + /// <param name="targetId">The target identifier.</param> + /// <param name="user">The user.</param> + /// <returns>IEnumerable<SyncProfileOption>.</returns> + IEnumerable<SyncProfileOption> GetProfileOptions(string targetId, User user); } } diff --git a/MediaBrowser.Controller/Sync/SyncedFileInfo.cs b/MediaBrowser.Controller/Sync/SyncedFileInfo.cs index 550af2d554..844e7d890d 100644 --- a/MediaBrowser.Controller/Sync/SyncedFileInfo.cs +++ b/MediaBrowser.Controller/Sync/SyncedFileInfo.cs @@ -20,6 +20,11 @@ namespace MediaBrowser.Controller.Sync /// </summary> /// <value>The required HTTP headers.</value> public Dictionary<string, string> RequiredHttpHeaders { get; set; } + /// <summary> + /// Gets or sets the identifier. + /// </summary> + /// <value>The identifier.</value> + public string Id { get; set; } public SyncedFileInfo() { diff --git a/MediaBrowser.Controller/packages.config b/MediaBrowser.Controller/packages.config index 6df1662040..8d10de2f1b 100644 --- a/MediaBrowser.Controller/packages.config +++ b/MediaBrowser.Controller/packages.config @@ -1,4 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> <packages> <package id="morelinq" version="1.1.0" targetFramework="net45" /> + <package id="Patterns.IO" version="1.0.0.3" targetFramework="net45" /> </packages>
\ No newline at end of file |
