Parcourir la source

reduce device discovery traffic

Luke Pulverenti il y a 8 ans
Parent
commit
e0d5f7d158

+ 9 - 37
Emby.Dlna/Ssdp/DeviceDiscovery.cs

@@ -19,13 +19,12 @@ using Rssdp.Infrastructure;
 
 namespace Emby.Dlna.Ssdp
 {
-    public class DeviceDiscovery : IDeviceDiscovery, IDisposable
+    public class DeviceDiscovery : IDeviceDiscovery
     {
         private bool _disposed;
 
         private readonly ILogger _logger;
         private readonly IServerConfigurationManager _config;
-        private readonly CancellationTokenSource _tokenSource;
 
         public event EventHandler<GenericEventArgs<UpnpDeviceInfo>> DeviceDiscovered;
         public event EventHandler<GenericEventArgs<UpnpDeviceInfo>> DeviceLeft;
@@ -37,8 +36,6 @@ namespace Emby.Dlna.Ssdp
 
         public DeviceDiscovery(ILogger logger, IServerConfigurationManager config, ISocketFactory socketFactory, ITimerFactory timerFactory)
         {
-            _tokenSource = new CancellationTokenSource();
-
             _logger = logger;
             _config = config;
             _socketFactory = socketFactory;
@@ -59,39 +56,10 @@ namespace Emby.Dlna.Ssdp
             _deviceLocator.DeviceAvailable += deviceLocator_DeviceAvailable;
             _deviceLocator.DeviceUnavailable += _DeviceLocator_DeviceUnavailable;
 
-            // Perform a search so we don't have to wait for devices to broadcast notifications 
-            // again to get any results right away (notifications are broadcast periodically).
-            StartAsyncSearch();
-        }
-
-        private void StartAsyncSearch()
-        {
-            Task.Factory.StartNew(async (o) =>
-            {
-                while (!_tokenSource.IsCancellationRequested)
-                {
-                    try
-                    {
-                        // Enable listening for notifications (optional)
-                        _deviceLocator.StartListeningForNotifications();
-
-                        await _deviceLocator.SearchAsync(_tokenSource.Token).ConfigureAwait(false);
+            var dueTime = TimeSpan.FromSeconds(5);
+            var interval = TimeSpan.FromSeconds(_config.GetDlnaConfiguration().ClientDiscoveryIntervalSeconds);
 
-                        var delay = _config.GetDlnaConfiguration().ClientDiscoveryIntervalSeconds * 1000;
-
-                        await Task.Delay(delay, _tokenSource.Token).ConfigureAwait(false);
-                    }
-                    catch (OperationCanceledException)
-                    {
-
-                    }
-                    catch (Exception ex)
-                    {
-                        _logger.ErrorException("Error searching for devices", ex);
-                    }
-                }
-
-            }, CancellationToken.None, TaskCreationOptions.LongRunning);
+            _deviceLocator.RestartBroadcastTimer(dueTime, interval);
         }
 
         // Process each found device in the event handler
@@ -141,7 +109,11 @@ namespace Emby.Dlna.Ssdp
             if (!_disposed)
             {
                 _disposed = true;
-                _tokenSource.Cancel();
+                if (_deviceLocator != null)
+                {
+                    _deviceLocator.Dispose();
+                    _deviceLocator = null;
+                }
             }
         }
     }

+ 56 - 51
MediaBrowser.WebDashboard/Api/DashboardService.cs

@@ -139,6 +139,23 @@ namespace MediaBrowser.WebDashboard.Api
             _memoryStreamFactory = memoryStreamFactory;
         }
 
