Explorar o código

Merge pull request #954 from MediaBrowser/dev

3.0.5462.0
Luke %!s(int64=10) %!d(string=hai) anos
pai
achega
524293ea79
Modificáronse 100 ficheiros con 1337 adicións e 1062 borrados
  1. 3 15
      MediaBrowser.Api/Devices/DeviceService.cs
  2. 49 0
      MediaBrowser.Api/IHasDtoOptions.cs
  3. 18 18
      MediaBrowser.Api/Images/ImageService.cs
  4. 0 13
      MediaBrowser.Api/ItemLookupService.cs
  5. 3 3
      MediaBrowser.Api/Library/LibraryService.cs
  6. 1 0
      MediaBrowser.Api/MediaBrowser.Api.csproj
  7. 14 1
      MediaBrowser.Api/Movies/MoviesService.cs
  8. 1 1
      MediaBrowser.Api/Movies/TrailersService.cs
  9. 17 0
      MediaBrowser.Api/Music/InstantMixService.cs
  10. 4 4
      MediaBrowser.Api/PackageReviewService.cs
  11. 4 3
      MediaBrowser.Api/PackageService.cs
  12. 0 5
      MediaBrowser.Api/Playback/BaseStreamingService.cs
  13. 1 1
      MediaBrowser.Api/PlaylistService.cs
  14. 11 1
      MediaBrowser.Api/Session/SessionsService.cs
  15. 1 0
      MediaBrowser.Api/StartupWizardService.cs
  16. 24 6
      MediaBrowser.Api/TvShowsService.cs
  17. 1 1
      MediaBrowser.Api/UserLibrary/ArtistsService.cs
  18. 6 6
      MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs
  19. 15 1
      MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs
  20. 4 7
      MediaBrowser.Api/UserLibrary/ItemsService.cs
  21. 13 4
      MediaBrowser.Api/UserLibrary/UserLibraryService.cs
  22. 15 1
      MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs
  23. 5 0
      MediaBrowser.Common.Implementations/Logging/LogHelper.cs
  24. 16 2
      MediaBrowser.Common.Implementations/Networking/BaseNetworkManager.cs
  25. 4 2
      MediaBrowser.Common.Implementations/Security/PluginSecurityManager.cs
  26. 3 2
      MediaBrowser.Common.Implementations/Updates/InstallationManager.cs
  27. 0 8
      MediaBrowser.Common/Constants/Constants.cs
  28. 2 53
      MediaBrowser.Common/Extensions/BaseExtensions.cs
  29. 0 122
      MediaBrowser.Common/IO/FileSystemRepository.cs
  30. 0 2
      MediaBrowser.Common/MediaBrowser.Common.csproj
  31. 2 0
      MediaBrowser.Controller/Collections/CollectionCreationOptions.cs
  32. 3 1
      MediaBrowser.Controller/Devices/IDeviceManager.cs
  33. 0 22
      MediaBrowser.Controller/Dlna/DlnaIconResponse.cs
  34. 3 2
      MediaBrowser.Controller/Dlna/IDlnaManager.cs
  35. 1 1
      MediaBrowser.Controller/Drawing/IImageProcessor.cs
  36. 0 11
      MediaBrowser.Controller/Drawing/ImageFormat.cs
  37. 1 1
      MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs
  38. 28 0
      MediaBrowser.Controller/Drawing/ImageStream.cs
  39. 15 3
      MediaBrowser.Controller/Dto/IDtoService.cs
  40. 92 136
      MediaBrowser.Controller/Entities/BaseItem.cs
  41. 24 45
      MediaBrowser.Controller/Entities/Folder.cs
  42. 13 0
      MediaBrowser.Controller/Entities/Game.cs
  43. 6 0
      MediaBrowser.Controller/Entities/IHasImages.cs
  44. 8 1
      MediaBrowser.Controller/Entities/IHasTrailers.cs
  45. 32 1
      MediaBrowser.Controller/Entities/Movies/BoxSet.cs
  46. 30 1
      MediaBrowser.Controller/Entities/Movies/Movie.cs
  47. 36 0
      MediaBrowser.Controller/Entities/MusicVideo.cs
  48. 15 0
      MediaBrowser.Controller/Entities/Share.cs
  49. 14 1
      MediaBrowser.Controller/Entities/TV/Series.cs
  50. 2 4
      MediaBrowser.Controller/Entities/Trailer.cs
  51. 2 1
      MediaBrowser.Controller/Entities/User.cs
  52. 1 2
      MediaBrowser.Controller/Entities/UserView.cs
  53. 1 1
      MediaBrowser.Controller/Entities/UserViewBuilder.cs
  54. 90 184
      MediaBrowser.Controller/Entities/Video.cs
  55. 31 50
      MediaBrowser.Controller/Library/ILibraryManager.cs
  56. 8 0
      MediaBrowser.Controller/Library/IMusicManager.cs
  57. 14 0
      MediaBrowser.Controller/Library/IUserManager.cs
  58. 4 3
      MediaBrowser.Controller/LiveTv/ILiveTvService.cs
  59. 0 20
      MediaBrowser.Controller/LiveTv/StreamResponseInfo.cs
  60. 2 5
      MediaBrowser.Controller/MediaBrowser.Controller.csproj
  61. 0 6
      MediaBrowser.Controller/Persistence/IUserRepository.cs
  62. 17 2
      MediaBrowser.Controller/Playlists/Playlist.cs
  63. 84 0
      MediaBrowser.Controller/Providers/BaseItemXmlParser.cs
  64. 3 3
      MediaBrowser.Controller/Providers/IImageEnhancer.cs
  65. 2 2
      MediaBrowser.Controller/Providers/IImageSaver.cs
  66. 2 2
      MediaBrowser.Controller/Providers/ILocalImageProvider.cs
  67. 23 0
      MediaBrowser.Controller/Resolvers/IItemResolver.cs
  68. 10 4
      MediaBrowser.Controller/Sync/ISyncManager.cs
  69. 14 0
      MediaBrowser.Controller/Sync/ISyncRepository.cs
  70. 6 2
      MediaBrowser.Dlna/ContentDirectory/ContentDirectory.cs
  71. 182 38
      MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs
  72. 36 5
      MediaBrowser.Dlna/Didl/DidlBuilder.cs
  73. 5 5
      MediaBrowser.Dlna/DlnaManager.cs
  74. BIN=BIN
      MediaBrowser.Dlna/Images/people48.jpg
  75. BIN=BIN
      MediaBrowser.Dlna/Images/people48.png
  76. BIN=BIN
      MediaBrowser.Dlna/Images/people480.jpg
  77. BIN=BIN
      MediaBrowser.Dlna/Images/people480.png
  78. 8 5
      MediaBrowser.Dlna/MediaBrowser.Dlna.csproj
  79. 1 1
      MediaBrowser.Dlna/Profiles/SamsungSmartTvProfile.cs
  80. 6 1
      MediaBrowser.Dlna/Profiles/XboxOneProfile.cs
  81. 0 24
      MediaBrowser.Dlna/Profiles/Xml/Android.xml
  82. 1 1
      MediaBrowser.Dlna/Profiles/Xml/Samsung Smart TV.xml
  83. 0 28
      MediaBrowser.Dlna/Profiles/Xml/Windows 8 RT.xml
  84. 0 24
      MediaBrowser.Dlna/Profiles/Xml/Windows Phone.xml
  85. 3 1
      MediaBrowser.Dlna/Profiles/Xml/Xbox One.xml
  86. 95 43
      MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs
  87. 0 1
      MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj
  88. 11 1
      MediaBrowser.LocalMetadata/Parsers/PlaylistXmlParser.cs
  89. 5 17
      MediaBrowser.LocalMetadata/Providers/GameXmlProvider.cs
  90. 0 37
      MediaBrowser.LocalMetadata/Providers/TrailerXmlProvider.cs
  91. 0 5
      MediaBrowser.LocalMetadata/Savers/PlaylistXmlSaver.cs
  92. 23 0
      MediaBrowser.LocalMetadata/Savers/XmlSaverHelpers.cs
  93. 3 0
      MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
  94. 18 9
      MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj
  95. 18 9
      MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj
  96. 2 1
      MediaBrowser.Model/ApiClient/ConnectionMode.cs
  97. 16 1
      MediaBrowser.Model/ApiClient/IApiClient.cs
  98. 7 3
      MediaBrowser.Model/ApiClient/ServerCredentials.cs
  99. 5 0
      MediaBrowser.Model/ApiClient/ServerDiscoveryInfo.cs
  100. 18 3
      MediaBrowser.Model/ApiClient/ServerInfo.cs

+ 3 - 15
MediaBrowser.Api/Devices/DeviceService.cs

@@ -1,22 +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<List<DeviceInfo>>
+    public class GetDevices : DeviceQuery, IReturn<QueryResult<DeviceInfo>>
     {
-        [ApiMember(Name = "SupportsContentUploading", Description = "SupportsContentUploading", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
-        public bool? SupportsContentUploading { get; set; }
     }
 
     [Route("/Devices", "DELETE", Summary = "Deletes a device")]
@@ -109,16 +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);
-            }
-
-            return ToOptimizedResult(devices.ToList());
+            return ToOptimizedResult(_deviceManager.GetDevices(request));
         }
 
         public object Get(GetCameraUploads request)

+ 49 - 0
MediaBrowser.Api/IHasDtoOptions.cs

@@ -0,0 +1,49 @@
+using MediaBrowser.Model.Dto;
+using MediaBrowser.Model.Entities;
+using System;
+using System.Linq;
+
+namespace MediaBrowser.Api
+{
+    public interface IHasDtoOptions : IHasItemFields
+    {
+        bool? EnableImages { get; set; }
+
+        int? ImageTypeLimit { get; set; }
+
+        string EnableImageTypes { get; set; }
+    }
+
+    public static class HasDtoOptionsExtensions
+    {
+        public static DtoOptions GetDtoOptions(this IHasDtoOptions request)
+        {
+            var options = new DtoOptions();
+
+            options.Fields = request.GetItemFields().ToList();
+            options.EnableImages = request.EnableImages ?? true;
+
+            if (request.ImageTypeLimit.HasValue)
+            {
+                options.ImageTypeLimit = request.ImageTypeLimit.Value;
+            }
+
+            if (string.IsNullOrWhiteSpace(request.EnableImageTypes))
+            {
+                if (options.EnableImages)
+                {
+                    // Get everything
+                    options.ImageTypes = Enum.GetNames(typeof(ImageType))
+                        .Select(i => (ImageType)Enum.Parse(typeof(ImageType), i, true))
+                        .ToList();
+                }
+            }
+            else
+            {
+                options.ImageTypes = (request.EnableImageTypes ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).Select(v => (ImageType)Enum.Parse(typeof(ImageType), v, true)).ToList();
+            }
+
+            return options;
+        }
+    }
+}

+ 18 - 18
MediaBrowser.Api/Images/ImageService.cs

@@ -567,7 +567,7 @@ namespace MediaBrowser.Api.Images
         private async Task<object> GetImageResult(IHasImages item,
             ImageRequest request,
             ItemImageInfo image,
-            ImageOutputFormat format,
+            ImageFormat format,
             List<IImageEnhancer> enhancers,
             string contentType,
             TimeSpan? cacheDuration,
@@ -612,11 +612,11 @@ namespace MediaBrowser.Api.Images
             });
         }
 
-        private ImageOutputFormat GetOutputFormat(ImageRequest request, ItemImageInfo image, List<IImageEnhancer> enhancers)
+        private ImageFormat GetOutputFormat(ImageRequest request, ItemImageInfo image, List<IImageEnhancer> enhancers)
         {
             if (!string.IsNullOrWhiteSpace(request.Format))
             {
-                ImageOutputFormat format;
+                ImageFormat format;
                 if (Enum.TryParse(request.Format, true, out format))
                 {
                     return format;
@@ -627,28 +627,28 @@ namespace MediaBrowser.Api.Images
 
             var clientFormats = GetClientSupportedFormats();
 
-            if (serverFormats.Contains(ImageOutputFormat.Webp) &&
-                clientFormats.Contains(ImageOutputFormat.Webp))
+            if (serverFormats.Contains(ImageFormat.Webp) &&
+                clientFormats.Contains(ImageFormat.Webp))
             {
-                return ImageOutputFormat.Webp;
+                return ImageFormat.Webp;
             }
 
             if (enhancers.Count > 0)
             {
-                return ImageOutputFormat.Png;
+                return ImageFormat.Png;
             }
 
             if (string.Equals(Path.GetExtension(image.Path), ".jpg", StringComparison.OrdinalIgnoreCase) ||
                 string.Equals(Path.GetExtension(image.Path), ".jpeg", StringComparison.OrdinalIgnoreCase))
             {
-                return ImageOutputFormat.Jpg;
+                return ImageFormat.Jpg;
             }
 
             // We can't predict if there will be transparency or not, so play it safe
-            return ImageOutputFormat.Png;
+            return ImageFormat.Png;
         }
 
-        private ImageOutputFormat[] GetClientSupportedFormats()
+        private ImageFormat[] GetClientSupportedFormats()
         {
             if ((Request.AcceptTypes ?? new string[] { }).Contains("image/webp", StringComparer.OrdinalIgnoreCase))
             {
@@ -657,32 +657,32 @@ namespace MediaBrowser.Api.Images
                 // Not displaying properly on iOS
                 if (userAgent.IndexOf("cfnetwork", StringComparison.OrdinalIgnoreCase) == -1)
                 {
-                    return new[] { ImageOutputFormat.Webp, ImageOutputFormat.Jpg, ImageOutputFormat.Png };
+                    return new[] { ImageFormat.Webp, ImageFormat.Jpg, ImageFormat.Png };
                 }
             }
 
-            return new[] { ImageOutputFormat.Jpg, ImageOutputFormat.Png };
+            return new[] { ImageFormat.Jpg, ImageFormat.Png };
         }
 
-        private string GetMimeType(ImageOutputFormat format, string path)
+        private string GetMimeType(ImageFormat format, string path)
         {
-            if (format == ImageOutputFormat.Bmp)
+            if (format == ImageFormat.Bmp)
             {
                 return Common.Net.MimeTypes.GetMimeType("i.bmp");
             }
-            if (format == ImageOutputFormat.Gif)
+            if (format == ImageFormat.Gif)
             {
                 return Common.Net.MimeTypes.GetMimeType("i.gif");
             }
-            if (format == ImageOutputFormat.Jpg)
+            if (format == ImageFormat.Jpg)
             {
                 return Common.Net.MimeTypes.GetMimeType("i.jpg");
             }
-            if (format == ImageOutputFormat.Png)
+            if (format == ImageFormat.Png)
             {
                 return Common.Net.MimeTypes.GetMimeType("i.png");
             }
-            if (format == ImageOutputFormat.Webp)
+            if (format == ImageFormat.Webp)
             {
                 return Common.Net.MimeTypes.GetMimeType("i.webp");
             }

+ 0 - 13
MediaBrowser.Api/ItemLookupService.cs

@@ -37,12 +37,6 @@ namespace MediaBrowser.Api
     {
     }
 
-    [Route("/Items/RemoteSearch/Trailer", "POST")]
-    [Authenticated]
-    public class GetTrailerRemoteSearchResults : RemoteSearchQuery<TrailerInfo>, IReturn<List<RemoteSearchResult>>
-    {
-    }
-
     [Route("/Items/RemoteSearch/AdultVideo", "POST")]
     [Authenticated]
     public class GetAdultVideoRemoteSearchResults : RemoteSearchQuery<ItemLookupInfo>, IReturn<List<RemoteSearchResult>>
@@ -162,13 +156,6 @@ namespace MediaBrowser.Api
             return ToOptimizedResult(result);
         }
 
-        public object Post(GetTrailerRemoteSearchResults request)
-        {
-            var result = _providerManager.GetRemoteSearchResults<Trailer, TrailerInfo>(request, CancellationToken.None).Result;
-
-            return ToOptimizedResult(result);
-        }
-
         public object Post(GetMusicAlbumRemoteSearchResults request)
         {
             var result = _providerManager.GetRemoteSearchResults<MusicAlbum, AlbumInfo>(request, CancellationToken.None).Result;

+ 3 - 3
MediaBrowser.Api/Library/LibraryService.cs

@@ -276,7 +276,7 @@ namespace MediaBrowser.Api.Library
             var fields = Enum.GetNames(typeof(ItemFields))
                     .Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true))
                     .ToList();
-
+            
             var result = new ItemsResult
             {
                 TotalRecordCount = items.Count,
@@ -353,7 +353,7 @@ namespace MediaBrowser.Api.Library
                     .ToList();
 
             BaseItem parent = item.Parent;
-
+            
             while (parent != null)
             {
                 if (user != null)
@@ -607,7 +607,7 @@ namespace MediaBrowser.Api.Library
                     }
                 }
             }
-
+           
             var dtos = themeSongIds.Select(_libraryManager.GetItemById)
                             .OrderBy(i => i.SortName)
                             .Select(i => _dtoService.GetBaseItemDto(i, fields, user, item));

+ 1 - 0
MediaBrowser.Api/MediaBrowser.Api.csproj

@@ -77,6 +77,7 @@
     <Compile Include="Dlna\DlnaServerService.cs" />
     <Compile Include="Dlna\DlnaService.cs" />
     <Compile Include="FilterService.cs" />
+    <Compile Include="IHasDtoOptions.cs" />
     <Compile Include="Library\ChapterService.cs" />
     <Compile Include="Playback\Hls\MpegDashService.cs" />
     <Compile Include="Playback\MediaInfoService.cs" />

+ 14 - 1
MediaBrowser.Api/Movies/MoviesService.cs

@@ -200,6 +200,19 @@ namespace MediaBrowser.Api.Movies
                     .ToList();
             }
 
+            if (item is Video)
+            {
+                var imdbId = item.GetProviderId(MetadataProviders.Imdb);
+
+                // Use imdb id to try to filter duplicates of the same item
+                if (!string.IsNullOrWhiteSpace(imdbId))
+                {
+                    list = list
+                        .Where(i => !string.Equals(imdbId, i.GetProviderId(MetadataProviders.Imdb), StringComparison.OrdinalIgnoreCase))
+                        .ToList();
+                }
+            }
+
             var items = SimilarItemsHelper.GetSimilaritems(item, list, getSimilarityScore).ToList();
 
             IEnumerable<BaseItem> returnItems = items;
@@ -208,7 +221,7 @@ namespace MediaBrowser.Api.Movies
             {
                 returnItems = returnItems.Take(request.Limit.Value);
             }
-
+          
             var result = new ItemsResult
             {
                 Items = returnItems.Select(i => _dtoService.GetBaseItemDto(i, fields, user)).ToArray(),

+ 1 - 1
MediaBrowser.Api/Movies/TrailersService.cs

@@ -92,7 +92,7 @@ namespace MediaBrowser.Api.Movies
                 Logger,
 
                 // Strip out secondary versions
-                request, item => (item is Movie || item is Trailer) && !((Video)item).PrimaryVersionId.HasValue,
+                request, item => (item is Movie) && !((Video)item).PrimaryVersionId.HasValue,
 
                 SimilarItemsHelper.GetSimiliarityScore);
 

+ 17 - 0
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);

+ 4 - 4
MediaBrowser.Api/PackageReviewService.cs

@@ -1,5 +1,4 @@
-using MediaBrowser.Common.Constants;
-using MediaBrowser.Common.Net;
+using MediaBrowser.Common.Net;
 using MediaBrowser.Controller.Net;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Serialization;
@@ -103,6 +102,7 @@ namespace MediaBrowser.Api
         private readonly IHttpClient _httpClient;
         private readonly INetworkManager _netManager;
         private readonly IJsonSerializer _serializer;
+        private const string MbAdminUrl = "https://www.mb3admin.com/admin/";
 
         public PackageReviewService(IHttpClient client, INetworkManager net, IJsonSerializer serializer)
         {
@@ -132,7 +132,7 @@ namespace MediaBrowser.Api
                 parms += "&title=true";
             }
 
-            var result = _httpClient.Get(Constants.MbAdminUrl + "/service/packageReview/retrieve" + parms, CancellationToken.None).Result;
+            var result = _httpClient.Get(MbAdminUrl + "/service/packageReview/retrieve" + parms, CancellationToken.None).Result;
 
             var reviews = _serializer.DeserializeFromStream<List<PackageReviewInfo>>(result);
 
@@ -153,7 +153,7 @@ namespace MediaBrowser.Api
                                { "review", reviewText },
                              };
 
-            Task.WaitAll(_httpClient.Post(Constants.MbAdminUrl + "/service/packageReview/update", review, CancellationToken.None));
+            Task.WaitAll(_httpClient.Post(MbAdminUrl + "/service/packageReview/update", review, CancellationToken.None));
         }
     }
 }

+ 4 - 3
MediaBrowser.Api/PackageService.cs

