瀏覽代碼

add new guide settings

Luke Pulverenti 8 年之前
父節點
當前提交
6a7fabc3bd

+ 51 - 1
MediaBrowser.Api/LiveTv/LiveTvService.cs

@@ -104,6 +104,26 @@ namespace MediaBrowser.Api.LiveTv
         [ApiMember(Name = "EnableUserData", Description = "Optional, include user data", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
         public bool? EnableUserData { get; set; }
 
+        public string SortBy { get; set; }
+
+        public SortOrder? SortOrder { get; set; }
+
+        /// <summary>
+        /// Gets the order by.
+        /// </summary>
+        /// <returns>IEnumerable{ItemSortBy}.</returns>
+        public string[] GetOrderBy()
+        {
+            var val = SortBy;
+
+            if (string.IsNullOrEmpty(val))
+            {
+                return new string[] { };
+            }
+
+            return val.Split(',');
+        }
+
         public GetChannels()
         {
             AddCurrentProgram = true;
@@ -650,6 +670,8 @@ namespace MediaBrowser.Api.LiveTv
     {
         public string Id { get; set; }
         public string Container { get; set; }
+        public long T { get; set; }
+        public long S { get; set; }
     }
 
     public class LiveTvService : BaseApiService
@@ -681,9 +703,35 @@ namespace MediaBrowser.Api.LiveTv
 
             outputHeaders["Content-Type"] = MimeTypes.GetMimeType(filePath);
 
+            long startPosition = 0;
+
+            if (request.T > 0)
+            {
+                var now = DateTime.UtcNow;
+
+                var totalTicks = now.Ticks - request.S;
+
+                if (totalTicks > 0)
+                {
+                    double requestedOffset = request.T;
+                    requestedOffset = Math.Max(0, requestedOffset - TimeSpan.FromSeconds(10).Ticks);
+
+                    var pct = requestedOffset / totalTicks;
+
+                    Logger.Info("Live stream offset pct {0}", pct);
+
+                    var bytes = new FileInfo(filePath).Length;
+                    Logger.Info("Live stream total bytes {0}", bytes);
+                    startPosition = Convert.ToInt64(pct * bytes);
+                }
+            }
+
+            Logger.Info("Live stream starting byte position {0}", startPosition);
+
             var streamSource = new ProgressiveFileCopier(_fileSystem, filePath, outputHeaders, null, Logger, CancellationToken.None)
             {
-                AllowEndOfFile = false
+                AllowEndOfFile = false,
+                StartPosition = startPosition
             };
 
             return ResultFactory.GetAsyncStreamWriter(streamSource);
@@ -848,6 +896,8 @@ namespace MediaBrowser.Api.LiveTv
                 IsNews = request.IsNews,
                 IsKids = request.IsKids,
                 IsSports = request.IsSports,
+                SortBy = request.GetOrderBy(),
+                SortOrder = request.SortOrder ?? SortOrder.Ascending,
                 AddCurrentProgram = request.AddCurrentProgram
 
             }, CancellationToken.None).ConfigureAwait(false);

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

@@ -2602,6 +2602,7 @@ namespace MediaBrowser.Api.Playback
             inputModifier += " " + GetFastSeekCommandLineParameter(state.Request);
             inputModifier = inputModifier.Trim();
 
+            //inputModifier += " -fflags +genpts+ignidx+igndts";
             if (state.VideoRequest != null && genPts)
             {
                 inputModifier += " -fflags +genpts";

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

@@ -151,6 +151,8 @@ namespace MediaBrowser.Controller.Entities
         public Dictionary<string, string> ExcludeProviderIds { get; set; }
         public bool EnableGroupByMetadataKey { get; set; }
 
+        public List<Tuple<string, SortOrder>> OrderBy { get; set; }
+
         public InternalItemsQuery()
         {
             GroupByPresentationUniqueKey = true;
@@ -193,6 +195,7 @@ namespace MediaBrowser.Controller.Entities
             TrailerTypes = new TrailerType[] { };
             AirDays = new DayOfWeek[] { };
             SeriesStatuses = new SeriesStatus[] { };
+            OrderBy = new List<Tuple<string, SortOrder>>();
         }
 
         public InternalItemsQuery(User user)

+ 2 - 0
MediaBrowser.Controller/LiveTv/LiveStream.cs

@@ -13,11 +13,13 @@ namespace MediaBrowser.Controller.LiveTv
         public int ConsumerCount { get; set; }
         public ITunerHost TunerHost { get; set; }
         public string OriginalStreamId { get; set; }
+        public bool EnableStreamSharing { get; set; }
 
         public LiveStream(MediaSourceInfo mediaSource)
         {
             OriginalMediaSource = mediaSource;
             OpenedMediaSource = mediaSource;
+            EnableStreamSharing = true;
         }
 
         public async Task Open(CancellationToken cancellationToken)

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

@@ -235,6 +235,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
                 throw new ResourceNotFoundException("ffprobe not found");
             }
 
+            path = newPaths.Item1;
+
             if (!ValidateVersion(path))
             {
                 throw new ResourceNotFoundException("ffmpeg version 3.0 or greater is required.");

+ 15 - 2
MediaBrowser.Model/Dlna/StreamInfo.cs

@@ -215,13 +215,26 @@ namespace MediaBrowser.Model.Dlna
             list.Add(new NameValuePair("MaxWidth", item.MaxWidth.HasValue ? StringHelper.ToStringCultureInvariant(item.MaxWidth.Value) : string.Empty));
             list.Add(new NameValuePair("MaxHeight", item.MaxHeight.HasValue ? StringHelper.ToStringCultureInvariant(item.MaxHeight.Value) : string.Empty));
 
-            if (StringHelper.EqualsIgnoreCase(item.SubProtocol, "hls"))
+            var forceStartPosition = false;
+            long startPositionTicks = item.StartPositionTicks;
+            //if (item.MediaSource.DateLiveStreamOpened.HasValue && startPositionTicks == 0)
+            //{
+            //    var elapsed = DateTime.UtcNow - item.MediaSource.DateLiveStreamOpened.Value;
+            //    elapsed -= TimeSpan.FromSeconds(20);
+            //    if (elapsed.TotalSeconds >= 0)
+            //    {
+            //        startPositionTicks = elapsed.Ticks + startPositionTicks;
+            //        forceStartPosition = true;
+            //    }
+            //}
+
+            if (StringHelper.EqualsIgnoreCase(item.SubProtocol, "hls") && !forceStartPosition)
             {
                 list.Add(new NameValuePair("StartTimeTicks", string.Empty));
             }
             else
             {
-                list.Add(new NameValuePair("StartTimeTicks", StringHelper.ToStringCultureInvariant(item.StartPositionTicks)));
+                list.Add(new NameValuePair("StartTimeTicks", StringHelper.ToStringCultureInvariant(startPositionTicks)));
             }
 
             list.Add(new NameValuePair("Level", item.VideoLevel.HasValue ? StringHelper.ToStringCultureInvariant(item.VideoLevel.Value) : string.Empty));

+ 11 - 1
MediaBrowser.Model/LiveTv/LiveTvChannelQuery.cs

@@ -1,4 +1,5 @@
-
+using MediaBrowser.Model.Entities;
+
 namespace MediaBrowser.Model.LiveTv
 {
     /// <summary>
@@ -85,9 +86,18 @@ namespace MediaBrowser.Model.LiveTv
         public bool? IsSports { get; set; }
         public bool? IsSeries { get; set; }
 
+        public string[] SortBy { get; set; }
+
+        /// <summary>
+        /// The sort order to return results with
+        /// </summary>
+        /// <value>The sort order.</value>
+        public SortOrder? SortOrder { get; set; }
+
         public LiveTvChannelQuery()
         {
             EnableUserData = true;
+            SortBy = new string[] { };
         }
     }
 }

+ 0 - 1
MediaBrowser.Model/LiveTv/LiveTvOptions.cs

@@ -60,7 +60,6 @@ namespace MediaBrowser.Model.LiveTv
         public TunerHostInfo()
         {
             IsEnabled = true;
-            AllowHWTranscoding = true;
         }
     }
 