+        /// <summary>
+        /// Gets the dashboard UI path.
+        /// </summary>
+        /// <value>The dashboard UI path.</value>
+        public string DashboardUIPath
+        {
+            get
+            {
+                if (!string.IsNullOrEmpty(_serverConfigurationManager.Configuration.DashboardSourcePath))
+                {
+                    return _serverConfigurationManager.Configuration.DashboardSourcePath;
+                }
+
+                return Path.Combine(_serverConfigurationManager.ApplicationPaths.ApplicationResourcesPath, "dashboard-ui");
+            }
+        }
+
         public object Get(GetFavIcon request)
         {
             return Get(new GetDashboardResource
@@ -176,7 +193,7 @@ namespace MediaBrowser.WebDashboard.Api
 
             if (plugin != null && stream != null)
             {
-                return _resultFactory.GetStaticResult(Request, plugin.Version.ToString().GetMD5(), null, null, MimeTypes.GetMimeType("page.html"), () => GetPackageCreator().ModifyHtml("dummy.html", stream, null, _appHost.ApplicationVersion.ToString(), null));
+                return _resultFactory.GetStaticResult(Request, plugin.Version.ToString().GetMD5(), null, null, MimeTypes.GetMimeType("page.html"), () => GetPackageCreator(DashboardUIPath).ModifyHtml("dummy.html", stream, null, _appHost.ApplicationVersion.ToString(), null));
             }
 
             throw new ResourceNotFoundException();
@@ -274,9 +291,11 @@ namespace MediaBrowser.WebDashboard.Api
             path = path.Replace("bower_components" + _appHost.ApplicationVersion, "bower_components", StringComparison.OrdinalIgnoreCase);
 
             var contentType = MimeTypes.GetMimeType(path);
+            var basePath = DashboardUIPath;
 
             // Bounce them to the startup wizard if it hasn't been completed yet
-            if (!_serverConfigurationManager.Configuration.IsStartupWizardCompleted && path.IndexOf("wizard", StringComparison.OrdinalIgnoreCase) == -1 && GetPackageCreator().IsCoreHtml(path))
+            if (!_serverConfigurationManager.Configuration.IsStartupWizardCompleted &&
+                path.IndexOf("wizard", StringComparison.OrdinalIgnoreCase) == -1 && GetPackageCreator(basePath).IsCoreHtml(path))
             {
                 // But don't redirect if an html import is being requested.
                 if (path.IndexOf("bower_components", StringComparison.OrdinalIgnoreCase) == -1)
@@ -296,7 +315,7 @@ namespace MediaBrowser.WebDashboard.Api
                 !contentType.StartsWith("image/", StringComparison.OrdinalIgnoreCase) &&
                 !contentType.StartsWith("font/", StringComparison.OrdinalIgnoreCase))
             {
-                var stream = await GetResourceStream(path, localizationCulture).ConfigureAwait(false);
+                var stream = await GetResourceStream(basePath, path, localizationCulture).ConfigureAwait(false);
                 return _resultFactory.GetResult(stream, contentType);
             }
 
@@ -311,7 +330,7 @@ namespace MediaBrowser.WebDashboard.Api
 
             var cacheKey = (_appHost.ApplicationVersion + (localizationCulture ?? string.Empty) + path).GetMD5();
 
-            return await _resultFactory.GetStaticResult(Request, cacheKey, null, cacheDuration, contentType, () => GetResourceStream(path, localizationCulture)).ConfigureAwait(false);
+            return await _resultFactory.GetStaticResult(Request, cacheKey, null, cacheDuration, contentType, () => GetResourceStream(basePath, path, localizationCulture)).ConfigureAwait(false);
         }
 
         private string GetLocalizationCulture()
@@ -322,86 +341,72 @@ namespace MediaBrowser.WebDashboard.Api
         /// <summary>
         /// Gets the resource stream.
         /// </summary>
-        /// <param name="path">The path.</param>
-        /// <param name="localizationCulture">The localization culture.</param>
-        /// <returns>Task{Stream}.</returns>
-        private Task<Stream> GetResourceStream(string path, string localizationCulture)
+        private Task<Stream> GetResourceStream(string basePath, string virtualPath, string localizationCulture)
         {
-            return GetPackageCreator()
-                .GetResource(path, null, localizationCulture, _appHost.ApplicationVersion.ToString());
+            return GetPackageCreator(basePath)
+                .GetResource(virtualPath, null, localizationCulture, _appHost.ApplicationVersion.ToString());
         }
 
-        private PackageCreator GetPackageCreator()
+        private PackageCreator GetPackageCreator(string basePath)
         {
-            return new PackageCreator(_fileSystem, _logger, _serverConfigurationManager, _memoryStreamFactory);
+            return new PackageCreator(basePath, _fileSystem, _logger, _serverConfigurationManager, _memoryStreamFactory);
         }
 
         public async Task<object> Get(GetDashboardPackage request)
         {
             var mode = request.Mode;
 
-            var path = !string.IsNullOrWhiteSpace(mode) ?
-                Path.Combine(_serverConfigurationManager.ApplicationPaths.ProgramDataPath, "webclient-dump")
+            var inputPath = string.IsNullOrWhiteSpace(mode) ?
+                DashboardUIPath
+                : "C:\\dev\\emby-web-mobile-master\\dist";
+
+            var targetPath = !string.IsNullOrWhiteSpace(mode) ?
+                inputPath
                 : "C:\\dev\\emby-web-mobile\\src";
 
-            try
-            {
-                _fileSystem.DeleteDirectory(path, true);
-            }
-            catch (IOException)
-            {
+            var packageCreator = GetPackageCreator(inputPath);
 
-            }
+            if (!string.Equals(inputPath, targetPath, StringComparison.OrdinalIgnoreCase))
+            {
+                try
+                {
+                    _fileSystem.DeleteDirectory(targetPath, true);
+                }
+                catch (IOException)
+                {
 
-            var creator = GetPackageCreator();
+                }
 
-            CopyDirectory(creator.DashboardUIPath, path);
+                CopyDirectory(inputPath, targetPath);
+            }
 
             string culture = null;
 
             var appVersion = _appHost.ApplicationVersion.ToString();
 
-            // Try to trim the output size a bit
-            var bowerPath = Path.Combine(path, "bower_components");
-
-            if (!string.IsNullOrWhiteSpace(mode))
-            {
-                // Delete things that are unneeded in an attempt to keep the output as trim as possible
-
-                DeleteFoldersByName(Path.Combine(bowerPath, "emby-webcomponents", "fonts"), "roboto");
-                _fileSystem.DeleteDirectory(Path.Combine(path, "css", "images", "tour"), true);
-            }
-
-            await DumpHtml(creator.DashboardUIPath, path, mode, culture, appVersion);
+            await DumpHtml(packageCreator, inputPath, targetPath, mode, culture, appVersion);
 
             return "";
         }
 
-        private void DeleteFoldersByName(string path, string name)
-        {
-            var directories = _fileSystem.GetDirectories(path, true)
-                .Where(i => string.Equals(i.Name, name, StringComparison.OrdinalIgnoreCase))
-                .ToList();
-
-            foreach (var directory in directories)
-            {
-                _fileSystem.DeleteDirectory(directory.FullName, true);
-            }
-        }
-
-        private async Task DumpHtml(string source, string destination, string mode, string culture, string appVersion)
+        private async Task DumpHtml(PackageCreator packageCreator, string source, string destination, string mode, string culture, string appVersion)
         {
             foreach (var file in _fileSystem.GetFiles(source))
             {
                 var filename = file.Name;
 
-                await DumpFile(filename, Path.Combine(destination, filename), mode, culture, appVersion).ConfigureAwait(false);
+                if (!string.Equals(file.Extension, ".html", StringComparison.OrdinalIgnoreCase))
+                {
+                    continue;
+                }
+
+                await DumpFile(packageCreator, filename, Path.Combine(destination, filename), mode, culture, appVersion).ConfigureAwait(false);
             }
         }
 
-        private async Task DumpFile(string resourceVirtualPath, string destinationFilePath, string mode, string culture, string appVersion)
+        private async Task DumpFile(PackageCreator packageCreator, string resourceVirtualPath, string destinationFilePath, string mode, string culture, string appVersion)
         {
-            using (var stream = await GetPackageCreator().GetResource(resourceVirtualPath, mode, culture, appVersion).ConfigureAwait(false))
+            using (var stream = await packageCreator.GetResource(resourceVirtualPath, mode, culture, appVersion).ConfigureAwait(false))
             {
                 using (var fs = _fileSystem.GetFileStream(destinationFilePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read))
                 {

+ 14 - 36
MediaBrowser.WebDashboard/Api/PackageCreator.cs

@@ -19,31 +19,33 @@ namespace MediaBrowser.WebDashboard.Api
         private readonly ILogger _logger;
         private readonly IServerConfigurationManager _config;
         private readonly IMemoryStreamFactory _memoryStreamFactory;
+        private readonly string _basePath;
 
-        public PackageCreator(IFileSystem fileSystem, ILogger logger, IServerConfigurationManager config, IMemoryStreamFactory memoryStreamFactory)
+        public PackageCreator(string basePath, IFileSystem fileSystem, ILogger logger, IServerConfigurationManager config, IMemoryStreamFactory memoryStreamFactory)
         {
             _fileSystem = fileSystem;
             _logger = logger;
             _config = config;
             _memoryStreamFactory = memoryStreamFactory;
+            _basePath = basePath;
         }
 
-        public async Task<Stream> GetResource(string path,
+        public async Task<Stream> GetResource(string virtualPath,
             string mode,
             string localizationCulture,
             string appVersion)
         {
-            var resourceStream = GetRawResourceStream(path);
+            var resourceStream = GetRawResourceStream(virtualPath);
 
             if (resourceStream != null)
             {
                 // Don't apply any caching for html pages
                 // jQuery ajax doesn't seem to handle if-modified-since correctly
-                if (IsFormat(path, "html"))
+                if (IsFormat(virtualPath, "html"))
                 {
-                    if (IsCoreHtml(path))
+                    if (IsCoreHtml(virtualPath))
                     {
-                        resourceStream = await ModifyHtml(path, resourceStream, mode, appVersion, localizationCulture).ConfigureAwait(false);
+                        resourceStream = await ModifyHtml(virtualPath, resourceStream, mode, appVersion, localizationCulture).ConfigureAwait(false);
                     }
                 }
             }
@@ -62,33 +64,13 @@ namespace MediaBrowser.WebDashboard.Api
             return Path.GetExtension(path).EndsWith(format, StringComparison.OrdinalIgnoreCase);
         }
 
-        /// <summary>
-        /// Gets the dashboard UI path.
-        /// </summary>
-        /// <value>The dashboard UI path.</value>
-        public string DashboardUIPath
-        {
-            get
-            {
-                if (!string.IsNullOrEmpty(_config.Configuration.DashboardSourcePath))
-                {
-                    return _config.Configuration.DashboardSourcePath;
-                }
-
-                return Path.Combine(_config.ApplicationPaths.ApplicationResourcesPath, "dashboard-ui");
-            }
-        }
-
         /// <summary>
         /// Gets the dashboard resource path.
         /// </summary>
-        /// <param name="virtualPath">The virtual path.</param>
         /// <returns>System.String.</returns>
         private string GetDashboardResourcePath(string virtualPath)
         {
-            var rootPath = DashboardUIPath;
-
-            var fullPath = Path.Combine(rootPath, virtualPath.Replace('/', _fileSystem.DirectorySeparatorChar));
+            var fullPath = Path.Combine(_basePath, virtualPath.Replace('/', _fileSystem.DirectorySeparatorChar));
 
             try
             {
@@ -100,7 +82,7 @@ namespace MediaBrowser.WebDashboard.Api
             }
 
             // Don't allow file system access outside of the source folder
-            if (!_fileSystem.ContainsSubPath(rootPath, fullPath))
+            if (!_fileSystem.ContainsSubPath(_basePath, fullPath))
             {
                 throw new SecurityException("Access denied");
             }
@@ -118,10 +100,8 @@ namespace MediaBrowser.WebDashboard.Api
             path = GetDashboardResourcePath(path);
             var parent = Path.GetDirectoryName(path);
 
-            var basePath = DashboardUIPath;
-
-            return string.Equals(basePath, parent, StringComparison.OrdinalIgnoreCase) ||
-                   string.Equals(Path.Combine(basePath, "voice"), parent, StringComparison.OrdinalIgnoreCase);
+            return string.Equals(_basePath, parent, StringComparison.OrdinalIgnoreCase) ||
+                   string.Equals(Path.Combine(_basePath, "voice"), parent, StringComparison.OrdinalIgnoreCase);
         }
 
         /// <summary>
@@ -319,11 +299,9 @@ namespace MediaBrowser.WebDashboard.Api
         /// <summary>
         /// Gets the raw resource stream.
         /// </summary>
-        /// <param name="path">The path.</param>
-        /// <returns>Task{Stream}.</returns>
-        private Stream GetRawResourceStream(string path)
+        private Stream GetRawResourceStream(string virtualPath)
         {
-            return _fileSystem.GetFileStream(GetDashboardResourcePath(path), FileOpenMode.Open, FileAccessMode.Read, FileShareMode.ReadWrite, true);
+            return _fileSystem.GetFileStream(GetDashboardResourcePath(virtualPath), FileOpenMode.Open, FileAccessMode.Read, FileShareMode.ReadWrite, true);
         }
 
     }

+ 51 - 122
RSSDP/SsdpDeviceLocatorBase.cs

@@ -24,11 +24,9 @@ namespace Rssdp.Infrastructure
         private List<DiscoveredSsdpDevice> _Devices;
         private ISsdpCommunicationsServer _CommunicationsServer;
 
-        private IList<DiscoveredSsdpDevice> _SearchResults;
-        private object _SearchResultsSynchroniser;
-
-        private ITimer _ExpireCachedDevicesTimer;
+        private ITimer _BroadcastTimer;
         private ITimerFactory _timerFactory;
+        private object _timerLock = new object();
 
         private static readonly TimeSpan DefaultSearchWaitTime = TimeSpan.FromSeconds(4);
         private static readonly TimeSpan OneSecond = TimeSpan.FromSeconds(1);
@@ -48,7 +46,6 @@ namespace Rssdp.Infrastructure
             _timerFactory = timerFactory;
             _CommunicationsServer.ResponseReceived += CommsServer_ResponseReceived;
 
-            _SearchResultsSynchroniser = new object();
             _Devices = new List<DiscoveredSsdpDevice>();
         }
 
@@ -92,11 +89,52 @@ namespace Rssdp.Infrastructure
 
         #region Search Overloads
 
+        public void RestartBroadcastTimer(TimeSpan dueTime, TimeSpan period)
+        {
+            lock (_timerLock)
+            {
+                if (_BroadcastTimer == null)
+                {
+                    _BroadcastTimer = _timerFactory.Create(OnBroadcastTimerCallback, null, dueTime, period);
+                }
+                else
+                {
+                    _BroadcastTimer.Change(dueTime, period);
+                }
+            }
+        }
+
+        public void DisposeBroadcastTimer()
+        {
+            lock (_timerLock)
+            {
+                if (_BroadcastTimer != null)
+                {
+                    _BroadcastTimer.Dispose();
+                    _BroadcastTimer = null;
+                }
+            }
+        }
+
+        private async void OnBroadcastTimerCallback(object state)
+        {
+            StartListeningForNotifications();
+            RemoveExpiredDevicesFromCache();
+
+            try
+            {
+                await SearchAsync(CancellationToken.None).ConfigureAwait(false);
+            }
+            catch (Exception ex)
+            {
+                
+            }
+        }
+
         /// <summary>
         /// Performs a search for all devices using the default search timeout.
         /// </summary>
-        /// <returns>A task whose result is an <see cref="IEnumerable{T}"/> of <see cref="DiscoveredSsdpDevice" /> instances, representing all found devices.</returns>
-        public Task<IEnumerable<DiscoveredSsdpDevice>> SearchAsync(CancellationToken cancellationToken)
+        private Task SearchAsync(CancellationToken cancellationToken)
         {
             return SearchAsync(SsdpConstants.SsdpDiscoverAllSTHeader, DefaultSearchWaitTime, cancellationToken);
         }
@@ -111,8 +149,7 @@ namespace Rssdp.Infrastructure
         /// <item><term>Device type</term><description>Fully qualified device type starting with urn: i.e urn:schemas-upnp-org:Basic:1</description></item>
         /// </list>
         /// </param>
-        /// <returns>A task whose result is an <see cref="IEnumerable{T}"/> of <see cref="DiscoveredSsdpDevice" /> instances, representing all found devices.</returns>
-        public Task<IEnumerable<DiscoveredSsdpDevice>> SearchAsync(string searchTarget)
+        private Task SearchAsync(string searchTarget)
         {
             return SearchAsync(searchTarget, DefaultSearchWaitTime, CancellationToken.None);
         }
@@ -121,13 +158,12 @@ namespace Rssdp.Infrastructure
         /// Performs a search for all devices using the specified search timeout.
         /// </summary>
         /// <param name="searchWaitTime">The amount of time to wait for network responses to the search request. Longer values will likely return more devices, but increase search time. A value between 1 and 5 seconds is recommended by the UPnP 1.1 specification, this method requires the value be greater 1 second if it is not zero. Specify TimeSpan.Zero to return only devices already in the cache.</param>
-        /// <returns>A task whose result is an <see cref="IEnumerable{T}"/> of <see cref="DiscoveredSsdpDevice" /> instances, representing all found devices.</returns>
-        public Task<IEnumerable<DiscoveredSsdpDevice>> SearchAsync(TimeSpan searchWaitTime)
+        private Task SearchAsync(TimeSpan searchWaitTime)
         {
             return SearchAsync(SsdpConstants.SsdpDiscoverAllSTHeader, searchWaitTime, CancellationToken.None);
         }
 
-        public async Task<IEnumerable<DiscoveredSsdpDevice>> SearchAsync(string searchTarget, TimeSpan searchWaitTime, CancellationToken cancellationToken)
+        private Task SearchAsync(string searchTarget, TimeSpan searchWaitTime, CancellationToken cancellationToken)
         {
             if (searchTarget == null) throw new ArgumentNullException("searchTarget");
             if (searchTarget.Length == 0) throw new ArgumentException("searchTarget cannot be an empty string.", "searchTarget");
@@ -136,48 +172,7 @@ namespace Rssdp.Infrastructure
 
             ThrowIfDisposed();
 
-            if (_SearchResults != null) throw new InvalidOperationException("Search already in progress. Only one search at a time is allowed.");
-            _SearchResults = new List<DiscoveredSsdpDevice>();
-
-            // If searchWaitTime == 0 then we are only going to report unexpired cached items, not actually do a search.
-            if (searchWaitTime > TimeSpan.Zero)
-                await BroadcastDiscoverMessage(searchTarget, SearchTimeToMXValue(searchWaitTime), cancellationToken).ConfigureAwait(false);
-
-            lock (_SearchResultsSynchroniser)
-            {
-                foreach (var device in GetUnexpiredDevices().Where(NotificationTypeMatchesFilter))
-                {
-                    DeviceFound(device, false, null);
-                }
-            }
-
-            if (searchWaitTime != TimeSpan.Zero)
-                await Task.Delay(searchWaitTime, cancellationToken).ConfigureAwait(false);
-
-            IEnumerable<DiscoveredSsdpDevice> retVal = null;
-
-            try
-            {
-                lock (_SearchResultsSynchroniser)
-                {
-                    retVal = _SearchResults;
-                    _SearchResults = null;
-                }
-
-                RemoveExpiredDevicesFromCache();
-            }
-            finally
-            {
-                var server = _CommunicationsServer;
-                try
-                {
-                    if (server != null) // In case we were disposed while searching.
-                        server.StopListeningForResponses();
-                }
-                catch (ObjectDisposedException) { }
-            }
-
-            return retVal;
+            return BroadcastDiscoverMessage(searchTarget, SearchTimeToMXValue(searchWaitTime), cancellationToken);
         }
 
         #endregion
@@ -287,9 +282,7 @@ namespace Rssdp.Infrastructure
 
             if (disposing)
             {
-                var timer = _ExpireCachedDevicesTimer;
-                if (timer != null)
-                    timer.Dispose();
+                DisposeBroadcastTimer();
 
                 var commsServer = _CommunicationsServer;
                 _CommunicationsServer = null;
@@ -332,40 +325,9 @@ namespace Rssdp.Infrastructure
 
         private void DeviceFound(DiscoveredSsdpDevice device, bool isNewDevice, IpAddressInfo localIpAddress)
         {
-            // Don't raise the event if we've already done it for a cached
-            // version of this device, and the cached version isn't
-            // "significantly" different, i.e location and cachelifetime 
-            // haven't changed.
-            var raiseEvent = false;
-
             if (!NotificationTypeMatchesFilter(device)) return;
 
-            lock (_SearchResultsSynchroniser)
-            {
-                if (_SearchResults != null)
-                {
-                    var existingDevice = FindExistingDeviceNotification(_SearchResults, device.NotificationType, device.Usn);
-                    if (existingDevice == null)
-                    {
-                        _SearchResults.Add(device);
-                        raiseEvent = true;
-                    }
-                    else
-                    {
-                        if (existingDevice.DescriptionLocation != device.DescriptionLocation || existingDevice.CacheLifetime != device.CacheLifetime)
-                        {
-                            _SearchResults.Remove(existingDevice);
-                            _SearchResults.Add(device);
-                            raiseEvent = true;
-                        }
-                    }
-                }
-                else
-                    raiseEvent = true;
-            }
-
-            if (raiseEvent)
-                OnDeviceAvailable(device, isNewDevice, localIpAddress);
+            OnDeviceAvailable(device, isNewDevice, localIpAddress);
         }
 
         private bool NotificationTypeMatchesFilter(DiscoveredSsdpDevice device)
@@ -450,8 +412,6 @@ namespace Rssdp.Infrastructure
                 };
 
                 AddOrUpdateDiscoveredDevice(device, localIpAddress);
-
-                ResetExpireCachedDevicesTimer();
             }
         }
 
@@ -477,26 +437,9 @@ namespace Rssdp.Infrastructure
                     if (NotificationTypeMatchesFilter(deadDevice))
                         OnDeviceUnavailable(deadDevice, false);
                 }