@@ -173,9 +173,10 @@ namespace MediaBrowser.Api
         public object Get(GetPackage request)
         {
             var packages = _installationManager.GetAvailablePackages(CancellationToken.None, applicationVersion: _appHost.ApplicationVersion).Result;
+            var list = packages.ToList();
 
-            var result = packages.FirstOrDefault(p => string.Equals(p.guid, request.AssemblyGuid ?? "none", StringComparison.OrdinalIgnoreCase))
-                         ?? packages.FirstOrDefault(p => p.name.Equals(request.Name, StringComparison.OrdinalIgnoreCase));
+            var result = list.FirstOrDefault(p => string.Equals(p.guid, request.AssemblyGuid ?? "none", StringComparison.OrdinalIgnoreCase))
+                         ?? list.FirstOrDefault(p => p.name.Equals(request.Name, StringComparison.OrdinalIgnoreCase));
 
             return ToOptimizedResult(result);
         }
@@ -243,4 +244,4 @@ namespace MediaBrowser.Api
         }
     }
 
-}
+}

+ 0 - 5
MediaBrowser.Api/Playback/BaseStreamingService.cs

@@ -2011,11 +2011,6 @@ namespace MediaBrowser.Api.Playback
                 state.EstimateContentLength = transcodingProfile.EstimateContentLength;
                 state.EnableMpegtsM2TsMode = transcodingProfile.EnableMpegtsM2TsMode;
                 state.TranscodeSeekInfo = transcodingProfile.TranscodeSeekInfo;
-
-                if (state.VideoRequest != null && string.IsNullOrWhiteSpace(state.VideoRequest.Profile))
-                {
-                    state.VideoRequest.Profile = transcodingProfile.VideoProfile;
-                }
             }
         }
 

+ 1 - 1
MediaBrowser.Api/PlaylistService.cs

@@ -151,7 +151,7 @@ namespace MediaBrowser.Api
             {
                 items = items.Take(request.Limit.Value).ToArray();
             }
-
+            
             var dtos = items
                    .Select(i => _dtoService.GetBaseItemDto(i.Item2, request.GetItemFields().ToList(), user))
                    .ToArray();

+ 11 - 1
MediaBrowser.Api/Session/SessionsService.cs

@@ -238,6 +238,12 @@ 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 = "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")]
@@ -516,7 +522,11 @@ namespace MediaBrowser.Api.Session
 
                 MessageCallbackUrl = request.MessageCallbackUrl,
 
-                SupportsContentUploading = request.SupportsContentUploading
+                SupportsContentUploading = request.SupportsContentUploading,
+
+                SupportsSync = request.SupportsSync,
+
+                SupportsUniqueIdentifier = request.SupportsUniqueIdentifier
             });
         }
     }

+ 1 - 0
MediaBrowser.Api/StartupWizardService.cs

@@ -61,6 +61,7 @@ namespace MediaBrowser.Api
         public void Post(ReportStartupWizardComplete request)
         {
             _config.Configuration.IsStartupWizardCompleted = true;
+            _config.Configuration.EnableLocalizedGuids = true;
             _config.SaveConfiguration();
         }
 

+ 24 - 6
MediaBrowser.Api/TvShowsService.cs

@@ -19,7 +19,7 @@ namespace MediaBrowser.Api
     /// Class GetNextUpEpisodes
     /// </summary>
     [Route("/Shows/NextUp", "GET", Summary = "Gets a list of next up episodes")]
