< Summary - Jellyfin

Information
Class: Emby.Server.Implementations.IO.FileRefresher
Assembly: Emby.Server.Implementations
File(s): /srv/git/jellyfin/Emby.Server.Implementations/IO/FileRefresher.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 84
Coverable lines: 84
Total lines: 215
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 36
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%210%
AddAffectedPath(...)0%620%
AddPath(...)100%210%
RestartTimer()0%4260%
ResetPath(...)0%620%
OnTimerCallback(...)0%620%
ProcessPathChanges(...)0%2040%
GetAffectedBaseItem(...)0%272160%
DisposeTimer()0%620%
Dispose()0%620%

File(s)

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

#LineLine coverage
 1#pragma warning disable CS1591
 2
 3using System;
 4using System.Collections.Generic;
 5using System.IO;
 6using System.Linq;
 7using System.Threading;
 8using MediaBrowser.Controller.Configuration;
 9using MediaBrowser.Controller.Entities;
 10using MediaBrowser.Controller.Library;
 11using Microsoft.Extensions.Logging;
 12
 13namespace Emby.Server.Implementations.IO
 14{
 15    public sealed class FileRefresher : IDisposable
 16    {
 17        private readonly ILogger _logger;
 18        private readonly ILibraryManager _libraryManager;
 19        private readonly IServerConfigurationManager _configurationManager;
 20
 021        private readonly List<string> _affectedPaths = new List<string>();
 022        private readonly object _timerLock = new object();
 23        private Timer? _timer;
 24        private bool _disposed;
 25
 26        public FileRefresher(string path, IServerConfigurationManager configurationManager, ILibraryManager libraryManag
 27        {
 028            logger.LogDebug("New file refresher created for {0}", path);
 029            Path = path;
 30
 031            _configurationManager = configurationManager;
 032            _libraryManager = libraryManager;
 033            _logger = logger;
 034            AddPath(path);
 035        }
 36
 37        public event EventHandler<EventArgs>? Completed;
 38
 39        public string Path { get; private set; }
 40
 41        private void AddAffectedPath(string path)
 42        {
 043            ArgumentException.ThrowIfNullOrEmpty(path);
 44
 045            if (!_affectedPaths.Contains(path, StringComparer.Ordinal))
 46            {
 047                _affectedPaths.Add(path);
 48            }
 049        }
 50
 51        public void AddPath(string path)
 52        {
 053            ArgumentException.ThrowIfNullOrEmpty(path);
 54
 055            lock (_timerLock)
 56            {
 057                AddAffectedPath(path);
 058            }
 59
 060            RestartTimer();
 061        }
 62
 63        public void RestartTimer()
 64        {
 065            if (_disposed)
 66            {
 067                return;
 68            }
 69
 070            lock (_timerLock)
 71            {
 072                if (_disposed)
 73                {
 074                    return;
 75                }
 76
 077                if (_timer is null)
 78                {
 079                    _timer = new Timer(OnTimerCallback, null, TimeSpan.FromSeconds(_configurationManager.Configuration.L
 80                }
 81                else
 82                {
 083                    _timer.Change(TimeSpan.FromSeconds(_configurationManager.Configuration.LibraryMonitorDelay), TimeSpa
 84                }
 085            }
 086        }
 87
 88        public void ResetPath(string path, string? affectedFile)
 89        {
 090            lock (_timerLock)
 91            {
 092                _logger.LogDebug("Resetting file refresher from {0} to {1}", Path, path);
 93
 094                Path = path;
 095                AddAffectedPath(path);
 96
 097                if (!string.IsNullOrEmpty(affectedFile))
 98                {
 099                    AddAffectedPath(affectedFile);
 100                }
 0101            }
 102
 0103            RestartTimer();
 0104        }
 105
 106        private void OnTimerCallback(object? state)
 107        {
 108            List<string> paths;
 109
 0110            lock (_timerLock)
 111            {
 0112                paths = _affectedPaths.ToList();
 0113            }
 114
 0115            _logger.LogDebug("Timer stopped.");
 116
 0117            DisposeTimer();
 0118            Completed?.Invoke(this, EventArgs.Empty);
 119
 120            try
 121            {
 0122                ProcessPathChanges(paths);
 0123            }
 0124            catch (Exception ex)
 125            {
 0126                _logger.LogError(ex, "Error processing directory changes");
 0127            }
 0128        }
 129
 130        private void ProcessPathChanges(List<string> paths)
 131        {
 0132            IEnumerable<BaseItem> itemsToRefresh = paths
 0133                .Distinct(StringComparer.OrdinalIgnoreCase)
 0134                .Select(GetAffectedBaseItem)
 0135                .Where(item => item is not null)
 0136                .DistinctBy(x => x!.Id)!;  // Removed null values in the previous .Where()
 137
 0138            foreach (var item in itemsToRefresh)
 139            {
 0140                if (item is AggregateFolder)
 141                {
 142                    continue;
 143                }
 144
 0145                _logger.LogInformation("{Name} ({Path}) will be refreshed.", item.Name, item.Path);
 146
 147                try
 148                {
 0149                    item.ChangedExternally();
 0150                }
 0151                catch (Exception ex)
 152                {
 0153                    _logger.LogError(ex, "Error refreshing {Name}", item.Name);
 0154                }
 155            }
 0156        }
 157
 158        /// <summary>
 159        /// Gets the affected base item.
 160        /// </summary>
 161        /// <param name="path">The path.</param>
 162        /// <returns>BaseItem.</returns>
 163        private BaseItem? GetAffectedBaseItem(string path)
 164        {
 0165            BaseItem? item = null;
 166
 0167            while (item is null && !string.IsNullOrEmpty(path))
 168            {
 0169                item = _libraryManager.FindByPath(path, null);
 170
 0171                path = System.IO.Path.GetDirectoryName(path) ?? string.Empty;
 172            }
 173
 0174            if (item is not null)
 175            {
 176                // If the item has been deleted find the first valid parent that still exists
 0177                while (!Directory.Exists(item.Path) && !File.Exists(item.Path))
 178                {
 0179                    item = item.GetOwner() ?? item.GetParent();
 180
 0181                    if (item is null)
 182                    {
 183                        break;
 184                    }
 185                }
 186            }
 187
 0188            return item;
 189        }
 190
 191        private void DisposeTimer()
 192        {
 0193            lock (_timerLock)
 194            {
 0195                if (_timer is not null)
 196                {
 0197                    _timer.Dispose();
 0198                    _timer = null;
 199                }
 0200            }
 0201        }
 202
 203        /// <inheritdoc />
 204        public void Dispose()
 205        {
 0206            if (_disposed)
 207            {
 0208                return;
 209            }
 210
 0211            DisposeTimer();
 0212            _disposed = true;
 0213        }
 214    }
 215}