Created ILibraryMonitor to replace IDirectoryWatchers

pull/702/head
Luke Pulverenti 11 years ago
parent 2ae17a8d52
commit 7c5b222463

@ -187,7 +187,7 @@ namespace MediaBrowser.Api.Library
/// </summary>
private readonly ILibraryManager _libraryManager;
private readonly IDirectoryWatchers _directoryWatchers;
private readonly ILibraryMonitor _libraryMonitor;
private readonly IFileSystem _fileSystem;
private readonly ILogger _logger;
@ -199,7 +199,7 @@ namespace MediaBrowser.Api.Library
/// <param name="userManager">The user manager.</param>
/// <param name="libraryManager">The library manager.</param>
/// <exception cref="System.ArgumentNullException">appPaths</exception>
public LibraryStructureService(IServerApplicationPaths appPaths, IUserManager userManager, ILibraryManager libraryManager, IDirectoryWatchers directoryWatchers, IFileSystem fileSystem, ILogger logger)
public LibraryStructureService(IServerApplicationPaths appPaths, IUserManager userManager, ILibraryManager libraryManager, ILibraryMonitor libraryMonitor, IFileSystem fileSystem, ILogger logger)
{
if (appPaths == null)
{
@ -209,7 +209,7 @@ namespace MediaBrowser.Api.Library
_userManager = userManager;
_appPaths = appPaths;
_libraryManager = libraryManager;
_directoryWatchers = directoryWatchers;
_libraryMonitor = libraryMonitor;
_fileSystem = fileSystem;
_logger = logger;
}
@ -270,8 +270,7 @@ namespace MediaBrowser.Api.Library
throw new ArgumentException("There is already a media collection with the name " + name + ".");
}
_directoryWatchers.Stop();
_directoryWatchers.TemporarilyIgnore(virtualFolderPath);
_libraryMonitor.Stop();
try
{
@ -294,10 +293,8 @@ namespace MediaBrowser.Api.Library
// No need to start if scanning the library because it will handle it
if (!request.RefreshLibrary)
{
_directoryWatchers.Start();
_libraryMonitor.Start();
}
_directoryWatchers.RemoveTempIgnore(virtualFolderPath);
}
if (request.RefreshLibrary)
@ -348,9 +345,7 @@ namespace MediaBrowser.Api.Library
throw new ArgumentException("There is already a media collection with the name " + newPath + ".");
}
_directoryWatchers.Stop();
_directoryWatchers.TemporarilyIgnore(currentPath);
_directoryWatchers.TemporarilyIgnore(newPath);
_libraryMonitor.Stop();
try
{
@ -376,11 +371,8 @@ namespace MediaBrowser.Api.Library
// No need to start if scanning the library because it will handle it
if (!request.RefreshLibrary)
{
_directoryWatchers.Start();
_libraryMonitor.Start();
}
_directoryWatchers.RemoveTempIgnore(currentPath);
_directoryWatchers.RemoveTempIgnore(newPath);
}
if (request.RefreshLibrary)
@ -420,8 +412,7 @@ namespace MediaBrowser.Api.Library
throw new DirectoryNotFoundException("The media folder does not exist");
}
_directoryWatchers.Stop();
_directoryWatchers.TemporarilyIgnore(path);
_libraryMonitor.Stop();
try
{
@ -437,10 +428,8 @@ namespace MediaBrowser.Api.Library
// No need to start if scanning the library because it will handle it
if (!request.RefreshLibrary)
{
_directoryWatchers.Start();
_libraryMonitor.Start();
}
_directoryWatchers.RemoveTempIgnore(path);
}
if (request.RefreshLibrary)
@ -460,7 +449,7 @@ namespace MediaBrowser.Api.Library
throw new ArgumentNullException("request");
}
_directoryWatchers.Stop();
_libraryMonitor.Stop();
try
{
@ -485,7 +474,7 @@ namespace MediaBrowser.Api.Library
// No need to start if scanning the library because it will handle it
if (!request.RefreshLibrary)
{
_directoryWatchers.Start();
_libraryMonitor.Start();
}
}
@ -506,7 +495,7 @@ namespace MediaBrowser.Api.Library
throw new ArgumentNullException("request");
}
_directoryWatchers.Stop();
_libraryMonitor.Stop();
try
{
@ -531,7 +520,7 @@ namespace MediaBrowser.Api.Library
// No need to start if scanning the library because it will handle it
if (!request.RefreshLibrary)
{
_directoryWatchers.Start();
_libraryMonitor.Start();
}
}

