jellyfin-server/Emby.Server.Implementations/IO/FileRefresher.cs

245 lines
7.5 KiB
C#
Raw Normal View History

2016-05-26 04:11:27 +00:00
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
2016-10-25 19:02:04 +00:00
using MediaBrowser.Model.IO;
using MediaBrowser.Common.Events;
2017-06-23 16:04:45 +00:00
using MediaBrowser.Common.Progress;
2016-05-26 04:11:27 +00:00
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
2016-10-25 19:02:04 +00:00
using MediaBrowser.Controller.IO;
2016-05-26 04:11:27 +00:00
using MediaBrowser.Controller.Library;
2016-10-22 02:08:34 +00:00
using MediaBrowser.Model.Extensions;
2016-05-26 04:11:27 +00:00
using MediaBrowser.Model.Logging;
2016-11-04 19:51:59 +00:00
using MediaBrowser.Model.System;
2016-10-23 19:47:34 +00:00
using MediaBrowser.Model.Tasks;
using MediaBrowser.Model.Threading;
2016-05-26 04:11:27 +00:00
2016-11-04 18:56:47 +00:00
namespace Emby.Server.Implementations.IO
2016-05-26 04:11:27 +00:00
{
public class FileRefresher : IDisposable
{
private ILogger Logger { get; set; }
private ITaskManager TaskManager { get; set; }
private ILibraryManager LibraryManager { get; set; }
private IServerConfigurationManager ConfigurationManager { get; set; }
private readonly IFileSystem _fileSystem;
private readonly List<string> _affectedPaths = new List<string>();
private ITimer _timer;
private readonly ITimerFactory _timerFactory;
2016-05-26 04:11:27 +00:00
private readonly object _timerLock = new object();
public string Path { get; private set; }
public event EventHandler<EventArgs> Completed;
2016-11-04 19:51:59 +00:00
private readonly IEnvironmentInfo _environmentInfo;
2017-05-12 05:00:45 +00:00
private readonly ILibraryManager _libraryManager;
2016-05-26 04:11:27 +00:00
2017-05-12 05:00:45 +00:00
public FileRefresher(string path, IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, ITaskManager taskManager, ILogger logger, ITimerFactory timerFactory, IEnvironmentInfo environmentInfo, ILibraryManager libraryManager1)
2016-05-26 04:11:27 +00:00
{
logger.Debug("New file refresher created for {0}", path);
Path = path;
2016-05-26 04:11:27 +00:00
_fileSystem = fileSystem;
ConfigurationManager = configurationManager;
LibraryManager = libraryManager;
TaskManager = taskManager;
Logger = logger;
_timerFactory = timerFactory;
2016-11-04 19:51:59 +00:00
_environmentInfo = environmentInfo;
2017-05-12 05:00:45 +00:00
_libraryManager = libraryManager1;
2016-07-03 02:47:39 +00:00
AddPath(path);
2016-05-26 04:11:27 +00:00
}
private void AddAffectedPath(string path)
{
if (string.IsNullOrWhiteSpace(path))
{
throw new ArgumentNullException("path");
}
if (!_affectedPaths.Contains(path, StringComparer.Ordinal))
{
_affectedPaths.Add(path);
}
}
public void AddPath(string path)
{
if (string.IsNullOrWhiteSpace(path))
{
throw new ArgumentNullException("path");
}
lock (_timerLock)
{
AddAffectedPath(path);
}
RestartTimer();
}
public void RestartTimer()
2016-05-26 04:11:27 +00:00
{
2016-08-11 03:56:01 +00:00
if (_disposed)
{
return;
}
2016-05-26 04:11:27 +00:00
lock (_timerLock)
{
2016-09-17 06:08:38 +00:00
if (_disposed)
{
return;
}
2016-05-26 04:11:27 +00:00
if (_timer == null)
{
_timer = _timerFactory.Create(OnTimerCallback, null, TimeSpan.FromSeconds(ConfigurationManager.Configuration.LibraryMonitorDelay), TimeSpan.FromMilliseconds(-1));
2016-05-26 04:11:27 +00:00
}
else
{
_timer.Change(TimeSpan.FromSeconds(ConfigurationManager.Configuration.LibraryMonitorDelay), TimeSpan.FromMilliseconds(-1));
}
}
}
public void ResetPath(string path, string affectedFile)
{
lock (_timerLock)
{
Logger.Debug("Resetting file refresher from {0} to {1}", Path, path);
Path = path;
AddAffectedPath(path);
if (!string.IsNullOrWhiteSpace(affectedFile))
{
AddAffectedPath(affectedFile);
}
}
RestartTimer();
}
2017-08-16 17:30:16 +00:00
private void OnTimerCallback(object state)
2016-05-26 04:11:27 +00:00
{
2016-06-11 15:56:15 +00:00
List<string> paths;
lock (_timerLock)
{
paths = _affectedPaths.ToList();
}
2016-05-26 04:11:27 +00:00
Logger.Debug("Timer stopped.");
DisposeTimer();
EventHelper.FireEventIfNotNull(Completed, this, EventArgs.Empty, Logger);
2016-05-26 04:11:27 +00:00
try
{
2017-08-16 17:30:16 +00:00
ProcessPathChanges(paths.ToList());
2016-05-26 04:11:27 +00:00
}
catch (Exception ex)
{
Logger.ErrorException("Error processing directory changes", ex);
}
}
2017-08-16 17:30:16 +00:00
private void ProcessPathChanges(List<string> paths)
2016-05-26 04:11:27 +00:00
{
var itemsToRefresh = paths
2016-09-20 19:43:27 +00:00
.Distinct(StringComparer.OrdinalIgnoreCase)
2016-05-26 04:11:27 +00:00
.Select(GetAffectedBaseItem)
.Where(item => item != null)
2016-09-20 19:43:27 +00:00
.DistinctBy(i => i.Id)
2016-05-26 04:11:27 +00:00
.ToList();
2017-03-08 19:05:33 +00:00
//foreach (var p in paths)
//{
// Logger.Info(p + " reports change.");
//}
2016-05-26 04:11:27 +00:00
// If the root folder changed, run the library task so the user can see it
if (itemsToRefresh.Any(i => i is AggregateFolder))
{
2017-06-23 16:04:45 +00:00
LibraryManager.ValidateMediaLibrary(new SimpleProgress<double>(), CancellationToken.None);
2016-05-26 04:11:27 +00:00
return;
}
foreach (var item in itemsToRefresh)
{
Logger.Info(item.Name + " (" + item.Path + ") will be refreshed.");
try
{
2017-05-27 07:19:09 +00:00
item.ChangedExternally();
2016-05-26 04:11:27 +00:00
}
catch (IOException ex)
{
// For now swallow and log.
// Research item: If an IOException occurs, the item may be in a disconnected state (media unavailable)
// Should we remove it from it's parent?
Logger.ErrorException("Error refreshing {0}", ex, item.Name);
}
catch (Exception ex)
{
Logger.ErrorException("Error refreshing {0}", ex, item.Name);
}
}
}
/// <summary>
/// Gets the affected base item.
/// </summary>
/// <param name="path">The path.</param>
/// <returns>BaseItem.</returns>
private BaseItem GetAffectedBaseItem(string path)
{
BaseItem item = null;
while (item == null && !string.IsNullOrEmpty(path))
{
item = LibraryManager.FindByPath(path, null);
2017-05-04 18:14:45 +00:00
path = _fileSystem.GetDirectoryName(path);
2016-05-26 04:11:27 +00:00
}
if (item != null)
{
// If the item has been deleted find the first valid parent that still exists
while (!_fileSystem.DirectoryExists(item.Path) && !_fileSystem.FileExists(item.Path))
{
item = item.GetParent();
if (item == null)
{
break;
}
}
}
return item;
}
private void DisposeTimer()
2016-05-26 04:11:27 +00:00
{
lock (_timerLock)
{
if (_timer != null)
{
_timer.Dispose();
2016-09-17 06:08:38 +00:00
_timer = null;
2016-05-26 04:11:27 +00:00
}
}
}
2016-08-11 03:56:01 +00:00
private bool _disposed;
2016-05-26 04:11:27 +00:00
public void Dispose()
{
2016-08-11 03:56:01 +00:00
_disposed = true;
2016-05-26 04:11:27 +00:00
DisposeTimer();
2017-09-05 19:49:02 +00:00
GC.SuppressFinalize(this);
2016-05-26 04:11:27 +00:00
}
}
}