-    public class GetNextUpEpisodes : IReturn<ItemsResult>, IHasItemFields
+    public class GetNextUpEpisodes : IReturn<ItemsResult>, IHasDtoOptions
     {
         /// <summary>
         /// Gets or sets the user id.
@@ -58,10 +58,19 @@ namespace MediaBrowser.Api
         /// <value>The parent id.</value>
         [ApiMember(Name = "ParentId", Description = "Specify this to localize the search to a specific item or folder. Omit to use the root", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
         public string ParentId { get; set; }
+
+        [ApiMember(Name = "EnableImages", Description = "Optional, include image information in output", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
+        public bool? EnableImages { get; set; }
+
+        [ApiMember(Name = "ImageTypeLimit", Description = "Optional, the max number of images to return, per image type", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
+        public int? ImageTypeLimit { get; set; }
+
+        [ApiMember(Name = "EnableImageTypes", Description = "Optional. The image types to include in the output.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+        public string EnableImageTypes { get; set; }
     }
 
     [Route("/Shows/Upcoming", "GET", Summary = "Gets a list of upcoming episodes")]
-    public class GetUpcomingEpisodes : IReturn<ItemsResult>, IHasItemFields
+    public class GetUpcomingEpisodes : IReturn<ItemsResult>, IHasDtoOptions
     {
         /// <summary>
         /// Gets or sets the user id.
@@ -97,6 +106,15 @@ namespace MediaBrowser.Api
         /// <value>The parent id.</value>
         [ApiMember(Name = "ParentId", Description = "Specify this to localize the search to a specific item or folder. Omit to use the root", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
         public string ParentId { get; set; }
+
+        [ApiMember(Name = "EnableImages", Description = "Optional, include image information in output", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
+        public bool? EnableImages { get; set; }
+
+        [ApiMember(Name = "ImageTypeLimit", Description = "Optional, the max number of images to return, per image type", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
+        public int? ImageTypeLimit { get; set; }
+
+        [ApiMember(Name = "EnableImageTypes", Description = "Optional. The image types to include in the output.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+        public string EnableImageTypes { get; set; }
     }
 
     [Route("/Shows/{Id}/Similar", "GET", Summary = "Finds tv shows similar to a given one.")]
@@ -252,9 +270,9 @@ namespace MediaBrowser.Api
 
             var pagedItems = ApplyPaging(previousEpisodes, request.StartIndex, request.Limit);
 
-            var fields = request.GetItemFields().ToList();
+            var options = request.GetDtoOptions();
 
-            var returnItems = pagedItems.Select(i => _dtoService.GetBaseItemDto(i, fields, user)).ToArray();
+            var returnItems = pagedItems.Select(i => _dtoService.GetBaseItemDto(i, options, user)).ToArray();
 
             var result = new ItemsResult
             {
@@ -283,9 +301,9 @@ namespace MediaBrowser.Api
 
             var user = _userManager.GetUserById(request.UserId);
 
-            var fields = request.GetItemFields().ToList();
+            var options = request.GetDtoOptions();
 
-            var returnItems = result.Items.Select(i => _dtoService.GetBaseItemDto(i, fields, user)).ToArray();
+            var returnItems = result.Items.Select(i => _dtoService.GetBaseItemDto(i, options, user)).ToArray();
 
             return ToOptimizedSerializedResultUsingCache(new ItemsResult
             {

+ 1 - 1
MediaBrowser.Api/UserLibrary/ArtistsService.cs

@@ -89,7 +89,7 @@ namespace MediaBrowser.Api.UserLibrary
             if (request.UserId.HasValue)
             {
                 var user = UserManager.GetUserById(request.UserId.Value);
-
+                
                 return DtoService.GetBaseItemDto(item, fields.ToList(), user);
             }
 

+ 6 - 6
MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs

@@ -127,11 +127,11 @@ namespace MediaBrowser.Api.UserLibrary
 
             }
 
-            var fields = request.GetItemFields().ToList();
-
             var tuples = ibnItems.Select(i => new Tuple<TItemType, List<BaseItem>>(i, i.GetTaggedItems(libraryItems).ToList()));
 
-            var dtos = tuples.Select(i => GetDto(i.Item1, user, fields, i.Item2));
+            var dtoOptions = request.GetDtoOptions();
+
+            var dtos = tuples.Select(i => GetDto(i.Item1, user, dtoOptions, i.Item2));
 
             result.Items = dtos.Where(i => i != null).ToArray();
 
@@ -332,12 +332,12 @@ namespace MediaBrowser.Api.UserLibrary
         /// </summary>
         /// <param name="item">The item.</param>
         /// <param name="user">The user.</param>
-        /// <param name="fields">The fields.</param>
+        /// <param name="options">The options.</param>
         /// <param name="libraryItems">The library items.</param>
         /// <returns>Task{DtoBaseItem}.</returns>
-        private BaseItemDto GetDto(TItemType item, User user, List<ItemFields> fields, List<BaseItem> libraryItems)
+        private BaseItemDto GetDto(TItemType item, User user, DtoOptions options, List<BaseItem> libraryItems)
         {
-            var dto = DtoService.GetItemByNameDto(item, fields, libraryItems, user);
+            var dto = DtoService.GetItemByNameDto(item, options, libraryItems, user);
 
             return dto;
         }

+ 15 - 1
MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs

@@ -7,8 +7,13 @@ using System.Linq;
 
 namespace MediaBrowser.Api.UserLibrary
 {
-    public abstract class BaseItemsRequest : IHasItemFields
+    public abstract class BaseItemsRequest : IHasDtoOptions
     {
+        protected BaseItemsRequest()
+        {
+            EnableImages = true;
+        }
+
         /// <summary>
         /// Skips over a given number of items within the results. Use for paging.
         /// </summary>
@@ -116,6 +121,15 @@ namespace MediaBrowser.Api.UserLibrary
         [ApiMember(Name = "Years", Description = "Optional. If specified, results will be filtered based on production year. This allows multiple, comma delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
         public string Years { get; set; }
 
+        [ApiMember(Name = "EnableImages", Description = "Optional, include image information in output", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
+        public bool? EnableImages { get; set; }
+
+        [ApiMember(Name = "ImageTypeLimit", Description = "Optional, the max number of images to return, per image type", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
+        public int? ImageTypeLimit { get; set; }
+
+        [ApiMember(Name = "EnableImageTypes", Description = "Optional. The image types to include in the output.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+        public string EnableImageTypes { get; set; }
+
         public string[] GetGenres()
         {
             return (Genres ?? string.Empty).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);

+ 4 - 7
MediaBrowser.Api/UserLibrary/ItemsService.cs

@@ -321,15 +321,14 @@ namespace MediaBrowser.Api.UserLibrary
             var result = await GetItemsToSerialize(request, user, parentItem).ConfigureAwait(false);
 
             var isFiltered = result.Item2;
+            var dtoOptions = request.GetDtoOptions();
 
             if (isFiltered)
             {
-                var currentFields = request.GetItemFields().ToList();
-
                 return new ItemsResult
                 {
                     TotalRecordCount = result.Item1.TotalRecordCount,
-                    Items = result.Item1.Items.Select(i => _dtoService.GetBaseItemDto(i, currentFields, user)).ToArray()
+                    Items = result.Item1.Items.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user)).ToArray()
                 };
             }
 
@@ -363,9 +362,7 @@ namespace MediaBrowser.Api.UserLibrary
 
             var pagedItems = ApplyPaging(request, itemsArray);
 
-            var fields = request.GetItemFields().ToList();
-
-            var returnItems = pagedItems.Select(i => _dtoService.GetBaseItemDto(i, fields, user)).ToArray();
+            var returnItems = pagedItems.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user)).ToArray();
 
             return new ItemsResult
             {
@@ -880,7 +877,7 @@ namespace MediaBrowser.Api.UserLibrary
                     var hasTrailers = i as IHasTrailers;
                     if (hasTrailers != null)
                     {
-                        trailerCount = hasTrailers.LocalTrailerIds.Count;
+                        trailerCount = hasTrailers.GetTrailerIds().Count;
                     }
 
                     var ok = val ? trailerCount > 0 : trailerCount == 0;

+ 13 - 4
MediaBrowser.Api/UserLibrary/UserLibraryService.cs

@@ -221,7 +221,7 @@ namespace MediaBrowser.Api.UserLibrary
     }
 
     [Route("/Users/{UserId}/Items/Latest", "GET", Summary = "Gets latest media")]
-    public class GetLatestMedia : IReturn<List<BaseItemDto>>, IHasItemFields
+    public class GetLatestMedia : IReturn<List<BaseItemDto>>, IHasDtoOptions
     {
         /// <summary>
         /// Gets or sets the user id.
@@ -251,6 +251,15 @@ namespace MediaBrowser.Api.UserLibrary
         [ApiMember(Name = "GroupItems", Description = "Whether or not to group items into a parent container.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
         public bool GroupItems { get; set; }
 
+        [ApiMember(Name = "EnableImages", Description = "Optional, include image information in output", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
+        public bool? EnableImages { get; set; }
+
+        [ApiMember(Name = "ImageTypeLimit", Description = "Optional, the max number of images to return, per image type", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
+        public int? ImageTypeLimit { get; set; }
+
+        [ApiMember(Name = "EnableImageTypes", Description = "Optional. The image types to include in the output.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+        public string EnableImageTypes { get; set; }
+        
         public GetLatestMedia()
         {
             Limit = 20;
@@ -362,7 +371,7 @@ namespace MediaBrowser.Api.UserLibrary
                 }
             }
 
-            var fields = request.GetItemFields().ToList();
+            var options = request.GetDtoOptions();
 
             var dtos = list.Select(i =>
             {
@@ -375,7 +384,7 @@ namespace MediaBrowser.Api.UserLibrary
                     childCount = i.Item2.Count;
                 }
 
-                var dto = _dtoService.GetBaseItemDto(item, fields, user);
+                var dto = _dtoService.GetBaseItemDto(item, options, user);
 
                 dto.ChildCount = childCount;
 
@@ -506,7 +515,7 @@ namespace MediaBrowser.Api.UserLibrary
             var hasTrailers = item as IHasTrailers;
             if (hasTrailers != null)
             {
-                trailerIds = hasTrailers.LocalTrailerIds;
+                trailerIds = hasTrailers.GetTrailerIds();
             }
 
             var dtos = trailerIds

+ 15 - 1
MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs

@@ -1,4 +1,5 @@
-using MediaBrowser.Common.Configuration;
+using System.Net.Sockets;
+using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.IO;
 using MediaBrowser.Common.Net;
@@ -134,9 +135,22 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
                 request.Referer = options.Referer;
             }
 
+            //request.ServicePoint.BindIPEndPointDelegate = BindIPEndPointCallback;
+
             return request;
         }
 
+        private static IPEndPoint BindIPEndPointCallback(ServicePoint servicePoint, IPEndPoint remoteEndPoint, int retryCount)
+        {
+            // Prefer local ipv4
+            if (remoteEndPoint.AddressFamily == AddressFamily.InterNetworkV6)
+            {
+                return new IPEndPoint(IPAddress.IPv6Any, 0);
+            }
+
+            return new IPEndPoint(IPAddress.Any, 0);
+        }
+
         private void AddRequestHeaders(HttpWebRequest request, HttpRequestOptions options)
         {
             foreach (var header in options.RequestHeaders.ToList())

+ 5 - 0
MediaBrowser.Common.Implementations/Logging/LogHelper.cs

@@ -15,6 +15,11 @@ namespace MediaBrowser.Common.Implementations.Logging
         /// <returns>StringBuilder.</returns>
         public static StringBuilder GetLogMessage(Exception exception)
         {
+            if (exception == null)
+            {
+                throw new ArgumentNullException("exception");
+            }
+
             var messageText = new StringBuilder();
 
             messageText.AppendLine(exception.Message);

+ 16 - 2
MediaBrowser.Common.Implementations/Networking/BaseNetworkManager.cs

@@ -24,14 +24,28 @@ namespace MediaBrowser.Common.Implementations.Networking
         /// <returns>IPAddress.</returns>
         public IEnumerable<string> GetLocalIpAddresses()
         {
-            var list = GetIPsDefault().Where(i => !IPAddress.IsLoopback(i)).Select(i => i.ToString()).ToList();
+            var list = GetIPsDefault()
+                .Where(i => !IPAddress.IsLoopback(i))
+                .Select(i => i.ToString())
+                .Where(FilterIpAddress)
+                .ToList();
 
             if (list.Count > 0)
             {
                 return list;
             }
 
-            return GetLocalIpAddressesFallback();
+            return GetLocalIpAddressesFallback().Where(FilterIpAddress);
+        }
+
+        private bool FilterIpAddress(string address)
+        {
+            if (address.StartsWith("169.", StringComparison.OrdinalIgnoreCase))
+            {
+                return false;
+            }
+
+            return true;
         }
 
         private bool IsInPrivateAddressSpace(string endpoint)

+ 4 - 2
MediaBrowser.Common.Implementations/Security/PluginSecurityManager.cs

@@ -17,7 +17,9 @@ namespace MediaBrowser.Common.Implementations.Security
     /// </summary>
     public class PluginSecurityManager : ISecurityManager
     {
-        private const string MBValidateUrl = Constants.Constants.MbAdminUrl + "service/registration/validate";
+        private const string MbAdminUrl = "https://www.mb3admin.com/admin/";
+        
+        private const string MBValidateUrl = MbAdminUrl + "service/registration/validate";
 
         /// <summary>
         /// The _is MB supporter
@@ -160,7 +162,7 @@ namespace MediaBrowser.Common.Implementations.Security
                 return new SupporterInfo();
             }
 
-            var url = Constants.Constants.MbAdminUrl + "/service/supporter/retrieve?key=" + key;
+            var url = MbAdminUrl + "/service/supporter/retrieve?key=" + key;
 
             using (var stream = await _httpClient.Get(url, CancellationToken.None).ConfigureAwait(false))
             {

+ 3 - 2
MediaBrowser.Common.Implementations/Updates/InstallationManager.cs

@@ -161,7 +161,7 @@ namespace MediaBrowser.Common.Implementations.Updates
                 { "systemid", _applicationHost.SystemId }
             };
 
-            using (var json = await _httpClient.Post(Constants.Constants.MbAdminUrl + "service/package/retrieveall", data, cancellationToken).ConfigureAwait(false))
+            using (var json = await _httpClient.Post(MbAdminUrl + "service/package/retrieveall", data, cancellationToken).ConfigureAwait(false))
             {
                 cancellationToken.ThrowIfCancellationRequested();
 
@@ -172,6 +172,7 @@ namespace MediaBrowser.Common.Implementations.Updates
         }
 
         private Tuple<List<PackageInfo>, DateTime> _lastPackageListResult;
+        private const string MbAdminUrl = "https://www.mb3admin.com/admin/";
 
         /// <summary>
         /// Gets all available packages.
@@ -203,7 +204,7 @@ namespace MediaBrowser.Common.Implementations.Updates
                 }
             }
 
-            using (var json = await _httpClient.Get(Constants.Constants.MbAdminUrl + "service/MB3Packages.json", cancellationToken).ConfigureAwait(false))
+            using (var json = await _httpClient.Get(MbAdminUrl + "service/MB3Packages.json", cancellationToken).ConfigureAwait(false))
             {
                 cancellationToken.ThrowIfCancellationRequested();
 

+ 0 - 8
MediaBrowser.Common/Constants/Constants.cs

@@ -1,8 +0,0 @@
-
-namespace MediaBrowser.Common.Constants
-{
-    public static class Constants
-    {
-        public const string MbAdminUrl = "http://www.mb3admin.com/admin/";
-    }
-}

+ 2 - 53
MediaBrowser.Common/Extensions/BaseExtensions.cs

@@ -1,5 +1,4 @@
 using System;
-using System.Globalization;
 using System.Security.Cryptography;
 using System.Text;
 using System.Text.RegularExpressions;
@@ -55,28 +54,6 @@ namespace MediaBrowser.Common.Extensions
             return sb.ToString();
         }
 
-        /// <summary>
-        /// Removes the accent.
-        /// </summary>
-        /// <param name="text">The text.</param>
-        /// <returns>System.String.</returns>
-        public static string RemoveAccent(this string text)
-        {
-            var normalizedString = text.Normalize(NormalizationForm.FormD);
-            var stringBuilder = new StringBuilder();
-
-            foreach (var c in normalizedString)
-            {
-                var unicodeCategory = CharUnicodeInfo.GetUnicodeCategory(c);
-                if (unicodeCategory != UnicodeCategory.NonSpacingMark)
-                {
-                    stringBuilder.Append(c);
-                }
-            }
-
-            return stringBuilder.ToString().Normalize(NormalizationForm.FormC);
-        }
-
         /// <summary>
         /// Gets the M d5.
         /// </summary>
@@ -96,6 +73,8 @@ namespace MediaBrowser.Common.Extensions
         /// <param name="str">The STR.</param>
         /// <param name="type">The type.</param>
         /// <returns>Guid.</returns>
+        /// <exception cref="System.ArgumentNullException">type</exception>
+        [Obsolete("Use LibraryManager.GetNewItemId")]
         public static Guid GetMBId(this string str, Type type)
         {
             if (type == null)
@@ -107,35 +86,5 @@ namespace MediaBrowser.Common.Extensions
 
             return key.GetMD5();
         }
-
-        /// <summary>
-        /// Gets the attribute value.
-        /// </summary>
-        /// <param name="str">The STR.</param>
-        /// <param name="attrib">The attrib.</param>
-        /// <returns>System.String.</returns>
-        /// <exception cref="System.ArgumentNullException">attrib</exception>
-        public static string GetAttributeValue(this string str, string attrib)
-        {
-            if (string.IsNullOrEmpty(str))
-            {
-                throw new ArgumentNullException("str");
-            }
-
-            if (string.IsNullOrEmpty(attrib))
-            {
-                throw new ArgumentNullException("attrib");
-            }
-            
-            string srch = "[" + attrib + "=";
-            int start = str.IndexOf(srch, StringComparison.OrdinalIgnoreCase);
-            if (start > -1)
-            {
-                start += srch.Length;
-                int end = str.IndexOf(']', start);
-                return str.Substring(start, end - start);
-            }
-            return null;
-        }
     }
 }

+ 0 - 122
MediaBrowser.Common/IO/FileSystemRepository.cs

@@ -1,122 +0,0 @@
-using MediaBrowser.Common.Extensions;
-using System;
-using System.IO;
-
-namespace MediaBrowser.Common.IO
-{
-    /// <summary>
-    /// This is a wrapper for storing large numbers of files within a directory on a file system.
-    /// Simply pass a filename into GetResourcePath and it will return a full path location of where the file should be stored.
-    /// </summary>
-    public class FileSystemRepository
-    {
-        /// <summary>
-        /// Gets or sets the path.
-        /// </summary>
-        /// <value>The path.</value>
-        protected string Path { get; set; }
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="FileSystemRepository" /> class.
-        /// </summary>
-        /// <param name="path">The path.</param>
-        /// <exception cref="System.ArgumentNullException"></exception>
-        public FileSystemRepository(string path)
-        {
-            if (string.IsNullOrEmpty(path))
-            {
-                throw new ArgumentNullException();
-            }
-
-            Path = path;
-        }
-
-        /// <summary>
-        /// Gets the full path of where a resource should be stored within the repository
-        /// </summary>
-        /// <param name="uniqueName">Name of the unique.</param>
-        /// <param name="fileExtension">The file extension.</param>
-        /// <returns>System.String.</returns>
-        /// <exception cref="System.ArgumentNullException">
-        /// </exception>
-        public string GetResourcePath(string uniqueName, string fileExtension)
-        {
-            if (string.IsNullOrEmpty(uniqueName))
-            {
-                throw new ArgumentNullException("uniqueName");
-            }
-
-            if (string.IsNullOrEmpty(fileExtension))
-            {
-                throw new ArgumentNullException("fileExtension");
-            }
-            
-            var filename = uniqueName.GetMD5() + fileExtension;
-
-            return GetResourcePath(filename);
-        }
-
-        /// <summary>
-        /// Gets the resource path.
-        /// </summary>
-        /// <param name="filename">The filename.</param>
-        /// <returns>System.String.</returns>
-        /// <exception cref="System.ArgumentNullException"></exception>
-        public string GetResourcePath(string filename)
-        {
-            if (string.IsNullOrEmpty(filename))
-            {
-                throw new ArgumentNullException("filename");
-            }
-            
-            var prefix = filename.Substring(0, 1);
-
-            var path = System.IO.Path.Combine(Path, prefix);
-            
-            return System.IO.Path.Combine(path, filename);
-        }
-
-        /// <summary>
-        /// Determines if a resource is present in the repository
-        /// </summary>
-        /// <param name="uniqueName">Name of the unique.</param>
-        /// <param name="fileExtension">The file extension.</param>
-        /// <returns><c>true</c> if the specified unique name contains resource; otherwise, <c>false</c>.</returns>
-        public bool ContainsResource(string uniqueName, string fileExtension)
-        {
-            return ContainsFilePath(GetResourcePath(uniqueName, fileExtension));
-        }
-
-        /// <summary>
-        /// Determines if a file with a given name is present in the repository
-        /// </summary>
-        /// <param name="filename">The filename.</param>
-        /// <returns><c>true</c> if the specified filename contains filename; otherwise, <c>false</c>.</returns>
-        /// <exception cref="System.ArgumentNullException"></exception>
-        public bool ContainsFilename(string filename)
-        {
-            if (string.IsNullOrEmpty(filename))
-            {
-                throw new ArgumentNullException();
-            }
-
-            return ContainsFilePath(GetResourcePath(filename));
-        }
-
-        /// <summary>
-        /// Determines if a file is present in the repository
-        /// </summary>
-        /// <param name="path">The path.</param>
-        /// <returns><c>true</c> if [contains file path] [the specified path]; otherwise, <c>false</c>.</returns>
-        /// <exception cref="System.ArgumentNullException"></exception>
-        public bool ContainsFilePath(string path)
-        {
-            if (string.IsNullOrEmpty(path))
-            {
-                throw new ArgumentNullException();
-            }
-            
-            return File.Exists(path);
-        }
-    }
-}

+ 0 - 2
MediaBrowser.Common/MediaBrowser.Common.csproj

@@ -57,12 +57,10 @@
     <Compile Include="Configuration\ConfigurationUpdateEventArgs.cs" />
     <Compile Include="Configuration\IConfigurationManager.cs" />
     <Compile Include="Configuration\IConfigurationFactory.cs" />
-    <Compile Include="Constants\Constants.cs" />
     <Compile Include="Events\EventHelper.cs" />
     <Compile Include="Extensions\BaseExtensions.cs" />
     <Compile Include="Extensions\ResourceNotFoundException.cs" />
     <Compile Include="IDependencyContainer.cs" />
-    <Compile Include="IO\FileSystemRepository.cs" />
     <Compile Include="IO\IFileSystem.cs" />
     <Compile Include="IO\ProgressStream.cs" />
     <Compile Include="IO\StreamDefaults.cs" />

+ 2 - 0
MediaBrowser.Controller/Collections/CollectionCreationOptions.cs

@@ -15,11 +15,13 @@ namespace MediaBrowser.Controller.Collections
         public Dictionary<string, string> ProviderIds { get; set; }
 
         public List<Guid> ItemIdList { get; set; }
+        public List<Guid> UserIds { get; set; }
 
         public CollectionCreationOptions()
         {
             ProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
             ItemIdList = new List<Guid>();
+            UserIds = new List<Guid>();
         }
     }
 }

+ 3 - 1
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
         /// <summary>
         /// Gets the devices.
         /// </summary>
+        /// <param name="query">The query.</param>
         /// <returns>IEnumerable&lt;DeviceInfo&gt;.</returns>
-        IEnumerable<DeviceInfo> GetDevices();
+        QueryResult<DeviceInfo> GetDevices(DeviceQuery query);
 
         /// <summary>
         /// Deletes the device.

+ 0 - 22
MediaBrowser.Controller/Dlna/DlnaIconResponse.cs

@@ -1,22 +0,0 @@
-using MediaBrowser.Controller.Drawing;
-using System;
-using System.IO;
-
-namespace MediaBrowser.Controller.Dlna
-{
-    public class DlnaIconResponse : IDisposable
-    {
-        public Stream Stream { get; set; }
-
-        public ImageFormat Format { get; set; }
-
-        public void Dispose()
-        {
-            if (Stream != null)
-            {
-                Stream.Dispose();
-                Stream = null;
-            }
-        }
-    }
-}

+ 3 - 2
MediaBrowser.Controller/Dlna/IDlnaManager.cs

@@ -1,4 +1,5 @@
-using MediaBrowser.Model.Dlna;
+using MediaBrowser.Controller.Drawing;
+using MediaBrowser.Model.Dlna;
 using System.Collections.Generic;
 
 namespace MediaBrowser.Controller.Dlna
@@ -69,6 +70,6 @@ namespace MediaBrowser.Controller.Dlna
         /// </summary>
         /// <param name="filename">The filename.</param>
         /// <returns>DlnaIconResponse.</returns>
-        DlnaIconResponse GetIcon(string filename);
+        ImageStream GetIcon(string filename);
     }
 }

+ 1 - 1
MediaBrowser.Controller/Drawing/IImageProcessor.cs

@@ -94,6 +94,6 @@ namespace MediaBrowser.Controller.Drawing
         /// Gets the supported image output formats.
         /// </summary>
         /// <returns>ImageOutputFormat[].</returns>
-        ImageOutputFormat[] GetSupportedImageOutputFormats();
+        ImageFormat[] GetSupportedImageOutputFormats();
     }
 }

+ 0 - 11
MediaBrowser.Controller/Drawing/ImageFormat.cs

@@ -1,11 +0,0 @@
-
-namespace MediaBrowser.Controller.Drawing
-{
-    public enum ImageFormat
-    {
-        Jpg,
-        Png,
-        Gif,
-        Bmp
-    }
-}

+ 1 - 1
MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs

@@ -29,7 +29,7 @@ namespace MediaBrowser.Controller.Drawing
 
         public List<IImageEnhancer> Enhancers { get; set; }
 
-        public ImageOutputFormat OutputFormat { get; set; }
+        public ImageFormat OutputFormat { get; set; }
 
         public bool AddPlayedIndicator { get; set; }
 

+ 28 - 0
MediaBrowser.Controller/Drawing/ImageStream.cs

@@ -0,0 +1,28 @@
+using MediaBrowser.Model.Drawing;
+using System;
+using System.IO;
+
+namespace MediaBrowser.Controller.Drawing
+{
+    public class ImageStream : IDisposable
+    {
+        /// <summary>
+        /// Gets or sets the stream.
+        /// </summary>
+        /// <value>The stream.</value>
+        public Stream Stream { get; set; }
+        /// <summary>
+        /// Gets or sets the format.
+        /// </summary>
+        /// <value>The format.</value>
+        public ImageFormat Format { get; set; }
+
+        public void Dispose()
+        {
+            if (Stream != null)
+            {
+                Stream.Dispose();
+            }
+        }
+    }
+}

+ 15 - 3
MediaBrowser.Controller/Dto/IDtoService.cs

@@ -22,7 +22,8 @@ namespace MediaBrowser.Controller.Dto
         /// </summary>
         /// <param name="dto">The dto.</param>
         /// <param name="item">The item.</param>
-        void AttachPrimaryImageAspectRatio(IItemDto dto, IHasImages item);
+        /// <param name="fields">The fields.</param>
+        void AttachPrimaryImageAspectRatio(IItemDto dto, IHasImages item, List<ItemFields> fields);
 
         /// <summary>
         /// Gets the base item dto.
@@ -34,6 +35,16 @@ namespace MediaBrowser.Controller.Dto
         /// <returns>Task{BaseItemDto}.</returns>
         BaseItemDto GetBaseItemDto(BaseItem item, List<ItemFields> fields, User user = null, BaseItem owner = null);
 
+        /// <summary>
+        /// Gets the base item dto.
+        /// </summary>
+        /// <param name="item">The item.</param>
+        /// <param name="options">The options.</param>
+        /// <param name="user">The user.</param>
+        /// <param name="owner">The owner.</param>
+        /// <returns>BaseItemDto.</returns>
+        BaseItemDto GetBaseItemDto(BaseItem item, DtoOptions options, User user = null, BaseItem owner = null);
+        
         /// <summary>
         /// Gets the chapter information dto.
         /// </summary>
@@ -51,12 +62,13 @@ namespace MediaBrowser.Controller.Dto
         /// <summary>
         /// Gets the item by name dto.
         /// </summary>
+        /// <typeparam name="T"></typeparam>
         /// <param name="item">The item.</param>
-        /// <param name="fields">The fields.</param>
+        /// <param name="options">The options.</param>
         /// <param name="taggedItems">The tagged items.</param>
         /// <param name="user">The user.</param>
         /// <returns>BaseItemDto.</returns>
-        BaseItemDto GetItemByNameDto<T>(T item, List<ItemFields> fields, List<BaseItem> taggedItems, User user = null)
+        BaseItemDto GetItemByNameDto<T>(T item, DtoOptions options, List<BaseItem> taggedItems, User user = null)
             where T : BaseItem, IItemByName;
     }
 }

+ 92 - 136
MediaBrowser.Controller/Entities/BaseItem.cs

@@ -53,16 +53,6 @@ namespace MediaBrowser.Controller.Entities
         public static string ThemeSongFilename = "theme";
         public static string ThemeVideosFolderName = "backdrops";
 
-        public static List<KeyValuePair<string, ExtraType>> ExtraSuffixes = new List<KeyValuePair<string, ExtraType>>
-        {
-            new KeyValuePair<string,ExtraType>("-trailer", ExtraType.Trailer),
-            new KeyValuePair<string,ExtraType>("-deleted", ExtraType.DeletedScene),
-            new KeyValuePair<string,ExtraType>("-behindthescenes", ExtraType.BehindTheScenes),
-            new KeyValuePair<string,ExtraType>("-interview", ExtraType.Interview),
-            new KeyValuePair<string,ExtraType>("-scene", ExtraType.Scene),
-            new KeyValuePair<string,ExtraType>("-sample", ExtraType.Sample)
-        };
-
         public List<ItemImageInfo> ImageInfos { get; set; }
 
         [IgnoreDataMember]
@@ -222,6 +212,20 @@ namespace MediaBrowser.Controller.Entities
             }
         }
 
+        [IgnoreDataMember]
+        public virtual string FileNameWithoutExtension
+        {
+            get
+            {
+                if (LocationType == LocationType.FileSystem)
+                {
+                    return System.IO.Path.GetFileNameWithoutExtension(Path);
+                }
+
+                return null;
+            }
+        }
+
         /// <summary>
         /// This is just a helper for convenience
         /// </summary>
@@ -361,6 +365,15 @@ namespace MediaBrowser.Controller.Entities
             }
         }
 
+        public bool ContainsPerson(string name)
+        {
+            if (string.IsNullOrWhiteSpace(name))
+            {
+                throw new ArgumentNullException("name");
+            }
+            return People.Any(i => string.Equals(i.Name, name, StringComparison.OrdinalIgnoreCase));
+        }
+
         public string GetInternalMetadataPath()
         {
             return GetInternalMetadataPath(ConfigurationManager.ApplicationPaths.InternalMetadataPath);
@@ -593,118 +606,6 @@ namespace MediaBrowser.Controller.Entities
             return PlayAccess.Full;
         }
 
-        /// <summary>
-        /// Loads local trailers from the file system
-        /// </summary>
-        /// <returns>List{Video}.</returns>
-        private IEnumerable<Trailer> LoadLocalTrailers(List<FileSystemInfo> fileSystemChildren, IDirectoryService directoryService)
-        {
-            var files = fileSystemChildren.OfType<DirectoryInfo>()
-                .Where(i => string.Equals(i.Name, TrailerFolderName, StringComparison.OrdinalIgnoreCase))
-                .SelectMany(i => i.EnumerateFiles("*", SearchOption.TopDirectoryOnly))
-                .ToList();
-
-            var extraTypes = new List<ExtraType> { ExtraType.Trailer };
-            var suffixes = ExtraSuffixes.Where(i => extraTypes.Contains(i.Value))
-                .Select(i => i.Key)
-                .ToList();
-
-            files.AddRange(fileSystemChildren.OfType<FileInfo>()
-                .Where(i =>
-                {
-                    var nameEithoutExtension = FileSystem.GetFileNameWithoutExtension(i);
-
-                    if (!suffixes.Any(s => nameEithoutExtension.EndsWith(s, StringComparison.OrdinalIgnoreCase)))
-                    {
-                        return false;
-                    }
-
-                    return !string.Equals(Path, i.FullName, StringComparison.OrdinalIgnoreCase);
-                }));
-
-            return LibraryManager.ResolvePaths<Trailer>(files, directoryService, null).Select(video =>
-            {
-                // Try to retrieve it from the db. If we don't find it, use the resolved version
-                var dbItem = LibraryManager.GetItemById(video.Id) as Trailer;
-
-                if (dbItem != null)
-                {
-                    video = dbItem;
-                }
-
-                if (video != null)
-                {
-                    video.ExtraType = ExtraType.Trailer;
-                }
-
-                return video;
-
-                // Sort them so that the list can be easily compared for changes
-            }).OrderBy(i => i.Path).ToList();
-        }
-
-        protected IEnumerable<Video> LoadSpecialFeatures(List<FileSystemInfo> fileSystemChildren, IDirectoryService directoryService)
-        {
-            var files = fileSystemChildren.OfType<DirectoryInfo>()
-                .Where(i => string.Equals(i.Name, "extras", StringComparison.OrdinalIgnoreCase) || string.Equals(i.Name, "specials", StringComparison.OrdinalIgnoreCase))
-                .SelectMany(i => i.EnumerateFiles("*", SearchOption.TopDirectoryOnly))
-                .ToList();
-
-            var extraTypes = new List<ExtraType> { ExtraType.BehindTheScenes, ExtraType.DeletedScene, ExtraType.Interview, ExtraType.Sample, ExtraType.Scene, ExtraType.Clip };
-            var suffixes = ExtraSuffixes.Where(i => extraTypes.Contains(i.Value))
-                .Select(i => i.Key)
-                .ToList();
-
-            files.AddRange(fileSystemChildren.OfType<FileInfo>()
-                .Where(i =>
-                {
-                    var nameEithoutExtension = FileSystem.GetFileNameWithoutExtension(i);
-
-                    if (!suffixes.Any(s => nameEithoutExtension.EndsWith(s, StringComparison.OrdinalIgnoreCase)))
-                    {
-                        return false;
-                    }
-
-                    return !string.Equals(Path, i.FullName, StringComparison.OrdinalIgnoreCase);
-                }));
-
-            return LibraryManager.ResolvePaths<Video>(files, directoryService, null).Select(video =>
-            {
-                // Try to retrieve it from the db. If we don't find it, use the resolved version
-                var dbItem = LibraryManager.GetItemById(video.Id) as Video;
-
-                if (dbItem != null)
-                {
-                    video = dbItem;
-                }
-
-                if (video != null)
-                {
-                    SetExtraTypeFromFilename(video);
-                }
-
-                return video;
-
-                // Sort them so that the list can be easily compared for changes
-            }).OrderBy(i => i.Path).ToList();
-        }
-
-        private void SetExtraTypeFromFilename(Video item)
-        {
-            var name = System.IO.Path.GetFileNameWithoutExtension(item.Path) ?? string.Empty;
-
-            foreach (var suffix in ExtraSuffixes)
-            {
-                if (name.EndsWith(suffix.Key, StringComparison.OrdinalIgnoreCase))
-                {
-                    item.ExtraType = suffix.Value;
-                    return;
-                }
-            }
-
-            item.ExtraType = ExtraType.Clip;
-        }
-
         /// <summary>
         /// Loads the theme songs.
         /// </summary>
@@ -721,7 +622,9 @@ namespace MediaBrowser.Controller.Entities
                 .Where(i => string.Equals(FileSystem.GetFileNameWithoutExtension(i), ThemeSongFilename, StringComparison.OrdinalIgnoreCase))
                 );
 
-            return LibraryManager.ResolvePaths<Audio.Audio>(files, directoryService, null).Select(audio =>
+            return LibraryManager.ResolvePaths(files, directoryService, null)
+                .OfType<Audio.Audio>()
+                .Select(audio =>
             {
                 // Try to retrieve it from the db. If we don't find it, use the resolved version
                 var dbItem = LibraryManager.GetItemById(audio.Id) as Audio.Audio;
@@ -731,10 +634,7 @@ namespace MediaBrowser.Controller.Entities
                     audio = dbItem;
                 }
 
-                if (audio != null)
-                {
-                    audio.ExtraType = ExtraType.ThemeSong;
-                }
+                audio.ExtraType = ExtraType.ThemeSong;
 
                 return audio;
 
@@ -752,7 +652,9 @@ namespace MediaBrowser.Controller.Entities
                 .Where(i => string.Equals(i.Name, ThemeVideosFolderName, StringComparison.OrdinalIgnoreCase))
                 .SelectMany(i => i.EnumerateFiles("*", SearchOption.TopDirectoryOnly));
 
-            return LibraryManager.ResolvePaths<Video>(files, directoryService, null).Select(item =>
+            return LibraryManager.ResolvePaths(files, directoryService, null)
+                .OfType<Video>()
+                .Select(item =>
             {
                 // Try to retrieve it from the db. If we don't find it, use the resolved version
                 var dbItem = LibraryManager.GetItemById(item.Id) as Video;
@@ -762,10 +664,7 @@ namespace MediaBrowser.Controller.Entities
                     item = dbItem;
                 }
 
-                if (item != null)
-                {
-                    item.ExtraType = ExtraType.ThemeVideo;
-                }
+                item.ExtraType = ExtraType.ThemeVideo;
 
                 return item;
 
@@ -870,7 +769,8 @@ namespace MediaBrowser.Controller.Entities
 
         private async Task<bool> RefreshLocalTrailers(IHasTrailers item, MetadataRefreshOptions options, List<FileSystemInfo> fileSystemChildren, CancellationToken cancellationToken)
         {
-            var newItems = LoadLocalTrailers(fileSystemChildren, options.DirectoryService).ToList();
+            var newItems = LibraryManager.FindTrailers(this, fileSystemChildren, options.DirectoryService).ToList();
+
             var newItemIds = newItems.Select(i => i.Id).ToList();
 
             var itemsChanged = !item.LocalTrailerIds.SequenceEqual(newItemIds);
@@ -995,6 +895,28 @@ namespace MediaBrowser.Controller.Entities
             return Id.ToString();
         }
 
+        internal virtual bool IsValidFromResolver(BaseItem newItem)
+        {
+            var current = this;
+
+            var currentAsPlaceHolder = current as ISupportsPlaceHolders;
+
+            if (currentAsPlaceHolder != null)
+            {
+                var newHasPlaceHolder = newItem as ISupportsPlaceHolders;
+
+                if (newHasPlaceHolder != null)
+                {
+                    if (currentAsPlaceHolder.IsPlaceHolder != newHasPlaceHolder.IsPlaceHolder)
+                    {
+                        return false;
+                    }
+                }
+            }
+
+            return current.IsInMixedFolder == newItem.IsInMixedFolder;
+        }
+
         /// <summary>
         /// Gets the preferred metadata language.
         /// </summary>
@@ -1390,7 +1312,7 @@ namespace MediaBrowser.Controller.Entities
         /// <param name="resetPosition">if set to <c>true</c> [reset position].</param>
         /// <returns>Task.</returns>
         /// <exception cref="System.ArgumentNullException"></exception>
-        public virtual async Task MarkPlayed(User user, 
+        public virtual async Task MarkPlayed(User user,
             DateTime? datePlayed,
             bool resetPosition)
         {
@@ -1778,7 +1700,8 @@ namespace MediaBrowser.Controller.Entities
                 Name = Name,
                 ProviderIds = ProviderIds,
                 IndexNumber = IndexNumber,
-                ParentIndexNumber = ParentIndexNumber
+                ParentIndexNumber = ParentIndexNumber,
+                Year = ProductionYear
             };
         }
 
@@ -1820,9 +1743,42 @@ namespace MediaBrowser.Controller.Entities
                 if (pct > 0)
                 {
                     pct = userData.PlaybackPositionTicks / pct;
-                    dto.PlayedPercentage = 100 * pct;
+
+                    if (pct > 0)
+                    {
+                        dto.PlayedPercentage = 100 * pct;
+                    }
                 }
             }
         }
+
+        protected Task RefreshMetadataForOwnedVideo(MetadataRefreshOptions options, string path, CancellationToken cancellationToken)
+        {
+            var newOptions = new MetadataRefreshOptions(options.DirectoryService)
+            {
+                ImageRefreshMode = options.ImageRefreshMode,
+                MetadataRefreshMode = options.MetadataRefreshMode,
+                ReplaceAllMetadata = options.ReplaceAllMetadata
+            };
+
+            var id = LibraryManager.GetNewItemId(path, typeof(Video));
+
+            // Try to retrieve it from the db. If we don't find it, use the resolved version
+            var video = LibraryManager.GetItemById(id) as Video;
+
+            if (video == null)
+            {
+                video = LibraryManager.ResolvePath(new FileInfo(path)) as Video;
+
+                newOptions.ForceSave = true;
+            }
+
+            if (video == null)
+            {
+                return Task.FromResult(true);
+            }
+
+            return video.RefreshMetadata(newOptions, cancellationToken);
+        }
     }
 }

+ 24 - 45
MediaBrowser.Controller/Entities/Folder.cs

@@ -1,5 +1,4 @@
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.Progress;
+using MediaBrowser.Common.Progress;
 using MediaBrowser.Controller.Entities.TV;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Localization;
@@ -59,6 +58,20 @@ namespace MediaBrowser.Controller.Entities
             }
         }
 
+        [IgnoreDataMember]
+        public override string FileNameWithoutExtension
+        {
+            get
+            {
+                if (LocationType == LocationType.FileSystem)
+                {
+                    return System.IO.Path.GetFileName(Path);
+                }
+
+                return null;
+            }
+        }
+
         /// <summary>
         /// Gets or sets a value indicating whether this instance is physical root.
         /// </summary>
@@ -103,7 +116,7 @@ namespace MediaBrowser.Controller.Entities
 
             if (item.Id == Guid.Empty)
             {
-                item.Id = item.Path.GetMBId(item.GetType());
+                item.Id = LibraryManager.GetNewItemId(item.Path, item.GetType());
             }
 
             if (ActualChildren.Any(i => i.Id == item.Id))
@@ -364,47 +377,7 @@ namespace MediaBrowser.Controller.Entities
 
         private bool IsValidFromResolver(BaseItem current, BaseItem newItem)
         {
-            var currentAsVideo = current as Video;
-
-            if (currentAsVideo != null)
-            {
-                var newAsVideo = newItem as Video;
-
-                if (newAsVideo != null)
-                {
-                    if (currentAsVideo.IsPlaceHolder != newAsVideo.IsPlaceHolder)
-                    {
-                        return false;
-                    }
-                    if (currentAsVideo.IsMultiPart != newAsVideo.IsMultiPart)
-                    {
-                        return false;
-                    }
-                    if (currentAsVideo.HasLocalAlternateVersions != newAsVideo.HasLocalAlternateVersions)
-                    {
-                        return false;
-                    }
-                }
-            }
-            else
-            {
-                var currentAsPlaceHolder = current as ISupportsPlaceHolders;
-
-                if (currentAsPlaceHolder != null)
-                {
-                    var newHasPlaceHolder = newItem as ISupportsPlaceHolders;
-
-                    if (newHasPlaceHolder != null)
-                    {
-                        if (currentAsPlaceHolder.IsPlaceHolder != newHasPlaceHolder.IsPlaceHolder)
-                        {
-                            return false;
-                        }
-                    }
-                }
-            }
-
-            return current.IsInMixedFolder == newItem.IsInMixedFolder;
+            return current.IsValidFromResolver(newItem);
         }
 
         /// <summary>
@@ -737,7 +710,7 @@ namespace MediaBrowser.Controller.Entities
         {
             var collectionType = LibraryManager.FindCollectionType(this);
 
-            return LibraryManager.ResolvePaths<BaseItem>(GetFileSystemChildren(directoryService), directoryService, this, collectionType);
+            return LibraryManager.ResolvePaths(GetFileSystemChildren(directoryService), directoryService, this, collectionType);
         }
 
         /// <summary>
@@ -782,6 +755,12 @@ namespace MediaBrowser.Controller.Entities
 
         private BaseItem RetrieveChild(BaseItem child)
         {
+            if (child.Id == Guid.Empty)
+            {
+                Logger.Error("Item found with empty Id: " + (child.Path ?? child.Name));
+                return null;
+            }
+
             var item = LibraryManager.GetMemoryItemById(child.Id);
 
             if (item != null)

+ 13 - 0
MediaBrowser.Controller/Entities/Game.cs

@@ -28,12 +28,14 @@ namespace MediaBrowser.Controller.Entities
             SoundtrackIds = new List<Guid>();
             RemoteTrailers = new List<MediaUrl>();
             LocalTrailerIds = new List<Guid>();
+            RemoteTrailerIds = new List<Guid>();
             ThemeSongIds = new List<Guid>();
             ThemeVideoIds = new List<Guid>();
             Tags = new List<string>();
         }
 
         public List<Guid> LocalTrailerIds { get; set; }
+        public List<Guid> RemoteTrailerIds { get; set; }
 
         /// <summary>
         /// Gets or sets the tags.
@@ -119,5 +121,16 @@ namespace MediaBrowser.Controller.Entities
 
             return id;
         }
+
+        /// <summary>
+        /// Gets the trailer ids.
+        /// </summary>
+        /// <returns>List&lt;Guid&gt;.</returns>
+        public List<Guid> GetTrailerIds()
+        {
+            var list = LocalTrailerIds.ToList();
+            list.AddRange(RemoteTrailerIds);
+            return list;
+        }
     }
 }

+ 6 - 0
MediaBrowser.Controller/Entities/IHasImages.cs

@@ -21,6 +21,12 @@ namespace MediaBrowser.Controller.Entities
         /// <value>The path.</value>
         string Path { get; set; }
 
+        /// <summary>
+        /// Gets the file name without extension.
+        /// </summary>
+        /// <value>The file name without extension.</value>
+        string FileNameWithoutExtension { get; }
+
         /// <summary>
         /// Gets the identifier.
         /// </summary>

+ 8 - 1
MediaBrowser.Controller/Entities/IHasTrailers.cs

@@ -4,7 +4,7 @@ using System.Collections.Generic;
 
 namespace MediaBrowser.Controller.Entities
 {
-    public interface IHasTrailers
+    public interface IHasTrailers : IHasProviderIds
     {
         /// <summary>
         /// Gets or sets the remote trailers.
@@ -17,5 +17,12 @@ namespace MediaBrowser.Controller.Entities
         /// </summary>
         /// <value>The local trailer ids.</value>
         List<Guid> LocalTrailerIds { get; set; }
+        List<Guid> RemoteTrailerIds { get; set; }
+
+        /// <summary>
+        /// Gets the trailer ids.
+        /// </summary>
+        /// <returns>List&lt;Guid&gt;.</returns>
+        List<Guid> GetTrailerIds();
     }
 }

+ 32 - 1
MediaBrowser.Controller/Entities/Movies/BoxSet.cs

@@ -15,15 +15,19 @@ namespace MediaBrowser.Controller.Entities.Movies
     /// <summary>
     /// Class BoxSet
     /// </summary>
-    public class BoxSet : Folder, IHasTrailers, IHasKeywords, IHasPreferredMetadataLanguage, IHasDisplayOrder, IHasLookupInfo<BoxSetInfo>, IMetadataContainer
+    public class BoxSet : Folder, IHasTrailers, IHasKeywords, IHasPreferredMetadataLanguage, IHasDisplayOrder, IHasLookupInfo<BoxSetInfo>, IMetadataContainer, IHasShares
     {
+        public List<Share> Shares { get; set; }
+        
         public BoxSet()
         {
             RemoteTrailers = new List<MediaUrl>();
             LocalTrailerIds = new List<Guid>();
+            RemoteTrailerIds = new List<Guid>();
 
             DisplayOrder = ItemSortBy.PremiereDate;
             Keywords = new List<string>();
+            Shares = new List<Share>();
         }
 
         protected override bool FilterLinkedChildrenPerUser
@@ -35,6 +39,7 @@ namespace MediaBrowser.Controller.Entities.Movies
         }
 
         public List<Guid> LocalTrailerIds { get; set; }
+        public List<Guid> RemoteTrailerIds { get; set; }
 
         /// <summary>
         /// Gets or sets the remote trailers.
@@ -76,6 +81,17 @@ namespace MediaBrowser.Controller.Entities.Movies
             }
         }
 
+        /// <summary>
+        /// Gets the trailer ids.
+        /// </summary>
+        /// <returns>List&lt;Guid&gt;.</returns>
+        public List<Guid> GetTrailerIds()
+        {
+            var list = LocalTrailerIds.ToList();
+            list.AddRange(RemoteTrailerIds);
+            return list;
+        }
+
         public override IEnumerable<BaseItem> GetChildren(User user, bool includeLinkedChildren)
         {
             var children = base.GetChildren(user, includeLinkedChildren);
@@ -147,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;
+        }
     }
 }

+ 30 - 1
MediaBrowser.Controller/Entities/Movies/Movie.cs

@@ -36,6 +36,7 @@ namespace MediaBrowser.Controller.Entities.Movies
             SoundtrackIds = new List<Guid>();
             RemoteTrailers = new List<MediaUrl>();
             LocalTrailerIds = new List<Guid>();
+            RemoteTrailerIds = new List<Guid>();
             ThemeSongIds = new List<Guid>();
             ThemeVideoIds = new List<Guid>();
             BoxSetIdList = new List<Guid>();
@@ -49,6 +50,7 @@ namespace MediaBrowser.Controller.Entities.Movies
         public float? Metascore { get; set; }
 
         public List<Guid> LocalTrailerIds { get; set; }
+        public List<Guid> RemoteTrailerIds { get; set; }
         public List<string> Keywords { get; set; }
 
         public List<MediaUrl> RemoteTrailers { get; set; }
@@ -89,6 +91,17 @@ namespace MediaBrowser.Controller.Entities.Movies
         /// <value>The name of the TMDB collection.</value>
         public string TmdbCollectionName { get; set; }
 
+        /// <summary>
+        /// Gets the trailer ids.
+        /// </summary>
+        /// <returns>List&lt;Guid&gt;.</returns>
+        public List<Guid> GetTrailerIds()
+        {
+            var list = LocalTrailerIds.ToList();
+            list.AddRange(RemoteTrailerIds);
+            return list;
+        }
+
         /// <summary>
         /// Gets the user data key.
         /// </summary>
@@ -119,7 +132,7 @@ namespace MediaBrowser.Controller.Entities.Movies
 
         private async Task<bool> RefreshSpecialFeatures(MetadataRefreshOptions options, List<FileSystemInfo> fileSystemChildren, CancellationToken cancellationToken)
         {
-            var newItems = LoadSpecialFeatures(fileSystemChildren, options.DirectoryService).ToList();
+            var newItems = LibraryManager.FindExtras(this, fileSystemChildren, options.DirectoryService).ToList();
             var newItemIds = newItems.Select(i => i.Id).ToList();
 
             var itemsChanged = !SpecialFeatureIds.SequenceEqual(newItemIds);
@@ -158,6 +171,22 @@ namespace MediaBrowser.Controller.Entities.Movies
                     ProductionYear = yearInName;
                     hasChanges = true;
                 }
+                else
+                {
+                    // Try to get the year from the folder name
+                    if (!IsInMixedFolder)
+                    {
+                        info = LibraryManager.ParseName(System.IO.Path.GetFileName(ContainingFolderPath));
+
+                        yearInName = info.Year;
+
+                        if (yearInName.HasValue)
+                        {
+                            ProductionYear = yearInName;
+                            hasChanges = true;
+                        }
+                    }
+                }
             }
 
             return hasChanges;

+ 36 - 0
MediaBrowser.Controller/Entities/MusicVideo.cs

@@ -89,5 +89,41 @@ namespace MediaBrowser.Controller.Entities
         {
             return GetItemLookupInfo<MusicVideoInfo>();
         }
+
+        public override bool BeforeMetadataRefresh()
+        {
+            var hasChanges = base.BeforeMetadataRefresh();
+
+            if (!ProductionYear.HasValue)
+            {
+                var info = LibraryManager.ParseName(Name);
+
+                var yearInName = info.Year;
+
+                if (yearInName.HasValue)
+                {
+                    ProductionYear = yearInName;
+                    hasChanges = true;
+                }
+                else
+                {
+                    // Try to get the year from the folder name
+                    if (!IsInMixedFolder)
+                    {
+                        info = LibraryManager.ParseName(System.IO.Path.GetFileName(ContainingFolderPath));
+
+                        yearInName = info.Year;
+
+                        if (yearInName.HasValue)
+                        {
+                            ProductionYear = yearInName;
+                            hasChanges = true;
+                        }
+                    }
+                }
+            }
+
+            return hasChanges;
+        }
     }
 }

+ 15 - 0
MediaBrowser.Controller/Entities/Share.cs

@@ -0,0 +1,15 @@
+using System.Collections.Generic;
+
+namespace MediaBrowser.Controller.Entities
+{
+    public interface IHasShares
+    {
+        List<Share> Shares { get; set; }
+    }
+
+    public class Share
+    {
+        public string UserId { get; set; }
+        public bool CanEdit { get; set; }
+    }
+}

+ 14 - 1
MediaBrowser.Controller/Entities/TV/Series.cs

@@ -36,6 +36,7 @@ namespace MediaBrowser.Controller.Entities.TV
             SoundtrackIds = new List<Guid>();
             RemoteTrailers = new List<MediaUrl>();
             LocalTrailerIds = new List<Guid>();
+            RemoteTrailerIds = new List<Guid>();
             DisplaySpecialsWithSeasons = true;
         }
 
@@ -57,7 +58,8 @@ namespace MediaBrowser.Controller.Entities.TV
         public bool DisplaySpecialsWithSeasons { get; set; }
 
         public List<Guid> LocalTrailerIds { get; set; }
-        
+        public List<Guid> RemoteTrailerIds { get; set; }
+
         public List<MediaUrl> RemoteTrailers { get; set; }
 
         /// <summary>
@@ -109,6 +111,17 @@ namespace MediaBrowser.Controller.Entities.TV
             return this.GetProviderId(MetadataProviders.Tvdb) ?? this.GetProviderId(MetadataProviders.Tvcom) ?? base.GetUserDataKey();
         }
 
+        /// <summary>
+        /// Gets the trailer ids.
+        /// </summary>
+        /// <returns>List&lt;Guid&gt;.</returns>
+        public List<Guid> GetTrailerIds()
+        {
+            var list = LocalTrailerIds.ToList();
+            list.AddRange(RemoteTrailerIds);
+            return list;
+        }
+
         // Studio, Genre and Rating will all be the same so makes no sense to index by these
         protected override IEnumerable<string> GetIndexByOptions()
         {

+ 2 - 4
MediaBrowser.Controller/Entities/Trailer.cs

@@ -12,7 +12,8 @@ namespace MediaBrowser.Controller.Entities
     /// <summary>
     /// Class Trailer
     /// </summary>
-    public class Trailer : Video, IHasCriticRating, IHasSoundtracks, IHasProductionLocations, IHasBudget, IHasTrailers, IHasKeywords, IHasTaglines, IHasMetascore, IHasLookupInfo<TrailerInfo>
+    [Obsolete]
+    public class Trailer : Video, IHasCriticRating, IHasSoundtracks, IHasProductionLocations, IHasBudget, IHasKeywords, IHasTaglines, IHasMetascore, IHasLookupInfo<TrailerInfo>
     {
         public List<Guid> SoundtrackIds { get; set; }
 
@@ -23,15 +24,12 @@ namespace MediaBrowser.Controller.Entities
             RemoteTrailers = new List<MediaUrl>();
             Taglines = new List<string>();
             SoundtrackIds = new List<Guid>();
-            LocalTrailerIds = new List<Guid>();
             Keywords = new List<string>();
             ProductionLocations = new List<string>();
         }
 
         public float? Metascore { get; set; }
 
-        public List<Guid> LocalTrailerIds { get; set; }
-
         public List<MediaUrl> RemoteTrailers { get; set; }
 
         public List<string> Keywords { get; set; }

+ 2 - 1
MediaBrowser.Controller/Entities/User.cs

@@ -4,6 +4,7 @@ using MediaBrowser.Controller.Providers;
 using MediaBrowser.Model.Configuration;
 using MediaBrowser.Model.Connect;
 using MediaBrowser.Model.Serialization;
+using MediaBrowser.Model.Users;
 using System;
 using System.IO;
 using System.Linq;
@@ -287,7 +288,7 @@ namespace MediaBrowser.Controller.Entities
 
             var localTime = date.ToLocalTime();
 
-            return DayOfWeekHelper.GetDaysOfWeek(schedule.DayOfWeek).Contains(localTime.DayOfWeek) && 
+            return DayOfWeekHelper.GetDaysOfWeek(schedule.DayOfWeek).Contains(localTime.DayOfWeek) &&
                 IsWithinTime(schedule, localTime);
         }
 

+ 1 - 2
MediaBrowser.Controller/Entities/UserView.cs

@@ -63,8 +63,7 @@ namespace MediaBrowser.Controller.Entities
             {
                 CollectionType.Books,
                 CollectionType.HomeVideos,
-                CollectionType.Photos,
-                CollectionType.Trailers
+                CollectionType.Photos
             };
 
             var collectionFolder = folder as ICollectionFolder;

+ 1 - 1
MediaBrowser.Controller/Entities/UserViewBuilder.cs

@@ -1428,7 +1428,7 @@ namespace MediaBrowser.Controller.Entities
                 var hasTrailers = item as IHasTrailers;
                 if (hasTrailers != null)
                 {
-                    trailerCount = hasTrailers.LocalTrailerIds.Count;
+                    trailerCount = hasTrailers.GetTrailerIds().Count;
                 }
 
                 var ok = val ? trailerCount > 0 : trailerCount == 0;

+ 90 - 184
MediaBrowser.Controller/Entities/Video.cs

@@ -1,12 +1,10 @@
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Persistence;
 using MediaBrowser.Controller.Providers;
-using MediaBrowser.Controller.Resolvers;
 using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.MediaInfo;
 using System;
-using System.Collections;
 using System.Collections.Generic;
 using System.IO;
 using System.Linq;
@@ -19,24 +17,23 @@ namespace MediaBrowser.Controller.Entities
     /// <summary>
     /// Class Video
     /// </summary>
-    public class Video : BaseItem, 
-        IHasAspectRatio, 
-        IHasTags, 
+    public class Video : BaseItem,
+        IHasAspectRatio,
+        IHasTags,
         ISupportsPlaceHolders,
         IHasMediaSources,
         IHasShortOverview,
         IHasPreferredMetadataLanguage,
         IThemeMedia
     {
-        public bool IsMultiPart { get; set; }
-        public bool HasLocalAlternateVersions { get; set; }
         public Guid? PrimaryVersionId { get; set; }
 
-        public List<Guid> AdditionalPartIds { get; set; }
-        public List<Guid> LocalAlternateVersionIds { get; set; }
+        public List<string> AdditionalParts { get; set; }
+        public List<string> LocalAlternateVersions { get; set; }
+        public List<LinkedChild> LinkedAlternateVersions { get; set; }
 
         public bool IsThemeMedia { get; set; }
-        
+
         public string FormatName { get; set; }
         public long? Size { get; set; }
         public string Container { get; set; }
@@ -56,12 +53,12 @@ namespace MediaBrowser.Controller.Entities
         /// </summary>
         /// <value>The timestamp.</value>
         public TransportStreamTimestamp? Timestamp { get; set; }
-        
+
         public Video()
         {
             PlayableStreamFileNames = new List<string>();
-            AdditionalPartIds = new List<Guid>();
-            LocalAlternateVersionIds = new List<Guid>();
+            AdditionalParts = new List<string>();
+            LocalAlternateVersions = new List<string>();
             Tags = new List<string>();
             SubtitleFiles = new List<string>();
             LinkedAlternateVersions = new List<LinkedChild>();
@@ -78,11 +75,31 @@ namespace MediaBrowser.Controller.Entities
         {
             get
             {
-                return LinkedAlternateVersions.Count + LocalAlternateVersionIds.Count + 1;
+                return LinkedAlternateVersions.Count + LocalAlternateVersions.Count + 1;
             }
         }
 
-        public List<LinkedChild> LinkedAlternateVersions { get; set; }
+        [IgnoreDataMember]
+        public bool IsStacked
+        {
+            get { return AdditionalParts.Count > 0; }
+        }
+
+        [IgnoreDataMember]
+        public bool HasLocalAlternateVersions
+        {
+            get { return LocalAlternateVersions.Count > 0; }
+        }
+
+        public IEnumerable<Guid> GetAdditionalPartIds()
+        {
+            return AdditionalParts.Select(i => LibraryManager.GetNewItemId(i, typeof(Video)));
+        }
+
+        public IEnumerable<Guid> GetLocalAlternateVersionIds()
+        {
+            return LocalAlternateVersions.Select(i => LibraryManager.GetNewItemId(i, typeof(Video)));
+        }
 
         /// <summary>
         /// Gets the linked children.
@@ -90,7 +107,7 @@ namespace MediaBrowser.Controller.Entities
         /// <returns>IEnumerable{BaseItem}.</returns>
         public IEnumerable<Video> GetAlternateVersions()
         {
-            var filesWithinSameDirectory = LocalAlternateVersionIds
+            var filesWithinSameDirectory = GetLocalAlternateVersionIds()
                 .Select(i => LibraryManager.GetItemById(i))
                 .Where(i => i != null)
                 .OfType<Video>();
@@ -116,7 +133,7 @@ namespace MediaBrowser.Controller.Entities
         /// <returns>IEnumerable{Video}.</returns>
         public IEnumerable<Video> GetAdditionalParts()
         {
-            return AdditionalPartIds
+            return GetAdditionalPartIds()
                 .Select(i => LibraryManager.GetItemById(i))
                 .Where(i => i != null)
                 .OfType<Video>()
@@ -200,7 +217,7 @@ namespace MediaBrowser.Controller.Entities
         {
             get
             {
-                if (IsMultiPart)
+                if (IsStacked)
                 {
                     return System.IO.Path.GetDirectoryName(Path);
                 }
@@ -218,6 +235,46 @@ namespace MediaBrowser.Controller.Entities
             }
         }
 
+        [IgnoreDataMember]
+        public override string FileNameWithoutExtension
+        {
+            get
+            {
+                if (LocationType == LocationType.FileSystem)
+                {
+                    if (VideoType == VideoType.BluRay || VideoType == VideoType.Dvd || VideoType == VideoType.HdDvd)
+                    {
+                        return System.IO.Path.GetFileName(Path);
+                    }
+                    
+                    return System.IO.Path.GetFileNameWithoutExtension(Path);
+                }
+
+                return null;
+            }
+        }
+
+        internal override bool IsValidFromResolver(BaseItem newItem)
+        {
+            var current = this;
+
+            var newAsVideo = newItem as Video;
+
+            if (newAsVideo != null)
+            {
+                if (!current.AdditionalParts.SequenceEqual(newAsVideo.AdditionalParts, StringComparer.OrdinalIgnoreCase))
+                {
+                    return false;
+                }
+                if (!current.LocalAlternateVersions.SequenceEqual(newAsVideo.LocalAlternateVersions, StringComparer.OrdinalIgnoreCase))
+                {
+                    return false;
+                }
+            }
+
+            return base.IsValidFromResolver(newItem);
+        }
+
         public string MainFeaturePlaylistName { get; set; }
 
         /// <summary>
@@ -263,37 +320,34 @@ namespace MediaBrowser.Controller.Entities
         {
             var hasChanges = await base.RefreshedOwnedItems(options, fileSystemChildren, cancellationToken).ConfigureAwait(false);
 
+            if (IsStacked)
+            {
+                var tasks = AdditionalParts
+                    .Select(i => RefreshMetadataForOwnedVideo(options, i, cancellationToken));
+
+                await Task.WhenAll(tasks).ConfigureAwait(false);
+            }
+
             // Must have a parent to have additional parts or alternate versions
             // In other words, it must be part of the Parent/Child tree
             // The additional parts won't have additional parts themselves
             if (LocationType == LocationType.FileSystem && Parent != null)
             {
-                if (IsMultiPart)
-                {
-                    var additionalPartsChanged = await RefreshAdditionalParts(options, fileSystemChildren, cancellationToken).ConfigureAwait(false);
-
-                    if (additionalPartsChanged)
-                    {
-                        hasChanges = true;
-                    }
-                }
-                else
+                if (!IsStacked)
                 {
                     RefreshLinkedAlternateVersions();
 
-                    var additionalPartsChanged = await RefreshAlternateVersionsWithinSameDirectory(options, fileSystemChildren, cancellationToken).ConfigureAwait(false);
+                    var tasks = LocalAlternateVersions
+                        .Select(i => RefreshMetadataForOwnedVideo(options, i, cancellationToken));
 
-                    if (additionalPartsChanged)
-                    {
-                        hasChanges = true;
-                    }
+                    await Task.WhenAll(tasks).ConfigureAwait(false);
                 }
             }
 
             return hasChanges;
         }
 
-        private bool RefreshLinkedAlternateVersions()
+        private void RefreshLinkedAlternateVersions()
         {
             foreach (var child in LinkedAlternateVersions)
             {
@@ -303,111 +357,13 @@ namespace MediaBrowser.Controller.Entities
                     child.ItemId = null;
                 }
             }
-
-            return false;
-        }
-
-        /// <summary>
-        /// Refreshes the additional parts.
-        /// </summary>
-        /// <param name="options">The options.</param>
-        /// <param name="fileSystemChildren">The file system children.</param>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <returns>Task{System.Boolean}.</returns>
-        private async Task<bool> RefreshAdditionalParts(MetadataRefreshOptions options, IEnumerable<FileSystemInfo> fileSystemChildren, CancellationToken cancellationToken)
-        {
-            var newItems = LoadAdditionalParts(fileSystemChildren, options.DirectoryService).ToList();
-
-            var newItemIds = newItems.Select(i => i.Id).ToList();
-
-            var itemsChanged = !AdditionalPartIds.SequenceEqual(newItemIds);
-
-            var tasks = newItems.Select(i => i.RefreshMetadata(options, cancellationToken));
-
-            await Task.WhenAll(tasks).ConfigureAwait(false);
-
-            AdditionalPartIds = newItemIds;
-
-            return itemsChanged;
-        }
-
-        /// <summary>
-        /// Loads the additional parts.
-        /// </summary>
-        /// <returns>IEnumerable{Video}.</returns>
-        private IEnumerable<Video> LoadAdditionalParts(IEnumerable<FileSystemInfo> fileSystemChildren, IDirectoryService directoryService)
-        {
-            var files = LibraryManager.GetAdditionalParts(Path, VideoType, fileSystemChildren);
-
-            return LibraryManager.ResolvePaths<Video>(files, directoryService, null).Select(video =>
-            {
-                // Try to retrieve it from the db. If we don't find it, use the resolved version
-                var dbItem = LibraryManager.GetItemById(video.Id) as Video;
-
-                if (dbItem != null)
-                {
-                    video = dbItem;
-                }
-
-                return video;
-
-                // Sort them so that the list can be easily compared for changes
-            }).OrderBy(i => i.Path).ToList();
-        }
-
-        private async Task<bool> RefreshAlternateVersionsWithinSameDirectory(MetadataRefreshOptions options, IEnumerable<FileSystemInfo> fileSystemChildren, CancellationToken cancellationToken)
-        {
-            var newItems = HasLocalAlternateVersions ?
-                LoadAlternateVersionsWithinSameDirectory(fileSystemChildren, options.DirectoryService).ToList() :
-                new List<Video>();
-
-            var newItemIds = newItems.Select(i => i.Id).ToList();
-
-            var itemsChanged = !LocalAlternateVersionIds.SequenceEqual(newItemIds);
-
-            var tasks = newItems.Select(i => RefreshAlternateVersion(options, i, cancellationToken));
-
-            await Task.WhenAll(tasks).ConfigureAwait(false);
-
-            LocalAlternateVersionIds = newItemIds;
-
-            return itemsChanged;
-        }
-
-        private Task RefreshAlternateVersion(MetadataRefreshOptions options, Video video, CancellationToken cancellationToken)
-        {
-            var currentImagePath = video.GetImagePath(ImageType.Primary);
-            var ownerImagePath = this.GetImagePath(ImageType.Primary);
-
-            var newOptions = new MetadataRefreshOptions(options.DirectoryService)
-            {
-                ImageRefreshMode = options.ImageRefreshMode,
-                MetadataRefreshMode = options.MetadataRefreshMode,
-                ReplaceAllMetadata = options.ReplaceAllMetadata
-            };
-
-            if (!string.Equals(currentImagePath, ownerImagePath, StringComparison.OrdinalIgnoreCase))
-            {
-                newOptions.ForceSave = true;
-
-                if (string.IsNullOrWhiteSpace(ownerImagePath))
-                {
-                    video.ImageInfos.Clear();
-                }
-                else
-                {
-                    video.SetImagePath(ImageType.Primary, ownerImagePath);
-                }
-            }
-
-            return video.RefreshMetadata(newOptions, cancellationToken);
         }
 
         public override async Task UpdateToRepository(ItemUpdateType updateReason, CancellationToken cancellationToken)
         {
             await base.UpdateToRepository(updateReason, cancellationToken).ConfigureAwait(false);
 
-            foreach (var item in LocalAlternateVersionIds.Select(i => LibraryManager.GetItemById(i)))
+            foreach (var item in GetLocalAlternateVersionIds().Select(i => LibraryManager.GetItemById(i)))
             {
                 item.ImageInfos = ImageInfos;
                 item.Overview = Overview;
@@ -422,56 +378,6 @@ namespace MediaBrowser.Controller.Entities
             }
         }
 
-        /// <summary>
-        /// Loads the additional parts.
-        /// </summary>
-        /// <returns>IEnumerable{Video}.</returns>
-        private IEnumerable<Video> LoadAlternateVersionsWithinSameDirectory(IEnumerable<FileSystemInfo> fileSystemChildren, IDirectoryService directoryService)
-        {
-            IEnumerable<FileSystemInfo> files;
-
-            // Only support this for video files. For folder rips, they'll have to use the linking feature
-            if (VideoType == VideoType.VideoFile || VideoType == VideoType.Iso)
-            {
-                var path = Path;
-
-                var filenamePrefix = System.IO.Path.GetFileName(System.IO.Path.GetDirectoryName(path));
-
-                files = fileSystemChildren.Where(i =>
-                {
-                    if ((i.Attributes & FileAttributes.Directory) == FileAttributes.Directory)
-                    {
-                        return false;
-                    }
-
-                    return !string.Equals(i.FullName, path, StringComparison.OrdinalIgnoreCase) &&
-                           LibraryManager.IsVideoFile(i.FullName) &&
-                           i.Name.StartsWith(filenamePrefix + " - ", StringComparison.OrdinalIgnoreCase);
-                });
-            }
-            else
-            {
-                files = new List<FileSystemInfo>();
-            }
-
-            return LibraryManager.ResolvePaths<Video>(files, directoryService, null).Select(video =>
-            {
-                // Try to retrieve it from the db. If we don't find it, use the resolved version
-                var dbItem = LibraryManager.GetItemById(video.Id) as Video;
-
-                if (dbItem != null)
-                {
-                    video = dbItem;
-                }
-
-                video.PrimaryVersionId = Id;
-
-                return video;
-
-                // Sort them so that the list can be easily compared for changes
-            }).OrderBy(i => i.Path).ToList();
-        }
-
         public override IEnumerable<string> GetDeletePaths()
         {
             if (!IsInMixedFolder)
@@ -539,7 +445,7 @@ namespace MediaBrowser.Controller.Entities
             var mediaStreams = ItemRepository.GetMediaStreams(new MediaStreamQuery { ItemId = i.Id }).ToList();
 
             var locationType = i.LocationType;
-            
+
             var info = new MediaSourceInfo
             {
                 Id = i.Id.ToString("N"),

+ 31 - 50
MediaBrowser.Controller/Library/ILibraryManager.cs

@@ -24,7 +24,9 @@ namespace MediaBrowser.Controller.Library
         /// <param name="parent">The parent.</param>
         /// <param name="collectionType">Type of the collection.</param>
         /// <returns>BaseItem.</returns>
-        BaseItem ResolvePath(FileSystemInfo fileInfo, Folder parent = null, string collectionType = null);
+        BaseItem ResolvePath(FileSystemInfo fileInfo, 
+            Folder parent = null, 
+            string collectionType = null);
 
         /// <summary>
         /// Resolves a set of files into a list of BaseItem
@@ -35,8 +37,10 @@ namespace MediaBrowser.Controller.Library
         /// <param name="parent">The parent.</param>
         /// <param name="collectionType">Type of the collection.</param>
         /// <returns>List{``0}.</returns>
-        List<T> ResolvePaths<T>(IEnumerable<FileSystemInfo> files, IDirectoryService directoryService, Folder parent, string collectionType = null)
-            where T : BaseItem;
+        IEnumerable<BaseItem> ResolvePaths(IEnumerable<FileSystemInfo> files, 
+            IDirectoryService directoryService,
+            Folder parent, string 
+            collectionType = null);
 
         /// <summary>
         /// Gets the root folder.
@@ -229,46 +233,6 @@ namespace MediaBrowser.Controller.Library
         /// <returns>BaseItem.</returns>
         BaseItem RetrieveItem(Guid id);
 
-        /// <summary>
-        /// Validates the artists.
-        /// </summary>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <param name="progress">The progress.</param>
-        /// <returns>Task.</returns>
-        Task ValidateArtists(CancellationToken cancellationToken, IProgress<double> progress);
-
-        /// <summary>
-        /// Validates the music genres.
-        /// </summary>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <param name="progress">The progress.</param>
-        /// <returns>Task.</returns>
-        Task ValidateMusicGenres(CancellationToken cancellationToken, IProgress<double> progress);
-
-        /// <summary>
-        /// Validates the game genres.
-        /// </summary>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <param name="progress">The progress.</param>
-        /// <returns>Task.</returns>
-        Task ValidateGameGenres(CancellationToken cancellationToken, IProgress<double> progress);
-
-        /// <summary>
-        /// Validates the genres.
-        /// </summary>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <param name="progress">The progress.</param>
-        /// <returns>Task.</returns>
-        Task ValidateGenres(CancellationToken cancellationToken, IProgress<double> progress);
-
-        /// <summary>
-        /// Validates the studios.
-        /// </summary>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <param name="progress">The progress.</param>
-        /// <returns>Task.</returns>
-        Task ValidateStudios(CancellationToken cancellationToken, IProgress<double> progress);
-
         /// <summary>
         /// Occurs when [item added].
         /// </summary>
@@ -405,14 +369,31 @@ namespace MediaBrowser.Controller.Library
         ItemLookupInfo ParseName(string name);
 
         /// <summary>
-        /// Gets the additional parts.
+        /// Gets the new item identifier.
         /// </summary>
-        /// <param name="file">The file.</param>
+        /// <param name="key">The key.</param>
         /// <param name="type">The type.</param>
-        /// <param name="files">The files.</param>
-        /// <returns>IEnumerable&lt;System.String&gt;.</returns>
-        IEnumerable<FileSystemInfo> GetAdditionalParts(string file,
-            VideoType type,
-            IEnumerable<FileSystemInfo> files);
+        /// <returns>Guid.</returns>
+        Guid GetNewItemId(string key, Type type);
+
+        /// <summary>
+        /// Finds the trailers.
+        /// </summary>
+        /// <param name="owner">The owner.</param>
+        /// <param name="fileSystemChildren">The file system children.</param>
+        /// <param name="directoryService">The directory service.</param>
+        /// <returns>IEnumerable&lt;Trailer&gt;.</returns>
+        IEnumerable<Video> FindTrailers(BaseItem owner, List<FileSystemInfo> fileSystemChildren,
+            IDirectoryService directoryService);
+
+        /// <summary>
+        /// Finds the extras.
+        /// </summary>
+        /// <param name="owner">The owner.</param>
+        /// <param name="fileSystemChildren">The file system children.</param>
+        /// <param name="directoryService">The directory service.</param>
+        /// <returns>IEnumerable&lt;Video&gt;.</returns>
+        IEnumerable<Video> FindExtras(BaseItem owner, List<FileSystemInfo> fileSystemChildren,
+            IDirectoryService directoryService);
     }
 }

+ 8 - 0
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
         /// <returns>IEnumerable{Audio}.</returns>
         IEnumerable<Audio> GetInstantMixFromAlbum(MusicAlbum item, User user);
         /// <summary>
+        /// Gets the instant mix from playlist.
+        /// </summary>
+        /// <param name="item">The item.</param>
+        /// <param name="user">The user.</param>
+        /// <returns>IEnumerable&lt;Audio&gt;.</returns>
+        IEnumerable<Audio> GetInstantMixFromPlaylist(Playlist item, User user);
+        /// <summary>
         /// Gets the instant mix from genre.
         /// </summary>
         /// <param name="genres">The genres.</param>

+ 14 - 0
MediaBrowser.Controller/Library/IUserManager.cs

@@ -164,5 +164,19 @@ namespace MediaBrowser.Controller.Library
         /// <param name="pin">The pin.</param>
         /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
         Task<PinRedeemResult> RedeemPasswordResetPin(string pin);
+
+        /// <summary>
+        /// Gets the user policy.
+        /// </summary>
+        /// <param name="userId">The user identifier.</param>
+        /// <returns>UserPolicy.</returns>
+        UserPolicy GetUserPolicy(string userId);
+
+        /// <summary>
+        /// Updates the user policy.
+        /// </summary>
+        /// <param name="userId">The user identifier.</param>
+        /// <param name="userPolicy">The user policy.</param>
+        Task UpdateUserPolicy(string userId, UserPolicy userPolicy);
     }
 }

+ 4 - 3
MediaBrowser.Controller/LiveTv/ILiveTvService.cs

@@ -3,6 +3,7 @@ using System.Collections.Generic;
 using System.Threading;
 using System.Threading.Tasks;
 using MediaBrowser.Controller.Channels;
+using MediaBrowser.Controller.Drawing;
 
 namespace MediaBrowser.Controller.LiveTv
 {
@@ -109,7 +110,7 @@ namespace MediaBrowser.Controller.LiveTv
         /// <param name="channelId">The channel identifier.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task{Stream}.</returns>
-        Task<StreamResponseInfo> GetChannelImageAsync(string channelId, CancellationToken cancellationToken);
+        Task<ImageStream> GetChannelImageAsync(string channelId, CancellationToken cancellationToken);
 
         /// <summary>
         /// Gets the recording image asynchronous. This only needs to be implemented if an image path or url cannot be supplied to RecordingInfo
@@ -117,7 +118,7 @@ namespace MediaBrowser.Controller.LiveTv
         /// <param name="recordingId">The recording identifier.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task{ImageResponseInfo}.</returns>
-        Task<StreamResponseInfo> GetRecordingImageAsync(string recordingId, CancellationToken cancellationToken);
+        Task<ImageStream> GetRecordingImageAsync(string recordingId, CancellationToken cancellationToken);
 
         /// <summary>
         /// Gets the program image asynchronous. This only needs to be implemented if an image path or url cannot be supplied to ProgramInfo
@@ -126,7 +127,7 @@ namespace MediaBrowser.Controller.LiveTv
         /// <param name="channelId">The channel identifier.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task{ImageResponseInfo}.</returns>
-        Task<StreamResponseInfo> GetProgramImageAsync(string programId, string channelId, CancellationToken cancellationToken);
+        Task<ImageStream> GetProgramImageAsync(string programId, string channelId, CancellationToken cancellationToken);
 
         /// <summary>
         /// Gets the recordings asynchronous.

+ 0 - 20
MediaBrowser.Controller/LiveTv/StreamResponseInfo.cs

@@ -1,20 +0,0 @@
-using MediaBrowser.Controller.Drawing;
-using System.IO;
-
-namespace MediaBrowser.Controller.LiveTv
-{
-    public class StreamResponseInfo
-    {
-        /// <summary>
-        /// Gets or sets the stream.
-        /// </summary>
-        /// <value>The stream.</value>
-        public Stream Stream { get; set; }
-
-        /// <summary>
-        /// Gets or sets the type of the MIME.
-        /// </summary>
-        /// <value>The type of the MIME.</value>
-        public ImageFormat Format { get; set; }
-    }
-}

+ 2 - 5
MediaBrowser.Controller/MediaBrowser.Controller.csproj

@@ -55,7 +55,6 @@
     <Reference Include="System" />
     <Reference Include="System.Core" />
     <Reference Include="System.Data" />
-    <Reference Include="System.Drawing" />
     <Reference Include="System.Net" />
     <Reference Include="System.Runtime.Serialization" />
     <Reference Include="Microsoft.CSharp" />
@@ -106,7 +105,6 @@
     <Compile Include="Devices\IDeviceRepository.cs" />
     <Compile Include="Dlna\ControlRequest.cs" />
     <Compile Include="Dlna\ControlResponse.cs" />
-    <Compile Include="Dlna\DlnaIconResponse.cs" />
     <Compile Include="Dlna\EventSubscriptionResponse.cs" />
     <Compile Include="Dlna\IConnectionManager.cs" />
     <Compile Include="Dlna\IContentDirectory.cs" />
@@ -114,9 +112,9 @@
     <Compile Include="Dlna\IEventManager.cs" />
     <Compile Include="Dlna\IUpnpService.cs" />
     <Compile Include="Drawing\IImageProcessor.cs" />
-    <Compile Include="Drawing\ImageFormat.cs" />
     <Compile Include="Drawing\ImageProcessingOptions.cs" />
     <Compile Include="Drawing\ImageProcessorExtensions.cs" />
+    <Compile Include="Drawing\ImageStream.cs" />
     <Compile Include="Dto\IDtoService.cs" />
     <Compile Include="Entities\AdultVideo.cs" />
     <Compile Include="Entities\Audio\IHasAlbumArtist.cs" />
@@ -164,6 +162,7 @@
     <Compile Include="Entities\IHasAwards.cs" />
     <Compile Include="Entities\Photo.cs" />
     <Compile Include="Entities\PhotoAlbum.cs" />
+    <Compile Include="Entities\Share.cs" />
     <Compile Include="Entities\UserView.cs" />
     <Compile Include="Entities\UserViewBuilder.cs" />
     <Compile Include="FileOrganization\IFileOrganizationService.cs" />
@@ -192,7 +191,6 @@
     <Compile Include="LiveTv\LiveTvException.cs" />
     <Compile Include="LiveTv\LiveTvServiceStatusInfo.cs" />
     <Compile Include="LiveTv\LiveTvTunerInfo.cs" />
-    <Compile Include="LiveTv\StreamResponseInfo.cs" />
     <Compile Include="LiveTv\LiveTvProgram.cs" />
     <Compile Include="LiveTv\LiveTvVideoRecording.cs" />
     <Compile Include="LiveTv\ProgramInfo.cs" />
@@ -270,7 +268,6 @@
     <Compile Include="Providers\MetadataStatus.cs" />
     <Compile Include="Providers\ISeriesOrderManager.cs" />
     <Compile Include="Session\ISessionManager.cs" />
-    <Compile Include="Drawing\ImageExtensions.cs" />
     <Compile Include="Entities\AggregateFolder.cs" />
     <Compile Include="Entities\Audio\Audio.cs" />
     <Compile Include="Entities\Audio\MusicAlbum.cs" />

+ 0 - 6
MediaBrowser.Controller/Persistence/IUserRepository.cs

@@ -10,12 +10,6 @@ namespace MediaBrowser.Controller.Persistence
     /// </summary>
     public interface IUserRepository : IRepository
     {
-        /// <summary>
-        /// Opens the connection to the repository
-        /// </summary>
-        /// <returns>Task.</returns>
-        Task Initialize();
-
         /// <summary>
         /// Deletes the user.
         /// </summary>

+ 17 - 2
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<Share> Shares { get; set; }
+
+        public Playlist()
+        {
+            Shares = new List<Share>();
+        }
+
         [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;
         }
     }
 }

+ 84 - 0
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();
@@ -1340,6 +1418,12 @@ namespace MediaBrowser.Controller.Providers
                 }
             }
 
+            // This is valid
+            if (!string.IsNullOrWhiteSpace(linkedItem.Path))
+            {
+                return linkedItem;
+            }
+
             return string.IsNullOrWhiteSpace(linkedItem.ItemName) || string.IsNullOrWhiteSpace(linkedItem.ItemType) ? null : linkedItem;
         }
 