+ 24 - 14
MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs

@@ -483,7 +483,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
 
             if (existingTimer != null)
             {
-                if (existingTimer.Status == RecordingStatus.Cancelled)
+                if (existingTimer.Status == RecordingStatus.Cancelled ||
+                    existingTimer.Status == RecordingStatus.Completed)
                 {
                     existingTimer.Status = RecordingStatus.New;
                     _timerProvider.Update(existingTimer);
@@ -832,12 +833,19 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
             return result.Item2;
         }
 
-        private MediaSourceInfo CloneMediaSource(MediaSourceInfo mediaSource, int consumerId)
+        private MediaSourceInfo CloneMediaSource(MediaSourceInfo mediaSource, int consumerId, bool enableStreamSharing)
         {
             var json = _jsonSerializer.SerializeToString(mediaSource);
             mediaSource = _jsonSerializer.DeserializeFromString<MediaSourceInfo>(json);
 
-            mediaSource.Id = consumerId.ToString(CultureInfo.InvariantCulture) + "_" + mediaSource.Id;
+            mediaSource.Id = Guid.NewGuid().ToString("N") + "_" + mediaSource.Id;
+
+            if (mediaSource.DateLiveStreamOpened.HasValue && enableStreamSharing)
+            {
+                var ticks = (DateTime.UtcNow - mediaSource.DateLiveStreamOpened.Value).Ticks - TimeSpan.FromSeconds(10).Ticks;
+                ticks = Math.Max(0, ticks);
+                mediaSource.Path += "?t=" + ticks.ToString(CultureInfo.InvariantCulture) + "&s=" + mediaSource.DateLiveStreamOpened.Value.Ticks.ToString(CultureInfo.InvariantCulture);
+            }
 
             return mediaSource;
         }
@@ -850,14 +858,15 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
 
             var result = _liveStreams.Values.FirstOrDefault(i => string.Equals(i.OriginalStreamId, streamId, StringComparison.OrdinalIgnoreCase));
 
-            if (result != null)
+            if (result != null && result.EnableStreamSharing)
             {
-                //result.ConsumerCount++;
+                result.ConsumerCount++;
 
-                //_logger.Info("Live stream {0} consumer count is now {1}", streamId, result.ConsumerCount);
+                _logger.Info("Live stream {0} consumer count is now {1}", streamId, result.ConsumerCount);
 
-                //_liveStreamsSemaphore.Release();
-                //return new Tuple<LiveStream, MediaSourceInfo, ITunerHost>(result, CloneMediaSource(result.OpenedMediaSource, result.ConsumerCount - 1), result.TunerHost);
+                var openedMediaSource = CloneMediaSource(result.OpenedMediaSource, result.ConsumerCount - 1, result.EnableStreamSharing);
+                _liveStreamsSemaphore.Release();
+                return new Tuple<LiveStream, MediaSourceInfo, ITunerHost>(result, openedMediaSource, result.TunerHost);
             }
 
             try
@@ -868,16 +877,18 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
                     {
                         result = await hostInstance.GetChannelStream(channelId, streamId, cancellationToken).ConfigureAwait(false);
 
-                        _liveStreams[result.OpenedMediaSource.Id] = result;
+                        var openedMediaSource = CloneMediaSource(result.OpenedMediaSource, 0, result.EnableStreamSharing);
+
+                        _liveStreams[openedMediaSource.Id] = result;
 
                         result.ConsumerCount++;
                         result.TunerHost = hostInstance;
                         result.OriginalStreamId = streamId;
 
                         _logger.Info("Returning mediasource streamId {0}, mediaSource.Id {1}, mediaSource.LiveStreamId {2}",
-                            streamId, result.OpenedMediaSource.Id, result.OpenedMediaSource.LiveStreamId);
+                            streamId, openedMediaSource.Id, openedMediaSource.LiveStreamId);
 
-                        return new Tuple<LiveStream, MediaSourceInfo, ITunerHost>(result, CloneMediaSource(result.OpenedMediaSource, 0), hostInstance);
+                        return new Tuple<LiveStream, MediaSourceInfo, ITunerHost>(result, openedMediaSource, hostInstance);
                     }
                     catch (FileNotFoundException)
                     {
@@ -925,7 +936,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
         public async Task CloseLiveStream(string id, CancellationToken cancellationToken)
         {
             // Ignore the consumer id
-            id = id.Substring(id.IndexOf('_') + 1);
+            //id = id.Substring(id.IndexOf('_') + 1);
 
             await _liveStreamsSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
 
@@ -1143,8 +1154,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
 
             try
             {
-                var allMediaSources =
-                    await GetChannelStreamMediaSources(timer.ChannelId, CancellationToken.None).ConfigureAwait(false);
+                var allMediaSources = await GetChannelStreamMediaSources(timer.ChannelId, CancellationToken.None).ConfigureAwait(false);
 
                 var liveStreamInfo = await GetChannelStreamInternal(timer.ChannelId, allMediaSources[0].Id, CancellationToken.None)
                             .ConfigureAwait(false);

+ 21 - 4
MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs

@@ -141,7 +141,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
             {
                 var maxBitrate = 25000000;
                 videoArgs = string.Format(
-                        "-codec:v:0 libx264 -force_key_frames \"expr:gte(t,n_forced*5)\" {0} -pix_fmt yuv420p -preset superfast -crf 23 -b:v {1} -maxrate {1} -bufsize ({1}*2) -vsync -1 -profile:v high -level 41",
+                        "-codec:v:0 libx264 -force_key_frames \"expr:gte(t,n_forced*5)\" {0} -pix_fmt yuv420p -preset superfast -crf 23 -b:v {1} -maxrate {1} -bufsize ({1}*2) -vsync -1 -profile:v high -level 41 -tune zerolatency",
                         GetOutputSizeParam(),
                         maxBitrate.ToString(CultureInfo.InvariantCulture));
             }
@@ -151,16 +151,33 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
             }
 
             var durationParam = " -t " + _mediaEncoder.GetTimeParameter(duration.Ticks);
-            var commandLineArgs = "-fflags +genpts -async 1 -vsync -1 -i \"{0}\"{4} -sn {2} -map_metadata -1 -threads 0 {3} -y \"{1}\"";
+            var inputModifiers = "-fflags +genpts -async 1 -vsync -1";
+            var commandLineArgs = "-i \"{0}\"{4} -sn {2} -map_metadata -1 -threads 0 {3} -y \"{1}\"";
+
+            long startTimeTicks = 0;
+            //if (mediaSource.DateLiveStreamOpened.HasValue)
+            //{
+            //    var elapsed = DateTime.UtcNow - mediaSource.DateLiveStreamOpened.Value;
+            //    elapsed -= TimeSpan.FromSeconds(10);
+            //    if (elapsed.TotalSeconds >= 0)
+            //    {
+            //        startTimeTicks = elapsed.Ticks + startTimeTicks;
+            //    }
+            //}
 
             if (mediaSource.ReadAtNativeFramerate)
             {
-                commandLineArgs = "-re " + commandLineArgs;
+                inputModifiers += " -re";
+            }
+
+            if (startTimeTicks > 0)
+            {
+                inputModifiers = "-ss " + _mediaEncoder.GetTimeParameter(startTimeTicks) + " " + inputModifiers;
             }
 
             commandLineArgs = string.Format(commandLineArgs, inputTempFile, targetFile, videoArgs, GetAudioArgs(mediaSource), durationParam);
 
-            return commandLineArgs;
+            return inputModifiers + " " + commandLineArgs;
         }
 
         private string GetAudioArgs(MediaSourceInfo mediaSource)

+ 16 - 93
MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs

@@ -148,7 +148,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
 
             var topFolder = await GetInternalLiveTvFolder(cancellationToken).ConfigureAwait(false);
 
-            var channels = _libraryManager.GetItemList(new InternalItemsQuery
+            var internalQuery = new InternalItemsQuery(user)
             {
                 IsMovie = query.IsMovie,
                 IsNews = query.IsNews,
@@ -156,109 +156,32 @@ namespace MediaBrowser.Server.Implementations.LiveTv
                 IsSports = query.IsSports,
                 IsSeries = query.IsSeries,
                 IncludeItemTypes = new[] { typeof(LiveTvChannel).Name },
-                SortBy = new[] { ItemSortBy.SortName },
-                TopParentIds = new[] { topFolder.Id.ToString("N") }
+                SortOrder = query.SortOrder ?? SortOrder.Ascending,
+                TopParentIds = new[] { topFolder.Id.ToString("N") },
+                IsFavorite = query.IsFavorite,
+                IsLiked = query.IsLiked,
+                StartIndex = query.StartIndex,
+                Limit = query.Limit
+            };
 
-            }).Cast<LiveTvChannel>();
+            internalQuery.OrderBy.AddRange(query.SortBy.Select(i => new Tuple<string, SortOrder>(i, query.SortOrder ?? SortOrder.Ascending)));
 
