Sfoglia il codice sorgente

Merge pull request #1744 from Bond-009/dataprovider

Rewrite `ItemDataProvider` to be more robust
Joshua M. Boniface 5 anni fa
parent
commit
adc2a68a98

+ 1 - 1
Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs

@@ -102,7 +102,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
             _streamHelper = streamHelper;
             _streamHelper = streamHelper;
 
 
             _seriesTimerProvider = new SeriesTimerManager(jsonSerializer, _logger, Path.Combine(DataPath, "seriestimers.json"));
             _seriesTimerProvider = new SeriesTimerManager(jsonSerializer, _logger, Path.Combine(DataPath, "seriestimers.json"));
-            _timerProvider = new TimerManager(jsonSerializer, _logger, Path.Combine(DataPath, "timers.json"), _logger);
+            _timerProvider = new TimerManager(jsonSerializer, _logger, Path.Combine(DataPath, "timers.json"));
             _timerProvider.TimerFired += _timerProvider_TimerFired;
             _timerProvider.TimerFired += _timerProvider_TimerFired;
 
 
             _config.NamedConfigurationUpdated += _config_NamedConfigurationUpdated;
             _config.NamedConfigurationUpdated += _config_NamedConfigurationUpdated;

+ 84 - 64
Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs

@@ -10,67 +10,64 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
     public class ItemDataProvider<T>
     public class ItemDataProvider<T>
         where T : class
         where T : class
     {
     {
-        private readonly object _fileDataLock = new object();
-        private List<T> _items;
         private readonly IJsonSerializer _jsonSerializer;
         private readonly IJsonSerializer _jsonSerializer;
-        protected readonly ILogger Logger;
         private readonly string _dataPath;
         private readonly string _dataPath;
-        protected readonly Func<T, T, bool> EqualityComparer;
+        private readonly object _fileDataLock = new object();
+        private T[] _items;
 
 
-        public ItemDataProvider(IJsonSerializer jsonSerializer, ILogger logger, string dataPath, Func<T, T, bool> equalityComparer)
+        public ItemDataProvider(
+            IJsonSerializer jsonSerializer,
+            ILogger logger,
+            string dataPath,
+            Func<T, T, bool> equalityComparer)
         {
         {
+            _jsonSerializer = jsonSerializer;
             Logger = logger;
             Logger = logger;
             _dataPath = dataPath;
             _dataPath = dataPath;
             EqualityComparer = equalityComparer;
             EqualityComparer = equalityComparer;
-            _jsonSerializer = jsonSerializer;
         }
         }
 
 
-        public IReadOnlyList<T> GetAll()
-        {
-            lock (_fileDataLock)
-            {
-                if (_items == null)
-                {
-                    if (!File.Exists(_dataPath))
-                    {
-                        return new List<T>();
-                    }
-
-                    Logger.LogInformation("Loading live tv data from {0}", _dataPath);
-                    _items = GetItemsFromFile(_dataPath);
-                }
+        protected ILogger Logger { get; }
 
 
-                return _items.ToList();
-            }
-        }
+        protected Func<T, T, bool> EqualityComparer { get; }
 
 
-        private List<T> GetItemsFromFile(string path)
+        private void EnsureLoaded()
         {
         {
-            try
+            if (_items != null)
             {
             {
-                return _jsonSerializer.DeserializeFromFile<List<T>>(path);
+                return;
             }
             }
-            catch (Exception ex)
+
+            if (File.Exists(_dataPath))
             {
             {
-                Logger.LogError(ex, "Error deserializing {Path}", path);
+                Logger.LogInformation("Loading live tv data from {Path}", _dataPath);
+
+                try
+                {
+                    _items = _jsonSerializer.DeserializeFromFile<T[]>(_dataPath);
+                    return;
+                }
+                catch (Exception ex)
+                {
+                    Logger.LogError(ex, "Error deserializing {Path}", _dataPath);
+                }
             }
             }
 
 
-            return new List<T>();
+            _items = Array.Empty<T>();
         }
         }
 
 
-        private void UpdateList(List<T> newList)
+        private void SaveList()
         {
         {
-            if (newList == null)
-            {
-                throw new ArgumentNullException(nameof(newList));
-            }
-
             Directory.CreateDirectory(Path.GetDirectoryName(_dataPath));
             Directory.CreateDirectory(Path.GetDirectoryName(_dataPath));
+            _jsonSerializer.SerializeToFile(_items, _dataPath);
+        }
 
 
+        public IReadOnlyList<T> GetAll()
+        {
             lock (_fileDataLock)
             lock (_fileDataLock)
             {
             {
-                _jsonSerializer.SerializeToFile(newList, _dataPath);
-                _items = newList;
+                EnsureLoaded();
+                return (T[])_items.Clone();
             }
             }
         }
         }
 
 
@@ -81,18 +78,20 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
                 throw new ArgumentNullException(nameof(item));
                 throw new ArgumentNullException(nameof(item));
             }
             }
 
 
-            var list = GetAll().ToList();
-
-            var index = list.FindIndex(i => EqualityComparer(i, item));
-
-            if (index == -1)
+            lock (_fileDataLock)
             {
             {
-                throw new ArgumentException("item not found");
-            }
+                EnsureLoaded();
 
 
-            list[index] = item;
+                var index = Array.FindIndex(_items, i => EqualityComparer(i, item));
+                if (index == -1)
+                {
+                    throw new ArgumentException("item not found");
+                }
 
 
-            UpdateList(list);
+                _items[index] = item;
+
+                SaveList();
+            }
         }
         }
 
 
         public virtual void Add(T item)
         public virtual void Add(T item)
@@ -102,37 +101,58 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
                 throw new ArgumentNullException(nameof(item));
                 throw new ArgumentNullException(nameof(item));
             }
             }
 
 
