diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs
index 96c87b6a50..91493cf53c 100644
--- a/Emby.Server.Implementations/ApplicationHost.cs
+++ b/Emby.Server.Implementations/ApplicationHost.cs
@@ -792,6 +792,11 @@ namespace Emby.Server.Implementations
protected abstract IConnectManager CreateConnectManager();
protected abstract ISyncManager CreateSyncManager();
+
+ protected virtual IHttpClient CreateHttpClient()
+ {
+ return new HttpClientManager.HttpClientManager(ApplicationPaths, LogManager.GetLogger("HttpClient"), FileSystemManager, MemoryStreamFactory, GetDefaultUserAgent);
+ }
///
/// Registers resources that classes will depend on
@@ -814,7 +819,7 @@ namespace Emby.Server.Implementations
RegisterSingleInstance(FileSystemManager);
- HttpClient = new HttpClientManager.HttpClientManager(ApplicationPaths, LogManager.GetLogger("HttpClient"), FileSystemManager, MemoryStreamFactory, GetDefaultUserAgent);
+ HttpClient = CreateHttpClient();
RegisterSingleInstance(HttpClient);
RegisterSingleInstance(NetworkManager);
@@ -1118,7 +1123,7 @@ namespace Emby.Server.Implementations
IsoManager.AddParts(list);
}
- private string GetDefaultUserAgent()
+ protected string GetDefaultUserAgent()
{
var name = FormatAttribute(Name);
diff --git a/Emby.Server.Implementations/Devices/SqliteDeviceRepository.cs b/Emby.Server.Implementations/Devices/SqliteDeviceRepository.cs
index ca0552d98f..a15eb3558b 100644
--- a/Emby.Server.Implementations/Devices/SqliteDeviceRepository.cs
+++ b/Emby.Server.Implementations/Devices/SqliteDeviceRepository.cs
@@ -72,10 +72,18 @@ namespace Emby.Server.Implementations.Devices
private void MigrateDevices()
{
- var files = FileSystem
- .GetFilePaths(GetDevicesPath(), true)
- .Where(i => string.Equals(Path.GetFileName(i), "device.json", StringComparison.OrdinalIgnoreCase))
- .ToList();
+ List files;
+ try
+ {
+ files = FileSystem
+ .GetFilePaths(GetDevicesPath(), true)
+ .Where(i => string.Equals(Path.GetFileName(i), "device.json", StringComparison.OrdinalIgnoreCase))
+ .ToList();
+ }
+ catch (IOException)
+ {
+ return;
+ }
foreach (var file in files)
{
diff --git a/Emby.Server.Implementations/HttpClientManager/HttpClientInfo.cs b/Emby.Server.Implementations/HttpClientManager/HttpClientInfo.cs
index 6d17bf94de..21cec9d2be 100644
--- a/Emby.Server.Implementations/HttpClientManager/HttpClientInfo.cs
+++ b/Emby.Server.Implementations/HttpClientManager/HttpClientInfo.cs
@@ -1,4 +1,5 @@
using System;
+using System.Net.Http;
namespace Emby.Server.Implementations.HttpClientManager
{
@@ -12,5 +13,6 @@ namespace Emby.Server.Implementations.HttpClientManager
///
/// The last timeout.
public DateTime LastTimeout { get; set; }
+ public HttpClient HttpClient { get; set; }
}
}
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHttpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHttpStream.cs
index 5e3923972d..ad9c0d8943 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHttpStream.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHttpStream.cs
@@ -22,7 +22,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
private readonly IHttpClient _httpClient;
private readonly IServerApplicationHost _appHost;
- private readonly CancellationTokenSource _liveStreamCancellationTokenSource = new CancellationTokenSource();
private readonly TaskCompletionSource _liveStreamTaskCompletionSource = new TaskCompletionSource();
public HdHomerunHttpStream(MediaSourceInfo mediaSource, string originalStreamId, IFileSystem fileSystem, IHttpClient httpClient, ILogger logger, IServerApplicationPaths appPaths, IServerApplicationHost appHost, IEnvironmentInfo environment)
@@ -35,7 +34,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
protected override Task OpenInternal(CancellationToken openCancellationToken)
{
- _liveStreamCancellationTokenSource.Token.ThrowIfCancellationRequested();
+ LiveStreamCancellationTokenSource.Token.ThrowIfCancellationRequested();
var mediaSource = OriginalMediaSource;
@@ -45,7 +44,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
var taskCompletionSource = new TaskCompletionSource();
- StartStreaming(url, taskCompletionSource, _liveStreamCancellationTokenSource.Token);
+ StartStreaming(url, taskCompletionSource, LiveStreamCancellationTokenSource.Token);
//OpenedMediaSource.Protocol = MediaProtocol.File;
//OpenedMediaSource.Path = tempFile;
@@ -65,12 +64,13 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
//await Task.Delay(5000).ConfigureAwait(false);
}
- public override Task Close()
+ public override async Task Close()
{
Logger.Info("Closing HDHR live stream");
- _liveStreamCancellationTokenSource.Cancel();
+ LiveStreamCancellationTokenSource.Cancel();
- return _liveStreamTaskCompletionSource.Task;
+ await _liveStreamTaskCompletionSource.Task.ConfigureAwait(false);
+ await DeleteTempFile(TempFilePath).ConfigureAwait(false);
}
private Task StartStreaming(string url, TaskCompletionSource openTaskCompletionSource, CancellationToken cancellationToken)
@@ -112,7 +112,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
}
_liveStreamTaskCompletionSource.TrySetResult(true);
- await DeleteTempFile(TempFilePath).ConfigureAwait(false);
});
}
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs
index ff8fd1bc4b..4187fcd8fb 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs
@@ -26,7 +26,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
private readonly IServerApplicationHost _appHost;
private readonly ISocketFactory _socketFactory;
- private readonly CancellationTokenSource _liveStreamCancellationTokenSource = new CancellationTokenSource();
private readonly TaskCompletionSource _liveStreamTaskCompletionSource = new TaskCompletionSource();
private readonly IHdHomerunChannelCommands _channelCommands;
private readonly int _numTuners;
@@ -45,7 +44,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
protected override Task OpenInternal(CancellationToken openCancellationToken)
{
- _liveStreamCancellationTokenSource.Token.ThrowIfCancellationRequested();
+ LiveStreamCancellationTokenSource.Token.ThrowIfCancellationRequested();
var mediaSource = OriginalMediaSource;
@@ -56,7 +55,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
var taskCompletionSource = new TaskCompletionSource();
- StartStreaming(uri.Host, localPort, taskCompletionSource, _liveStreamCancellationTokenSource.Token);
+ StartStreaming(uri.Host, localPort, taskCompletionSource, LiveStreamCancellationTokenSource.Token);
//OpenedMediaSource.Protocol = MediaProtocol.File;
//OpenedMediaSource.Path = tempFile;
@@ -76,7 +75,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
public override Task Close()
{
Logger.Info("Closing HDHR UDP live stream");
- _liveStreamCancellationTokenSource.Cancel();
+ LiveStreamCancellationTokenSource.Cancel();
return _liveStreamTaskCompletionSource.Task;
}
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs
index 5be91c6c1b..685f794fde 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs
@@ -32,6 +32,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
protected readonly string TempFilePath;
protected readonly ILogger Logger;
+ protected readonly CancellationTokenSource LiveStreamCancellationTokenSource = new CancellationTokenSource();
public LiveStream(MediaSourceInfo mediaSource, IEnvironmentInfo environment, IFileSystem fileSystem, ILogger logger, IServerApplicationPaths appPaths)
{
@@ -80,6 +81,14 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
FileSystem.DeleteFile(path);
return;
}
+ catch (DirectoryNotFoundException)
+ {
+ return;
+ }
+ catch (FileNotFoundException)
+ {
+ return;
+ }
catch
{
@@ -96,6 +105,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
public async Task CopyToAsync(Stream stream, CancellationToken cancellationToken)
{
+ cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, LiveStreamCancellationTokenSource.Token).Token;
+
var allowAsync = false;//Environment.OperatingSystem != MediaBrowser.Model.System.OperatingSystem.Windows;
// use non-async filestream along with read due to https://github.com/dotnet/corefx/issues/6039
@@ -110,16 +121,27 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
private static async Task CopyTo(Stream source, Stream destination, int bufferSize, Action onStarted, CancellationToken cancellationToken)
{
byte[] buffer = new byte[bufferSize];
- while (true)
+
+ var eofCount = 0;
+ var emptyReadLimit = 1000;
+
+ while (eofCount < emptyReadLimit)
{
cancellationToken.ThrowIfCancellationRequested();
- var read = source.Read(buffer, 0, buffer.Length);
+ var bytesRead = source.Read(buffer, 0, buffer.Length);
- if (read > 0)
+ if (bytesRead == 0)
+ {
+ eofCount++;
+ await Task.Delay(10, cancellationToken).ConfigureAwait(false);
+ }
+ else
{
+ eofCount = 0;
+
//await destination.WriteAsync(buffer, 0, read).ConfigureAwait(false);
- destination.Write(buffer, 0, read);
+ destination.Write(buffer, 0, bytesRead);
if (onStarted != null)
{
@@ -127,10 +149,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
onStarted = null;
}
}
- else
- {
- await Task.Delay(10).ConfigureAwait(false);
- }
}
}
diff --git a/MediaBrowser.Api/LiveTv/LiveTvService.cs b/MediaBrowser.Api/LiveTv/LiveTvService.cs
index 1ae7323dc4..703c96e0c6 100644
--- a/MediaBrowser.Api/LiveTv/LiveTvService.cs
+++ b/MediaBrowser.Api/LiveTv/LiveTvService.cs
@@ -734,7 +734,7 @@ namespace MediaBrowser.Api.LiveTv
outputHeaders["Content-Type"] = Model.Net.MimeTypes.GetMimeType(path);
- return new ProgressiveFileCopier(_fileSystem, path, outputHeaders, Logger, _environment, CancellationToken.None)
+ return new ProgressiveFileCopier(_fileSystem, path, outputHeaders, Logger, _environment)
{
AllowEndOfFile = false
};
@@ -753,7 +753,7 @@ namespace MediaBrowser.Api.LiveTv
outputHeaders["Content-Type"] = Model.Net.MimeTypes.GetMimeType("file." + request.Container);
- return new ProgressiveFileCopier(directStreamProvider, outputHeaders, Logger, _environment, CancellationToken.None)
+ return new ProgressiveFileCopier(directStreamProvider, outputHeaders, Logger, _environment)
{
AllowEndOfFile = false
};
diff --git a/MediaBrowser.Api/LiveTv/ProgressiveFileCopier.cs b/MediaBrowser.Api/LiveTv/ProgressiveFileCopier.cs
index 9ce109fc40..74293ccd90 100644
--- a/MediaBrowser.Api/LiveTv/ProgressiveFileCopier.cs
+++ b/MediaBrowser.Api/LiveTv/ProgressiveFileCopier.cs
@@ -16,7 +16,6 @@ namespace MediaBrowser.Api.LiveTv
private readonly IFileSystem _fileSystem;
private readonly ILogger _logger;
private readonly string _path;
- private readonly CancellationToken _cancellationToken;
private readonly Dictionary _outputHeaders;
const int StreamCopyToBufferSize = 81920;
@@ -28,22 +27,20 @@ namespace MediaBrowser.Api.LiveTv
private readonly IDirectStreamProvider _directStreamProvider;
private readonly IEnvironmentInfo _environment;
- public ProgressiveFileCopier(IFileSystem fileSystem, string path, Dictionary outputHeaders, ILogger logger, IEnvironmentInfo environment, CancellationToken cancellationToken)
+ public ProgressiveFileCopier(IFileSystem fileSystem, string path, Dictionary outputHeaders, ILogger logger, IEnvironmentInfo environment)
{
_fileSystem = fileSystem;
_path = path;
_outputHeaders = outputHeaders;
_logger = logger;
- _cancellationToken = cancellationToken;
_environment = environment;
}
- public ProgressiveFileCopier(IDirectStreamProvider directStreamProvider, Dictionary outputHeaders, ILogger logger, IEnvironmentInfo environment, CancellationToken cancellationToken)
+ public ProgressiveFileCopier(IDirectStreamProvider directStreamProvider, Dictionary outputHeaders, ILogger logger, IEnvironmentInfo environment)
{
_directStreamProvider = directStreamProvider;
_outputHeaders = outputHeaders;
_logger = logger;
- _cancellationToken = cancellationToken;
_environment = environment;
}
@@ -69,8 +66,6 @@ namespace MediaBrowser.Api.LiveTv
public async Task WriteToAsync(Stream outputStream, CancellationToken cancellationToken)
{
- cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, _cancellationToken).Token;
-
if (_directStreamProvider != null)
{
await _directStreamProvider.CopyToAsync(outputStream, cancellationToken).ConfigureAwait(false);
@@ -89,7 +84,9 @@ namespace MediaBrowser.Api.LiveTv
inputStream.Position = StartPosition;
}
- while (eofCount < 20 || !AllowEndOfFile)
+ var emptyReadLimit = AllowEndOfFile ? 20 : 100;
+
+ while (eofCount < emptyReadLimit)
{
int bytesRead;
if (allowAsyncFileRead)
diff --git a/MediaBrowser.Providers/Manager/MetadataService.cs b/MediaBrowser.Providers/Manager/MetadataService.cs
index c37e05d957..3759670d4f 100644
--- a/MediaBrowser.Providers/Manager/MetadataService.cs
+++ b/MediaBrowser.Providers/Manager/MetadataService.cs
@@ -13,6 +13,7 @@ using System.Threading.Tasks;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Providers;
+using MediaBrowser.Providers.MediaInfo;
namespace MediaBrowser.Providers.Manager
{
@@ -37,6 +38,28 @@ namespace MediaBrowser.Providers.Manager
LibraryManager = libraryManager;
}
+ private bool RequiresRefresh(IHasMetadata item, IDirectoryService directoryService)
+ {
+ if (item.RequiresRefresh())
+ {
+ return true;
+ }
+
+ if (item.SupportsLocalMetadata)
+ {
+ var video = item as Video;
+
+ if (video != null && !video.IsPlaceHolder)
+ {
+ return !video.SubtitleFiles
+ .SequenceEqual(SubtitleResolver.GetSubtitleFiles(video, directoryService, FileSystem, false)
+ .OrderBy(i => i), StringComparer.OrdinalIgnoreCase);
+ }
+ }
+
+ return false;
+ }
+
public async Task RefreshMetadata(IHasMetadata item, MetadataRefreshOptions refreshOptions, CancellationToken cancellationToken)
{
var itemOfType = (TItemType)item;
@@ -47,19 +70,35 @@ namespace MediaBrowser.Providers.Manager
var libraryOptions = LibraryManager.GetLibraryOptions((BaseItem)item);
- if (refreshOptions.MetadataRefreshMode != MetadataRefreshMode.None)
+ DateTime? newDateModified = null;
+ if (item.LocationType == LocationType.FileSystem)
{
- // TODO: If this returns true, should we instead just change metadata refresh mode to Full?
- requiresRefresh = item.RequiresRefresh();
+ var file = refreshOptions.DirectoryService.GetFile(item.Path);
+ if (file != null)
+ {
+ newDateModified = file.LastWriteTimeUtc;
+ if (item.EnableRefreshOnDateModifiedChange)
+ {
+ if (newDateModified != item.DateModified)
+ {
+ Logger.Debug("Date modified for {0}. Old date {1} new date {2} Id {3}", item.Path, item.DateModified, newDateModified, item.Id);
+ requiresRefresh = true;
+ }
+ }
+ }
}
- if (!requiresRefresh &&
- libraryOptions.AutomaticRefreshIntervalDays > 0 &&
- (DateTime.UtcNow - item.DateLastRefreshed).TotalDays >= libraryOptions.AutomaticRefreshIntervalDays)
+ if (!requiresRefresh && libraryOptions.AutomaticRefreshIntervalDays > 0 && (DateTime.UtcNow - item.DateLastRefreshed).TotalDays >= libraryOptions.AutomaticRefreshIntervalDays)
{
requiresRefresh = true;
}
+ if (!requiresRefresh && refreshOptions.MetadataRefreshMode != MetadataRefreshMode.None)
+ {
+ // TODO: If this returns true, should we instead just change metadata refresh mode to Full?
+ requiresRefresh = RequiresRefresh(item, refreshOptions.DirectoryService);
+ }
+
var itemImageProvider = new ItemImageProvider(Logger, ProviderManager, ServerConfigurationManager, FileSystem);
var localImagesFailed = false;
@@ -145,20 +184,9 @@ namespace MediaBrowser.Providers.Manager
var beforeSaveResult = BeforeSave(itemOfType, isFirstRefresh || refreshOptions.ReplaceAllMetadata || refreshOptions.MetadataRefreshMode == MetadataRefreshMode.FullRefresh || requiresRefresh, updateType);
updateType = updateType | beforeSaveResult;
- if (item.LocationType == LocationType.FileSystem)
+ if (newDateModified.HasValue)
{
- var file = refreshOptions.DirectoryService.GetFile(item.Path);
- if (file != null)
- {
- var fileLastWriteTime = file.LastWriteTimeUtc;
- if (item.EnableRefreshOnDateModifiedChange && fileLastWriteTime != item.DateModified)
- {
- Logger.Debug("Date modified for {0}. Old date {1} new date {2} Id {3}", item.Path, item.DateModified, fileLastWriteTime, item.Id);
- requiresRefresh = true;
- }
-
- item.DateModified = fileLastWriteTime;
- }
+ item.DateModified = newDateModified.Value;
}
// Save if changes were made, or it's never been saved before
diff --git a/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs b/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs
index 44e3cff6a1..b68e6e4a9f 100644
--- a/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs
+++ b/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs
@@ -350,8 +350,6 @@ namespace MediaBrowser.Providers.TV
foreach (var episodeToRemove in episodesToRemove.Select(e => e.Episode))
{
- _logger.Info("Removing missing/unaired episode {0} {1}x{2}", episodeToRemove.Series.Name, episodeToRemove.ParentIndexNumber, episodeToRemove.IndexNumber);
-
await episodeToRemove.Delete(new DeleteOptions
{
DeleteFileLocation = true
@@ -418,8 +416,6 @@ namespace MediaBrowser.Providers.TV
foreach (var seasonToRemove in seasonsToRemove)
{
- _logger.Info("Removing virtual season {0} {1}", seasonToRemove.Series.Name, seasonToRemove.IndexNumber);
-
await seasonToRemove.Delete(new DeleteOptions
{
DeleteFileLocation = true