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

216 lines
5.9 KiB
C#
Raw Normal View History

2019-11-01 17:38:54 +00:00
#pragma warning disable CS1591
using System;
2016-05-26 04:11:27 +00:00
using System.Collections.Generic;
using System.IO;
using System.Linq;
2019-02-05 08:49:46 +00:00
using System.Threading;
2016-05-26 04:11:27 +00:00
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using Microsoft.Extensions.Logging;
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
{
2021-11-15 14:57:07 +00:00
public sealed class FileRefresher : IDisposable
2016-05-26 04:11:27 +00:00
{
private readonly ILogger _logger;
private readonly ILibraryManager _libraryManager;
private readonly IServerConfigurationManager _configurationManager;
2016-05-26 04:11:27 +00:00
private readonly List<string> _affectedPaths = new List<string>();
private readonly object _timerLock = new object();
2021-11-15 14:57:07 +00:00
private Timer? _timer;
2020-07-24 14:37:54 +00:00
private bool _disposed;
2016-05-26 04:11:27 +00:00
2019-02-06 19:38:42 +00:00
public FileRefresher(string path, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, ILogger logger)
2016-05-26 04:11:27 +00:00
{
logger.LogDebug("New file refresher created for {0}", path);
Path = path;
2016-05-26 04:11:27 +00:00
_configurationManager = configurationManager;
_libraryManager = libraryManager;
_logger = logger;
2016-07-03 02:47:39 +00:00
AddPath(path);
2016-05-26 04:11:27 +00:00
}
2021-11-15 14:57:07 +00:00
public event EventHandler<EventArgs>? Completed;
public string Path { get; private set; }
private void AddAffectedPath(string path)
{
ArgumentException.ThrowIfNullOrEmpty(path);
if (!_affectedPaths.Contains(path, StringComparer.Ordinal))
{
_affectedPaths.Add(path);
}
}
public void AddPath(string path)
{
ArgumentException.ThrowIfNullOrEmpty(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;
}
2022-12-05 14:00:20 +00:00
if (_timer is null)
2016-05-26 04:11:27 +00:00
{
_timer = new Timer(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));
2016-05-26 04:11:27 +00:00
}
}
}
public void ResetPath(string path, string? affectedFile)
{
lock (_timerLock)
{
_logger.LogDebug("Resetting file refresher from {0} to {1}", Path, path);
Path = path;
AddAffectedPath(path);
2018-09-12 17:26:21 +00:00
if (!string.IsNullOrEmpty(affectedFile))
{
AddAffectedPath(affectedFile);
}
}
RestartTimer();
}
2021-11-15 14:57:07 +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();
}
_logger.LogDebug("Timer stopped.");
2016-05-26 04:11:27 +00:00
DisposeTimer();
Completed?.Invoke(this, EventArgs.Empty);
2016-05-26 04:11:27 +00:00
try
{
2021-11-15 14:57:07 +00:00
ProcessPathChanges(paths);
2016-05-26 04:11:27 +00:00
}
catch (Exception ex)
{
_logger.LogError(ex, "Error processing directory changes");
2016-05-26 04:11:27 +00:00
}
}
2017-08-16 17:30:16 +00:00
private void ProcessPathChanges(List<string> paths)
2016-05-26 04:11:27 +00:00
{
2021-11-15 14:57:07 +00:00
IEnumerable<BaseItem> itemsToRefresh = paths
2016-09-20 19:43:27 +00:00
.Distinct(StringComparer.OrdinalIgnoreCase)
2016-05-26 04:11:27 +00:00
.Select(GetAffectedBaseItem)
2022-12-05 14:01:13 +00:00
.Where(item => item is not null)
2022-12-19 14:21:42 +00:00
.DistinctBy(x => x!.Id)!; // Removed null values in the previous .Where()
2016-05-26 04:11:27 +00:00
foreach (var item in itemsToRefresh)
{
2017-10-03 18:39:37 +00:00
if (item is AggregateFolder)
{
continue;
}
2020-08-31 20:20:19 +00:00
_logger.LogInformation("{Name} ({Path}) will be refreshed.", item.Name, item.Path);
2016-05-26 04:11:27 +00:00
try
{
2017-05-27 07:19:09 +00:00
item.ChangedExternally();
2016-05-26 04:11:27 +00:00
}
catch (Exception ex)
{
2020-08-31 20:20:19 +00:00
_logger.LogError(ex, "Error refreshing {Name}", item.Name);
2016-05-26 04:11:27 +00:00
}
}
}
/// <summary>
/// Gets the affected base item.
/// </summary>
/// <param name="path">The path.</param>
/// <returns>BaseItem.</returns>
2021-11-15 14:57:07 +00:00
private BaseItem? GetAffectedBaseItem(string path)
2016-05-26 04:11:27 +00:00
{
2021-11-15 14:57:07 +00:00
BaseItem? item = null;
2016-05-26 04:11:27 +00:00
2022-12-05 14:00:20 +00:00
while (item is null && !string.IsNullOrEmpty(path))
2016-05-26 04:11:27 +00:00
{
item = _libraryManager.FindByPath(path, null);
2016-05-26 04:11:27 +00:00
2021-11-15 14:57:07 +00:00
path = System.IO.Path.GetDirectoryName(path) ?? string.Empty;
2016-05-26 04:11:27 +00:00
}
2022-12-05 14:01:13 +00:00
if (item is not null)
2016-05-26 04:11:27 +00:00
{
// If the item has been deleted find the first valid parent that still exists
while (!Directory.Exists(item.Path) && !File.Exists(item.Path))
2016-05-26 04:11:27 +00:00
{
2018-09-12 17:26:21 +00:00
item = item.GetOwner() ?? item.GetParent();
2016-05-26 04:11:27 +00:00
2022-12-05 14:00:20 +00:00
if (item is null)
2016-05-26 04:11:27 +00:00
{
break;
}
}
}
return item;
}
private void DisposeTimer()
2016-05-26 04:11:27 +00:00
{
lock (_timerLock)
{
2022-12-05 14:01:13 +00:00
if (_timer is not null)
2016-05-26 04:11:27 +00:00
{
_timer.Dispose();
2016-09-17 06:08:38 +00:00
_timer = null;
2016-05-26 04:11:27 +00:00
}
}
}
2020-08-31 20:20:19 +00:00
/// <inheritdoc />
2016-05-26 04:11:27 +00:00
public void Dispose()
{
2021-12-15 17:25:36 +00:00
if (_disposed)
{
return;
}
2016-05-26 04:11:27 +00:00
DisposeTimer();
2021-12-15 17:25:36 +00:00
_disposed = true;
2016-05-26 04:11:27 +00:00
}
}
}