-            if (user != null)
+            if (query.EnableFavoriteSorting)
             {
-                // Avoid implicitly captured closure
-                var currentUser = user;
-
-                channels = channels
-                    .Where(i => i.IsVisible(currentUser))
-                    .OrderBy(i =>
-                    {
-                        double number = 0;
-
-                        if (!string.IsNullOrEmpty(i.Number))
-                        {
-                            double.TryParse(i.Number, out number);
-                        }
-
-                        return number;
-
-                    });
-
-                if (query.IsFavorite.HasValue)
-                {
-                    var val = query.IsFavorite.Value;
-
-                    channels = channels
-                        .Where(i => _userDataManager.GetUserData(user, i).IsFavorite == val);
-                }
-
-                if (query.IsLiked.HasValue)
-                {
-                    var val = query.IsLiked.Value;
-
-                    channels = channels
-                        .Where(i =>
-                        {
-                            var likes = _userDataManager.GetUserData(user, i).Likes;
-
-                            return likes.HasValue && likes.Value == val;
-                        });
-                }
-
-                if (query.IsDisliked.HasValue)
-                {
-                    var val = query.IsDisliked.Value;
-
-                    channels = channels
-                        .Where(i =>
-                        {
-                            var likes = _userDataManager.GetUserData(user, i).Likes;
-
-                            return likes.HasValue && likes.Value != val;
-                        });
-                }
+                internalQuery.OrderBy.Insert(0, new Tuple<string, SortOrder>(ItemSortBy.IsFavoriteOrLiked, SortOrder.Descending));
             }
 
