diff options
Diffstat (limited to 'Emby.Server.Core/IO')
| -rw-r--r-- | Emby.Server.Core/IO/LibraryMonitor.cs | 653 | ||||
| -rw-r--r-- | Emby.Server.Core/IO/MemoryStreamProvider.cs | 56 |
2 files changed, 0 insertions, 709 deletions
diff --git a/Emby.Server.Core/IO/LibraryMonitor.cs b/Emby.Server.Core/IO/LibraryMonitor.cs deleted file mode 100644 index ebc5e5e55..000000000 --- a/Emby.Server.Core/IO/LibraryMonitor.cs +++ /dev/null @@ -1,653 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Plugins; -using MediaBrowser.Model.IO; -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.System; -using MediaBrowser.Model.Tasks; -using MediaBrowser.Model.Threading; -using Emby.Server.Implementations.IO; - -namespace Emby.Server.Core.IO -{ - public class LibraryMonitor : ILibraryMonitor - { - /// <summary> - /// The file system watchers - /// </summary> - private readonly ConcurrentDictionary<string, FileSystemWatcher> _fileSystemWatchers = new ConcurrentDictionary<string, FileSystemWatcher>(StringComparer.OrdinalIgnoreCase); - /// <summary> - /// The affected paths - /// </summary> - private readonly List<FileRefresher> _activeRefreshers = new List<FileRefresher>(); - - /// <summary> - /// A dynamic list of paths that should be ignored. Added to during our own file sytem modifications. - /// </summary> - private readonly ConcurrentDictionary<string, string> _tempIgnoredPaths = new ConcurrentDictionary<string, string>(StringComparer.OrdinalIgnoreCase); - - /// <summary> - /// Any file name ending in any of these will be ignored by the watchers - /// </summary> - private readonly IReadOnlyList<string> _alwaysIgnoreFiles = new List<string> - { - "small.jpg", - "albumart.jpg", - - // WMC temp recording directories that will constantly be written to - "TempRec", - "TempSBE" - }; - - private readonly IReadOnlyList<string> _alwaysIgnoreSubstrings = new List<string> - { - // Synology - "eaDir", - "#recycle", - ".wd_tv", - ".actors" - }; - - private readonly IReadOnlyList<string> _alwaysIgnoreExtensions = new List<string> - { - // thumbs.db - ".db", - - // bts sync files - ".bts", - ".sync" - }; - - /// <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> - private void TemporarilyIgnore(string path) - { - _tempIgnoredPaths[path] = path; - } - - public void ReportFileSystemChangeBeginning(string path) - { - if (string.IsNullOrEmpty(path)) - { - throw new ArgumentNullException("path"); - } - - TemporarilyIgnore(path); - } - - public bool IsPathLocked(string path) - { - var lockedPaths = _tempIgnoredPaths.Keys.ToList(); - return lockedPaths.Any(i => _fileSystem.AreEqual(i, path) || _fileSystem.ContainsSubPath(i, path)); - } - - public async void ReportFileSystemChangeComplete(string path, bool refreshPath) - { - if (string.IsNullOrEmpty(path)) - { - throw new ArgumentNullException("path"); - } - - // This is an arbitraty amount of time, but delay it because file system writes often trigger events long after the file was actually written to. - // Seeing long delays in some situations, especially over the network, sometimes up to 45 seconds - // But if we make this delay too high, we risk missing legitimate changes, such as user adding a new file, or hand-editing metadata - await Task.Delay(45000).ConfigureAwait(false); - - string val; - _tempIgnoredPaths.TryRemove(path, out val); - - if (refreshPath) - { - try - { - ReportFileSystemChanged(path); - } - catch (Exception ex) - { - Logger.ErrorException("Error in ReportFileSystemChanged for {0}", ex, path); - } - } - } - - /// <summary> - /// Gets or sets the logger. - /// </summary> - /// <value>The logger.</value> - private ILogger Logger { get; set; } - - /// <summary> - /// Gets or sets the task manager. - /// </summary> - /// <value>The task manager.</value> - private ITaskManager TaskManager { get; set; } - - private ILibraryManager LibraryManager { get; set; } - private IServerConfigurationManager ConfigurationManager { get; set; } - - private readonly IFileSystem _fileSystem; - private readonly ITimerFactory _timerFactory; - private readonly IEnvironmentInfo _environmentInfo; - - /// <summary> - /// Initializes a new instance of the <see cref="LibraryMonitor" /> class. - /// </summary> - public LibraryMonitor(ILogManager logManager, ITaskManager taskManager, ILibraryManager libraryManager, IServerConfigurationManager configurationManager, IFileSystem fileSystem, ITimerFactory timerFactory, ISystemEvents systemEvents, IEnvironmentInfo environmentInfo) - { - if (taskManager == null) - { - throw new ArgumentNullException("taskManager"); - } - - LibraryManager = libraryManager; - TaskManager = taskManager; - Logger = logManager.GetLogger(GetType().Name); - ConfigurationManager = configurationManager; - _fileSystem = fileSystem; - _timerFactory = timerFactory; - _environmentInfo = environmentInfo; - - systemEvents.Resume += _systemEvents_Resume; - } - - private void _systemEvents_Resume(object sender, EventArgs e) - { - Restart(); - } - - private void Restart() - { - Stop(); - - if (!_disposed) - { - Start(); - } - } - - private bool IsLibraryMonitorEnabaled(BaseItem item) - { - if (item is BasePluginFolder) - { - return false; - } - - var options = LibraryManager.GetLibraryOptions(item); - - if (options != null) - { - return options.EnableRealtimeMonitor; - } - - return false; - } - - public void Start() - { - LibraryManager.ItemAdded += LibraryManager_ItemAdded; - LibraryManager.ItemRemoved += LibraryManager_ItemRemoved; - - var pathsToWatch = new List<string> { }; - - var paths = LibraryManager - .RootFolder - .Children - .Where(IsLibraryMonitorEnabaled) - .OfType<Folder>() - .SelectMany(f => f.PhysicalLocations) - .Distinct(StringComparer.OrdinalIgnoreCase) - .OrderBy(i => i) - .ToList(); - - foreach (var path in paths) - { - if (!ContainsParentFolder(pathsToWatch, path)) - { - pathsToWatch.Add(path); - } - } - - foreach (var path in pathsToWatch) - { - StartWatchingPath(path); - } - } - - private void StartWatching(BaseItem item) - { - if (IsLibraryMonitorEnabaled(item)) - { - StartWatchingPath(item.Path); - } - } - - /// <summary> - /// Handles the ItemRemoved event of the LibraryManager control. - /// </summary> - /// <param name="sender">The source of the event.</param> - /// <param name="e">The <see cref="ItemChangeEventArgs"/> instance containing the event data.</param> - void LibraryManager_ItemRemoved(object sender, ItemChangeEventArgs e) - { - if (e.Item.GetParent() is AggregateFolder) - { - StopWatchingPath(e.Item.Path); - } - } - - /// <summary> - /// Handles the ItemAdded event of the LibraryManager control. - /// </summary> - /// <param name="sender">The source of the event.</param> - /// <param name="e">The <see cref="ItemChangeEventArgs"/> instance containing the event data.</param> - void LibraryManager_ItemAdded(object sender, ItemChangeEventArgs e) - { - if (e.Item.GetParent() is AggregateFolder) - { - StartWatching(e.Item); - } - } - - /// <summary> - /// Examine a list of strings assumed to be file paths to see if it contains a parent of - /// the provided path. - /// </summary> - /// <param name="lst">The LST.</param> - /// <param name="path">The path.</param> - /// <returns><c>true</c> if [contains parent folder] [the specified LST]; otherwise, <c>false</c>.</returns> - /// <exception cref="System.ArgumentNullException">path</exception> - private static bool ContainsParentFolder(IEnumerable<string> lst, string path) - { - if (string.IsNullOrWhiteSpace(path)) - { - throw new ArgumentNullException("path"); - } - - path = path.TrimEnd(Path.DirectorySeparatorChar); - - return lst.Any(str => - { - //this should be a little quicker than examining each actual parent folder... - var compare = str.TrimEnd(Path.DirectorySeparatorChar); - - return path.Equals(compare, StringComparison.OrdinalIgnoreCase) || (path.StartsWith(compare, StringComparison.OrdinalIgnoreCase) && path[compare.Length] == Path.DirectorySeparatorChar); - }); - } - - /// <summary> - /// Starts the watching path. - /// </summary> - /// <param name="path">The path.</param> - private void StartWatchingPath(string path) - { - if (!_fileSystem.DirectoryExists(path)) - { - // Seeing a crash in the mono runtime due to an exception being thrown on a different thread - Logger.Info("Skipping realtime monitor for {0} because the path does not exist", path); - return; - } - - if (_environmentInfo.OperatingSystem != MediaBrowser.Model.System.OperatingSystem.Windows) - { - if (path.StartsWith("\\\\", StringComparison.OrdinalIgnoreCase) || path.StartsWith("smb://", StringComparison.OrdinalIgnoreCase)) - { - // not supported - return; - } - } - - // Already being watched - if (_fileSystemWatchers.ContainsKey(path)) - { - return; - } - - // Creating a FileSystemWatcher over the LAN can take hundreds of milliseconds, so wrap it in a Task to do them all in parallel - Task.Run(() => - { - try - { - var newWatcher = new FileSystemWatcher(path, "*") - { - IncludeSubdirectories = true - }; - - if (_environmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Windows) - { - newWatcher.InternalBufferSize = 32767; - } - - newWatcher.NotifyFilter = NotifyFilters.CreationTime | - NotifyFilters.DirectoryName | - NotifyFilters.FileName | - NotifyFilters.LastWrite | - NotifyFilters.Size | - NotifyFilters.Attributes; - - newWatcher.Created += watcher_Changed; - - // Seeing mono crashes on background threads we can't catch, testing if this might help - if (_environmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Windows) - { - newWatcher.Deleted += watcher_Changed; - } - - newWatcher.Renamed += watcher_Changed; - newWatcher.Changed += watcher_Changed; - - newWatcher.Error += watcher_Error; - - if (_fileSystemWatchers.TryAdd(path, newWatcher)) - { - newWatcher.EnableRaisingEvents = true; - Logger.Info("Watching directory " + path); - } - else - { - newWatcher.Dispose(); - } - - } - catch (Exception ex) - { - Logger.ErrorException("Error watching path: {0}", ex, path); - } - }); - } - - /// <summary> - /// Stops the watching path. - /// </summary> - /// <param name="path">The path.</param> - private void StopWatchingPath(string path) - { - FileSystemWatcher watcher; - - if (_fileSystemWatchers.TryGetValue(path, out watcher)) - { - DisposeWatcher(watcher); - } - } - - /// <summary> - /// Disposes the watcher. - /// </summary> - /// <param name="watcher">The watcher.</param> - private void DisposeWatcher(FileSystemWatcher watcher) - { - try - { - using (watcher) - { - Logger.Info("Stopping directory watching for path {0}", watcher.Path); - - watcher.EnableRaisingEvents = false; - } - } - catch - { - - } - finally - { - RemoveWatcherFromList(watcher); - } - } - - /// <summary> - /// Removes the watcher from list. - /// </summary> - /// <param name="watcher">The watcher.</param> - private void RemoveWatcherFromList(FileSystemWatcher watcher) - { - FileSystemWatcher removed; - - _fileSystemWatchers.TryRemove(watcher.Path, out removed); - } - - /// <summary> - /// Handles the Error event of the watcher control. - /// </summary> - /// <param name="sender">The source of the event.</param> - /// <param name="e">The <see cref="ErrorEventArgs" /> instance containing the event data.</param> - void watcher_Error(object sender, ErrorEventArgs e) - { - var ex = e.GetException(); - var dw = (FileSystemWatcher)sender; - - Logger.ErrorException("Error in Directory watcher for: " + dw.Path, ex); - - DisposeWatcher(dw); - } - - /// <summary> - /// Handles the Changed event of the watcher control. - /// </summary> - /// <param name="sender">The source of the event.</param> - /// <param name="e">The <see cref="FileSystemEventArgs" /> instance containing the event data.</param> - void watcher_Changed(object sender, FileSystemEventArgs e) - { - try - { - //Logger.Debug("Changed detected of type " + e.ChangeType + " to " + e.FullPath); - - var path = e.FullPath; - - ReportFileSystemChanged(path); - } - catch (Exception ex) - { - Logger.ErrorException("Exception in ReportFileSystemChanged. Path: {0}", ex, e.FullPath); - } - } - - public void ReportFileSystemChanged(string path) - { - if (string.IsNullOrEmpty(path)) - { - throw new ArgumentNullException("path"); - } - - var filename = Path.GetFileName(path); - - var monitorPath = !string.IsNullOrEmpty(filename) && - !_alwaysIgnoreFiles.Contains(filename, StringComparer.OrdinalIgnoreCase) && - !_alwaysIgnoreExtensions.Contains(Path.GetExtension(path) ?? string.Empty, StringComparer.OrdinalIgnoreCase) && - _alwaysIgnoreSubstrings.All(i => path.IndexOf(i, StringComparison.OrdinalIgnoreCase) == -1); - - // Ignore certain files - var tempIgnorePaths = _tempIgnoredPaths.Keys.ToList(); - - // If the parent of an ignored path has a change event, ignore that too - if (tempIgnorePaths.Any(i => - { - if (_fileSystem.AreEqual(i, path)) - { - Logger.Debug("Ignoring change to {0}", path); - return true; - } - - if (_fileSystem.ContainsSubPath(i, path)) - { - Logger.Debug("Ignoring change to {0}", path); - return true; - } - - // Go up a level - var parent = _fileSystem.GetDirectoryName(i); - if (!string.IsNullOrEmpty(parent)) - { - if (_fileSystem.AreEqual(parent, path)) - { - Logger.Debug("Ignoring change to {0}", path); - return true; - } - } - - return false; - - })) - { - monitorPath = false; - } - - if (monitorPath) - { - // Avoid implicitly captured closure - CreateRefresher(path); - } - } - - private void CreateRefresher(string path) - { - var parentPath = _fileSystem.GetDirectoryName(path); - - lock (_activeRefreshers) - { - var refreshers = _activeRefreshers.ToList(); - foreach (var refresher in refreshers) - { - // Path is already being refreshed - if (_fileSystem.AreEqual(path, refresher.Path)) - { - refresher.RestartTimer(); - return; - } - - // Parent folder is already being refreshed - if (_fileSystem.ContainsSubPath(refresher.Path, path)) - { - refresher.AddPath(path); - return; - } - - // New path is a parent - if (_fileSystem.ContainsSubPath(path, refresher.Path)) - { - refresher.ResetPath(path, null); - return; - } - - // They are siblings. Rebase the refresher to the parent folder. - if (string.Equals(parentPath, _fileSystem.GetDirectoryName(refresher.Path), StringComparison.Ordinal)) - { - refresher.ResetPath(parentPath, path); - return; - } - } - - var newRefresher = new FileRefresher(path, _fileSystem, ConfigurationManager, LibraryManager, TaskManager, Logger, _timerFactory, _environmentInfo, LibraryManager); - newRefresher.Completed += NewRefresher_Completed; - _activeRefreshers.Add(newRefresher); - } - } - - private void NewRefresher_Completed(object sender, EventArgs e) - { - var refresher = (FileRefresher)sender; - DisposeRefresher(refresher); - } - - /// <summary> - /// Stops this instance. - /// </summary> - public void Stop() - { - LibraryManager.ItemAdded -= LibraryManager_ItemAdded; - LibraryManager.ItemRemoved -= LibraryManager_ItemRemoved; - - foreach (var watcher in _fileSystemWatchers.Values.ToList()) - { - watcher.Created -= watcher_Changed; - watcher.Deleted -= watcher_Changed; - watcher.Renamed -= watcher_Changed; - watcher.Changed -= watcher_Changed; - - try - { - watcher.EnableRaisingEvents = false; - } - catch (InvalidOperationException) - { - // Seeing this under mono on linux sometimes - // Collection was modified; enumeration operation may not execute. - } - - watcher.Dispose(); - } - - _fileSystemWatchers.Clear(); - DisposeRefreshers(); - } - - private void DisposeRefresher(FileRefresher refresher) - { - lock (_activeRefreshers) - { - refresher.Dispose(); - _activeRefreshers.Remove(refresher); - } - } - - private void DisposeRefreshers() - { - lock (_activeRefreshers) - { - foreach (var refresher in _activeRefreshers.ToList()) - { - refresher.Dispose(); - } - _activeRefreshers.Clear(); - } - } - - private bool _disposed; - /// <summary> - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// </summary> - public void Dispose() - { - _disposed = true; - Dispose(true); - GC.SuppressFinalize(this); - } - - /// <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) - { - Stop(); - } - } - } - - public class LibraryMonitorStartup : IServerEntryPoint - { - private readonly ILibraryMonitor _monitor; - - public LibraryMonitorStartup(ILibraryMonitor monitor) - { - _monitor = monitor; - } - - public void Run() - { - _monitor.Start(); - } - - public void Dispose() - { - } - } -} diff --git a/Emby.Server.Core/IO/MemoryStreamProvider.cs b/Emby.Server.Core/IO/MemoryStreamProvider.cs deleted file mode 100644 index f6dd1ecbc..000000000 --- a/Emby.Server.Core/IO/MemoryStreamProvider.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System.IO; -using MediaBrowser.Model.IO; -using Microsoft.IO; - -namespace Emby.Server.Core.IO -{ - public class RecyclableMemoryStreamProvider : IMemoryStreamFactory - { - readonly RecyclableMemoryStreamManager _manager = new RecyclableMemoryStreamManager(); - - public MemoryStream CreateNew() - { - return _manager.GetStream(); - } - - public MemoryStream CreateNew(int capacity) - { - return _manager.GetStream("RecyclableMemoryStream", capacity); - } - - public MemoryStream CreateNew(byte[] buffer) - { - return _manager.GetStream("RecyclableMemoryStream", buffer, 0, buffer.Length); - } - - public bool TryGetBuffer(MemoryStream stream, out byte[] buffer) - { - buffer = stream.GetBuffer(); - return true; - } - } - - public class MemoryStreamProvider : IMemoryStreamFactory - { - public MemoryStream CreateNew() - { - return new MemoryStream(); - } - - public MemoryStream CreateNew(int capacity) - { - return new MemoryStream(capacity); - } - - public MemoryStream CreateNew(byte[] buffer) - { - return new MemoryStream(buffer); - } - - public bool TryGetBuffer(MemoryStream stream, out byte[] buffer) - { - buffer = stream.GetBuffer(); - return true; - } - } -} |
