瀏覽代碼

Merge pull request #1118 from MediaBrowser/dev

3.0.5641.4
Luke 10 年之前
父節點
當前提交
7d9d57a36e
共有 100 個文件被更改,包括 859 次插入1154 次删除
  1. 1 1
      MediaBrowser.Api/ApiEntryPoint.cs
  2. 6 6
      MediaBrowser.Api/BaseApiService.cs
  3. 2 1
      MediaBrowser.Api/BrandingService.cs
  4. 2 2
      MediaBrowser.Api/DisplayPreferencesService.cs
  5. 3 3
      MediaBrowser.Api/GamesService.cs
  6. 10 2
      MediaBrowser.Api/Images/ImageService.cs
  7. 1 1
      MediaBrowser.Api/ItemUpdateService.cs
  8. 14 14
      MediaBrowser.Api/Library/LibraryService.cs
  9. 7 7
      MediaBrowser.Api/LiveTv/LiveTvService.cs
  10. 8 8
      MediaBrowser.Api/Movies/CollectionService.cs
  11. 4 4
      MediaBrowser.Api/Movies/MoviesService.cs
  12. 2 2
      MediaBrowser.Api/Movies/TrailersService.cs
  13. 7 7
      MediaBrowser.Api/Music/InstantMixService.cs
  14. 39 17
      MediaBrowser.Api/Playback/BaseStreamingService.cs
  15. 3 3
      MediaBrowser.Api/Playback/Dash/MpegDashService.cs
  16. 36 16
      MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
  17. 3 3
      MediaBrowser.Api/Playback/Hls/VideoHlsService.cs
  18. 1 1
      MediaBrowser.Api/Playback/MediaInfoService.cs
  19. 2 3
      MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs
  20. 3 3
      MediaBrowser.Api/Playback/Progressive/VideoService.cs
  21. 7 5
      MediaBrowser.Api/PluginService.cs
  22. 4 5
      MediaBrowser.Api/Reports/ReportRequests.cs
  23. 1 1
      MediaBrowser.Api/Reports/ReportsService.cs
  24. 6 6
      MediaBrowser.Api/Session/SessionsService.cs
  25. 3 3
      MediaBrowser.Api/SimilarItemsHelper.cs
  26. 5 5
      MediaBrowser.Api/TvShowsService.cs
  27. 3 3
      MediaBrowser.Api/UserLibrary/ArtistsService.cs
  28. 4 4
      MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs
  29. 3 3
      MediaBrowser.Api/UserLibrary/GameGenresService.cs
  30. 3 3
      MediaBrowser.Api/UserLibrary/GenresService.cs
  31. 2 2
      MediaBrowser.Api/UserLibrary/ItemsService.cs
  32. 3 3
      MediaBrowser.Api/UserLibrary/MusicGenresService.cs
  33. 3 3
      MediaBrowser.Api/UserLibrary/PersonsService.cs
  34. 5 5
      MediaBrowser.Api/UserLibrary/PlaystateService.cs
  35. 3 3
      MediaBrowser.Api/UserLibrary/StudiosService.cs
  36. 11 13
      MediaBrowser.Api/UserLibrary/UserLibraryService.cs
  37. 3 3
      MediaBrowser.Api/UserLibrary/YearsService.cs
  38. 3 3
      MediaBrowser.Api/VideosService.cs
  39. 4 1
      MediaBrowser.Common.Implementations/BaseApplicationPaths.cs
  40. 31 3
      MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs
  41. 0 1
      MediaBrowser.Controller/Channels/ChannelAudioItem.cs
  42. 0 1
      MediaBrowser.Controller/Channels/ChannelFolderItem.cs
  43. 0 1
      MediaBrowser.Controller/Channels/ChannelVideoItem.cs
  44. 9 0
      MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
  45. 6 0
      MediaBrowser.Controller/Entities/BaseItem.cs
  46. 9 0
      MediaBrowser.Controller/Entities/IHasId.cs
  47. 1 8
      MediaBrowser.Controller/Entities/IHasImages.cs
  48. 1 8
      MediaBrowser.Controller/Entities/IHasMediaSources.cs
  49. 19 0
      MediaBrowser.Controller/Entities/IHasProgramAttributes.cs
  50. 9 0
      MediaBrowser.Controller/Entities/IHasStartDate.cs
  51. 1 8
      MediaBrowser.Controller/Entities/IHasUserData.cs
  52. 13 0
      MediaBrowser.Controller/Entities/InternalItemsQuery.cs
  53. 8 0
      MediaBrowser.Controller/Library/ILibraryManager.cs
  54. 8 0
      MediaBrowser.Controller/Library/IUserDataManager.cs
  55. 3 3
      MediaBrowser.Controller/LiveTv/ILiveTvItem.cs
  56. 22 6
      MediaBrowser.Controller/LiveTv/ILiveTvManager.cs
  57. 17 3
      MediaBrowser.Controller/LiveTv/ILiveTvRecording.cs
  58. 26 5
      MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs
  59. 1 19
      MediaBrowser.Controller/LiveTv/LiveTvProgram.cs
  60. 26 5
      MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs
  61. 4 0
      MediaBrowser.Controller/MediaBrowser.Controller.csproj
  62. 3 3
      MediaBrowser.Controller/Persistence/IDisplayPreferencesRepository.cs
  63. 18 11
      MediaBrowser.Controller/Persistence/IItemRepository.cs
  64. 2 2
      MediaBrowser.Controller/Session/ISessionManager.cs
  65. 8 0
      MediaBrowser.Controller/Subtitles/ISubtitleProvider.cs
  66. 1 1
      MediaBrowser.Controller/Sync/ISyncManager.cs
  67. 1 1
      MediaBrowser.Controller/Sync/ISyncRepository.cs
  68. 10 0
      MediaBrowser.Controller/Sync/SyncedItemProgress.cs
  69. 2 1
      MediaBrowser.Dlna/Profiles/SamsungSmartTvProfile.cs
  70. 1 1
      MediaBrowser.Dlna/Profiles/SonyBravia2010Profile.cs
  71. 1 1
      MediaBrowser.Dlna/Profiles/SonyBravia2011Profile.cs
  72. 1 1
      MediaBrowser.Dlna/Profiles/SonyBravia2012Profile.cs
  73. 1 1
      MediaBrowser.Dlna/Profiles/SonyBravia2013Profile.cs
  74. 1 1
      MediaBrowser.Dlna/Profiles/Xml/Samsung Smart TV.xml
  75. 1 1
      MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2010).xml
  76. 1 1
      MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2011).xml
  77. 1 1
      MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2012).xml
  78. 1 1
      MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2013).xml
  79. 0 3
      MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj
  80. 0 30
      MediaBrowser.LocalMetadata/Providers/AlbumXmlProvider.cs
  81. 0 30
      MediaBrowser.LocalMetadata/Providers/ArtistXmlProvider.cs
  82. 0 95
      MediaBrowser.LocalMetadata/Savers/SeasonXmlSaver.cs
  83. 2 2
      MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs
  84. 24 1
      MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
  85. 6 9
      MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj
  86. 6 9
      MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj
  87. 10 0
      MediaBrowser.Model/ApiClient/ConnectSignupResponse.cs
  88. 7 23
      MediaBrowser.Model/ApiClient/IApiClient.cs
  89. 10 0
      MediaBrowser.Model/ApiClient/IConnectionManager.cs
  90. 10 0
      MediaBrowser.Model/Configuration/AutoOnOff.cs
  91. 10 4
      MediaBrowser.Model/Configuration/ServerConfiguration.cs
  92. 170 1
      MediaBrowser.Model/Dto/BaseItemDto.cs
  93. 3 3
      MediaBrowser.Model/LiveTv/BaseTimerInfoDto.cs
  94. 1 1
      MediaBrowser.Model/LiveTv/ChannelInfoDto.cs
  95. 0 250
      MediaBrowser.Model/LiveTv/ProgramInfoDto.cs
  96. 0 40
      MediaBrowser.Model/LiveTv/RecordingGroupDto.cs
  97. 0 313
      MediaBrowser.Model/LiveTv/RecordingInfoDto.cs
  98. 4 2
      MediaBrowser.Model/LiveTv/TimerInfoDto.cs
  99. 2 3
      MediaBrowser.Model/MediaBrowser.Model.csproj
  100. 96 58
      MediaBrowser.Model/Net/MimeTypes.cs

+ 1 - 1
MediaBrowser.Api/ApiEntryPoint.cs

@@ -340,7 +340,7 @@ namespace MediaBrowser.Api
                 // We can really reduce the timeout for apps that are using the newer api
                 if (!string.IsNullOrWhiteSpace(job.PlaySessionId))
                 {
-                    timerDuration = 60000;
+                    timerDuration = 120000;
                 }
             }
 

+ 6 - 6
MediaBrowser.Api/BaseApiService.cs

@@ -198,15 +198,15 @@ namespace MediaBrowser.Api
             return libraryManager.GetPerson(DeSlugPersonName(name, libraryManager));
         }
 