-            var enableFavoriteSorting = query.EnableFavoriteSorting;
-
-            channels = channels.OrderBy(i =>
-            {
-                if (enableFavoriteSorting)
-                {
-                    var userData = _userDataManager.GetUserData(user, i);
-
-                    if (userData.IsFavorite)
-                    {
-                        return 0;
-                    }
-                    if (userData.Likes.HasValue)
-                    {
-                        if (!userData.Likes.Value)
-                        {
-                            return 3;
-                        }
-
-                        return 1;
-                    }
-                }
-
-                return 2;
-            });
-
-            var allChannels = channels.ToList();
-            IEnumerable<LiveTvChannel> allEnumerable = allChannels;
-
-            if (query.StartIndex.HasValue)
+            if (!internalQuery.OrderBy.Any(i => string.Equals(i.Item1, ItemSortBy.SortName, StringComparison.OrdinalIgnoreCase)))
             {
-                allEnumerable = allEnumerable.Skip(query.StartIndex.Value);
+                internalQuery.OrderBy.Add(new Tuple<string, SortOrder>(ItemSortBy.SortName, SortOrder.Ascending));
             }
 
-            if (query.Limit.HasValue)
-            {
-                allEnumerable = allEnumerable.Take(query.Limit.Value);
-            }
+            var channelResult = _libraryManager.GetItemsResult(internalQuery);
 
             var result = new QueryResult<LiveTvChannel>
             {
-                Items = allEnumerable.ToArray(),
-                TotalRecordCount = allChannels.Count
+                Items = channelResult.Items.Cast<LiveTvChannel>().ToArray(),
+                TotalRecordCount = channelResult.TotalRecordCount
             };
 
             return result;

+ 59 - 11
MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs

@@ -104,8 +104,18 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun
             });
         }
 
+        private Dictionary<string, DiscoverResponse> _modelCache = new Dictionary<string, DiscoverResponse>();
         private async Task<string> GetModelInfo(TunerHostInfo info, CancellationToken cancellationToken)
         {
+            lock (_modelCache)
+            {
+                DiscoverResponse response;
+                if (_modelCache.TryGetValue(info.Url, out response))
+                {
+                    return response.ModelNumber;
+                }
+            }
+
             try
             {
                 using (var stream = await _httpClient.Get(new HttpRequestOptions()
@@ -119,6 +129,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun
                 {
                     var response = JsonSerializer.DeserializeFromStream<DiscoverResponse>(stream);
 
+                    lock (_modelCache)
+                    {
+                        _modelCache[info.Id] = response;
+                    }
+
                     return response.ModelNumber;
                 }
             }
@@ -126,8 +141,16 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun
             {
                 if (ex.StatusCode.HasValue && ex.StatusCode.Value == System.Net.HttpStatusCode.NotFound)
                 {
+                    var defaultValue = "HDHR";
                     // HDHR4 doesn't have this api
-                    return "HDHR";
+                    lock (_modelCache)
+                    {
+                        _modelCache[info.Id] = new DiscoverResponse
+                        {
+                            ModelNumber = defaultValue
+                        };
+                    }
+                    return defaultValue;
                 }
 
                 throw;
@@ -427,18 +450,21 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun
 
             try
             {
-                string model = await GetModelInfo(info, cancellationToken).ConfigureAwait(false);
-                model = model ?? string.Empty;
-
-                if (info.AllowHWTranscoding && (model.IndexOf("hdtc", StringComparison.OrdinalIgnoreCase) != -1))
+                if (info.AllowHWTranscoding)
                 {
-                    list.Add(await GetMediaSource(info, hdhrId, "heavy").ConfigureAwait(false));
+                    string model = await GetModelInfo(info, cancellationToken).ConfigureAwait(false);
+                    model = model ?? string.Empty;
+
+                    if ((model.IndexOf("hdtc", StringComparison.OrdinalIgnoreCase) != -1))
+                    {
+                        list.Add(await GetMediaSource(info, hdhrId, "heavy").ConfigureAwait(false));
 
-                    list.Add(await GetMediaSource(info, hdhrId, "internet540").ConfigureAwait(false));
-                    list.Add(await GetMediaSource(info, hdhrId, "internet480").ConfigureAwait(false));
-                    list.Add(await GetMediaSource(info, hdhrId, "internet360").ConfigureAwait(false));
-                    list.Add(await GetMediaSource(info, hdhrId, "internet240").ConfigureAwait(false));
-                    list.Add(await GetMediaSource(info, hdhrId, "mobile").ConfigureAwait(false));
+                        list.Add(await GetMediaSource(info, hdhrId, "internet540").ConfigureAwait(false));
+                        list.Add(await GetMediaSource(info, hdhrId, "internet480").ConfigureAwait(false));
+                        list.Add(await GetMediaSource(info, hdhrId, "internet360").ConfigureAwait(false));
+                        list.Add(await GetMediaSource(info, hdhrId, "internet240").ConfigureAwait(false));
+                        list.Add(await GetMediaSource(info, hdhrId, "mobile").ConfigureAwait(false));
+                    }
                 }
             }
             catch
@@ -474,6 +500,23 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun
             var mediaSource = await GetMediaSource(info, hdhrId, profile).ConfigureAwait(false);
 
             var liveStream = new HdHomerunLiveStream(mediaSource, _fileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost);
+            if (info.AllowHWTranscoding)
+            {
+                var model = await GetModelInfo(info, cancellationToken).ConfigureAwait(false);
+
+                if ((model ?? string.Empty).IndexOf("hdtc", StringComparison.OrdinalIgnoreCase) != -1)
+                {
+                    liveStream.EnableStreamSharing = !info.AllowHWTranscoding;
+                }
+                else
+                {
+                    liveStream.EnableStreamSharing = true;
+                }
+            }
+            else
+            {
+                liveStream.EnableStreamSharing = true;
+            }
             return liveStream;
         }
 
@@ -484,6 +527,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun
                 return;
             }
 
+            lock (_modelCache)
+            {
+                _modelCache.Clear();
+            }
+
             try
             {
                 // Test it by pulling down the lineup

+ 7 - 2
MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunLiveStream.cs

@@ -52,11 +52,16 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun
 
             StartStreamingToTempFile(output, tempFile, url, taskCompletionSource, _liveStreamCancellationTokenSource.Token);
 
-            await taskCompletionSource.Task.ConfigureAwait(false);
+            //OpenedMediaSource.Protocol = MediaProtocol.File;
+            //OpenedMediaSource.Path = tempFile;
+            //OpenedMediaSource.ReadAtNativeFramerate = true;
 
             OpenedMediaSource.Path = _appHost.GetLocalApiUrl("localhost") + "/LiveTv/LiveStreamFiles/" + Path.GetFileNameWithoutExtension(tempFile) + "/stream.ts";
-
             OpenedMediaSource.Protocol = MediaProtocol.Http;
+
+            await taskCompletionSource.Task.ConfigureAwait(false);
+
+            //await Task.Delay(5000).ConfigureAwait(false);
         }
 
         public override Task Close()

+ 44 - 37
MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs

@@ -1730,28 +1730,28 @@ namespace MediaBrowser.Server.Implementations.Persistence
                 return true;
             }
 
-            if (query.SortBy != null && query.SortBy.Length > 0)
+            var sortingFields = query.SortBy.ToList();
+            sortingFields.AddRange(query.OrderBy.Select(i => i.Item1));
+
+            if (sortingFields.Contains(ItemSortBy.IsFavoriteOrLiked, StringComparer.OrdinalIgnoreCase))
             {
-                if (query.SortBy.Contains(ItemSortBy.IsFavoriteOrLiked, StringComparer.OrdinalIgnoreCase))
-                {
-                    return true;
-                }
-                if (query.SortBy.Contains(ItemSortBy.IsPlayed, StringComparer.OrdinalIgnoreCase))
-                {
-                    return true;
-                }
-                if (query.SortBy.Contains(ItemSortBy.IsUnplayed, StringComparer.OrdinalIgnoreCase))
-                {
-                    return true;
-                }
-                if (query.SortBy.Contains(ItemSortBy.PlayCount, StringComparer.OrdinalIgnoreCase))
-                {
-                    return true;
-                }
-                if (query.SortBy.Contains(ItemSortBy.DatePlayed, StringComparer.OrdinalIgnoreCase))
-                {
-                    return true;
-                }
+                return true;
+            }
+            if (sortingFields.Contains(ItemSortBy.IsPlayed, StringComparer.OrdinalIgnoreCase))
+            {
+                return true;
+            }
+            if (sortingFields.Contains(ItemSortBy.IsUnplayed, StringComparer.OrdinalIgnoreCase))
+            {
+                return true;
+            }
+            if (sortingFields.Contains(ItemSortBy.PlayCount, StringComparer.OrdinalIgnoreCase))
+            {
+                return true;
+            }
+            if (sortingFields.Contains(ItemSortBy.DatePlayed, StringComparer.OrdinalIgnoreCase))
+            {
+                return true;
             }
 
             if (query.IsFavoriteOrLiked.HasValue)
@@ -2151,34 +2151,41 @@ namespace MediaBrowser.Server.Implementations.Persistence
 
         private string GetOrderByText(InternalItemsQuery query)
         {
+            var orderBy = query.OrderBy.ToList();
+            var enableOrderInversion = true;
+
+            if (orderBy.Count == 0)
+            {
+                orderBy.AddRange(query.SortBy.Select(i => new Tuple<string, SortOrder>(i, query.SortOrder)));
+            }
+            else
+            {
+                enableOrderInversion = false;
+            }
+
             if (query.SimilarTo != null)
             {
-                if (query.SortBy == null || query.SortBy.Length == 0)
+                if (orderBy.Count == 0)
                 {
-                    if (query.User != null)
-                    {
-                        query.SortBy = new[] { "SimilarityScore", ItemSortBy.Random };
-                    }
-                    else
-                    {
-                        query.SortBy = new[] { "SimilarityScore", ItemSortBy.Random };
-                    }
+                    orderBy.Add(new Tuple<string, SortOrder>("SimilarityScore", SortOrder.Descending));
+                    orderBy.Add(new Tuple<string, SortOrder>(ItemSortBy.Random, SortOrder.Ascending));
                     query.SortOrder = SortOrder.Descending;
+                    enableOrderInversion = false;
                 }
             }
 
-            if (query.SortBy == null || query.SortBy.Length == 0)
+            query.OrderBy = orderBy;
+
+            if (orderBy.Count == 0)
             {
                 return string.Empty;
             }
 
-            var isAscending = query.SortOrder != SortOrder.Descending;
-
-            return " ORDER BY " + string.Join(",", query.SortBy.Select(i =>
+            return " ORDER BY " + string.Join(",", orderBy.Select(i =>
             {
-                var columnMap = MapOrderByField(i, query);
-                var columnAscending = isAscending;
-                if (columnMap.Item2)
+                var columnMap = MapOrderByField(i.Item1, query);
+                var columnAscending = i.Item2 == SortOrder.Ascending;
+                if (columnMap.Item2 && enableOrderInversion)
                 {
                     columnAscending = !columnAscending;
                 }