@ -1,29 +0,0 @@
using System;
namespace MediaBrowser.Controller.IO
{
public interface IDirectoryWatchers : IDisposable
{
/// <summary>
/// Add the path to our temporary ignore list. Use when writing to a path within our listening scope.
/// </summary>
/// <param name="path">The path.</param>
void TemporarilyIgnore(string path);
/// <summary>
/// Removes the temp ignore.
/// </summary>
/// <param name="path">The path.</param>
void RemoveTempIgnore(string path);
/// <summary>
/// Starts this instance.
/// </summary>
void Start();
/// <summary>
/// Stops this instance.
/// </summary>
void Stop();
}
}

@ -0,0 +1,36 @@
using System;
namespace MediaBrowser.Controller.Library
{
public interface ILibraryMonitor : IDisposable
{
/// <summary>
/// Starts this instance.
/// </summary>
void Start();
/// <summary>
/// Stops this instance.
/// </summary>
void Stop();
/// <summary>
/// Reports the file system change beginning.
/// </summary>
/// <param name="path">The path.</param>
void ReportFileSystemChangeBeginning(string path);
/// <summary>
/// Reports the file system change complete.
/// </summary>
/// <param name="path">The path.</param>
/// <param name="refreshPath">if set to <c>true</c> [refresh path].</param>
void ReportFileSystemChangeComplete(string path, bool refreshPath);
/// <summary>
/// Reports the file system changed.
/// </summary>
/// <param name="path">The path.</param>
void ReportFileSystemChanged(string path);
}
}

@ -182,7 +182,7 @@
<Compile Include="Entities\Video.cs" />
<Compile Include="Entities\CollectionFolder.cs" />
<Compile Include="Entities\Year.cs" />
<Compile Include="IO\IDirectoryWatchers.cs" />
<Compile Include="Library\ILibraryMonitor.cs" />
<Compile Include="IServerApplicationHost.cs" />
<Compile Include="IServerApplicationPaths.cs" />
<Compile Include="Library\SearchHintInfo.cs" />

@ -165,7 +165,7 @@ namespace MediaBrowser.Model.Configuration
/// different directories and files.
/// </summary>
/// <value>The file watcher delay.</value>
public int FileWatcherDelay { get; set; }
public int RealtimeWatcherDelay { get; set; }
/// <summary>
/// Gets or sets a value indicating whether [enable dashboard response caching].
@ -250,7 +250,7 @@ namespace MediaBrowser.Model.Configuration
MaxResumePct = 90;
MinResumeDurationSeconds = Convert.ToInt32(TimeSpan.FromMinutes(5).TotalSeconds);
FileWatcherDelay = 8;
RealtimeWatcherDelay = 20;
RecentItemDays = 10;

@ -3,7 +3,7 @@ using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
@ -36,7 +36,7 @@ namespace MediaBrowser.Providers.Manager
/// <summary>
/// The _directory watchers
/// </summary>
private readonly IDirectoryWatchers _directoryWatchers;
private readonly ILibraryMonitor _libraryMonitor;
private readonly IFileSystem _fileSystem;
private readonly ILogger _logger;
@ -44,11 +44,11 @@ namespace MediaBrowser.Providers.Manager
/// Initializes a new instance of the <see cref="ImageSaver"/> class.
/// </summary>
/// <param name="config">The config.</param>
/// <param name="directoryWatchers">The directory watchers.</param>
public ImageSaver(IServerConfigurationManager config, IDirectoryWatchers directoryWatchers, IFileSystem fileSystem, ILogger logger)
/// <param name="libraryMonitor">The directory watchers.</param>
public ImageSaver(IServerConfigurationManager config, ILibraryMonitor libraryMonitor, IFileSystem fileSystem, ILogger logger)
{
_config = config;
_directoryWatchers = directoryWatchers;
_libraryMonitor = libraryMonitor;
_fileSystem = fileSystem;
_logger = logger;
_remoteImageCache = new FileSystemRepository(config.ApplicationPaths.DownloadedImagesDataPath);
@ -160,7 +160,7 @@ namespace MediaBrowser.Providers.Manager
// Delete the current path
if (!string.IsNullOrEmpty(currentPath) && !paths.Contains(currentPath, StringComparer.OrdinalIgnoreCase))
{
_directoryWatchers.TemporarilyIgnore(currentPath);
_libraryMonitor.ReportFileSystemChangeBeginning(currentPath);
try
{
@ -179,7 +179,7 @@ namespace MediaBrowser.Providers.Manager
}
finally
{
_directoryWatchers.RemoveTempIgnore(currentPath);
_libraryMonitor.ReportFileSystemChangeComplete(currentPath, false);
}
}
}
@ -197,8 +197,8 @@ namespace MediaBrowser.Providers.Manager
var parentFolder = Path.GetDirectoryName(path);
_directoryWatchers.TemporarilyIgnore(path);
_directoryWatchers.TemporarilyIgnore(parentFolder);
_libraryMonitor.ReportFileSystemChangeBeginning(path);
_libraryMonitor.ReportFileSystemChangeBeginning(parentFolder);
try
{
@ -223,8 +223,8 @@ namespace MediaBrowser.Providers.Manager
}
finally
{
_directoryWatchers.RemoveTempIgnore(path);
_directoryWatchers.RemoveTempIgnore(parentFolder);
_libraryMonitor.ReportFileSystemChangeComplete(path, false);
_libraryMonitor.ReportFileSystemChangeComplete(parentFolder, false);
}
}

