diff --git a/MediaBrowser.Api/Devices/DeviceService.cs b/MediaBrowser.Api/Devices/DeviceService.cs index 135397308..ab0a4a4b2 100644 --- a/MediaBrowser.Api/Devices/DeviceService.cs +++ b/MediaBrowser.Api/Devices/DeviceService.cs @@ -1,25 +1,19 @@ using MediaBrowser.Controller.Devices; using MediaBrowser.Controller.Net; using MediaBrowser.Model.Devices; +using MediaBrowser.Model.Querying; using MediaBrowser.Model.Session; using ServiceStack; using ServiceStack.Web; -using System.Collections.Generic; using System.IO; -using System.Linq; using System.Threading.Tasks; namespace MediaBrowser.Api.Devices { [Route("/Devices", "GET", Summary = "Gets all devices")] [Authenticated(Roles = "Admin")] - public class GetDevices : IReturn> + public class GetDevices : DeviceQuery, IReturn> { - [ApiMember(Name = "SupportsContentUploading", Description = "SupportsContentUploading", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public bool? SupportsContentUploading { get; set; } - - [ApiMember(Name = "SupportsDeviceId", Description = "SupportsDeviceId", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public bool? SupportsDeviceId { get; set; } } [Route("/Devices", "DELETE", Summary = "Deletes a device")] @@ -112,23 +106,7 @@ namespace MediaBrowser.Api.Devices public object Get(GetDevices request) { - var devices = _deviceManager.GetDevices(); - - if (request.SupportsContentUploading.HasValue) - { - var val = request.SupportsContentUploading.Value; - - devices = devices.Where(i => _deviceManager.GetCapabilities(i.Id).SupportsContentUploading == val); - } - - if (request.SupportsDeviceId.HasValue) - { - var val = request.SupportsDeviceId.Value; - - devices = devices.Where(i => _deviceManager.GetCapabilities(i.Id).SupportsDeviceId == val); - } - - return ToOptimizedResult(devices.ToList()); + return ToOptimizedResult(_deviceManager.GetDevices(request)); } public object Get(GetCameraUploads request) diff --git a/MediaBrowser.Api/Music/InstantMixService.cs b/MediaBrowser.Api/Music/InstantMixService.cs index f34242242..43fd0894b 100644 --- a/MediaBrowser.Api/Music/InstantMixService.cs +++ b/MediaBrowser.Api/Music/InstantMixService.cs @@ -3,6 +3,7 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Net; +using MediaBrowser.Controller.Playlists; using MediaBrowser.Model.Querying; using ServiceStack; using System.Collections.Generic; @@ -20,6 +21,11 @@ namespace MediaBrowser.Api.Music { } + [Route("/Playlists/{Id}/InstantMix", "GET", Summary = "Creates an instant playlist based on a given playlist")] + public class GetInstantMixFromPlaylist : BaseGetSimilarItemsFromItem + { + } + [Route("/Artists/{Name}/InstantMix", "GET", Summary = "Creates an instant playlist based on a given artist")] public class GetInstantMixFromArtist : BaseGetSimilarItems { @@ -109,6 +115,17 @@ namespace MediaBrowser.Api.Music return GetResult(items, user, request); } + public object Get(GetInstantMixFromPlaylist request) + { + var playlist = (Playlist)_libraryManager.GetItemById(request.Id); + + var user = _userManager.GetUserById(request.UserId.Value); + + var items = _musicManager.GetInstantMixFromPlaylist(playlist, user); + + return GetResult(items, user, request); + } + public object Get(GetInstantMixFromMusicGenre request) { var user = _userManager.GetUserById(request.UserId.Value); diff --git a/MediaBrowser.Api/Session/SessionsService.cs b/MediaBrowser.Api/Session/SessionsService.cs index 551771338..df50255ab 100644 --- a/MediaBrowser.Api/Session/SessionsService.cs +++ b/MediaBrowser.Api/Session/SessionsService.cs @@ -239,8 +239,11 @@ namespace MediaBrowser.Api.Session [ApiMember(Name = "SupportsContentUploading", Description = "Determines whether camera upload is supported.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "POST")] public bool SupportsContentUploading { get; set; } - [ApiMember(Name = "SupportsDeviceId", Description = "Determines whether the device supports a unique identifier.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "POST")] - public bool SupportsDeviceId { get; set; } + [ApiMember(Name = "SupportsSync", Description = "Determines whether sync is supported.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "POST")] + public bool SupportsSync { get; set; } + + [ApiMember(Name = "SupportsUniqueIdentifier", Description = "Determines whether the device supports a unique identifier.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "POST")] + public bool SupportsUniqueIdentifier { get; set; } } [Route("/Sessions/Logout", "POST", Summary = "Reports that a session has ended")] @@ -521,7 +524,9 @@ namespace MediaBrowser.Api.Session SupportsContentUploading = request.SupportsContentUploading, - SupportsDeviceId = request.SupportsDeviceId + SupportsSync = request.SupportsSync, + + SupportsUniqueIdentifier = request.SupportsUniqueIdentifier }); } } diff --git a/MediaBrowser.Controller/Collections/CollectionCreationOptions.cs b/MediaBrowser.Controller/Collections/CollectionCreationOptions.cs index 74ae42095..4a2d39066 100644 --- a/MediaBrowser.Controller/Collections/CollectionCreationOptions.cs +++ b/MediaBrowser.Controller/Collections/CollectionCreationOptions.cs @@ -15,11 +15,13 @@ namespace MediaBrowser.Controller.Collections public Dictionary ProviderIds { get; set; } public List ItemIdList { get; set; } + public List UserIds { get; set; } public CollectionCreationOptions() { ProviderIds = new Dictionary(StringComparer.OrdinalIgnoreCase); ItemIdList = new List(); + UserIds = new List(); } } } diff --git a/MediaBrowser.Controller/Devices/IDeviceManager.cs b/MediaBrowser.Controller/Devices/IDeviceManager.cs index af184e6e9..efd24336a 100644 --- a/MediaBrowser.Controller/Devices/IDeviceManager.cs +++ b/MediaBrowser.Controller/Devices/IDeviceManager.cs @@ -1,5 +1,6 @@ using MediaBrowser.Model.Devices; using MediaBrowser.Model.Events; +using MediaBrowser.Model.Querying; using MediaBrowser.Model.Session; using System; using System.Collections.Generic; @@ -58,8 +59,9 @@ namespace MediaBrowser.Controller.Devices /// /// Gets the devices. /// + /// The query. /// IEnumerable<DeviceInfo>. - IEnumerable GetDevices(); + QueryResult GetDevices(DeviceQuery query); /// /// Deletes the device. diff --git a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs index 731226ede..9dc600675 100644 --- a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs +++ b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs @@ -15,8 +15,10 @@ namespace MediaBrowser.Controller.Entities.Movies /// /// Class BoxSet /// - public class BoxSet : Folder, IHasTrailers, IHasKeywords, IHasPreferredMetadataLanguage, IHasDisplayOrder, IHasLookupInfo, IMetadataContainer + public class BoxSet : Folder, IHasTrailers, IHasKeywords, IHasPreferredMetadataLanguage, IHasDisplayOrder, IHasLookupInfo, IMetadataContainer, IHasShares { + public List Shares { get; set; } + public BoxSet() { RemoteTrailers = new List(); @@ -25,6 +27,7 @@ namespace MediaBrowser.Controller.Entities.Movies DisplayOrder = ItemSortBy.PremiereDate; Keywords = new List(); + Shares = new List(); } protected override bool FilterLinkedChildrenPerUser @@ -160,5 +163,20 @@ namespace MediaBrowser.Controller.Entities.Movies progress.Report(100); } + + public override bool IsVisible(User user) + { + if (base.IsVisible(user)) + { + var userId = user.Id.ToString("N"); + + return Shares.Any(i => string.Equals(userId, i.UserId, StringComparison.OrdinalIgnoreCase)) || + + // Need to support this for boxsets created prior to the creation of Shares + Shares.Count == 0; + } + + return false; + } } } diff --git a/MediaBrowser.Controller/Entities/Share.cs b/MediaBrowser.Controller/Entities/Share.cs new file mode 100644 index 000000000..e194f6238 --- /dev/null +++ b/MediaBrowser.Controller/Entities/Share.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; + +namespace MediaBrowser.Controller.Entities +{ + public interface IHasShares + { + List Shares { get; set; } + } + + public class Share + { + public string UserId { get; set; } + public bool CanEdit { get; set; } + } +} diff --git a/MediaBrowser.Controller/Library/IMusicManager.cs b/MediaBrowser.Controller/Library/IMusicManager.cs index 192ce2e83..f66f18401 100644 --- a/MediaBrowser.Controller/Library/IMusicManager.cs +++ b/MediaBrowser.Controller/Library/IMusicManager.cs @@ -1,5 +1,6 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Controller.Playlists; using System.Collections.Generic; namespace MediaBrowser.Controller.Library @@ -28,6 +29,13 @@ namespace MediaBrowser.Controller.Library /// IEnumerable{Audio}. IEnumerable public interface IUserRepository : IRepository { - /// - /// Opens the connection to the repository - /// - /// Task. - Task Initialize(); - /// /// Deletes the user. /// diff --git a/MediaBrowser.Controller/Playlists/Playlist.cs b/MediaBrowser.Controller/Playlists/Playlist.cs index 75e1bbde7..e48cddaaa 100644 --- a/MediaBrowser.Controller/Playlists/Playlist.cs +++ b/MediaBrowser.Controller/Playlists/Playlist.cs @@ -11,10 +11,17 @@ using System.Runtime.Serialization; namespace MediaBrowser.Controller.Playlists { - public class Playlist : Folder + public class Playlist : Folder, IHasShares { public string OwnerUserId { get; set; } + public List Shares { get; set; } + + public Playlist() + { + Shares = new List(); + } + [IgnoreDataMember] protected override bool FilterLinkedChildrenPerUser { @@ -166,7 +173,15 @@ namespace MediaBrowser.Controller.Playlists public override bool IsVisible(User user) { - return base.IsVisible(user) && string.Equals(user.Id.ToString("N"), OwnerUserId); + if (base.IsVisible(user)) + { + var userId = user.Id.ToString("N"); + + return Shares.Any(i => string.Equals(userId, i.UserId, StringComparison.OrdinalIgnoreCase)) || + string.Equals(OwnerUserId, userId, StringComparison.OrdinalIgnoreCase); + } + + return false; } } } diff --git a/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs b/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs index 307ab3cb8..a37f7eb8a 100644 --- a/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs +++ b/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs @@ -819,6 +819,19 @@ namespace MediaBrowser.Controller.Providers break; } + case "Shares": + { + using (var subtree = reader.ReadSubtree()) + { + var hasShares = item as IHasShares; + if (hasShares != null) + { + FetchFromSharesNode(subtree, hasShares); + } + } + break; + } + case "Format3D": { var video = item as Video; @@ -853,6 +866,71 @@ namespace MediaBrowser.Controller.Providers } } + private void FetchFromSharesNode(XmlReader reader, IHasShares item) + { + reader.MoveToContent(); + + while (reader.Read()) + { + if (reader.NodeType == XmlNodeType.Element) + { + switch (reader.Name) + { + case "Share": + { + using (var subtree = reader.ReadSubtree()) + { + var share = GetShareFromNode(subtree); + if (share != null) + { + item.Shares.Add(share); + } + } + break; + } + + default: + reader.Skip(); + break; + } + } + } + } + + private Share GetShareFromNode(XmlReader reader) + { + var share = new Share(); + + reader.MoveToContent(); + + while (reader.Read()) + { + if (reader.NodeType == XmlNodeType.Element) + { + switch (reader.Name) + { + case "UserId": + { + share.UserId = reader.ReadElementContentAsString(); + break; + } + + case "CanEdit": + { + share.CanEdit = string.Equals(reader.ReadElementContentAsString(), true.ToString(), StringComparison.OrdinalIgnoreCase); + break; + } + + default: + reader.Skip(); + break; + } + } + } + + return share; + } + private void FetchFromCountriesNode(XmlReader reader, T item) { reader.MoveToContent(); diff --git a/MediaBrowser.Controller/Sync/ISyncManager.cs b/MediaBrowser.Controller/Sync/ISyncManager.cs index 1d5ab7d3e..31c3c0c6d 100644 --- a/MediaBrowser.Controller/Sync/ISyncManager.cs +++ b/MediaBrowser.Controller/Sync/ISyncManager.cs @@ -1,4 +1,5 @@ using MediaBrowser.Controller.Entities; +using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Querying; using MediaBrowser.Model.Sync; using System.Collections.Generic; @@ -27,7 +28,7 @@ namespace MediaBrowser.Controller.Sync /// The identifier. /// SyncJob. SyncJob GetJob(string id); - + /// /// Cancels the job. /// @@ -51,5 +52,12 @@ namespace MediaBrowser.Controller.Sync /// The item. /// true if XXXX, false otherwise. bool SupportsSync(BaseItem item); + + /// + /// Gets the device profile. + /// + /// The target identifier. + /// DeviceProfile. + DeviceProfile GetDeviceProfile(string targetId); } } diff --git a/MediaBrowser.Controller/Sync/ISyncRepository.cs b/MediaBrowser.Controller/Sync/ISyncRepository.cs index d0cf87182..f1bcd7f07 100644 --- a/MediaBrowser.Controller/Sync/ISyncRepository.cs +++ b/MediaBrowser.Controller/Sync/ISyncRepository.cs @@ -1,6 +1,5 @@ using MediaBrowser.Model.Querying; using MediaBrowser.Model.Sync; -using System.Collections.Generic; using System.Threading.Tasks; namespace MediaBrowser.Controller.Sync @@ -66,8 +65,8 @@ namespace MediaBrowser.Controller.Sync /// /// Gets the job items. /// - /// The job identifier. + /// The query. /// IEnumerable<SyncJobItem>. - IEnumerable GetJobItems(string jobId); + QueryResult GetJobItems(SyncJobItemQuery query); } } diff --git a/MediaBrowser.LocalMetadata/Parsers/PlaylistXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/PlaylistXmlParser.cs index c7f974200..a12724ff7 100644 --- a/MediaBrowser.LocalMetadata/Parsers/PlaylistXmlParser.cs +++ b/MediaBrowser.LocalMetadata/Parsers/PlaylistXmlParser.cs @@ -2,7 +2,9 @@ using MediaBrowser.Controller.Playlists; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Logging; +using System; using System.Collections.Generic; +using System.Linq; using System.Xml; namespace MediaBrowser.LocalMetadata.Parsers @@ -20,7 +22,15 @@ namespace MediaBrowser.LocalMetadata.Parsers { case "OwnerUserId": { - item.OwnerUserId = reader.ReadElementContentAsString(); + var userId = reader.ReadElementContentAsString(); + if (!item.Shares.Any(i => string.Equals(userId, i.UserId, StringComparison.OrdinalIgnoreCase))) + { + item.Shares.Add(new Share + { + UserId = userId, + CanEdit = true + }); + } break; } diff --git a/MediaBrowser.LocalMetadata/Savers/PlaylistXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/PlaylistXmlSaver.cs index 16c437381..76ef4d4bf 100644 --- a/MediaBrowser.LocalMetadata/Savers/PlaylistXmlSaver.cs +++ b/MediaBrowser.LocalMetadata/Savers/PlaylistXmlSaver.cs @@ -57,11 +57,6 @@ namespace MediaBrowser.LocalMetadata.Savers builder.Append(""); - if (!string.IsNullOrEmpty(playlist.OwnerUserId)) - { - builder.Append("" + SecurityElement.Escape(playlist.OwnerUserId) + ""); - } - if (!string.IsNullOrEmpty(playlist.PlaylistMediaType)) { builder.Append("" + SecurityElement.Escape(playlist.PlaylistMediaType) + ""); diff --git a/MediaBrowser.LocalMetadata/Savers/XmlSaverHelpers.cs b/MediaBrowser.LocalMetadata/Savers/XmlSaverHelpers.cs index 93876f474..3e11c994b 100644 --- a/MediaBrowser.LocalMetadata/Savers/XmlSaverHelpers.cs +++ b/MediaBrowser.LocalMetadata/Savers/XmlSaverHelpers.cs @@ -645,6 +645,29 @@ namespace MediaBrowser.LocalMetadata.Savers { AddLinkedChildren(playlist, builder, "PlaylistItems", "PlaylistItem"); } + + var hasShares = item as IHasShares; + if (hasShares != null) + { + + } + } + + public static void AddShares(IHasShares item, StringBuilder builder) + { + builder.Append(""); + + foreach (var share in item.Shares) + { + builder.Append(""); + + builder.Append("" + SecurityElement.Escape(share.UserId) + ""); + builder.Append("" + SecurityElement.Escape(share.CanEdit.ToString().ToLower()) + ""); + + builder.Append(""); + } + + builder.Append(""); } public static void AddChapters(Video item, StringBuilder builder, IItemRepository repository) diff --git a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj index bcd6e08f1..701d7d70b 100644 --- a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj +++ b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj @@ -275,6 +275,9 @@ Devices\DeviceOptions.cs + + Devices\DeviceQuery.cs + Devices\DevicesOptions.cs @@ -1034,6 +1037,12 @@ Sync\SyncJobItem.cs + + SyncJobItemQuery.cs + + + Sync\SyncJobItemStatus.cs + Sync\SyncJobQuery.cs diff --git a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj index 55d18c1b3..9affbb199 100644 --- a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj +++ b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj @@ -240,6 +240,9 @@ Devices\DeviceOptions.cs + + Devices\DeviceQuery.cs + Devices\DevicesOptions.cs @@ -993,6 +996,12 @@ Sync\SyncJobItem.cs + + Sync\SyncJobItemQuery.cs + + + Sync\SyncJobItemStatus.cs + Sync\SyncJobQuery.cs diff --git a/MediaBrowser.Model/Devices/DeviceQuery.cs b/MediaBrowser.Model/Devices/DeviceQuery.cs new file mode 100644 index 000000000..76f7117b6 --- /dev/null +++ b/MediaBrowser.Model/Devices/DeviceQuery.cs @@ -0,0 +1,17 @@ + +namespace MediaBrowser.Model.Devices +{ + public class DeviceQuery + { + /// + /// Gets or sets a value indicating whether [supports content uploading]. + /// + /// null if [supports content uploading] contains no value, true if [supports content uploading]; otherwise, false. + public bool? SupportsContentUploading { get; set; } + /// + /// Gets or sets a value indicating whether [supports unique identifier]. + /// + /// null if [supports unique identifier] contains no value, true if [supports unique identifier]; otherwise, false. + public bool? SupportsUniqueIdentifier { get; set; } + } +} diff --git a/MediaBrowser.Model/Dto/BaseItemDto.cs b/MediaBrowser.Model/Dto/BaseItemDto.cs index b83243ba8..45f681066 100644 --- a/MediaBrowser.Model/Dto/BaseItemDto.cs +++ b/MediaBrowser.Model/Dto/BaseItemDto.cs @@ -186,6 +186,12 @@ namespace MediaBrowser.Model.Dto /// /// The genres. public List Genres { get; set; } + + /// + /// Gets or sets the series genres. + /// + /// The series genres. + public List SeriesGenres { get; set; } /// /// Gets or sets the community rating. diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index 4825cb4cc..5aebe42eb 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -113,6 +113,7 @@ + @@ -365,6 +366,8 @@ + + diff --git a/MediaBrowser.Model/Querying/ItemFields.cs b/MediaBrowser.Model/Querying/ItemFields.cs index a5a906f95..19e30cd8a 100644 --- a/MediaBrowser.Model/Querying/ItemFields.cs +++ b/MediaBrowser.Model/Querying/ItemFields.cs @@ -161,6 +161,11 @@ namespace MediaBrowser.Model.Querying /// ScreenshotImageTags, + /// + /// The series genres + /// + SeriesGenres, + /// /// The series studio /// diff --git a/MediaBrowser.Model/Session/ClientCapabilities.cs b/MediaBrowser.Model/Session/ClientCapabilities.cs index fc0d3a1fb..f2faa0545 100644 --- a/MediaBrowser.Model/Session/ClientCapabilities.cs +++ b/MediaBrowser.Model/Session/ClientCapabilities.cs @@ -13,13 +13,14 @@ namespace MediaBrowser.Model.Session public string MessageCallbackUrl { get; set; } public bool SupportsContentUploading { get; set; } - public bool SupportsDeviceId { get; set; } + public bool SupportsUniqueIdentifier { get; set; } + public bool SupportsSync { get; set; } public ClientCapabilities() { PlayableMediaTypes = new List(); SupportedCommands = new List(); - SupportsDeviceId = true; + SupportsUniqueIdentifier = true; } } } \ No newline at end of file diff --git a/MediaBrowser.Model/Sync/SyncJobItem.cs b/MediaBrowser.Model/Sync/SyncJobItem.cs index 141546eb5..063f7feb2 100644 --- a/MediaBrowser.Model/Sync/SyncJobItem.cs +++ b/MediaBrowser.Model/Sync/SyncJobItem.cs @@ -1,4 +1,5 @@ - +using System; + namespace MediaBrowser.Model.Sync { public class SyncJobItem @@ -37,12 +38,18 @@ namespace MediaBrowser.Model.Sync /// Gets or sets the status. /// /// The status. - public SyncJobStatus Status { get; set; } + public SyncJobItemStatus Status { get; set; } /// /// Gets or sets the current progress. /// /// The current progress. - public double? CurrentProgress { get; set; } + public double? Progress { get; set; } + + /// + /// Gets or sets the date created. + /// + /// The date created. + public DateTime DateCreated { get; set; } } } diff --git a/MediaBrowser.Model/Sync/SyncJobItemQuery.cs b/MediaBrowser.Model/Sync/SyncJobItemQuery.cs new file mode 100644 index 000000000..e9af642ac --- /dev/null +++ b/MediaBrowser.Model/Sync/SyncJobItemQuery.cs @@ -0,0 +1,27 @@ + +namespace MediaBrowser.Model.Sync +{ + public class SyncJobItemQuery + { + /// + /// Gets or sets the start index. + /// + /// The start index. + public int? StartIndex { get; set; } + /// + /// Gets or sets the limit. + /// + /// The limit. + public int? Limit { get; set; } + /// + /// Gets or sets the job identifier. + /// + /// The job identifier. + public string JobId { get; set; } + /// + /// Gets or sets a value indicating whether this instance is completed. + /// + /// null if [is completed] contains no value, true if [is completed]; otherwise, false. + public bool? IsCompleted { get; set; } + } +} diff --git a/MediaBrowser.Model/Sync/SyncJobItemStatus.cs b/MediaBrowser.Model/Sync/SyncJobItemStatus.cs new file mode 100644 index 000000000..3d0579a3c --- /dev/null +++ b/MediaBrowser.Model/Sync/SyncJobItemStatus.cs @@ -0,0 +1,12 @@ + +namespace MediaBrowser.Model.Sync +{ + public enum SyncJobItemStatus + { + Queued = 0, + Converting = 1, + Transferring = 2, + Completed = 3, + Failed = 4 + } +} diff --git a/MediaBrowser.Model/Sync/SyncJobQuery.cs b/MediaBrowser.Model/Sync/SyncJobQuery.cs index 74b35186e..218b3823e 100644 --- a/MediaBrowser.Model/Sync/SyncJobQuery.cs +++ b/MediaBrowser.Model/Sync/SyncJobQuery.cs @@ -13,5 +13,10 @@ namespace MediaBrowser.Model.Sync /// /// The limit. public int? Limit { get; set; } + /// + /// Gets or sets a value indicating whether this instance is completed. + /// + /// null if [is completed] contains no value, true if [is completed]; otherwise, false. + public bool? IsCompleted { get; set; } } } diff --git a/MediaBrowser.Model/Sync/SyncJobStatus.cs b/MediaBrowser.Model/Sync/SyncJobStatus.cs index 42af96509..961ccf544 100644 --- a/MediaBrowser.Model/Sync/SyncJobStatus.cs +++ b/MediaBrowser.Model/Sync/SyncJobStatus.cs @@ -4,9 +4,8 @@ namespace MediaBrowser.Model.Sync public enum SyncJobStatus { Queued = 0, - Converting = 1, - Transferring = 2, - Completed = 3, - Cancelled = 4 + InProgress = 1, + Completed = 2, + CompletedWithError = 3 } } diff --git a/MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs b/MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs index 31136b919..062519f9d 100644 --- a/MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs +++ b/MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs @@ -44,6 +44,11 @@ namespace MediaBrowser.Providers.BoxSets target.LinkedChildren = list; } + + if (replaceData || target.Shares.Count == 0) + { + target.Shares = source.Shares; + } } protected override ItemUpdateType BeforeSave(BoxSet item) diff --git a/MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs b/MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs index 586c17d07..2e407f10c 100644 --- a/MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs +++ b/MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs @@ -7,7 +7,6 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using MediaBrowser.Providers.Manager; using System.Collections.Generic; -using System.Linq; namespace MediaBrowser.Providers.Playlists { @@ -34,18 +33,14 @@ namespace MediaBrowser.Providers.Playlists target.PlaylistMediaType = source.PlaylistMediaType; } - if (replaceData || string.IsNullOrEmpty(target.OwnerUserId)) + if (replaceData || target.Shares.Count == 0) { - target.OwnerUserId = source.OwnerUserId; + target.Shares = source.Shares; } if (mergeMetadataSettings) { - var list = source.LinkedChildren.ToList(); - - list.AddRange(target.LinkedChildren); - - target.LinkedChildren = list; + target.LinkedChildren = source.LinkedChildren; } } } diff --git a/MediaBrowser.Server.Implementations/Collections/CollectionManager.cs b/MediaBrowser.Server.Implementations/Collections/CollectionManager.cs index e346d6d01..9fee27db9 100644 --- a/MediaBrowser.Server.Implementations/Collections/CollectionManager.cs +++ b/MediaBrowser.Server.Implementations/Collections/CollectionManager.cs @@ -72,7 +72,12 @@ namespace MediaBrowser.Server.Implementations.Collections DisplayMediaType = "Collection", Path = path, IsLocked = options.IsLocked, - ProviderIds = options.ProviderIds + ProviderIds = options.ProviderIds, + Shares = options.UserIds.Select(i => new Share + { + UserId = i.ToString("N") + + }).ToList() }; await parentFolder.AddChild(collection, CancellationToken.None).ConfigureAwait(false); @@ -156,7 +161,7 @@ namespace MediaBrowser.Server.Implementations.Collections } itemList.Add(item); - + if (currentLinkedChildren.Any(i => i.Id == itemId)) { throw new ArgumentException("Item already exists in collection"); diff --git a/MediaBrowser.Server.Implementations/Devices/DeviceManager.cs b/MediaBrowser.Server.Implementations/Devices/DeviceManager.cs index e2c729b2d..8c67013ea 100644 --- a/MediaBrowser.Server.Implementations/Devices/DeviceManager.cs +++ b/MediaBrowser.Server.Implementations/Devices/DeviceManager.cs @@ -6,6 +6,7 @@ using MediaBrowser.Controller.Library; using MediaBrowser.Model.Devices; using MediaBrowser.Model.Events; using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Querying; using MediaBrowser.Model.Session; using System; using System.Collections.Generic; @@ -28,7 +29,7 @@ namespace MediaBrowser.Server.Implementations.Devices /// Occurs when [device options updated]. /// public event EventHandler> DeviceOptionsUpdated; - + public DeviceManager(IDeviceRepository repo, IUserManager userManager, IFileSystem fileSystem, ILibraryMonitor libraryMonitor, IConfigurationManager config, ILogger logger) { _repo = repo; @@ -79,9 +80,30 @@ namespace MediaBrowser.Server.Implementations.Devices return _repo.GetDevice(id); } - public IEnumerable GetDevices() + public QueryResult GetDevices(DeviceQuery query) { - return _repo.GetDevices().OrderByDescending(i => i.DateLastModified); + IEnumerable devices = _repo.GetDevices().OrderByDescending(i => i.DateLastModified); + + if (query.SupportsContentUploading.HasValue) + { + var val = query.SupportsContentUploading.Value; + + devices = devices.Where(i => GetCapabilities(i.Id).SupportsContentUploading == val); + } + + if (query.SupportsUniqueIdentifier.HasValue) + { + var val = query.SupportsUniqueIdentifier.Value; + + devices = devices.Where(i => GetCapabilities(i.Id).SupportsUniqueIdentifier == val); + } + + var array = devices.ToArray(); + return new QueryResult + { + Items = array, + TotalRecordCount = array.Length + }; } public Task DeleteDevice(string id) diff --git a/MediaBrowser.Server.Implementations/Dto/DtoService.cs b/MediaBrowser.Server.Implementations/Dto/DtoService.cs index e1e8da5c5..a6f9f0675 100644 --- a/MediaBrowser.Server.Implementations/Dto/DtoService.cs +++ b/MediaBrowser.Server.Implementations/Dto/DtoService.cs @@ -1142,6 +1142,15 @@ namespace MediaBrowser.Server.Implementations.Dto dto.SeasonId = episodeSeason.Id.ToString("N"); dto.SeasonName = episodeSeason.Name; } + + if (fields.Contains(ItemFields.SeriesGenres)) + { + var episodeseries = episode.Series; + if (episodeseries != null) + { + dto.SeriesGenres = episodeseries.Genres.ToList(); + } + } } // Add SeriesInfo diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs index e06720192..bfdfc03ba 100644 --- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs +++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs @@ -736,16 +736,27 @@ namespace MediaBrowser.Server.Implementations.Library } private UserRootFolder _userRootFolder; + private readonly object _syncLock = new object(); public Folder GetUserRootFolder() { if (_userRootFolder == null) { - var userRootPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath; + lock (_syncLock) + { + if (_userRootFolder == null) + { + var userRootPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath; - Directory.CreateDirectory(userRootPath); + Directory.CreateDirectory(userRootPath); - _userRootFolder = GetItemById(GetNewItemId(userRootPath, typeof(UserRootFolder))) as UserRootFolder ?? - (UserRootFolder)ResolvePath(new DirectoryInfo(userRootPath)); + _userRootFolder = GetItemById(GetNewItemId(userRootPath, typeof(UserRootFolder))) as UserRootFolder; + + if (_userRootFolder == null) + { + _userRootFolder = (UserRootFolder)ResolvePath(new DirectoryInfo(userRootPath)); + } + } + } } return _userRootFolder; diff --git a/MediaBrowser.Server.Implementations/Library/MusicManager.cs b/MediaBrowser.Server.Implementations/Library/MusicManager.cs index 7ffbab860..b8c29c19b 100644 --- a/MediaBrowser.Server.Implementations/Library/MusicManager.cs +++ b/MediaBrowser.Server.Implementations/Library/MusicManager.cs @@ -1,6 +1,7 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Playlists; using System; using System.Collections.Generic; using System.Linq; @@ -53,6 +54,18 @@ namespace MediaBrowser.Server.Implementations.Library return GetInstantMixFromGenres(genres, user); } + public IEnumerable