+ 3 - 3
MediaBrowser.Controller/Providers/IImageEnhancer.cs

@@ -1,7 +1,7 @@
-using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Drawing;
+using MediaBrowser.Controller.Entities;
 using MediaBrowser.Model.Drawing;
 using MediaBrowser.Model.Entities;
-using System.Drawing;
 using System.Threading.Tasks;
 
 namespace MediaBrowser.Controller.Providers
@@ -49,6 +49,6 @@ namespace MediaBrowser.Controller.Providers
         /// <param name="imageIndex">Index of the image.</param>
         /// <returns>Task{Image}.</returns>
         /// <exception cref="System.ArgumentNullException"></exception>
-        Task<Image> EnhanceImageAsync(IHasImages item, Image originalImage, ImageType imageType, int imageIndex);
+        Task<ImageStream> EnhanceImageAsync(IHasImages item, ImageStream originalImage, ImageType imageType, int imageIndex);
     }
 }

+ 2 - 2
MediaBrowser.Controller/Providers/IImageSaver.cs

@@ -1,5 +1,5 @@
-using MediaBrowser.Controller.Drawing;
-using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Model.Drawing;
 using MediaBrowser.Model.Entities;
 using System.Collections.Generic;
 

+ 2 - 2
MediaBrowser.Controller/Providers/ILocalImageProvider.cs