@ -38,7 +38,6 @@ namespace MediaBrowser.Providers.Manager
public void AddParts(IEnumerable<IMetadataProvider> providers, IEnumerable<IImageProvider> imageProviders)
{
_providers = providers.OfType<IMetadataProvider<TItemType>>()
.OrderBy(GetSortOrder)
.ToArray();
_imageProviders = imageProviders.OrderBy(i => i.Order).ToArray();
@ -179,21 +178,6 @@ namespace MediaBrowser.Providers.Manager
return providers;
}
/// <summary>
/// Gets the sort order.
/// </summary>
/// <param name="provider">The provider.</param>
/// <returns>System.Int32.</returns>
protected virtual int GetSortOrder(IMetadataProvider<TItemType> provider)
{
if (provider is IRemoteMetadataProvider)
{
return 1;
}
return 0;
}
/// <summary>
/// Determines whether this instance can refresh the specified provider.
/// </summary>
@ -217,7 +201,7 @@ namespace MediaBrowser.Providers.Manager
protected abstract Task SaveItem(TItemType item, ItemUpdateType reason, CancellationToken cancellationToken);
protected virtual ItemId GetId(TItemType item)
protected virtual ItemId GetId(IHasMetadata item)
{
return new ItemId
{

@ -36,7 +36,7 @@ namespace MediaBrowser.Providers.Manager
/// <summary>
/// The _directory watchers
/// </summary>
private readonly IDirectoryWatchers _directoryWatchers;
private readonly ILibraryMonitor _libraryMonitor;
/// <summary>
/// Gets or sets the configuration manager.
@ -57,23 +57,23 @@ namespace MediaBrowser.Providers.Manager
private readonly IItemRepository _itemRepo;
private IMetadataService[] _metadataServices = {};
private IMetadataService[] _metadataServices = { };
/// <summary>
/// Initializes a new instance of the <see cref="ProviderManager" /> class.
/// </summary>
/// <param name="httpClient">The HTTP client.</param>
/// <param name="configurationManager">The configuration manager.</param>
/// <param name="directoryWatchers">The directory watchers.</param>
/// <param name="libraryMonitor">The directory watchers.</param>
/// <param name="logManager">The log manager.</param>
/// <param name="fileSystem">The file system.</param>
/// <param name="itemRepo">The item repo.</param>
public ProviderManager(IHttpClient httpClient, IServerConfigurationManager configurationManager, IDirectoryWatchers directoryWatchers, ILogManager logManager, IFileSystem fileSystem, IItemRepository itemRepo)
public ProviderManager(IHttpClient httpClient, IServerConfigurationManager configurationManager, ILibraryMonitor libraryMonitor, ILogManager logManager, IFileSystem fileSystem, IItemRepository itemRepo)
{
_logger = logManager.GetLogger("ProviderManager");
_httpClient = httpClient;
ConfigurationManager = configurationManager;
_directoryWatchers = directoryWatchers;
_libraryMonitor = libraryMonitor;
_fileSystem = fileSystem;
_itemRepo = itemRepo;
}
@ -315,7 +315,7 @@ namespace MediaBrowser.Providers.Manager
}
//Tell the watchers to ignore
_directoryWatchers.TemporarilyIgnore(path);
_libraryMonitor.ReportFileSystemChangeBeginning(path);
if (dataToSave.CanSeek)
{
@ -338,7 +338,7 @@ namespace MediaBrowser.Providers.Manager
finally
{
//Remove the ignore
_directoryWatchers.RemoveTempIgnore(path);
_libraryMonitor.ReportFileSystemChangeComplete(path, false);
}
}
@ -380,7 +380,7 @@ namespace MediaBrowser.Providers.Manager
/// <returns>Task.</returns>
public Task SaveImage(BaseItem item, Stream source, string mimeType, ImageType type, int? imageIndex, string sourceUrl, CancellationToken cancellationToken)
{
return new ImageSaver(ConfigurationManager, _directoryWatchers, _fileSystem, _logger).SaveImage(item, source, mimeType, type, imageIndex, sourceUrl, cancellationToken);
return new ImageSaver(ConfigurationManager, _libraryMonitor, _fileSystem, _logger).SaveImage(item, source, mimeType, type, imageIndex, sourceUrl, cancellationToken);
}
/// <summary>

@ -22,7 +22,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
{
public class EpisodeFileOrganizer
{
private readonly IDirectoryWatchers _directoryWatchers;
private readonly ILibraryMonitor _libraryMonitor;
private readonly ILibraryManager _libraryManager;
private readonly ILogger _logger;
private readonly IFileSystem _fileSystem;
@ -31,14 +31,14 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
public EpisodeFileOrganizer(IFileOrganizationService organizationService, IServerConfigurationManager config, IFileSystem fileSystem, ILogger logger, ILibraryManager libraryManager, IDirectoryWatchers directoryWatchers)
public EpisodeFileOrganizer(IFileOrganizationService organizationService, IServerConfigurationManager config, IFileSystem fileSystem, ILogger logger, ILibraryManager libraryManager, ILibraryMonitor libraryMonitor)
{
_organizationService = organizationService;
_config = config;
_fileSystem = fileSystem;
_logger = logger;
_libraryManager = libraryManager;
_directoryWatchers = directoryWatchers;
_libraryMonitor = libraryMonitor;
}
public async Task<FileOrganizationResult> OrganizeEpisodeFile(string path, TvFileOrganizationOptions options, bool overwriteExisting)
@ -174,6 +174,8 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
{
_logger.Debug("Removing duplicate episode {0}", path);
_libraryMonitor.ReportFileSystemChangeBeginning(path);
try
{
File.Delete(path);
@ -182,6 +184,10 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
{
_logger.ErrorException("Error removing duplicate episode", ex, path);
}
finally
{
_libraryMonitor.ReportFileSystemChangeComplete(path, true);
}
}
}
}
@ -232,7 +238,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
private void PerformFileSorting(TvFileOrganizationOptions options, FileOrganizationResult result)
{
_directoryWatchers.TemporarilyIgnore(result.TargetPath);
_libraryMonitor.ReportFileSystemChangeBeginning(result.TargetPath);
Directory.CreateDirectory(Path.GetDirectoryName(result.TargetPath));
@ -264,7 +270,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
}
finally
{
_directoryWatchers.RemoveTempIgnore(result.TargetPath);
_libraryMonitor.ReportFileSystemChangeComplete(result.TargetPath, true);
}
if (copy)

@ -21,17 +21,17 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
private readonly ITaskManager _taskManager;
private readonly IFileOrganizationRepository _repo;
private readonly ILogger _logger;
private readonly IDirectoryWatchers _directoryWatchers;
private readonly ILibraryMonitor _libraryMonitor;
private readonly ILibraryManager _libraryManager;
private readonly IServerConfigurationManager _config;
private readonly IFileSystem _fileSystem;
public FileOrganizationService(ITaskManager taskManager, IFileOrganizationRepository repo, ILogger logger, IDirectoryWatchers directoryWatchers, ILibraryManager libraryManager, IServerConfigurationManager config, IFileSystem fileSystem)
public FileOrganizationService(ITaskManager taskManager, IFileOrganizationRepository repo, ILogger logger, ILibraryMonitor libraryMonitor, ILibraryManager libraryManager, IServerConfigurationManager config, IFileSystem fileSystem)
{
_taskManager = taskManager;
_repo = repo;
_logger = logger;
_directoryWatchers = directoryWatchers;
_libraryMonitor = libraryMonitor;
_libraryManager = libraryManager;
_config = config;
_fileSystem = fileSystem;
@ -91,13 +91,10 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
}
var organizer = new EpisodeFileOrganizer(this, _config, _fileSystem, _logger, _libraryManager,
_directoryWatchers);
_libraryMonitor);
await organizer.OrganizeEpisodeFile(result.OriginalPath, _config.Configuration.TvFileOrganizationOptions, true)
.ConfigureAwait(false);
await _libraryManager.ValidateMediaLibrary(new Progress<double>(), CancellationToken.None)
.ConfigureAwait(false);
}
public Task ClearLog()
@ -108,12 +105,9 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
public async Task PerformEpisodeOrganization(EpisodeFileOrganizationRequest request)
{
var organizer = new EpisodeFileOrganizer(this, _config, _fileSystem, _logger, _libraryManager,
_directoryWatchers);
_libraryMonitor);
await organizer.OrganizeWithCorrection(request, _config.Configuration.TvFileOrganizationOptions).ConfigureAwait(false);
await _libraryManager.ValidateMediaLibrary(new Progress<double>(), CancellationToken.None)
.ConfigureAwait(false);
}
}
}