-            var list = GetAll().ToList();
-
-            if (list.Any(i => EqualityComparer(i, item)))
+            lock (_fileDataLock)
             {
             {
-                throw new ArgumentException("item already exists");
-            }
+                EnsureLoaded();
 
 
-            list.Add(item);
+                if (_items.Any(i => EqualityComparer(i, item)))
+                {
+                    throw new ArgumentException("item already exists", nameof(item));
+                }
 
 
-            UpdateList(list);
+                int oldLen = _items.Length;
+                var newList = new T[oldLen + 1];
+                _items.CopyTo(newList, 0);
+                newList[oldLen] = item;
+                _items = newList;
+
+                SaveList();
+            }
         }
         }
 
 
-        public void AddOrUpdate(T item)
+        public virtual void AddOrUpdate(T item)
         {
         {
-            var list = GetAll().ToList();
-
-            if (!list.Any(i => EqualityComparer(i, item)))
-            {
-                Add(item);
-            }
-            else
+            lock (_fileDataLock)
             {
             {
-                Update(item);
+                EnsureLoaded();
+
+                int index = Array.FindIndex(_items, i => EqualityComparer(i, item));
+                if (index == -1)
+                {
+                    int oldLen = _items.Length;
+                    var newList = new T[oldLen + 1];
+                    _items.CopyTo(newList, 0);
+                    newList[oldLen] = item;
+                    _items = newList;
+                }
+                else
+                {
+                    _items[index] = item;
+                }
+
+                SaveList();
             }
             }
         }
         }
 
 
         public virtual void Delete(T item)
         public virtual void Delete(T item)
         {
         {
-            var list = GetAll().Where(i => !EqualityComparer(i, item)).ToList();
+            lock (_fileDataLock)
+            {
+                EnsureLoaded();
+                _items = _items.Where(i => !EqualityComparer(i, item)).ToArray();
 
 
-            UpdateList(list);
+                SaveList();
+            }
         }
         }
     }
     }
 }
 }

+ 18 - 19
Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs

@@ -14,21 +14,19 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
     public class TimerManager : ItemDataProvider<TimerInfo>
     public class TimerManager : ItemDataProvider<TimerInfo>
     {
     {
         private readonly ConcurrentDictionary<string, Timer> _timers = new ConcurrentDictionary<string, Timer>(StringComparer.OrdinalIgnoreCase);
         private readonly ConcurrentDictionary<string, Timer> _timers = new ConcurrentDictionary<string, Timer>(StringComparer.OrdinalIgnoreCase);
-        private readonly ILogger _logger;
 
 
-        public event EventHandler<GenericEventArgs<TimerInfo>> TimerFired;
-
-        public TimerManager(IJsonSerializer jsonSerializer, ILogger logger, string dataPath, ILogger logger1)
+        public TimerManager(IJsonSerializer jsonSerializer, ILogger logger, string dataPath)
             : base(jsonSerializer, logger, dataPath, (r1, r2) => string.Equals(r1.Id, r2.Id, StringComparison.OrdinalIgnoreCase))
             : base(jsonSerializer, logger, dataPath, (r1, r2) => string.Equals(r1.Id, r2.Id, StringComparison.OrdinalIgnoreCase))
         {
         {
-            _logger = logger1;
         }
         }
 
 
+        public event EventHandler<GenericEventArgs<TimerInfo>> TimerFired;
+
         public void RestartTimers()
         public void RestartTimers()
         {
         {
             StopTimers();
             StopTimers();
 
 
-            foreach (var item in GetAll().ToList())
+            foreach (var item in GetAll())
             {
             {
                 AddOrUpdateSystemTimer(item);
                 AddOrUpdateSystemTimer(item);
             }
             }
@@ -64,16 +62,13 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
                 return;
                 return;
             }
             }
 
 
-            var list = GetAll().ToList();
+            base.AddOrUpdate(item);
+        }
 
 
-            if (!list.Any(i => EqualityComparer(i, item)))
-            {
-                base.Add(item);
-            }
-            else
-            {
-                base.Update(item);
-            }
+        public override void AddOrUpdate(TimerInfo item)
+        {
+            base.AddOrUpdate(item);
+            AddOrUpdateSystemTimer(item);
         }
         }
 
 
         public override void Add(TimerInfo item)
         public override void Add(TimerInfo item)
@@ -89,8 +84,8 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
 
 
         private static bool ShouldStartTimer(TimerInfo item)
         private static bool ShouldStartTimer(TimerInfo item)
         {
         {
-            if (item.Status == RecordingStatus.Completed ||
-                item.Status == RecordingStatus.Cancelled)
+            if (item.Status == RecordingStatus.Completed
+                || item.Status == RecordingStatus.Cancelled)
             {
             {
                 return false;
                 return false;
             }
             }
@@ -126,12 +121,16 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
 
 
             if (_timers.TryAdd(item.Id, timer))
             if (_timers.TryAdd(item.Id, timer))
             {
             {
-                _logger.LogInformation("Creating recording timer for {id}, {name}. Timer will fire in {minutes} minutes", item.Id, item.Name, dueTime.TotalMinutes.ToString(CultureInfo.InvariantCulture));
+                Logger.LogInformation(
+                    "Creating recording timer for {Id}, {Name}. Timer will fire in {Minutes} minutes",
+                    item.Id,
+                    item.Name,
+                    dueTime.TotalMinutes.ToString(CultureInfo.InvariantCulture));
             }
             }
             else
             else
             {
             {
                 timer.Dispose();
                 timer.Dispose();
-                _logger.LogWarning("Timer already exists for item {id}", item.Id);
+                Logger.LogWarning("Timer already exists for item {Id}", item.Id);
             }
             }
         }
         }