Merge pull request #1683 from MediaBrowser/dev

Dev
This commit is contained in:
Luke 2016-04-25 23:53:52 -04:00
commit 9126a8555b
10 changed files with 194 additions and 36 deletions

View File

@ -243,6 +243,8 @@ namespace MediaBrowser.Controller.Library
/// <returns>BaseItem.</returns> /// <returns>BaseItem.</returns>
BaseItem RetrieveItem(Guid id); BaseItem RetrieveItem(Guid id);
bool IsScanRunning { get; }
/// <summary> /// <summary>
/// Occurs when [item added]. /// Occurs when [item added].
/// </summary> /// </summary>

View File

@ -46,6 +46,8 @@ namespace MediaBrowser.Controller.LiveTv
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task&lt;List&lt;MediaSourceInfo&gt;&gt;.</returns> /// <returns>Task&lt;List&lt;MediaSourceInfo&gt;&gt;.</returns>
Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(string channelId, CancellationToken cancellationToken); Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(string channelId, CancellationToken cancellationToken);
string ApplyDuration(string streamPath, TimeSpan duration);
} }
public interface IConfigurableTunerHost public interface IConfigurableTunerHost
{ {

View File

@ -27,6 +27,8 @@ namespace MediaBrowser.Providers.TV
private readonly IFileSystem _fileSystem; private readonly IFileSystem _fileSystem;
private readonly CultureInfo _usCulture = new CultureInfo("en-US"); private readonly CultureInfo _usCulture = new CultureInfo("en-US");
private static readonly SemaphoreSlim _resourceLock = new SemaphoreSlim(1, 1);
public static bool IsRunning = false;
public MissingEpisodeProvider(ILogger logger, IServerConfigurationManager config, ILibraryManager libraryManager, ILocalizationManager localization, IFileSystem fileSystem) public MissingEpisodeProvider(ILogger logger, IServerConfigurationManager config, ILibraryManager libraryManager, ILocalizationManager localization, IFileSystem fileSystem)
{ {
@ -37,13 +39,16 @@ namespace MediaBrowser.Providers.TV
_fileSystem = fileSystem; _fileSystem = fileSystem;
} }
public async Task Run(IEnumerable<IGrouping<string, Series>> series, CancellationToken cancellationToken) public async Task Run(List<IGrouping<string, Series>> series, bool addNewItems, CancellationToken cancellationToken)
{ {
await _resourceLock.WaitAsync(cancellationToken).ConfigureAwait(false);
IsRunning = true;
foreach (var seriesGroup in series) foreach (var seriesGroup in series)
{ {
try try
{ {
await Run(seriesGroup, cancellationToken).ConfigureAwait(false); await Run(seriesGroup, addNewItems, cancellationToken).ConfigureAwait(false);
} }
catch (OperationCanceledException) catch (OperationCanceledException)
{ {
@ -58,9 +63,12 @@ namespace MediaBrowser.Providers.TV
_logger.ErrorException("Error in missing episode provider for series id {0}", ex, seriesGroup.Key); _logger.ErrorException("Error in missing episode provider for series id {0}", ex, seriesGroup.Key);
} }
} }
IsRunning = false;
_resourceLock.Release();
} }
private async Task Run(IGrouping<string, Series> group, CancellationToken cancellationToken) private async Task Run(IGrouping<string, Series> group, bool addNewItems, CancellationToken cancellationToken)
{ {
var tvdbId = group.Key; var tvdbId = group.Key;
@ -110,7 +118,7 @@ namespace MediaBrowser.Providers.TV
var hasNewEpisodes = false; var hasNewEpisodes = false;
if (_config.Configuration.EnableInternetProviders) if (_config.Configuration.EnableInternetProviders && addNewItems)
{ {
var seriesConfig = _config.Configuration.MetadataOptions.FirstOrDefault(i => string.Equals(i.ItemType, typeof(Series).Name, StringComparison.OrdinalIgnoreCase)); var seriesConfig = _config.Configuration.MetadataOptions.FirstOrDefault(i => string.Equals(i.ItemType, typeof(Series).Name, StringComparison.OrdinalIgnoreCase));

View File

@ -11,6 +11,10 @@ using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using CommonIO; using CommonIO;
using MediaBrowser.Common.ScheduledTasks;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Plugins;
using MediaBrowser.Model.Tasks;
namespace MediaBrowser.Providers.TV namespace MediaBrowser.Providers.TV
{ {
@ -46,14 +50,17 @@ namespace MediaBrowser.Providers.TV
private async Task RunInternal(IProgress<double> progress, CancellationToken cancellationToken) private async Task RunInternal(IProgress<double> progress, CancellationToken cancellationToken)
{ {
var seriesList = _libraryManager.RootFolder var seriesList = _libraryManager.GetItemList(new InternalItemsQuery()
.GetRecursiveChildren(i => i is Series) {
.Cast<Series>() IncludeItemTypes = new[] { typeof(Series).Name },
.ToList(); Recursive = true
}).Cast<Series>().ToList();
var seriesGroups = FindSeriesGroups(seriesList).Where(g => !string.IsNullOrEmpty(g.Key)).ToList(); var seriesGroups = FindSeriesGroups(seriesList).Where(g => !string.IsNullOrEmpty(g.Key)).ToList();
await new MissingEpisodeProvider(_logger, _config, _libraryManager, _localization, _fileSystem).Run(seriesGroups, cancellationToken).ConfigureAwait(false); await new MissingEpisodeProvider(_logger, _config, _libraryManager, _localization, _fileSystem)
.Run(seriesGroups, true, cancellationToken).ConfigureAwait(false);
var numComplete = 0; var numComplete = 0;
@ -82,7 +89,7 @@ namespace MediaBrowser.Providers.TV
} }
} }
private IEnumerable<IGrouping<string, Series>> FindSeriesGroups(List<Series> seriesList) internal static IEnumerable<IGrouping<string, Series>> FindSeriesGroups(List<Series> seriesList)
{ {
var links = seriesList.ToDictionary(s => s, s => seriesList.Where(c => c != s && ShareProviderId(s, c)).ToList()); var links = seriesList.ToDictionary(s => s, s => seriesList.Where(c => c != s && ShareProviderId(s, c)).ToList());
@ -102,7 +109,7 @@ namespace MediaBrowser.Providers.TV
} }
} }
private void FindAllLinked(Series series, HashSet<Series> visited, IDictionary<Series, List<Series>> linksMap, List<Series> results) private static void FindAllLinked(Series series, HashSet<Series> visited, IDictionary<Series, List<Series>> linksMap, List<Series> results)
{ {
results.Add(series); results.Add(series);
visited.Add(series); visited.Add(series);
@ -118,7 +125,7 @@ namespace MediaBrowser.Providers.TV
} }
} }
private bool ShareProviderId(Series a, Series b) private static bool ShareProviderId(Series a, Series b)
{ {
return a.ProviderIds.Any(id => return a.ProviderIds.Any(id =>
{ {
@ -137,4 +144,108 @@ namespace MediaBrowser.Providers.TV
} }
} }
public class CleanMissingEpisodesEntryPoint : IServerEntryPoint
{
private readonly ILibraryManager _libraryManager;
private readonly IServerConfigurationManager _config;
private readonly ILogger _logger;
private readonly ILocalizationManager _localization;
private readonly IFileSystem _fileSystem;
private readonly object _libraryChangedSyncLock = new object();
private const int LibraryUpdateDuration = 180000;
private readonly ITaskManager _taskManager;
public CleanMissingEpisodesEntryPoint(ILibraryManager libraryManager, IServerConfigurationManager config, ILogger logger, ILocalizationManager localization, IFileSystem fileSystem, ITaskManager taskManager)
{
_libraryManager = libraryManager;
_config = config;
_logger = logger;
_localization = localization;
_fileSystem = fileSystem;
_taskManager = taskManager;
}
private Timer LibraryUpdateTimer { get; set; }
public void Run()
{
_libraryManager.ItemAdded += _libraryManager_ItemAdded;
}
private void _libraryManager_ItemAdded(object sender, ItemChangeEventArgs e)
{
if (!FilterItem(e.Item))
{
return;
}
lock (_libraryChangedSyncLock)
{
if (LibraryUpdateTimer == null)
{
LibraryUpdateTimer = new Timer(LibraryUpdateTimerCallback, null, LibraryUpdateDuration, Timeout.Infinite);
}
else
{
LibraryUpdateTimer.Change(LibraryUpdateDuration, Timeout.Infinite);
}
}
}
private async void LibraryUpdateTimerCallback(object state)
{
if (MissingEpisodeProvider.IsRunning)
{
return;
}
if (_libraryManager.IsScanRunning)
{
return ;
}
var seriesList = _libraryManager.GetItemList(new InternalItemsQuery()
{
IncludeItemTypes = new[] { typeof(Series).Name },
Recursive = true
}).Cast<Series>().ToList();
var seriesGroups = SeriesPostScanTask.FindSeriesGroups(seriesList).Where(g => !string.IsNullOrEmpty(g.Key)).ToList();
await new MissingEpisodeProvider(_logger, _config, _libraryManager, _localization, _fileSystem)
.Run(seriesGroups, false, CancellationToken.None).ConfigureAwait(false);
}
private bool FilterItem(BaseItem item)
{
return item is Episode && item.LocationType != LocationType.Virtual;
}
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
Dispose(true);
}
/// <summary>
/// Releases unmanaged and - optionally - managed resources.
/// </summary>
/// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
protected virtual void Dispose(bool dispose)
{
if (dispose)
{
if (LibraryUpdateTimer != null)
{
LibraryUpdateTimer.Dispose();
LibraryUpdateTimer = null;
}
_libraryManager.ItemAdded -= _libraryManager_ItemAdded;
}
}
}
} }

View File

@ -143,6 +143,7 @@ namespace MediaBrowser.Server.Implementations.Library
private readonly Func<ILibraryMonitor> _libraryMonitorFactory; private readonly Func<ILibraryMonitor> _libraryMonitorFactory;
private readonly Func<IProviderManager> _providerManagerFactory; private readonly Func<IProviderManager> _providerManagerFactory;
private readonly Func<IUserViewManager> _userviewManager; private readonly Func<IUserViewManager> _userviewManager;
public bool IsScanRunning { get; private set; }
/// <summary> /// <summary>
/// The _library items cache /// The _library items cache
@ -1102,6 +1103,7 @@ namespace MediaBrowser.Server.Implementations.Library
/// <returns>Task.</returns> /// <returns>Task.</returns>
public async Task ValidateMediaLibraryInternal(IProgress<double> progress, CancellationToken cancellationToken) public async Task ValidateMediaLibraryInternal(IProgress<double> progress, CancellationToken cancellationToken)
{ {
IsScanRunning = true;
_libraryMonitorFactory().Stop(); _libraryMonitorFactory().Stop();
try try
@ -1111,6 +1113,7 @@ namespace MediaBrowser.Server.Implementations.Library
finally finally
{ {
_libraryMonitorFactory().Start(); _libraryMonitorFactory().Start();
IsScanRunning = false;
} }
} }

View File

@ -42,10 +42,13 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
_logger.Info("Copying recording stream to file {0}", targetFile); _logger.Info("Copying recording stream to file {0}", targetFile);
if (!mediaSource.RunTimeTicks.HasValue)
{
var durationToken = new CancellationTokenSource(duration); var durationToken = new CancellationTokenSource(duration);
var linkedToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token).Token; cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token).Token;
}
await response.Content.CopyToAsync(output, StreamDefaults.DefaultCopyToBufferSize, linkedToken).ConfigureAwait(false); await response.Content.CopyToAsync(output, StreamDefaults.DefaultCopyToBufferSize, cancellationToken).ConfigureAwait(false);
} }
} }