@@ -1,5 +1,5 @@
-using MediaBrowser.Controller.Drawing;
-using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Model.Drawing;
 using MediaBrowser.Model.Entities;
 using System;
 using System.Collections.Generic;

+ 23 - 0
MediaBrowser.Controller/Resolvers/IItemResolver.cs

@@ -1,5 +1,8 @@
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Providers;
+using System.Collections.Generic;
+using System.IO;
 
 namespace MediaBrowser.Controller.Resolvers
 {
@@ -20,4 +23,24 @@ namespace MediaBrowser.Controller.Resolvers
         /// <value>The priority.</value>
         ResolverPriority Priority { get; }
     }
+
+    public interface IMultiItemResolver
+    {
+        MultiItemResolverResult ResolveMultiple(Folder parent, 
+            List<FileSystemInfo> files, 
+            string collectionType,
+            IDirectoryService directoryService);
+    }
+
+    public class MultiItemResolverResult
+    {
+        public List<BaseItem> Items { get; set; }
+        public List<FileSystemInfo> ExtraFiles { get; set; }
+
+        public MultiItemResolverResult()
+        {
+            Items = new List<BaseItem>();
+            ExtraFiles = new List<FileSystemInfo>();
+        }
+    }
 }

+ 10 - 4
MediaBrowser.Controller/Sync/ISyncManager.cs

@@ -1,6 +1,5 @@
-using System.IO;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Model.Devices;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Model.Dlna;
 using MediaBrowser.Model.Querying;
 using MediaBrowser.Model.Sync;
 using System.Collections.Generic;
@@ -29,7 +28,7 @@ namespace MediaBrowser.Controller.Sync
         /// <param name="id">The identifier.</param>
         /// <returns>SyncJob.</returns>
         SyncJob GetJob(string id);
-        
+
         /// <summary>
         /// Cancels the job.
         /// </summary>
@@ -53,5 +52,12 @@ namespace MediaBrowser.Controller.Sync
         /// <param name="item">The item.</param>
         /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
         bool SupportsSync(BaseItem item);
+
+        /// <summary>
+        /// Gets the device profile.
+        /// </summary>
+        /// <param name="targetId">The target identifier.</param>
+        /// <returns>DeviceProfile.</returns>
+        DeviceProfile GetDeviceProfile(string targetId);
     }
 }

+ 14 - 0
MediaBrowser.Controller/Sync/ISyncRepository.cs

@@ -27,6 +27,13 @@ namespace MediaBrowser.Controller.Sync
         /// <returns>Task.</returns>
         Task Update(SyncJob job);
 
+        /// <summary>
+        /// Deletes the job.
+        /// </summary>
+        /// <param name="id">The identifier.</param>
+        /// <returns>Task.</returns>
+        Task DeleteJob(string id);
+
         /// <summary>
         /// Gets the jobs.
         /// </summary>
@@ -54,5 +61,12 @@ namespace MediaBrowser.Controller.Sync
         /// <param name="jobItem">The job item.</param>
         /// <returns>Task.</returns>
         Task Update(SyncJobItem jobItem);
+
+        /// <summary>
+        /// Gets the job items.
+        /// </summary>
+        /// <param name="query">The query.</param>
+        /// <returns>IEnumerable&lt;SyncJobItem&gt;.</returns>
+        QueryResult<SyncJobItem> GetJobItems(SyncJobItemQuery query);
     }
 }

+ 6 - 2
MediaBrowser.Dlna/ContentDirectory/ContentDirectory.cs

@@ -1,4 +1,5 @@
 using MediaBrowser.Common.Net;
+using MediaBrowser.Controller.Channels;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Dlna;
 using MediaBrowser.Controller.Drawing;
@@ -23,6 +24,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
         private readonly IServerConfigurationManager _config;
         private readonly IUserManager _userManager;
         private readonly ILocalizationManager _localization;
+        private readonly IChannelManager _channelManager;
 
         public ContentDirectory(IDlnaManager dlna,
             IUserDataManager userDataManager,
@@ -31,7 +33,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
             IServerConfigurationManager config,
             IUserManager userManager,
             ILogger logger,
-            IHttpClient httpClient, ILocalizationManager localization)
+            IHttpClient httpClient, ILocalizationManager localization, IChannelManager channelManager)
             : base(logger, httpClient)
         {
             _dlna = dlna;
@@ -41,6 +43,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
             _config = config;
             _userManager = userManager;
             _localization = localization;
+            _channelManager = channelManager;
         }
 
         private int SystemUpdateId
@@ -77,7 +80,8 @@ namespace MediaBrowser.Dlna.ContentDirectory
                 user,
                 SystemUpdateId,
                 _config,
-                _localization)
+                _localization,
+                _channelManager)
                 .ProcessControlRequest(request);
         }
 

+ 182 - 38
MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs

@@ -1,13 +1,16 @@
 using MediaBrowser.Common.Extensions;
+using MediaBrowser.Controller.Channels;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Drawing;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities.Movies;
+using MediaBrowser.Controller.Entities.TV;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Localization;
 using MediaBrowser.Dlna.Didl;
 using MediaBrowser.Dlna.Server;
 using MediaBrowser.Dlna.Service;
+using MediaBrowser.Model.Channels;
 using MediaBrowser.Model.Dlna;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Logging;
@@ -26,6 +29,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
     public class ControlHandler : BaseControlHandler
     {
         private readonly ILibraryManager _libraryManager;
+        private readonly IChannelManager _channelManager;
         private readonly IUserDataManager _userDataManager;
         private readonly User _user;
 
@@ -41,13 +45,14 @@ namespace MediaBrowser.Dlna.ContentDirectory
 
         private readonly DeviceProfile _profile;
 
-        public ControlHandler(ILogger logger, ILibraryManager libraryManager, DeviceProfile profile, string serverAddress, IImageProcessor imageProcessor, IUserDataManager userDataManager, User user, int systemUpdateId, IServerConfigurationManager config, ILocalizationManager localization)
+        public ControlHandler(ILogger logger, ILibraryManager libraryManager, DeviceProfile profile, string serverAddress, IImageProcessor imageProcessor, IUserDataManager userDataManager, User user, int systemUpdateId, IServerConfigurationManager config, ILocalizationManager localization, IChannelManager channelManager)
             : base(config, logger)
         {
             _libraryManager = libraryManager;
             _userDataManager = userDataManager;
             _user = user;
             _systemUpdateId = systemUpdateId;
+            _channelManager = channelManager;
             _profile = profile;
 
             _didlBuilder = new DidlBuilder(profile, user, imageProcessor, serverAddress, userDataManager, localization);
@@ -208,14 +213,15 @@ namespace MediaBrowser.Dlna.ContentDirectory
             var serverItem = GetItemFromObjectId(id, user);
             var item = serverItem.Item;
 
-            var totalCount = 0;
+            int totalCount;
 
             if (string.Equals(flag, "BrowseMetadata"))
             {
+                totalCount = 1;
+                
                 if (item.IsFolder || serverItem.StubType.HasValue)
                 {
                     var childrenResult = (await GetUserItems(item, serverItem.StubType, user, sortCriteria, start, requested).ConfigureAwait(false));
-                    totalCount = 1;
 
                     result.DocumentElement.AppendChild(_didlBuilder.GetFolderElement(result, item, serverItem.StubType, null, childrenResult.TotalRecordCount, filter, id));
                 }
@@ -235,18 +241,19 @@ namespace MediaBrowser.Dlna.ContentDirectory
 
                 foreach (var i in childrenResult.Items)
                 {
-                    var displayStubType = GetDisplayStubType(i, serverItem.Item);
+                    var childItem = i.Item;
+                    var displayStubType = i.StubType;
 
-                    if (i.IsFolder || displayStubType.HasValue)
+                    if (childItem.IsFolder || displayStubType.HasValue)
                     {
-                        var childCount = (await GetUserItems(i, displayStubType, user, sortCriteria, null, 0).ConfigureAwait(false))
+                        var childCount = (await GetUserItems(childItem, displayStubType, user, sortCriteria, null, 0).ConfigureAwait(false))
                             .TotalRecordCount;
 
-                        result.DocumentElement.AppendChild(_didlBuilder.GetFolderElement(result, i, displayStubType, item, childCount, filter));
+                        result.DocumentElement.AppendChild(_didlBuilder.GetFolderElement(result, childItem, displayStubType, item, childCount, filter));
                     }
                     else
                     {
-                        result.DocumentElement.AppendChild(_didlBuilder.GetItemElement(result, i, item, serverItem.StubType, deviceId, filter));
+                        result.DocumentElement.AppendChild(_didlBuilder.GetItemElement(result, childItem, item, serverItem.StubType, deviceId, filter));
                     }
                 }
             }
@@ -262,24 +269,6 @@ namespace MediaBrowser.Dlna.ContentDirectory
             };
         }
 
-        private StubType? GetDisplayStubType(BaseItem item, BaseItem context)
-        {
-            if (context == null || context.IsFolder)
-            {
-                var movie = item as Movie;
-                if (movie != null)
-                {
-                    if (movie.LocalTrailerIds.Count > 0 ||
-                        movie.SpecialFeatureIds.Count > 0)
-                    {
-                        return StubType.Folder;
-                    }
-                }
-            }
-
-            return null;
-        }
-
         private async Task<IEnumerable<KeyValuePair<string, string>>> HandleSearch(Headers sparams, User user, string deviceId)
         {
             var searchCriteria = new SearchCriteria(sparams.GetValueOrDefault("SearchCriteria", ""));
@@ -408,16 +397,49 @@ namespace MediaBrowser.Dlna.ContentDirectory
             }).ConfigureAwait(false);
         }
 
-        private async Task<QueryResult<BaseItem>> GetUserItems(BaseItem item, StubType? stubType, User user, SortCriteria sort, int? startIndex, int? limit)
+        private async Task<QueryResult<ServerItem>> GetUserItems(BaseItem item, StubType? stubType, User user, SortCriteria sort, int? startIndex, int? limit)
         {
             if (stubType.HasValue)
             {
-                var movie = item as Movie;
+                if (stubType.Value == StubType.People)
+                {
+                    var items = item.People.Select(i =>
+                    {
+                        try
+                        {
+                            return _libraryManager.GetPerson(i.Name);
+                        }
+                        catch
+                        {
+                            return null;
+                        }
+
+                    }).Where(i => i != null).ToArray();
+
+                    var result = new QueryResult<ServerItem>
+                    {
+                        Items = items.Select(i => new ServerItem { Item = i, StubType = StubType.Folder }).ToArray(),
+                        TotalRecordCount = items.Length
+                    };
 
-                if (movie != null)
+                    return ApplyPaging(result, startIndex, limit);
+                }
+                if (stubType.Value == StubType.Folder)
                 {
-                    return await GetMovieItems(movie).ConfigureAwait(false);
+                    var movie = item as Movie;
+                    if (movie != null)
+                    {
+                        return ApplyPaging(await GetMovieItems(movie).ConfigureAwait(false), startIndex, limit);
+                    }
+                }
+
+                var person = item as Person;
+                if (person != null)
+                {
+                    return await GetItemsFromPerson(person, user, startIndex, limit).ConfigureAwait(false);
                 }
+
+                return ApplyPaging(new QueryResult<ServerItem>(), startIndex, limit);
             }
 
             var folder = (Folder)item;
@@ -428,7 +450,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
                 sortOrders.Add(ItemSortBy.SortName);
             }
 
-            return await folder.GetItems(new InternalItemsQuery
+            var queryResult = await folder.GetItems(new InternalItemsQuery
             {
                 Limit = limit,
                 StartIndex = startIndex,
@@ -438,22 +460,138 @@ namespace MediaBrowser.Dlna.ContentDirectory
                 Filter = FilterUnsupportedContent
 
             }).ConfigureAwait(false);
+
+            var serverItems = queryResult
+                .Items
+                .Select(i => new ServerItem
+                {
+                    Item = i,
+                    StubType = GetDisplayStubType(i, item)
+                })
+                .ToArray();
+
+            return new QueryResult<ServerItem>
+            {
+                TotalRecordCount = queryResult.TotalRecordCount,
+                Items = serverItems
+            };
+        }
+
+        private async Task<QueryResult<ServerItem>> GetItemsFromPerson(Person person, User user, int? startIndex, int? limit)
+        {
+            var items = user.RootFolder.GetRecursiveChildren(user)
+                .Where(i => i is Movie || i is Series)
+                .Where(i => i.ContainsPerson(person.Name))
+                .ToList();
+
+            var trailerResult = await _channelManager.GetAllMediaInternal(new AllChannelMediaQuery
+            {
+                ContentTypes = new[] { ChannelMediaContentType.MovieExtra },
+                ExtraTypes = new[] { ExtraType.Trailer },
+                UserId = user.Id.ToString("N")
+
+            }, CancellationToken.None).ConfigureAwait(false);
+
+            var currentIds = items.Select(i => i.GetProviderId(MetadataProviders.Imdb))
+                .ToList();
+
+            var trailersToAdd = trailerResult.Items
+                .Where(i => i.ContainsPerson(person.Name))
+                .Where(i =>
+                {
+                    // Try to filter out dupes using imdb id
+                    var imdb = i.GetProviderId(MetadataProviders.Imdb);
+                    if (!string.IsNullOrWhiteSpace(imdb) &&
+                        currentIds.Contains(imdb, StringComparer.OrdinalIgnoreCase))
+                    {
+                        return false;
+                    }
+                    return true;
+                });
+
+            items.AddRange(trailersToAdd);
+
+            items = _libraryManager.Sort(items, user, new[] { ItemSortBy.SortName }, SortOrder.Ascending)
+                .Skip(startIndex ?? 0)
+                .Take(limit ?? int.MaxValue)
+                .ToList();
+
+            var serverItems = items.Select(i => new ServerItem
+            {
+                Item = i,
+                StubType = null
+            })
+            .ToArray();
+
+            return new QueryResult<ServerItem>
+            {
+                TotalRecordCount = serverItems.Length,
+                Items = serverItems
+            };
         }
 
