mirror of
https://github.com/jellyfin/jellyfin.git
synced 2025-04-24 14:08:44 -04:00
add new guide settings
This commit is contained in:
parent
dcfda6d777
commit
6a7fabc3bd
14 changed files with 256 additions and 166 deletions
|
@ -104,6 +104,26 @@ namespace MediaBrowser.Api.LiveTv
|
||||||
[ApiMember(Name = "EnableUserData", Description = "Optional, include user data", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
|
[ApiMember(Name = "EnableUserData", Description = "Optional, include user data", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
|
||||||
public bool? EnableUserData { get; set; }
|
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()
|
public GetChannels()
|
||||||
{
|
{
|
||||||
AddCurrentProgram = true;
|
AddCurrentProgram = true;
|
||||||
|
@ -650,6 +670,8 @@ namespace MediaBrowser.Api.LiveTv
|
||||||
{
|
{
|
||||||
public string Id { get; set; }
|
public string Id { get; set; }
|
||||||
public string Container { get; set; }
|
public string Container { get; set; }
|
||||||
|
public long T { get; set; }
|
||||||
|
public long S { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class LiveTvService : BaseApiService
|
public class LiveTvService : BaseApiService
|
||||||
|
@ -681,9 +703,35 @@ namespace MediaBrowser.Api.LiveTv
|
||||||
|
|
||||||
outputHeaders["Content-Type"] = MimeTypes.GetMimeType(filePath);
|
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)
|
var streamSource = new ProgressiveFileCopier(_fileSystem, filePath, outputHeaders, null, Logger, CancellationToken.None)
|
||||||
{
|
{
|
||||||
AllowEndOfFile = false
|
AllowEndOfFile = false,
|
||||||
|
StartPosition = startPosition
|
||||||
};
|
};
|
||||||
|
|
||||||
return ResultFactory.GetAsyncStreamWriter(streamSource);
|
return ResultFactory.GetAsyncStreamWriter(streamSource);
|
||||||
|
@ -848,6 +896,8 @@ namespace MediaBrowser.Api.LiveTv
|
||||||
IsNews = request.IsNews,
|
IsNews = request.IsNews,
|
||||||
IsKids = request.IsKids,
|
IsKids = request.IsKids,
|
||||||
IsSports = request.IsSports,
|
IsSports = request.IsSports,
|
||||||
|
SortBy = request.GetOrderBy(),
|
||||||
|
SortOrder = request.SortOrder ?? SortOrder.Ascending,
|
||||||
AddCurrentProgram = request.AddCurrentProgram
|
AddCurrentProgram = request.AddCurrentProgram
|
||||||
|
|
||||||
}, CancellationToken.None).ConfigureAwait(false);
|
}, CancellationToken.None).ConfigureAwait(false);
|
||||||
|
|
|
@ -2602,6 +2602,7 @@ namespace MediaBrowser.Api.Playback
|
||||||
inputModifier += " " + GetFastSeekCommandLineParameter(state.Request);
|
inputModifier += " " + GetFastSeekCommandLineParameter(state.Request);
|
||||||
inputModifier = inputModifier.Trim();
|
inputModifier = inputModifier.Trim();
|
||||||
|
|
||||||
|
//inputModifier += " -fflags +genpts+ignidx+igndts";
|
||||||
if (state.VideoRequest != null && genPts)
|
if (state.VideoRequest != null && genPts)
|
||||||
{
|
{
|
||||||
inputModifier += " -fflags +genpts";
|
inputModifier += " -fflags +genpts";
|
||||||
|
|
|
@ -151,6 +151,8 @@ namespace MediaBrowser.Controller.Entities
|
||||||
public Dictionary<string, string> ExcludeProviderIds { get; set; }
|
public Dictionary<string, string> ExcludeProviderIds { get; set; }
|
||||||
public bool EnableGroupByMetadataKey { get; set; }
|
public bool EnableGroupByMetadataKey { get; set; }
|
||||||
|
|
||||||
|
public List<Tuple<string, SortOrder>> OrderBy { get; set; }
|
||||||
|
|
||||||
public InternalItemsQuery()
|
public InternalItemsQuery()
|
||||||
{
|
{
|
||||||
GroupByPresentationUniqueKey = true;
|
GroupByPresentationUniqueKey = true;
|
||||||
|
@ -193,6 +195,7 @@ namespace MediaBrowser.Controller.Entities
|
||||||
TrailerTypes = new TrailerType[] { };
|
TrailerTypes = new TrailerType[] { };
|
||||||
AirDays = new DayOfWeek[] { };
|
AirDays = new DayOfWeek[] { };
|
||||||
SeriesStatuses = new SeriesStatus[] { };
|
SeriesStatuses = new SeriesStatus[] { };
|
||||||
|
OrderBy = new List<Tuple<string, SortOrder>>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public InternalItemsQuery(User user)
|
public InternalItemsQuery(User user)
|
||||||
|
|
|
@ -13,11 +13,13 @@ namespace MediaBrowser.Controller.LiveTv
|
||||||
public int ConsumerCount { get; set; }
|
public int ConsumerCount { get; set; }
|
||||||
public ITunerHost TunerHost { get; set; }
|
public ITunerHost TunerHost { get; set; }
|
||||||
public string OriginalStreamId { get; set; }
|
public string OriginalStreamId { get; set; }
|
||||||
|
public bool EnableStreamSharing { get; set; }
|
||||||
|
|
||||||
public LiveStream(MediaSourceInfo mediaSource)
|
public LiveStream(MediaSourceInfo mediaSource)
|
||||||
{
|
{
|
||||||
OriginalMediaSource = mediaSource;
|
OriginalMediaSource = mediaSource;
|
||||||
OpenedMediaSource = mediaSource;
|
OpenedMediaSource = mediaSource;
|
||||||
|
EnableStreamSharing = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Open(CancellationToken cancellationToken)
|
public async Task Open(CancellationToken cancellationToken)
|
||||||
|
|
|
@ -235,6 +235,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
throw new ResourceNotFoundException("ffprobe not found");
|
throw new ResourceNotFoundException("ffprobe not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
path = newPaths.Item1;
|
||||||
|
|
||||||
if (!ValidateVersion(path))
|
if (!ValidateVersion(path))
|
||||||
{
|
{
|
||||||
throw new ResourceNotFoundException("ffmpeg version 3.0 or greater is required.");
|
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("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));
|
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));
|
list.Add(new NameValuePair("StartTimeTicks", string.Empty));
|
||||||
}
|
}
|
||||||
else
|
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));
|
list.Add(new NameValuePair("Level", item.VideoLevel.HasValue ? StringHelper.ToStringCultureInvariant(item.VideoLevel.Value) : string.Empty));
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
|
using MediaBrowser.Model.Entities;
|
||||||
|
|
||||||
namespace MediaBrowser.Model.LiveTv
|
namespace MediaBrowser.Model.LiveTv
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -85,9 +86,18 @@ namespace MediaBrowser.Model.LiveTv
|
||||||
public bool? IsSports { get; set; }
|
public bool? IsSports { get; set; }
|
||||||
public bool? IsSeries { 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()
|
public LiveTvChannelQuery()
|
||||||
{
|
{
|
||||||
EnableUserData = true;
|
EnableUserData = true;
|
||||||
|
SortBy = new string[] { };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,7 +60,6 @@ namespace MediaBrowser.Model.LiveTv
|
||||||
public TunerHostInfo()
|
public TunerHostInfo()
|
||||||
{
|
{
|
||||||
IsEnabled = true;
|
IsEnabled = true;
|
||||||
AllowHWTranscoding = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -483,7 +483,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
||||||
|
|
||||||
if (existingTimer != null)
|
if (existingTimer != null)
|
||||||
{
|
{
|
||||||
if (existingTimer.Status == RecordingStatus.Cancelled)
|
if (existingTimer.Status == RecordingStatus.Cancelled ||
|
||||||
|
existingTimer.Status == RecordingStatus.Completed)
|
||||||
{
|
{
|
||||||
existingTimer.Status = RecordingStatus.New;
|
existingTimer.Status = RecordingStatus.New;
|
||||||
_timerProvider.Update(existingTimer);
|
_timerProvider.Update(existingTimer);
|
||||||
|
@ -832,12 +833,19 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
||||||
return result.Item2;
|
return result.Item2;
|
||||||
}
|
}
|
||||||
|
|
||||||
private MediaSourceInfo CloneMediaSource(MediaSourceInfo mediaSource, int consumerId)
|
private MediaSourceInfo CloneMediaSource(MediaSourceInfo mediaSource, int consumerId, bool enableStreamSharing)
|
||||||
{
|
{
|
||||||
var json = _jsonSerializer.SerializeToString(mediaSource);
|
var json = _jsonSerializer.SerializeToString(mediaSource);
|
||||||
mediaSource = _jsonSerializer.DeserializeFromString<MediaSourceInfo>(json);
|
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;
|
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));
|
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();
|
var openedMediaSource = CloneMediaSource(result.OpenedMediaSource, result.ConsumerCount - 1, result.EnableStreamSharing);
|
||||||
//return new Tuple<LiveStream, MediaSourceInfo, ITunerHost>(result, CloneMediaSource(result.OpenedMediaSource, result.ConsumerCount - 1), result.TunerHost);
|
_liveStreamsSemaphore.Release();
|
||||||
|
return new Tuple<LiveStream, MediaSourceInfo, ITunerHost>(result, openedMediaSource, result.TunerHost);
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
|
@ -868,16 +877,18 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
||||||
{
|
{
|
||||||
result = await hostInstance.GetChannelStream(channelId, streamId, cancellationToken).ConfigureAwait(false);
|
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.ConsumerCount++;
|
||||||
result.TunerHost = hostInstance;
|
result.TunerHost = hostInstance;
|
||||||
result.OriginalStreamId = streamId;
|
result.OriginalStreamId = streamId;
|
||||||
|
|
||||||
_logger.Info("Returning mediasource streamId {0}, mediaSource.Id {1}, mediaSource.LiveStreamId {2}",
|
_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)
|
catch (FileNotFoundException)
|
||||||
{
|
{
|
||||||
|
@ -925,7 +936,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
||||||
public async Task CloseLiveStream(string id, CancellationToken cancellationToken)
|
public async Task CloseLiveStream(string id, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
// Ignore the consumer id
|
// Ignore the consumer id
|
||||||
id = id.Substring(id.IndexOf('_') + 1);
|
//id = id.Substring(id.IndexOf('_') + 1);
|
||||||
|
|
||||||
await _liveStreamsSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
|
await _liveStreamsSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
|
@ -1143,8 +1154,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var allMediaSources =
|
var allMediaSources = await GetChannelStreamMediaSources(timer.ChannelId, CancellationToken.None).ConfigureAwait(false);
|
||||||
await GetChannelStreamMediaSources(timer.ChannelId, CancellationToken.None).ConfigureAwait(false);
|
|
||||||
|
|
||||||
var liveStreamInfo = await GetChannelStreamInternal(timer.ChannelId, allMediaSources[0].Id, CancellationToken.None)
|
var liveStreamInfo = await GetChannelStreamInternal(timer.ChannelId, allMediaSources[0].Id, CancellationToken.None)
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
|
|
|
@ -141,7 +141,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
||||||
{
|
{
|
||||||
var maxBitrate = 25000000;
|
var maxBitrate = 25000000;
|
||||||
videoArgs = string.Format(
|
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(),
|
GetOutputSizeParam(),
|
||||||
maxBitrate.ToString(CultureInfo.InvariantCulture));
|
maxBitrate.ToString(CultureInfo.InvariantCulture));
|
||||||
}
|
}
|
||||||
|
@ -151,16 +151,33 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
||||||
}
|
}
|
||||||
|
|
||||||
var durationParam = " -t " + _mediaEncoder.GetTimeParameter(duration.Ticks);
|
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)
|
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);
|
commandLineArgs = string.Format(commandLineArgs, inputTempFile, targetFile, videoArgs, GetAudioArgs(mediaSource), durationParam);
|
||||||
|
|
||||||
return commandLineArgs;
|
return inputModifiers + " " + commandLineArgs;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetAudioArgs(MediaSourceInfo mediaSource)
|
private string GetAudioArgs(MediaSourceInfo mediaSource)
|
||||||
|
|
|
@ -148,7 +148,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
|
||||||
|
|
||||||
var topFolder = await GetInternalLiveTvFolder(cancellationToken).ConfigureAwait(false);
|
var topFolder = await GetInternalLiveTvFolder(cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
var channels = _libraryManager.GetItemList(new InternalItemsQuery
|
var internalQuery = new InternalItemsQuery(user)
|
||||||
{
|
{
|
||||||
IsMovie = query.IsMovie,
|
IsMovie = query.IsMovie,
|
||||||
IsNews = query.IsNews,
|
IsNews = query.IsNews,
|
||||||
|
@ -156,109 +156,32 @@ namespace MediaBrowser.Server.Implementations.LiveTv
|
||||||
IsSports = query.IsSports,
|
IsSports = query.IsSports,
|
||||||
IsSeries = query.IsSeries,
|
IsSeries = query.IsSeries,
|
||||||
IncludeItemTypes = new[] { typeof(LiveTvChannel).Name },
|
IncludeItemTypes = new[] { typeof(LiveTvChannel).Name },
|
||||||
SortBy = new[] { ItemSortBy.SortName },
|
SortOrder = query.SortOrder ?? SortOrder.Ascending,
|
||||||
TopParentIds = new[] { topFolder.Id.ToString("N") }
|
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
|
internalQuery.OrderBy.Insert(0, new Tuple<string, SortOrder>(ItemSortBy.IsFavoriteOrLiked, SortOrder.Descending));
|
||||||
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;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var enableFavoriteSorting = query.EnableFavoriteSorting;
|
if (!internalQuery.OrderBy.Any(i => string.Equals(i.Item1, ItemSortBy.SortName, StringComparison.OrdinalIgnoreCase)))
|
||||||
|
|
||||||
channels = channels.OrderBy(i =>
|
|
||||||
{
|
{
|
||||||
if (enableFavoriteSorting)
|
internalQuery.OrderBy.Add(new Tuple<string, SortOrder>(ItemSortBy.SortName, SortOrder.Ascending));
|
||||||
{
|
|
||||||
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)
|
var channelResult = _libraryManager.GetItemsResult(internalQuery);
|
||||||
{
|
|
||||||
allEnumerable = allEnumerable.Take(query.Limit.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
var result = new QueryResult<LiveTvChannel>
|
var result = new QueryResult<LiveTvChannel>
|
||||||
{
|
{
|
||||||
Items = allEnumerable.ToArray(),
|
Items = channelResult.Items.Cast<LiveTvChannel>().ToArray(),
|
||||||
TotalRecordCount = allChannels.Count
|
TotalRecordCount = channelResult.TotalRecordCount
|
||||||
};
|
};
|
||||||
|
|
||||||
return result;
|
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)
|
private async Task<string> GetModelInfo(TunerHostInfo info, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
|
lock (_modelCache)
|
||||||
|
{
|
||||||
|
DiscoverResponse response;
|
||||||
|
if (_modelCache.TryGetValue(info.Url, out response))
|
||||||
|
{
|
||||||
|
return response.ModelNumber;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using (var stream = await _httpClient.Get(new HttpRequestOptions()
|
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);
|
var response = JsonSerializer.DeserializeFromStream<DiscoverResponse>(stream);
|
||||||
|
|
||||||
|
lock (_modelCache)
|
||||||
|
{
|
||||||
|
_modelCache[info.Id] = response;
|
||||||
|
}
|
||||||
|
|
||||||
return response.ModelNumber;
|
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)
|
if (ex.StatusCode.HasValue && ex.StatusCode.Value == System.Net.HttpStatusCode.NotFound)
|
||||||
{
|
{
|
||||||
|
var defaultValue = "HDHR";
|
||||||
// HDHR4 doesn't have this api
|
// HDHR4 doesn't have this api
|
||||||
return "HDHR";
|
lock (_modelCache)
|
||||||
|
{
|
||||||
|
_modelCache[info.Id] = new DiscoverResponse
|
||||||
|
{
|
||||||
|
ModelNumber = defaultValue
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw;
|
throw;
|
||||||
|
@ -427,18 +450,21 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
string model = await GetModelInfo(info, cancellationToken).ConfigureAwait(false);
|
if (info.AllowHWTranscoding)
|
||||||
model = model ?? string.Empty;
|
|
||||||
|
|
||||||
if (info.AllowHWTranscoding && (model.IndexOf("hdtc", StringComparison.OrdinalIgnoreCase) != -1))
|
|
||||||
{
|
{
|
||||||
list.Add(await GetMediaSource(info, hdhrId, "heavy").ConfigureAwait(false));
|
string model = await GetModelInfo(info, cancellationToken).ConfigureAwait(false);
|
||||||
|
model = model ?? string.Empty;
|
||||||
|
|
||||||
list.Add(await GetMediaSource(info, hdhrId, "internet540").ConfigureAwait(false));
|
if ((model.IndexOf("hdtc", StringComparison.OrdinalIgnoreCase) != -1))
|
||||||
list.Add(await GetMediaSource(info, hdhrId, "internet480").ConfigureAwait(false));
|
{
|
||||||
list.Add(await GetMediaSource(info, hdhrId, "internet360").ConfigureAwait(false));
|
list.Add(await GetMediaSource(info, hdhrId, "heavy").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
|
catch
|
||||||
|
@ -474,6 +500,23 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
||||||
var mediaSource = await GetMediaSource(info, hdhrId, profile).ConfigureAwait(false);
|
var mediaSource = await GetMediaSource(info, hdhrId, profile).ConfigureAwait(false);
|
||||||
|
|
||||||
var liveStream = new HdHomerunLiveStream(mediaSource, _fileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost);
|
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;
|
return liveStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -484,6 +527,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lock (_modelCache)
|
||||||
|
{
|
||||||
|
_modelCache.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Test it by pulling down the lineup
|
// 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);
|
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.Path = _appHost.GetLocalApiUrl("localhost") + "/LiveTv/LiveStreamFiles/" + Path.GetFileNameWithoutExtension(tempFile) + "/stream.ts";
|
||||||
|
|
||||||
OpenedMediaSource.Protocol = MediaProtocol.Http;
|
OpenedMediaSource.Protocol = MediaProtocol.Http;
|
||||||
|
|
||||||
|
await taskCompletionSource.Task.ConfigureAwait(false);
|
||||||
|
|
||||||
|
//await Task.Delay(5000).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Task Close()
|
public override Task Close()
|
||||||
|
|
|
@ -1730,28 +1730,28 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
||||||
return true;
|
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;
|
||||||
{
|
}
|
||||||
return true;
|
if (sortingFields.Contains(ItemSortBy.IsPlayed, StringComparer.OrdinalIgnoreCase))
|
||||||
}
|
{
|
||||||
if (query.SortBy.Contains(ItemSortBy.IsPlayed, StringComparer.OrdinalIgnoreCase))
|
return true;
|
||||||
{
|
}
|
||||||
return true;
|
if (sortingFields.Contains(ItemSortBy.IsUnplayed, StringComparer.OrdinalIgnoreCase))
|
||||||
}
|
{
|
||||||
if (query.SortBy.Contains(ItemSortBy.IsUnplayed, StringComparer.OrdinalIgnoreCase))
|
return true;
|
||||||
{
|
}
|
||||||
return true;
|
if (sortingFields.Contains(ItemSortBy.PlayCount, StringComparer.OrdinalIgnoreCase))
|
||||||
}
|
{
|
||||||
if (query.SortBy.Contains(ItemSortBy.PlayCount, StringComparer.OrdinalIgnoreCase))
|
return true;
|
||||||
{
|
}
|
||||||
return true;
|
if (sortingFields.Contains(ItemSortBy.DatePlayed, StringComparer.OrdinalIgnoreCase))
|
||||||
}
|
{
|
||||||
if (query.SortBy.Contains(ItemSortBy.DatePlayed, StringComparer.OrdinalIgnoreCase))
|
return true;
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (query.IsFavoriteOrLiked.HasValue)
|
if (query.IsFavoriteOrLiked.HasValue)
|
||||||
|
@ -2151,34 +2151,41 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
||||||
|
|
||||||
private string GetOrderByText(InternalItemsQuery query)
|
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.SimilarTo != null)
|
||||||
{
|
{
|
||||||
if (query.SortBy == null || query.SortBy.Length == 0)
|
if (orderBy.Count == 0)
|
||||||
{
|
{
|
||||||
if (query.User != null)
|
orderBy.Add(new Tuple<string, SortOrder>("SimilarityScore", SortOrder.Descending));
|
||||||
{
|
orderBy.Add(new Tuple<string, SortOrder>(ItemSortBy.Random, SortOrder.Ascending));
|
||||||
query.SortBy = new[] { "SimilarityScore", ItemSortBy.Random };
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
query.SortBy = new[] { "SimilarityScore", ItemSortBy.Random };
|
|
||||||
}
|
|
||||||
query.SortOrder = SortOrder.Descending;
|
query.SortOrder = SortOrder.Descending;
|
||||||
|
enableOrderInversion = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (query.SortBy == null || query.SortBy.Length == 0)
|
query.OrderBy = orderBy;
|
||||||
|
|
||||||
|
if (orderBy.Count == 0)
|
||||||
{
|
{
|
||||||
return string.Empty;
|
return string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
var isAscending = query.SortOrder != SortOrder.Descending;
|
return " ORDER BY " + string.Join(",", orderBy.Select(i =>
|
||||||
|
|
||||||
return " ORDER BY " + string.Join(",", query.SortBy.Select(i =>
|
|
||||||
{
|
{
|
||||||
var columnMap = MapOrderByField(i, query);
|
var columnMap = MapOrderByField(i.Item1, query);
|
||||||
var columnAscending = isAscending;
|
var columnAscending = i.Item2 == SortOrder.Ascending;
|
||||||
if (columnMap.Item2)
|
if (columnMap.Item2 && enableOrderInversion)
|
||||||
{
|
{
|
||||||
columnAscending = !columnAscending;
|
columnAscending = !columnAscending;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue