浏览代码

update query fields

Luke Pulverenti 8 年之前
父节点
当前提交
54cf0da758
共有 41 个文件被更改,包括 620 次插入385 次删除
  1. 170 84
      Emby.Server.Implementations/Data/SqliteItemRepository.cs
  2. 9 3
      Emby.Server.Implementations/Dto/DtoService.cs
  3. 3 5
      Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
  4. 2 1
      Emby.Server.Implementations/HttpServer/IHttpListener.cs
  5. 5 4
      Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpListener.cs
  6. 14 57
      Emby.Server.Implementations/Library/LibraryManager.cs
  7. 1 2
      Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
  8. 1 1
      Emby.Server.Implementations/LiveTv/LiveTvManager.cs
  9. 7 9
      Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
  10. 25 17
      Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHttpStream.cs
  11. 13 2
      Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs
  12. 31 24
      Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs
  13. 5 2
      Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
  14. 6 10
      Emby.Server.Implementations/Services/ResponseHelper.cs
  15. 3 2
      Emby.Server.Implementations/Services/ServiceHandler.cs
  16. 26 24
      MediaBrowser.Api/BaseApiService.cs
  17. 3 2
      MediaBrowser.Api/Images/ImageService.cs
  18. 6 3
      MediaBrowser.Api/LiveTv/LiveTvService.cs
  19. 1 1
      MediaBrowser.Api/Music/InstantMixService.cs
  20. 5 4
      MediaBrowser.Api/Playback/Progressive/AudioService.cs
  21. 7 4
      MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs
  22. 76 19
      MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs
  23. 2 1
      MediaBrowser.Api/Playback/Progressive/VideoService.cs
  24. 6 2
      MediaBrowser.Api/Playback/UniversalAudioService.cs
  25. 2 2
      MediaBrowser.Api/UserLibrary/ArtistsService.cs
  26. 2 1
      MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs
  27. 3 3
      MediaBrowser.Api/UserLibrary/GameGenresService.cs
  28. 3 3
      MediaBrowser.Api/UserLibrary/GenresService.cs
  29. 1 1
      MediaBrowser.Api/UserLibrary/ItemsService.cs
  30. 2 2
      MediaBrowser.Api/UserLibrary/MusicGenresService.cs
  31. 2 2
      MediaBrowser.Api/UserLibrary/PersonsService.cs
  32. 3 3
      MediaBrowser.Api/UserLibrary/StudiosService.cs
  33. 14 11
      MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
  34. 0 36
      MediaBrowser.Controller/Entities/InternalItemsQuery.cs
  35. 2 12
      MediaBrowser.Controller/Library/ILibraryManager.cs
  36. 138 3
      MediaBrowser.Controller/LiveTv/LiveStream.cs
  37. 4 1
      MediaBrowser.Model/Querying/ItemFields.cs
  38. 7 3
      MediaBrowser.WebDashboard/Api/DashboardService.cs
  39. 4 4
      MediaBrowser.WebDashboard/Api/PackageCreator.cs
  40. 1 1
      SharedVersion.cs
  41. 5 14
      SocketHttpListener.Portable/Net/ResponseStream.cs

+ 170 - 84
Emby.Server.Implementations/Data/SqliteItemRepository.cs

@@ -1415,66 +1415,69 @@ namespace Emby.Server.Implementations.Data
 
 
             var index = 5;
             var index = 5;
 
 
-            var hasProgramAttributes = item as IHasProgramAttributes;
-            if (hasProgramAttributes != null)
+            if (HasProgramAttributes(query))
             {
             {
-                if (!reader.IsDBNull(index))
+                var hasProgramAttributes = item as IHasProgramAttributes;
+                if (hasProgramAttributes != null)
                 {
                 {
-                    hasProgramAttributes.IsMovie = reader.GetBoolean(index);
-                }
-                index++;
+                    if (!reader.IsDBNull(index))
+                    {
+                        hasProgramAttributes.IsMovie = reader.GetBoolean(index);
+                    }
+                    index++;
 
 
-                if (!reader.IsDBNull(index))
-                {
-                    hasProgramAttributes.IsSports = reader.GetBoolean(index);
-                }
-                index++;
+                    if (!reader.IsDBNull(index))
+                    {
+                        hasProgramAttributes.IsSports = reader.GetBoolean(index);
+                    }
+                    index++;
 
 
-                if (!reader.IsDBNull(index))
-                {
-                    hasProgramAttributes.IsKids = reader.GetBoolean(index);
-                }
-                index++;
+                    if (!reader.IsDBNull(index))
+                    {
+                        hasProgramAttributes.IsKids = reader.GetBoolean(index);
+                    }
+                    index++;
 
 
-                if (!reader.IsDBNull(index))
-                {
-                    hasProgramAttributes.IsSeries = reader.GetBoolean(index);
-                }
-                index++;
+                    if (!reader.IsDBNull(index))
+                    {
+                        hasProgramAttributes.IsSeries = reader.GetBoolean(index);
+                    }
+                    index++;
 
 
-                if (!reader.IsDBNull(index))
-                {
-                    hasProgramAttributes.IsLive = reader.GetBoolean(index);
-                }
-                index++;
+                    if (!reader.IsDBNull(index))
+                    {
+                        hasProgramAttributes.IsLive = reader.GetBoolean(index);
+                    }
+                    index++;
 
 
-                if (!reader.IsDBNull(index))
-                {
-                    hasProgramAttributes.IsNews = reader.GetBoolean(index);
-                }
-                index++;
+                    if (!reader.IsDBNull(index))
+                    {
+                        hasProgramAttributes.IsNews = reader.GetBoolean(index);
+                    }
+                    index++;
 
 
-                if (!reader.IsDBNull(index))
-                {
-                    hasProgramAttributes.IsPremiere = reader.GetBoolean(index);
-                }
-                index++;
+                    if (!reader.IsDBNull(index))
+                    {
+                        hasProgramAttributes.IsPremiere = reader.GetBoolean(index);
+                    }
+                    index++;
 
 
-                if (!reader.IsDBNull(index))
-                {
-                    hasProgramAttributes.EpisodeTitle = reader.GetString(index);
-                }
-                index++;
+                    if (!reader.IsDBNull(index))
+                    {
+                        hasProgramAttributes.EpisodeTitle = reader.GetString(index);
+                    }
+                    index++;
 
 
-                if (!reader.IsDBNull(index))
+                    if (!reader.IsDBNull(index))
+                    {
+                        hasProgramAttributes.IsRepeat = reader.GetBoolean(index);
+                    }
+                    index++;
+                }
+                else
                 {
                 {
-                    hasProgramAttributes.IsRepeat = reader.GetBoolean(index);
+                    index += 9;
                 }
                 }
-                index++;
-            }
-            else
-            {
-                index += 9;
             }
             }
 
 
             if (!reader.IsDBNull(index))
             if (!reader.IsDBNull(index))
@@ -1483,7 +1486,7 @@ namespace Emby.Server.Implementations.Data
             }
             }
             index++;
             index++;
 
 
