mirror of
https://github.com/jellyfin/jellyfin.git
synced 2026-03-09 03:36:17 +00:00
move classes to new server project
This commit is contained in:
@@ -1,323 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Common.Events;
|
||||
using MediaBrowser.Common.IO;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.IO;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Model.Extensions;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using MediaBrowser.Model.Tasks;
|
||||
using MediaBrowser.Model.Threading;
|
||||
|
||||
namespace MediaBrowser.Server.Implementations.IO
|
||||
{
|
||||
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;
|
||||
private readonly object _timerLock = new object();
|
||||
public string Path { get; private set; }
|
||||
|
||||
public event EventHandler<EventArgs> Completed;
|
||||
|
||||
public FileRefresher(string path, IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, ITaskManager taskManager, ILogger logger, ITimerFactory timerFactory)
|
||||
{
|
||||
logger.Debug("New file refresher created for {0}", path);
|
||||
Path = path;
|
||||
|
||||
_fileSystem = fileSystem;
|
||||
ConfigurationManager = configurationManager;
|
||||
LibraryManager = libraryManager;
|
||||
TaskManager = taskManager;
|
||||
Logger = logger;
|
||||
_timerFactory = timerFactory;
|
||||
AddPath(path);
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
lock (_timerLock)
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_timer == null)
|
||||
{
|
||||
_timer = _timerFactory.Create(OnTimerCallback, null, TimeSpan.FromSeconds(ConfigurationManager.Configuration.LibraryMonitorDelay), TimeSpan.FromMilliseconds(-1));
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
||||
private async void OnTimerCallback(object state)
|
||||
{
|
||||
List<string> paths;
|
||||
|
||||
lock (_timerLock)
|
||||
{
|
||||
paths = _affectedPaths.ToList();
|
||||
}
|
||||
|
||||
// Extend the timer as long as any of the paths are still being written to.
|
||||
if (paths.Any(IsFileLocked))
|
||||
{
|
||||
Logger.Info("Timer extended.");
|
||||
RestartTimer();
|
||||
return;
|
||||
}
|
||||
|
||||
Logger.Debug("Timer stopped.");
|
||||
|
||||
DisposeTimer();
|
||||
EventHelper.FireEventIfNotNull(Completed, this, EventArgs.Empty, Logger);
|
||||
|
||||
try
|
||||
{
|
||||
await ProcessPathChanges(paths.ToList()).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.ErrorException("Error processing directory changes", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ProcessPathChanges(List<string> paths)
|
||||
{
|
||||
var itemsToRefresh = paths
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.Select(GetAffectedBaseItem)
|
||||
.Where(item => item != null)
|
||||
.DistinctBy(i => i.Id)
|
||||
.ToList();
|
||||
|
||||
foreach (var p in paths)
|
||||
{
|
||||
Logger.Info(p + " reports change.");
|
||||
}
|
||||
|
||||
// If the root folder changed, run the library task so the user can see it
|
||||
if (itemsToRefresh.Any(i => i is AggregateFolder))
|
||||
{
|
||||
LibraryManager.ValidateMediaLibrary(new Progress<double>(), CancellationToken.None);
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var item in itemsToRefresh)
|
||||
{
|
||||
Logger.Info(item.Name + " (" + item.Path + ") will be refreshed.");
|
||||
|
||||
try
|
||||
{
|
||||
await item.ChangedExternally().ConfigureAwait(false);
|
||||
}
|
||||
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);
|
||||
|
||||
path = System.IO.Path.GetDirectoryName(path);
|
||||
}
|
||||
|
||||
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 bool IsFileLocked(string path)
|
||||
{
|
||||
if (Environment.OSVersion.Platform != PlatformID.Win32NT)
|
||||
{
|
||||
// Causing lockups on linux
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var data = _fileSystem.GetFileSystemInfo(path);
|
||||
|
||||
if (!data.Exists
|
||||
|| data.IsDirectory
|
||||
|
||||
// Opening a writable stream will fail with readonly files
|
||||
|| data.IsReadOnly)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.ErrorException("Error getting file system info for: {0}", ex, path);
|
||||
return false;
|
||||
}
|
||||
|
||||
// In order to determine if the file is being written to, we have to request write access
|
||||
// But if the server only has readonly access, this is going to cause this entire algorithm to fail
|
||||
// So we'll take a best guess about our access level
|
||||
var requestedFileAccess = ConfigurationManager.Configuration.SaveLocalMeta
|
||||
? FileAccessMode.ReadWrite
|
||||
: FileAccessMode.Read;
|
||||
|
||||
try
|
||||
{
|
||||
using (_fileSystem.GetFileStream(path, FileOpenMode.Open, requestedFileAccess, FileShareMode.ReadWrite))
|
||||
{
|
||||
//file is not locked
|
||||
return false;
|
||||
}
|
||||
}
|
||||
//catch (DirectoryNotFoundException)
|
||||
//{
|
||||
// // File may have been deleted
|
||||
// return false;
|
||||
//}
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
// File may have been deleted
|
||||
return false;
|
||||
}
|
||||
catch (UnauthorizedAccessException)
|
||||
{
|
||||
Logger.Debug("No write permission for: {0}.", path);
|
||||
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 (Exception ex)
|
||||
{
|
||||
Logger.ErrorException("Error determining if file is locked: {0}", ex, path);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void DisposeTimer()
|
||||
{
|
||||
lock (_timerLock)
|
||||
{
|
||||
if (_timer != null)
|
||||
{
|
||||
_timer.Dispose();
|
||||
_timer = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool _disposed;
|
||||
public void Dispose()
|
||||
{
|
||||
_disposed = true;
|
||||
DisposeTimer();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,13 +10,14 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Emby.Server.Implementations.IO;
|
||||
using MediaBrowser.Common.IO;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.IO;
|
||||
using MediaBrowser.Model.System;
|
||||
using MediaBrowser.Model.Tasks;
|
||||
using MediaBrowser.Model.Threading;
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace MediaBrowser.Server.Implementations.IO
|
||||
{
|
||||
@@ -142,7 +143,7 @@ namespace MediaBrowser.Server.Implementations.IO
|
||||
/// <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)
|
||||
public LibraryMonitor(ILogManager logManager, ITaskManager taskManager, ILibraryManager libraryManager, IServerConfigurationManager configurationManager, IFileSystem fileSystem, ITimerFactory timerFactory, ISystemEvents systemEvents)
|
||||
{
|
||||
if (taskManager == null)
|
||||
{
|
||||
@@ -156,15 +157,10 @@ namespace MediaBrowser.Server.Implementations.IO
|
||||
_fileSystem = fileSystem;
|
||||
_timerFactory = timerFactory;
|
||||
|
||||
SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged;
|
||||
systemEvents.Resume += _systemEvents_Resume;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles the PowerModeChanged event of the SystemEvents control.
|
||||
/// </summary>
|
||||
/// <param name="sender">The source of the event.</param>
|
||||
/// <param name="e">The <see cref="PowerModeChangedEventArgs"/> instance containing the event data.</param>
|
||||
void SystemEvents_PowerModeChanged(object sender, PowerModeChangedEventArgs e)
|
||||
private void _systemEvents_Resume(object sender, EventArgs e)
|
||||
{
|
||||
Restart();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user