-
-                ResetExpireCachedDevicesTimer();
             }
         }
 
-        private void ResetExpireCachedDevicesTimer()
-        {
-            if (IsDisposed) return;
-
-            if (_ExpireCachedDevicesTimer == null)
-                _ExpireCachedDevicesTimer = _timerFactory.Create(this.ExpireCachedDevices, null, System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
-
-            _ExpireCachedDevicesTimer.Change(60000, System.Threading.Timeout.Infinite);
-        }
-
-        private void ExpireCachedDevices(object state)
-        {
-            RemoveExpiredDevicesFromCache();
-        }
-
         #region Header/Message Processing Utilities
 
         private static string GetFirstHeaderStringValue(string headerName, HttpResponseMessage message)
@@ -624,20 +567,6 @@ namespace Rssdp.Infrastructure
 
             if (existingDevices != null && existingDevices.Any())
             {
-                lock (_SearchResultsSynchroniser)
-                {
-                    if (_SearchResults != null)
-                    {
-                        var resultsToRemove = (from result in _SearchResults where result.Usn == deviceUsn select result).ToArray();
-                        foreach (var result in resultsToRemove)
-                        {
-                            if (this.IsDisposed) return true;
-
-                            _SearchResults.Remove(result);
-                        }
-                    }
-                }
-
                 foreach (var removedDevice in existingDevices)
                 {
                     if (NotificationTypeMatchesFilter(removedDevice))