@ -14,16 +14,16 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
{
public class OrganizerScheduledTask : IScheduledTask, IConfigurableScheduledTask
{
private readonly IDirectoryWatchers _directoryWatchers;
private readonly ILibraryMonitor _libraryMonitor;
private readonly ILibraryManager _libraryManager;
private readonly ILogger _logger;
private readonly IFileSystem _fileSystem;
private readonly IServerConfigurationManager _config;
private readonly IFileOrganizationService _organizationService;
public OrganizerScheduledTask(IDirectoryWatchers directoryWatchers, ILibraryManager libraryManager, ILogger logger, IFileSystem fileSystem, IServerConfigurationManager config, IFileOrganizationService organizationService)
public OrganizerScheduledTask(ILibraryMonitor libraryMonitor, ILibraryManager libraryManager, ILogger logger, IFileSystem fileSystem, IServerConfigurationManager config, IFileOrganizationService organizationService)
{
_directoryWatchers = directoryWatchers;
_libraryMonitor = libraryMonitor;
_libraryManager = libraryManager;
_logger = logger;
_fileSystem = fileSystem;
@ -48,7 +48,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
public Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
{
return new TvFolderOrganizer(_libraryManager, _logger, _fileSystem, _directoryWatchers, _organizationService, _config)
return new TvFolderOrganizer(_libraryManager, _logger, _fileSystem, _libraryMonitor, _organizationService, _config)
.Organize(_config.Configuration.TvFileOrganizationOptions, cancellationToken, progress);
}

@ -18,19 +18,19 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
{
public class TvFolderOrganizer
{
private readonly IDirectoryWatchers _directoryWatchers;
private readonly ILibraryMonitor _libraryMonitor;
private readonly ILibraryManager _libraryManager;
private readonly ILogger _logger;
private readonly IFileSystem _fileSystem;
private readonly IFileOrganizationService _organizationService;
private readonly IServerConfigurationManager _config;
public TvFolderOrganizer(ILibraryManager libraryManager, ILogger logger, IFileSystem fileSystem, IDirectoryWatchers directoryWatchers, IFileOrganizationService organizationService, IServerConfigurationManager config)
public TvFolderOrganizer(ILibraryManager libraryManager, ILogger logger, IFileSystem fileSystem, ILibraryMonitor libraryMonitor, IFileOrganizationService organizationService, IServerConfigurationManager config)
{
_libraryManager = libraryManager;
_logger = logger;
_fileSystem = fileSystem;
_directoryWatchers = directoryWatchers;
_libraryMonitor = libraryMonitor;
_organizationService = organizationService;
_config = config;
}
@ -57,7 +57,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
foreach (var file in eligibleFiles)
{
var organizer = new EpisodeFileOrganizer(_organizationService, _config, _fileSystem, _logger, _libraryManager,
_directoryWatchers);
_libraryMonitor);
var result = await organizer.OrganizeEpisodeFile(file.FullName, options, false).ConfigureAwait(false);

@ -1,8 +1,6 @@
using MediaBrowser.Common.IO;
using MediaBrowser.Common.ScheduledTasks;
using MediaBrowser.Common.ScheduledTasks;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
@ -18,10 +16,7 @@ using System.Threading.Tasks;
namespace MediaBrowser.Server.Implementations.IO
{
/// <summary>
/// Class DirectoryWatchers
/// </summary>
public class DirectoryWatchers : IDirectoryWatchers
public class LibraryMonitor : ILibraryMonitor
{
/// <summary>
/// The file system watchers
@ -55,7 +50,7 @@ namespace MediaBrowser.Server.Implementations.IO
/// Add the path to our temporary ignore list. Use when writing to a path within our listening scope.
/// </summary>
/// <param name="path">The path.</param>
public void TemporarilyIgnore(string path)
private void TemporarilyIgnore(string path)
{
_tempIgnoredPaths[path] = path;
}
@ -64,7 +59,7 @@ namespace MediaBrowser.Server.Implementations.IO
/// Removes the temp ignore.
/// </summary>
/// <param name="path">The path.</param>
public async void RemoveTempIgnore(string path)
private async void RemoveTempIgnore(string path)
{
// This is an arbitraty amount of time, but delay it because file system writes often trigger events after RemoveTempIgnore has been called.
// Seeing long delays in some situations, especially over the network.
@ -75,6 +70,21 @@ namespace MediaBrowser.Server.Implementations.IO
_tempIgnoredPaths.TryRemove(path, out val);
}
public void ReportFileSystemChangeBeginning(string path)
{
TemporarilyIgnore(path);
}
public void ReportFileSystemChangeComplete(string path, bool refreshPath)
{
RemoveTempIgnore(path);
if (refreshPath)
{
ReportFileSystemChanged(path);
}
}
/// <summary>
/// Gets or sets the logger.
/// </summary>
@ -90,12 +100,10 @@ namespace MediaBrowser.Server.Implementations.IO
private ILibraryManager LibraryManager { get; set; }
private IServerConfigurationManager ConfigurationManager { get; set; }
private readonly IFileSystem _fileSystem;
/// <summary>
/// Initializes a new instance of the <see cref="DirectoryWatchers" /> class.
/// Initializes a new instance of the <see cref="LibraryMonitor" /> class.
/// </summary>
public DirectoryWatchers(ILogManager logManager, ITaskManager taskManager, ILibraryManager libraryManager, IServerConfigurationManager configurationManager, IFileSystem fileSystem)
public LibraryMonitor(ILogManager logManager, ITaskManager taskManager, ILibraryManager libraryManager, IServerConfigurationManager configurationManager)
{
if (taskManager == null)
{
@ -104,9 +112,8 @@ namespace MediaBrowser.Server.Implementations.IO
LibraryManager = libraryManager;
TaskManager = taskManager;
Logger = logManager.GetLogger("DirectoryWatchers");
Logger = logManager.GetLogger(GetType().Name);
ConfigurationManager = configurationManager;
_fileSystem = fileSystem;
SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged;
}
@ -328,31 +335,25 @@ namespace MediaBrowser.Server.Implementations.IO
{
OnWatcherChanged(e);
}
catch (IOException ex)
catch (Exception ex)
{
Logger.ErrorException("IOException in watcher changed. Path: {0}", ex, e.FullPath);
Logger.ErrorException("Exception in watcher changed. Path: {0}", ex, e.FullPath);
}
}
private void OnWatcherChanged(FileSystemEventArgs e)
{
var name = e.Name;
Logger.Debug("Watcher sees change of type " + e.ChangeType + " to " + e.FullPath);
// Ignore certain files
if (_alwaysIgnoreFiles.Contains(name, StringComparer.OrdinalIgnoreCase))
{
return;
}
ReportFileSystemChanged(e.FullPath);
}
var nameFromFullPath = Path.GetFileName(e.FullPath);
// Ignore certain files
if (!string.IsNullOrEmpty(nameFromFullPath) && _alwaysIgnoreFiles.Contains(nameFromFullPath, StringComparer.OrdinalIgnoreCase))
{
return;
}
public void ReportFileSystemChanged(string path)
{
var filename = Path.GetFileName(path);
// Ignore when someone manually creates a new folder
if (e.ChangeType == WatcherChangeTypes.Created && name == "New folder")
// Ignore certain files
if (!string.IsNullOrEmpty(filename) && _alwaysIgnoreFiles.Contains(filename, StringComparer.OrdinalIgnoreCase))
{
return;
}
@ -362,17 +363,17 @@ namespace MediaBrowser.Server.Implementations.IO
// If the parent of an ignored path has a change event, ignore that too
if (tempIgnorePaths.Any(i =>
{
if (string.Equals(i, e.FullPath, StringComparison.OrdinalIgnoreCase))
if (string.Equals(i, path, StringComparison.OrdinalIgnoreCase))
{
Logger.Debug("Watcher ignoring change to {0}", e.FullPath);
Logger.Debug("Ignoring change to {0}", path);
return true;
}
// Go up a level
var parent = Path.GetDirectoryName(i);
if (string.Equals(parent, e.FullPath, StringComparison.OrdinalIgnoreCase))
if (string.Equals(parent, path, StringComparison.OrdinalIgnoreCase))
{
Logger.Debug("Watcher ignoring change to {0}", e.FullPath);
Logger.Debug("Ignoring change to {0}", path);
return true;
}
@ -380,17 +381,17 @@ namespace MediaBrowser.Server.Implementations.IO
if (!string.IsNullOrEmpty(parent))
{
parent = Path.GetDirectoryName(i);
if (string.Equals(parent, e.FullPath, StringComparison.OrdinalIgnoreCase))
if (string.Equals(parent, path, StringComparison.OrdinalIgnoreCase))
{
Logger.Debug("Watcher ignoring change to {0}", e.FullPath);
Logger.Debug("Ignoring change to {0}", path);
return true;
}
}
if (i.StartsWith(e.FullPath, StringComparison.OrdinalIgnoreCase) ||
e.FullPath.StartsWith(i, StringComparison.OrdinalIgnoreCase))
if (i.StartsWith(path, StringComparison.OrdinalIgnoreCase) ||
path.StartsWith(i, StringComparison.OrdinalIgnoreCase))
{
Logger.Debug("Watcher ignoring change to {0}", e.FullPath);
Logger.Debug("Ignoring change to {0}", path);
return true;
}
@ -401,22 +402,19 @@ namespace MediaBrowser.Server.Implementations.IO
return;
}
Logger.Info("Watcher sees change of type " + e.ChangeType + " to " + e.FullPath);
//Since we're watching created, deleted and renamed we always want the parent of the item to be the affected path
var affectedPath = e.FullPath;
_affectedPaths.AddOrUpdate(affectedPath, affectedPath, (key, oldValue) => affectedPath);
// Avoid implicitly captured closure
var affectedPath = path;
_affectedPaths.AddOrUpdate(path, path, (key, oldValue) => affectedPath);
lock (_timerLock)
{
if (_updateTimer == null)
{
_updateTimer = new Timer(TimerStopped, null, TimeSpan.FromSeconds(ConfigurationManager.Configuration.FileWatcherDelay), TimeSpan.FromMilliseconds(-1));
_updateTimer = new Timer(TimerStopped, null, TimeSpan.FromSeconds(ConfigurationManager.Configuration.RealtimeWatcherDelay), TimeSpan.FromMilliseconds(-1));
}
else
{
_updateTimer.Change(TimeSpan.FromSeconds(ConfigurationManager.Configuration.FileWatcherDelay), TimeSpan.FromMilliseconds(-1));
_updateTimer.Change(TimeSpan.FromSeconds(ConfigurationManager.Configuration.RealtimeWatcherDelay), TimeSpan.FromMilliseconds(-1));
}
}
}
@ -427,24 +425,9 @@ namespace MediaBrowser.Server.Implementations.IO
/// <param name="stateInfo">The state info.</param>
private async void TimerStopped(object stateInfo)
{
lock (_timerLock)
{
// Extend the timer as long as any of the paths are still being written to.
if (_affectedPaths.Any(p => IsFileLocked(p.Key)))
{
Logger.Info("Timer extended.");
_updateTimer.Change(TimeSpan.FromSeconds(ConfigurationManager.Configuration.FileWatcherDelay), TimeSpan.FromMilliseconds(-1));
return;
}
Logger.Debug("Timer stopped.");
Logger.Info("Timer stopped.");
if (_updateTimer != null)
{
_updateTimer.Dispose();
_updateTimer = null;
}
}
DisposeTimer();
var paths = _affectedPaths.Keys.ToList();
_affectedPaths.Clear();
@ -452,59 +435,16 @@ namespace MediaBrowser.Server.Implementations.IO
await ProcessPathChanges(paths).ConfigureAwait(false);
}
/// <summary>
/// Try and determine if a file is locked
/// This is not perfect, and is subject to race conditions, so I'd rather not make this a re-usable library method.
/// </summary>
/// <param name="path">The path.</param>
/// <returns><c>true</c> if [is file locked] [the specified path]; otherwise, <c>false</c>.</returns>
private bool IsFileLocked(string path)
private void DisposeTimer()
{
try
{
var data = _fileSystem.GetFileSystemInfo(path);
if (!data.Exists
|| data.Attributes.HasFlag(FileAttributes.Directory)
|| data.Attributes.HasFlag(FileAttributes.ReadOnly))
{
return false;
}
}
catch (IOException)
{
return false;
}
try
lock (_timerLock)
{
using (_fileSystem.GetFileStream(path, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite))
if (_updateTimer != null)
{
//file is not locked
return false;
_updateTimer.Dispose();
_updateTimer = null;
}
}
catch (DirectoryNotFoundException)
{
return false;
}
catch (FileNotFoundException)
{
return false;
}
catch (IOException)
{
//the file is unavailable because it is:
//still being written to
//or being processed by another thread
//or does not exist (has already been processed)
Logger.Debug("{0} is locked.", path);
return true;
}
catch
{
return false;
}
}
/// <summary>
@ -599,14 +539,7 @@ namespace MediaBrowser.Server.Implementations.IO
watcher.Dispose();
}
lock (_timerLock)
{
if (_updateTimer != null)
{
_updateTimer.Dispose();
_updateTimer = null;
}
}
DisposeTimer();
_fileSystemWatchers.Clear();
_affectedPaths.Clear();

@ -137,7 +137,7 @@ namespace MediaBrowser.Server.Implementations.Library
private IEnumerable<IMetadataSaver> _savers;
private readonly Func<IDirectoryWatchers> _directoryWatchersFactory;
private readonly Func<ILibraryMonitor> _libraryMonitorFactory;
/// <summary>
/// The _library items cache
@ -180,14 +180,14 @@ namespace MediaBrowser.Server.Implementations.Library
/// <param name="userManager">The user manager.</param>
/// <param name="configurationManager">The configuration manager.</param>
/// <param name="userDataRepository">The user data repository.</param>
public LibraryManager(ILogger logger, ITaskManager taskManager, IUserManager userManager, IServerConfigurationManager configurationManager, IUserDataManager userDataRepository, Func<IDirectoryWatchers> directoryWatchersFactory, IFileSystem fileSystem)
public LibraryManager(ILogger logger, ITaskManager taskManager, IUserManager userManager, IServerConfigurationManager configurationManager, IUserDataManager userDataRepository, Func<ILibraryMonitor> libraryMonitorFactory, IFileSystem fileSystem)
{
_logger = logger;
_taskManager = taskManager;
_userManager = userManager;
ConfigurationManager = configurationManager;
_userDataRepository = userDataRepository;
_directoryWatchersFactory = directoryWatchersFactory;
_libraryMonitorFactory = libraryMonitorFactory;
_fileSystem = fileSystem;
ByReferenceItems = new ConcurrentDictionary<Guid, BaseItem>();
@ -934,7 +934,7 @@ namespace MediaBrowser.Server.Implementations.Library
/// <returns>Task.</returns>
public async Task ValidateMediaLibraryInternal(IProgress<double> progress, CancellationToken cancellationToken)
{
_directoryWatchersFactory().Stop();
_libraryMonitorFactory().Stop();
try
{
@ -942,7 +942,7 @@ namespace MediaBrowser.Server.Implementations.Library
}
finally
{
_directoryWatchersFactory().Start();
_libraryMonitorFactory().Start();
}
}
@ -1462,13 +1462,13 @@ namespace MediaBrowser.Server.Implementations.Library
var semaphore = _fileLocks.GetOrAdd(path, key => new SemaphoreSlim(1, 1));
var directoryWatchers = _directoryWatchersFactory();
var directoryWatchers = _libraryMonitorFactory();
await semaphore.WaitAsync().ConfigureAwait(false);
try
{
directoryWatchers.TemporarilyIgnore(path);
directoryWatchers.ReportFileSystemChangeBeginning(path);
saver.Save(item, CancellationToken.None);
}
catch (Exception ex)
@ -1477,7 +1477,7 @@ namespace MediaBrowser.Server.Implementations.Library
}
finally
{
directoryWatchers.RemoveTempIgnore(path);
directoryWatchers.ReportFileSystemChangeComplete(path, false);
semaphore.Release();
}
}

@ -137,7 +137,7 @@
<Compile Include="HttpServer\StreamWriter.cs" />
<Compile Include="HttpServer\SwaggerService.cs" />
<Compile Include="Drawing\ImageProcessor.cs" />
<Compile Include="IO\DirectoryWatchers.cs" />
<Compile Include="IO\LibraryMonitor.cs" />
<Compile Include="Library\CoreResolutionIgnoreRule.cs" />
<Compile Include="Library\LibraryManager.cs" />
<Compile Include="Library\SearchEngine.cs" />

@ -137,7 +137,7 @@ namespace MediaBrowser.ServerApplication
/// Gets or sets the directory watchers.
/// </summary>
/// <value>The directory watchers.</value>
private IDirectoryWatchers DirectoryWatchers { get; set; }
private ILibraryMonitor LibraryMonitor { get; set; }
/// <summary>
/// Gets or sets the provider manager.
/// </summary>
@ -273,13 +273,13 @@ namespace MediaBrowser.ServerApplication
UserManager = new UserManager(Logger, ServerConfigurationManager, UserRepository);
RegisterSingleInstance(UserManager);
LibraryManager = new LibraryManager(Logger, TaskManager, UserManager, ServerConfigurationManager, UserDataManager, () => DirectoryWatchers, FileSystemManager);
LibraryManager = new LibraryManager(Logger, TaskManager, UserManager, ServerConfigurationManager, UserDataManager, () => LibraryMonitor, FileSystemManager);
RegisterSingleInstance(LibraryManager);
DirectoryWatchers = new DirectoryWatchers(LogManager, TaskManager, LibraryManager, ServerConfigurationManager, FileSystemManager);
RegisterSingleInstance(DirectoryWatchers);
LibraryMonitor = new LibraryMonitor(LogManager, TaskManager, LibraryManager, ServerConfigurationManager);
RegisterSingleInstance(LibraryMonitor);
ProviderManager = new ProviderManager(HttpClient, ServerConfigurationManager, DirectoryWatchers, LogManager, FileSystemManager, ItemRepository);
ProviderManager = new ProviderManager(HttpClient, ServerConfigurationManager, LibraryMonitor, LogManager, FileSystemManager, ItemRepository);
RegisterSingleInstance(ProviderManager);
RegisterSingleInstance<ISearchEngine>(() => new SearchEngine(LogManager, LibraryManager, UserManager));
@ -306,7 +306,7 @@ namespace MediaBrowser.ServerApplication
var newsService = new Server.Implementations.News.NewsService(ApplicationPaths, JsonSerializer);
RegisterSingleInstance<INewsService>(newsService);
var fileOrganizationService = new FileOrganizationService(TaskManager, FileOrganizationRepository, Logger, DirectoryWatchers, LibraryManager, ServerConfigurationManager, FileSystemManager);
var fileOrganizationService = new FileOrganizationService(TaskManager, FileOrganizationRepository, Logger, LibraryMonitor, LibraryManager, ServerConfigurationManager, FileSystemManager);
RegisterSingleInstance<IFileOrganizationService>(fileOrganizationService);
progress.Report(15);

Loading…
Cancel
Save