-            if (query.HasField(ItemFields.CustomRating))
+            if (HasField(query, ItemFields.CustomRating))
             {
             {
                 if (!reader.IsDBNull(index))
                 if (!reader.IsDBNull(index))
                 {
                 {
@@ -1498,7 +1501,7 @@ namespace Emby.Server.Implementations.Data
             }
             }
             index++;
             index++;
 
 
-            if (query.HasField(ItemFields.Settings))
+            if (HasField(query, ItemFields.Settings))
             {
             {
                 if (!reader.IsDBNull(index))
                 if (!reader.IsDBNull(index))
                 {
                 {
@@ -1525,7 +1528,7 @@ namespace Emby.Server.Implementations.Data
             }
             }
             index++;
             index++;
 
 
-            if (query.HasField(ItemFields.ExternalEtag))
+            if (HasField(query, ItemFields.ExternalEtag))
             {
             {
                 if (!reader.IsDBNull(index))
                 if (!reader.IsDBNull(index))
                 {
                 {
@@ -1534,11 +1537,14 @@ namespace Emby.Server.Implementations.Data
                 index++;
                 index++;
             }
             }
 
 
-            if (!reader.IsDBNull(index))
+            if (HasField(query, ItemFields.DateLastRefreshed))
             {
             {
-                item.DateLastRefreshed = reader[index].ReadDateTime();
+                if (!reader.IsDBNull(index))
+                {
+                    item.DateLastRefreshed = reader[index].ReadDateTime();
+                }
+                index++;
             }
             }
-            index++;
 
 
             if (!reader.IsDBNull(index))
             if (!reader.IsDBNull(index))
             {
             {
@@ -1558,7 +1564,7 @@ namespace Emby.Server.Implementations.Data
             }
             }
             index++;
             index++;
 
 
-            if (query.HasField(ItemFields.Overview))
+            if (HasField(query, ItemFields.Overview))
             {
             {
                 if (!reader.IsDBNull(index))
                 if (!reader.IsDBNull(index))
                 {
                 {
@@ -1585,7 +1591,7 @@ namespace Emby.Server.Implementations.Data
             }
             }
             index++;
             index++;
 
 
-            if (query.HasField(ItemFields.HomePageUrl))
+            if (HasField(query, ItemFields.HomePageUrl))
             {
             {
                 if (!reader.IsDBNull(index))
                 if (!reader.IsDBNull(index))
                 {
                 {
@@ -1594,7 +1600,7 @@ namespace Emby.Server.Implementations.Data
                 index++;
                 index++;
             }
             }
 
 
-            if (query.HasField(ItemFields.DisplayMediaType))
+            if (HasField(query, ItemFields.DisplayMediaType))
             {
             {
                 if (!reader.IsDBNull(index))
                 if (!reader.IsDBNull(index))
                 {
                 {
@@ -1603,7 +1609,7 @@ namespace Emby.Server.Implementations.Data
                 index++;
                 index++;
             }
             }
 
 
-            if (query.HasField(ItemFields.SortName))
+            if (HasField(query, ItemFields.SortName))
             {
             {
                 if (!reader.IsDBNull(index))
                 if (!reader.IsDBNull(index))
                 {
                 {
@@ -1618,7 +1624,7 @@ namespace Emby.Server.Implementations.Data
             }
             }
             index++;
             index++;
 
 
-            if (query.HasField(ItemFields.VoteCount))
+            if (HasField(query, ItemFields.VoteCount))
             {
             {
                 if (!reader.IsDBNull(index))
                 if (!reader.IsDBNull(index))
                 {
                 {
@@ -1627,7 +1633,7 @@ namespace Emby.Server.Implementations.Data
                 index++;
                 index++;
             }
             }
 
 
-            if (query.HasField(ItemFields.DateCreated))
+            if (HasField(query, ItemFields.DateCreated))
             {
             {
                 if (!reader.IsDBNull(index))
                 if (!reader.IsDBNull(index))
                 {
                 {
@@ -1645,7 +1651,7 @@ namespace Emby.Server.Implementations.Data
             item.Id = reader.GetGuid(index);
             item.Id = reader.GetGuid(index);
             index++;
             index++;
 
 
-            if (query.HasField(ItemFields.Genres))
+            if (HasField(query, ItemFields.Genres))
             {
             {
                 if (!reader.IsDBNull(index))
                 if (!reader.IsDBNull(index))
                 {
                 {
@@ -1680,13 +1686,16 @@ namespace Emby.Server.Implementations.Data
             }
             }
             index++;
             index++;
 
 
-            if (!reader.IsDBNull(index))
+            if (HasField(query, ItemFields.DateLastSaved))
             {
             {
-                item.DateLastSaved = reader[index].ReadDateTime();
+                if (!reader.IsDBNull(index))
+                {
+                    item.DateLastSaved = reader[index].ReadDateTime();
+                }
+                index++;
             }
             }
-            index++;
 
 
-            if (query.HasField(ItemFields.Settings))
+            if (HasField(query, ItemFields.Settings))
             {
             {
                 if (!reader.IsDBNull(index))
                 if (!reader.IsDBNull(index))
                 {
                 {
@@ -1695,7 +1704,7 @@ namespace Emby.Server.Implementations.Data
                 index++;
                 index++;
             }
             }
 
 
-            if (query.HasField(ItemFields.Studios))
+            if (HasField(query, ItemFields.Studios))
             {
             {
                 if (!reader.IsDBNull(index))
                 if (!reader.IsDBNull(index))
                 {
                 {
@@ -1704,7 +1713,7 @@ namespace Emby.Server.Implementations.Data
                 index++;
                 index++;
             }
             }
 
 
-            if (query.HasField(ItemFields.Tags))
+            if (HasField(query, ItemFields.Tags))
             {
             {
                 if (!reader.IsDBNull(index))
                 if (!reader.IsDBNull(index))
                 {
                 {
@@ -1729,7 +1738,7 @@ namespace Emby.Server.Implementations.Data
             }
             }
             index++;
             index++;
 
 
-            if (query.HasField(ItemFields.OriginalTitle))
+            if (HasField(query, ItemFields.OriginalTitle))
             {
             {
                 if (!reader.IsDBNull(index))
                 if (!reader.IsDBNull(index))
                 {
                 {
@@ -1748,7 +1757,7 @@ namespace Emby.Server.Implementations.Data
             }
             }
             index++;
             index++;
 
 
-            if (query.HasField(ItemFields.DateLastMediaAdded))
+            if (HasField(query, ItemFields.DateLastMediaAdded))
             {
             {
                 var folder = item as Folder;
                 var folder = item as Folder;
                 if (folder != null && !reader.IsDBNull(index))
                 if (folder != null && !reader.IsDBNull(index))
@@ -1814,7 +1823,7 @@ namespace Emby.Server.Implementations.Data
             }
             }
             index++;
             index++;
 
 
-            if (query.HasField(ItemFields.PresentationUniqueKey))
+            if (HasField(query, ItemFields.PresentationUniqueKey))
             {
             {
                 if (!reader.IsDBNull(index))
                 if (!reader.IsDBNull(index))
                 {
                 {
@@ -1823,7 +1832,7 @@ namespace Emby.Server.Implementations.Data
                 index++;
                 index++;
             }
             }
 
 
-            if (query.HasField(ItemFields.InheritedParentalRatingValue))
+            if (HasField(query, ItemFields.InheritedParentalRatingValue))
             {
             {
                 if (!reader.IsDBNull(index))
                 if (!reader.IsDBNull(index))
                 {
                 {
@@ -1832,7 +1841,7 @@ namespace Emby.Server.Implementations.Data
                 index++;
                 index++;
             }
             }
 
 
-            if (query.HasField(ItemFields.Tags))
+            if (HasField(query, ItemFields.Tags))
             {
             {
                 if (!reader.IsDBNull(index))
                 if (!reader.IsDBNull(index))
                 {
                 {
@@ -1841,7 +1850,7 @@ namespace Emby.Server.Implementations.Data
                 index++;
                 index++;
             }
             }
 
 
-            if (query.HasField(ItemFields.ExternalSeriesId))
+            if (HasField(query, ItemFields.ExternalSeriesId))
             {
             {
                 if (!reader.IsDBNull(index))
                 if (!reader.IsDBNull(index))
                 {
                 {
@@ -1850,7 +1859,7 @@ namespace Emby.Server.Implementations.Data
                 index++;
                 index++;
             }
             }
 
 
-            if (query.HasField(ItemFields.Taglines))
+            if (HasField(query, ItemFields.Taglines))
             {
             {
                 if (!reader.IsDBNull(index))
                 if (!reader.IsDBNull(index))
                 {
                 {
@@ -1859,7 +1868,7 @@ namespace Emby.Server.Implementations.Data
                 index++;
                 index++;
             }
             }
 
 
-            if (query.HasField(ItemFields.Keywords))
+            if (HasField(query, ItemFields.Keywords))
             {
             {
                 if (!reader.IsDBNull(index))
                 if (!reader.IsDBNull(index))
                 {
                 {
@@ -1883,7 +1892,7 @@ namespace Emby.Server.Implementations.Data
                 index++;
                 index++;
             }
             }
 
 
-            if (query.HasField(ItemFields.ProductionLocations))
+            if (HasField(query, ItemFields.ProductionLocations))
             {
             {
                 if (!reader.IsDBNull(index))
                 if (!reader.IsDBNull(index))
                 {
                 {
@@ -1892,7 +1901,7 @@ namespace Emby.Server.Implementations.Data
                 index++;
                 index++;
             }
             }
 
 
-            if (query.HasField(ItemFields.ThemeSongIds))
+            if (HasField(query, ItemFields.ThemeSongIds))
             {
             {
                 if (!reader.IsDBNull(index))
                 if (!reader.IsDBNull(index))
                 {
                 {
@@ -1901,7 +1910,7 @@ namespace Emby.Server.Implementations.Data
                 index++;
                 index++;
             }
             }
 
 
-            if (query.HasField(ItemFields.ThemeVideoIds))
+            if (HasField(query, ItemFields.ThemeVideoIds))
             {
             {
                 if (!reader.IsDBNull(index))
                 if (!reader.IsDBNull(index))
                 {
                 {
@@ -1942,14 +1951,17 @@ namespace Emby.Server.Implementations.Data
             }
             }
             index++;
             index++;
 
 
-            if (hasSeries != null)
+            if (HasField(query, ItemFields.SeriesPresentationUniqueKey))
             {
             {
-                if (!reader.IsDBNull(index))
+                if (hasSeries != null)
                 {
                 {
-                    hasSeries.SeriesPresentationUniqueKey = reader.GetString(index);
+                    if (!reader.IsDBNull(index))
+                    {
+                        hasSeries.SeriesPresentationUniqueKey = reader.GetString(index);
+                    }
                 }
                 }
+                index++;
             }
             }
-            index++;
 
 
             return item;
             return item;
         }
         }
@@ -2259,13 +2271,73 @@ namespace Emby.Server.Implementations.Data
             return new[] { field.ToString() };
             return new[] { field.ToString() };
         }
         }
 
 
+        private bool HasField(InternalItemsQuery query, ItemFields name)
+        {
+            var fields = query.DtoOptions.Fields;
+
+            switch (name)
+            {
+                case ItemFields.HomePageUrl:
+                case ItemFields.Keywords:
+                case ItemFields.DisplayMediaType:
+                case ItemFields.VoteCount:
+                case ItemFields.CustomRating:
+                case ItemFields.ProductionLocations:
+                case ItemFields.Settings:
+                case ItemFields.OriginalTitle:
+                case ItemFields.Taglines:
+                case ItemFields.SortName:
+                case ItemFields.Studios:
+                case ItemFields.Tags:
+                case ItemFields.ThemeSongIds:
+                case ItemFields.ThemeVideoIds:
+                case ItemFields.DateCreated:
+                case ItemFields.Overview:
+                case ItemFields.Genres:
+                case ItemFields.DateLastMediaAdded:
+                case ItemFields.ExternalEtag:
+                case ItemFields.PresentationUniqueKey:
+                case ItemFields.InheritedParentalRatingValue:
+                case ItemFields.ExternalSeriesId:
+                case ItemFields.SeriesPresentationUniqueKey:
+                case ItemFields.DateLastRefreshed:
+                case ItemFields.DateLastSaved:
+                    return fields.Contains(name);
+                case ItemFields.ServiceName:
+                    return true;
+                default:
+                    return true;
+            }
+        }
+
+        private bool HasProgramAttributes(InternalItemsQuery query)
+        {
+            if (query.IncludeItemTypes.Length == 0)
+            {
+                return true;
+            }
+
+            var types = new string[]
+            {
+                "Program",
+                "Recording",
+                "TvChannel",
+                "LiveTvAudioRecording",
+                "LiveTvVideoRecording",
+                "LiveTvProgram",
+                "LiveTvTvChannel"
+            };
+
+            return types.Any(i => query.IncludeItemTypes.Contains(i, StringComparer.OrdinalIgnoreCase));
+        }
+
         private string[] GetFinalColumnsToSelect(InternalItemsQuery query, string[] startColumns)
         private string[] GetFinalColumnsToSelect(InternalItemsQuery query, string[] startColumns)
         {
         {
             var list = startColumns.ToList();
             var list = startColumns.ToList();
 
 
             foreach (var field in allFields)
             foreach (var field in allFields)
             {
             {
-                if (!query.HasField(field))
+                if (!HasField(query, field))
                 {
                 {
                     foreach (var fieldToRemove in GetColumnNamesFromField(field).ToList())
                     foreach (var fieldToRemove in GetColumnNamesFromField(field).ToList())
                     {
                     {
@@ -2274,6 +2346,19 @@ namespace Emby.Server.Implementations.Data
                 }
                 }
             }
             }
 
 
+            if (!HasProgramAttributes(query))
+            {
+                list.Remove("IsKids");
+                list.Remove("IsMovie");
+                list.Remove("IsSports");
+                list.Remove("IsSeries");
+                list.Remove("IsLive");
+                list.Remove("IsNews");
+                list.Remove("IsPremiere");
+                list.Remove("EpisodeTitle");
+                list.Remove("IsRepeat");
+            }
+
             if (!query.DtoOptions.EnableImages)
             if (!query.DtoOptions.EnableImages)
             {
             {
                 list.Remove("Images");
                 list.Remove("Images");
@@ -2400,7 +2485,7 @@ namespace Emby.Server.Implementations.Data
                 query.Limit = query.Limit.Value + 4;
                 query.Limit = query.Limit.Value + 4;
             }
             }
 
 
-            var commandText = "select " + string.Join(",", GetFinalColumnsToSelect(query, new [] { "count(distinct PresentationUniqueKey)" })) + GetFromText();
+            var commandText = "select " + string.Join(",", GetFinalColumnsToSelect(query, new[] { "count(distinct PresentationUniqueKey)" })) + GetFromText();
             commandText += GetJoinUserDataText(query);
             commandText += GetJoinUserDataText(query);
 
 
             var whereClauses = GetWhereClauses(query, null);
             var whereClauses = GetWhereClauses(query, null);
@@ -3671,6 +3756,7 @@ namespace Emby.Server.Implementations.Data
 
 
             if (!string.IsNullOrWhiteSpace(query.SlugName))
             if (!string.IsNullOrWhiteSpace(query.SlugName))
             {
             {
+                Logger.Info("Searching by SlugName for {0}", query.SlugName);
                 whereClauses.Add("CleanName=@SlugName");
                 whereClauses.Add("CleanName=@SlugName");
                 if (statement != null)
                 if (statement != null)
                 {
                 {

+ 9 - 3
Emby.Server.Implementations/Dto/DtoService.cs

@@ -1137,7 +1137,10 @@ namespace Emby.Server.Implementations.Dto
                             return null;
                             return null;
                         }
                         }
 
 
-                        var artist = _libraryManager.GetArtist(i);
+                        var artist = _libraryManager.GetArtist(i, new DtoOptions(false)
+                        {
+                            EnableImages = false
+                        });
                         if (artist != null)
                         if (artist != null)
                         {
                         {
                             return new NameIdPair
                             return new NameIdPair
@@ -1186,7 +1189,10 @@ namespace Emby.Server.Implementations.Dto
                             return null;
                             return null;
                         }
                         }
 
 
-                        var artist = _libraryManager.GetArtist(i);
+                        var artist = _libraryManager.GetArtist(i, new DtoOptions(false)
+                        {
+                            EnableImages = false
+                        });
                         if (artist != null)
                         if (artist != null)
                         {
                         {
                             return new NameIdPair
                             return new NameIdPair
@@ -1456,7 +1462,7 @@ namespace Emby.Server.Implementations.Dto
             var musicAlbum = item as MusicAlbum;
             var musicAlbum = item as MusicAlbum;
             if (musicAlbum != null)
             if (musicAlbum != null)
             {
             {
-                var artist = musicAlbum.MusicArtist;
+                var artist = musicAlbum.GetMusicArtist(new DtoOptions(false));
                 if (artist != null)
                 if (artist != null)
                 {
                 {
                     return artist;
                     return artist;

+ 3 - 5
Emby.Server.Implementations/HttpServer/HttpListenerHost.cs

@@ -8,6 +8,7 @@ using System.IO;
 using System.Linq;
 using System.Linq;
 using System.Reflection;
 using System.Reflection;
 using System.Text;
 using System.Text;
+using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using Emby.Server.Implementations.HttpServer;
 using Emby.Server.Implementations.HttpServer;
 using Emby.Server.Implementations.HttpServer.SocketSharp;
 using Emby.Server.Implementations.HttpServer.SocketSharp;
@@ -445,10 +446,7 @@ namespace Emby.Server.Implementations.HttpServer
         /// <summary>
         /// <summary>
         /// Overridable method that can be used to implement a custom hnandler
         /// Overridable method that can be used to implement a custom hnandler
         /// </summary>
         /// </summary>
-        /// <param name="httpReq">The HTTP req.</param>
-        /// <param name="url">The URL.</param>
-        /// <returns>Task.</returns>
-        protected async Task RequestHandler(IHttpRequest httpReq, Uri url)
+        protected async Task RequestHandler(IHttpRequest httpReq, Uri url, CancellationToken cancellationToken)
         {
         {
             var date = DateTime.Now;
             var date = DateTime.Now;
             var httpRes = httpReq.Response;
             var httpRes = httpReq.Response;
@@ -589,7 +587,7 @@ namespace Emby.Server.Implementations.HttpServer
 
 
                 if (handler != null)
                 if (handler != null)
                 {
                 {
-                    await handler.ProcessRequestAsync(this, httpReq, httpRes, Logger, operationName).ConfigureAwait(false);
+                    await handler.ProcessRequestAsync(this, httpReq, httpRes, Logger, operationName, cancellationToken).ConfigureAwait(false);
                 }
                 }
                 else
                 else
                 {
                 {

+ 2 - 1
Emby.Server.Implementations/HttpServer/IHttpListener.cs

@@ -1,6 +1,7 @@
 using MediaBrowser.Controller.Net;
 using MediaBrowser.Controller.Net;
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
+using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using MediaBrowser.Model.Services;
 using MediaBrowser.Model.Services;
 
 
@@ -18,7 +19,7 @@ namespace Emby.Server.Implementations.HttpServer
         /// Gets or sets the request handler.
         /// Gets or sets the request handler.
         /// </summary>
         /// </summary>
         /// <value>The request handler.</value>
         /// <value>The request handler.</value>
-        Func<IHttpRequest, Uri, Task> RequestHandler { get; set; }
+        Func<IHttpRequest, Uri, CancellationToken, Task> RequestHandler { get; set; }
 
 
         /// <summary>
         /// <summary>
         /// Gets or sets the web socket handler.
         /// Gets or sets the web socket handler.

+ 5 - 4
Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpListener.cs

@@ -4,6 +4,7 @@ using SocketHttpListener.Net;
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
+using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Model.Cryptography;
 using MediaBrowser.Model.Cryptography;
@@ -50,7 +51,7 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
         }
         }
 
 
         public Action<Exception, IRequest, bool> ErrorHandler { get; set; }
         public Action<Exception, IRequest, bool> ErrorHandler { get; set; }
-        public Func<IHttpRequest, Uri, Task> RequestHandler { get; set; }
+        public Func<IHttpRequest, Uri, CancellationToken, Task> RequestHandler { get; set; }
 
 
         public Action<WebSocketConnectingEventArgs> WebSocketConnecting { get; set; }
         public Action<WebSocketConnectingEventArgs> WebSocketConnecting { get; set; }
 
 
@@ -82,10 +83,10 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
         private void ProcessContext(HttpListenerContext context)
         private void ProcessContext(HttpListenerContext context)
         {
         {
             //Task.Factory.StartNew(() => InitTask(context), TaskCreationOptions.DenyChildAttach | TaskCreationOptions.PreferFairness);
             //Task.Factory.StartNew(() => InitTask(context), TaskCreationOptions.DenyChildAttach | TaskCreationOptions.PreferFairness);
-            Task.Run(() => InitTask(context));
+            Task.Run(() => InitTask(context, CancellationToken.None));
         }
         }
 
 
-        private Task InitTask(HttpListenerContext context)
+        private Task InitTask(HttpListenerContext context, CancellationToken cancellationToken)
         {
         {
             IHttpRequest httpReq = null;
             IHttpRequest httpReq = null;
             var request = context.Request;
             var request = context.Request;
@@ -111,7 +112,7 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
                 return Task.FromResult(true);
                 return Task.FromResult(true);
             }
             }
 
 
-            return RequestHandler(httpReq, request.Url);
+            return RequestHandler(httpReq, request.Url, cancellationToken);
         }
         }
 
 
         private void ProcessWebSocketRequest(HttpListenerContext ctx)
         private void ProcessWebSocketRequest(HttpListenerContext ctx)

+ 14 - 57
Emby.Server.Implementations/Library/LibraryManager.cs

@@ -885,7 +885,7 @@ namespace Emby.Server.Implementations.Library
         /// <returns>Task{Person}.</returns>
         /// <returns>Task{Person}.</returns>
         public Person GetPerson(string name)
         public Person GetPerson(string name)
         {
         {
-            return CreateItemByName<Person>(Person.GetPath, name);
+            return CreateItemByName<Person>(Person.GetPath, name, new DtoOptions(true));
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -895,7 +895,7 @@ namespace Emby.Server.Implementations.Library
         /// <returns>Task{Studio}.</returns>
         /// <returns>Task{Studio}.</returns>
         public Studio GetStudio(string name)
         public Studio GetStudio(string name)
         {
         {
-            return CreateItemByName<Studio>(Studio.GetPath, name);
+            return CreateItemByName<Studio>(Studio.GetPath, name, new DtoOptions(true));
         }
         }
 
 
         public Guid GetStudioId(string name)
         public Guid GetStudioId(string name)
@@ -925,7 +925,7 @@ namespace Emby.Server.Implementations.Library
         /// <returns>Task{Genre}.</returns>
         /// <returns>Task{Genre}.</returns>
         public Genre GetGenre(string name)
         public Genre GetGenre(string name)
         {
         {
-            return CreateItemByName<Genre>(Genre.GetPath, name);
+            return CreateItemByName<Genre>(Genre.GetPath, name, new DtoOptions(true));
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -935,7 +935,7 @@ namespace Emby.Server.Implementations.Library
         /// <returns>Task{MusicGenre}.</returns>
         /// <returns>Task{MusicGenre}.</returns>
         public MusicGenre GetMusicGenre(string name)
         public MusicGenre GetMusicGenre(string name)
         {
         {
-            return CreateItemByName<MusicGenre>(MusicGenre.GetPath, name);
+            return CreateItemByName<MusicGenre>(MusicGenre.GetPath, name, new DtoOptions(true));
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -945,7 +945,7 @@ namespace Emby.Server.Implementations.Library
         /// <returns>Task{GameGenre}.</returns>
         /// <returns>Task{GameGenre}.</returns>
         public GameGenre GetGameGenre(string name)
         public GameGenre GetGameGenre(string name)
         {
         {
-            return CreateItemByName<GameGenre>(GameGenre.GetPath, name);
+            return CreateItemByName<GameGenre>(GameGenre.GetPath, name, new DtoOptions(true));
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -963,7 +963,7 @@ namespace Emby.Server.Implementations.Library
 
 
             var name = value.ToString(CultureInfo.InvariantCulture);
             var name = value.ToString(CultureInfo.InvariantCulture);
 
 
-            return CreateItemByName<Year>(Year.GetPath, name);
+            return CreateItemByName<Year>(Year.GetPath, name, new DtoOptions(true));
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -973,10 +973,15 @@ namespace Emby.Server.Implementations.Library
         /// <returns>Task{Genre}.</returns>
         /// <returns>Task{Genre}.</returns>
         public MusicArtist GetArtist(string name)
         public MusicArtist GetArtist(string name)
         {
         {
-            return CreateItemByName<MusicArtist>(MusicArtist.GetPath, name);
+            return GetArtist(name, new DtoOptions(true));
         }
         }
 
 
-        private T CreateItemByName<T>(Func<string, string> getPathFn, string name)
+        public MusicArtist GetArtist(string name, DtoOptions options)
+        {
+            return CreateItemByName<MusicArtist>(MusicArtist.GetPath, name, options);
+        }
+
+        private T CreateItemByName<T>(Func<string, string> getPathFn, string name, DtoOptions options)
             where T : BaseItem, new()
             where T : BaseItem, new()
         {
         {
             if (typeof(T) == typeof(MusicArtist))
             if (typeof(T) == typeof(MusicArtist))
@@ -985,7 +990,7 @@ namespace Emby.Server.Implementations.Library
                 {
                 {
                     IncludeItemTypes = new[] { typeof(T).Name },
                     IncludeItemTypes = new[] { typeof(T).Name },
                     Name = name,
                     Name = name,
-                    DtoOptions = new DtoOptions(true)
+                    DtoOptions = options
 
 
                 }).Cast<MusicArtist>()
                 }).Cast<MusicArtist>()
                 .OrderBy(i => i.IsAccessedByName ? 1 : 0)
                 .OrderBy(i => i.IsAccessedByName ? 1 : 0)
@@ -1029,54 +1034,6 @@ namespace Emby.Server.Implementations.Library
             return GetNewItemIdInternal(path, typeof(T), forceCaseInsensitiveId);
             return GetNewItemIdInternal(path, typeof(T), forceCaseInsensitiveId);
         }
         }
 
 
-        public IEnumerable<MusicArtist> GetAlbumArtists(IEnumerable<IHasAlbumArtist> items)
-        {
-            var names = items
-                .SelectMany(i => i.AlbumArtists)
-                .DistinctNames()
-                .Select(i =>
-                {
-                    try
-                    {
-                        var artist = GetArtist(i);
-
-                        return artist;
-                    }
-                    catch
-                    {
-                        // Already logged at lower levels
-                        return null;
-                    }
-                })
-                .Where(i => i != null);
-
-            return names;
-        }
-
-        public IEnumerable<MusicArtist> GetArtists(IEnumerable<IHasArtist> items)
-        {
-            var names = items
-                .SelectMany(i => i.AllArtists)
-                .DistinctNames()
-                .Select(i =>
-                {
-                    try
-                    {
-                        var artist = GetArtist(i);
-
-                        return artist;
-                    }
-                    catch
-                    {
-                        // Already logged at lower levels
-                        return null;
-                    }
-                })
-                .Where(i => i != null);
-
-            return names;
-        }
-
         /// <summary>
         /// <summary>
         /// Validate and refresh the People sub-set of the IBN.
         /// Validate and refresh the People sub-set of the IBN.
         /// The items are stored in the db but not loaded into memory until actually requested by an operation.
         /// The items are stored in the db but not loaded into memory until actually requested by an operation.

+ 1 - 2
Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs

@@ -1234,8 +1234,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
                     Protocol = MediaBrowser.Model.MediaInfo.MediaProtocol.Http,
                     Protocol = MediaBrowser.Model.MediaInfo.MediaProtocol.Http,
                     BufferMs = 0,
                     BufferMs = 0,
                     IgnoreDts = true,
                     IgnoreDts = true,
-                    IgnoreIndex = true,
-                    GenPtsInput = true
+                    IgnoreIndex = true
                 };
                 };
 
 
                 var isAudio = false;
                 var isAudio = false;

+ 1 - 1
Emby.Server.Implementations/LiveTv/LiveTvManager.cs

@@ -479,7 +479,7 @@ namespace Emby.Server.Implementations.LiveTv
             if (!(service is EmbyTV.EmbyTV))
             if (!(service is EmbyTV.EmbyTV))
             {
             {
                 // We can't trust that we'll be able to direct stream it through emby server, no matter what the provider says
                 // We can't trust that we'll be able to direct stream it through emby server, no matter what the provider says
-                mediaSource.SupportsDirectPlay = false;
+                //mediaSource.SupportsDirectPlay = false;
                 mediaSource.SupportsDirectStream = false;
                 mediaSource.SupportsDirectStream = false;
                 mediaSource.SupportsTranscoding = true;
                 mediaSource.SupportsTranscoding = true;
                 foreach (var stream in mediaSource.MediaStreams)
                 foreach (var stream in mediaSource.MediaStreams)

+ 7 - 9
Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs

@@ -422,8 +422,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
                 SupportsTranscoding = true,
                 SupportsTranscoding = true,
                 IsInfiniteStream = true,
                 IsInfiniteStream = true,
                 IgnoreDts = true,
                 IgnoreDts = true,
-                IgnoreIndex = true,
-                GenPtsInput = true
+                IgnoreIndex = true
             };
             };
 
 
             mediaSource.InferTotalBitrate();
             mediaSource.InferTotalBitrate();
@@ -507,12 +506,12 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
 
 
             if (hdhomerunChannel != null && hdhomerunChannel.IsLegacyTuner)
             if (hdhomerunChannel != null && hdhomerunChannel.IsLegacyTuner)
             {
             {
-                return new HdHomerunUdpStream(mediaSource, streamId, new LegacyHdHomerunChannelCommands(hdhomerunChannel.Url), modelInfo.TunerCount, _fileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost, _socketFactory, _networkManager);
+                return new HdHomerunUdpStream(mediaSource, streamId, new LegacyHdHomerunChannelCommands(hdhomerunChannel.Url), modelInfo.TunerCount, _fileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost, _socketFactory, _networkManager, _environment);
             }
             }
 
 
             // The UDP method is not working reliably on OSX, and on BSD it hasn't been tested yet
             // The UDP method is not working reliably on OSX, and on BSD it hasn't been tested yet
-            var enableHttpStream = _environment.OperatingSystem == OperatingSystem.OSX ||
-                _environment.OperatingSystem == OperatingSystem.BSD;
+            var enableHttpStream = _environment.OperatingSystem == OperatingSystem.OSX 
+                || _environment.OperatingSystem == OperatingSystem.BSD;
             enableHttpStream = true;
             enableHttpStream = true;
             if (enableHttpStream)
             if (enableHttpStream)
             {
             {
@@ -521,17 +520,16 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
                 var httpUrl = GetApiUrl(info, true) + "/auto/v" + hdhrId;
                 var httpUrl = GetApiUrl(info, true) + "/auto/v" + hdhrId;
 
 
                 // If raw was used, the tuner doesn't support params
                 // If raw was used, the tuner doesn't support params
-                if (!string.IsNullOrWhiteSpace(profile)
-                    && !string.Equals(profile, "native", StringComparison.OrdinalIgnoreCase))
+                if (!string.IsNullOrWhiteSpace(profile) && !string.Equals(profile, "native", StringComparison.OrdinalIgnoreCase))
                 {
                 {
                     httpUrl += "?transcode=" + profile;
                     httpUrl += "?transcode=" + profile;
                 }
                 }
                 mediaSource.Path = httpUrl;
                 mediaSource.Path = httpUrl;
 
 
-                return new HdHomerunHttpStream(mediaSource, streamId, _fileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost);
+                return new HdHomerunHttpStream(mediaSource, streamId, _fileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost, _environment);
             }
             }
 
 
-            return new HdHomerunUdpStream(mediaSource, streamId, new HdHomerunChannelCommands(hdhomerunChannel.Number), modelInfo.TunerCount, _fileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost, _socketFactory, _networkManager);
+            return new HdHomerunUdpStream(mediaSource, streamId, new HdHomerunChannelCommands(hdhomerunChannel.Number, profile), modelInfo.TunerCount, _fileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost, _socketFactory, _networkManager, _environment);
         }
         }
 
 
         public async Task Validate(TunerHostInfo info)
         public async Task Validate(TunerHostInfo info)

+ 25 - 17
Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHttpStream.cs

@@ -10,6 +10,7 @@ using MediaBrowser.Controller.Library;
 using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.Logging;
 using MediaBrowser.Model.Logging;
 using MediaBrowser.Model.MediaInfo;
 using MediaBrowser.Model.MediaInfo;
+using MediaBrowser.Model.System;
 
 
 namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
 namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
 {
 {
@@ -17,24 +18,22 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
     {
     {
         private readonly ILogger _logger;
         private readonly ILogger _logger;
         private readonly IHttpClient _httpClient;
         private readonly IHttpClient _httpClient;
-        private readonly IFileSystem _fileSystem;
-        private readonly IServerApplicationPaths _appPaths;
         private readonly IServerApplicationHost _appHost;
         private readonly IServerApplicationHost _appHost;
 
 
         private readonly CancellationTokenSource _liveStreamCancellationTokenSource = new CancellationTokenSource();
         private readonly CancellationTokenSource _liveStreamCancellationTokenSource = new CancellationTokenSource();
         private readonly TaskCompletionSource<bool> _liveStreamTaskCompletionSource = new TaskCompletionSource<bool>();
         private readonly TaskCompletionSource<bool> _liveStreamTaskCompletionSource = new TaskCompletionSource<bool>();
-        private readonly MulticastStream _multicastStream;
 
 
-        public HdHomerunHttpStream(MediaSourceInfo mediaSource, string originalStreamId, IFileSystem fileSystem, IHttpClient httpClient, ILogger logger, IServerApplicationPaths appPaths, IServerApplicationHost appHost)
-            : base(mediaSource)
+        private readonly string _tempFilePath;
+
+        public HdHomerunHttpStream(MediaSourceInfo mediaSource, string originalStreamId, IFileSystem fileSystem, IHttpClient httpClient, ILogger logger, IServerApplicationPaths appPaths, IServerApplicationHost appHost, IEnvironmentInfo environment)
+            : base(mediaSource, environment, fileSystem)
         {
         {
-            _fileSystem = fileSystem;
             _httpClient = httpClient;
             _httpClient = httpClient;
             _logger = logger;
             _logger = logger;
-            _appPaths = appPaths;
             _appHost = appHost;
             _appHost = appHost;
             OriginalStreamId = originalStreamId;
             OriginalStreamId = originalStreamId;
-            _multicastStream = new MulticastStream(_logger);
+
+            _tempFilePath = Path.Combine(appPaths.TranscodingTempPath, UniqueId + ".ts");
         }
         }
 
 
         protected override async Task OpenInternal(CancellationToken openCancellationToken)
         protected override async Task OpenInternal(CancellationToken openCancellationToken)
@@ -74,9 +73,9 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
             return _liveStreamTaskCompletionSource.Task;
             return _liveStreamTaskCompletionSource.Task;
         }
         }
 
 
-        private async Task StartStreaming(string url, TaskCompletionSource<bool> openTaskCompletionSource, CancellationToken cancellationToken)
+        private Task StartStreaming(string url, TaskCompletionSource<bool> openTaskCompletionSource, CancellationToken cancellationToken)
         {
         {
-            await Task.Run(async () =>
+            return Task.Run(async () =>
             {
             {
                 var isFirstAttempt = true;
                 var isFirstAttempt = true;
 
 
@@ -101,13 +100,13 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
                             {
                             {
                                 _logger.Info("Beginning multicastStream.CopyUntilCancelled");
                                 _logger.Info("Beginning multicastStream.CopyUntilCancelled");
 
 
-                                Action onStarted = null;
-                                if (isFirstAttempt)
+                                FileSystem.CreateDirectory(FileSystem.GetDirectoryName(_tempFilePath));
+                                using (var fileStream = FileSystem.GetFileStream(_tempFilePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, FileOpenOptions.Asynchronous | FileOpenOptions.SequentialScan))
                                 {
                                 {
-                                    onStarted = () => openTaskCompletionSource.TrySetResult(true);
-                                }
+                                    ResolveAfterDelay(2000, openTaskCompletionSource);
 
 
-                                await _multicastStream.CopyUntilCancelled(response.Content, onStarted, cancellationToken).ConfigureAwait(false);
+                                    await response.Content.CopyToAsync(fileStream, 81920, cancellationToken).ConfigureAwait(false);
+                                }
                             }
                             }
                         }
                         }
                     }
                     }
@@ -131,13 +130,22 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
                 }
                 }
 
 
                 _liveStreamTaskCompletionSource.TrySetResult(true);
                 _liveStreamTaskCompletionSource.TrySetResult(true);
+                await DeleteTempFile(_tempFilePath).ConfigureAwait(false);
+            });
+        }
 
 
-            }).ConfigureAwait(false);
+        private void ResolveAfterDelay(int delayMs, TaskCompletionSource<bool> openTaskCompletionSource)
+        {
+            Task.Run(async () =>
+            {
+                await Task.Delay(delayMs).ConfigureAwait(false);
+                openTaskCompletionSource.TrySetResult(true);
+            });
         }
         }
 
 
         public Task CopyToAsync(Stream stream, CancellationToken cancellationToken)
         public Task CopyToAsync(Stream stream, CancellationToken cancellationToken)
         {
         {
-            return _multicastStream.CopyToAsync(stream , cancellationToken);
+            return CopyFileTo(_tempFilePath, false, stream, cancellationToken);
         }
         }
     }
     }
 }
 }

+ 13 - 2
Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs

@@ -46,10 +46,12 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
     public class HdHomerunChannelCommands : IHdHomerunChannelCommands
     public class HdHomerunChannelCommands : IHdHomerunChannelCommands
     {
     {
         private string _channel;
         private string _channel;
+        private string _profile;
 
 
-        public HdHomerunChannelCommands(string channel)
+        public HdHomerunChannelCommands(string channel, string profile)
         {
         {
             _channel = channel;
             _channel = channel;
+            _profile = profile;
         }
         }
 
 
         public IEnumerable<Tuple<string, string>> GetCommands()
         public IEnumerable<Tuple<string, string>> GetCommands()
@@ -57,7 +59,16 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
             var commands = new List<Tuple<string, string>>();
             var commands = new List<Tuple<string, string>>();
 
 
             if (!String.IsNullOrEmpty(_channel))
             if (!String.IsNullOrEmpty(_channel))
-                commands.Add(Tuple.Create("vchannel", _channel));
+            {
+                if (!string.IsNullOrWhiteSpace(_profile) && !string.Equals(_profile, "native", StringComparison.OrdinalIgnoreCase))
+                {
+                    commands.Add(Tuple.Create("vchannel", String.Format("{0} transcode={1}", _channel, _profile)));
+                }
+                else
+                {
+                    commands.Add(Tuple.Create("vchannel", _channel));
+                }
+            }
 
 
             return commands;
             return commands;
         }
         }

+ 31 - 24
Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs

@@ -14,39 +14,35 @@ using MediaBrowser.Model.IO;
 using MediaBrowser.Model.Logging;
 using MediaBrowser.Model.Logging;
 using MediaBrowser.Model.MediaInfo;
 using MediaBrowser.Model.MediaInfo;
 using MediaBrowser.Model.Net;
 using MediaBrowser.Model.Net;
+using MediaBrowser.Model.System;
 
 
 namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
 namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
 {
 {
     public class HdHomerunUdpStream : LiveStream, IDirectStreamProvider
     public class HdHomerunUdpStream : LiveStream, IDirectStreamProvider
     {
     {
         private readonly ILogger _logger;
         private readonly ILogger _logger;
-        private readonly IHttpClient _httpClient;
-        private readonly IFileSystem _fileSystem;
-        private readonly IServerApplicationPaths _appPaths;
         private readonly IServerApplicationHost _appHost;
         private readonly IServerApplicationHost _appHost;
         private readonly ISocketFactory _socketFactory;
         private readonly ISocketFactory _socketFactory;
 
 
         private readonly CancellationTokenSource _liveStreamCancellationTokenSource = new CancellationTokenSource();
         private readonly CancellationTokenSource _liveStreamCancellationTokenSource = new CancellationTokenSource();
         private readonly TaskCompletionSource<bool> _liveStreamTaskCompletionSource = new TaskCompletionSource<bool>();
         private readonly TaskCompletionSource<bool> _liveStreamTaskCompletionSource = new TaskCompletionSource<bool>();
-        private readonly MulticastStream _multicastStream;
         private readonly IHdHomerunChannelCommands _channelCommands;
         private readonly IHdHomerunChannelCommands _channelCommands;
         private readonly int _numTuners;
         private readonly int _numTuners;
         private readonly INetworkManager _networkManager;
         private readonly INetworkManager _networkManager;
 
 
-        public HdHomerunUdpStream(MediaSourceInfo mediaSource, string originalStreamId, IHdHomerunChannelCommands channelCommands, int numTuners, IFileSystem fileSystem, IHttpClient httpClient, ILogger logger, IServerApplicationPaths appPaths, IServerApplicationHost appHost, ISocketFactory socketFactory, INetworkManager networkManager)
-            : base(mediaSource)
+        private readonly string _tempFilePath;
+
+        public HdHomerunUdpStream(MediaSourceInfo mediaSource, string originalStreamId, IHdHomerunChannelCommands channelCommands, int numTuners, IFileSystem fileSystem, IHttpClient httpClient, ILogger logger, IServerApplicationPaths appPaths, IServerApplicationHost appHost, ISocketFactory socketFactory, INetworkManager networkManager, IEnvironmentInfo environment)
+            : base(mediaSource, environment, fileSystem)
         {
         {
-            _fileSystem = fileSystem;
-            _httpClient = httpClient;
             _logger = logger;
             _logger = logger;
-            _appPaths = appPaths;
             _appHost = appHost;
             _appHost = appHost;
             _socketFactory = socketFactory;
             _socketFactory = socketFactory;
             _networkManager = networkManager;
             _networkManager = networkManager;
             OriginalStreamId = originalStreamId;
             OriginalStreamId = originalStreamId;
-            _multicastStream = new MulticastStream(_logger);
             _channelCommands = channelCommands;
             _channelCommands = channelCommands;
             _numTuners = numTuners;
             _numTuners = numTuners;
+            _tempFilePath = Path.Combine(appPaths.TranscodingTempPath, UniqueId + ".ts");
         }
         }
 
 
         protected override async Task OpenInternal(CancellationToken openCancellationToken)
         protected override async Task OpenInternal(CancellationToken openCancellationToken)
@@ -87,9 +83,9 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
             return _liveStreamTaskCompletionSource.Task;
             return _liveStreamTaskCompletionSource.Task;
         }
         }
 
 
-        private async Task StartStreaming(string remoteIp, int localPort, TaskCompletionSource<bool> openTaskCompletionSource, CancellationToken cancellationToken)
+        private Task StartStreaming(string remoteIp, int localPort, TaskCompletionSource<bool> openTaskCompletionSource, CancellationToken cancellationToken)
         {
         {
-            await Task.Run(async () =>
+            return Task.Run(async () =>
             {
             {
                 var isFirstAttempt = true;
                 var isFirstAttempt = true;
                 using (var udpClient = _socketFactory.CreateUdpSocket(localPort))
                 using (var udpClient = _socketFactory.CreateUdpSocket(localPort))
@@ -124,13 +120,13 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
 
 
                                 if (!cancellationToken.IsCancellationRequested)
                                 if (!cancellationToken.IsCancellationRequested)
                                 {
                                 {
-                                    Action onStarted = null;
-                                    if (isFirstAttempt)
+                                    FileSystem.CreateDirectory(FileSystem.GetDirectoryName(_tempFilePath));
+                                    using (var fileStream = FileSystem.GetFileStream(_tempFilePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, FileOpenOptions.Asynchronous | FileOpenOptions.SequentialScan))
                                     {
                                     {
-                                        onStarted = () => openTaskCompletionSource.TrySetResult(true);
-                                    }
+                                        ResolveAfterDelay(2000, openTaskCompletionSource);
 
 
-                                    await _multicastStream.CopyUntilCancelled(new UdpClientStream(udpClient), onStarted, cancellationToken).ConfigureAwait(false);
+                                        await new UdpClientStream(udpClient).CopyToAsync(fileStream, 81920, cancellationToken).ConfigureAwait(false);
+                                    }
                                 }
                                 }
                             }
                             }
                             catch (OperationCanceledException ex)
                             catch (OperationCanceledException ex)
@@ -159,12 +155,22 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
                     }
                     }
                 }
                 }
 
 
-            }).ConfigureAwait(false);
+                await DeleteTempFile(_tempFilePath).ConfigureAwait(false);
+            });
+        }
+
+        private void ResolveAfterDelay(int delayMs, TaskCompletionSource<bool> openTaskCompletionSource)
+        {
+            Task.Run(async () =>
+            {
+                await Task.Delay(delayMs).ConfigureAwait(false);
+                openTaskCompletionSource.TrySetResult(true);
+            });
         }
         }
 
 
         public Task CopyToAsync(Stream stream, CancellationToken cancellationToken)
         public Task CopyToAsync(Stream stream, CancellationToken cancellationToken)
         {
         {
-            return _multicastStream.CopyToAsync(stream, cancellationToken);
+            return CopyFileTo(_tempFilePath, false, stream, cancellationToken);
         }
         }
     }
     }
 
 
@@ -198,7 +204,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
 
 
             // This will always receive a 1328 packet size (PacketSize + RtpHeaderSize)
             // This will always receive a 1328 packet size (PacketSize + RtpHeaderSize)
             // The RTP header will be stripped so see how many reads we need to make to fill the buffer.
             // The RTP header will be stripped so see how many reads we need to make to fill the buffer.
-            int numReads = count / PacketSize;
+            var numReads = count / PacketSize;
+
             int totalBytesRead = 0;
             int totalBytesRead = 0;
 
 
             for (int i = 0; i < numReads; ++i)
             for (int i = 0; i < numReads; ++i)
@@ -206,7 +213,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
                 var data = await _udpClient.ReceiveAsync(cancellationToken).ConfigureAwait(false);
                 var data = await _udpClient.ReceiveAsync(cancellationToken).ConfigureAwait(false);
 
 
                 var bytesRead = data.ReceivedBytes - RtpHeaderBytes;
                 var bytesRead = data.ReceivedBytes - RtpHeaderBytes;
-                
+
                 // remove rtp header
                 // remove rtp header
                 Buffer.BlockCopy(data.Buffer, RtpHeaderBytes, buffer, offset, bytesRead);
                 Buffer.BlockCopy(data.Buffer, RtpHeaderBytes, buffer, offset, bytesRead);
                 offset += bytesRead;
                 offset += bytesRead;
@@ -224,7 +231,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
         {
         {
             get
             get
             {
             {
-                throw new NotImplementedException();
+                return true;
             }
             }
         }
         }
 
 
@@ -232,7 +239,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
         {
         {
             get
             get
             {
             {
-                throw new NotImplementedException();
+                return false;
             }
             }
         }
         }
 
 
@@ -240,7 +247,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
         {
         {
             get
             get
             {
             {
-                throw new NotImplementedException();
+                return false;
             }
             }
         }
         }
 
 

+ 5 - 2
Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs

@@ -19,6 +19,7 @@ using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.IO;
 using MediaBrowser.Controller.IO;
 using MediaBrowser.Controller.MediaEncoding;
 using MediaBrowser.Controller.MediaEncoding;
 using MediaBrowser.Model.Serialization;
 using MediaBrowser.Model.Serialization;
+using MediaBrowser.Model.System;
 
 
 namespace Emby.Server.Implementations.LiveTv.TunerHosts
 namespace Emby.Server.Implementations.LiveTv.TunerHosts
 {
 {
@@ -27,13 +28,15 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
         private readonly IFileSystem _fileSystem;
         private readonly IFileSystem _fileSystem;
         private readonly IHttpClient _httpClient;
         private readonly IHttpClient _httpClient;
         private readonly IServerApplicationHost _appHost;
         private readonly IServerApplicationHost _appHost;
+        private readonly IEnvironmentInfo _environment;
 
 
-        public M3UTunerHost(IServerConfigurationManager config, ILogger logger, IJsonSerializer jsonSerializer, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IHttpClient httpClient, IServerApplicationHost appHost)
+        public M3UTunerHost(IServerConfigurationManager config, ILogger logger, IJsonSerializer jsonSerializer, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IHttpClient httpClient, IServerApplicationHost appHost, IEnvironmentInfo environment)
             : base(config, logger, jsonSerializer, mediaEncoder)
             : base(config, logger, jsonSerializer, mediaEncoder)
         {
         {
             _fileSystem = fileSystem;
             _fileSystem = fileSystem;
             _httpClient = httpClient;
             _httpClient = httpClient;
             _appHost = appHost;
             _appHost = appHost;
+            _environment = environment;
         }
         }
 
 
         public override string Type
         public override string Type
@@ -73,7 +76,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
         {
         {
             var sources = await GetChannelStreamMediaSources(info, channelId, cancellationToken).ConfigureAwait(false);
             var sources = await GetChannelStreamMediaSources(info, channelId, cancellationToken).ConfigureAwait(false);
 
 
-            var liveStream = new LiveStream(sources.First());
+            var liveStream = new LiveStream(sources.First(), _environment, _fileSystem);
             return liveStream;
             return liveStream;
         }
         }
 
 

+ 6 - 10
Emby.Server.Implementations/Services/ResponseHelper.cs

@@ -12,7 +12,7 @@ namespace Emby.Server.Implementations.Services
 {
 {
     public static class ResponseHelper
     public static class ResponseHelper
     {
     {
-        public static Task WriteToResponse(IResponse httpRes, IRequest httpReq, object result)
+        public static Task WriteToResponse(IResponse httpRes, IRequest httpReq, object result, CancellationToken cancellationToken)
         {
         {
             if (result == null)
             if (result == null)
             {
             {
@@ -30,21 +30,17 @@ namespace Emby.Server.Implementations.Services
             {
             {
                 httpResult.RequestContext = httpReq;
                 httpResult.RequestContext = httpReq;
                 httpReq.ResponseContentType = httpResult.ContentType ?? httpReq.ResponseContentType;
                 httpReq.ResponseContentType = httpResult.ContentType ?? httpReq.ResponseContentType;
-                return WriteToResponseInternal(httpRes, httpResult, httpReq);
+                return WriteToResponseInternal(httpRes, httpResult, httpReq, cancellationToken);
             }
             }
 
 
-            return WriteToResponseInternal(httpRes, result, httpReq);
+            return WriteToResponseInternal(httpRes, result, httpReq, cancellationToken);
         }
         }
 
 
         /// <summary>
         /// <summary>
         /// Writes to response.
         /// Writes to response.
         /// Response headers are customizable by implementing IHasHeaders an returning Dictionary of Http headers.
         /// Response headers are customizable by implementing IHasHeaders an returning Dictionary of Http headers.
         /// </summary>
         /// </summary>
-        /// <param name="response">The response.</param>
-        /// <param name="result">Whether or not it was implicity handled by ServiceStack's built-in handlers.</param>
-        /// <param name="request">The serialization context.</param>
-        /// <returns></returns>
-        private static async Task WriteToResponseInternal(IResponse response, object result, IRequest request)
+        private static async Task WriteToResponseInternal(IResponse response, object result, IRequest request, CancellationToken cancellationToken)
         {
         {
             var defaultContentType = request.ResponseContentType;
             var defaultContentType = request.ResponseContentType;
 
 
@@ -105,7 +101,7 @@ namespace Emby.Server.Implementations.Services
             var asyncStreamWriter = result as IAsyncStreamWriter;
             var asyncStreamWriter = result as IAsyncStreamWriter;
             if (asyncStreamWriter != null)
             if (asyncStreamWriter != null)
             {
             {
-                await asyncStreamWriter.WriteToAsync(response.OutputStream, CancellationToken.None).ConfigureAwait(false);
+                await asyncStreamWriter.WriteToAsync(response.OutputStream, cancellationToken).ConfigureAwait(false);
                 return;
                 return;
             }
             }
 
 
@@ -119,7 +115,7 @@ namespace Emby.Server.Implementations.Services
             var fileWriter = result as FileWriter;
             var fileWriter = result as FileWriter;
             if (fileWriter != null)
             if (fileWriter != null)
             {
             {
-                await fileWriter.WriteToAsync(response, CancellationToken.None).ConfigureAwait(false);
+                await fileWriter.WriteToAsync(response, cancellationToken).ConfigureAwait(false);
                 return;
                 return;
             }
             }
 
 

+ 3 - 2
Emby.Server.Implementations/Services/ServiceHandler.cs

@@ -1,6 +1,7 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Reflection;
 using System.Reflection;
+using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using Emby.Server.Implementations.HttpServer;
 using Emby.Server.Implementations.HttpServer;
 using MediaBrowser.Model.Logging;
 using MediaBrowser.Model.Logging;
@@ -123,7 +124,7 @@ namespace Emby.Server.Implementations.Services
         // Set from SSHHF.GetHandlerForPathInfo()
         // Set from SSHHF.GetHandlerForPathInfo()
         public string ResponseContentType { get; set; }
         public string ResponseContentType { get; set; }
 
 
-        public async Task ProcessRequestAsync(HttpListenerHost appHost, IRequest httpReq, IResponse httpRes, ILogger logger, string operationName)
+        public async Task ProcessRequestAsync(HttpListenerHost appHost, IRequest httpReq, IResponse httpRes, ILogger logger, string operationName, CancellationToken cancellationToken)
         {
         {
             var restPath = GetRestPath(httpReq.Verb, httpReq.PathInfo);
             var restPath = GetRestPath(httpReq.Verb, httpReq.PathInfo);
             if (restPath == null)
             if (restPath == null)
@@ -150,7 +151,7 @@ namespace Emby.Server.Implementations.Services
                 responseFilter(httpReq, httpRes, response);
                 responseFilter(httpReq, httpRes, response);
             }
             }
 
 
-            await ResponseHelper.WriteToResponse(httpRes, httpReq, response).ConfigureAwait(false);
+            await ResponseHelper.WriteToResponse(httpRes, httpReq, response, cancellationToken).ConfigureAwait(false);
         }
         }
 
 
         public static object CreateRequest(HttpListenerHost host, IRequest httpReq, RestPath restPath, ILogger logger)
         public static object CreateRequest(HttpListenerHost host, IRequest httpReq, RestPath restPath, ILogger logger)

+ 26 - 24
MediaBrowser.Api/BaseApiService.cs

@@ -174,14 +174,15 @@ namespace MediaBrowser.Api
             return options;
             return options;
         }
         }
 
 
-        protected MusicArtist GetArtist(string name, ILibraryManager libraryManager)
+        protected MusicArtist GetArtist(string name, ILibraryManager libraryManager, DtoOptions dtoOptions)
         {
         {
             if (name.IndexOf(BaseItem.SlugChar) != -1)
             if (name.IndexOf(BaseItem.SlugChar) != -1)
             {
             {
                 var result = libraryManager.GetItemList(new InternalItemsQuery
                 var result = libraryManager.GetItemList(new InternalItemsQuery
                 {
                 {
                     SlugName = name,
                     SlugName = name,
-                    IncludeItemTypes = new[] { typeof(MusicArtist).Name }
+                    IncludeItemTypes = new[] { typeof(MusicArtist).Name },
+                    DtoOptions = dtoOptions
 
 
                 }).OfType<MusicArtist>().FirstOrDefault();
                 }).OfType<MusicArtist>().FirstOrDefault();
 
 
@@ -191,17 +192,18 @@ namespace MediaBrowser.Api
                 }
                 }
             }
             }
 
 
-            return libraryManager.GetArtist(name);
+            return libraryManager.GetArtist(name, dtoOptions);
         }
         }
 
 
-        protected Studio GetStudio(string name, ILibraryManager libraryManager)
+        protected Studio GetStudio(string name, ILibraryManager libraryManager, DtoOptions dtoOptions)
         {
         {
             if (name.IndexOf(BaseItem.SlugChar) != -1)
             if (name.IndexOf(BaseItem.SlugChar) != -1)
             {
             {
                 var result = libraryManager.GetItemList(new InternalItemsQuery
                 var result = libraryManager.GetItemList(new InternalItemsQuery
                 {
                 {
                     SlugName = name,
                     SlugName = name,
-                    IncludeItemTypes = new[] { typeof(Studio).Name }
+                    IncludeItemTypes = new[] { typeof(Studio).Name },
+                    DtoOptions = dtoOptions
 
 
                 }).OfType<Studio>().FirstOrDefault();
                 }).OfType<Studio>().FirstOrDefault();
 
 
@@ -214,14 +216,15 @@ namespace MediaBrowser.Api
             return libraryManager.GetStudio(name);
             return libraryManager.GetStudio(name);
         }
         }
 
 
-        protected Genre GetGenre(string name, ILibraryManager libraryManager)
+        protected Genre GetGenre(string name, ILibraryManager libraryManager, DtoOptions dtoOptions)
         {
         {
             if (name.IndexOf(BaseItem.SlugChar) != -1)
             if (name.IndexOf(BaseItem.SlugChar) != -1)
             {
             {
                 var result = libraryManager.GetItemList(new InternalItemsQuery
                 var result = libraryManager.GetItemList(new InternalItemsQuery
                 {
                 {
                     SlugName = name,
                     SlugName = name,
-                    IncludeItemTypes = new[] { typeof(Genre).Name }
+                    IncludeItemTypes = new[] { typeof(Genre).Name },
+                    DtoOptions = dtoOptions
 
 
                 }).OfType<Genre>().FirstOrDefault();
                 }).OfType<Genre>().FirstOrDefault();
 
 
@@ -234,14 +237,15 @@ namespace MediaBrowser.Api
             return libraryManager.GetGenre(name);
             return libraryManager.GetGenre(name);
         }
         }
 
 
-        protected MusicGenre GetMusicGenre(string name, ILibraryManager libraryManager)
+        protected MusicGenre GetMusicGenre(string name, ILibraryManager libraryManager, DtoOptions dtoOptions)
         {
         {
             if (name.IndexOf(BaseItem.SlugChar) != -1)
             if (name.IndexOf(BaseItem.SlugChar) != -1)
             {
             {
                 var result = libraryManager.GetItemList(new InternalItemsQuery
                 var result = libraryManager.GetItemList(new InternalItemsQuery
                 {
                 {
                     SlugName = name,
                     SlugName = name,
-                    IncludeItemTypes = new[] { typeof(MusicGenre).Name }
+                    IncludeItemTypes = new[] { typeof(MusicGenre).Name },
+                    DtoOptions = dtoOptions
 
 
                 }).OfType<MusicGenre>().FirstOrDefault();
                 }).OfType<MusicGenre>().FirstOrDefault();
 
 
@@ -254,14 +258,15 @@ namespace MediaBrowser.Api
             return libraryManager.GetMusicGenre(name);
             return libraryManager.GetMusicGenre(name);
         }
         }
 
 
-        protected GameGenre GetGameGenre(string name, ILibraryManager libraryManager)
+        protected GameGenre GetGameGenre(string name, ILibraryManager libraryManager, DtoOptions dtoOptions)
         {
         {
             if (name.IndexOf(BaseItem.SlugChar) != -1)
             if (name.IndexOf(BaseItem.SlugChar) != -1)
             {
             {
                 var result = libraryManager.GetItemList(new InternalItemsQuery
                 var result = libraryManager.GetItemList(new InternalItemsQuery
                 {
                 {
                     SlugName = name,
                     SlugName = name,
-                    IncludeItemTypes = new[] { typeof(GameGenre).Name }
+                    IncludeItemTypes = new[] { typeof(GameGenre).Name },
+                    DtoOptions = dtoOptions
 
 
                 }).OfType<GameGenre>().FirstOrDefault();
                 }).OfType<GameGenre>().FirstOrDefault();
 
 
@@ -274,14 +279,15 @@ namespace MediaBrowser.Api
             return libraryManager.GetGameGenre(name);
             return libraryManager.GetGameGenre(name);
         }
         }
 
 
-        protected Person GetPerson(string name, ILibraryManager libraryManager)
+        protected Person GetPerson(string name, ILibraryManager libraryManager, DtoOptions dtoOptions)
         {
         {
             if (name.IndexOf(BaseItem.SlugChar) != -1)
             if (name.IndexOf(BaseItem.SlugChar) != -1)
             {
             {
                 var result = libraryManager.GetItemList(new InternalItemsQuery
                 var result = libraryManager.GetItemList(new InternalItemsQuery
                 {
                 {
                     SlugName = name,
                     SlugName = name,
-                    IncludeItemTypes = new[] { typeof(Person).Name }
+                    IncludeItemTypes = new[] { typeof(Person).Name },
+                    DtoOptions = dtoOptions
 
 
                 }).OfType<Person>().FirstOrDefault();
                 }).OfType<Person>().FirstOrDefault();
 
 
@@ -329,37 +335,33 @@ namespace MediaBrowser.Api
         /// <summary>
         /// <summary>
         /// Gets the name of the item by.
         /// Gets the name of the item by.
         /// </summary>
         /// </summary>
-        /// <param name="name">The name.</param>
-        /// <param name="type">The type.</param>
-        /// <param name="libraryManager">The library manager.</param>
-        /// <returns>Task{BaseItem}.</returns>
-        protected BaseItem GetItemByName(string name, string type, ILibraryManager libraryManager)
+        protected BaseItem GetItemByName(string name, string type, ILibraryManager libraryManager, DtoOptions dtoOptions)
         {
         {
             BaseItem item;
             BaseItem item;
 
 
             if (type.IndexOf("Person", StringComparison.OrdinalIgnoreCase) == 0)
             if (type.IndexOf("Person", StringComparison.OrdinalIgnoreCase) == 0)
             {
             {
-                item = GetPerson(name, libraryManager);
+                item = GetPerson(name, libraryManager, dtoOptions);
             }
             }
             else if (type.IndexOf("Artist", StringComparison.OrdinalIgnoreCase) == 0)
             else if (type.IndexOf("Artist", StringComparison.OrdinalIgnoreCase) == 0)
             {
             {
-                item = GetArtist(name, libraryManager);
+                item = GetArtist(name, libraryManager, dtoOptions);
             }
             }
             else if (type.IndexOf("Genre", StringComparison.OrdinalIgnoreCase) == 0)
             else if (type.IndexOf("Genre", StringComparison.OrdinalIgnoreCase) == 0)
             {
             {
-                item = GetGenre(name, libraryManager);
+                item = GetGenre(name, libraryManager, dtoOptions);
             }
             }
             else if (type.IndexOf("MusicGenre", StringComparison.OrdinalIgnoreCase) == 0)
             else if (type.IndexOf("MusicGenre", StringComparison.OrdinalIgnoreCase) == 0)
             {
             {
-                item = GetMusicGenre(name, libraryManager);
+                item = GetMusicGenre(name, libraryManager, dtoOptions);
             }
             }
             else if (type.IndexOf("GameGenre", StringComparison.OrdinalIgnoreCase) == 0)
             else if (type.IndexOf("GameGenre", StringComparison.OrdinalIgnoreCase) == 0)
             {
             {
-                item = GetGameGenre(name, libraryManager);
+                item = GetGameGenre(name, libraryManager, dtoOptions);
             }
             }
             else if (type.IndexOf("Studio", StringComparison.OrdinalIgnoreCase) == 0)
             else if (type.IndexOf("Studio", StringComparison.OrdinalIgnoreCase) == 0)
             {
             {
-                item = GetStudio(name, libraryManager);
+                item = GetStudio(name, libraryManager, dtoOptions);
             }
             }
             else if (type.IndexOf("Year", StringComparison.OrdinalIgnoreCase) == 0)
             else if (type.IndexOf("Year", StringComparison.OrdinalIgnoreCase) == 0)
             {
             {

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

@@ -15,6 +15,7 @@ using System.Linq;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using MediaBrowser.Common.IO;
 using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.Dto;
 using MediaBrowser.Controller.IO;
 using MediaBrowser.Controller.IO;
 using MediaBrowser.Model.IO;
 using MediaBrowser.Model.IO;
 using MediaBrowser.Model.Services;
 using MediaBrowser.Model.Services;
@@ -406,7 +407,7 @@ namespace MediaBrowser.Api.Images
         {
         {
             var type = GetPathValue(0);
             var type = GetPathValue(0);
 
 
-            var item = GetItemByName(request.Name, type, _libraryManager);
+            var item = GetItemByName(request.Name, type, _libraryManager, new DtoOptions(false));
 
 
             return GetImage(request, item, false);
             return GetImage(request, item, false);
         }
         }
@@ -415,7 +416,7 @@ namespace MediaBrowser.Api.Images
         {
         {
             var type = GetPathValue(0);
             var type = GetPathValue(0);
 
 
-            var item = GetItemByName(request.Name, type, _libraryManager);
+            var item = GetItemByName(request.Name, type, _libraryManager, new DtoOptions(false));
 
 
             return GetImage(request, item, true);
             return GetImage(request, item, true);
         }
         }

+ 6 - 3
MediaBrowser.Api/LiveTv/LiveTvService.cs

@@ -22,6 +22,7 @@ using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Entities.TV;
 using MediaBrowser.Controller.Entities.TV;
 using MediaBrowser.Controller.IO;
 using MediaBrowser.Controller.IO;
 using MediaBrowser.Model.Services;
 using MediaBrowser.Model.Services;
+using MediaBrowser.Model.System;
 
 
 namespace MediaBrowser.Api.LiveTv
 namespace MediaBrowser.Api.LiveTv
 {
 {
@@ -698,8 +699,9 @@ namespace MediaBrowser.Api.LiveTv
         private readonly IFileSystem _fileSystem;
         private readonly IFileSystem _fileSystem;
         private readonly IAuthorizationContext _authContext;
         private readonly IAuthorizationContext _authContext;
         private readonly ISessionContext _sessionContext;
         private readonly ISessionContext _sessionContext;
+        private readonly IEnvironmentInfo _environment;
 
 
-        public LiveTvService(ILiveTvManager liveTvManager, IUserManager userManager, IServerConfigurationManager config, IHttpClient httpClient, ILibraryManager libraryManager, IDtoService dtoService, IFileSystem fileSystem, IAuthorizationContext authContext, ISessionContext sessionContext)
+        public LiveTvService(ILiveTvManager liveTvManager, IUserManager userManager, IServerConfigurationManager config, IHttpClient httpClient, ILibraryManager libraryManager, IDtoService dtoService, IFileSystem fileSystem, IAuthorizationContext authContext, ISessionContext sessionContext, IEnvironmentInfo environment)
         {
         {
             _liveTvManager = liveTvManager;
             _liveTvManager = liveTvManager;
             _userManager = userManager;
             _userManager = userManager;
@@ -710,6 +712,7 @@ namespace MediaBrowser.Api.LiveTv
             _fileSystem = fileSystem;
             _fileSystem = fileSystem;
             _authContext = authContext;
             _authContext = authContext;
             _sessionContext = sessionContext;
             _sessionContext = sessionContext;
+            _environment = environment;
         }
         }
 
 
         public object Get(GetTunerHostTypes request)
         public object Get(GetTunerHostTypes request)
@@ -731,7 +734,7 @@ namespace MediaBrowser.Api.LiveTv
 
 
             outputHeaders["Content-Type"] = Model.Net.MimeTypes.GetMimeType(path);
             outputHeaders["Content-Type"] = Model.Net.MimeTypes.GetMimeType(path);
 
 
-            return new ProgressiveFileCopier(_fileSystem, path, outputHeaders, null, Logger, CancellationToken.None)
+            return new ProgressiveFileCopier(_fileSystem, path, outputHeaders, null, Logger, _environment, CancellationToken.None)
             {
             {
                 AllowEndOfFile = false
                 AllowEndOfFile = false
             };
             };
@@ -750,7 +753,7 @@ namespace MediaBrowser.Api.LiveTv
 
 
             outputHeaders["Content-Type"] = Model.Net.MimeTypes.GetMimeType("file." + request.Container);
             outputHeaders["Content-Type"] = Model.Net.MimeTypes.GetMimeType("file." + request.Container);
 
 
-            return new ProgressiveFileCopier(directStreamProvider, outputHeaders, null, Logger, CancellationToken.None)
+            return new ProgressiveFileCopier(directStreamProvider, outputHeaders, null, Logger, _environment, CancellationToken.None)
             {
             {
                 AllowEndOfFile = false
                 AllowEndOfFile = false
             };
             };

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

@@ -171,7 +171,7 @@ namespace MediaBrowser.Api.Music
         public Task<object> Get(GetInstantMixFromArtist request)
         public Task<object> Get(GetInstantMixFromArtist request)
         {
         {
             var user = _userManager.GetUserById(request.UserId);
             var user = _userManager.GetUserById(request.UserId);
-            var artist = _libraryManager.GetArtist(request.Name);
+            var artist = _libraryManager.GetArtist(request.Name, new DtoOptions(false));
 
 
             var dtoOptions = GetDtoOptions(_authContext, request);
             var dtoOptions = GetDtoOptions(_authContext, request);
 
 

+ 5 - 4
MediaBrowser.Api/Playback/Progressive/AudioService.cs

@@ -14,6 +14,7 @@ using MediaBrowser.Controller.IO;
 using MediaBrowser.Controller.Net;
 using MediaBrowser.Controller.Net;
 using MediaBrowser.Model.IO;
 using MediaBrowser.Model.IO;
 using MediaBrowser.Model.Services;
 using MediaBrowser.Model.Services;
+using MediaBrowser.Model.System;
 
 
 namespace MediaBrowser.Api.Playback.Progressive
 namespace MediaBrowser.Api.Playback.Progressive
 {
 {
@@ -35,6 +36,10 @@ namespace MediaBrowser.Api.Playback.Progressive
     //[Authenticated]
     //[Authenticated]
     public class AudioService : BaseProgressiveStreamingService
     public class AudioService : BaseProgressiveStreamingService
     {
     {
+        public AudioService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer, IAuthorizationContext authorizationContext, IImageProcessor imageProcessor, IEnvironmentInfo environmentInfo) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, jsonSerializer, authorizationContext, imageProcessor, environmentInfo)
+        {
+        }
+
         /// <summary>
         /// <summary>
         /// Gets the specified request.
         /// Gets the specified request.
         /// </summary>
         /// </summary>
@@ -61,9 +66,5 @@ namespace MediaBrowser.Api.Playback.Progressive
 
 
             return EncodingHelper.GetProgressiveAudioFullCommandLine(state, encodingOptions, outputPath);
             return EncodingHelper.GetProgressiveAudioFullCommandLine(state, encodingOptions, outputPath);
         }
         }
-
-        public AudioService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer, IAuthorizationContext authorizationContext, IImageProcessor imageProcessor) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, jsonSerializer, authorizationContext, imageProcessor)
-        {
-        }
     }
     }
 }
 }

+ 7 - 4
MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs

@@ -16,6 +16,7 @@ using System.IO;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using MediaBrowser.Model.Services;
 using MediaBrowser.Model.Services;
+using MediaBrowser.Model.System;
 
 
 namespace MediaBrowser.Api.Playback.Progressive
 namespace MediaBrowser.Api.Playback.Progressive
 {
 {
@@ -25,10 +26,12 @@ namespace MediaBrowser.Api.Playback.Progressive
     public abstract class BaseProgressiveStreamingService : BaseStreamingService
     public abstract class BaseProgressiveStreamingService : BaseStreamingService
     {
     {
         protected readonly IImageProcessor ImageProcessor;
         protected readonly IImageProcessor ImageProcessor;
+        protected readonly IEnvironmentInfo EnvironmentInfo;
 
 
-        public BaseProgressiveStreamingService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer, IAuthorizationContext authorizationContext, IImageProcessor imageProcessor) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, jsonSerializer, authorizationContext)
+        public BaseProgressiveStreamingService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer, IAuthorizationContext authorizationContext, IImageProcessor imageProcessor, IEnvironmentInfo environmentInfo) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, jsonSerializer, authorizationContext)
         {
         {
             ImageProcessor = imageProcessor;
             ImageProcessor = imageProcessor;
+            EnvironmentInfo = environmentInfo;
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -130,7 +133,7 @@ namespace MediaBrowser.Api.Playback.Progressive
                     // TODO: Don't hardcode this
                     // TODO: Don't hardcode this
                     outputHeaders["Content-Type"] = MediaBrowser.Model.Net.MimeTypes.GetMimeType("file.ts");
                     outputHeaders["Content-Type"] = MediaBrowser.Model.Net.MimeTypes.GetMimeType("file.ts");
 
 
-                    return new ProgressiveFileCopier(state.DirectStreamProvider, outputHeaders, null, Logger, CancellationToken.None)
+                    return new ProgressiveFileCopier(state.DirectStreamProvider, outputHeaders, null, Logger, EnvironmentInfo, CancellationToken.None)
                     {
                     {
                         AllowEndOfFile = false
                         AllowEndOfFile = false
                     };
                     };
@@ -174,7 +177,7 @@ namespace MediaBrowser.Api.Playback.Progressive
 
 
                         outputHeaders["Content-Type"] = contentType;
                         outputHeaders["Content-Type"] = contentType;
 
 
-                        return new ProgressiveFileCopier(FileSystem, state.MediaPath, outputHeaders, null, Logger, CancellationToken.None)
+                        return new ProgressiveFileCopier(FileSystem, state.MediaPath, outputHeaders, null, Logger, EnvironmentInfo, CancellationToken.None)
                         {
                         {
                             AllowEndOfFile = false
                             AllowEndOfFile = false
                         };
                         };
@@ -398,7 +401,7 @@ namespace MediaBrowser.Api.Playback.Progressive
                     outputHeaders[item.Key] = item.Value;
                     outputHeaders[item.Key] = item.Value;
                 }
                 }
 
 
-                return new ProgressiveFileCopier(FileSystem, outputPath, outputHeaders, job, Logger, CancellationToken.None);
+                return new ProgressiveFileCopier(FileSystem, outputPath, outputHeaders, job, Logger, EnvironmentInfo, CancellationToken.None);
             }
             }
             finally
             finally
             {
             {

+ 76 - 19
MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs

@@ -10,6 +10,7 @@ using MediaBrowser.Common.IO;
 using MediaBrowser.Controller.IO;
 using MediaBrowser.Controller.IO;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Model.Services;
 using MediaBrowser.Model.Services;
+using MediaBrowser.Model.System;
 
 
 namespace MediaBrowser.Api.Playback.Progressive
 namespace MediaBrowser.Api.Playback.Progressive
 {
 {
@@ -22,16 +23,16 @@ namespace MediaBrowser.Api.Playback.Progressive
         private readonly CancellationToken _cancellationToken;
         private readonly CancellationToken _cancellationToken;
         private readonly Dictionary<string, string> _outputHeaders;
         private readonly Dictionary<string, string> _outputHeaders;
 
 
-        // 256k
-        private const int BufferSize = 81920;
+        const int StreamCopyToBufferSize = 81920;
 
 
         private long _bytesWritten = 0;
         private long _bytesWritten = 0;
         public long StartPosition { get; set; }
         public long StartPosition { get; set; }
         public bool AllowEndOfFile = true;
         public bool AllowEndOfFile = true;
 
 
         private readonly IDirectStreamProvider _directStreamProvider;
         private readonly IDirectStreamProvider _directStreamProvider;
+        private readonly IEnvironmentInfo _environment;
 
 
-        public ProgressiveFileCopier(IFileSystem fileSystem, string path, Dictionary<string, string> outputHeaders, TranscodingJob job, ILogger logger, CancellationToken cancellationToken)
+        public ProgressiveFileCopier(IFileSystem fileSystem, string path, Dictionary<string, string> outputHeaders, TranscodingJob job, ILogger logger, IEnvironmentInfo environment, CancellationToken cancellationToken)
         {
         {
             _fileSystem = fileSystem;
             _fileSystem = fileSystem;
             _path = path;
             _path = path;
@@ -39,15 +40,17 @@ namespace MediaBrowser.Api.Playback.Progressive
             _job = job;
             _job = job;
             _logger = logger;
             _logger = logger;
             _cancellationToken = cancellationToken;
             _cancellationToken = cancellationToken;
+            _environment = environment;
         }
         }
 
 
-        public ProgressiveFileCopier(IDirectStreamProvider directStreamProvider, Dictionary<string, string> outputHeaders, TranscodingJob job, ILogger logger, CancellationToken cancellationToken)
+        public ProgressiveFileCopier(IDirectStreamProvider directStreamProvider, Dictionary<string, string> outputHeaders, TranscodingJob job, ILogger logger, IEnvironmentInfo environment, CancellationToken cancellationToken)
         {
         {
             _directStreamProvider = directStreamProvider;
             _directStreamProvider = directStreamProvider;
             _outputHeaders = outputHeaders;
             _outputHeaders = outputHeaders;
             _job = job;
             _job = job;
             _logger = logger;
             _logger = logger;
             _cancellationToken = cancellationToken;
             _cancellationToken = cancellationToken;
+            _environment = environment;
         }
         }
 
 
         public IDictionary<string, string> Headers
         public IDictionary<string, string> Headers
@@ -58,33 +61,55 @@ namespace MediaBrowser.Api.Playback.Progressive
             }
             }
         }
         }
 
 
-        private Stream GetInputStream()
+        private Stream GetInputStream(bool allowAsyncFileRead)
         {
         {
-            return _fileSystem.GetFileStream(_path, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.ReadWrite, true);
+            var fileOpenOptions = StartPosition > 0
+                ? FileOpenOptions.RandomAccess
+                : FileOpenOptions.SequentialScan;
+
+            if (allowAsyncFileRead)
+            {
+                fileOpenOptions |= FileOpenOptions.Asynchronous;
+            }
+
+            return _fileSystem.GetFileStream(_path, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.ReadWrite, fileOpenOptions);
         }
         }
 
 
         public async Task WriteToAsync(Stream outputStream, CancellationToken cancellationToken)
         public async Task WriteToAsync(Stream outputStream, CancellationToken cancellationToken)
         {
         {
+            cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, _cancellationToken).Token;
+
             try
             try
             {
             {
                 if (_directStreamProvider != null)
                 if (_directStreamProvider != null)
                 {
                 {
-                    await _directStreamProvider.CopyToAsync(outputStream, _cancellationToken).ConfigureAwait(false);
+                    await _directStreamProvider.CopyToAsync(outputStream, cancellationToken).ConfigureAwait(false);
                     return;
                     return;
                 }
                 }
 
 
                 var eofCount = 0;
                 var eofCount = 0;
 
 
-                using (var inputStream = GetInputStream())
+                // use non-async filestream along with read due to https://github.com/dotnet/corefx/issues/6039
+                var allowAsyncFileRead = _environment.OperatingSystem != OperatingSystem.Windows;
+
+                using (var inputStream = GetInputStream(allowAsyncFileRead))
                 {
                 {
                     if (StartPosition > 0)
                     if (StartPosition > 0)
                     {
                     {
                         inputStream.Position = StartPosition;
                         inputStream.Position = StartPosition;
                     }
                     }
 
 
-                    while (eofCount < 15 || !AllowEndOfFile)
+                    while (eofCount < 20 || !AllowEndOfFile)
                     {
                     {
-                        var bytesRead = await CopyToAsyncInternal(inputStream, outputStream, BufferSize, _cancellationToken).ConfigureAwait(false);
+                        int bytesRead;
+                        if (allowAsyncFileRead)
+                        {
+                            bytesRead = await CopyToInternalAsync(inputStream, outputStream, cancellationToken).ConfigureAwait(false);
+                        }
+                        else
+                        {
+                            bytesRead = await CopyToInternalAsyncWithSyncRead(inputStream, outputStream, cancellationToken).ConfigureAwait(false);
+                        }
 
 
                         //var position = fs.Position;
                         //var position = fs.Position;
                         //_logger.Debug("Streamed {0} bytes to position {1} from file {2}", bytesRead, position, path);
                         //_logger.Debug("Streamed {0} bytes to position {1} from file {2}", bytesRead, position, path);
@@ -95,7 +120,7 @@ namespace MediaBrowser.Api.Playback.Progressive
                             {
                             {
                                 eofCount++;
                                 eofCount++;
                             }
                             }
-                            await Task.Delay(100, _cancellationToken).ConfigureAwait(false);
+                            await Task.Delay(100, cancellationToken).ConfigureAwait(false);
                         }
                         }
                         else
                         else
                         {
                         {
@@ -113,22 +138,54 @@ namespace MediaBrowser.Api.Playback.Progressive
             }
             }
         }
         }
 
 
-        private async Task<int> CopyToAsyncInternal(Stream source, Stream destination, Int32 bufferSize, CancellationToken cancellationToken)
+        private async Task<int> CopyToInternalAsyncWithSyncRead(Stream source, Stream destination, CancellationToken cancellationToken)
         {
         {
-            byte[] buffer = new byte[bufferSize];
+            var array = new byte[StreamCopyToBufferSize];
             int bytesRead;
             int bytesRead;
             int totalBytesRead = 0;
             int totalBytesRead = 0;
 
 
-            while ((bytesRead = await source.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false)) != 0)
+            while ((bytesRead = source.Read(array, 0, array.Length)) != 0)
             {
             {
-                await destination.WriteAsync(buffer, 0, bytesRead, cancellationToken).ConfigureAwait(false);
+                var bytesToWrite = bytesRead;
+
+                if (bytesToWrite > 0)
+                {
+                    await destination.WriteAsync(array, 0, Convert.ToInt32(bytesToWrite), cancellationToken).ConfigureAwait(false);
 
 
-                _bytesWritten += bytesRead;
-                totalBytesRead += bytesRead;
+                    _bytesWritten += bytesRead;
+                    totalBytesRead += bytesRead;
 
 
-                if (_job != null)
+                    if (_job != null)
+                    {
+                        _job.BytesDownloaded = Math.Max(_job.BytesDownloaded ?? _bytesWritten, _bytesWritten);
+                    }
+                }
+            }
+
+            return totalBytesRead;
+        }
+
+        private async Task<int> CopyToInternalAsync(Stream source, Stream destination, CancellationToken cancellationToken)
+        {
+            var array = new byte[StreamCopyToBufferSize];
+            int bytesRead;
+            int totalBytesRead = 0;
+
+            while ((bytesRead = await source.ReadAsync(array, 0, array.Length, cancellationToken).ConfigureAwait(false)) != 0)
+            {
+                var bytesToWrite = bytesRead;
+
+                if (bytesToWrite > 0)
                 {
                 {
-                    _job.BytesDownloaded = Math.Max(_job.BytesDownloaded ?? _bytesWritten, _bytesWritten);
+                    await destination.WriteAsync(array, 0, Convert.ToInt32(bytesToWrite), cancellationToken).ConfigureAwait(false);
+
+                    _bytesWritten += bytesRead;
+                    totalBytesRead += bytesRead;
+
+                    if (_job != null)
+                    {
+                        _job.BytesDownloaded = Math.Max(_job.BytesDownloaded ?? _bytesWritten, _bytesWritten);
+                    }
                 }
                 }
             }
             }
 
 

+ 2 - 1
MediaBrowser.Api/Playback/Progressive/VideoService.cs

@@ -9,6 +9,7 @@ using MediaBrowser.Model.Serialization;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using MediaBrowser.Controller.Net;
 using MediaBrowser.Controller.Net;
 using MediaBrowser.Model.Services;
 using MediaBrowser.Model.Services;
+using MediaBrowser.Model.System;
 
 
 namespace MediaBrowser.Api.Playback.Progressive
 namespace MediaBrowser.Api.Playback.Progressive
 {
 {
@@ -66,7 +67,7 @@ namespace MediaBrowser.Api.Playback.Progressive
     //[Authenticated]
     //[Authenticated]
     public class VideoService : BaseProgressiveStreamingService
     public class VideoService : BaseProgressiveStreamingService
     {
     {
-        public VideoService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer, IAuthorizationContext authorizationContext, IImageProcessor imageProcessor) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, jsonSerializer, authorizationContext, imageProcessor)
+        public VideoService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer, IAuthorizationContext authorizationContext, IImageProcessor imageProcessor, IEnvironmentInfo environmentInfo) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, jsonSerializer, authorizationContext, imageProcessor, environmentInfo)
         {
         {
         }
         }
 
 

+ 6 - 2
MediaBrowser.Api/Playback/UniversalAudioService.cs

@@ -18,6 +18,7 @@ using MediaBrowser.Model.IO;
 using MediaBrowser.Model.MediaInfo;
 using MediaBrowser.Model.MediaInfo;
 using MediaBrowser.Model.Serialization;
 using MediaBrowser.Model.Serialization;
 using MediaBrowser.Model.Services;
 using MediaBrowser.Model.Services;
+using MediaBrowser.Model.System;
 
 
 namespace MediaBrowser.Api.Playback
 namespace MediaBrowser.Api.Playback
 {
 {
@@ -63,7 +64,7 @@ namespace MediaBrowser.Api.Playback
     //[Authenticated]
     //[Authenticated]
     public class UniversalAudioService : BaseApiService
     public class UniversalAudioService : BaseApiService
     {
     {
-        public UniversalAudioService(IServerConfigurationManager serverConfigurationManager, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, IDeviceManager deviceManager, ISubtitleEncoder subtitleEncoder, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer, IAuthorizationContext authorizationContext, IImageProcessor imageProcessor, INetworkManager networkManager)
+        public UniversalAudioService(IServerConfigurationManager serverConfigurationManager, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, IDeviceManager deviceManager, ISubtitleEncoder subtitleEncoder, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer, IAuthorizationContext authorizationContext, IImageProcessor imageProcessor, INetworkManager networkManager, IEnvironmentInfo environmentInfo)
         {
         {
             ServerConfigurationManager = serverConfigurationManager;
             ServerConfigurationManager = serverConfigurationManager;
             UserManager = userManager;
             UserManager = userManager;
@@ -80,6 +81,7 @@ namespace MediaBrowser.Api.Playback
             AuthorizationContext = authorizationContext;
             AuthorizationContext = authorizationContext;
             ImageProcessor = imageProcessor;
             ImageProcessor = imageProcessor;
             NetworkManager = networkManager;
             NetworkManager = networkManager;
+            EnvironmentInfo = environmentInfo;
         }
         }
 
 
         protected IServerConfigurationManager ServerConfigurationManager { get; private set; }
         protected IServerConfigurationManager ServerConfigurationManager { get; private set; }
@@ -97,6 +99,7 @@ namespace MediaBrowser.Api.Playback
         protected IAuthorizationContext AuthorizationContext { get; private set; }
         protected IAuthorizationContext AuthorizationContext { get; private set; }
         protected IImageProcessor ImageProcessor { get; private set; }
         protected IImageProcessor ImageProcessor { get; private set; }
         protected INetworkManager NetworkManager { get; private set; }
         protected INetworkManager NetworkManager { get; private set; }
+        protected IEnvironmentInfo EnvironmentInfo { get; private set; }
 
 
         public Task<object> Get(GetUniversalAudioStream request)
         public Task<object> Get(GetUniversalAudioStream request)
         {
         {
@@ -263,7 +266,8 @@ namespace MediaBrowser.Api.Playback
                     ZipClient,
                     ZipClient,
                     JsonSerializer,
                     JsonSerializer,
                     AuthorizationContext,
                     AuthorizationContext,
-                    ImageProcessor)
+                    ImageProcessor,
+                    EnvironmentInfo)
                 {
                 {
                     Request = Request
                     Request = Request
                 };
                 };

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

@@ -68,10 +68,10 @@ namespace MediaBrowser.Api.UserLibrary
         /// <returns>Task{BaseItemDto}.</returns>
         /// <returns>Task{BaseItemDto}.</returns>
         private BaseItemDto GetItem(GetArtist request)
         private BaseItemDto GetItem(GetArtist request)
         {
         {
-            var item = GetArtist(request.Name, LibraryManager);
-            
             var dtoOptions = GetDtoOptions(AuthorizationContext, request);
             var dtoOptions = GetDtoOptions(AuthorizationContext, request);
 
 
+            var item = GetArtist(request.Name, LibraryManager, dtoOptions);
+            
             if (!string.IsNullOrWhiteSpace(request.UserId))
             if (!string.IsNullOrWhiteSpace(request.UserId))
             {
             {
                 var user = UserManager.GetUserById(request.UserId);
                 var user = UserManager.GetUserById(request.UserId);

+ 2 - 1
MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs

@@ -267,7 +267,8 @@ namespace MediaBrowser.Api.UserLibrary
             {
             {
                 ExcludeItemTypes = excludeItemTypes,
                 ExcludeItemTypes = excludeItemTypes,
                 IncludeItemTypes = includeItemTypes,
                 IncludeItemTypes = includeItemTypes,
-                MediaTypes = mediaTypes
+                MediaTypes = mediaTypes,
+                DtoOptions = dtoOptions
             };
             };
 
 
             Func<BaseItem, bool> filter = i => FilterItem(request, i, excludeItemTypes, includeItemTypes, mediaTypes);
             Func<BaseItem, bool> filter = i => FilterItem(request, i, excludeItemTypes, includeItemTypes, mediaTypes);

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

@@ -56,10 +56,10 @@ namespace MediaBrowser.Api.UserLibrary
         /// <returns>Task{BaseItemDto}.</returns>
         /// <returns>Task{BaseItemDto}.</returns>
         private BaseItemDto GetItem(GetGameGenre request)
         private BaseItemDto GetItem(GetGameGenre request)
         {
         {
-            var item = GetGameGenre(request.Name, LibraryManager);
-
             var dtoOptions = GetDtoOptions(AuthorizationContext, request);
             var dtoOptions = GetDtoOptions(AuthorizationContext, request);
-            
+
+            var item = GetGameGenre(request.Name, LibraryManager, dtoOptions);
+
             if (!string.IsNullOrWhiteSpace(request.UserId))
             if (!string.IsNullOrWhiteSpace(request.UserId))
             {
             {
                 var user = UserManager.GetUserById(request.UserId);
                 var user = UserManager.GetUserById(request.UserId);

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

@@ -66,10 +66,10 @@ namespace MediaBrowser.Api.UserLibrary
         /// <returns>Task{BaseItemDto}.</returns>
         /// <returns>Task{BaseItemDto}.</returns>
         private BaseItemDto GetItem(GetGenre request)
         private BaseItemDto GetItem(GetGenre request)
         {
         {
-            var item = GetGenre(request.Name, LibraryManager);
-            
-            var dtoOptions = GetDtoOptions(AuthorizationContext ,request);
+            var dtoOptions = GetDtoOptions(AuthorizationContext, request);
 
 
+            var item = GetGenre(request.Name, LibraryManager, dtoOptions);
+            
             if (!string.IsNullOrWhiteSpace(request.UserId))
             if (!string.IsNullOrWhiteSpace(request.UserId))
             {
             {
                 var user = UserManager.GetUserById(request.UserId);
                 var user = UserManager.GetUserById(request.UserId);

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

@@ -358,7 +358,7 @@ namespace MediaBrowser.Api.UserLibrary
                 {
                 {
                     try
                     try
                     {
                     {
-                        return _libraryManager.GetArtist(i);
+                        return _libraryManager.GetArtist(i, new DtoOptions(false));
                     }
                     }
                     catch
                     catch
                     {
                     {

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

@@ -57,10 +57,10 @@ namespace MediaBrowser.Api.UserLibrary
         /// <returns>Task{BaseItemDto}.</returns>
         /// <returns>Task{BaseItemDto}.</returns>
         private BaseItemDto GetItem(GetMusicGenre request)
         private BaseItemDto GetItem(GetMusicGenre request)
         {
         {
-            var item = GetMusicGenre(request.Name, LibraryManager);
-            
             var dtoOptions = GetDtoOptions(AuthorizationContext, request);
             var dtoOptions = GetDtoOptions(AuthorizationContext, request);
 
 
+            var item = GetMusicGenre(request.Name, LibraryManager, dtoOptions);
+            
             if (!string.IsNullOrWhiteSpace(request.UserId))
             if (!string.IsNullOrWhiteSpace(request.UserId))
             {
             {
                 var user = UserManager.GetUserById(request.UserId);
                 var user = UserManager.GetUserById(request.UserId);

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

@@ -64,10 +64,10 @@ namespace MediaBrowser.Api.UserLibrary
         /// <returns>Task{BaseItemDto}.</returns>
         /// <returns>Task{BaseItemDto}.</returns>
         private BaseItemDto GetItem(GetPerson request)
         private BaseItemDto GetItem(GetPerson request)
         {
         {
-            var item = GetPerson(request.Name, LibraryManager);
-            
             var dtoOptions = GetDtoOptions(AuthorizationContext, request);
             var dtoOptions = GetDtoOptions(AuthorizationContext, request);
 
 
+            var item = GetPerson(request.Name, LibraryManager, dtoOptions);
+            
             if (!string.IsNullOrWhiteSpace(request.UserId))
             if (!string.IsNullOrWhiteSpace(request.UserId))
             {
             {
                 var user = UserManager.GetUserById(request.UserId);
                 var user = UserManager.GetUserById(request.UserId);

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

@@ -66,10 +66,10 @@ namespace MediaBrowser.Api.UserLibrary
         /// <returns>Task{BaseItemDto}.</returns>
         /// <returns>Task{BaseItemDto}.</returns>
         private BaseItemDto GetItem(GetStudio request)
         private BaseItemDto GetItem(GetStudio request)
         {
         {
-            var item = GetStudio(request.Name, LibraryManager);
-
             var dtoOptions = GetDtoOptions(AuthorizationContext, request);
             var dtoOptions = GetDtoOptions(AuthorizationContext, request);
-            
+
+            var item = GetStudio(request.Name, LibraryManager, dtoOptions);
+
             if (!string.IsNullOrWhiteSpace(request.UserId))
             if (!string.IsNullOrWhiteSpace(request.UserId))
             {
             {
                 var user = UserManager.GetUserById(request.UserId);
                 var user = UserManager.GetUserById(request.UserId);

+ 14 - 11
MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs

@@ -8,6 +8,7 @@ using System.Linq;
 using MediaBrowser.Model.Serialization;
 using MediaBrowser.Model.Serialization;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
+using MediaBrowser.Controller.Dto;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.Dto;
 
 
@@ -42,20 +43,22 @@ namespace MediaBrowser.Controller.Entities.Audio
         [IgnoreDataMember]
         [IgnoreDataMember]
         public MusicArtist MusicArtist
         public MusicArtist MusicArtist
         {
         {
-            get
-            {
-                var artist = GetParents().OfType<MusicArtist>().FirstOrDefault();
+            get { return GetMusicArtist(new DtoOptions(true)); }
+        }
 
 
-                if (artist == null)
+        public MusicArtist GetMusicArtist(DtoOptions options)
+        {
+            var artist = GetParents().OfType<MusicArtist>().FirstOrDefault();
+
+            if (artist == null)
+            {
+                var name = AlbumArtist;
+                if (!string.IsNullOrWhiteSpace(name))
                 {
                 {
-                    var name = AlbumArtist;
-                    if (!string.IsNullOrWhiteSpace(name))
-                    {
-                        artist = LibraryManager.GetArtist(name);
-                    }
+                    artist = LibraryManager.GetArtist(name, options);
                 }
                 }
-                return artist;
             }
             }
+            return artist;
         }
         }
 
 
         [IgnoreDataMember]
         [IgnoreDataMember]
@@ -171,7 +174,7 @@ namespace MediaBrowser.Controller.Entities.Audio
 
 
             id.AlbumArtists = AlbumArtists;
             id.AlbumArtists = AlbumArtists;
 
 
-            var artist = MusicArtist;
+            var artist = GetMusicArtist(new DtoOptions(false));
 
 
             if (artist != null)
             if (artist != null)
             {
             {

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

@@ -160,42 +160,6 @@ namespace MediaBrowser.Controller.Entities
         public DtoOptions DtoOptions { get; set; }
         public DtoOptions DtoOptions { get; set; }
         public int MinSimilarityScore { get; set; }
         public int MinSimilarityScore { get; set; }
 
 
-        public bool HasField(ItemFields name)
-        {
-            var fields = DtoOptions.Fields;
-
-            switch (name)
-            {
-                case ItemFields.HomePageUrl:
-                case ItemFields.Keywords:
-                case ItemFields.DisplayMediaType:
-                case ItemFields.VoteCount:
-                case ItemFields.CustomRating:
-                case ItemFields.ProductionLocations:
-                case ItemFields.Settings:
-                case ItemFields.OriginalTitle:
-                case ItemFields.Taglines:
-                case ItemFields.SortName:
-                case ItemFields.Studios:
-                case ItemFields.Tags:
-                case ItemFields.ThemeSongIds:
-                case ItemFields.ThemeVideoIds:
-                case ItemFields.DateCreated:
-                case ItemFields.Overview:
-                case ItemFields.Genres:
-                case ItemFields.DateLastMediaAdded:
-                case ItemFields.ExternalEtag:
-                case ItemFields.PresentationUniqueKey:
-                case ItemFields.InheritedParentalRatingValue:
-                case ItemFields.ExternalSeriesId:
-                    return fields.Contains(name);
-                case ItemFields.ServiceName:
-                    return true;
-                default:
-                    return true;
-            }
-        }
-
         public InternalItemsQuery()
         public InternalItemsQuery()
         {
         {
             MinSimilarityScore = 20;
             MinSimilarityScore = 20;

+ 2 - 12
MediaBrowser.Controller/Library/ILibraryManager.cs

@@ -12,6 +12,7 @@ using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using MediaBrowser.Common.IO;
 using MediaBrowser.Common.IO;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Dto;
 using MediaBrowser.Controller.IO;
 using MediaBrowser.Controller.IO;
 using MediaBrowser.Model.Configuration;
 using MediaBrowser.Model.Configuration;
 using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.Dto;
@@ -68,18 +69,7 @@ namespace MediaBrowser.Controller.Library
         /// <param name="name">The name.</param>
         /// <param name="name">The name.</param>
         /// <returns>Task{Artist}.</returns>
         /// <returns>Task{Artist}.</returns>
         MusicArtist GetArtist(string name);
         MusicArtist GetArtist(string name);
-        /// <summary>
-        /// Gets the album artists.
-        /// </summary>
-        /// <param name="items">The items.</param>
-        /// <returns>IEnumerable&lt;MusicArtist&gt;.</returns>
-        IEnumerable<MusicArtist> GetAlbumArtists(IEnumerable<IHasAlbumArtist> items);
-        /// <summary>
-        /// Gets the artists.
-        /// </summary>
-        /// <param name="items">The items.</param>
-        /// <returns>IEnumerable&lt;MusicArtist&gt;.</returns>
-        IEnumerable<MusicArtist> GetArtists(IEnumerable<IHasArtist> items);
+        MusicArtist GetArtist(string name, DtoOptions options);
         /// <summary>
         /// <summary>
         /// Gets a Studio
         /// Gets a Studio
         /// </summary>
         /// </summary>

+ 138 - 3
MediaBrowser.Controller/LiveTv/LiveStream.cs

@@ -1,8 +1,11 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
+using System.IO;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.Dto;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.System;
 
 
 namespace MediaBrowser.Controller.LiveTv
 namespace MediaBrowser.Controller.LiveTv
 {
 {
@@ -10,7 +13,8 @@ namespace MediaBrowser.Controller.LiveTv
     {
     {
         public MediaSourceInfo OriginalMediaSource { get; set; }
         public MediaSourceInfo OriginalMediaSource { get; set; }
         public MediaSourceInfo OpenedMediaSource { get; set; }
         public MediaSourceInfo OpenedMediaSource { get; set; }
-        public int ConsumerCount {
+        public int ConsumerCount
+        {
             get { return SharedStreamIds.Count; }
             get { return SharedStreamIds.Count; }
         }
         }
         public ITunerHost TunerHost { get; set; }
         public ITunerHost TunerHost { get; set; }
@@ -18,11 +22,16 @@ namespace MediaBrowser.Controller.LiveTv
         public bool EnableStreamSharing { get; set; }
         public bool EnableStreamSharing { get; set; }
         public string UniqueId = Guid.NewGuid().ToString("N");
         public string UniqueId = Guid.NewGuid().ToString("N");
 
 
-        public List<string> SharedStreamIds = new List<string>(); 
+        public List<string> SharedStreamIds = new List<string>();
+        protected readonly IEnvironmentInfo Environment;
+        protected readonly IFileSystem FileSystem;
+        const int StreamCopyToBufferSize = 81920;
 
 
-        public LiveStream(MediaSourceInfo mediaSource)
+        public LiveStream(MediaSourceInfo mediaSource, IEnvironmentInfo environment, IFileSystem fileSystem)
         {
         {
             OriginalMediaSource = mediaSource;
             OriginalMediaSource = mediaSource;
+            Environment = environment;
+            FileSystem = fileSystem;
             OpenedMediaSource = mediaSource;
             OpenedMediaSource = mediaSource;
             EnableStreamSharing = true;
             EnableStreamSharing = true;
         }
         }
@@ -41,5 +50,131 @@ namespace MediaBrowser.Controller.LiveTv
         {
         {
             return Task.FromResult(true);
             return Task.FromResult(true);
         }
         }
+
+        private Stream GetInputStream(string path, long startPosition, bool allowAsyncFileRead)
+        {
+            var fileOpenOptions = startPosition > 0
+                ? FileOpenOptions.RandomAccess
+                : FileOpenOptions.SequentialScan;
+
+            if (allowAsyncFileRead)
+            {
+                fileOpenOptions |= FileOpenOptions.Asynchronous;
+            }
+
+            return FileSystem.GetFileStream(path, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.ReadWrite, fileOpenOptions);
+        }
+
+        protected async Task DeleteTempFile(string path, int retryCount = 0)
+        {
+            try
+            {
+                FileSystem.DeleteFile(path);
+                return;
+            }
+            catch
+            {
+
+            }
+
+            if (retryCount > 20)
+            {
+                return;
+            }
+
+            await Task.Delay(500).ConfigureAwait(false);
+            await DeleteTempFile(path, retryCount + 1).ConfigureAwait(false);
+        }
+
+        protected async Task CopyFileTo(string path, bool allowEndOfFile, Stream outputStream, CancellationToken cancellationToken)
+        {
+            var eofCount = 0;
+
+            long startPosition = -25000;
+            if (startPosition < 0)
+            {
+                var length = FileSystem.GetFileInfo(path).Length;
+                startPosition = Math.Max(length - startPosition, 0);
+            }
+
+            // use non-async filestream along with read due to https://github.com/dotnet/corefx/issues/6039
+            var allowAsyncFileRead = Environment.OperatingSystem != OperatingSystem.Windows;
+
+            using (var inputStream = GetInputStream(path, startPosition, allowAsyncFileRead))
+            {
+                if (startPosition > 0)
+                {
+                    inputStream.Position = startPosition;
+                }
+
+                while (eofCount < 20 || !allowEndOfFile)
+                {
+                    int bytesRead;
+                    if (allowAsyncFileRead)
+                    {
+                        bytesRead = await CopyToInternalAsync(inputStream, outputStream, cancellationToken).ConfigureAwait(false);
+                    }
+                    else
+                    {
+                        bytesRead = await CopyToInternalAsyncWithSyncRead(inputStream, outputStream, cancellationToken).ConfigureAwait(false);
+                    }
+
+                    //var position = fs.Position;
+                    //_logger.Debug("Streamed {0} bytes to position {1} from file {2}", bytesRead, position, path);
+
+                    if (bytesRead == 0)
+                    {
+                        eofCount++;
+                        await Task.Delay(100, cancellationToken).ConfigureAwait(false);
+                    }
+                    else
+                    {
+                        eofCount = 0;
+                    }
+                }
+            }
+        }
+
+        private async Task<int> CopyToInternalAsyncWithSyncRead(Stream source, Stream destination, CancellationToken cancellationToken)
+        {
+            var array = new byte[StreamCopyToBufferSize];
+            int bytesRead;
+            int totalBytesRead = 0;
+
+            while ((bytesRead = source.Read(array, 0, array.Length)) != 0)
+            {
+                var bytesToWrite = bytesRead;
+
+                if (bytesToWrite > 0)
+                {
+                    await destination.WriteAsync(array, 0, Convert.ToInt32(bytesToWrite), cancellationToken).ConfigureAwait(false);
+
+                    totalBytesRead += bytesRead;
+                }
+            }
+
+            return totalBytesRead;
+        }
+
+        private async Task<int> CopyToInternalAsync(Stream source, Stream destination, CancellationToken cancellationToken)
+        {
+            var array = new byte[StreamCopyToBufferSize];
+            int bytesRead;
+            int totalBytesRead = 0;
+
+            while ((bytesRead = await source.ReadAsync(array, 0, array.Length, cancellationToken).ConfigureAwait(false)) != 0)
+            {
+                var bytesToWrite = bytesRead;
+
+                if (bytesToWrite > 0)
+                {
+                    await destination.WriteAsync(array, 0, Convert.ToInt32(bytesToWrite), cancellationToken).ConfigureAwait(false);
+
+                    totalBytesRead += bytesRead;
+                }
+            }
+
+            return totalBytesRead;
+        }
     }
     }
 }
 }

+ 4 - 1
MediaBrowser.Model/Querying/ItemFields.cs

@@ -235,6 +235,9 @@
         ExternalEtag,
         ExternalEtag,
         PresentationUniqueKey,
         PresentationUniqueKey,
         InheritedParentalRatingValue,
         InheritedParentalRatingValue,
-        ExternalSeriesId
+        ExternalSeriesId,
+        SeriesPresentationUniqueKey,
+        DateLastRefreshed,
+        DateLastSaved
     }
     }
 }
 }

+ 7 - 3
MediaBrowser.WebDashboard/Api/DashboardService.cs

@@ -305,8 +305,6 @@ namespace MediaBrowser.WebDashboard.Api
                 }
                 }
             }
             }
 
 
-            path = path.Replace("scripts/jquery.mobile-1.4.5.min.map", "thirdparty/jquerymobile-1.4.5/jquery.mobile-1.4.5.min.map", StringComparison.OrdinalIgnoreCase);
-
             var localizationCulture = GetLocalizationCulture();
             var localizationCulture = GetLocalizationCulture();
 
 
             // Don't cache if not configured to do so
             // Don't cache if not configured to do so
@@ -330,7 +328,13 @@ namespace MediaBrowser.WebDashboard.Api
 
 
             var cacheKey = (_appHost.ApplicationVersion + (localizationCulture ?? string.Empty) + path).GetMD5();
             var cacheKey = (_appHost.ApplicationVersion + (localizationCulture ?? string.Empty) + path).GetMD5();
 
 
-            return await _resultFactory.GetStaticResult(Request, cacheKey, null, cacheDuration, contentType, () => GetResourceStream(basePath, path, localizationCulture)).ConfigureAwait(false);
+            // html gets modified on the fly
+            if (contentType.StartsWith("text/html", StringComparison.OrdinalIgnoreCase))
+            {
+                return await _resultFactory.GetStaticResult(Request, cacheKey, null, cacheDuration, contentType, () => GetResourceStream(basePath, path, localizationCulture)).ConfigureAwait(false);
+            }
+
+            return await _resultFactory.GetStaticFileResult(Request, GetPackageCreator(basePath).GetResourcePath(path));
         }
         }
 
 
         private string GetLocalizationCulture()
         private string GetLocalizationCulture()

+ 4 - 4
MediaBrowser.WebDashboard/Api/PackageCreator.cs

@@ -68,7 +68,7 @@ namespace MediaBrowser.WebDashboard.Api
         /// Gets the dashboard resource path.
         /// Gets the dashboard resource path.
         /// </summary>
         /// </summary>
         /// <returns>System.String.</returns>
         /// <returns>System.String.</returns>
-        private string GetDashboardResourcePath(string virtualPath)
+        public string GetResourcePath(string virtualPath)
         {
         {
             var fullPath = Path.Combine(_basePath, virtualPath.Replace('/', _fileSystem.DirectorySeparatorChar));
             var fullPath = Path.Combine(_basePath, virtualPath.Replace('/', _fileSystem.DirectorySeparatorChar));
 
 
@@ -97,7 +97,7 @@ namespace MediaBrowser.WebDashboard.Api
                 return false;
                 return false;
             }
             }
 
 
-            path = GetDashboardResourcePath(path);
+            path = GetResourcePath(path);
             var parent = _fileSystem.GetDirectoryName(path);
             var parent = _fileSystem.GetDirectoryName(path);
             
             
             return string.Equals(_basePath, parent, StringComparison.OrdinalIgnoreCase) ||
             return string.Equals(_basePath, parent, StringComparison.OrdinalIgnoreCase) ||
@@ -140,7 +140,7 @@ namespace MediaBrowser.WebDashboard.Api
                                 html = html.Substring(0, index);
                                 html = html.Substring(0, index);
                             }
                             }
                         }
                         }
-                        var mainFile = _fileSystem.ReadAllText(GetDashboardResourcePath("index.html"));
+                        var mainFile = _fileSystem.ReadAllText(GetResourcePath("index.html"));
 
 
                         html = ReplaceFirst(mainFile, "<div class=\"mainAnimatedPages skinBody\"></div>", "<div class=\"mainAnimatedPages skinBody hide\">" + html + "</div>");
                         html = ReplaceFirst(mainFile, "<div class=\"mainAnimatedPages skinBody\"></div>", "<div class=\"mainAnimatedPages skinBody hide\">" + html + "</div>");
                     }
                     }
@@ -299,7 +299,7 @@ namespace MediaBrowser.WebDashboard.Api
         /// </summary>
         /// </summary>
         private Stream GetRawResourceStream(string virtualPath)
         private Stream GetRawResourceStream(string virtualPath)
         {
         {
-            return _fileSystem.GetFileStream(GetDashboardResourcePath(virtualPath), FileOpenMode.Open, FileAccessMode.Read, FileShareMode.ReadWrite, true);
+            return _fileSystem.GetFileStream(GetResourcePath(virtualPath), FileOpenMode.Open, FileAccessMode.Read, FileShareMode.ReadWrite, true);
         }
         }
 
 
     }
     }

+ 1 - 1
SharedVersion.cs

@@ -1,3 +1,3 @@
 using System.Reflection;
 using System.Reflection;
 
 
-[assembly: AssemblyVersion("3.2.17.9")]
+[assembly: AssemblyVersion("3.2.17.10")]

+ 5 - 14
SocketHttpListener.Portable/Net/ResponseStream.cs

@@ -148,6 +148,7 @@ namespace SocketHttpListener.Net
         }
         }
 
 
         const int MsCopyBufferSize = 81920;
         const int MsCopyBufferSize = 81920;
+        const int StreamCopyToBufferSize = 81920;
         public override void Write(byte[] buffer, int offset, int count)
         public override void Write(byte[] buffer, int offset, int count)
         {
         {
             if (disposed)
             if (disposed)
@@ -340,11 +341,11 @@ namespace SocketHttpListener.Net
                 {
                 {
                     if (allowAsync)
                     if (allowAsync)
                     {
                     {
-                        await fs.CopyToAsync(targetStream, 81920, cancellationToken).ConfigureAwait(false);
+                        await fs.CopyToAsync(targetStream, StreamCopyToBufferSize, cancellationToken).ConfigureAwait(false);
                     }
                     }
                     else
                     else
                     {
                     {
-                        fs.CopyTo(targetStream, 81920);
+                        fs.CopyTo(targetStream, StreamCopyToBufferSize);
                     }
                     }
                 }
                 }
             }
             }
@@ -352,16 +353,11 @@ namespace SocketHttpListener.Net
 
 
         private static async Task CopyToInternalAsyncWithSyncRead(Stream source, Stream destination, long copyLength, CancellationToken cancellationToken)
         private static async Task CopyToInternalAsyncWithSyncRead(Stream source, Stream destination, long copyLength, CancellationToken cancellationToken)
         {
         {
-            var array = new byte[81920];
+            var array = new byte[StreamCopyToBufferSize];
             int bytesRead;
             int bytesRead;
 
 
             while ((bytesRead = source.Read(array, 0, array.Length)) != 0)
             while ((bytesRead = source.Read(array, 0, array.Length)) != 0)
             {
             {
-                if (bytesRead == 0)
-                {
-                    break;
-                }
-
                 var bytesToWrite = Math.Min(bytesRead, copyLength);
                 var bytesToWrite = Math.Min(bytesRead, copyLength);
 
 
                 if (bytesToWrite > 0)
                 if (bytesToWrite > 0)
@@ -380,16 +376,11 @@ namespace SocketHttpListener.Net
 
 
         private static async Task CopyToInternalAsync(Stream source, Stream destination, long copyLength, CancellationToken cancellationToken)
         private static async Task CopyToInternalAsync(Stream source, Stream destination, long copyLength, CancellationToken cancellationToken)
         {
         {
-            var array = new byte[81920];
+            var array = new byte[StreamCopyToBufferSize];
             int bytesRead;
             int bytesRead;
 
 
             while ((bytesRead = await source.ReadAsync(array, 0, array.Length, cancellationToken).ConfigureAwait(false)) != 0)
             while ((bytesRead = await source.ReadAsync(array, 0, array.Length, cancellationToken).ConfigureAwait(false)) != 0)
             {
             {
-                if (bytesRead == 0)
-                {
-                    break;
-                }
-
                 var bytesToWrite = Math.Min(bytesRead, copyLength);
                 var bytesToWrite = Math.Min(bytesRead, copyLength);
 
 
                 if (bytesToWrite > 0)
                 if (bytesToWrite > 0)