-        private Task<QueryResult<BaseItem>> GetMovieItems(Movie item)
+        private QueryResult<ServerItem> ApplyPaging(QueryResult<ServerItem> result, int? startIndex, int? limit)
+        {
+            result.Items = result.Items.Skip(startIndex ?? 0).Take(limit ?? int.MaxValue).ToArray();
+
+            return result;
+        }
+
+        private StubType? GetDisplayStubType(BaseItem item, BaseItem context)
+        {
+            if (context == null || context.IsFolder)
+            {
+                var movie = item as Movie;
+                if (movie != null)
+                {
+                    if (movie.GetTrailerIds().Count > 0 ||
+                        movie.SpecialFeatureIds.Count > 0)
+                    {
+                        return StubType.Folder;
+                    }
+                }
+
+                if (EnablePeopleDisplay(item))
+                {
+                    return StubType.Folder;
+                }
+            }
+
+            return null;
+        }
+
+        private bool EnablePeopleDisplay(BaseItem item)
+        {
+            if (item.People.Count > 0)
+            {
+                return item is Movie;
+            }
+
+            return false;
+        }
+
+        private Task<QueryResult<ServerItem>> GetMovieItems(Movie item)
         {
             var list = new List<BaseItem>();
 
             list.Add(item);
 
-            list.AddRange(item.LocalTrailerIds.Select(i => _libraryManager.GetItemById(i)).Where(i => i != null));
+            list.AddRange(item.GetTrailerIds().Select(i => _libraryManager.GetItemById(i)).Where(i => i != null));
             list.AddRange(item.SpecialFeatureIds.Select(i => _libraryManager.GetItemById(i)).Where(i => i != null));
-            list.AddRange(item.ThemeVideoIds.Select(i => _libraryManager.GetItemById(i)).Where(i => i != null));
 
-            return Task.FromResult(new QueryResult<BaseItem>
+            var serverItems = list.Select(i => new ServerItem { Item = i, StubType = null })
+                .ToList();
+
+            serverItems.Add(new ServerItem
             {
-                Items = list.ToArray(),
-                TotalRecordCount = list.Count
+                Item = item,
+                StubType = StubType.People
+            });
+
+            return Task.FromResult(new QueryResult<ServerItem>
+            {
+                Items = serverItems.ToArray(),
+                TotalRecordCount = serverItems.Count
             });
         }
 
@@ -498,6 +636,11 @@ namespace MediaBrowser.Dlna.ContentDirectory
                 stubType = StubType.Folder;
                 id = id.Split(new[] { '_' }, 2)[1];
             }
+            else if (id.StartsWith("people_", StringComparison.OrdinalIgnoreCase))
+            {
+                stubType = StubType.People;
+                id = id.Split(new[] { '_' }, 2)[1];
+            }
 
             if (Guid.TryParse(id, out itemId))
             {
@@ -524,6 +667,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
 
     public enum StubType
     {
-        Folder = 0
+        Folder = 0,
+        People = 1
     }
 }

+ 36 - 5
MediaBrowser.Dlna/Didl/DidlBuilder.cs

@@ -111,7 +111,7 @@ namespace MediaBrowser.Dlna.Didl
                 }
             }
 
-            AddCover(item, element);
+            AddCover(item, null, element);
 
             return element;
         }
@@ -293,8 +293,17 @@ namespace MediaBrowser.Dlna.Didl
             container.AppendChild(res);
         }
 
-        private string GetDisplayName(BaseItem item, BaseItem context)
+        private string GetDisplayName(BaseItem item, StubType? itemStubType, BaseItem context)
         {
+            if (itemStubType.HasValue && itemStubType.Value == StubType.People)
+            {
+                if (item is Video)
+                {
+                    return _localization.GetLocalizedString("HeaderCastCrew");
+                }
+                return _localization.GetLocalizedString("HeaderPeople");
+            }
+
             var episode = item as Episode;
             var season = context as Season;
 
@@ -460,7 +469,7 @@ namespace MediaBrowser.Dlna.Didl
 
             AddCommonFields(folder, stubType, null, container, filter);
 
-            AddCover(folder, container);
+            AddCover(folder, stubType, container);
 
             return container;
         }
@@ -491,7 +500,7 @@ namespace MediaBrowser.Dlna.Didl
             // MediaMonkey for example won't display content without a title
             //if (filter.Contains("dc:title"))
             {
-                AddValue(element, "dc", "title", GetDisplayName(item, context), NS_DC);
+                AddValue(element, "dc", "title", GetDisplayName(item, itemStubType, context), NS_DC);
             }
 
             element.AppendChild(CreateObjectClass(element.OwnerDocument, item, itemStubType));
@@ -741,8 +750,14 @@ namespace MediaBrowser.Dlna.Didl
             }
         }
 
-        private void AddCover(BaseItem item, XmlElement element)
+        private void AddCover(BaseItem item, StubType? stubType, XmlElement element)
         {
+            if (stubType.HasValue && stubType.Value == StubType.People)
+            {
+                AddEmbeddedImageAsCover("people", element);
+                return;
+            }
+
             var imageInfo = GetImageInfo(item);
 
             if (imageInfo == null)
@@ -801,6 +816,22 @@ namespace MediaBrowser.Dlna.Didl
             }
         }
 
+        private void AddEmbeddedImageAsCover(string name, XmlElement element)
+        {
+            var result = element.OwnerDocument;
+            
+            var icon = result.CreateElement("upnp", "albumArtURI", NS_UPNP);
+            var profile = result.CreateAttribute("dlna", "profileID", NS_DLNA);
+            profile.InnerText = _profile.AlbumArtPn;
+            icon.SetAttributeNode(profile);
+            icon.InnerText = _serverAddress + "/Dlna/icons/people480.jpg";
+            element.AppendChild(icon);
+
+            icon = result.CreateElement("upnp", "icon", NS_UPNP);
+            icon.InnerText = _serverAddress + "/Dlna/icons/people48.jpg";
+            element.AppendChild(icon);
+        }
+
         private void AddImageResElement(BaseItem item,
             XmlElement element,
             int maxWidth,

+ 5 - 5
MediaBrowser.Dlna/DlnaManager.cs

@@ -8,6 +8,7 @@ using MediaBrowser.Dlna.Profiles;
 using MediaBrowser.Dlna.Server;
 using MediaBrowser.Model.Dlna;
 using MediaBrowser.Model.Dlna.Profiles;
+using MediaBrowser.Model.Drawing;
 using MediaBrowser.Model.Logging;
 using MediaBrowser.Model.Serialization;
 using System;
@@ -469,13 +470,13 @@ namespace MediaBrowser.Dlna
             return new DescriptionXmlBuilder(profile, serverUuId, "").GetXml();
         }
 
-        public DlnaIconResponse GetIcon(string filename)
+        public ImageStream GetIcon(string filename)
         {
             var format = filename.EndsWith(".png", StringComparison.OrdinalIgnoreCase)
                 ? ImageFormat.Png
                 : ImageFormat.Jpg;
 
-            return new DlnaIconResponse
+            return new ImageStream
             {
                 Format = format,
                 Stream = GetType().Assembly.GetManifestResourceStream("MediaBrowser.Dlna.Images." + filename.ToLower())
@@ -522,10 +523,9 @@ namespace MediaBrowser.Dlna
                 new LgTvProfile(),
                 new Foobar2000Profile(),
                 new MediaMonkeyProfile(),
-                new Windows81Profile(),
+                //new Windows81Profile(),
                 //new WindowsMediaCenterProfile(),
-                new WindowsPhoneProfile(),
-                new AndroidProfile(),
+                //new WindowsPhoneProfile(),
                 new DirectTvProfile(),
                 new DishHopperJoeyProfile(),
                 new DefaultProfile(),

BIN=BIN
MediaBrowser.Dlna/Images/people48.jpg


BIN=BIN
MediaBrowser.Dlna/Images/people48.png


BIN=BIN
MediaBrowser.Dlna/Images/people480.jpg


BIN=BIN
MediaBrowser.Dlna/Images/people480.png


+ 8 - 5
MediaBrowser.Dlna/MediaBrowser.Dlna.csproj

@@ -180,11 +180,6 @@
   <ItemGroup>
     <EmbeddedResource Include="Profiles\Xml\MediaMonkey.xml" />
   </ItemGroup>
-  <ItemGroup>
-    <EmbeddedResource Include="Profiles\Xml\Android.xml" />
-    <EmbeddedResource Include="Profiles\Xml\Windows 8 RT.xml" />
-    <EmbeddedResource Include="Profiles\Xml\Windows Phone.xml" />
-  </ItemGroup>
   <ItemGroup>
     <EmbeddedResource Include="Images\logo240.jpg" />
     <EmbeddedResource Include="Images\logo240.png" />
@@ -198,6 +193,14 @@
   <ItemGroup>
     <EmbeddedResource Include="Profiles\Xml\Popcorn Hour.xml" />
   </ItemGroup>
+  <ItemGroup>
+    <EmbeddedResource Include="Images\people48.jpg" />
+    <EmbeddedResource Include="Images\people48.png" />
+  </ItemGroup>
+  <ItemGroup>
+    <EmbeddedResource Include="Images\people480.jpg" />
+    <EmbeddedResource Include="Images\people480.png" />
+  </ItemGroup>
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
   <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
        Other similar extension points exist, see Microsoft.Common.targets.

+ 1 - 1
MediaBrowser.Dlna/Profiles/SamsungSmartTvProfile.cs

@@ -91,7 +91,7 @@ namespace MediaBrowser.Dlna.Profiles
                 },
                 new DirectPlayProfile
                 {
-                    Container = "3gpp",
+                    Container = "3gp",
                     VideoCodec = "h264,mpeg4",
                     AudioCodec = "aac,he-aac",
                     Type = DlnaProfileType.Video

+ 6 - 1
MediaBrowser.Dlna/Profiles/XboxOneProfile.cs

@@ -16,7 +16,12 @@ namespace MediaBrowser.Dlna.Profiles
             Identification = new DeviceIdentification
             {
                 ModelName = "Xbox One",
-                FriendlyName = "Xbox-SystemOS"
+                FriendlyName = "Xbox-SystemOS",
+
+                Headers = new[]
+                {
+                    new HttpHeaderInfo {Name = "User-Agent", Value = "NSPlayer", Match = HeaderMatchType.Substring}
+                }
             };
 
             TranscodingProfiles = new[]

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 24
MediaBrowser.Dlna/Profiles/Xml/Android.xml


+ 1 - 1
MediaBrowser.Dlna/Profiles/Xml/Samsung Smart TV.xml

@@ -42,7 +42,7 @@
     <DirectPlayProfile container="avi" audioCodec="mp3,ac3,dca" videoCodec="h264,mpeg4,mjpeg" type="Video" />
     <DirectPlayProfile container="mkv" audioCodec="mp3,ac3,dca,aac" videoCodec="h264,mpeg4,mjpeg4" type="Video" />
     <DirectPlayProfile container="mp4" audioCodec="mp3,aac" videoCodec="h264,mpeg4" type="Video" />
-    <DirectPlayProfile container="3gpp" audioCodec="aac,he-aac" videoCodec="h264,mpeg4" type="Video" />
+    <DirectPlayProfile container="3gp" audioCodec="aac,he-aac" videoCodec="h264,mpeg4" type="Video" />
     <DirectPlayProfile container="mpg,mpeg" audioCodec="ac3,mp2,mp3,aac" videoCodec="mpeg1video,mpeg2video,h264" type="Video" />
     <DirectPlayProfile container="vro,vob" audioCodec="ac3,mp2,mp3" videoCodec="mpeg1video,mpeg2video" type="Video" />
     <DirectPlayProfile container="ts" audioCodec="ac3,aac,mp3,eac3" videoCodec="mpeg2video,h264,vc1" type="Video" />

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 28
MediaBrowser.Dlna/Profiles/Xml/Windows 8 RT.xml


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 24
MediaBrowser.Dlna/Profiles/Xml/Windows Phone.xml


+ 3 - 1
MediaBrowser.Dlna/Profiles/Xml/Xbox One.xml

@@ -4,7 +4,9 @@
   <Identification>
     <FriendlyName>Xbox-SystemOS</FriendlyName>
     <ModelName>Xbox One</ModelName>
-    <Headers />
+    <Headers>
+      <HttpHeaderInfo name="User-Agent" value="NSPlayer" match="Substring" />
+    </Headers>
   </Identification>
   <FriendlyName>Media Browser</FriendlyName>
   <Manufacturer>Media Browser</Manufacturer>

+ 95 - 43
MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs

@@ -126,33 +126,29 @@ namespace MediaBrowser.LocalMetadata.Images
 
         private void PopulateImages(IHasImages item, List<LocalImageInfo> images, List<FileSystemInfo> files, bool supportParentSeriesFiles, IDirectoryService directoryService)
         {
-            var imagePrefix = string.Empty;
+            var imagePrefix = item.FileNameWithoutExtension + "-";
+            var isInMixedFolder = item.IsInMixedFolder;
 
-            var baseItem = item as BaseItem;
-            if (baseItem != null && baseItem.IsInMixedFolder)
-            {
-                imagePrefix = _fileSystem.GetFileNameWithoutExtension(item.Path) + "-";
-            }
-
-            PopulatePrimaryImages(item, images, files, imagePrefix);
-            PopulateBackdrops(item, images, files, imagePrefix, directoryService);
-            PopulateScreenshots(images, files, imagePrefix);
+            PopulatePrimaryImages(item, images, files, imagePrefix, isInMixedFolder);
 
-            AddImage(files, images, imagePrefix + "logo", ImageType.Logo);
-            AddImage(files, images, imagePrefix + "clearart", ImageType.Art);
-            AddImage(files, images, imagePrefix + "disc", ImageType.Disc);
-            AddImage(files, images, imagePrefix + "cdart", ImageType.Disc);
-            AddImage(files, images, imagePrefix + "box", ImageType.Box);
-            AddImage(files, images, imagePrefix + "back", ImageType.BoxRear);
-            AddImage(files, images, imagePrefix + "boxrear", ImageType.BoxRear);
-            AddImage(files, images, imagePrefix + "menu", ImageType.Menu);
+            AddImage(files, images, "logo", imagePrefix, isInMixedFolder, ImageType.Logo);
+            AddImage(files, images, "clearart", imagePrefix, isInMixedFolder, ImageType.Art);
+            AddImage(files, images, "disc", imagePrefix, isInMixedFolder, ImageType.Disc);
+            AddImage(files, images, "cdart", imagePrefix, isInMixedFolder, ImageType.Disc);
+            AddImage(files, images, "box", imagePrefix, isInMixedFolder, ImageType.Box);
+            AddImage(files, images, "back", imagePrefix, isInMixedFolder, ImageType.BoxRear);
+            AddImage(files, images, "boxrear", imagePrefix, isInMixedFolder, ImageType.BoxRear);
+            AddImage(files, images, "menu", imagePrefix, isInMixedFolder, ImageType.Menu);
 
             // Banner
-            AddImage(files, images, imagePrefix + "banner", ImageType.Banner);
+            AddImage(files, images, "banner", imagePrefix, isInMixedFolder, ImageType.Banner);
 
             // Thumb
-            AddImage(files, images, imagePrefix + "thumb", ImageType.Thumb);
-            AddImage(files, images, imagePrefix + "landscape", ImageType.Thumb);
+            AddImage(files, images, "thumb", imagePrefix, isInMixedFolder, ImageType.Thumb);
+            AddImage(files, images, "landscape", imagePrefix, isInMixedFolder, ImageType.Thumb);
+
+            PopulateBackdrops(item, images, files, imagePrefix, isInMixedFolder, directoryService);
+            PopulateScreenshots(images, files, imagePrefix, isInMixedFolder);
 
             if (supportParentSeriesFiles)
             {
@@ -165,54 +161,72 @@ namespace MediaBrowser.LocalMetadata.Images
             }
         }
 
-        private void PopulatePrimaryImages(IHasImages item, List<LocalImageInfo> images, List<FileSystemInfo> files, string imagePrefix)
+        private void PopulatePrimaryImages(IHasImages item, List<LocalImageInfo> images, List<FileSystemInfo> files, string imagePrefix, bool isInMixedFolder)
         {
-            AddImage(files, images, imagePrefix + "folder", ImageType.Primary);
-            AddImage(files, images, imagePrefix + "cover", ImageType.Primary);
-            AddImage(files, images, imagePrefix + "poster", ImageType.Primary);
-            AddImage(files, images, imagePrefix + "default", ImageType.Primary);
+            var names = new List<string>
+            {
+                "folder",
+                "cover",
+                "poster",
+                "default"
+            };
 
-            // Support plex/xbmc convention
+            // Support plex/kodi convention
             if (item is Series)
             {
-                AddImage(files, images, imagePrefix + "show", ImageType.Primary);
+                names.Add("show");
             }
 
-            // Support plex/xbmc convention
+            // Support plex/kodi convention
             if (item is Video && !(item is Episode))
             {
-                AddImage(files, images, imagePrefix + "movie", ImageType.Primary);
+                names.Add("movie");
             }
 
-            if (!string.IsNullOrEmpty(item.Path))
+            foreach (var name in names)
             {
-                var name = _fileSystem.GetFileNameWithoutExtension(item.Path);
+                AddImage(files, images, imagePrefix + name, ImageType.Primary);
+            }
 
-                if (!string.IsNullOrEmpty(name))
+            var fileNameWithoutExtension = item.FileNameWithoutExtension;
+
+            if (!string.IsNullOrEmpty(fileNameWithoutExtension))
+            {
+                AddImage(files, images, fileNameWithoutExtension, ImageType.Primary);
+            }
+
+            if (!isInMixedFolder)
+            {
+                foreach (var name in names)
                 {
                     AddImage(files, images, name, ImageType.Primary);
-                    AddImage(files, images, name + "-poster", ImageType.Primary);
                 }
             }
         }
 
-        private void PopulateBackdrops(IHasImages item, List<LocalImageInfo> images, List<FileSystemInfo> files, string imagePrefix, IDirectoryService directoryService)
+        private void PopulateBackdrops(IHasImages item, List<LocalImageInfo> images, List<FileSystemInfo> files, string imagePrefix, bool isInMixedFolder, IDirectoryService directoryService)
         {
-            PopulateBackdrops(images, files, imagePrefix, "backdrop", "backdrop", ImageType.Backdrop);
+            PopulateBackdrops(images, files, imagePrefix, "backdrop", "backdrop", isInMixedFolder, ImageType.Backdrop);
 
             if (!string.IsNullOrEmpty(item.Path))
             {
-                var name = _fileSystem.GetFileNameWithoutExtension(item.Path);
+                var name = item.FileNameWithoutExtension;
 
                 if (!string.IsNullOrEmpty(name))
                 {
                     AddImage(files, images, imagePrefix + name + "-fanart", ImageType.Backdrop);
+
+                    // Support without the prefix if it's in it's own folder
+                    if (!isInMixedFolder)
+                    {
+                        AddImage(files, images, name + "-fanart", ImageType.Backdrop);
+                    }
                 }
             }
 
-            PopulateBackdrops(images, files, imagePrefix, "fanart", "fanart-", ImageType.Backdrop);
-            PopulateBackdrops(images, files, imagePrefix, "background", "background-", ImageType.Backdrop);
-            PopulateBackdrops(images, files, imagePrefix, "art", "art-", ImageType.Backdrop);
+            PopulateBackdrops(images, files, imagePrefix, "fanart", "fanart-", isInMixedFolder, ImageType.Backdrop);
+            PopulateBackdrops(images, files, imagePrefix, "background", "background-", isInMixedFolder, ImageType.Backdrop);
+            PopulateBackdrops(images, files, imagePrefix, "art", "art-", isInMixedFolder, ImageType.Backdrop);
 
             var extraFanartFolder = files
                 .FirstOrDefault(i => string.Equals(i.Name, "extrafanart", StringComparison.OrdinalIgnoreCase));
@@ -245,12 +259,12 @@ namespace MediaBrowser.LocalMetadata.Images
             }));
         }
 
-        private void PopulateScreenshots(List<LocalImageInfo> images, List<FileSystemInfo> files, string imagePrefix)
+        private void PopulateScreenshots(List<LocalImageInfo> images, List<FileSystemInfo> files, string imagePrefix, bool isInMixedFolder)
         {
-            PopulateBackdrops(images, files, imagePrefix, "screenshot", "screenshot", ImageType.Screenshot);
+            PopulateBackdrops(images, files, imagePrefix, "screenshot", "screenshot", isInMixedFolder, ImageType.Screenshot);
         }
 
-        private void PopulateBackdrops(List<LocalImageInfo> images, List<FileSystemInfo> files, string imagePrefix, string firstFileName, string subsequentFileNamePrefix, ImageType type)
+        private void PopulateBackdrops(List<LocalImageInfo> images, List<FileSystemInfo> files, string imagePrefix, string firstFileName, string subsequentFileNamePrefix, bool isInMixedFolder, ImageType type)
         {
             AddImage(files, images, imagePrefix + firstFileName, type);
 
@@ -270,6 +284,29 @@ namespace MediaBrowser.LocalMetadata.Images
                     }
                 }
             }