View File

@ -591,7 +591,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
throw new ApplicationException("Tuner not found."); throw new ApplicationException("Tuner not found.");
} }
private async Task<Tuple<MediaSourceInfo, SemaphoreSlim>> GetChannelStreamInternal(string channelId, string streamId, CancellationToken cancellationToken) private async Task<Tuple<MediaSourceInfo, ITunerHost, SemaphoreSlim>> GetChannelStreamInternal(string channelId, string streamId, CancellationToken cancellationToken)
{ {
_logger.Info("Streaming Channel " + channelId); _logger.Info("Streaming Channel " + channelId);
@ -599,7 +599,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
{ {
try try
{ {
return await hostInstance.GetChannelStream(channelId, streamId, cancellationToken).ConfigureAwait(false); var result = await hostInstance.GetChannelStream(channelId, streamId, cancellationToken).ConfigureAwait(false);
return new Tuple<MediaSourceInfo, ITunerHost, SemaphoreSlim>(result.Item1, hostInstance, result.Item2);
} }
catch (Exception e) catch (Exception e)
{ {
@ -797,8 +799,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
// HDHR doesn't seem to release the tuner right away after first probing with ffmpeg // HDHR doesn't seem to release the tuner right away after first probing with ffmpeg
//await Task.Delay(3000, cancellationToken).ConfigureAwait(false); //await Task.Delay(3000, cancellationToken).ConfigureAwait(false);
var duration = recordingEndDate - DateTime.UtcNow;
var recorder = await GetRecorder().ConfigureAwait(false); var recorder = await GetRecorder().ConfigureAwait(false);
if (recorder is EncodedRecorder) if (recorder is EncodedRecorder)
@ -816,6 +816,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
recording.DateLastUpdated = DateTime.UtcNow; recording.DateLastUpdated = DateTime.UtcNow;
_recordingProvider.AddOrUpdate(recording); _recordingProvider.AddOrUpdate(recording);
var duration = recordingEndDate - DateTime.UtcNow;
_logger.Info("Beginning recording. Will record for {0} minutes.", duration.TotalMinutes.ToString(CultureInfo.InvariantCulture)); _logger.Info("Beginning recording. Will record for {0} minutes.", duration.TotalMinutes.ToString(CultureInfo.InvariantCulture));
_logger.Info("Writing file to path: " + recordPath); _logger.Info("Writing file to path: " + recordPath);
@ -823,10 +825,19 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
Action onStarted = () => Action onStarted = () =>
{ {
result.Item2.Release(); result.Item3.Release();
isResourceOpen = false; isResourceOpen = false;
}; };
var pathWithDuration = result.Item2.ApplyDuration(mediaStreamInfo.Path, duration);
// If it supports supplying duration via url
if (!string.Equals(pathWithDuration, mediaStreamInfo.Path, StringComparison.OrdinalIgnoreCase))
{
mediaStreamInfo.Path = pathWithDuration;
mediaStreamInfo.RunTimeTicks = duration.Ticks;
}
await recorder.Record(mediaStreamInfo, recordPath, duration, onStarted, cancellationToken).ConfigureAwait(false); await recorder.Record(mediaStreamInfo, recordPath, duration, onStarted, cancellationToken).ConfigureAwait(false);
recording.Status = RecordingStatus.Completed; recording.Status = RecordingStatus.Completed;
@ -836,7 +847,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
{ {
if (isResourceOpen) if (isResourceOpen)
{ {
result.Item2.Release(); result.Item3.Release();
} }
_libraryMonitor.ReportFileSystemChangeComplete(recordPath, false); _libraryMonitor.ReportFileSystemChangeComplete(recordPath, false);

View File

@ -59,6 +59,14 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun
return id; return id;
} }
public string ApplyDuration(string streamPath, TimeSpan duration)
{
streamPath += streamPath.IndexOf('?') == -1 ? "?" : "&";
streamPath += "duration=" + Convert.ToInt32(duration.TotalSeconds).ToString(CultureInfo.InvariantCulture);
return streamPath;
}
private async Task<IEnumerable<Channels>> GetLineup(TunerHostInfo info, CancellationToken cancellationToken) private async Task<IEnumerable<Channels>> GetLineup(TunerHostInfo info, CancellationToken cancellationToken)
{ {
var options = new HttpRequestOptions var options = new HttpRequestOptions

View File

@ -146,5 +146,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
{ {
return Task.FromResult(true); return Task.FromResult(true);
} }
public string ApplyDuration(string streamPath, TimeSpan duration)
{
return streamPath;
}
} }
} }

View File

@ -164,5 +164,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp
return list; return list;
} }
public string ApplyDuration(string streamPath, TimeSpan duration)
{
return streamPath;
}
} }
} }