-        protected IList<BaseItem> GetAllLibraryItems(Guid? userId, IUserManager userManager, ILibraryManager libraryManager, string parentId, Func<BaseItem,bool> filter)
+        protected IList<BaseItem> GetAllLibraryItems(string userId, IUserManager userManager, ILibraryManager libraryManager, string parentId, Func<BaseItem,bool> filter)
         {
             if (!string.IsNullOrEmpty(parentId))
             {
                 var folder = (Folder)libraryManager.GetItemById(new Guid(parentId));
 
-                if (userId.HasValue)
+                if (!string.IsNullOrWhiteSpace(userId))
                 {
-                    var user = userManager.GetUserById(userId.Value);
+                    var user = userManager.GetUserById(userId);
 
                     if (user == null)
                     {
@@ -221,9 +221,9 @@ namespace MediaBrowser.Api
                 return folder
                     .GetRecursiveChildren(filter);
             }
-            if (userId.HasValue)
+            if (!string.IsNullOrWhiteSpace(userId))
             {
-                var user = userManager.GetUserById(userId.Value);
+                var user = userManager.GetUserById(userId);
 
                 if (user == null)
                 {
@@ -231,7 +231,7 @@ namespace MediaBrowser.Api
                 }
 
                 return userManager
-                    .GetUserById(userId.Value)
+                    .GetUserById(userId)
                     .RootFolder
                     .GetRecursiveChildren(user, filter)
                     .ToList();

+ 2 - 1
MediaBrowser.Api/BrandingService.cs

@@ -34,7 +34,8 @@ namespace MediaBrowser.Api
         {
             var result = _config.GetConfiguration<BrandingOptions>("branding");
 
-            return ResultFactory.GetResult(result.CustomCss, "text/css");
+			// When null this throws a 405 error under Mono OSX, so default to empty string
+			return ResultFactory.GetResult(result.CustomCss ?? string.Empty, "text/css");
         }
     }
 }

+ 2 - 2
MediaBrowser.Api/DisplayPreferencesService.cs

@@ -23,7 +23,7 @@ namespace MediaBrowser.Api
         public string DisplayPreferencesId { get; set; }
 
         [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
-        public Guid UserId { get; set; }
+        public string UserId { get; set; }
     }
 
     [Route("/DisplayPreferences/{Id}", "GET", Summary = "Gets a user's display preferences for an item")]
@@ -37,7 +37,7 @@ namespace MediaBrowser.Api
         public string Id { get; set; }
 
         [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
-        public Guid UserId { get; set; }
+        public string UserId { get; set; }
 
         [ApiMember(Name = "Client", Description = "Client", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
         public string Client { get; set; }

+ 3 - 3
MediaBrowser.Api/GamesService.cs

@@ -32,7 +32,7 @@ namespace MediaBrowser.Api
         /// </summary>
         /// <value>The user id.</value>
         [ApiMember(Name = "UserId", Description = "Optional. Filter by user id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
-        public Guid? UserId { get; set; }
+        public string UserId { get; set; }
     }
 
     /// <summary>
@@ -46,7 +46,7 @@ namespace MediaBrowser.Api
         /// </summary>
         /// <value>The user id.</value>
         [ApiMember(Name = "UserId", Description = "Optional. Filter by user id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
-        public Guid? UserId { get; set; }
+        public string UserId { get; set; }
     }
 
     /// <summary>
@@ -106,7 +106,7 @@ namespace MediaBrowser.Api
                 .Cast<GameSystem>()
                 .ToList();
 
-            var user = request.UserId == null ? null : _userManager.GetUserById(request.UserId.Value);
+            var user = request.UserId == null ? null : _userManager.GetUserById(request.UserId);
 
             var result = gameSystems
                 .Select(i => GetSummary(i, user))

+ 10 - 2
MediaBrowser.Api/Images/ImageService.cs

@@ -675,10 +675,18 @@ namespace MediaBrowser.Api.Images
 
         private ImageFormat[] GetClientSupportedFormats()
         {
-            if ((Request.AcceptTypes ?? new string[] { }).Contains("image/webp", StringComparer.OrdinalIgnoreCase))
+            var supportsWebP = (Request.AcceptTypes ?? new string[] {}).Contains("image/webp", StringComparer.OrdinalIgnoreCase);
+
+            var userAgent = Request.UserAgent ?? string.Empty;
+
+            if (userAgent.IndexOf("crosswalk", StringComparison.OrdinalIgnoreCase) != -1 &&
+                userAgent.IndexOf("android", StringComparison.OrdinalIgnoreCase) != -1)
             {
-                var userAgent = Request.UserAgent ?? string.Empty;
+                supportsWebP = true;
+            }
 
+            if (supportsWebP)
+            {
                 // Not displaying properly on iOS
                 if (userAgent.IndexOf("cfnetwork", StringComparison.OrdinalIgnoreCase) == -1)
                 {

+ 1 - 1
MediaBrowser.Api/ItemUpdateService.cs

@@ -428,7 +428,7 @@ namespace MediaBrowser.Api
             var series = item as Series;
             if (series != null)
             {
-                series.Status = request.Status;
+                series.Status = request.SeriesStatus;
                 series.AirDays = request.AirDays;
                 series.AirTime = request.AirTime;
 

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

@@ -77,7 +77,7 @@ namespace MediaBrowser.Api.Library
         /// </summary>
         /// <value>The user id.</value>
         [ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
-        public Guid? UserId { get; set; }
+        public string UserId { get; set; }
 
         /// <summary>
         /// Gets or sets the id.
@@ -102,7 +102,7 @@ namespace MediaBrowser.Api.Library
         /// </summary>
         /// <value>The user id.</value>
         [ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
-        public Guid? UserId { get; set; }
+        public string UserId { get; set; }
 
         /// <summary>
         /// Gets or sets the id.
@@ -127,7 +127,7 @@ namespace MediaBrowser.Api.Library
         /// </summary>
         /// <value>The user id.</value>
         [ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
-        public Guid? UserId { get; set; }
+        public string UserId { get; set; }
 
         /// <summary>
         /// Gets or sets the id.
@@ -159,7 +159,7 @@ namespace MediaBrowser.Api.Library
     public class GetItemCounts : IReturn<ItemCounts>
     {
         [ApiMember(Name = "UserId", Description = "Optional. Get counts from a specific user's library.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
-        public Guid? UserId { get; set; }
+        public string UserId { get; set; }
 
         [ApiMember(Name = "IsFavorite", Description = "Optional. Get counts of favorite items", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
         public bool? IsFavorite { get; set; }
@@ -174,7 +174,7 @@ namespace MediaBrowser.Api.Library
         /// </summary>
         /// <value>The user id.</value>
         [ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
-        public Guid? UserId { get; set; }
+        public string UserId { get; set; }
 
         /// <summary>
         /// Gets or sets the id.
@@ -193,7 +193,7 @@ namespace MediaBrowser.Api.Library
         /// </summary>
         /// <value>The user id.</value>
         [ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
-        public Guid? UserId { get; set; }
+        public string UserId { get; set; }
 
         [ApiMember(Name = "IncludeItemTypes", Description = "Optional. If specified, results will be filtered based on item type. This allows multiple, comma delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
         public string IncludeItemTypes { get; set; }
@@ -412,7 +412,7 @@ namespace MediaBrowser.Api.Library
 
             var baseItemDtos = new List<BaseItemDto>();
 
-            var user = request.UserId.HasValue ? _userManager.GetUserById(request.UserId.Value) : null;
+            var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null;
 
             var dtoOptions = GetDtoOptions(request);
 
@@ -483,15 +483,15 @@ namespace MediaBrowser.Api.Library
             return ToOptimizedSerializedResultUsingCache(counts);
         }
 
-        private bool FilterItem(BaseItem item, GetItemCounts request, Guid? userId)
+        private bool FilterItem(BaseItem item, GetItemCounts request, string userId)
         {
-            if (userId.HasValue)
+            if (!string.IsNullOrWhiteSpace(userId))
             {
                 if (request.IsFavorite.HasValue)
                 {
                     var val = request.IsFavorite.Value;
 
-                    if (_userDataManager.GetUserData(userId.Value, item.GetUserDataKey()).IsFavorite != val)
+                    if (_userDataManager.GetUserData(userId, item.GetUserDataKey()).IsFavorite != val)
                     {
                         return false;
                     }
@@ -609,10 +609,10 @@ namespace MediaBrowser.Api.Library
 
         private ThemeMediaResult GetThemeSongs(GetThemeSongs request)
         {
-            var user = request.UserId.HasValue ? _userManager.GetUserById(request.UserId.Value) : null;
+            var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null;
 
             var item = string.IsNullOrEmpty(request.Id)
-                           ? (request.UserId.HasValue
+                           ? (!string.IsNullOrWhiteSpace(request.UserId)
                                   ? user.RootFolder
                                   : (Folder)_libraryManager.RootFolder)
                            : _libraryManager.GetItemById(request.Id);
@@ -652,10 +652,10 @@ namespace MediaBrowser.Api.Library
 
         public ThemeMediaResult GetThemeVideos(GetThemeVideos request)
         {
-            var user = request.UserId.HasValue ? _userManager.GetUserById(request.UserId.Value) : null;
+            var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null;
 
             var item = string.IsNullOrEmpty(request.Id)
-                           ? (request.UserId.HasValue
+                           ? (!string.IsNullOrWhiteSpace(request.UserId)
                                   ? user.RootFolder
                                   : (Folder)_libraryManager.RootFolder)
                            : _libraryManager.GetItemById(request.Id);

+ 7 - 7
MediaBrowser.Api/LiveTv/LiveTvService.cs

@@ -78,7 +78,7 @@ namespace MediaBrowser.Api.LiveTv
 
     [Route("/LiveTv/Recordings", "GET", Summary = "Gets live tv recordings")]
     [Authenticated]
-    public class GetRecordings : IReturn<QueryResult<RecordingInfoDto>>
+    public class GetRecordings : IReturn<QueryResult<BaseItemDto>>
     {
         [ApiMember(Name = "ChannelId", Description = "Optional filter by channel id.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
         public string ChannelId { get; set; }
@@ -107,7 +107,7 @@ namespace MediaBrowser.Api.LiveTv
 
     [Route("/LiveTv/Recordings/Groups", "GET", Summary = "Gets live tv recording groups")]
     [Authenticated]
-    public class GetRecordingGroups : IReturn<QueryResult<RecordingGroupDto>>
+    public class GetRecordingGroups : IReturn<QueryResult<BaseItemDto>>
     {
         [ApiMember(Name = "UserId", Description = "Optional filter by user and attach user data.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
         public string UserId { get; set; }
@@ -115,7 +115,7 @@ namespace MediaBrowser.Api.LiveTv
 
     [Route("/LiveTv/Recordings/{Id}", "GET", Summary = "Gets a live tv recording")]
     [Authenticated]
-    public class GetRecording : IReturn<RecordingInfoDto>
+    public class GetRecording : IReturn<BaseItemDto>
     {
         [ApiMember(Name = "Id", Description = "Recording Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
         public string Id { get; set; }
@@ -161,7 +161,7 @@ namespace MediaBrowser.Api.LiveTv
 
     [Route("/LiveTv/Programs", "GET,POST", Summary = "Gets available live tv epgs..")]
     [Authenticated]
-    public class GetPrograms : IReturn<QueryResult<ProgramInfoDto>>
+    public class GetPrograms : IReturn<QueryResult<BaseItemDto>>
     {
         [ApiMember(Name = "ChannelIds", Description = "The channels to return guide information for.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET,POST")]
         public string ChannelIds { get; set; }
@@ -208,7 +208,7 @@ namespace MediaBrowser.Api.LiveTv
 
     [Route("/LiveTv/Programs/Recommended", "GET", Summary = "Gets available live tv epgs..")]
     [Authenticated]
-    public class GetRecommendedPrograms : IReturn<QueryResult<ProgramInfoDto>>
+    public class GetRecommendedPrograms : IReturn<QueryResult<BaseItemDto>>
     {
         [ApiMember(Name = "UserId", Description = "Optional filter by user id.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET,POST")]
         public string UserId { get; set; }
@@ -231,7 +231,7 @@ namespace MediaBrowser.Api.LiveTv
 
     [Route("/LiveTv/Programs/{Id}", "GET", Summary = "Gets a live tv program")]
     [Authenticated]
-    public class GetProgram : IReturn<ProgramInfoDto>
+    public class GetProgram : IReturn<BaseItemDto>
     {
         [ApiMember(Name = "Id", Description = "Program Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
         public string Id { get; set; }
@@ -310,7 +310,7 @@ namespace MediaBrowser.Api.LiveTv
 
     [Route("/LiveTv/Recordings/Groups/{Id}", "GET", Summary = "Gets a recording group")]
     [Authenticated]
-    public class GetRecordingGroup : IReturn<RecordingGroupDto>
+    public class GetRecordingGroup : IReturn<BaseItemDto>
     {
         [ApiMember(Name = "Id", Description = "Recording group Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
         public string Id { get; set; }

+ 8 - 8
MediaBrowser.Api/Movies/CollectionService.cs

@@ -2,8 +2,6 @@
 using MediaBrowser.Controller.Dto;
 using MediaBrowser.Controller.Net;
 using MediaBrowser.Model.Collections;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Querying;
 using ServiceStack;
 using System;
 using System.Collections.Generic;
@@ -22,7 +20,7 @@ namespace MediaBrowser.Api.Movies
         public string Name { get; set; }
 
         [ApiMember(Name = "ParentId", Description = "Optional - create the collection within a specific folder", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
-        public Guid? ParentId { get; set; }
+        public string ParentId { get; set; }
 
         [ApiMember(Name = "Ids", Description = "Item Ids to add to the collection", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST", AllowMultiple = true)]
         public string Ids { get; set; }
@@ -35,7 +33,7 @@ namespace MediaBrowser.Api.Movies
         public string Ids { get; set; }
 
         [ApiMember(Name = "Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
-        public Guid Id { get; set; }
+        public string Id { get; set; }
     }
 
     [Route("/Collections/{Id}/Items", "DELETE", Summary = "Removes items from a collection")]
@@ -45,7 +43,7 @@ namespace MediaBrowser.Api.Movies
         public string Ids { get; set; }
 
         [ApiMember(Name = "Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
-        public Guid Id { get; set; }
+        public string Id { get; set; }
     }
 
     [Authenticated]
@@ -64,11 +62,13 @@ namespace MediaBrowser.Api.Movies
         {
             var userId = AuthorizationContext.GetAuthorizationInfo(Request).UserId;
 
+            var parentId = string.IsNullOrWhiteSpace(request.ParentId) ? (Guid?)null : new Guid(request.ParentId);
+
             var item = await _collectionManager.CreateCollection(new CollectionCreationOptions
             {
                 IsLocked = request.IsLocked,
                 Name = request.Name,
-                ParentId = request.ParentId,
+                ParentId = parentId,
                 ItemIdList = (request.Ids ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).Select(i => new Guid(i)).ToList(),
                 UserIds = new List<Guid> { new Guid(userId) }
 
@@ -86,14 +86,14 @@ namespace MediaBrowser.Api.Movies
 
         public void Post(AddToCollection request)
         {
-            var task = _collectionManager.AddToCollection(request.Id, request.Ids.Split(',').Select(i => new Guid(i)));
+            var task = _collectionManager.AddToCollection(new Guid(request.Id), request.Ids.Split(',').Select(i => new Guid(i)));
 
             Task.WaitAll(task);
         }
 
         public void Delete(RemoveFromCollection request)
         {
-            var task = _collectionManager.RemoveFromCollection(request.Id, request.Ids.Split(',').Select(i => new Guid(i)));
+            var task = _collectionManager.RemoveFromCollection(new Guid(request.Id), request.Ids.Split(',').Select(i => new Guid(i)));
 
             Task.WaitAll(task);
         }

+ 4 - 4
MediaBrowser.Api/Movies/MoviesService.cs

@@ -42,7 +42,7 @@ namespace MediaBrowser.Api.Movies
         /// </summary>
         /// <value>The user id.</value>
         [ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
-        public Guid? UserId { get; set; }
+        public string UserId { get; set; }
 
         /// <summary>
         /// Specify this to localize the search to a specific item or folder. Omit to use the root.
@@ -119,7 +119,7 @@ namespace MediaBrowser.Api.Movies
 
         public async Task<object> Get(GetMovieRecommendations request)
         {
-            var user = _userManager.GetUserById(request.UserId.Value);
+            var user = _userManager.GetUserById(request.UserId);
 
             IEnumerable<BaseItem> movies = GetAllLibraryItems(request.UserId, _userManager, _libraryManager, request.ParentId, i => i is Movie);
 
@@ -167,10 +167,10 @@ namespace MediaBrowser.Api.Movies
 
         private async Task<ItemsResult> GetSimilarItemsResult(BaseGetSimilarItemsFromItem request, Func<BaseItem, bool> includeInSearch, Func<BaseItem, BaseItem, int> getSimilarityScore)
         {
-            var user = request.UserId.HasValue ? _userManager.GetUserById(request.UserId.Value) : null;
+            var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null;
 
             var item = string.IsNullOrEmpty(request.Id) ?
-                (request.UserId.HasValue ? user.RootFolder :
+                (!string.IsNullOrWhiteSpace(request.UserId) ? user.RootFolder :
                 _libraryManager.RootFolder) : _libraryManager.GetItemById(request.Id);
 
             Func<BaseItem, bool> filter = i => i.Id != item.Id && includeInSearch(i);

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

@@ -34,7 +34,7 @@ namespace MediaBrowser.Api.Movies
         /// </summary>
         /// <value>The user id.</value>
         [ApiMember(Name = "UserId", Description = "User Id", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")]
-        public Guid? UserId { get; set; }
+        public string UserId { get; set; }
     }
 
     /// <summary>
@@ -103,7 +103,7 @@ namespace MediaBrowser.Api.Movies
 
         public async Task<object> Get(Getrailers request)
         {
-            var user = request.UserId.HasValue ? _userManager.GetUserById(request.UserId.Value) : null;
+            var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null;
             var result = await GetAllTrailers(user).ConfigureAwait(false);
 
             IEnumerable<BaseItem> items = result.Items;

+ 7 - 7
MediaBrowser.Api/Music/InstantMixService.cs

@@ -75,7 +75,7 @@ namespace MediaBrowser.Api.Music
         {
             var item = _libraryManager.GetItemById(request.Id);
 
-            var user = _userManager.GetUserById(request.UserId.Value);
+            var user = _userManager.GetUserById(request.UserId);
 
             var items = _musicManager.GetInstantMixFromItem(item, user);
 
@@ -86,7 +86,7 @@ namespace MediaBrowser.Api.Music
         {
             var item = _libraryManager.GetItemById(request.Id);
 
-            var user = _userManager.GetUserById(request.UserId.Value);
+            var user = _userManager.GetUserById(request.UserId);
 
             var items = _musicManager.GetInstantMixFromItem(item, user);
 
@@ -97,7 +97,7 @@ namespace MediaBrowser.Api.Music
         {
             var item = _libraryManager.GetItemById(request.Id);
 
-            var user = _userManager.GetUserById(request.UserId.Value);
+            var user = _userManager.GetUserById(request.UserId);
 
             var items = _musicManager.GetInstantMixFromItem(item, user);
 
@@ -108,7 +108,7 @@ namespace MediaBrowser.Api.Music
         {
             var album = _libraryManager.GetItemById(request.Id);
 
-            var user = _userManager.GetUserById(request.UserId.Value);
+            var user = _userManager.GetUserById(request.UserId);
 
             var items = _musicManager.GetInstantMixFromItem(album, user);
 
@@ -119,7 +119,7 @@ namespace MediaBrowser.Api.Music
         {
             var playlist = (Playlist)_libraryManager.GetItemById(request.Id);
 
-            var user = _userManager.GetUserById(request.UserId.Value);
+            var user = _userManager.GetUserById(request.UserId);
 
             var items = _musicManager.GetInstantMixFromItem(playlist, user);
 
@@ -128,7 +128,7 @@ namespace MediaBrowser.Api.Music
 
         public object Get(GetInstantMixFromMusicGenre request)
         {
-            var user = _userManager.GetUserById(request.UserId.Value);
+            var user = _userManager.GetUserById(request.UserId);
 
             var items = _musicManager.GetInstantMixFromGenres(new[] { request.Name }, user);
 
@@ -137,7 +137,7 @@ namespace MediaBrowser.Api.Music
 
         public object Get(GetInstantMixFromArtist request)
         {
-            var user = _userManager.GetUserById(request.UserId.Value);
+            var user = _userManager.GetUserById(request.UserId);
 
             var items = _musicManager.GetInstantMixFromArtist(request.Name, user);
 

+ 39 - 17
MediaBrowser.Api/Playback/BaseStreamingService.cs

@@ -8,12 +8,12 @@ using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.MediaEncoding;
 using MediaBrowser.Model.Configuration;
 using MediaBrowser.Model.Dlna;
-using MediaBrowser.Model.Drawing;
 using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Extensions;
 using MediaBrowser.Model.IO;
 using MediaBrowser.Model.MediaInfo;
+using MediaBrowser.Model.Serialization;
 using System;
 using System.Collections.Generic;
 using System.Diagnostics;
@@ -23,7 +23,6 @@ using System.Linq;
 using System.Text;
 using System.Threading;
 using System.Threading.Tasks;
-using MediaBrowser.Model.Serialization;
 
 namespace MediaBrowser.Api.Playback
 {
@@ -690,7 +689,7 @@ namespace MediaBrowser.Api.Playback
 
                 // TODO: Perhaps also use original_size=1920x800 ??
                 return string.Format("subtitles=filename='{0}'{1},setpts=PTS -{2}/TB",
-                    subtitlePath.Replace("'", "\\'").Replace('\\', '/').Replace(":/", "\\:/"),
+                    subtitlePath.Replace('\\', '/').Replace("'", "\\'").Replace(":/", "\\:/"),
                     charsetParam,
                     seconds.ToString(UsCulture));
             }
@@ -698,7 +697,7 @@ namespace MediaBrowser.Api.Playback
             var mediaPath = state.MediaPath ?? string.Empty;
 
             return string.Format("subtitles='{0}:si={1}',setpts=PTS -{2}/TB",
-                mediaPath.Replace("'", "\\'").Replace('\\', '/').Replace(":/", "\\:/"),
+                mediaPath.Replace('\\', '/').Replace("'", "\\'").Replace(":/", "\\:/"),
                 state.InternalSubtitleStreamOffset.ToString(UsCulture),
                 seconds.ToString(UsCulture));
         }
@@ -773,6 +772,11 @@ namespace MediaBrowser.Api.Playback
                 ? null
                 : audioStream.Channels;
 
+            if (inputChannels <= 0)
+            {
+                inputChannels = null;
+            }
+
             var codec = outputAudioCodec ?? string.Empty;
 
             if (codec.IndexOf("wma", StringComparison.OrdinalIgnoreCase) != -1)
@@ -813,11 +817,11 @@ namespace MediaBrowser.Api.Playback
         }
 
         /// <summary>
-        /// Gets the name of the output audio codec
+        /// Gets the audio encoder.
         /// </summary>
         /// <param name="request">The request.</param>
         /// <returns>System.String.</returns>
-        private string GetAudioCodec(StreamRequest request)
+        protected string GetAudioEncoder(StreamRequest request)
         {
             var codec = request.AudioCodec;
 
@@ -846,7 +850,7 @@ namespace MediaBrowser.Api.Playback
         /// </summary>
         /// <param name="request">The request.</param>
         /// <returns>System.String.</returns>
-        private string GetVideoCodec(VideoStreamRequest request)
+        protected string GetVideoEncoder(VideoStreamRequest request)
         {
             var codec = request.VideoCodec;
 
@@ -1665,13 +1669,13 @@ namespace MediaBrowser.Api.Playback
             state.OutputAudioBitrate = GetAudioBitrateParam(state.Request, state.AudioStream);
             state.OutputAudioSampleRate = request.AudioSampleRate;
 
-            state.OutputAudioCodec = GetAudioCodec(state.Request);
+            state.OutputAudioCodec = state.Request.AudioCodec;
 
             state.OutputAudioChannels = GetNumAudioChannelsParam(state.Request, state.AudioStream, state.OutputAudioCodec);
 
             if (videoRequest != null)
             {
-                state.OutputVideoCodec = GetVideoCodec(videoRequest);
+                state.OutputVideoCodec = state.VideoRequest.VideoCodec;
                 state.OutputVideoBitrate = GetVideoBitrateParamValue(state.VideoRequest, state.VideoStream);
 
                 if (state.OutputVideoBitrate.HasValue)
@@ -1768,6 +1772,12 @@ namespace MediaBrowser.Api.Playback
                 state.InputAudioSync = "1";
             }
 
+            if (string.Equals(mediaSource.Container, "wma", StringComparison.OrdinalIgnoreCase))
+            {
+                // Seeing some stuttering when transcoding wma to audio-only HLS
+                state.InputAudioSync = "1";
+            }
+
             var mediaStreams = mediaSource.MediaStreams;
 
             if (videoRequest != null)
@@ -2061,15 +2071,18 @@ namespace MediaBrowser.Api.Playback
                 state.MimeType = mediaProfile.MimeType;
             }
 
-            var transcodingProfile = state.VideoRequest == null ?
-                profile.GetAudioTranscodingProfile(state.OutputContainer, audioCodec) :
-                profile.GetVideoTranscodingProfile(state.OutputContainer, audioCodec, videoCodec);
-
-            if (transcodingProfile != null)
+            if (!state.Request.Static)
             {
-                state.EstimateContentLength = transcodingProfile.EstimateContentLength;
-                state.EnableMpegtsM2TsMode = transcodingProfile.EnableMpegtsM2TsMode;
-                state.TranscodeSeekInfo = transcodingProfile.TranscodeSeekInfo;
+                var transcodingProfile = state.VideoRequest == null ?
+                    profile.GetAudioTranscodingProfile(state.OutputContainer, audioCodec) :
+                    profile.GetVideoTranscodingProfile(state.OutputContainer, audioCodec, videoCodec);
+
+                if (transcodingProfile != null)
+                {
+                    state.EstimateContentLength = transcodingProfile.EstimateContentLength;
+                    state.EnableMpegtsM2TsMode = transcodingProfile.EnableMpegtsM2TsMode;
+                    state.TranscodeSeekInfo = transcodingProfile.TranscodeSeekInfo;
+                }
             }
         }
 
@@ -2088,6 +2101,15 @@ namespace MediaBrowser.Api.Playback
             responseHeaders["transferMode.dlna.org"] = string.IsNullOrEmpty(transferMode) ? "Streaming" : transferMode;
             responseHeaders["realTimeInfo.dlna.org"] = "DLNA.ORG_TLAG=*";
 
+            if (string.Equals(GetHeader("getMediaInfo.sec"), "1", StringComparison.OrdinalIgnoreCase))
+            {
+                if (state.RunTimeTicks.HasValue)
+                {
+                    var ms = TimeSpan.FromTicks(state.RunTimeTicks.Value).TotalMilliseconds;
+                    responseHeaders["MediaInfo.sec"] = string.Format("SEC_Duration={0};", Convert.ToInt32(ms).ToString(CultureInfo.InvariantCulture));
+                }
+            }
+
             if (state.RunTimeTicks.HasValue && !isStaticallyStreamed && profile != null)
             {
                 AddTimeSeekResponseHeaders(state, responseHeaders);

+ 3 - 3
MediaBrowser.Api/Playback/Dash/MpegDashService.cs

@@ -378,9 +378,9 @@ namespace MediaBrowser.Api.Playback.Dash
 
         protected override string GetAudioArguments(StreamState state)
         {
-            var codec = state.OutputAudioCodec;
+            var codec = GetAudioEncoder(state.Request);
 
-            if (codec.Equals("copy", StringComparison.OrdinalIgnoreCase))
+            if (string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase))
             {
                 return "-codec:a:0 copy";
             }
@@ -408,7 +408,7 @@ namespace MediaBrowser.Api.Playback.Dash
 
         protected override string GetVideoArguments(StreamState state)
         {
-            var codec = state.OutputVideoCodec;
+            var codec = GetVideoEncoder(state.VideoRequest);
 
             var args = "-codec:v:0 " + codec;
 

+ 36 - 16
MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs

@@ -294,6 +294,10 @@ namespace MediaBrowser.Api.Playback.Hls
                         }
                     }
                 }
+            }
+            catch (DirectoryNotFoundException)
+            {
+
             }
             catch (FileNotFoundException)
             {
@@ -446,22 +450,29 @@ namespace MediaBrowser.Api.Playback.Hls
 
             while (!cancellationToken.IsCancellationRequested)
             {
-                using (var fileStream = GetPlaylistFileStream(playlistPath))
+                try
                 {
-                    using (var reader = new StreamReader(fileStream))
+                    using (var fileStream = GetPlaylistFileStream(playlistPath))
                     {
-                        while (!reader.EndOfStream)
+                        using (var reader = new StreamReader(fileStream))
                         {
-                            var text = await reader.ReadLineAsync().ConfigureAwait(false);
-
-                            // If it appears in the playlist, it's done
-                            if (text.IndexOf(segmentFilename, StringComparison.OrdinalIgnoreCase) != -1)
+                            while (!reader.EndOfStream)
                             {
-                                return GetSegmentResult(segmentPath, segmentIndex, segmentLength, transcodingJob);
+                                var text = await reader.ReadLineAsync().ConfigureAwait(false);
+
+                                // If it appears in the playlist, it's done
+                                if (text.IndexOf(segmentFilename, StringComparison.OrdinalIgnoreCase) != -1)
+                                {
+                                    return GetSegmentResult(segmentPath, segmentIndex, segmentLength, transcodingJob);
+                                }
                             }
                         }
                     }
                 }
+                catch (IOException)
+                {
+                    // May get an error if the file is locked
+                }
 
                 await Task.Delay(100, cancellationToken).ConfigureAwait(false);
             }
@@ -775,9 +786,19 @@ namespace MediaBrowser.Api.Playback.Hls
 
         protected override string GetAudioArguments(StreamState state)
         {
+            var codec = GetAudioEncoder(state.Request);
+
             if (!state.IsOutputVideo)
             {
+                if (string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase))
+                {
+                    return "-acodec copy";
+                }
+
                 var audioTranscodeParams = new List<string>();
+
+                audioTranscodeParams.Add("-acodec " + codec);
+                
                 if (state.OutputAudioBitrate.HasValue)
                 {
                     audioTranscodeParams.Add("-ab " + state.OutputAudioBitrate.Value.ToString(UsCulture));
@@ -797,8 +818,6 @@ namespace MediaBrowser.Api.Playback.Hls
                 return string.Join(" ", audioTranscodeParams.ToArray());
             }
 
-            var codec = state.OutputAudioCodec;
-
             if (string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase))
             {
                 return "-codec:a:0 copy";
@@ -832,7 +851,7 @@ namespace MediaBrowser.Api.Playback.Hls
                 return string.Empty;
             }
 
-            var codec = state.OutputVideoCodec;
+            var codec = GetVideoEncoder(state.VideoRequest);
 
             var args = "-codec:v:0 " + codec;
 
@@ -879,7 +898,7 @@ namespace MediaBrowser.Api.Playback.Hls
 
             if (!EnableSplitTranscoding(state))
             {
-                args += " -copyts";
+                //args += " -copyts";
             }
 
             return args;
@@ -910,11 +929,11 @@ namespace MediaBrowser.Api.Playback.Hls
                     //toTimeParam = " -to " + MediaEncoder.GetTimeParameter(endTime);
                     toTimeParam = " -t " + MediaEncoder.GetTimeParameter(TimeSpan.FromSeconds(durationSeconds).Ticks);
                 }
+            }
 
-                if (state.IsOutputVideo && !string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase) && (state.Request.StartTimeTicks ?? 0) > 0)
-                {
-                    timestampOffsetParam = " -output_ts_offset " + MediaEncoder.GetTimeParameter(state.Request.StartTimeTicks ?? 0).ToString(CultureInfo.InvariantCulture);
-                }
+            if (state.IsOutputVideo && !string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase) && (state.Request.StartTimeTicks ?? 0) > 0)
+            {
+                timestampOffsetParam = " -output_ts_offset " + MediaEncoder.GetTimeParameter(state.Request.StartTimeTicks ?? 0).ToString(CultureInfo.InvariantCulture);
             }
 
             var mapArgs = state.IsOutputVideo ? GetMapArgs(state) : string.Empty;
@@ -959,6 +978,7 @@ namespace MediaBrowser.Api.Playback.Hls
 
         private bool EnableSplitTranscoding(StreamState state)
         {
+            return false;
             if (string.Equals(Request.QueryString["EnableSplitTranscoding"], "false", StringComparison.OrdinalIgnoreCase))
             {
                 return false;

+ 3 - 3
MediaBrowser.Api/Playback/Hls/VideoHlsService.cs

@@ -48,9 +48,9 @@ namespace MediaBrowser.Api.Playback.Hls
         /// <returns>System.String.</returns>
         protected override string GetAudioArguments(StreamState state)
         {
-            var codec = state.OutputAudioCodec;
+            var codec = GetAudioEncoder(state.Request);
 
-            if (codec.Equals("copy", StringComparison.OrdinalIgnoreCase))
+            if (string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase))
             {
                 return "-codec:a:0 copy";
             }
@@ -83,7 +83,7 @@ namespace MediaBrowser.Api.Playback.Hls
         /// <returns>System.String.</returns>
         protected override string GetVideoArguments(StreamState state)
         {
-            var codec = state.OutputVideoCodec;
+            var codec = GetVideoEncoder(state.VideoRequest);
 
             var args = "-codec:v:0 " + codec;
 

+ 1 - 1
MediaBrowser.Api/Playback/MediaInfoService.cs

@@ -46,7 +46,7 @@ namespace MediaBrowser.Api.Playback
     }
 
     [Route("/Playback/BitrateTest", "GET")]
-    public class GetBitrateTestBytes : IReturn<PlaybackInfoResponse>
+    public class GetBitrateTestBytes
     {
         [ApiMember(Name = "Size", Description = "Size", IsRequired = true, DataType = "int", ParameterType = "query", Verb = "GET")]
         public long Size { get; set; }

+ 2 - 3
MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs

@@ -1,12 +1,10 @@
-using System.Globalization;
-using MediaBrowser.Common.IO;
+using MediaBrowser.Common.IO;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Devices;
 using MediaBrowser.Controller.Dlna;
 using MediaBrowser.Controller.Drawing;
 using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.LiveTv;
 using MediaBrowser.Controller.MediaEncoding;
 using MediaBrowser.Controller.Net;
 using MediaBrowser.Model.IO;
@@ -15,6 +13,7 @@ using MediaBrowser.Model.Serialization;
 using ServiceStack.Web;
 using System;
 using System.Collections.Generic;
+using System.Globalization;
 using System.IO;
 using System.Threading;
 using System.Threading.Tasks;

+ 3 - 3
MediaBrowser.Api/Playback/Progressive/VideoService.cs

@@ -89,7 +89,7 @@ namespace MediaBrowser.Api.Playback.Progressive
         protected override string GetCommandLineArguments(string outputPath, StreamState state, bool isEncoding)
         {
             // Get the output codec name
-            var videoCodec = state.OutputVideoCodec;
+            var videoCodec = GetVideoEncoder(state.VideoRequest);
 
             var format = string.Empty;
             var keyFrame = string.Empty;
@@ -183,11 +183,11 @@ namespace MediaBrowser.Api.Playback.Progressive
             }
 
             // Get the output codec name
-            var codec = state.OutputAudioCodec;
+            var codec = GetAudioEncoder(state.Request);
 
             var args = "-codec:a:0 " + codec;
 
-            if (codec.Equals("copy", StringComparison.OrdinalIgnoreCase))
+            if (string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase))
             {
                 return args;
             }

+ 7 - 5
MediaBrowser.Api/PluginService.cs

@@ -42,7 +42,7 @@ namespace MediaBrowser.Api
         /// </summary>
         /// <value>The id.</value>
         [ApiMember(Name = "Id", Description = "Plugin Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
-        public Guid Id { get; set; }
+        public string Id { get; set; }
     }
 
     /// <summary>
@@ -57,7 +57,7 @@ namespace MediaBrowser.Api
         /// </summary>
         /// <value>The id.</value>
         [ApiMember(Name = "Id", Description = "Plugin Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
-        public Guid Id { get; set; }
+        public string Id { get; set; }
     }
 
     /// <summary>
@@ -72,7 +72,7 @@ namespace MediaBrowser.Api
         /// </summary>
         /// <value>The id.</value>
         [ApiMember(Name = "Id", Description = "Plugin Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
-        public Guid Id { get; set; }
+        public string Id { get; set; }
 
         /// <summary>
         /// The raw Http Request Input Stream
@@ -239,7 +239,8 @@ namespace MediaBrowser.Api
         /// <returns>System.Object.</returns>
         public object Get(GetPluginConfiguration request)
         {
-            var plugin = _appHost.Plugins.First(p => p.Id == request.Id);
+            var guid = new Guid(request.Id);
+            var plugin = _appHost.Plugins.First(p => p.Id == guid);
 
             var dateModified = plugin.ConfigurationDateLastModified;
 
@@ -298,7 +299,8 @@ namespace MediaBrowser.Api
         /// <param name="request">The request.</param>
         public void Delete(UninstallPlugin request)
         {
-            var plugin = _appHost.Plugins.First(p => p.Id == request.Id);
+            var guid = new Guid(request.Id);
+            var plugin = _appHost.Plugins.First(p => p.Id == guid);
 
             _installationManager.UninstallPlugin(plugin);
         }

+ 4 - 5
MediaBrowser.Api/Reports/ReportRequests.cs

@@ -1,10 +1,9 @@
-using System;
-using System.Linq;
-using MediaBrowser.Api.UserLibrary;
-using MediaBrowser.Controller.Net;
+using MediaBrowser.Api.UserLibrary;
 using MediaBrowser.Model.Entities;
 using ServiceStack;
+using System;
 using System.Collections.Generic;
+using System.Linq;
 
 namespace MediaBrowser.Api.Reports
 {
@@ -15,7 +14,7 @@ namespace MediaBrowser.Api.Reports
         /// </summary>
         /// <value>The user id.</value>
         [ApiMember(Name = "UserId", Description = "User Id", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")]
-        public Guid? UserId { get; set; }
+        public string UserId { get; set; }
 
         /// <summary>
         /// Limit results to items containing a specific person

+ 1 - 1
MediaBrowser.Api/Reports/ReportsService.cs

@@ -175,7 +175,7 @@ namespace MediaBrowser.Api.Reports
 		{
 			// Placeholder in case needed later
 			request.Recursive = true;
-			var user = request.UserId.HasValue ? _userManager.GetUserById(request.UserId.Value) : null;
+			var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null;
 			request.Fields = "MediaSources,DateCreated,Settings,Studios,SyncInfo,ItemCounts";
 
 			var parentItem = string.IsNullOrEmpty(request.ParentId) ?

+ 6 - 6
MediaBrowser.Api/Session/SessionsService.cs

@@ -21,7 +21,7 @@ namespace MediaBrowser.Api.Session
     public class GetSessions : IReturn<List<SessionInfoDto>>
     {
         [ApiMember(Name = "ControllableByUserId", Description = "Optional. Filter by sessions that a given user is allowed to remote control.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
-        public Guid? ControllableByUserId { get; set; }
+        public string ControllableByUserId { get; set; }
 
         [ApiMember(Name = "DeviceId", Description = "Optional. Filter by device id.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
         public string DeviceId { get; set; }
@@ -200,7 +200,7 @@ namespace MediaBrowser.Api.Session
         public string Id { get; set; }
 
         [ApiMember(Name = "UserId", Description = "UserId Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
-        public Guid UserId { get; set; }
+        public string UserId { get; set; }
     }
 
     [Route("/Sessions/{Id}/Users/{UserId}", "DELETE", Summary = "Removes an additional user from a session")]
@@ -211,7 +211,7 @@ namespace MediaBrowser.Api.Session
         public string Id { get; set; }
 
         [ApiMember(Name = "UserId", Description = "UserId Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
-        public Guid UserId { get; set; }
+        public string UserId { get; set; }
     }
 
     [Route("/Sessions/Capabilities", "POST", Summary = "Updates capabilities for a device")]
@@ -375,15 +375,15 @@ namespace MediaBrowser.Api.Session
                 result = result.Where(i => string.Equals(i.DeviceId, request.DeviceId, StringComparison.OrdinalIgnoreCase));
             }
 
-            if (request.ControllableByUserId.HasValue)
+            if (!string.IsNullOrWhiteSpace(request.ControllableByUserId))
             {
                 result = result.Where(i => i.SupportsMediaControl);
 
-                var user = _userManager.GetUserById(request.ControllableByUserId.Value);
+                var user = _userManager.GetUserById(request.ControllableByUserId);
 
                 if (!user.Policy.EnableRemoteControlOfOtherUsers)
                 {
-                    result = result.Where(i => !i.UserId.HasValue || i.ContainsUser(request.ControllableByUserId.Value));
+                    result = result.Where(i => !i.UserId.HasValue || i.ContainsUser(request.ControllableByUserId));
                 }
 
                 if (!user.Policy.EnableSharedDeviceControl)

+ 3 - 3
MediaBrowser.Api/SimilarItemsHelper.cs

@@ -32,7 +32,7 @@ namespace MediaBrowser.Api
         /// </summary>
         /// <value>The user id.</value>
         [ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
-        public Guid? UserId { get; set; }
+        public string UserId { get; set; }
 
         /// <summary>
         /// The maximum number of items to return
@@ -70,10 +70,10 @@ namespace MediaBrowser.Api
         /// <returns>ItemsResult.</returns>
         internal static ItemsResult GetSimilarItemsResult(DtoOptions dtoOptions, IUserManager userManager, IItemRepository itemRepository, ILibraryManager libraryManager, IUserDataManager userDataRepository, IDtoService dtoService, ILogger logger, BaseGetSimilarItemsFromItem request, Func<BaseItem, bool> includeInSearch, Func<BaseItem, BaseItem, int> getSimilarityScore)
         {
-            var user = request.UserId.HasValue ? userManager.GetUserById(request.UserId.Value) : null;
+            var user = !string.IsNullOrWhiteSpace(request.UserId) ? userManager.GetUserById(request.UserId) : null;
 
             var item = string.IsNullOrEmpty(request.Id) ?
-                (request.UserId.HasValue ? user.RootFolder :
+                (!string.IsNullOrWhiteSpace(request.UserId) ? user.RootFolder :
                 libraryManager.RootFolder) : libraryManager.GetItemById(request.Id);
 
             Func<BaseItem, bool> filter = i => i.Id != item.Id && includeInSearch(i);

+ 5 - 5
MediaBrowser.Api/TvShowsService.cs

@@ -77,7 +77,7 @@ namespace MediaBrowser.Api
         /// </summary>
         /// <value>The user id.</value>
         [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
-        public Guid UserId { get; set; }
+        public string UserId { get; set; }
 
         /// <summary>
         /// Skips over a given number of items within the results. Use for paging.
@@ -130,7 +130,7 @@ namespace MediaBrowser.Api
         /// </summary>
         /// <value>The user id.</value>
         [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
-        public Guid UserId { get; set; }
+        public string UserId { get; set; }
 
         /// <summary>
         /// Fields to return within the items, in addition to basic information
@@ -140,7 +140,7 @@ namespace MediaBrowser.Api
         public string Fields { get; set; }
 
         [ApiMember(Name = "Id", Description = "The series id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
-        public Guid Id { get; set; }
+        public string Id { get; set; }
 
         [ApiMember(Name = "Season", Description = "Optional filter by season number.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
         public int? Season { get; set; }
@@ -183,7 +183,7 @@ namespace MediaBrowser.Api
         /// </summary>
         /// <value>The user id.</value>
         [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
-        public Guid UserId { get; set; }
+        public string UserId { get; set; }
 
         /// <summary>
         /// Fields to return within the items, in addition to basic information
@@ -193,7 +193,7 @@ namespace MediaBrowser.Api
         public string Fields { get; set; }
 
         [ApiMember(Name = "Id", Description = "The series id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
-        public Guid Id { get; set; }
+        public string Id { get; set; }
 
         [ApiMember(Name = "IsSpecialSeason", Description = "Optional. Filter by special season.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
         public bool? IsSpecialSeason { get; set; }

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

@@ -40,7 +40,7 @@ namespace MediaBrowser.Api.UserLibrary
         /// </summary>
         /// <value>The user id.</value>
         [ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
-        public Guid? UserId { get; set; }
+        public string UserId { get; set; }
     }
 
     /// <summary>
@@ -84,9 +84,9 @@ namespace MediaBrowser.Api.UserLibrary
 
             var dtoOptions = GetDtoOptions(request);
 
-            if (request.UserId.HasValue)
+            if (!string.IsNullOrWhiteSpace(request.UserId))
             {
-                var user = UserManager.GetUserById(request.UserId.Value);
+                var user = UserManager.GetUserById(request.UserId);
 
                 return DtoService.GetBaseItemDto(item, dtoOptions, user);
             }

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

@@ -61,9 +61,9 @@ namespace MediaBrowser.Api.UserLibrary
             BaseItem parentItem;
             List<BaseItem> libraryItems = null;
 
-            if (request.UserId.HasValue)
+            if (!string.IsNullOrWhiteSpace(request.UserId))
             {
-                user = UserManager.GetUserById(request.UserId.Value);
+                user = UserManager.GetUserById(request.UserId);
                 parentItem = string.IsNullOrEmpty(request.ParentId) ? user.RootFolder : LibraryManager.GetItemById(request.ParentId);
 
                 if (RequiresLibraryItems(request, dtoOptions))
@@ -92,7 +92,7 @@ namespace MediaBrowser.Api.UserLibrary
             {
                 var folder = (Folder)parentItem;
 
-                if (request.UserId.HasValue)
+                if (!string.IsNullOrWhiteSpace(request.UserId))
                 {
                     items = request.Recursive ?
                         folder.GetRecursiveChildren(user, filter) :
@@ -388,7 +388,7 @@ namespace MediaBrowser.Api.UserLibrary
         /// </summary>
         /// <value>The user id.</value>
         [ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
-        public Guid? UserId { get; set; }
+        public string UserId { get; set; }
 
         [ApiMember(Name = "NameStartsWithOrGreater", Description = "Optional filter by items whose name is sorted equally or greater than a given input string.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
         public string NameStartsWithOrGreater { get; set; }

+ 3 - 3
MediaBrowser.Api/UserLibrary/GameGenresService.cs

@@ -37,7 +37,7 @@ namespace MediaBrowser.Api.UserLibrary
         /// </summary>
         /// <value>The user id.</value>
         [ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
-        public Guid? UserId { get; set; }
+        public string UserId { get; set; }
     }
 
     [Authenticated]
@@ -71,9 +71,9 @@ namespace MediaBrowser.Api.UserLibrary
 
             var dtoOptions = GetDtoOptions(request);
 
-            if (request.UserId.HasValue)
+            if (!string.IsNullOrWhiteSpace(request.UserId))
             {
-                var user = UserManager.GetUserById(request.UserId.Value);
+                var user = UserManager.GetUserById(request.UserId);
 
                 return DtoService.GetBaseItemDto(item, dtoOptions, user);
             }

+ 3 - 3
MediaBrowser.Api/UserLibrary/GenresService.cs

@@ -39,7 +39,7 @@ namespace MediaBrowser.Api.UserLibrary
         /// </summary>
         /// <value>The user id.</value>
         [ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
-        public Guid? UserId { get; set; }
+        public string UserId { get; set; }
     }
 
     /// <summary>
@@ -76,9 +76,9 @@ namespace MediaBrowser.Api.UserLibrary
 
             var dtoOptions = GetDtoOptions(request);
 
-            if (request.UserId.HasValue)
+            if (!string.IsNullOrWhiteSpace(request.UserId))
             {
-                var user = UserManager.GetUserById(request.UserId.Value);
+                var user = UserManager.GetUserById(request.UserId);
 
                 return DtoService.GetBaseItemDto(item, dtoOptions, user);
             }

+ 2 - 2
MediaBrowser.Api/UserLibrary/ItemsService.cs

@@ -30,7 +30,7 @@ namespace MediaBrowser.Api.UserLibrary
         /// </summary>
         /// <value>The user id.</value>
         [ApiMember(Name = "UserId", Description = "User Id", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")]
-        public Guid? UserId { get; set; }
+        public string UserId { get; set; }
 
         /// <summary>
         /// Limit results to items containing a specific person
@@ -325,7 +325,7 @@ namespace MediaBrowser.Api.UserLibrary
         private async Task<ItemsResult> GetItems(GetItems request)
         {
             var parentItem = string.IsNullOrEmpty(request.ParentId) ? null : _libraryManager.GetItemById(request.ParentId);
-            var user = request.UserId.HasValue ? _userManager.GetUserById(request.UserId.Value) : null;
+            var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null;
 
             var result = await GetItemsToSerialize(request, user, parentItem).ConfigureAwait(false);
 

+ 3 - 3
MediaBrowser.Api/UserLibrary/MusicGenresService.cs

@@ -37,7 +37,7 @@ namespace MediaBrowser.Api.UserLibrary
         /// </summary>
         /// <value>The user id.</value>
         [ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
-        public Guid? UserId { get; set; }
+        public string UserId { get; set; }
     }
 
     [Authenticated]
@@ -71,9 +71,9 @@ namespace MediaBrowser.Api.UserLibrary
 
             var dtoOptions = GetDtoOptions(request);
 
-            if (request.UserId.HasValue)
+            if (!string.IsNullOrWhiteSpace(request.UserId))
             {
-                var user = UserManager.GetUserById(request.UserId.Value);
+                var user = UserManager.GetUserById(request.UserId);
 
                 return DtoService.GetBaseItemDto(item, dtoOptions, user);
             }

+ 3 - 3
MediaBrowser.Api/UserLibrary/PersonsService.cs

@@ -43,7 +43,7 @@ namespace MediaBrowser.Api.UserLibrary
         /// </summary>
         /// <value>The user id.</value>
         [ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
-        public Guid? UserId { get; set; }
+        public string UserId { get; set; }
     }
 
     /// <summary>
@@ -87,9 +87,9 @@ namespace MediaBrowser.Api.UserLibrary
 
             var dtoOptions = GetDtoOptions(request);
 
-            if (request.UserId.HasValue)
+            if (!string.IsNullOrWhiteSpace(request.UserId))
             {
-                var user = UserManager.GetUserById(request.UserId.Value);
+                var user = UserManager.GetUserById(request.UserId);
 
                 return DtoService.GetBaseItemDto(item, dtoOptions, user);
             }

+ 5 - 5
MediaBrowser.Api/UserLibrary/PlaystateService.cs

@@ -23,7 +23,7 @@ namespace MediaBrowser.Api.UserLibrary
         /// </summary>
         /// <value>The user id.</value>
         [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
-        public Guid UserId { get; set; }
+        public string UserId { get; set; }
 
         [ApiMember(Name = "DatePlayed", Description = "The date the item was played (if any). Format = yyyyMMddHHmmss", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
         public string DatePlayed { get; set; }
@@ -47,7 +47,7 @@ namespace MediaBrowser.Api.UserLibrary
         /// </summary>
         /// <value>The user id.</value>
         [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
-        public Guid UserId { get; set; }
+        public string UserId { get; set; }
 
         /// <summary>
         /// Gets or sets the id.
@@ -90,7 +90,7 @@ namespace MediaBrowser.Api.UserLibrary
         /// </summary>
         /// <value>The user id.</value>
         [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
-        public Guid UserId { get; set; }
+        public string UserId { get; set; }
 
         /// <summary>
         /// Gets or sets the id.
@@ -143,7 +143,7 @@ namespace MediaBrowser.Api.UserLibrary
         /// </summary>
         /// <value>The user id.</value>
         [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
-        public Guid UserId { get; set; }
+        public string UserId { get; set; }
 
         /// <summary>
         /// Gets or sets the id.
@@ -198,7 +198,7 @@ namespace MediaBrowser.Api.UserLibrary
         /// </summary>
         /// <value>The user id.</value>
         [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
-        public Guid UserId { get; set; }
+        public string UserId { get; set; }
 
         /// <summary>
         /// Gets or sets the id.

+ 3 - 3
MediaBrowser.Api/UserLibrary/StudiosService.cs

@@ -38,7 +38,7 @@ namespace MediaBrowser.Api.UserLibrary
         /// </summary>
         /// <value>The user id.</value>
         [ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
-        public Guid? UserId { get; set; }
+        public string UserId { get; set; }
     }
 
     /// <summary>
@@ -75,9 +75,9 @@ namespace MediaBrowser.Api.UserLibrary
 
             var dtoOptions = GetDtoOptions(request);
 
-            if (request.UserId.HasValue)
+            if (!string.IsNullOrWhiteSpace(request.UserId))
             {
-                var user = UserManager.GetUserById(request.UserId.Value);
+                var user = UserManager.GetUserById(request.UserId);
 
                 return DtoService.GetBaseItemDto(item, dtoOptions, user);
             }

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

@@ -5,9 +5,7 @@ using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Net;
 using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Library;
 using MediaBrowser.Model.Querying;
-using MoreLinq;
 using ServiceStack;
 using System;
 using System.Collections.Generic;
@@ -28,7 +26,7 @@ namespace MediaBrowser.Api.UserLibrary
         /// </summary>
         /// <value>The user id.</value>
         [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
-        public Guid UserId { get; set; }
+        public string UserId { get; set; }
 
         /// <summary>
         /// Gets or sets the id.
@@ -49,7 +47,7 @@ namespace MediaBrowser.Api.UserLibrary
         /// </summary>
         /// <value>The user id.</value>
         [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
-        public Guid UserId { get; set; }
+        public string UserId { get; set; }
     }
 
     /// <summary>
@@ -63,7 +61,7 @@ namespace MediaBrowser.Api.UserLibrary
         /// </summary>
         /// <value>The user id.</value>
         [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
-        public Guid UserId { get; set; }
+        public string UserId { get; set; }
 
         /// <summary>
         /// Gets or sets the item id.
@@ -84,7 +82,7 @@ namespace MediaBrowser.Api.UserLibrary
         /// </summary>
         /// <value>The user id.</value>
         [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
-        public Guid UserId { get; set; }
+        public string UserId { get; set; }
 
         /// <summary>
         /// Gets or sets the id.
@@ -105,7 +103,7 @@ namespace MediaBrowser.Api.UserLibrary
         /// </summary>
         /// <value>The user id.</value>
         [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
-        public Guid UserId { get; set; }
+        public string UserId { get; set; }
 
         /// <summary>
         /// Gets or sets the id.
@@ -126,7 +124,7 @@ namespace MediaBrowser.Api.UserLibrary
         /// </summary>
         /// <value>The user id.</value>
         [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
-        public Guid UserId { get; set; }
+        public string UserId { get; set; }
 
         /// <summary>
         /// Gets or sets the id.
@@ -147,7 +145,7 @@ namespace MediaBrowser.Api.UserLibrary
         /// </summary>
         /// <value>The user id.</value>
         [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
-        public Guid UserId { get; set; }
+        public string UserId { get; set; }
 
         /// <summary>
         /// Gets or sets the id.
@@ -175,7 +173,7 @@ namespace MediaBrowser.Api.UserLibrary
         /// </summary>
         /// <value>The user id.</value>
         [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
-        public Guid UserId { get; set; }
+        public string UserId { get; set; }
 
         /// <summary>
         /// Gets or sets the id.
@@ -196,7 +194,7 @@ namespace MediaBrowser.Api.UserLibrary
         /// </summary>
         /// <value>The user id.</value>
         [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
-        public Guid UserId { get; set; }
+        public string UserId { get; set; }
 
         /// <summary>
         /// Gets or sets the id.
@@ -515,7 +513,7 @@ namespace MediaBrowser.Api.UserLibrary
         /// <param name="itemId">The item id.</param>
         /// <param name="isFavorite">if set to <c>true</c> [is favorite].</param>
         /// <returns>Task{UserItemDataDto}.</returns>
-        private async Task<UserItemDataDto> MarkFavorite(Guid userId, string itemId, bool isFavorite)
+        private async Task<UserItemDataDto> MarkFavorite(string userId, string itemId, bool isFavorite)
         {
             var user = _userManager.GetUserById(userId);
 
@@ -563,7 +561,7 @@ namespace MediaBrowser.Api.UserLibrary
         /// <param name="itemId">The item id.</param>
         /// <param name="likes">if set to <c>true</c> [likes].</param>
         /// <returns>Task{UserItemDataDto}.</returns>
-        private async Task<UserItemDataDto> UpdateUserItemRating(Guid userId, string itemId, bool? likes)
+        private async Task<UserItemDataDto> UpdateUserItemRating(string userId, string itemId, bool? likes)
         {
             var user = _userManager.GetUserById(userId);
 

+ 3 - 3
MediaBrowser.Api/UserLibrary/YearsService.cs

@@ -38,7 +38,7 @@ namespace MediaBrowser.Api.UserLibrary
         /// </summary>
         /// <value>The user id.</value>
         [ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
-        public Guid? UserId { get; set; }
+        public string UserId { get; set; }
     }
 
     /// <summary>
@@ -75,9 +75,9 @@ namespace MediaBrowser.Api.UserLibrary
 
             var dtoOptions = GetDtoOptions(request);
 
-            if (request.UserId.HasValue)
+            if (!string.IsNullOrWhiteSpace(request.UserId))
             {
-                var user = UserManager.GetUserById(request.UserId.Value);
+                var user = UserManager.GetUserById(request.UserId);
 
                 return DtoService.GetBaseItemDto(item, dtoOptions, user);
             }

+ 3 - 3
MediaBrowser.Api/VideosService.cs

@@ -19,7 +19,7 @@ namespace MediaBrowser.Api
     public class GetAdditionalParts : IReturn<ItemsResult>
     {
         [ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
-        public Guid? UserId { get; set; }
+        public string UserId { get; set; }
 
         /// <summary>
         /// Gets or sets the id.
@@ -71,10 +71,10 @@ namespace MediaBrowser.Api
         /// <returns>System.Object.</returns>
         public object Get(GetAdditionalParts request)
         {
-            var user = request.UserId.HasValue ? _userManager.GetUserById(request.UserId.Value) : null;
+            var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null;
 
             var item = string.IsNullOrEmpty(request.Id)
-                           ? (request.UserId.HasValue
+                           ? (!string.IsNullOrWhiteSpace(request.UserId)
                                   ? user.RootFolder
                                   : _libraryManager.RootFolder)
                            : _libraryManager.GetItemById(request.Id);

+ 4 - 1
MediaBrowser.Common.Implementations/BaseApplicationPaths.cs

@@ -24,7 +24,10 @@ namespace MediaBrowser.Common.Implementations
         /// <summary>
         /// Gets the path to the system folder
         /// </summary>
-        public string ProgramSystemPath { get { return Path.Combine(ProgramDataPath, "system"); } }
+        public string ProgramSystemPath
+        {
+            get { return Path.GetDirectoryName(ApplicationPath); }
+        }
 
         /// <summary>
         /// The _data directory

+ 31 - 3
MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs

@@ -105,6 +105,23 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
             return client;
         }
 
+        private WebRequest CreateWebRequest(string url)
+        {
+            try
+            {
+                return WebRequest.Create(url);
+            }
+            catch (NotSupportedException)
+            {
+                //Webrequest creation does fail on MONO randomly when using WebRequest.Create
+                //the issue occurs in the GetCreator method here: http://www.oschina.net/code/explore/mono-2.8.1/mcs/class/System/System.Net/WebRequest.cs
+
+                var type = Type.GetType("System.Net.HttpRequestCreator, System, Version=4.0.0.0,Culture=neutral, PublicKeyToken=b77a5c561934e089");
+                var creator = Activator.CreateInstance(type, nonPublic: true) as IWebRequestCreate;
+                return creator.Create(new Uri(url)) as HttpWebRequest;
+            }
+        }
+
         private WebRequest GetRequest(HttpRequestOptions options, string method, bool enableHttpCompression)
         {
             var request = WebRequest.Create(options.Url);
@@ -723,9 +740,20 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
         /// <returns>System.String.</returns>
         private string GetHostFromUrl(string url)
         {
-            var start = url.IndexOf("://", StringComparison.OrdinalIgnoreCase) + 3;
-            var len = url.IndexOf('/', start) - start;
-            return url.Substring(start, len);
+            var index = url.IndexOf("://", StringComparison.OrdinalIgnoreCase);
+
+            if (index != -1)
+            {
+                url = url.Substring(index + 3);
+                var host = url.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries).FirstOrDefault();
+
+                if (!string.IsNullOrWhiteSpace(host))
+                {
+                    return host;
+                }
+            }
+
+            return url;
         }
 
         /// <summary>

+ 0 - 1
MediaBrowser.Controller/Channels/ChannelAudioItem.cs

@@ -15,7 +15,6 @@ namespace MediaBrowser.Controller.Channels
     {
         public string ExternalId { get; set; }
 
-        public string ChannelId { get; set; }
         public string DataVersion { get; set; }
 
         public ChannelItemType ChannelItemType { get; set; }

+ 0 - 1
MediaBrowser.Controller/Channels/ChannelFolderItem.cs

@@ -12,7 +12,6 @@ namespace MediaBrowser.Controller.Channels
     {
         public string ExternalId { get; set; }
 
-        public string ChannelId { get; set; }
         public string DataVersion { get; set; }
 
         public ChannelItemType ChannelItemType { get; set; }

+ 0 - 1
MediaBrowser.Controller/Channels/ChannelVideoItem.cs

@@ -16,7 +16,6 @@ namespace MediaBrowser.Controller.Channels
     {
         public string ExternalId { get; set; }
 
-        public string ChannelId { get; set; }
         public string DataVersion { get; set; }
 
         public ChannelItemType ChannelItemType { get; set; }

+ 9 - 0
MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs

@@ -121,6 +121,15 @@ namespace MediaBrowser.Controller.Entities.Audio
                 .Select(i => i.GetLookupInfo())
                 .ToList();
 
+            var album = id.SongInfos
+                .Select(i => i.Album)
+                .FirstOrDefault(i => !string.IsNullOrWhiteSpace(i));
+
+            if (!string.IsNullOrWhiteSpace(album))
+            {
+                id.Name = album;
+            }
+
             return id;
         }
     }

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

@@ -59,6 +59,12 @@ namespace MediaBrowser.Controller.Entities
 
         public List<ItemImageInfo> ImageInfos { get; set; }
 
+        /// <summary>
+        /// Gets or sets the channel identifier.
+        /// </summary>
+        /// <value>The channel identifier.</value>
+        public string ChannelId { get; set; }
+
         [IgnoreDataMember]
         public virtual bool SupportsAddingToPlaylist
         {

+ 9 - 0
MediaBrowser.Controller/Entities/IHasId.cs

@@ -0,0 +1,9 @@
+using System;
+
+namespace MediaBrowser.Controller.Entities
+{
+    public interface IHasId
+    {
+        Guid Id { get; }
+    }
+}

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

@@ -1,13 +1,12 @@
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Model.Entities;
-using System;
 using System.Collections.Generic;
 using System.IO;
 using System.Threading.Tasks;
 
 namespace MediaBrowser.Controller.Entities
 {
-    public interface IHasImages : IHasProviderIds
+    public interface IHasImages : IHasProviderIds, IHasId
     {
         /// <summary>
         /// Gets the name.
@@ -27,12 +26,6 @@ namespace MediaBrowser.Controller.Entities
         /// <value>The file name without extension.</value>
         string FileNameWithoutExtension { get; }
 
-        /// <summary>
-        /// Gets the identifier.
-        /// </summary>
-        /// <value>The identifier.</value>
-        Guid Id { get; }
-
         /// <summary>
         /// Gets the type of the location.
         /// </summary>

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

@@ -1,17 +1,10 @@
 using MediaBrowser.Model.Dto;
-using System;
 using System.Collections.Generic;
 
 namespace MediaBrowser.Controller.Entities
 {
-    public interface IHasMediaSources
+    public interface IHasMediaSources : IHasId
     {
-        /// <summary>
-        /// Gets the identifier.
-        /// </summary>
-        /// <value>The identifier.</value>
-        Guid Id { get; }
-
         /// <summary>
         /// Gets the media sources.
         /// </summary>

+ 19 - 0
MediaBrowser.Controller/Entities/IHasProgramAttributes.cs

@@ -0,0 +1,19 @@
+using MediaBrowser.Model.LiveTv;
+using System;
+
+namespace MediaBrowser.Controller.Entities
+{
+    public interface IHasProgramAttributes
+    {
+        bool IsMovie { get; set; }
+        bool IsSports { get; set; }
+        bool IsNews { get; set; }
+        bool IsKids { get; set; }
+        bool IsRepeat { get; set; }
+        bool? IsHD { get; set; }
+        bool IsLive { get; set; }
+        bool IsPremiere { get; set; }
+        ProgramAudio? Audio { get; set; }
+        DateTime? OriginalAirDate { get; set; }
+    }
+}

+ 9 - 0
MediaBrowser.Controller/Entities/IHasStartDate.cs

@@ -0,0 +1,9 @@
+using System;
+
+namespace MediaBrowser.Controller.Entities
+{
+    public interface IHasStartDate
+    {
+        DateTime StartDate { get; set; }
+    }
+}

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

@@ -1,19 +1,12 @@
 using MediaBrowser.Model.Dto;
-using System;
 
 namespace MediaBrowser.Controller.Entities
 {
     /// <summary>
     /// Interface IHasUserData
     /// </summary>
-    public interface IHasUserData
+    public interface IHasUserData : IHasId
     {
-        /// <summary>
-        /// Gets or sets the identifier.
-        /// </summary>
-        /// <value>The identifier.</value>
-        Guid Id { get; set; }
-
         /// <summary>
         /// Gets the user data key.
         /// </summary>

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

@@ -73,6 +73,18 @@ namespace MediaBrowser.Controller.Entities
         public string[] Tags { get; set; }
         public string[] OfficialRatings { get; set; }
 
+        public DateTime? MinStartDate { get; set; }
+        public DateTime? MaxStartDate { get; set; }
+        public DateTime? MinEndDate { get; set; }
+        public DateTime? MaxEndDate { get; set; }
+        public bool? IsAiring { get; set; }
+
+        public bool? IsMovie { get; set; }
+        public bool? IsSports { get; set; }
+        public bool? IsKids { get; set; }
+     
+        public string[] ChannelIds { get; set; }
+        
         public InternalItemsQuery()
         {
             Tags = new string[] { };
@@ -89,6 +101,7 @@ namespace MediaBrowser.Controller.Entities
             Years = new int[] { };
             PersonTypes = new string[] { };
             PersonIds = new string[] { };
+            ChannelIds = new string[] { };
         }
     }
 }

+ 8 - 0
MediaBrowser.Controller/Library/ILibraryManager.cs

@@ -10,6 +10,7 @@ using System.Collections.Generic;
 using System.IO;
 using System.Threading;
 using System.Threading.Tasks;
+using MediaBrowser.Model.Querying;
 
 namespace MediaBrowser.Controller.Library
 {
@@ -132,6 +133,13 @@ namespace MediaBrowser.Controller.Library
         /// <returns>BaseItem.</returns>
         BaseItem GetItemById(Guid id);
 
+        /// <summary>
+        /// Gets the items.
+        /// </summary>
+        /// <param name="query">The query.</param>
+        /// <returns>QueryResult&lt;BaseItem&gt;.</returns>
+        QueryResult<BaseItem> GetItems(InternalItemsQuery query);
+
         /// <summary>
         /// Gets the memory item by identifier.
         /// </summary>

+ 8 - 0
MediaBrowser.Controller/Library/IUserDataManager.cs

@@ -29,6 +29,14 @@ namespace MediaBrowser.Controller.Library
         /// <returns>Task.</returns>
         Task SaveUserData(Guid userId, IHasUserData item, UserItemData userData, UserDataSaveReason reason, CancellationToken cancellationToken);
 
+        /// <summary>
+        /// Gets the user data.
+        /// </summary>
+        /// <param name="userId">The user id.</param>
+        /// <param name="key">The key.</param>
+        /// <returns>Task{UserItemData}.</returns>
+        UserItemData GetUserData(string userId, string key);
+
         /// <summary>
         /// Gets the user data.
         /// </summary>

+ 3 - 3
MediaBrowser.Controller/LiveTv/ILiveTvItem.cs

@@ -1,10 +1,10 @@
-using System;
+using MediaBrowser.Controller.Entities;
 
 namespace MediaBrowser.Controller.LiveTv
 {
-    public interface ILiveTvItem
+    public interface ILiveTvItem : IHasId
     {
-        Guid Id { get; }
         string ServiceName { get; set; }
+        string ExternalId { get; set; }
     }
 }

+ 22 - 6
MediaBrowser.Controller/LiveTv/ILiveTvManager.cs

@@ -79,7 +79,7 @@ namespace MediaBrowser.Controller.LiveTv
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <param name="user">The user.</param>
         /// <returns>Task{RecordingInfoDto}.</returns>
-        Task<RecordingInfoDto> GetRecording(string id, DtoOptions options, CancellationToken cancellationToken, User user = null);
+        Task<BaseItemDto> GetRecording(string id, DtoOptions options, CancellationToken cancellationToken, User user = null);
 
         /// <summary>
         /// Gets the channel.
@@ -113,7 +113,7 @@ namespace MediaBrowser.Controller.LiveTv
         /// <param name="options">The options.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>QueryResult{RecordingInfoDto}.</returns>
-        Task<QueryResult<RecordingInfoDto>> GetRecordings(RecordingQuery query, DtoOptions options, CancellationToken cancellationToken);
+        Task<QueryResult<BaseItemDto>> GetRecordings(RecordingQuery query, DtoOptions options, CancellationToken cancellationToken);
 
         /// <summary>
         /// Gets the timers.
@@ -170,7 +170,7 @@ namespace MediaBrowser.Controller.LiveTv
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <param name="user">The user.</param>
         /// <returns>Task{ProgramInfoDto}.</returns>
-        Task<ProgramInfoDto> GetProgram(string id, CancellationToken cancellationToken, User user = null);
+        Task<BaseItemDto> GetProgram(string id, CancellationToken cancellationToken, User user = null);
         
         /// <summary>
         /// Gets the programs.
@@ -178,7 +178,7 @@ namespace MediaBrowser.Controller.LiveTv
         /// <param name="query">The query.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>IEnumerable{ProgramInfo}.</returns>
-        Task<QueryResult<ProgramInfoDto>> GetPrograms(ProgramQuery query, CancellationToken cancellationToken);
+        Task<QueryResult<BaseItemDto>> GetPrograms(ProgramQuery query, CancellationToken cancellationToken);
 
         /// <summary>
         /// Updates the timer.
@@ -218,7 +218,7 @@ namespace MediaBrowser.Controller.LiveTv
         /// <param name="query">The query.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task{QueryResult{RecordingGroupDto}}.</returns>
-        Task<QueryResult<RecordingGroupDto>> GetRecordingGroups(RecordingGroupQuery query, CancellationToken cancellationToken);
+        Task<QueryResult<BaseItemDto>> GetRecordingGroups(RecordingGroupQuery query, CancellationToken cancellationToken);
 
         /// <summary>
         /// Closes the live stream.
@@ -240,7 +240,7 @@ namespace MediaBrowser.Controller.LiveTv
         /// <param name="query">The query.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task{QueryResult{ProgramInfoDto}}.</returns>
-        Task<QueryResult<ProgramInfoDto>> GetRecommendedPrograms(RecommendedProgramQuery query,
+        Task<QueryResult<BaseItemDto>> GetRecommendedPrograms(RecommendedProgramQuery query,
             CancellationToken cancellationToken);
 
         /// <summary>
@@ -321,5 +321,21 @@ namespace MediaBrowser.Controller.LiveTv
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task&lt;IEnumerable&lt;MediaSourceInfo&gt;&gt;.</returns>
         Task<IEnumerable<MediaSourceInfo>> GetChannelMediaSources(string id, CancellationToken cancellationToken);
+
+        /// <summary>
+        /// Adds the information to recording dto.
+        /// </summary>
+        /// <param name="item">The item.</param>
+        /// <param name="dto">The dto.</param>
+        /// <param name="user">The user.</param>
+        void AddInfoToRecordingDto(BaseItem item, BaseItemDto dto, User user = null);
+
+        /// <summary>
+        /// Adds the information to program dto.
+        /// </summary>
+        /// <param name="item">The item.</param>
+        /// <param name="dto">The dto.</param>
+        /// <param name="user">The user.</param>
+        void AddInfoToProgramDto(BaseItem item, BaseItemDto dto, User user = null);
     }
 }

+ 17 - 3
MediaBrowser.Controller/LiveTv/ILiveTvRecording.cs

@@ -2,19 +2,21 @@
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Model.Library;
+using MediaBrowser.Model.LiveTv;
+using System;
 using System.Threading;
 using System.Threading.Tasks;
 
 namespace MediaBrowser.Controller.LiveTv
 {
-    public interface ILiveTvRecording : IHasImages, IHasMediaSources, IHasUserData, ILiveTvItem
+    public interface ILiveTvRecording : IHasImages, IHasMediaSources, IHasUserData, ILiveTvItem, IHasStartDate, IHasProgramAttributes
     {
+        string ChannelId { get; }
+        string ProgramId { get; set; }
         string MediaType { get; }
 
         string Container { get; }
 
-        RecordingInfo RecordingInfo { get; set; }
-
         long? RunTimeTicks { get; set; }
 
         string GetClientTypeName();
@@ -28,5 +30,17 @@ namespace MediaBrowser.Controller.LiveTv
         bool CanDelete();
 
         bool CanDelete(User user);
+
+        string ProviderImagePath { get; set; }
+
+        string ProviderImageUrl { get; set; }
+
+        string ExternalId { get; set; }
+        string EpisodeTitle { get; set; }
+        bool IsSeries { get; set; }
+        string SeriesTimerId { get; set; }
+        RecordingStatus Status { get; set; }
+        DateTime? EndDate { get; set; }
+        ChannelType ChannelType { get; set; }
     }
 }

+ 26 - 5
MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs

@@ -3,7 +3,9 @@ using MediaBrowser.Controller.Entities.Audio;
 using MediaBrowser.Model.Configuration;
 using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.LiveTv;
 using MediaBrowser.Model.Users;
+using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Runtime.Serialization;
@@ -12,6 +14,27 @@ namespace MediaBrowser.Controller.LiveTv
 {
     public class LiveTvAudioRecording : Audio, ILiveTvRecording
     {
+        public string ExternalId { get; set; }
+        public string ProviderImagePath { get; set; }
+        public string ProviderImageUrl { get; set; }
+        public string EpisodeTitle { get; set; }
+        public bool IsSeries { get; set; }
+        public string SeriesTimerId { get; set; }
+        public DateTime StartDate { get; set; }
+        public RecordingStatus Status { get; set; }
+        public bool IsSports { get; set; }
+        public bool IsNews { get; set; }
+        public bool IsKids { get; set; }
+        public bool IsRepeat { get; set; }
+        public bool IsMovie { get; set; }
+        public bool? IsHD { get; set; }
+        public bool IsLive { get; set; }
+        public bool IsPremiere { get; set; }
+        public ChannelType ChannelType { get; set; }
+        public string ProgramId { get; set; }
+        public ProgramAudio? Audio { get; set; }
+        public DateTime? OriginalAirDate { get; set; }
+
         /// <summary>
         /// Gets the user data key.
         /// </summary>
@@ -20,16 +43,14 @@ namespace MediaBrowser.Controller.LiveTv
         {
             var name = GetClientTypeName();
 
-            if (!string.IsNullOrEmpty(RecordingInfo.ProgramId))
+            if (!string.IsNullOrEmpty(ProgramId))
             {
-                return name + "-" + RecordingInfo.ProgramId;
+                return name + "-" + ProgramId;
             }
 
-            return name + "-" + RecordingInfo.Name + (RecordingInfo.EpisodeTitle ?? string.Empty);
+            return name + "-" + Name + (EpisodeTitle ?? string.Empty);
         }
 
-        public RecordingInfo RecordingInfo { get; set; }
-
         public string ServiceName { get; set; }
 
         /// <summary>

+ 1 - 19
MediaBrowser.Controller/LiveTv/LiveTvProgram.cs

@@ -1,5 +1,4 @@
 using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Model.Configuration;
 using MediaBrowser.Model.LiveTv;
@@ -7,12 +6,10 @@ using MediaBrowser.Model.Users;
 using System;
 using System.Linq;
 using System.Runtime.Serialization;
-using System.Threading;
-using System.Threading.Tasks;
 
 namespace MediaBrowser.Controller.LiveTv
 {
-    public class LiveTvProgram : BaseItem, ILiveTvItem, IHasLookupInfo<LiveTvProgramLookupInfo>
+    public class LiveTvProgram : BaseItem, ILiveTvItem, IHasLookupInfo<LiveTvProgramLookupInfo>, IHasStartDate, IHasProgramAttributes
     {
         /// <summary>
         /// Gets the user data key.
@@ -28,12 +25,6 @@ namespace MediaBrowser.Controller.LiveTv
         /// </summary>
         public string ExternalId { get; set; }
 
-        /// <summary>
-        /// Gets or sets the channel identifier.
-        /// </summary>
-        /// <value>The channel identifier.</value>
-        public string ExternalChannelId { get; set; }
-
         /// <summary>
         /// Gets or sets the original air date.
         /// </summary>
@@ -204,15 +195,6 @@ namespace MediaBrowser.Controller.LiveTv
             return "Program";
         }
 
-        public override Task UpdateToRepository(ItemUpdateType updateReason, CancellationToken cancellationToken)
-        {
-            DateLastSaved = DateTime.UtcNow;
-            
-            // Avoid library manager and keep out of in-memory cache
-            // Not great that this class has to know about that, but we'll improve that later.
-            return ItemRepository.SaveItem(this, cancellationToken);
-        }
-
         protected override bool GetBlockUnratedValue(UserPolicy config)
         {
             return config.BlockUnratedItems.Contains(UnratedItem.LiveTvProgram);

+ 26 - 5
MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs

@@ -2,7 +2,9 @@
 using MediaBrowser.Model.Configuration;
 using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.LiveTv;
 using MediaBrowser.Model.Users;
+using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Runtime.Serialization;
@@ -11,6 +13,27 @@ namespace MediaBrowser.Controller.LiveTv
 {
     public class LiveTvVideoRecording : Video, ILiveTvRecording
     {
+        public string ExternalId { get; set; }
+        public string ProviderImagePath { get; set; }
+        public string ProviderImageUrl { get; set; }
+        public string EpisodeTitle { get; set; }
+        public bool IsSeries { get; set; }
+        public string SeriesTimerId { get; set; }
+        public DateTime StartDate { get; set; }
+        public RecordingStatus Status { get; set; }
+        public bool IsSports { get; set; }
+        public bool IsNews { get; set; }
+        public bool IsKids { get; set; }
+        public bool IsRepeat { get; set; }
+        public bool IsMovie { get; set; }
+        public bool? IsHD { get; set; }
+        public bool IsLive { get; set; }
+        public bool IsPremiere { get; set; }
+        public ChannelType ChannelType { get; set; }
+        public string ProgramId { get; set; }
+        public ProgramAudio? Audio { get; set; }
+        public DateTime? OriginalAirDate { get; set; }
+
         /// <summary>
         /// Gets the user data key.
         /// </summary>
@@ -19,16 +42,14 @@ namespace MediaBrowser.Controller.LiveTv
         {
             var name = GetClientTypeName();
 
-            if (!string.IsNullOrEmpty(RecordingInfo.ProgramId))
+            if (!string.IsNullOrEmpty(ProgramId))
             {
-                return name + "-" + RecordingInfo.ProgramId;
+                return name + "-" + ProgramId;
             }
 
-            return name + "-" + RecordingInfo.Name + (RecordingInfo.EpisodeTitle ?? string.Empty);
+            return name + "-" + Name + (EpisodeTitle ?? string.Empty);
         }
 
-        public RecordingInfo RecordingInfo { get; set; }
-
         public string ServiceName { get; set; }
 
         [IgnoreDataMember]

+ 4 - 0
MediaBrowser.Controller/MediaBrowser.Controller.csproj

@@ -142,6 +142,7 @@
     <Compile Include="Entities\IHasBudget.cs" />
     <Compile Include="Entities\IHasCriticRating.cs" />
     <Compile Include="Entities\IHasDisplayOrder.cs" />
+    <Compile Include="Entities\IHasId.cs" />
     <Compile Include="Entities\IHasImages.cs" />
     <Compile Include="Entities\IHasKeywords.cs" />
     <Compile Include="Entities\IHasMediaSources.cs" />
@@ -149,10 +150,12 @@
     <Compile Include="Entities\IHasOriginalTitle.cs" />
     <Compile Include="Entities\IHasPreferredMetadataLanguage.cs" />
     <Compile Include="Entities\IHasProductionLocations.cs" />
+    <Compile Include="Entities\IHasProgramAttributes.cs" />
     <Compile Include="Entities\IHasScreenshots.cs" />
     <Compile Include="Entities\IHasSeries.cs" />
     <Compile Include="Entities\IHasShortOverview.cs" />
     <Compile Include="Entities\IHasSpecialFeatures.cs" />
+    <Compile Include="Entities\IHasStartDate.cs" />
     <Compile Include="Entities\IHasTaglines.cs" />
     <Compile Include="Entities\IHasTags.cs" />
     <Compile Include="Entities\IHasThemeMedia.cs" />
@@ -408,6 +411,7 @@
     <Compile Include="Sync\ISyncProvider.cs" />
     <Compile Include="Sync\ISyncRepository.cs" />
     <Compile Include="Sync\SyncedFileInfo.cs" />
+    <Compile Include="Sync\SyncedItemProgress.cs" />
     <Compile Include="Themes\IAppThemeManager.cs" />
     <Compile Include="Themes\InternalThemeImage.cs" />
     <Compile Include="TV\ITVSeriesManager.cs" />

+ 3 - 3
MediaBrowser.Controller/Persistence/IDisplayPreferencesRepository.cs

@@ -25,9 +25,9 @@ namespace MediaBrowser.Controller.Persistence
         /// <param name="client">The client.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task.</returns>
-        Task SaveDisplayPreferences(DisplayPreferences displayPreferences, Guid userId, string client,
+        Task SaveDisplayPreferences(DisplayPreferences displayPreferences, string userId, string client,
                                     CancellationToken cancellationToken);
-
+        
         /// <summary>
         /// Saves all display preferences for a user
         /// </summary>
@@ -44,7 +44,7 @@ namespace MediaBrowser.Controller.Persistence
         /// <param name="userId">The user id.</param>
         /// <param name="client">The client.</param>
         /// <returns>Task{DisplayPreferences}.</returns>
-        DisplayPreferences GetDisplayPreferences(string displayPreferencesId, Guid userId, string client);
+        DisplayPreferences GetDisplayPreferences(string displayPreferencesId, string userId, string client);
 
         /// <summary>
         /// Gets all display preferences for the given user.

+ 18 - 11
MediaBrowser.Controller/Persistence/IItemRepository.cs

@@ -4,6 +4,7 @@ using System;
 using System.Collections.Generic;
 using System.Threading;
 using System.Threading.Tasks;
+using MediaBrowser.Model.Querying;
 
 namespace MediaBrowser.Controller.Persistence
 {
@@ -102,13 +103,6 @@ namespace MediaBrowser.Controller.Persistence
         /// <returns>IEnumerable{ChildDefinition}.</returns>
         IEnumerable<Guid> GetChildren(Guid parentId);
 
-        /// <summary>
-        /// Gets the type of the items of.
-        /// </summary>
-        /// <param name="type">The type.</param>
-        /// <returns>IEnumerable{Guid}.</returns>
-        IEnumerable<Guid> GetItemIdsOfType(Type type);
-
         /// <summary>
         /// Saves the children.
         /// </summary>
@@ -135,11 +129,24 @@ namespace MediaBrowser.Controller.Persistence
         Task SaveMediaStreams(Guid id, IEnumerable<MediaStream> streams, CancellationToken cancellationToken);
 
         /// <summary>
-        /// Gets the type of the items of.
+        /// Gets the item ids.
         /// </summary>
-        /// <param name="type">The type.</param>
-        /// <returns>IEnumerable&lt;BaseItem&gt;.</returns>
-        IEnumerable<BaseItem> GetItemsOfType(Type type);
+        /// <param name="query">The query.</param>
+        /// <returns>IEnumerable&lt;Guid&gt;.</returns>
+        QueryResult<Guid> GetItemIds(InternalItemsQuery query);
+        /// <summary>
+        /// Gets the items.
+        /// </summary>
+        /// <param name="query">The query.</param>
+        /// <returns>QueryResult&lt;BaseItem&gt;.</returns>
+        QueryResult<BaseItem> GetItems(InternalItemsQuery query);
+
+        /// <summary>
+        /// Gets the item ids list.
+        /// </summary>
+        /// <param name="query">The query.</param>
+        /// <returns>List&lt;Guid&gt;.</returns>
+        List<Guid> GetItemIdsList(InternalItemsQuery query);
     }
 }
 

+ 2 - 2
MediaBrowser.Controller/Session/ISessionManager.cs

@@ -220,14 +220,14 @@ namespace MediaBrowser.Controller.Session
         /// </summary>
         /// <param name="sessionId">The session identifier.</param>
         /// <param name="userId">The user identifier.</param>
-        void AddAdditionalUser(string sessionId, Guid userId);
+        void AddAdditionalUser(string sessionId, string userId);
 
         /// <summary>
         /// Removes the additional user.
         /// </summary>
         /// <param name="sessionId">The session identifier.</param>
         /// <param name="userId">The user identifier.</param>
-        void RemoveAdditionalUser(string sessionId, Guid userId);
+        void RemoveAdditionalUser(string sessionId, string userId);
 
         /// <summary>
         /// Reports the now viewing item.

+ 8 - 0
MediaBrowser.Controller/Subtitles/ISubtitleProvider.cs

@@ -1,4 +1,5 @@
 using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.Providers;
 using System.Collections.Generic;
 using System.Threading;
@@ -35,5 +36,12 @@ namespace MediaBrowser.Controller.Subtitles
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task{SubtitleResponse}.</returns>
         Task<SubtitleResponse> GetSubtitles(string id, CancellationToken cancellationToken);
+
+        /// <summary>
+        /// Gets the supported languages.
+        /// </summary>
+        /// <param name="cancellationToken">The cancellation token.</param>
+        /// <returns>Task&lt;IEnumerable&lt;NameIdPair&gt;&gt;.</returns>
+        Task<IEnumerable<NameIdPair>> GetSupportedLanguages(CancellationToken cancellationToken);
     }
 }

+ 1 - 1
MediaBrowser.Controller/Sync/ISyncManager.cs

@@ -152,7 +152,7 @@ namespace MediaBrowser.Controller.Sync
         /// </summary>
         /// <param name="query">The query.</param>
         /// <returns>QueryResult&lt;System.String&gt;.</returns>
-        QueryResult<string> GetLibraryItemIds(SyncJobItemQuery query);
+        QueryResult<SyncedItemProgress> GetSyncedItemProgresses(SyncJobItemQuery query);
 
         /// <summary>
         /// Reports the synchronize job item transfer beginning.

+ 1 - 1
MediaBrowser.Controller/Sync/ISyncRepository.cs

@@ -74,6 +74,6 @@ namespace MediaBrowser.Controller.Sync
         /// </summary>
         /// <param name="query">The query.</param>
         /// <returns>QueryResult&lt;System.String&gt;.</returns>
-        QueryResult<string> GetLibraryItemIds(SyncJobItemQuery query);
+        QueryResult<SyncedItemProgress> GetSyncedItemProgresses(SyncJobItemQuery query);
     }
 }

+ 10 - 0
MediaBrowser.Controller/Sync/SyncedItemProgress.cs

@@ -0,0 +1,10 @@
+using MediaBrowser.Model.Sync;
+
+namespace MediaBrowser.Controller.Sync
+{
+    public class SyncedItemProgress
+    {
+        public string ItemId { get; set; }
+        public SyncJobItemStatus Status { get; set; }
+    }
+}

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

@@ -42,7 +42,8 @@ namespace MediaBrowser.Dlna.Profiles
                    Container = "ts",
                    AudioCodec = "ac3",
                    VideoCodec = "h264",
-                   Type = DlnaProfileType.Video
+                   Type = DlnaProfileType.Video,
+                   EstimateContentLength = true
                },
                new TranscodingProfile
                {

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

@@ -54,7 +54,7 @@ namespace MediaBrowser.Dlna.Profiles
                 {
                     Container = "ts",
                     VideoCodec = "h264",
-                    AudioCodec = "ac3,aac",
+                    AudioCodec = "aac",
                     Type = DlnaProfileType.Video,
                     EnableMpegtsM2TsMode = true
                 },

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

@@ -51,7 +51,7 @@ namespace MediaBrowser.Dlna.Profiles
                 {
                     Container = "ts",
                     VideoCodec = "h264",
-                    AudioCodec = "ac3,aac",
+                    AudioCodec = "aac",
                     Type = DlnaProfileType.Video,
                     EnableMpegtsM2TsMode = true
                 },

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

@@ -51,7 +51,7 @@ namespace MediaBrowser.Dlna.Profiles
                 {
                     Container = "ts",
                     VideoCodec = "h264",
-                    AudioCodec = "ac3,aac",
+                    AudioCodec = "aac",
                     Type = DlnaProfileType.Video,
                     EnableMpegtsM2TsMode = true
                 },

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

@@ -50,7 +50,7 @@ namespace MediaBrowser.Dlna.Profiles
                 {
                     Container = "ts",
                     VideoCodec = "h264",
-                    AudioCodec = "ac3,aac",
+                    AudioCodec = "aac",
                     Type = DlnaProfileType.Video,
                     EnableMpegtsM2TsMode = true
                 },

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

@@ -52,7 +52,7 @@
   </DirectPlayProfiles>
   <TranscodingProfiles>
     <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" />
-    <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" />
+    <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3" estimateContentLength="true" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" />
     <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" />
   </TranscodingProfiles>
   <ContainerProfiles>

+ 1 - 1
MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2010).xml

@@ -47,7 +47,7 @@
   </DirectPlayProfiles>
   <TranscodingProfiles>
     <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" />
-    <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3,aac" estimateContentLength="false" enableMpegtsM2TsMode="true" transcodeSeekInfo="Auto" context="Streaming" />
+    <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="true" transcodeSeekInfo="Auto" context="Streaming" />
     <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" />
   </TranscodingProfiles>
   <ContainerProfiles>

+ 1 - 1
MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2011).xml

@@ -50,7 +50,7 @@
   </DirectPlayProfiles>
   <TranscodingProfiles>
     <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" />
-    <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3,aac" estimateContentLength="false" enableMpegtsM2TsMode="true" transcodeSeekInfo="Auto" context="Streaming" />
+    <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="true" transcodeSeekInfo="Auto" context="Streaming" />
     <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" />
   </TranscodingProfiles>
   <ContainerProfiles>

+ 1 - 1
MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2012).xml

@@ -52,7 +52,7 @@
   </DirectPlayProfiles>
   <TranscodingProfiles>
     <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" />
-    <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3,aac" estimateContentLength="false" enableMpegtsM2TsMode="true" transcodeSeekInfo="Auto" context="Streaming" />
+    <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="true" transcodeSeekInfo="Auto" context="Streaming" />
     <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" />
   </TranscodingProfiles>
   <ContainerProfiles>

+ 1 - 1
MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2013).xml

@@ -57,7 +57,7 @@
   </DirectPlayProfiles>
   <TranscodingProfiles>
     <TranscodingProfile container="mp3" type="Audio" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" />
-    <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3,aac" estimateContentLength="false" enableMpegtsM2TsMode="true" transcodeSeekInfo="Auto" context="Streaming" />
+    <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="true" transcodeSeekInfo="Auto" context="Streaming" />
     <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" context="Streaming" />
   </TranscodingProfiles>
   <ContainerProfiles>

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

@@ -58,8 +58,6 @@
     <Compile Include="Parsers\SeasonXmlParser.cs" />
     <Compile Include="Parsers\SeriesXmlParser.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
-    <Compile Include="Providers\AlbumXmlProvider.cs" />
-    <Compile Include="Providers\ArtistXmlProvider.cs" />
     <Compile Include="Providers\BoxSetXmlProvider.cs" />
     <Compile Include="Providers\EpisodeXmlProvider.cs" />
     <Compile Include="Providers\FolderXmlProvider.cs" />
@@ -80,7 +78,6 @@
     <Compile Include="Savers\MovieXmlSaver.cs" />
     <Compile Include="Savers\PersonXmlSaver.cs" />
     <Compile Include="Savers\PlaylistXmlSaver.cs" />
-    <Compile Include="Savers\SeasonXmlSaver.cs" />
     <Compile Include="Savers\SeriesXmlSaver.cs" />
     <Compile Include="Savers\XmlSaverHelpers.cs" />
   </ItemGroup>

+ 0 - 30
MediaBrowser.LocalMetadata/Providers/AlbumXmlProvider.cs

@@ -1,30 +0,0 @@
-using System.IO;
-using System.Threading;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Logging;
-
-namespace MediaBrowser.LocalMetadata.Providers
-{
-    public class AlbumXmlProvider : BaseXmlProvider<MusicAlbum>
-    {
-        private readonly ILogger _logger;
-
-        public AlbumXmlProvider(IFileSystem fileSystem, ILogger logger)
-            : base(fileSystem)
-        {
-            _logger = logger;
-        }
-
-        protected override void Fetch(LocalMetadataResult<MusicAlbum> result, string path, CancellationToken cancellationToken)
-        {
-            new BaseItemXmlParser<MusicAlbum>(_logger).Fetch(result.Item, path, cancellationToken);
-        }
-
-        protected override FileSystemInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService)
-        {
-            return directoryService.GetFile(Path.Combine(info.Path, "album.xml"));
-        }
-    }
-}

+ 0 - 30
MediaBrowser.LocalMetadata/Providers/ArtistXmlProvider.cs

@@ -1,30 +0,0 @@
-using System.IO;
-using System.Threading;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Logging;
-
-namespace MediaBrowser.LocalMetadata.Providers
-{
-    class ArtistXmlProvider : BaseXmlProvider<MusicArtist>
-    {
-        private readonly ILogger _logger;
-
-        public ArtistXmlProvider(IFileSystem fileSystem, ILogger logger)
-            : base(fileSystem)
-        {
-            _logger = logger;
-        }
-
-        protected override void Fetch(LocalMetadataResult<MusicArtist> result, string path, CancellationToken cancellationToken)
-        {
-            new BaseItemXmlParser<MusicArtist>(_logger).Fetch(result.Item, path, cancellationToken);
-        }
-
-        protected override FileSystemInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService)
-        {
-            return directoryService.GetFile(Path.Combine(info.Path, "artist.xml"));
-        }
-    }
-}

+ 0 - 95
MediaBrowser.LocalMetadata/Savers/SeasonXmlSaver.cs

@@ -1,95 +0,0 @@
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.Library;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.Security;
-using System.Text;
-using System.Threading;
-
-namespace MediaBrowser.LocalMetadata.Savers
-{
-    public class SeasonXmlSaver : IMetadataFileSaver
-    {
-        public string Name
-        {
-            get
-            {
-                return XmlProviderUtils.Name;
-            }
-        }
-
-        private readonly IServerConfigurationManager _config;
-
-        public SeasonXmlSaver(IServerConfigurationManager config)
-        {
-            _config = config;
-        }
-        
-        /// <summary>
-        /// Determines whether [is enabled for] [the specified item].
-        /// </summary>
-        /// <param name="item">The item.</param>
-        /// <param name="updateType">Type of the update.</param>
-        /// <returns><c>true</c> if [is enabled for] [the specified item]; otherwise, <c>false</c>.</returns>
-        public bool IsEnabledFor(IHasMetadata item, ItemUpdateType updateType)
-        {
-            if (!item.SupportsLocalMetadata)
-            {
-                return false;
-            }
-
-            if (!(item is Season))
-            {
-                return false;
-            }
-
-            return updateType >= ItemUpdateType.MetadataDownload || (updateType >= ItemUpdateType.MetadataImport && File.Exists(GetSavePath(item)));
-        }
-
-        private readonly CultureInfo _usCulture = new CultureInfo("en-US");
-
-        /// <summary>
-        /// Saves the specified item.
-        /// </summary>
-        /// <param name="item">The item.</param>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <returns>Task.</returns>
-        public void Save(IHasMetadata item, CancellationToken cancellationToken)
-        {
-            var builder = new StringBuilder();
-
-            builder.Append("<Item>");
-
-            var season = (Season)item;
-
-            if (season.IndexNumber.HasValue)
-            {
-                builder.Append("<SeasonNumber>" + SecurityElement.Escape(season.IndexNumber.Value.ToString(_usCulture)) + "</SeasonNumber>");
-            }
-            
-            XmlSaverHelpers.AddCommonNodes((Season)item, builder);
-
-            builder.Append("</Item>");
-
-            var xmlFilePath = GetSavePath(item);
-
-            XmlSaverHelpers.Save(builder, xmlFilePath, new List<string>
-            {
-                "SeasonNumber"
-            }, _config);
-        }
-
-        /// <summary>
-        /// Gets the save path.
-        /// </summary>
-        /// <param name="item">The item.</param>
-        /// <returns>System.String.</returns>
-        public string GetSavePath(IHasMetadata item)
-        {
-            return Path.Combine(item.Path, "season.xml");
-        }
-    }
-}

+ 2 - 2
MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs

@@ -902,13 +902,13 @@ namespace MediaBrowser.MediaEncoding.Encoder
 
                 // TODO: Perhaps also use original_size=1920x800 ??
                 return string.Format("subtitles=filename='{0}'{1},setpts=PTS -{2}/TB",
-                    subtitlePath.Replace('\\', '/').Replace(":/", "\\:/"),
+                    subtitlePath.Replace('\\', '/').Replace("'", "\\'").Replace(":/", "\\:/"),
                     charsetParam,
                     seconds.ToString(UsCulture));
             }
 
             return string.Format("subtitles='{0}:si={1}',setpts=PTS -{2}/TB",
-                state.MediaPath.Replace('\\', '/').Replace(":/", "\\:/"),
+                state.MediaPath.Replace('\\', '/').Replace("'", "\\'").Replace(":/", "\\:/"),
                 state.InternalSubtitleStreamOffset.ToString(UsCulture),
                 seconds.ToString(UsCulture));
         }

+ 24 - 1
MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs

@@ -56,10 +56,33 @@ namespace MediaBrowser.MediaEncoding.Probing
             {
                 SetAudioRuntimeTicks(data, info);
 
+                var tags = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
+
+                // tags are normally located under data.format, but we've seen some cases with ogg where they're part of the audio stream
+                // so let's create a combined list of both
+
+                if (data.streams != null)
+                {
+                    var audioStream = data.streams.FirstOrDefault(i => string.Equals(i.codec_type, "audio", StringComparison.OrdinalIgnoreCase));
+
+                    if (audioStream != null && audioStream.tags != null)
+                    {
+                        foreach (var pair in audioStream.tags)
+                        {
+                            tags[pair.Key] = pair.Value;
+                        }
+                    }
+                }
+
                 if (data.format != null && data.format.tags != null)
                 {
-                    SetAudioInfoFromTags(info, data.format.tags);
+                    foreach (var pair in data.format.tags)
+                    {
+                        tags[pair.Key] = pair.Value;
+                    }
                 }
+
+                SetAudioInfoFromTags(info, tags);
             }
             else
             {

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

@@ -89,6 +89,9 @@
     <Compile Include="..\MediaBrowser.Model\ApiClient\ConnectionState.cs">
       <Link>ApiClient\ConnectionState.cs</Link>
     </Compile>
+    <Compile Include="..\MediaBrowser.Model\ApiClient\ConnectSignupResponse.cs">
+      <Link>ApiClient\ConnectSignupResponse.cs</Link>
+    </Compile>
     <Compile Include="..\MediaBrowser.Model\ApiClient\GeneralCommandEventArgs.cs">
       <Link>ApiClient\GeneralCommandEventArgs.cs</Link>
     </Compile>
@@ -179,6 +182,9 @@
     <Compile Include="..\MediaBrowser.Model\Configuration\AccessSchedule.cs">
       <Link>Configuration\AccessSchedule.cs</Link>
     </Compile>
+    <Compile Include="..\MediaBrowser.Model\Configuration\AutoOnOff.cs">
+      <Link>Configuration\AutoOnOff.cs</Link>
+    </Compile>
     <Compile Include="..\MediaBrowser.Model\Configuration\BaseApplicationConfiguration.cs">
       <Link>Configuration\BaseApplicationConfiguration.cs</Link>
     </Compile>
@@ -743,24 +749,15 @@
     <Compile Include="..\MediaBrowser.Model\LiveTv\ProgramAudio.cs">
       <Link>LiveTv\ProgramAudio.cs</Link>
     </Compile>
-    <Compile Include="..\MediaBrowser.Model\LiveTv\ProgramInfoDto.cs">
-      <Link>LiveTv\ProgramInfoDto.cs</Link>
-    </Compile>
     <Compile Include="..\MediaBrowser.Model\LiveTv\ProgramQuery.cs">
       <Link>LiveTv\ProgramQuery.cs</Link>
     </Compile>
     <Compile Include="..\MediaBrowser.Model\LiveTv\RecommendedProgramQuery.cs">
       <Link>LiveTv\RecommendedProgramQuery.cs</Link>
     </Compile>
-    <Compile Include="..\MediaBrowser.Model\LiveTv\RecordingGroupDto.cs">
-      <Link>LiveTv\RecordingGroupDto.cs</Link>
-    </Compile>
     <Compile Include="..\MediaBrowser.Model\LiveTv\RecordingGroupQuery.cs">
       <Link>LiveTv\RecordingGroupQuery.cs</Link>
     </Compile>
-    <Compile Include="..\MediaBrowser.Model\LiveTv\RecordingInfoDto.cs">
-      <Link>LiveTv\RecordingInfoDto.cs</Link>
-    </Compile>
     <Compile Include="..\MediaBrowser.Model\LiveTv\RecordingQuery.cs">
       <Link>LiveTv\RecordingQuery.cs</Link>
     </Compile>

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

@@ -66,6 +66,9 @@
     <Compile Include="..\MediaBrowser.Model\ApiClient\ConnectionState.cs">
       <Link>ApiClient\ConnectionState.cs</Link>
     </Compile>
+    <Compile Include="..\MediaBrowser.Model\ApiClient\ConnectSignupResponse.cs">
+      <Link>ApiClient\ConnectSignupResponse.cs</Link>
+    </Compile>
     <Compile Include="..\MediaBrowser.Model\ApiClient\GeneralCommandEventArgs.cs">
       <Link>ApiClient\GeneralCommandEventArgs.cs</Link>
     </Compile>
@@ -144,6 +147,9 @@
     <Compile Include="..\MediaBrowser.Model\Configuration\AccessSchedule.cs">
       <Link>Configuration\AccessSchedule.cs</Link>
     </Compile>
+    <Compile Include="..\MediaBrowser.Model\Configuration\AutoOnOff.cs">
+      <Link>Configuration\AutoOnOff.cs</Link>
+    </Compile>
     <Compile Include="..\MediaBrowser.Model\Configuration\BaseApplicationConfiguration.cs">
       <Link>Configuration\BaseApplicationConfiguration.cs</Link>
     </Compile>
@@ -699,24 +705,15 @@
     <Compile Include="..\MediaBrowser.Model\LiveTv\ProgramAudio.cs">
       <Link>LiveTv\ProgramAudio.cs</Link>
     </Compile>
-    <Compile Include="..\MediaBrowser.Model\LiveTv\ProgramInfoDto.cs">
-      <Link>LiveTv\ProgramInfoDto.cs</Link>
-    </Compile>
     <Compile Include="..\MediaBrowser.Model\LiveTv\ProgramQuery.cs">
       <Link>LiveTv\ProgramQuery.cs</Link>
     </Compile>
     <Compile Include="..\MediaBrowser.Model\LiveTv\RecommendedProgramQuery.cs">
       <Link>LiveTv\RecommendedProgramQuery.cs</Link>
     </Compile>
-    <Compile Include="..\MediaBrowser.Model\LiveTv\RecordingGroupDto.cs">
-      <Link>LiveTv\RecordingGroupDto.cs</Link>
-    </Compile>
     <Compile Include="..\MediaBrowser.Model\LiveTv\RecordingGroupQuery.cs">
       <Link>LiveTv\RecordingGroupQuery.cs</Link>
     </Compile>
-    <Compile Include="..\MediaBrowser.Model\LiveTv\RecordingInfoDto.cs">
-      <Link>LiveTv\RecordingInfoDto.cs</Link>
-    </Compile>
     <Compile Include="..\MediaBrowser.Model\LiveTv\RecordingQuery.cs">
       <Link>LiveTv\RecordingQuery.cs</Link>
     </Compile>

+ 10 - 0
MediaBrowser.Model/ApiClient/ConnectSignupResponse.cs

@@ -0,0 +1,10 @@
+namespace MediaBrowser.Model.ApiClient
+{
+    public enum ConnectSignupResponse
+    {
+        Failure,
+        Success,
+        EmailInUse,
+        UsernameInUser
+    }
+}

+ 7 - 23
MediaBrowser.Model/ApiClient/IApiClient.cs

@@ -929,22 +929,6 @@ namespace MediaBrowser.Model.ApiClient
         /// <returns>System.String.</returns>
         string GetImageUrl(ChannelInfoDto item, ImageOptions options);
 
-        /// <summary>
-        /// Gets the image URL.
-        /// </summary>
-        /// <param name="item">The item.</param>
-        /// <param name="options">The options.</param>
-        /// <returns>System.String.</returns>
-        string GetImageUrl(RecordingInfoDto item, ImageOptions options);
-
-        /// <summary>
-        /// Gets the image URL.
-        /// </summary>
-        /// <param name="item">The item.</param>
-        /// <param name="options">The options.</param>
-        /// <returns>System.String.</returns>
-        string GetImageUrl(ProgramInfoDto item, ImageOptions options);
-
         /// <summary>
         /// Gets the subtitle URL.
         /// </summary>
@@ -1077,7 +1061,7 @@ namespace MediaBrowser.Model.ApiClient
         /// <param name="query">The query.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task{QueryResult{RecordingInfoDto}}.</returns>
-        Task<QueryResult<RecordingInfoDto>> GetLiveTvRecordingsAsync(RecordingQuery query, CancellationToken cancellationToken = default(CancellationToken));
+        Task<QueryResult<BaseItemDto>> GetLiveTvRecordingsAsync(RecordingQuery query, CancellationToken cancellationToken = default(CancellationToken));
 
         /// <summary>
         /// Gets the live tv recording asynchronous.
@@ -1086,7 +1070,7 @@ namespace MediaBrowser.Model.ApiClient
         /// <param name="userId">The user identifier.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task{RecordingInfoDto}.</returns>
-        Task<RecordingInfoDto> GetLiveTvRecordingAsync(string id, string userId, CancellationToken cancellationToken = default(CancellationToken));
+        Task<BaseItemDto> GetLiveTvRecordingAsync(string id, string userId, CancellationToken cancellationToken = default(CancellationToken));
 
         /// <summary>
         /// Gets the live tv recording groups asynchronous.
@@ -1094,7 +1078,7 @@ namespace MediaBrowser.Model.ApiClient
         /// <param name="query">The query.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task{QueryResult{RecordingGroupDto}}.</returns>
-        Task<QueryResult<RecordingGroupDto>> GetLiveTvRecordingGroupsAsync(RecordingGroupQuery query, CancellationToken cancellationToken = default(CancellationToken));
+        Task<QueryResult<BaseItemDto>> GetLiveTvRecordingGroupsAsync(RecordingGroupQuery query, CancellationToken cancellationToken = default(CancellationToken));
 
         /// <summary>
         /// Gets the live tv recording group asynchronous.
@@ -1103,7 +1087,7 @@ namespace MediaBrowser.Model.ApiClient
         /// <param name="userId">The user identifier.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task{RecordingGroupDto}.</returns>
-        Task<RecordingGroupDto> GetLiveTvRecordingGroupAsync(string id, string userId, CancellationToken cancellationToken = default(CancellationToken));
+        Task<BaseItemDto> GetLiveTvRecordingGroupAsync(string id, string userId, CancellationToken cancellationToken = default(CancellationToken));
 
         /// <summary>
         /// Gets the live tv timers asynchronous.
@@ -1119,7 +1103,7 @@ namespace MediaBrowser.Model.ApiClient
         /// <param name="query">The query.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task{QueryResult{ProgramInfoDto}}.</returns>
-        Task<QueryResult<ProgramInfoDto>> GetLiveTvProgramsAsync(ProgramQuery query, CancellationToken cancellationToken = default(CancellationToken));
+        Task<QueryResult<BaseItemDto>> GetLiveTvProgramsAsync(ProgramQuery query, CancellationToken cancellationToken = default(CancellationToken));
 
         /// <summary>
         /// Gets the live tv program asynchronous.
@@ -1128,7 +1112,7 @@ namespace MediaBrowser.Model.ApiClient
         /// <param name="userId">The user identifier.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task{ProgramInfoDto}.</returns>
-        Task<ProgramInfoDto> GetLiveTvProgramAsync(string id, string userId, CancellationToken cancellationToken = default(CancellationToken));
+        Task<BaseItemDto> GetLiveTvProgramAsync(string id, string userId, CancellationToken cancellationToken = default(CancellationToken));
 
         /// <summary>
         /// Gets the recommended live tv programs asynchronous.
@@ -1136,7 +1120,7 @@ namespace MediaBrowser.Model.ApiClient
         /// <param name="query">The query.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task{QueryResult{ProgramInfoDto}}.</returns>
-        Task<QueryResult<ProgramInfoDto>> GetRecommendedLiveTvProgramsAsync(RecommendedProgramQuery query, CancellationToken cancellationToken = default(CancellationToken));
+        Task<QueryResult<BaseItemDto>> GetRecommendedLiveTvProgramsAsync(RecommendedProgramQuery query, CancellationToken cancellationToken = default(CancellationToken));
 
         /// <summary>
         /// Creates the live tv timer asynchronous.

+ 10 - 0
MediaBrowser.Model/ApiClient/IConnectionManager.cs

@@ -178,5 +178,15 @@ namespace MediaBrowser.Model.ApiClient
         /// </summary>
         /// <returns>Task&lt;List&lt;UserDto&gt;&gt;.</returns>
         Task<List<UserDto>> GetOfflineUsers();
+
+        /// <summary>
+        /// Signups for connect.
+        /// </summary>
+        /// <param name="email">The email.</param>
+        /// <param name="username">The username.</param>
+        /// <param name="password">The password.</param>
+        /// <param name="cancellationToken">The cancellation token.</param>
+        /// <returns>Task.</returns>
+        Task<ConnectSignupResponse> SignupForConnect(string email, string username, string password, CancellationToken cancellationToken = default(CancellationToken));
     }
 }

+ 10 - 0
MediaBrowser.Model/Configuration/AutoOnOff.cs

@@ -0,0 +1,10 @@
+
+namespace MediaBrowser.Model.Configuration
+{
+    public enum AutoOnOff
+    {
+        Auto,
+        Enabled,
+        Disabled
+    }
+}

+ 10 - 4
MediaBrowser.Model/Configuration/ServerConfiguration.cs

@@ -187,7 +187,6 @@ namespace MediaBrowser.Model.Configuration
 
         public bool EnableAutomaticRestart { get; set; }
 
-        public bool EnableRealtimeMonitor { get; set; }
         public PathSubstitution[] PathSubstitutions { get; set; }
 
         public string ServerName { get; set; }
@@ -208,6 +207,10 @@ namespace MediaBrowser.Model.Configuration
         public bool EnableVideoArchiveFiles { get; set; }
         public int RemoteClientBitrateLimit { get; set; }
 
+        public bool DenyIFrameEmbedding { get; set; }
+
+        public AutoOnOff EnableLibraryMonitor { get; set; }
+
         /// <summary>
         /// Initializes a new instance of the <see cref="ServerConfiguration" /> class.
         /// </summary>
@@ -224,6 +227,7 @@ namespace MediaBrowser.Model.Configuration
             EnableDashboardResourceMinification = true;
 
             EnableAutomaticRestart = true;
+            DenyIFrameEmbedding = true;
 
             EnableUPnP = true;
 
@@ -233,6 +237,7 @@ namespace MediaBrowser.Model.Configuration
             // 5 minutes
             MinResumeDurationSeconds = 300;
 
+            EnableLibraryMonitor = AutoOnOff.Auto;
             RealtimeLibraryMonitorDelay = 40;
 
             EnableInternetProviders = true;
@@ -250,8 +255,6 @@ namespace MediaBrowser.Model.Configuration
 
             SeasonZeroDisplayName = "Specials";
 
-            EnableRealtimeMonitor = true;
-
             UICulture = "en-us";
 
             PeopleMetadataOptions = new PeopleMetadataOptions();
@@ -426,7 +429,10 @@ namespace MediaBrowser.Model.Configuration
                     }
                 },
 
-                new MetadataOptions(0, 1280) {ItemType = "Season"}
+                new MetadataOptions(0, 1280)
+                {
+                    ItemType = "Season"
+                }
             };
         }
     }

+ 170 - 1
MediaBrowser.Model/Dto/BaseItemDto.cs

@@ -2,6 +2,7 @@
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Extensions;
 using MediaBrowser.Model.Library;
+using MediaBrowser.Model.LiveTv;
 using MediaBrowser.Model.Providers;
 using MediaBrowser.Model.Sync;
 using System;
@@ -99,6 +100,11 @@ namespace MediaBrowser.Model.Dto
         /// </summary>
         /// <value>The synchronize status.</value>
         public SyncJobItemStatus? SyncStatus { get; set; }
+        /// <summary>
+        /// Gets or sets the synchronize percent.
+        /// </summary>
+        /// <value>The synchronize percent.</value>
+        public double? SyncPercent { get; set; }
 
         /// <summary>
         /// Gets or sets the DVD season number.
@@ -442,7 +448,57 @@ namespace MediaBrowser.Model.Dto
         /// Gets or sets the status.
         /// </summary>
         /// <value>The status.</value>
-        public SeriesStatus? Status { get; set; }
+        public string Status { get; set; }
+
+        [IgnoreDataMember]
+        public SeriesStatus? SeriesStatus
+        {
+            get
+            {
+                if (string.IsNullOrEmpty(Status))
+                {
+                    return null;
+                }
+
+                return (SeriesStatus)Enum.Parse(typeof(SeriesStatus), Status, true);
+            }
+            set
+            {
+                if (value == null)
+                {
+                    Status = null;
+                }
+                else
+                {
+                    Status = value.Value.ToString();
+                }
+            }
+        }
+
+        [IgnoreDataMember]
+        public RecordingStatus? RecordingStatus
+        {
+            get
+            {
+                if (string.IsNullOrEmpty(Status))
+                {
+                    return null;
+                }
+
+                return (RecordingStatus)Enum.Parse(typeof(RecordingStatus), Status, true);
+            }
+            set
+            {
+                if (value == null)
+                {
+                    Status = null;
+                }
+                else
+                {
+                    Status = value.Value.ToString();
+                }
+            }
+        }
 
         /// <summary>
         /// Gets or sets the air time.
@@ -797,6 +853,17 @@ namespace MediaBrowser.Model.Dto
         public double? Altitude { get; set; }
         public int? IsoSpeedRating { get; set; }
 
+        /// <summary>
+        /// Used by RecordingGroup
+        /// </summary>
+        public int? RecordingCount { get; set; }
+
+        /// <summary>
+        /// Gets or sets the series timer identifier.
+        /// </summary>
+        /// <value>The series timer identifier.</value>
+        public string SeriesTimerId { get; set; }
+
         /// <summary>
         /// Gets a value indicating whether this instance can resume.
         /// </summary>
@@ -1017,5 +1084,107 @@ namespace MediaBrowser.Model.Dto
         /// Occurs when [property changed].
         /// </summary>
         public event PropertyChangedEventHandler PropertyChanged;
+
+        /// <summary>
+        /// Gets or sets the program identifier.
+        /// </summary>
+        /// <value>The program identifier.</value>
+        public string ProgramId { get; set; }
+
+        /// <summary>
+        /// Gets or sets the channel primary image tag.
+        /// </summary>
+        /// <value>The channel primary image tag.</value>
+        public string ChannelPrimaryImageTag { get; set; }
+
+        /// <summary>
+        /// The start date of the recording, in UTC.
+        /// </summary>
+        public DateTime? StartDate { get; set; }
+
+        /// <summary>
+        /// Gets or sets the original air date.
+        /// </summary>
+        /// <value>The original air date.</value>
+        public DateTime? OriginalAirDate { get; set; }
+
+        /// <summary>
+        /// Gets or sets the completion percentage.
+        /// </summary>
+        /// <value>The completion percentage.</value>
+        public double? CompletionPercentage { get; set; }
+
+        /// <summary>
+        /// Gets or sets a value indicating whether this instance is repeat.
+        /// </summary>
+        /// <value><c>true</c> if this instance is repeat; otherwise, <c>false</c>.</value>
+        public bool? IsRepeat { get; set; }
+
+        /// <summary>
+        /// Gets or sets the episode title.
+        /// </summary>
+        /// <value>The episode title.</value>
+        public string EpisodeTitle { get; set; }
+
+        /// <summary>
+        /// Gets or sets the type of the channel.
+        /// </summary>
+        /// <value>The type of the channel.</value>
+        public ChannelType? ChannelType { get; set; }
+
+        /// <summary>
+        /// Gets or sets the audio.
+        /// </summary>
+        /// <value>The audio.</value>
+        public ProgramAudio? Audio { get; set; }
+
+        /// <summary>
+        /// Gets or sets a value indicating whether this instance is movie.
+        /// </summary>
+        /// <value><c>true</c> if this instance is movie; otherwise, <c>false</c>.</value>
+        public bool? IsMovie { get; set; }
+
+        /// <summary>
+        /// Gets or sets a value indicating whether this instance is sports.
+        /// </summary>
+        /// <value><c>true</c> if this instance is sports; otherwise, <c>false</c>.</value>
+        public bool? IsSports { get; set; }
+
+        /// <summary>
+        /// Gets or sets a value indicating whether this instance is series.
+        /// </summary>
+        /// <value><c>true</c> if this instance is series; otherwise, <c>false</c>.</value>
+        public bool? IsSeries { get; set; }
+
+        /// <summary>
+        /// Gets or sets a value indicating whether this instance is live.
+        /// </summary>
+        /// <value><c>true</c> if this instance is live; otherwise, <c>false</c>.</value>
+        public bool? IsLive { get; set; }
+
+        /// <summary>
+        /// Gets or sets a value indicating whether this instance is news.
+        /// </summary>
+        /// <value><c>true</c> if this instance is news; otherwise, <c>false</c>.</value>
+        public bool? IsNews { get; set; }
+
+        /// <summary>
+        /// Gets or sets a value indicating whether this instance is kids.
+        /// </summary>
+        /// <value><c>true</c> if this instance is kids; otherwise, <c>false</c>.</value>
+        public bool? IsKids { get; set; }
+
+        /// <summary>
+        /// Gets or sets a value indicating whether this instance is premiere.
+        /// </summary>
+        /// <value><c>true</c> if this instance is premiere; otherwise, <c>false</c>.</value>
+        public bool? IsPremiere { get; set; }
+
+        /// <summary>
+        /// Gets or sets the timer identifier.
+        /// </summary>
+        /// <value>The timer identifier.</value>
+        public string TimerId { get; set; }
+
     }
 }

+ 3 - 3
MediaBrowser.Model/LiveTv/BaseTimerInfoDto.cs

@@ -1,7 +1,7 @@
-using System;
-using System.ComponentModel;
-using MediaBrowser.Model.Dto;
+using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.Extensions;
+using System;
+using System.ComponentModel;
 
 namespace MediaBrowser.Model.LiveTv
 {

+ 1 - 1
MediaBrowser.Model/LiveTv/ChannelInfoDto.cs

@@ -97,7 +97,7 @@ namespace MediaBrowser.Model.LiveTv
         /// Gets or sets the now playing program.
         /// </summary>
         /// <value>The now playing program.</value>
-        public ProgramInfoDto CurrentProgram { get; set; }
+        public BaseItemDto CurrentProgram { get; set; }
 
         /// <summary>
         /// Gets or sets the primary image aspect ratio, after image enhancements.

+ 0 - 250
MediaBrowser.Model/LiveTv/ProgramInfoDto.cs

@@ -1,250 +0,0 @@
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Extensions;
-using MediaBrowser.Model.Library;
-using System;
-using System.Collections.Generic;
-using System.ComponentModel;
-using System.Diagnostics;
-using System.Runtime.Serialization;
-
-namespace MediaBrowser.Model.LiveTv
-{
-    [DebuggerDisplay("Name = {Name}, StartTime = {StartDate}, EndTime = {EndDate}")]
-    public class ProgramInfoDto : IHasPropertyChangedEvent, IItemDto, IHasServerId
-    {
-        /// <summary>
-        /// Id of the program.
-        /// </summary>
-        public string Id { get; set; }
-
-        /// <summary>
-        /// Gets or sets the primary image aspect ratio.
-        /// </summary>
-        /// <value>The primary image aspect ratio.</value>
-        public double? PrimaryImageAspectRatio { get; set; }
-
-        /// <summary>
-        /// Gets or sets the server identifier.
-        /// </summary>
-        /// <value>The server identifier.</value>
-        public string ServerId { get; set; }
-
-        /// <summary>
-        /// Gets or sets the original primary image aspect ratio.
-        /// </summary>
-        /// <value>The original primary image aspect ratio.</value>
-        public double? OriginalPrimaryImageAspectRatio { get; set; }
-
-        /// <summary>
-        /// Gets or sets the type of the media.
-        /// </summary>
-        /// <value>The type of the media.</value>
-        public string MediaType { get; set; }
-        
-        /// <summary>
-        /// Gets or sets the timer identifier.
-        /// </summary>
-        /// <value>The timer identifier.</value>
-        public string TimerId { get; set; }
-
-        /// <summary>
-        /// Gets or sets the series timer identifier.
-        /// </summary>
-        /// <value>The series timer identifier.</value>
-        public string SeriesTimerId { get; set; }
-        
-        /// <summary>
-        /// Gets or sets the external identifier.
-        /// </summary>
-        /// <value>The external identifier.</value>
-        public string ExternalId { get; set; }
-        
-        /// <summary>
-        /// Gets or sets the channel identifier.
-        /// </summary>
-        /// <value>The channel identifier.</value>
-        public string ChannelId { get; set; }
-
-        /// <summary>
-        /// Gets or sets the channel primary image tag.
-        /// </summary>
-        /// <value>The channel primary image tag.</value>
-        public string ChannelPrimaryImageTag { get; set; }
-
-        /// <summary>
-        /// Gets or sets the play access.
-        /// </summary>
-        /// <value>The play access.</value>
-        public PlayAccess PlayAccess { get; set; }
-        
-        /// <summary>
-        /// Gets or sets the name of the channel.
-        /// </summary>
-        /// <value>The name of the channel.</value>
-        public string ChannelName { get; set; }
-        
-        /// <summary>
-        /// Gets or sets the community rating.
-        /// </summary>
-        /// <value>The community rating.</value>
-        public float? CommunityRating { get; set; }
-        
-        /// <summary>
-        /// Gets or sets the official rating.
-        /// </summary>
-        /// <value>The official rating.</value>
-        public string OfficialRating { get; set; }
-
-        /// <summary>
-        /// Gets or sets the production year.
-        /// </summary>
-        /// <value>The production year.</value>
-        public int? ProductionYear { get; set; }
-
-        /// <summary>
-        /// Gets or sets the name of the service.
-        /// </summary>
-        /// <value>The name of the service.</value>
-        public string ServiceName { get; set; }
-
-        /// <summary>
-        /// Name of the program
-        /// </summary>
-        public string Name { get; set; }
-
-        /// <summary>
-        /// Overview of the recording.
-        /// </summary>
-        public string Overview { get; set; }
-
-        /// <summary>
-        /// The start date of the program, in UTC.
-        /// </summary>
-        public DateTime StartDate { get; set; }
-
-        /// <summary>
-        /// The end date of the program, in UTC.
-        /// </summary>
-        public DateTime EndDate { get; set; }
-
-        /// <summary>
-        /// Genre of the program.
-        /// </summary>
-        public List<string> Genres { get; set; }
-
-        /// <summary>
-        /// Gets or sets a value indicating whether this instance is hd.
-        /// </summary>
-        /// <value><c>true</c> if this instance is hd; otherwise, <c>false</c>.</value>
-        public bool? IsHD { get; set; }
-
-        /// <summary>
-        /// Gets or sets the audio.
-        /// </summary>
-        /// <value>The audio.</value>
-        public ProgramAudio? Audio { get; set; }
-        
-        /// <summary>
-        /// Gets or sets the original air date.
-        /// </summary>
-        /// <value>The original air date.</value>
-        public DateTime? OriginalAirDate { get; set; }
-
-        /// <summary>
-        /// Gets or sets a value indicating whether this instance is repeat.
-        /// </summary>
-        /// <value><c>true</c> if this instance is repeat; otherwise, <c>false</c>.</value>
-        public bool IsRepeat { get; set; }
-
-        /// <summary>
-        /// Gets or sets the episode title.
-        /// </summary>
-        /// <value>The episode title.</value>
-        public string EpisodeTitle { get; set; }
-
-        /// <summary>
-        /// Gets or sets the image tags.
-        /// </summary>
-        /// <value>The image tags.</value>
-        public Dictionary<ImageType, string> ImageTags { get; set; }
-        
-        /// <summary>
-        /// Gets or sets the user data.
-        /// </summary>
-        /// <value>The user data.</value>
-        public UserItemDataDto UserData { get; set; }
-
-        /// <summary>
-        /// Gets or sets a value indicating whether this instance is movie.
-        /// </summary>
-        /// <value><c>true</c> if this instance is movie; otherwise, <c>false</c>.</value>
-        public bool IsMovie { get; set; }
-
-        /// <summary>
-        /// Gets or sets a value indicating whether this instance is sports.
-        /// </summary>
-        /// <value><c>true</c> if this instance is sports; otherwise, <c>false</c>.</value>
-        public bool IsSports { get; set; }
-
-        /// <summary>
-        /// Gets or sets a value indicating whether this instance is series.
-        /// </summary>
-        /// <value><c>true</c> if this instance is series; otherwise, <c>false</c>.</value>
-        public bool IsSeries { get; set; }
-
-        /// <summary>
-        /// Gets or sets a value indicating whether this instance is live.
-        /// </summary>
-        /// <value><c>true</c> if this instance is live; otherwise, <c>false</c>.</value>
-        public bool IsLive { get; set; }
-
-        /// <summary>
-        /// Gets or sets the type.
-        /// </summary>
-        /// <value>The type.</value>
-        public string Type { get; set; }
-
-        /// <summary>
-        /// Gets or sets the run time ticks.
-        /// </summary>
-        /// <value>The run time ticks.</value>
-        public long? RunTimeTicks { get; set; }
-
-        /// <summary>
-        /// Gets or sets a value indicating whether this instance is news.
-        /// </summary>
-        /// <value><c>true</c> if this instance is news; otherwise, <c>false</c>.</value>
-        public bool IsNews { get; set; }
-
-        /// <summary>
-        /// Gets or sets a value indicating whether this instance is kids.
-        /// </summary>
-        /// <value><c>true</c> if this instance is kids; otherwise, <c>false</c>.</value>
-        public bool IsKids { get; set; }
-
-        /// <summary>
-        /// Gets or sets a value indicating whether this instance is premiere.
-        /// </summary>
-        /// <value><c>true</c> if this instance is premiere; otherwise, <c>false</c>.</value>
-        public bool IsPremiere { get; set; }
-
-        /// <summary>
-        /// Gets a value indicating whether this instance has primary image.
-        /// </summary>
-        /// <value><c>true</c> if this instance has primary image; otherwise, <c>false</c>.</value>
-        [IgnoreDataMember]
-        public bool HasPrimaryImage
-        {
-            get { return ImageTags != null && ImageTags.ContainsKey(ImageType.Primary); }
-        }
-
-        public ProgramInfoDto()
-        {
-            Genres = new List<string>();
-            ImageTags = new Dictionary<ImageType, string>();
-        }
-
-        public event PropertyChangedEventHandler PropertyChanged;
-    }
-}

+ 0 - 40
MediaBrowser.Model/LiveTv/RecordingGroupDto.cs

@@ -1,40 +0,0 @@
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Extensions;
-using System.ComponentModel;
-using System.Diagnostics;
-
-namespace MediaBrowser.Model.LiveTv
-{
-    /// <summary>
-    /// Class RecordingGroupDto.
-    /// </summary>
-    [DebuggerDisplay("Name = {Name}, Count = {RecordingCount}")]
-    public class RecordingGroupDto : IHasPropertyChangedEvent, IHasServerId
-    {
-        /// <summary>
-        /// Gets or sets the server identifier.
-        /// </summary>
-        /// <value>The server identifier.</value>
-        public string ServerId { get; set; }
-        
-        /// <summary>
-        /// Gets or sets the name.
-        /// </summary>
-        /// <value>The name.</value>
-        public string Name { get; set; }
-
-        /// <summary>
-        /// Gets or sets the identifier.
-        /// </summary>
-        /// <value>The identifier.</value>
-        public string Id { get; set; }
-
-        /// <summary>
-        /// Gets or sets the recording count.
-        /// </summary>
-        /// <value>The recording count.</value>
-        public int RecordingCount { get; set; }
-
-        public event PropertyChangedEventHandler PropertyChanged;
-    }
-}

+ 0 - 313
MediaBrowser.Model/LiveTv/RecordingInfoDto.cs

@@ -1,313 +0,0 @@
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Extensions;
-using MediaBrowser.Model.Library;
-using MediaBrowser.Model.Sync;
-using System;
-using System.Collections.Generic;
-using System.ComponentModel;
-using System.Diagnostics;
-using System.Runtime.Serialization;
-
-namespace MediaBrowser.Model.LiveTv
-{
-    [DebuggerDisplay("Name = {Name}, ChannelName = {ChannelName}")]
-    public class RecordingInfoDto : IHasPropertyChangedEvent, IItemDto, IHasServerId, IHasSyncInfo
-    {
-        /// <summary>
-        /// Id of the recording.
-        /// </summary>
-        public string Id { get; set; }
-
-        /// <summary>
-        /// Gets or sets the primary image aspect ratio.
-        /// </summary>
-        /// <value>The primary image aspect ratio.</value>
-        public double? PrimaryImageAspectRatio { get; set; }
-
-        /// <summary>
-        /// Gets or sets the server identifier.
-        /// </summary>
-        /// <value>The server identifier.</value>
-        public string ServerId { get; set; }
-
-        /// <summary>
-        /// Gets or sets the original primary image aspect ratio.
-        /// </summary>
-        /// <value>The original primary image aspect ratio.</value>
-        public double? OriginalPrimaryImageAspectRatio { get; set; }
-
-        /// <summary>
-        /// Gets or sets a value indicating whether [supports synchronize].
-        /// </summary>
-        /// <value><c>null</c> if [supports synchronize] contains no value, <c>true</c> if [supports synchronize]; otherwise, <c>false</c>.</value>
-        public bool? SupportsSync { get; set; }
-        /// <summary>
-        /// Gets or sets a value indicating whether this instance has synchronize job.
-        /// </summary>
-        /// <value><c>null</c> if [has synchronize job] contains no value, <c>true</c> if [has synchronize job]; otherwise, <c>false</c>.</value>
-        public bool? HasSyncJob { get; set; }
-        /// <summary>
-        /// Gets or sets a value indicating whether this instance is synced.
-        /// </summary>
-        /// <value><c>null</c> if [is synced] contains no value, <c>true</c> if [is synced]; otherwise, <c>false</c>.</value>
-        public bool? IsSynced { get; set; }
-        /// <summary>
-        /// Gets or sets the synchronize status.
-        /// </summary>
-        /// <value>The synchronize status.</value>
-        public SyncJobItemStatus? SyncStatus { get; set; }
-        
-        /// <summary>
-        /// Gets or sets the series timer identifier.
-        /// </summary>
-        /// <value>The series timer identifier.</value>
-        public string SeriesTimerId { get; set; }
-        
-        /// <summary>
-        /// Gets or sets the external identifier.
-        /// </summary>
-        /// <value>The external identifier.</value>
-        public string ExternalId { get; set; }
-
-        /// <summary>
-        /// Gets or sets the program identifier.
-        /// </summary>
-        /// <value>The program identifier.</value>
-        public string ProgramId { get; set; }
-
-        /// <summary>
-        /// ChannelId of the recording.
-        /// </summary>
-        public string ChannelId { get; set; }
-
-        /// <summary>
-        /// Gets or sets the play access.
-        /// </summary>
-        /// <value>The play access.</value>
-        public PlayAccess PlayAccess { get; set; }
-        
-        /// <summary>
-        /// Gets or sets the channel primary image tag.
-        /// </summary>
-        /// <value>The channel primary image tag.</value>
-        public string ChannelPrimaryImageTag { get; set; }
-
-        /// <summary>
-        /// ChannelName of the recording.
-        /// </summary>
-        public string ChannelName { get; set; }
-
-        /// <summary>
-        /// Gets or sets the name of the service.
-        /// </summary>
-        /// <value>The name of the service.</value>
-        public string ServiceName { get; set; }
-
-        /// <summary>
-        /// Name of the recording.
-        /// </summary>
-        public string Name { get; set; }
-
-        /// <summary>
-        /// Gets or sets the media streams.
-        /// </summary>
-        /// <value>The media streams.</value>
-        public List<MediaStream> MediaStreams { get; set; }
-
-        /// <summary>
-        /// Gets or sets the path.
-        /// </summary>
-        /// <value>The path.</value>
-        public string Path { get; set; }
-
-        /// <summary>
-        /// Gets or sets a value indicating whether this instance can delete.
-        /// </summary>
-        /// <value><c>null</c> if [can delete] contains no value, <c>true</c> if [can delete]; otherwise, <c>false</c>.</value>
-        public bool? CanDelete { get; set; }
-
-        /// <summary>
-        /// Overview of the recording.
-        /// </summary>
-        public string Overview { get; set; }
-
-        /// <summary>
-        /// The start date of the recording, in UTC.
-        /// </summary>
-        public DateTime StartDate { get; set; }
-
-        /// <summary>
-        /// The end date of the recording, in UTC.
-        /// </summary>
-        public DateTime EndDate { get; set; }
-
-        /// <summary>
-        /// Gets or sets the original air date.
-        /// </summary>
-        /// <value>The original air date.</value>
-        public DateTime? OriginalAirDate { get; set; }
-
-        /// <summary>
-        /// Gets or sets the status.
-        /// </summary>
-        /// <value>The status.</value>
-        public RecordingStatus Status { get; set; }
-
-        /// <summary>
-        /// Gets or sets the name of the status.
-        /// </summary>
-        /// <value>The name of the status.</value>
-        public string StatusName { get; set; }
-
-        /// <summary>
-        /// Gets or sets the completion percentage.
-        /// </summary>
-        /// <value>The completion percentage.</value>
-        public double? CompletionPercentage { get; set; }
-
-        /// <summary>
-        /// Genre of the program.
-        /// </summary>
-        public List<string> Genres { get; set; }
-
-        /// <summary>
-        /// Gets or sets a value indicating whether this instance is repeat.
-        /// </summary>
-        /// <value><c>true</c> if this instance is repeat; otherwise, <c>false</c>.</value>
-        public bool IsRepeat { get; set; }
-
-        /// <summary>
-        /// Gets or sets the episode title.
-        /// </summary>
-        /// <value>The episode title.</value>
-        public string EpisodeTitle { get; set; }
-
-        /// <summary>
-        /// Gets or sets the run time ticks.
-        /// </summary>
-        /// <value>The run time ticks.</value>
-        public long? RunTimeTicks { get; set; }
-
-        /// <summary>
-        /// Gets or sets the type of the media.
-        /// </summary>
-        /// <value>The type of the media.</value>
-        public string MediaType { get; set; }
-
-        /// <summary>
-        /// Gets or sets the type of the channel.
-        /// </summary>
-        /// <value>The type of the channel.</value>
-        public ChannelType ChannelType { get; set; }
-
-        /// <summary>
-        /// Gets or sets the official rating.
-        /// </summary>
-        /// <value>The official rating.</value>
-        public string OfficialRating { get; set; }
-
-        /// <summary>
-        /// Gets or sets the community rating.
-        /// </summary>
-        /// <value>The community rating.</value>
-        public float? CommunityRating { get; set; }
-
-        /// <summary>
-        /// Gets or sets a value indicating whether this instance is hd.
-        /// </summary>
-        /// <value><c>true</c> if this instance is hd; otherwise, <c>false</c>.</value>
-        public bool? IsHD { get; set; }
-
-        /// <summary>
-        /// Gets or sets the audio.
-        /// </summary>
-        /// <value>The audio.</value>
-        public ProgramAudio? Audio { get; set; }
-
-        /// <summary>
-        /// Gets or sets a value indicating whether this instance is movie.
-        /// </summary>
-        /// <value><c>true</c> if this instance is movie; otherwise, <c>false</c>.</value>
-        public bool IsMovie { get; set; }
-
-        /// <summary>
-        /// Gets or sets a value indicating whether this instance is sports.
-        /// </summary>
-        /// <value><c>true</c> if this instance is sports; otherwise, <c>false</c>.</value>
-        public bool IsSports { get; set; }
-
-        /// <summary>
-        /// Gets or sets a value indicating whether this instance is series.
-        /// </summary>
-        /// <value><c>true</c> if this instance is series; otherwise, <c>false</c>.</value>
-        public bool IsSeries { get; set; }
-
-        /// <summary>
-        /// Gets or sets a value indicating whether this instance is live.
-        /// </summary>
-        /// <value><c>true</c> if this instance is live; otherwise, <c>false</c>.</value>
-        public bool IsLive { get; set; }
-
-        /// <summary>
-        /// Gets or sets a value indicating whether this instance is news.
-        /// </summary>
-        /// <value><c>true</c> if this instance is news; otherwise, <c>false</c>.</value>
-        public bool IsNews { get; set; }
-
-        /// <summary>
-        /// Gets or sets a value indicating whether this instance is kids.
-        /// </summary>
-        /// <value><c>true</c> if this instance is kids; otherwise, <c>false</c>.</value>
-        public bool IsKids { get; set; }
-
-        /// <summary>
-        /// Gets or sets a value indicating whether this instance is premiere.
-        /// </summary>
-        /// <value><c>true</c> if this instance is premiere; otherwise, <c>false</c>.</value>
-        public bool IsPremiere { get; set; }
-
-        /// <summary>
-        /// Gets or sets the image tags.
-        /// </summary>
-        /// <value>The image tags.</value>
-        public Dictionary<ImageType, string> ImageTags { get; set; }
-        
-        /// <summary>
-        /// Gets or sets the user data.
-        /// </summary>
-        /// <value>The user data.</value>
-        public UserItemDataDto UserData { get; set; }
-
-        /// <summary>
-        /// Gets a value indicating whether this instance has primary image.
-        /// </summary>
-        /// <value><c>true</c> if this instance has primary image; otherwise, <c>false</c>.</value>
-        [IgnoreDataMember]
-        public bool HasPrimaryImage
-        {
-            get { return ImageTags != null && ImageTags.ContainsKey(ImageType.Primary); }
-        }
-
-        /// <summary>
-        /// Gets or sets the type.
-        /// </summary>
-        /// <value>The type.</value>
-        public string Type { get; set; }
-
-        /// <summary>
-        /// Gets or sets the media sources.
-        /// </summary>
-        /// <value>The media sources.</value>
-        public List<MediaSourceInfo> MediaSources { get; set; }
-        
-        public RecordingInfoDto()
-        {
-            Genres = new List<string>();
-            ImageTags = new Dictionary<ImageType, string>();
-            MediaSources = new List<MediaSourceInfo>();
-        }
-
-        public event PropertyChangedEventHandler PropertyChanged;
-    }
-}

+ 4 - 2
MediaBrowser.Model/LiveTv/TimerInfoDto.cs

@@ -1,4 +1,6 @@
-namespace MediaBrowser.Model.LiveTv
+using MediaBrowser.Model.Dto;
+
+namespace MediaBrowser.Model.LiveTv
 {
     public class TimerInfoDto : BaseTimerInfoDto
     {
@@ -30,7 +32,7 @@
         /// Gets or sets the program information.
         /// </summary>
         /// <value>The program information.</value>
-        public ProgramInfoDto ProgramInfo { get; set; }
+        public BaseItemDto ProgramInfo { get; set; }
 
     }
 }

+ 2 - 3
MediaBrowser.Model/MediaBrowser.Model.csproj

@@ -64,6 +64,7 @@
     <Compile Include="ApiClient\ConnectionMode.cs" />
     <Compile Include="ApiClient\ConnectionResult.cs" />
     <Compile Include="ApiClient\ConnectionState.cs" />
+    <Compile Include="ApiClient\ConnectSignupResponse.cs" />
     <Compile Include="ApiClient\HttpResponseEventArgs.cs" />
     <Compile Include="ApiClient\IApiClient.cs" />
     <Compile Include="ApiClient\ApiClientExtensions.cs" />
@@ -95,6 +96,7 @@
     <Compile Include="Chapters\RemoteChapterResult.cs" />
     <Compile Include="Collections\CollectionCreationResult.cs" />
     <Compile Include="Configuration\AccessSchedule.cs" />
+    <Compile Include="Configuration\AutoOnOff.cs" />
     <Compile Include="Configuration\ChannelOptions.cs" />
     <Compile Include="Configuration\ChapterOptions.cs" />
     <Compile Include="Configuration\CinemaModeConfiguration.cs" />
@@ -259,10 +261,8 @@
     <Compile Include="LiveTv\LiveTvTunerInfoDto.cs" />
     <Compile Include="LiveTv\LiveTvTunerStatus.cs" />
     <Compile Include="LiveTv\ProgramAudio.cs" />
-    <Compile Include="LiveTv\ProgramInfoDto.cs" />
     <Compile Include="LiveTv\ProgramQuery.cs" />
     <Compile Include="LiveTv\RecommendedProgramQuery.cs" />
-    <Compile Include="LiveTv\RecordingGroupDto.cs" />
     <Compile Include="LiveTv\RecordingGroupQuery.cs" />
     <Compile Include="LiveTv\RecordingQuery.cs" />
     <Compile Include="LiveTv\RecordingStatus.cs" />
@@ -299,7 +299,6 @@
     <Compile Include="IO\IIsoMounter.cs" />
     <Compile Include="LiveTv\ChannelType.cs" />
     <Compile Include="LiveTv\LiveTvServiceInfo.cs" />
-    <Compile Include="LiveTv\RecordingInfoDto.cs" />
     <Compile Include="Net\WebSocketMessage.cs" />
     <Compile Include="Net\WebSocketMessageType.cs" />
     <Compile Include="Net\WebSocketState.cs" />

+ 96 - 58
MediaBrowser.Model/Net/MimeTypes.cs

@@ -1,4 +1,5 @@
-using System;
+using MediaBrowser.Model.Extensions;
+using System;
 using System.Collections.Generic;
 using System.IO;
 using System.Linq;
@@ -45,33 +46,50 @@ namespace MediaBrowser.Model.Net
                 ".rec"
         };
 
-        private static readonly Dictionary<string, string> VideoFileExtensionsDictionary = VideoFileExtensions.ToDictionary(i => i, StringComparer.OrdinalIgnoreCase);
+        private static Dictionary<string, string> GetVideoFileExtensionsDictionary()
+        {
+            Dictionary<string, string> dict = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
+
+            foreach (string ext in VideoFileExtensions)
+            {
+                dict[ext] = ext;
+            }
+
+            return dict;
+        }
+
+        private static readonly Dictionary<string, string> VideoFileExtensionsDictionary = GetVideoFileExtensionsDictionary();
 
         // http://en.wikipedia.org/wiki/Internet_media_type
         // Add more as needed
 
-        private static readonly Dictionary<string, string> MimeTypeLookup =
-            new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
-            {
-                {".jpg", "image/jpeg"},
-                {".jpeg", "image/jpeg"},
-                {".tbn", "image/jpeg"},
-                {".png", "image/png"},
-                {".gif", "image/gif"},
-                {".webp", "image/webp"},
-                {".ico", "image/vnd.microsoft.icon"},
-                {".mpg", "video/mpeg"},
-                {".mpeg", "video/mpeg"},
-                {".ogv", "video/ogg"},
-                {".mov", "video/quicktime"},
-                {".webm", "video/webm"},
-                {".mkv", "video/x-matroska"},
-                {".wmv", "video/x-ms-wmv"},
-                {".flv", "video/x-flv"},
-                {".avi", "video/x-msvideo"},
-                {".asf", "video/x-ms-asf"},
-                {".m4v", "video/x-m4v"}
-            };
+        private static Dictionary<string, string> GetMimeTypeLookup()
+        {
+            Dictionary<string, string> dict = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
+
+            dict.Add(".jpg", "image/jpeg");
+            dict.Add(".jpeg", "image/jpeg");
+            dict.Add(".tbn", "image/jpeg");
+            dict.Add(".png", "image/png");
+            dict.Add(".gif", "image/gif");
+            dict.Add(".webp", "image/webp");
+            dict.Add(".ico", "image/vnd.microsoft.icon");
+            dict.Add(".mpg", "video/mpeg");
+            dict.Add(".mpeg", "video/mpeg");
+            dict.Add(".ogv", "video/ogg");
+            dict.Add(".mov", "video/quicktime");
+            dict.Add(".webm", "video/webm");
+            dict.Add(".mkv", "video/x-matroska");
+            dict.Add(".wmv", "video/x-ms-wmv");
+            dict.Add(".flv", "video/x-flv");
+            dict.Add(".avi", "video/x-msvideo");
+            dict.Add(".asf", "video/x-ms-asf");
+            dict.Add(".m4v", "video/x-m4v");
+
+            return dict;
+        }
+
+        private static readonly Dictionary<string, string> MimeTypeLookup = GetMimeTypeLookup();
 
         private static readonly Dictionary<string, string> ExtensionLookup = CreateExtensionLookup();
 
@@ -109,19 +127,19 @@ namespace MediaBrowser.Model.Net
             }
 
             // Type video
-            if (ext.Equals(".3gp", StringComparison.OrdinalIgnoreCase))
+            if (StringHelper.EqualsIgnoreCase(ext, ".3gp"))
             {
                 return "video/3gpp";
             }
-            if (ext.Equals(".3g2", StringComparison.OrdinalIgnoreCase))
+            if (StringHelper.EqualsIgnoreCase(ext, ".3g2"))
             {
                 return "video/3gpp2";
             }
-            if (ext.Equals(".ts", StringComparison.OrdinalIgnoreCase))
+            if (StringHelper.EqualsIgnoreCase(ext, ".ts"))
             {
                 return "video/mp2t";
             }
-            if (ext.Equals(".mpd", StringComparison.OrdinalIgnoreCase))
+            if (StringHelper.EqualsIgnoreCase(ext, ".mpd"))
             {
                 return "video/vnd.mpeg.dash.mpd";
             }
@@ -133,134 +151,154 @@ namespace MediaBrowser.Model.Net
             }
 
             // Type text
-            if (ext.Equals(".css", StringComparison.OrdinalIgnoreCase))
+            if (StringHelper.EqualsIgnoreCase(ext, ".css"))
             {
                 return "text/css";
             }
-            if (ext.Equals(".csv", StringComparison.OrdinalIgnoreCase))
+            if (StringHelper.EqualsIgnoreCase(ext, ".csv"))
             {
                 return "text/csv";
             }
-            if (ext.Equals(".html", StringComparison.OrdinalIgnoreCase) || ext.Equals(".htm", StringComparison.OrdinalIgnoreCase))
+            if (StringHelper.EqualsIgnoreCase(ext, ".html"))
+            {
+                return "text/html; charset=UTF-8";
+            }
+            if (StringHelper.EqualsIgnoreCase(ext, ".htm"))
             {
                 return "text/html; charset=UTF-8";
             }
-            if (ext.Equals(".txt", StringComparison.OrdinalIgnoreCase))
+            if (StringHelper.EqualsIgnoreCase(ext, ".txt"))
             {
                 return "text/plain";
             }
-            if (ext.Equals(".xml", StringComparison.OrdinalIgnoreCase))
+            if (StringHelper.EqualsIgnoreCase(ext, ".xml"))
             {
                 return "application/xml";
             }
 
             // Type document
-            if (ext.Equals(".pdf", StringComparison.OrdinalIgnoreCase))
+            if (StringHelper.EqualsIgnoreCase(ext, ".pdf"))
             {
                 return "application/pdf";
             }
-            if (ext.Equals(".mobi", StringComparison.OrdinalIgnoreCase))
+            if (StringHelper.EqualsIgnoreCase(ext, ".mobi"))
             {
                 return "application/x-mobipocket-ebook";
             }
-            if (ext.Equals(".epub", StringComparison.OrdinalIgnoreCase))
+            if (StringHelper.EqualsIgnoreCase(ext, ".epub"))
+            {
+                return "application/epub+zip";
+            }
+            if (StringHelper.EqualsIgnoreCase(ext, ".cbz"))
             {
                 return "application/epub+zip";
             }
-            if (ext.Equals(".cbz", StringComparison.OrdinalIgnoreCase) || ext.Equals(".cbr", StringComparison.OrdinalIgnoreCase))
+            if (StringHelper.EqualsIgnoreCase(ext, ".cbr"))
             {
-                return "application/x-cdisplay";
+                return "application/epub+zip";
             }
 
             // Type audio
-            if (ext.Equals(".mp3", StringComparison.OrdinalIgnoreCase))
+            if (StringHelper.EqualsIgnoreCase(ext, ".mp3"))
             {
                 return "audio/mpeg";
             }
-            if (ext.Equals(".m4a", StringComparison.OrdinalIgnoreCase) || ext.Equals(".aac", StringComparison.OrdinalIgnoreCase))
+            if (StringHelper.EqualsIgnoreCase(ext, ".m4a"))
             {
                 return "audio/mp4";
             }
-            if (ext.Equals(".webma", StringComparison.OrdinalIgnoreCase))
+            if (StringHelper.EqualsIgnoreCase(ext, ".aac"))
+            {
+                return "audio/mp4";
+            }
+            if (StringHelper.EqualsIgnoreCase(ext, ".webma"))
             {
                 return "audio/webm";
             }
-            if (ext.Equals(".wav", StringComparison.OrdinalIgnoreCase))
+            if (StringHelper.EqualsIgnoreCase(ext, ".wav"))
             {
                 return "audio/wav";
             }
-            if (ext.Equals(".wma", StringComparison.OrdinalIgnoreCase))
+            if (StringHelper.EqualsIgnoreCase(ext, ".wma"))
             {
                 return "audio/x-ms-wma";
             }
-            if (ext.Equals(".flac", StringComparison.OrdinalIgnoreCase))
+            if (StringHelper.EqualsIgnoreCase(ext, ".flac"))
             {
                 return "audio/flac";
             }
-            if (ext.Equals(".aac", StringComparison.OrdinalIgnoreCase))
+            if (StringHelper.EqualsIgnoreCase(ext, ".aac"))
             {
                 return "audio/x-aac";
             }
-            if (ext.Equals(".ogg", StringComparison.OrdinalIgnoreCase) || ext.Equals(".oga", StringComparison.OrdinalIgnoreCase))
+            if (StringHelper.EqualsIgnoreCase(ext, ".ogg"))
+            {
+                return "audio/ogg";
+            }
+            if (StringHelper.EqualsIgnoreCase(ext, ".oga"))
             {
                 return "audio/ogg";
             }
 
             // Playlists
-            if (ext.Equals(".m3u8", StringComparison.OrdinalIgnoreCase))
+            if (StringHelper.EqualsIgnoreCase(ext, ".m3u8"))
             {
                 return "application/x-mpegURL";
             }
 
             // Misc
-            if (ext.Equals(".dll", StringComparison.OrdinalIgnoreCase))
+            if (StringHelper.EqualsIgnoreCase(ext, ".dll"))
             {
                 return "application/octet-stream";
             }
 
             // Web
-            if (ext.Equals(".js", StringComparison.OrdinalIgnoreCase))
+            if (StringHelper.EqualsIgnoreCase(ext, ".js"))
             {
                 return "application/x-javascript";
             }
-            if (ext.Equals(".json", StringComparison.OrdinalIgnoreCase))
+            if (StringHelper.EqualsIgnoreCase(ext, ".json"))
             {
                 return "application/json";
             }
-            if (ext.Equals(".map", StringComparison.OrdinalIgnoreCase))
+            if (StringHelper.EqualsIgnoreCase(ext, ".map"))
             {
                 return "application/x-javascript";
             }
 
-            if (ext.Equals(".woff", StringComparison.OrdinalIgnoreCase))
+            if (StringHelper.EqualsIgnoreCase(ext, ".woff"))
             {
                 return "font/woff";
             }
 
-            if (ext.Equals(".ttf", StringComparison.OrdinalIgnoreCase))
+            if (StringHelper.EqualsIgnoreCase(ext, ".ttf"))
             {
                 return "font/ttf";
             }
-            if (ext.Equals(".eot", StringComparison.OrdinalIgnoreCase))
+            if (StringHelper.EqualsIgnoreCase(ext, ".eot"))
             {
                 return "application/vnd.ms-fontobject";
             }
-            if (ext.Equals(".svg", StringComparison.OrdinalIgnoreCase) || ext.Equals(".svgz", StringComparison.OrdinalIgnoreCase))
+            if (StringHelper.EqualsIgnoreCase(ext, ".svg"))
+            {
+                return "image/svg+xml";
+            }
+            if (StringHelper.EqualsIgnoreCase(ext, ".svgz"))
             {
                 return "image/svg+xml";
             }
 
-            if (ext.Equals(".srt", StringComparison.OrdinalIgnoreCase))
+            if (StringHelper.EqualsIgnoreCase(ext, ".srt"))
             {
                 return "text/plain";
             }
 
-            if (ext.Equals(".vtt", StringComparison.OrdinalIgnoreCase))
+            if (StringHelper.EqualsIgnoreCase(ext, ".vtt"))
             {
                 return "text/vtt";
             }
 
-            if (ext.Equals(".ttml", StringComparison.OrdinalIgnoreCase))
+            if (StringHelper.EqualsIgnoreCase(ext, ".ttml"))
             {
                 return "application/ttml+xml";
             }

部分文件因文件數量過多而無法顯示