+
+            // Support without the prefix
+            if (!isInMixedFolder)
+            {
+                AddImage(files, images, firstFileName, type);
+
+                unfound = 0;
+                for (var i = 1; i <= 20; i++)
+                {
+                    // Screenshot Image
+                    var found = AddImage(files, images, subsequentFileNamePrefix + i, type);
+
+                    if (!found)
+                    {
+                        unfound++;
+
+                        if (unfound >= 3)
+                        {
+                            break;
+                        }
+                    }
+                }
+            }
         }
 
         private readonly CultureInfo _usCulture = new CultureInfo("en-US");
@@ -310,6 +347,21 @@ namespace MediaBrowser.LocalMetadata.Images
             }
         }
 
+        private bool AddImage(List<FileSystemInfo> files, List<LocalImageInfo> images, string name, string imagePrefix, bool isInMixedFolder, ImageType type)
+        {
+            var added = AddImage(files, images, imagePrefix + name, type);
+
+            if (!isInMixedFolder)
+            {
+                if (AddImage(files, images, name, type))
+                {
+                    added = true;
+                }
+            }
+
+            return added;
+        }
+
         private bool AddImage(IEnumerable<FileSystemInfo> files, List<LocalImageInfo> images, string name, ImageType type)
         {
             var image = GetImage(files, name) as FileInfo;

+ 0 - 1
MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj

@@ -72,7 +72,6 @@
     <Compile Include="Providers\PlaylistXmlProvider.cs" />
     <Compile Include="Providers\SeasonXmlProvider.cs" />
     <Compile Include="Providers\SeriesXmlProvider.cs" />
-    <Compile Include="Providers\TrailerXmlProvider.cs" />
     <Compile Include="Providers\VideoXmlProvider.cs" />
     <Compile Include="Savers\BoxSetXmlSaver.cs" />
     <Compile Include="Savers\ChannelXmlSaver.cs" />

+ 11 - 1
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;
                     }

+ 5 - 17
MediaBrowser.LocalMetadata/Providers/GameXmlProvider.cs

@@ -1,10 +1,10 @@
-using System.IO;
-using System.Threading;
-using MediaBrowser.Common.IO;
+using MediaBrowser.Common.IO;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.LocalMetadata.Parsers;
 using MediaBrowser.Model.Logging;
+using System.IO;
+using System.Threading;
 
 namespace MediaBrowser.LocalMetadata.Providers
 {
@@ -25,22 +25,10 @@ namespace MediaBrowser.LocalMetadata.Providers
 
         protected override FileSystemInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService)
         {
-            var fileInfo = FileSystem.GetFileSystemInfo(info.Path);
-
-            var directoryInfo = fileInfo as DirectoryInfo;
-
-            if (directoryInfo == null)
-            {
-                directoryInfo = new DirectoryInfo(Path.GetDirectoryName(info.Path));
-            }
-
-            var directoryPath = directoryInfo.FullName;
-
-            var specificFile = Path.Combine(directoryPath, FileSystem.GetFileNameWithoutExtension(info.Path) + ".xml");
-
+            var specificFile = Path.ChangeExtension(info.Path, ".xml");
             var file = new FileInfo(specificFile);
 
-            return info.IsInMixedFolder || file.Exists ? file : new FileInfo(Path.Combine(directoryPath, "game.xml"));
+            return info.IsInMixedFolder || file.Exists ? file : new FileInfo(Path.Combine(Path.GetDirectoryName(info.Path), "game.xml"));
         }
     }
 }

+ 0 - 37
MediaBrowser.LocalMetadata/Providers/TrailerXmlProvider.cs

@@ -1,37 +0,0 @@
-using MediaBrowser.Common.IO;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.LocalMetadata.Parsers;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Logging;
-using System.Collections.Generic;
-using System.IO;
-using System.Threading;
-
-namespace MediaBrowser.LocalMetadata.Providers
-{
-    public class TrailerXmlProvider : BaseXmlProvider<Trailer>
-    {
-        private readonly ILogger _logger;
-
-        public TrailerXmlProvider(IFileSystem fileSystem, ILogger logger)
-            : base(fileSystem)
-        {
-            _logger = logger;
-        }
-
-        protected override void Fetch(LocalMetadataResult<Trailer> result, string path, CancellationToken cancellationToken)
-        {
-            var chapters = new List<ChapterInfo>();
-
-            new MovieXmlParser(_logger).Fetch(result.Item, chapters, path, cancellationToken);
-
-            result.Chapters = chapters;
-        }
-
-        protected override FileSystemInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService)
-        {
-            return MovieXmlProvider.GetXmlFileInfo(info, FileSystem);
-        }
-    }
-}

+ 0 - 5
MediaBrowser.LocalMetadata/Savers/PlaylistXmlSaver.cs

@@ -57,11 +57,6 @@ namespace MediaBrowser.LocalMetadata.Savers
 
             builder.Append("<Item>");
 
-            if (!string.IsNullOrEmpty(playlist.OwnerUserId))
-            {
-                builder.Append("<OwnerUserId>" + SecurityElement.Escape(playlist.OwnerUserId) + "</OwnerUserId>");
-            }
-
             if (!string.IsNullOrEmpty(playlist.PlaylistMediaType))
             {
                 builder.Append("<PlaylistMediaType>" + SecurityElement.Escape(playlist.PlaylistMediaType) + "</PlaylistMediaType>");

+ 23 - 0
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("<Shares>");
+
+            foreach (var share in item.Shares)
+            {
+                builder.Append("<Share>");
+
+                builder.Append("<UserId>" + SecurityElement.Escape(share.UserId) + "</UserId>");
+                builder.Append("<CanEdit>" + SecurityElement.Escape(share.CanEdit.ToString().ToLower()) + "</CanEdit>");
+
+                builder.Append("</Share>");
+            }
+
+            builder.Append("</Shares>");
         }
 
         public static void AddChapters(Video item, StringBuilder builder, IItemRepository repository)

+ 3 - 0
MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs

@@ -299,6 +299,9 @@ namespace MediaBrowser.MediaEncoding.Encoder
                 }
             }
 
+            // TODO: Output in webp for smaller sizes
+            // -f image2 -f webp
+
             // Use ffmpeg to sample 100 (we can drop this if required using thumbnail=50 for 50 frames) frames and pick the best thumbnail. Have a fall back just in case.
             var args = useIFrame ? string.Format("-i {0} -threads 0 -v quiet -vframes 1 -vf \"{2},thumbnail=30\" -f image2 \"{1}\"", inputPath, "-", vf) :
                 string.Format("-i {0} -threads 0 -v quiet -vframes 1 -vf \"{2}\" -f image2 \"{1}\"", inputPath, "-", vf);

+ 18 - 9
MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj

@@ -275,6 +275,9 @@
     <Compile Include="..\MediaBrowser.Model\Devices\DeviceOptions.cs">
       <Link>Devices\DeviceOptions.cs</Link>
     </Compile>
+    <Compile Include="..\MediaBrowser.Model\Devices\DeviceQuery.cs">
+      <Link>Devices\DeviceQuery.cs</Link>
+    </Compile>
     <Compile Include="..\MediaBrowser.Model\Devices\DevicesOptions.cs">
       <Link>Devices\DevicesOptions.cs</Link>
     </Compile>
@@ -347,9 +350,6 @@
     <Compile Include="..\MediaBrowser.Model\Dlna\ProfileConditionValue.cs">
       <Link>Dlna\ProfileConditionValue.cs</Link>
     </Compile>
-    <Compile Include="..\MediaBrowser.Model\Dlna\Profiles\AndroidProfile.cs">
-      <Link>Dlna\Profiles\AndroidProfile.cs</Link>
-    </Compile>
     <Compile Include="..\MediaBrowser.Model\Dlna\Profiles\DefaultProfile.cs">
       <Link>Dlna\Profiles\DefaultProfile.cs</Link>
     </Compile>
@@ -404,12 +404,12 @@
     <Compile Include="..\MediaBrowser.Model\Drawing\DrawingUtils.cs">
       <Link>Drawing\DrawingUtils.cs</Link>
     </Compile>
+    <Compile Include="..\MediaBrowser.Model\Drawing\ImageFormat.cs">
+      <Link>Drawing\ImageFormat.cs</Link>
+    </Compile>
     <Compile Include="..\MediaBrowser.Model\Drawing\ImageOrientation.cs">
       <Link>Drawing\ImageOrientation.cs</Link>
     </Compile>
-    <Compile Include="..\MediaBrowser.Model\Drawing\ImageOutputFormat.cs">
-      <Link>Drawing\ImageOutputFormat.cs</Link>
-    </Compile>
     <Compile Include="..\MediaBrowser.Model\Drawing\ImageSize.cs">
       <Link>Drawing\ImageSize.cs</Link>
     </Compile>
@@ -422,6 +422,9 @@
     <Compile Include="..\MediaBrowser.Model\Dto\ChapterInfoDto.cs">
       <Link>Dto\ChapterInfoDto.cs</Link>
     </Compile>
+    <Compile Include="..\MediaBrowser.Model\Dto\DtoOptions.cs">
+      <Link>Dto\DtoOptions.cs</Link>
+    </Compile>
     <Compile Include="..\MediaBrowser.Model\Dto\GameSystemSummary.cs">
       <Link>Dto\GameSystemSummary.cs</Link>
     </Compile>
@@ -1031,6 +1034,12 @@
     <Compile Include="..\MediaBrowser.Model\Sync\SyncJobItem.cs">
       <Link>Sync\SyncJobItem.cs</Link>
     </Compile>
+    <Compile Include="..\MediaBrowser.Model\Sync\SyncJobItemQuery.cs">
+      <Link>SyncJobItemQuery.cs</Link>
+    </Compile>
+    <Compile Include="..\MediaBrowser.Model\Sync\SyncJobItemStatus.cs">
+      <Link>Sync\SyncJobItemStatus.cs</Link>
+    </Compile>
     <Compile Include="..\MediaBrowser.Model\Sync\SyncJobQuery.cs">
       <Link>Sync\SyncJobQuery.cs</Link>
     </Compile>
@@ -1040,9 +1049,6 @@
     <Compile Include="..\MediaBrowser.Model\Sync\SyncJobStatus.cs">
       <Link>Sync\SyncJobStatus.cs</Link>
     </Compile>
-    <Compile Include="..\MediaBrowser.Model\Sync\SyncLimitType.cs">
-      <Link>Sync\SyncLimitType.cs</Link>
-    </Compile>
     <Compile Include="..\MediaBrowser.Model\Sync\SyncQuality.cs">
       <Link>Sync\SyncQuality.cs</Link>
     </Compile>
@@ -1118,6 +1124,9 @@
     <Compile Include="..\MediaBrowser.Model\Users\PinRedeemResult.cs">
       <Link>Users\PinRedeemResult.cs</Link>
     </Compile>
+    <Compile Include="..\MediaBrowser.Model\Users\UserPolicy.cs">
+      <Link>Users\UserPolicy.cs</Link>
+    </Compile>
     <Compile Include="..\SharedVersion.cs">
       <Link>Properties\SharedVersion.cs</Link>
     </Compile>

+ 18 - 9
MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj

@@ -240,6 +240,9 @@
     <Compile Include="..\MediaBrowser.Model\Devices\DeviceOptions.cs">
       <Link>Devices\DeviceOptions.cs</Link>
     </Compile>
+    <Compile Include="..\MediaBrowser.Model\Devices\DeviceQuery.cs">
+      <Link>Devices\DeviceQuery.cs</Link>
+    </Compile>
     <Compile Include="..\MediaBrowser.Model\Devices\DevicesOptions.cs">
       <Link>Devices\DevicesOptions.cs</Link>
     </Compile>
@@ -312,9 +315,6 @@
     <Compile Include="..\MediaBrowser.Model\Dlna\ProfileConditionValue.cs">
       <Link>Dlna\ProfileConditionValue.cs</Link>
     </Compile>
-    <Compile Include="..\MediaBrowser.Model\Dlna\Profiles\AndroidProfile.cs">
-      <Link>Dlna\Profiles\AndroidProfile.cs</Link>
-    </Compile>
     <Compile Include="..\MediaBrowser.Model\Dlna\Profiles\DefaultProfile.cs">
       <Link>Dlna\Profiles\DefaultProfile.cs</Link>
     </Compile>
@@ -369,12 +369,12 @@
     <Compile Include="..\MediaBrowser.Model\Drawing\DrawingUtils.cs">
       <Link>Drawing\DrawingUtils.cs</Link>
     </Compile>
+    <Compile Include="..\MediaBrowser.Model\Drawing\ImageFormat.cs">
+      <Link>Drawing\ImageFormat.cs</Link>
+    </Compile>
     <Compile Include="..\MediaBrowser.Model\Drawing\ImageOrientation.cs">
       <Link>Drawing\ImageOrientation.cs</Link>
     </Compile>
-    <Compile Include="..\MediaBrowser.Model\Drawing\ImageOutputFormat.cs">
-      <Link>Drawing\ImageOutputFormat.cs</Link>
-    </Compile>
     <Compile Include="..\MediaBrowser.Model\Drawing\ImageSize.cs">
       <Link>Drawing\ImageSize.cs</Link>
     </Compile>
@@ -387,6 +387,9 @@
     <Compile Include="..\MediaBrowser.Model\Dto\ChapterInfoDto.cs">
       <Link>Dto\ChapterInfoDto.cs</Link>
     </Compile>
+    <Compile Include="..\MediaBrowser.Model\Dto\DtoOptions.cs">
+      <Link>Dto\DtoOptions.cs</Link>
+    </Compile>
     <Compile Include="..\MediaBrowser.Model\Dto\GameSystemSummary.cs">
       <Link>Dto\GameSystemSummary.cs</Link>
     </Compile>
@@ -990,6 +993,12 @@
     <Compile Include="..\MediaBrowser.Model\Sync\SyncJobItem.cs">
       <Link>Sync\SyncJobItem.cs</Link>
     </Compile>
+    <Compile Include="..\MediaBrowser.Model\Sync\SyncJobItemQuery.cs">
+      <Link>Sync\SyncJobItemQuery.cs</Link>
+    </Compile>
+    <Compile Include="..\MediaBrowser.Model\Sync\SyncJobItemStatus.cs">
+      <Link>Sync\SyncJobItemStatus.cs</Link>
+    </Compile>
     <Compile Include="..\MediaBrowser.Model\Sync\SyncJobQuery.cs">
       <Link>Sync\SyncJobQuery.cs</Link>
     </Compile>
@@ -999,9 +1008,6 @@
     <Compile Include="..\MediaBrowser.Model\Sync\SyncJobStatus.cs">
       <Link>Sync\SyncJobStatus.cs</Link>
     </Compile>
-    <Compile Include="..\MediaBrowser.Model\Sync\SyncLimitType.cs">
-      <Link>Sync\SyncLimitType.cs</Link>
-    </Compile>
     <Compile Include="..\MediaBrowser.Model\Sync\SyncQuality.cs">
       <Link>Sync\SyncQuality.cs</Link>
     </Compile>
@@ -1077,6 +1083,9 @@
     <Compile Include="..\MediaBrowser.Model\Users\PinRedeemResult.cs">
       <Link>Users\PinRedeemResult.cs</Link>
     </Compile>
+    <Compile Include="..\MediaBrowser.Model\Users\UserPolicy.cs">
+      <Link>Users\UserPolicy.cs</Link>
+    </Compile>
     <Compile Include="..\SharedVersion.cs">
       <Link>Properties\SharedVersion.cs</Link>
     </Compile>

+ 2 - 1
MediaBrowser.Model/ApiClient/ConnectionMode.cs

@@ -3,6 +3,7 @@ namespace MediaBrowser.Model.ApiClient
     public enum ConnectionMode
     {
         Local = 1,
-        Remote = 2
+        Remote = 2,
+        Manual = 3
     }
 }

+ 16 - 1
MediaBrowser.Model/ApiClient/IApiClient.cs

@@ -14,6 +14,7 @@ using MediaBrowser.Model.Querying;
 using MediaBrowser.Model.Search;
 using MediaBrowser.Model.Serialization;
 using MediaBrowser.Model.Session;
+using MediaBrowser.Model.Sync;
 using MediaBrowser.Model.System;
 using MediaBrowser.Model.Tasks;
 using MediaBrowser.Model.Users;
@@ -413,7 +414,7 @@ namespace MediaBrowser.Model.ApiClient
         /// </summary>
         /// <param name="query">The query.</param>
         /// <returns>Task{ItemsResult}.</returns>
-        Task<ItemsResult> GetUpcomingEpisodesAsync(NextUpQuery query);
+        Task<ItemsResult> GetUpcomingEpisodesAsync(UpcomingEpisodesQuery query);
 
         /// <summary>
         /// Gets a genre
@@ -1371,6 +1372,20 @@ namespace MediaBrowser.Model.ApiClient
         /// <returns>Task&lt;DevicesOptions&gt;.</returns>
         Task<DevicesOptions> GetDevicesOptions();
 
+        /// <summary>
+        /// Updates the item.
+        /// </summary>
+        /// <param name="item">The item.</param>
+        /// <returns>Task.</returns>
+        Task UpdateItem(BaseItemDto item);
+
+        /// <summary>
+        /// Requests the synchronize.
+        /// </summary>
+        /// <param name="request">The request.</param>
+        /// <returns>Task&lt;SyncJob&gt;.</returns>
+        Task<SyncJob> RequestSync(SyncJobRequest request);
+
         /// <summary>
         /// Opens the web socket.
         /// </summary>

+ 7 - 3
MediaBrowser.Model/ApiClient/ServerCredentials.cs

@@ -50,10 +50,14 @@ namespace MediaBrowser.Model.ApiClient
                 {
                     existing.RemoteAddress = server.RemoteAddress;
                 }
-                if (!existing.IsLocalAddressFixed && !string.IsNullOrEmpty(server.LocalAddress))
+                if (!string.IsNullOrEmpty(server.LocalAddress))
                 {
                     existing.LocalAddress = server.LocalAddress;
                 }
+                if (!string.IsNullOrEmpty(server.ManualAddress))
+                {
+                    existing.LocalAddress = server.ManualAddress;
+                }
                 if (!string.IsNullOrEmpty(server.Name))
                 {
                     existing.Name = server.Name;
@@ -62,9 +66,9 @@ namespace MediaBrowser.Model.ApiClient
                 {
                     existing.WakeOnLanInfos = server.WakeOnLanInfos.ToList();
                 }
-                if (server.IsLocalAddressFixed)
+                if (server.LastConnectionMode.HasValue)
                 {
-                    existing.IsLocalAddressFixed = true;
+                    existing.LastConnectionMode = server.LastConnectionMode;
                 }
             }
             else

+ 5 - 0
MediaBrowser.Model/ApiClient/ServerDiscoveryInfo.cs

@@ -18,5 +18,10 @@ namespace MediaBrowser.Model.ApiClient
         /// </summary>
         /// <value>The name.</value>
         public string Name { get; set; }
+        /// <summary>
+        /// Gets or sets the endpoint address.
+        /// </summary>
+        /// <value>The endpoint address.</value>
+        public string EndpointAddress { get; set; }
     }
 }

+ 18 - 3
MediaBrowser.Model/ApiClient/ServerInfo.cs

@@ -11,14 +11,14 @@ namespace MediaBrowser.Model.ApiClient
         public String Id { get; set; }
         public String LocalAddress { get; set; }
         public String RemoteAddress { get; set; }
+        public String ManualAddress { get; set; }
         public String UserId { get; set; }
         public String AccessToken { get; set; }
         public List<WakeOnLanInfo> WakeOnLanInfos { get; set; }
         public DateTime DateLastAccessed { get; set; }
         public String ExchangeToken { get; set; }
         public UserLinkType? UserLinkType { get; set; }
-
-        public bool IsLocalAddressFixed { get; set; }
+        public ConnectionMode? LastConnectionMode { get; set; }
 
         public ServerInfo()
         {
@@ -30,7 +30,7 @@ namespace MediaBrowser.Model.ApiClient
             Name = systemInfo.ServerName;
             Id = systemInfo.Id;
 
-            if (!IsLocalAddressFixed && !string.IsNullOrEmpty(systemInfo.LocalAddress))
+            if (!string.IsNullOrEmpty(systemInfo.LocalAddress))
             {
                 LocalAddress = systemInfo.LocalAddress;
             }
@@ -55,5 +55,20 @@ namespace MediaBrowser.Model.ApiClient
                 }
             }
         }
+
+        public string GetAddress(ConnectionMode mode)
+        {
+            switch (mode)
+            {
+                case ConnectionMode.Local:
+                    return LocalAddress;
+                case ConnectionMode.Manual:
+                    return ManualAddress;
+                case ConnectionMode.Remote:
+                    return RemoteAddress;
+                default:
+                    throw new ArgumentException("Unexpected ConnectionMode");
+            }
+        }
     }
 }

Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio