commit
f1f566c130
|
@ -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);
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -170,6 +170,19 @@ namespace MediaBrowser.Api.Playback.Progressive
|
|||
|
||||
using (state)
|
||||
{
|
||||
if (state.MediaSource.IsInfiniteStream)
|
||||
{
|
||||
var outputHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
outputHeaders["Content-Type"] = contentType;
|
||||
|
||||
var streamSource = new ProgressiveFileCopier(FileSystem, state.MediaPath, outputHeaders, null, Logger, CancellationToken.None)
|
||||
{
|
||||
AllowEndOfFile = false
|
||||
};
|
||||
return ResultFactory.GetAsyncStreamWriter(streamSource);
|
||||
}
|
||||
|
||||
TimeSpan? cacheDuration = null;
|
||||
|
||||
if (!string.IsNullOrEmpty(request.Tag))
|
||||
|
|
|
@ -23,7 +23,7 @@ namespace MediaBrowser.Api.Playback.Progressive
|
|||
private const int BufferSize = 81920;
|
||||
|
||||
private long _bytesWritten = 0;
|
||||
|
||||
public long StartPosition { get; set; }
|
||||
public bool AllowEndOfFile = true;
|
||||
|
||||
public ProgressiveFileCopier(IFileSystem fileSystem, string path, Dictionary<string, string> outputHeaders, TranscodingJob job, ILogger logger, CancellationToken cancellationToken)
|
||||
|
@ -52,6 +52,11 @@ namespace MediaBrowser.Api.Playback.Progressive
|
|||
|
||||
using (var fs = _fileSystem.GetFileStream(_path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true))
|
||||
{
|
||||
if (StartPosition > 0)
|
||||
{
|
||||
fs.Position = StartPosition;
|
||||
}
|
||||
|
||||
while (eofCount < 15 || !AllowEndOfFile)
|
||||
{
|
||||
var bytesRead = await CopyToAsyncInternal(fs, outputStream, BufferSize, _cancellationToken).ConfigureAwait(false);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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.");
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -27,7 +27,7 @@ namespace MediaBrowser.Model.Dto
|
|||
public bool SupportsTranscoding { get; set; }
|
||||
public bool SupportsDirectStream { get; set; }
|
||||
public bool SupportsDirectPlay { get; set; }
|
||||
|
||||
public bool IsInfiniteStream { get; set; }
|
||||
public bool RequiresOpening { get; set; }
|
||||
public string OpenToken { get; set; }
|
||||
public bool RequiresClosing { get; set; }
|
||||
|
|
|
@ -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[] { };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,7 +60,6 @@ namespace MediaBrowser.Model.LiveTv
|
|||
public TunerHostInfo()
|
||||
{
|
||||
IsEnabled = true;
|
||||
AllowHWTranscoding = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
internalQuery.OrderBy.Insert(0, new Tuple<string, SortOrder>(ItemSortBy.IsFavoriteOrLiked, SortOrder.Descending));
|
||||
}
|
||||
|
||||
return number;
|
||||
|
||||
});
|
||||
|
||||
if (query.IsFavorite.HasValue)
|
||||
if (!internalQuery.OrderBy.Any(i => string.Equals(i.Item1, ItemSortBy.SortName, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
var val = query.IsFavorite.Value;
|
||||
|
||||
channels = channels
|
||||
.Where(i => _userDataManager.GetUserData(user, i).IsFavorite == val);
|
||||
internalQuery.OrderBy.Add(new Tuple<string, SortOrder>(ItemSortBy.SortName, SortOrder.Ascending));
|
||||
}
|
||||
|
||||
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;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
allEnumerable = allEnumerable.Skip(query.StartIndex.Value);
|
||||
}
|
||||
|
||||
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;
|
||||
|
|
|
@ -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;
|
||||
|
@ -396,7 +419,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
|||
Id = id,
|
||||
SupportsDirectPlay = false,
|
||||
SupportsDirectStream = true,
|
||||
SupportsTranscoding = true
|
||||
SupportsTranscoding = true,
|
||||
IsInfiniteStream = true
|
||||
};
|
||||
|
||||
return mediaSource;
|
||||
|
@ -425,11 +449,13 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
|||
list.Add(await GetMediaSource(info, hdhrId, "native").ConfigureAwait(false));
|
||||
|
||||
try
|
||||
{
|
||||
if (info.AllowHWTranscoding)
|
||||
{
|
||||
string model = await GetModelInfo(info, cancellationToken).ConfigureAwait(false);
|
||||
model = model ?? string.Empty;
|
||||
|
||||
if (info.AllowHWTranscoding && (model.IndexOf("hdtc", StringComparison.OrdinalIgnoreCase) != -1))
|
||||
if ((model.IndexOf("hdtc", StringComparison.OrdinalIgnoreCase) != -1))
|
||||
{
|
||||
list.Add(await GetMediaSource(info, hdhrId, "heavy").ConfigureAwait(false));
|
||||
|
||||
|
@ -440,6 +466,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
|||
list.Add(await GetMediaSource(info, hdhrId, "mobile").ConfigureAwait(false));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
|
@ -473,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;
|
||||
}
|
||||
|
||||
|
@ -483,6 +527,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
|||
return;
|
||||
}
|
||||
|
||||
lock (_modelCache)
|
||||
{
|
||||
_modelCache.Clear();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Test it by pulling down the lineup
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -141,7 +141,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
|
|||
|
||||
ReadAtNativeFramerate = false,
|
||||
|
||||
Id = channel.Path.GetMD5().ToString("N")
|
||||
Id = channel.Path.GetMD5().ToString("N"),
|
||||
IsInfiniteStream = true
|
||||
};
|
||||
|
||||
return new List<MediaSourceInfo> { mediaSource };
|
||||
|
|
|
@ -1730,29 +1730,29 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
|||
return true;
|
||||
}
|
||||
|
||||
if (query.SortBy != null && query.SortBy.Length > 0)
|
||||
{
|
||||
if (query.SortBy.Contains(ItemSortBy.IsFavoriteOrLiked, StringComparer.OrdinalIgnoreCase))
|
||||
var sortingFields = query.SortBy.ToList();
|
||||
sortingFields.AddRange(query.OrderBy.Select(i => i.Item1));
|
||||
|
||||
if (sortingFields.Contains(ItemSortBy.IsFavoriteOrLiked, StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (query.SortBy.Contains(ItemSortBy.IsPlayed, StringComparer.OrdinalIgnoreCase))
|
||||
if (sortingFields.Contains(ItemSortBy.IsPlayed, StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (query.SortBy.Contains(ItemSortBy.IsUnplayed, StringComparer.OrdinalIgnoreCase))
|
||||
if (sortingFields.Contains(ItemSortBy.IsUnplayed, StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (query.SortBy.Contains(ItemSortBy.PlayCount, StringComparer.OrdinalIgnoreCase))
|
||||
if (sortingFields.Contains(ItemSortBy.PlayCount, StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (query.SortBy.Contains(ItemSortBy.DatePlayed, StringComparer.OrdinalIgnoreCase))
|
||||
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)
|
||||
{
|
||||
if (query.SimilarTo != null)
|
||||
var orderBy = query.OrderBy.ToList();
|
||||
var enableOrderInversion = true;
|
||||
|
||||
if (orderBy.Count == 0)
|
||||
{
|
||||
if (query.SortBy == null || query.SortBy.Length == 0)
|
||||
{
|
||||
if (query.User != null)
|
||||
{
|
||||
query.SortBy = new[] { "SimilarityScore", ItemSortBy.Random };
|
||||
orderBy.AddRange(query.SortBy.Select(i => new Tuple<string, SortOrder>(i, query.SortOrder)));
|
||||
}
|
||||
else
|
||||
{
|
||||
query.SortBy = new[] { "SimilarityScore", ItemSortBy.Random };
|
||||
enableOrderInversion = false;
|
||||
}
|
||||
|
||||
if (query.SimilarTo != null)
|
||||
{
|
||||
if (orderBy.Count == 0)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user