commit
0b0aa85d1f
|
@ -205,6 +205,7 @@ namespace MediaBrowser.Api.UserLibrary
|
||||||
private void SetItemCounts(BaseItemDto dto, ItemCounts counts)
|
private void SetItemCounts(BaseItemDto dto, ItemCounts counts)
|
||||||
{
|
{
|
||||||
dto.ChildCount = counts.ItemCount;
|
dto.ChildCount = counts.ItemCount;
|
||||||
|
dto.ProgramCount = counts.ProgramCount;
|
||||||
dto.SeriesCount = counts.SeriesCount;
|
dto.SeriesCount = counts.SeriesCount;
|
||||||
dto.EpisodeCount = counts.EpisodeCount;
|
dto.EpisodeCount = counts.EpisodeCount;
|
||||||
dto.MovieCount = counts.MovieCount;
|
dto.MovieCount = counts.MovieCount;
|
||||||
|
|
|
@ -293,12 +293,9 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
|
||||||
/// </exception>
|
/// </exception>
|
||||||
public async Task<HttpResponseInfo> SendAsync(HttpRequestOptions options, string httpMethod)
|
public async Task<HttpResponseInfo> SendAsync(HttpRequestOptions options, string httpMethod)
|
||||||
{
|
{
|
||||||
HttpResponseInfo response;
|
|
||||||
|
|
||||||
if (options.CacheMode == CacheMode.None)
|
if (options.CacheMode == CacheMode.None)
|
||||||
{
|
{
|
||||||
response = await SendAsyncInternal(options, httpMethod).ConfigureAwait(false);
|
return await SendAsyncInternal(options, httpMethod).ConfigureAwait(false);
|
||||||
return response;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var url = options.Url;
|
var url = options.Url;
|
||||||
|
@ -306,7 +303,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
|
||||||
|
|
||||||
var responseCachePath = Path.Combine(_appPaths.CachePath, "httpclient", urlHash);
|
var responseCachePath = Path.Combine(_appPaths.CachePath, "httpclient", urlHash);
|
||||||
|
|
||||||
response = await GetCachedResponse(responseCachePath, options.CacheLength, url).ConfigureAwait(false);
|
var response = await GetCachedResponse(responseCachePath, options.CacheLength, url).ConfigureAwait(false);
|
||||||
if (response != null)
|
if (response != null)
|
||||||
{
|
{
|
||||||
return response;
|
return response;
|
||||||
|
|
|
@ -826,6 +826,7 @@ namespace MediaBrowser.Model.Dto
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The series count.</value>
|
/// <value>The series count.</value>
|
||||||
public int? SeriesCount { get; set; }
|
public int? SeriesCount { get; set; }
|
||||||
|
public int? ProgramCount { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the episode count.
|
/// Gets or sets the episode count.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
/// <value>The game count.</value>
|
/// <value>The game count.</value>
|
||||||
public int GameCount { get; set; }
|
public int GameCount { get; set; }
|
||||||
public int ArtistCount { get; set; }
|
public int ArtistCount { get; set; }
|
||||||
|
public int ProgramCount { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the game system count.
|
/// Gets or sets the game system count.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -20,6 +20,7 @@ using System.Text.RegularExpressions;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using CommonIO;
|
using CommonIO;
|
||||||
|
using MediaBrowser.Controller.LiveTv;
|
||||||
using MediaBrowser.Providers.TV;
|
using MediaBrowser.Providers.TV;
|
||||||
|
|
||||||
namespace MediaBrowser.Providers.Movies
|
namespace MediaBrowser.Providers.Movies
|
||||||
|
@ -59,29 +60,12 @@ namespace MediaBrowser.Providers.Movies
|
||||||
|
|
||||||
public bool Supports(IHasImages item)
|
public bool Supports(IHasImages item)
|
||||||
{
|
{
|
||||||
//var channelItem = item as IChannelMediaItem;
|
|
||||||
|
|
||||||
//if (channelItem != null)
|
|
||||||
//{
|
|
||||||
// if (channelItem.ContentType == ChannelMediaContentType.Movie)
|
|
||||||
// {
|
|
||||||
// return true;
|
|
||||||
// }
|
|
||||||
// if (channelItem.ContentType == ChannelMediaContentType.MovieExtra)
|
|
||||||
// {
|
|
||||||
// if (channelItem.ExtraType == ExtraType.Trailer)
|
|
||||||
// {
|
|
||||||
// return true;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
// Supports images for tv movies
|
// Supports images for tv movies
|
||||||
//var tvProgram = item as LiveTvProgram;
|
var tvProgram = item as LiveTvProgram;
|
||||||
//if (tvProgram != null && tvProgram.IsMovie)
|
if (tvProgram != null && tvProgram.IsMovie)
|
||||||
//{
|
{
|
||||||
// return true;
|
return true;
|
||||||
//}
|
}
|
||||||
|
|
||||||
return item is Movie || item is BoxSet || item is MusicVideo;
|
return item is Movie || item is BoxSet || item is MusicVideo;
|
||||||
}
|
}
|
||||||
|
|
|
@ -437,6 +437,7 @@ namespace MediaBrowser.Server.Implementations.Dto
|
||||||
dto.TrailerCount = taggedItems.Count(i => i is Trailer);
|
dto.TrailerCount = taggedItems.Count(i => i is Trailer);
|
||||||
dto.MusicVideoCount = taggedItems.Count(i => i is MusicVideo);
|
dto.MusicVideoCount = taggedItems.Count(i => i is MusicVideo);
|
||||||
dto.SeriesCount = taggedItems.Count(i => i is Series);
|
dto.SeriesCount = taggedItems.Count(i => i is Series);
|
||||||
|
dto.ProgramCount = taggedItems.Count(i => i is LiveTvProgram);
|
||||||
dto.SongCount = taggedItems.Count(i => i is Audio);
|
dto.SongCount = taggedItems.Count(i => i is Audio);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -61,11 +61,44 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
||||||
cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token).Token;
|
cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token).Token;
|
||||||
}
|
}
|
||||||
|
|
||||||
await response.Content.CopyToAsync(output, StreamDefaults.DefaultCopyToBufferSize, cancellationToken).ConfigureAwait(false);
|
await CopyUntilCancelled(response.Content, output, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.Info("Recording completed to file {0}", targetFile);
|
_logger.Info("Recording completed to file {0}", targetFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private const int BufferSize = 81920;
|
||||||
|
public static async Task CopyUntilCancelled(Stream source, Stream target, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
while (!cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
var bytesRead = await CopyToAsyncInternal(source, target, BufferSize, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
|
//var position = fs.Position;
|
||||||
|
//_logger.Debug("Streamed {0} bytes to position {1} from file {2}", bytesRead, position, path);
|
||||||
|
|
||||||
|
if (bytesRead == 0)
|
||||||
|
{
|
||||||
|
await Task.Delay(100).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<int> CopyToAsyncInternal(Stream source, Stream destination, Int32 bufferSize, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
byte[] buffer = new byte[bufferSize];
|
||||||
|
int bytesRead;
|
||||||
|
int totalBytesRead = 0;
|
||||||
|
|
||||||
|
while ((bytesRead = await source.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false)) != 0)
|
||||||
|
{
|
||||||
|
await destination.WriteAsync(buffer, 0, bytesRead, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
|
totalBytesRead += bytesRead;
|
||||||
|
}
|
||||||
|
|
||||||
|
return totalBytesRead;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -580,7 +580,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
||||||
PrePaddingSeconds = Math.Max(config.PrePaddingSeconds, 0),
|
PrePaddingSeconds = Math.Max(config.PrePaddingSeconds, 0),
|
||||||
RecordAnyChannel = true,
|
RecordAnyChannel = true,
|
||||||
RecordAnyTime = true,
|
RecordAnyTime = true,
|
||||||
RecordNewOnly = false,
|
RecordNewOnly = true,
|
||||||
|
|
||||||
Days = new List<DayOfWeek>
|
Days = new List<DayOfWeek>
|
||||||
{
|
{
|
||||||
|
@ -730,6 +730,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
||||||
|
|
||||||
return result.Item1;
|
return result.Item1;
|
||||||
}
|
}
|
||||||
|
catch (FileNotFoundException)
|
||||||
|
{
|
||||||
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
_logger.ErrorException("Error getting channel stream", e);
|
_logger.ErrorException("Error getting channel stream", e);
|
||||||
|
@ -751,6 +754,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
||||||
|
|
||||||
return new Tuple<MediaSourceInfo, ITunerHost, SemaphoreSlim>(result.Item1, hostInstance, result.Item2);
|
return new Tuple<MediaSourceInfo, ITunerHost, SemaphoreSlim>(result.Item1, hostInstance, result.Item2);
|
||||||
}
|
}
|
||||||
|
catch (FileNotFoundException)
|
||||||
|
{
|
||||||
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
_logger.ErrorException("Error getting channel stream", e);
|
_logger.ErrorException("Error getting channel stream", e);
|
||||||
|
@ -1213,7 +1219,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exclude programs that have already ended
|
// Exclude programs that have already ended
|
||||||
allPrograms = allPrograms.Where(i => i.EndDate > DateTime.UtcNow && i.StartDate > DateTime.UtcNow);
|
allPrograms = allPrograms.Where(i => i.EndDate > DateTime.UtcNow);
|
||||||
|
|
||||||
allPrograms = GetProgramsForSeries(seriesTimer, allPrograms);
|
allPrograms = GetProgramsForSeries(seriesTimer, allPrograms);
|
||||||
|
|
||||||
|
|
|
@ -78,21 +78,33 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
||||||
|
|
||||||
var tempfile = Path.Combine(_appPaths.TranscodingTempPath, Guid.NewGuid().ToString("N") + ".ts");
|
var tempfile = Path.Combine(_appPaths.TranscodingTempPath, Guid.NewGuid().ToString("N") + ".ts");
|
||||||
|
|
||||||
try
|
await RecordWithTempFile(mediaSource, tempfile, targetFile, duration, onStarted, cancellationToken)
|
||||||
{
|
.ConfigureAwait(false);
|
||||||
await RecordWithTempFile(mediaSource, tempfile, targetFile, duration, onStarted, cancellationToken)
|
}
|
||||||
.ConfigureAwait(false);
|
|
||||||
}
|
private async void DeleteTempFile(string path)
|
||||||
finally
|
{
|
||||||
|
for (var i = 0; i < 10; i++)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
File.Delete(tempfile);
|
File.Delete(path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
catch (FileNotFoundException)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
catch (DirectoryNotFoundException)
|
||||||
|
{
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.ErrorException("Error deleting recording temp file", ex);
|
_logger.ErrorException("Error deleting recording temp file", ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await Task.Delay(1000).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,7 +113,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
||||||
var durationToken = new CancellationTokenSource(duration);
|
var durationToken = new CancellationTokenSource(duration);
|
||||||
cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token).Token;
|
cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token).Token;
|
||||||
|
|
||||||
await RecordFromFile(mediaSource, mediaSource.Path, targetFile, duration, onStarted, cancellationToken).ConfigureAwait(false);
|
await RecordFromFile(mediaSource, mediaSource.Path, targetFile, false, duration, onStarted, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
_logger.Info("Recording completed to file {0}", targetFile);
|
_logger.Info("Recording completed to file {0}", targetFile);
|
||||||
}
|
}
|
||||||
|
@ -143,14 +155,21 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
||||||
cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token).Token;
|
cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token).Token;
|
||||||
}
|
}
|
||||||
|
|
||||||
var tempFileTask = response.Content.CopyToAsync(output, StreamDefaults.DefaultCopyToBufferSize, cancellationToken);
|
var tempFileTask = DirectRecorder.CopyUntilCancelled(response.Content, output, cancellationToken);
|
||||||
|
|
||||||
// Give the temp file a little time to build up
|
// Give the temp file a little time to build up
|
||||||
await Task.Delay(bufferMs, cancellationToken).ConfigureAwait(false);
|
await Task.Delay(bufferMs, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
var recordTask = Task.Run(() => RecordFromFile(mediaSource, tempFile, targetFile, duration, onStarted, cancellationToken), cancellationToken);
|
var recordTask = Task.Run(() => RecordFromFile(mediaSource, tempFile, targetFile, true, duration, onStarted, cancellationToken), CancellationToken.None);
|
||||||
|
|
||||||
await tempFileTask.ConfigureAwait(false);
|
try
|
||||||
|
{
|
||||||
|
await tempFileTask.ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
await recordTask.ConfigureAwait(false);
|
await recordTask.ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
@ -159,7 +178,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
||||||
_logger.Info("Recording completed to file {0}", targetFile);
|
_logger.Info("Recording completed to file {0}", targetFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task RecordFromFile(MediaSourceInfo mediaSource, string inputFile, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken)
|
private Task RecordFromFile(MediaSourceInfo mediaSource, string inputFile, string targetFile, bool deleteInputFileAfterCompletion, TimeSpan duration, Action onStarted, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
_targetPath = targetFile;
|
_targetPath = targetFile;
|
||||||
_fileSystem.CreateDirectory(Path.GetDirectoryName(targetFile));
|
_fileSystem.CreateDirectory(Path.GetDirectoryName(targetFile));
|
||||||
|
@ -200,7 +219,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
||||||
var commandLineLogMessageBytes = Encoding.UTF8.GetBytes(_json.SerializeToString(mediaSource) + Environment.NewLine + Environment.NewLine + commandLineLogMessage + Environment.NewLine + Environment.NewLine);
|
var commandLineLogMessageBytes = Encoding.UTF8.GetBytes(_json.SerializeToString(mediaSource) + Environment.NewLine + Environment.NewLine + commandLineLogMessage + Environment.NewLine + Environment.NewLine);
|
||||||
_logFileStream.Write(commandLineLogMessageBytes, 0, commandLineLogMessageBytes.Length);
|
_logFileStream.Write(commandLineLogMessageBytes, 0, commandLineLogMessageBytes.Length);
|
||||||
|
|
||||||
process.Exited += (sender, args) => OnFfMpegProcessExited(process);
|
process.Exited += (sender, args) => OnFfMpegProcessExited(process, inputFile, deleteInputFileAfterCompletion);
|
||||||
|
|
||||||
process.Start();
|
process.Start();
|
||||||
|
|
||||||
|
@ -309,8 +328,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Processes the exited.
|
/// Processes the exited.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="process">The process.</param>
|
private void OnFfMpegProcessExited(Process process, string inputFile, bool deleteInputFileAfterCompletion)
|
||||||
private void OnFfMpegProcessExited(Process process)
|
|
||||||
{
|
{
|
||||||
_hasExited = true;
|
_hasExited = true;
|
||||||
|
|
||||||
|
@ -336,6 +354,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
||||||
_logger.Error("FFMpeg recording exited with an error for {0}.", _targetPath);
|
_logger.Error("FFMpeg recording exited with an error for {0}.", _targetPath);
|
||||||
_taskCompletionSource.TrySetException(new Exception(string.Format("Recording for {0} failed", _targetPath)));
|
_taskCompletionSource.TrySetException(new Exception(string.Format("Recording for {0} failed", _targetPath)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (deleteInputFileAfterCompletion)
|
||||||
|
{
|
||||||
|
DeleteTempFile(inputFile);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DisposeLogStream()
|
private void DisposeLogStream()
|
||||||
|
|
|
@ -6,6 +6,7 @@ using MediaBrowser.Model.Logging;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
@ -195,7 +196,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
|
||||||
}
|
}
|
||||||
else if (streamId.StartsWith(host.Id, StringComparison.OrdinalIgnoreCase))
|
else if (streamId.StartsWith(host.Id, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
hostsWithChannel = new List<TunerHostInfo> { host };
|
hostsWithChannel = new List<TunerHostInfo> {host};
|
||||||
streamId = streamId.Substring(host.Id.Length);
|
streamId = streamId.Substring(host.Id.Length);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -222,7 +223,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var stream = await GetChannelStream(host, channelId, streamId, cancellationToken).ConfigureAwait(false);
|
var stream =
|
||||||
|
await GetChannelStream(host, channelId, streamId, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
if (EnableMediaProbing)
|
if (EnableMediaProbing)
|
||||||
{
|
{
|
||||||
|
@ -239,6 +241,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new FileNotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
throw new LiveTvConflictException();
|
throw new LiveTvConflictException();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user