Forráskód Böngészése

Merge branch 'beta'

Luke Pulverenti 9 éve
szülő
commit
37352785ac
61 módosított fájl, 835 hozzáadás és 606 törlés
  1. 6 2
      MediaBrowser.Api/EnvironmentService.cs
  2. 1 1
      MediaBrowser.Api/Playback/BaseStreamingService.cs
  3. 3 3
      MediaBrowser.Api/Playback/MediaInfoService.cs
  4. 1 2
      MediaBrowser.Api/StartupWizardService.cs
  5. 2 0
      MediaBrowser.Api/UserLibrary/PlaystateService.cs
  6. 9 15
      MediaBrowser.Common.Implementations/Networking/BaseNetworkManager.cs
  7. 4 4
      MediaBrowser.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs
  8. 1 0
      MediaBrowser.Common/MediaBrowser.Common.csproj
  9. 72 0
      MediaBrowser.Common/Threading/PeriodicTimer.cs
  10. 59 2
      MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
  11. 2 2
      MediaBrowser.Controller/Entities/User.cs
  12. 1 0
      MediaBrowser.Controller/MediaBrowser.Controller.csproj
  13. 13 0
      MediaBrowser.Controller/Power/IPowerManagement.cs
  14. 42 37
      MediaBrowser.Dlna/PlayTo/Device.cs
  15. 18 31
      MediaBrowser.Dlna/PlayTo/PlayToController.cs
  16. 7 17
      MediaBrowser.Dlna/PlayTo/PlayToManager.cs
  17. 5 5
      MediaBrowser.Dlna/Profiles/WdtvLiveProfile.cs
  18. 5 5
      MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml
  19. 15 115
      MediaBrowser.Dlna/Ssdp/SsdpHandler.cs
  20. 18 9
      MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
  21. 2 3
      MediaBrowser.Model/Configuration/ServerConfiguration.cs
  22. 1 0
      MediaBrowser.Model/Configuration/UserConfiguration.cs
  23. 11 3
      MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs
  24. 7 11
      MediaBrowser.Providers/People/MovieDbPersonProvider.cs
  25. 2 2
      MediaBrowser.Providers/TV/TvdbSeriesProvider.cs
  26. 3 58
      MediaBrowser.Server.Implementations/Channels/ChannelManager.cs
  27. 2 10
      MediaBrowser.Server.Implementations/Configuration/ServerConfigurationManager.cs
  28. 3 2
      MediaBrowser.Server.Implementations/Connect/ConnectEntryPoint.cs
  29. 5 8
      MediaBrowser.Server.Implementations/Devices/DeviceRepository.cs
  30. 3 2
      MediaBrowser.Server.Implementations/EntryPoints/ExternalPortForwarding.cs
  31. 3 2
      MediaBrowser.Server.Implementations/EntryPoints/LoadRegistrations.cs
  32. 5 11
      MediaBrowser.Server.Implementations/EntryPoints/UsageEntryPoint.cs
  33. 119 114
      MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs
  34. 31 3
      MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs
  35. 19 0
      MediaBrowser.Server.Implementations/HttpServer/LoggerUtils.cs
  36. 2 40
      MediaBrowser.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpListener.cs
  37. 9 3
      MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs
  38. 2 2
      MediaBrowser.Server.Implementations/Library/LibraryManager.cs
  39. 11 2
      MediaBrowser.Server.Implementations/Library/Resolvers/PhotoResolver.cs
  40. 8 1
      MediaBrowser.Server.Implementations/Library/UserViewManager.cs
  41. 80 20
      MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
  42. 1 1
      MediaBrowser.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs
  43. 34 3
      MediaBrowser.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs
  44. 1 0
      MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs
  45. 11 11
      MediaBrowser.Server.Implementations/Localization/Core/ca.json
  46. 2 2
      MediaBrowser.Server.Implementations/Localization/Core/nl.json
  47. 2 2
      MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj
  48. 3 3
      MediaBrowser.Server.Implementations/News/NewsEntryPoint.cs
  49. 1 1
      MediaBrowser.Server.Implementations/packages.config
  50. 14 0
      MediaBrowser.Server.Mono/Native/BaseMonoApp.cs
  51. 23 17
      MediaBrowser.Server.Startup.Common/ApplicationHost.cs
  52. 3 3
      MediaBrowser.Server.Startup.Common/EntryPoints/KeepServerAwake.cs
  53. 7 0
      MediaBrowser.Server.Startup.Common/INativeApp.cs
  54. 1 1
      MediaBrowser.ServerApplication/MainStartup.cs
  55. 1 0
      MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj
  56. 9 1
      MediaBrowser.ServerApplication/Native/WindowsApp.cs
  57. 94 0
      MediaBrowser.ServerApplication/Native/WindowsPowerManagement.cs
  58. 1 2
      MediaBrowser.WebDashboard/Api/DashboardService.cs
  59. 0 6
      MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj
  60. 11 4
      MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs
  61. 4 2
      MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs

+ 6 - 2
MediaBrowser.Api/EnvironmentService.cs

@@ -1,5 +1,4 @@
-using MediaBrowser.Common.IO;
-using MediaBrowser.Common.Net;
+using MediaBrowser.Common.Net;
 using MediaBrowser.Controller.Net;
 using MediaBrowser.Controller.Net;
 using MediaBrowser.Model.IO;
 using MediaBrowser.Model.IO;
 using MediaBrowser.Model.Net;
 using MediaBrowser.Model.Net;
@@ -46,6 +45,11 @@ namespace MediaBrowser.Api
         /// <value><c>true</c> if [include hidden]; otherwise, <c>false</c>.</value>
         /// <value><c>true</c> if [include hidden]; otherwise, <c>false</c>.</value>
         [ApiMember(Name = "IncludeHidden", Description = "An optional filter to include or exclude hidden files and folders. true/false", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
         [ApiMember(Name = "IncludeHidden", Description = "An optional filter to include or exclude hidden files and folders. true/false", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
         public bool IncludeHidden { get; set; }
         public bool IncludeHidden { get; set; }
+
+        public GetDirectoryContents()
+        {
+            IncludeHidden = true;
+        }
     }
     }
 
 
     [Route("/Environment/NetworkShares", "GET", Summary = "Gets shares from a network device")]
     [Route("/Environment/NetworkShares", "GET", Summary = "Gets shares from a network device")]

+ 1 - 1
MediaBrowser.Api/Playback/BaseStreamingService.cs

@@ -2192,7 +2192,7 @@ namespace MediaBrowser.Api.Playback
             {
             {
                 if (string.Equals(state.OutputContainer, "mkv", StringComparison.OrdinalIgnoreCase))
                 if (string.Equals(state.OutputContainer, "mkv", StringComparison.OrdinalIgnoreCase))
                 {
                 {
-                    inputModifier += " -noaccurate_seek";
+                    //inputModifier += " -noaccurate_seek";
                 }
                 }
             }
             }
             
             

+ 3 - 3
MediaBrowser.Api/Playback/MediaInfoService.cs

@@ -141,10 +141,10 @@ namespace MediaBrowser.Api.Playback
 
 
             var profile = request.DeviceProfile;
             var profile = request.DeviceProfile;
 
 
-            var caps = _deviceManager.GetCapabilities(authInfo.DeviceId);
-            if (caps != null)
+            if (profile == null)
             {
             {
-                if (profile == null)
+                var caps = _deviceManager.GetCapabilities(authInfo.DeviceId);
+                if (caps != null)
                 {
                 {
                     profile = caps.DeviceProfile;
                     profile = caps.DeviceProfile;
                 }
                 }

+ 1 - 2
MediaBrowser.Api/StartupWizardService.cs

@@ -66,13 +66,12 @@ namespace MediaBrowser.Api
         {
         {
             _config.Configuration.IsStartupWizardCompleted = true;
             _config.Configuration.IsStartupWizardCompleted = true;
             _config.Configuration.EnableLocalizedGuids = true;
             _config.Configuration.EnableLocalizedGuids = true;
-            _config.Configuration.MergeMetadataAndImagesByName = true;
-            _config.Configuration.EnableStandaloneMetadata = true;
             _config.Configuration.EnableLibraryMetadataSubFolder = true;
             _config.Configuration.EnableLibraryMetadataSubFolder = true;
             _config.Configuration.EnableCustomPathSubFolders = true;
             _config.Configuration.EnableCustomPathSubFolders = true;
             _config.Configuration.DisableStartupScan = true;
             _config.Configuration.DisableStartupScan = true;
             _config.Configuration.EnableUserViews = true;
             _config.Configuration.EnableUserViews = true;
             _config.Configuration.EnableDateLastRefresh = true;
             _config.Configuration.EnableDateLastRefresh = true;
+            _config.Configuration.MergeMetadataAndImagesByName = true;
             _config.SaveConfiguration();
             _config.SaveConfiguration();
         }
         }
 
 

+ 2 - 0
MediaBrowser.Api/UserLibrary/PlaystateService.cs

@@ -370,6 +370,8 @@ namespace MediaBrowser.Api.UserLibrary
 
 
         public void Post(ReportPlaybackStopped request)
         public void Post(ReportPlaybackStopped request)
         {
         {
+            Logger.Debug("ReportPlaybackStopped PlaySessionId: {0}", request.PlaySessionId ?? string.Empty);
+
             if (!string.IsNullOrWhiteSpace(request.PlaySessionId))
             if (!string.IsNullOrWhiteSpace(request.PlaySessionId))
             {
             {
                 ApiEntryPoint.Instance.KillTranscodingJobs(AuthorizationContext.GetAuthorizationInfo(Request).DeviceId, request.PlaySessionId, s => true);
                 ApiEntryPoint.Instance.KillTranscodingJobs(AuthorizationContext.GetAuthorizationInfo(Request).DeviceId, request.PlaySessionId, s => true);

+ 9 - 15
MediaBrowser.Common.Implementations/Networking/BaseNetworkManager.cs

@@ -6,7 +6,6 @@ using System.Linq;
 using System.Net;
 using System.Net;
 using System.Net.NetworkInformation;
 using System.Net.NetworkInformation;
 using System.Net.Sockets;
 using System.Net.Sockets;
-using System.Threading;
 using MoreLinq;
 using MoreLinq;
 
 
 namespace MediaBrowser.Common.Implementations.Networking
 namespace MediaBrowser.Common.Implementations.Networking
@@ -14,22 +13,11 @@ namespace MediaBrowser.Common.Implementations.Networking
     public abstract class BaseNetworkManager
     public abstract class BaseNetworkManager
     {
     {
         protected ILogger Logger { get; private set; }
         protected ILogger Logger { get; private set; }
-        private Timer _clearCacheTimer;
+        private DateTime _lastRefresh;
 
 
         protected BaseNetworkManager(ILogger logger)
         protected BaseNetworkManager(ILogger logger)
         {
         {
             Logger = logger;
             Logger = logger;
-
-            // Can't use network change events due to a crash in Linux
-            _clearCacheTimer = new Timer(ClearCacheTimerCallback, null, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1));
-        }
-
-        private void ClearCacheTimerCallback(object state)
-        {
-            lock (_localIpAddressSyncLock)
-            {
-                _localIpAddresses = null;
-            }
         }
         }
 
 
 		private volatile List<IPAddress> _localIpAddresses;
 		private volatile List<IPAddress> _localIpAddresses;
@@ -41,15 +29,21 @@ namespace MediaBrowser.Common.Implementations.Networking
         /// <returns>IPAddress.</returns>
         /// <returns>IPAddress.</returns>
 		public IEnumerable<IPAddress> GetLocalIpAddresses()
 		public IEnumerable<IPAddress> GetLocalIpAddresses()
         {
         {
-            if (_localIpAddresses == null)
+            const int cacheMinutes = 3;
+            var forceRefresh = (DateTime.UtcNow - _lastRefresh).TotalMinutes >= cacheMinutes;
+
+            if (_localIpAddresses == null || forceRefresh)
             {
             {
                 lock (_localIpAddressSyncLock)
                 lock (_localIpAddressSyncLock)
                 {
                 {
-                    if (_localIpAddresses == null)
+                    forceRefresh = (DateTime.UtcNow - _lastRefresh).TotalMinutes >= cacheMinutes;
+
+                    if (_localIpAddresses == null || forceRefresh)
                     {
                     {
                         var addresses = GetLocalIpAddressesInternal().ToList();
                         var addresses = GetLocalIpAddressesInternal().ToList();
 
 
                         _localIpAddresses = addresses;
                         _localIpAddresses = addresses;
+                        _lastRefresh = DateTime.UtcNow;
 
 
                         return addresses;
                         return addresses;
                     }
                     }

+ 4 - 4
MediaBrowser.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs

@@ -233,7 +233,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
         /// <summary>
         /// <summary>
         /// The _triggers
         /// The _triggers
         /// </summary>
         /// </summary>
-        private IEnumerable<ITaskTrigger> _triggers;
+        private volatile List<ITaskTrigger> _triggers;
         /// <summary>
         /// <summary>
         /// The _triggers sync lock
         /// The _triggers sync lock
         /// </summary>
         /// </summary>
@@ -532,7 +532,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
         /// Loads the triggers.
         /// Loads the triggers.
         /// </summary>
         /// </summary>
         /// <returns>IEnumerable{BaseTaskTrigger}.</returns>
         /// <returns>IEnumerable{BaseTaskTrigger}.</returns>
-        private IEnumerable<ITaskTrigger> LoadTriggers()
+        private List<ITaskTrigger> LoadTriggers()
         {
         {
             try
             try
             {
             {
@@ -543,12 +543,12 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
             catch (FileNotFoundException)
             catch (FileNotFoundException)
             {
             {
                 // File doesn't exist. No biggie. Return defaults.
                 // File doesn't exist. No biggie. Return defaults.
-                return ScheduledTask.GetDefaultTriggers();
+                return ScheduledTask.GetDefaultTriggers().ToList();
             }
             }
             catch (DirectoryNotFoundException)
             catch (DirectoryNotFoundException)
             {
             {
                 // File doesn't exist. No biggie. Return defaults.
                 // File doesn't exist. No biggie. Return defaults.
-                return ScheduledTask.GetDefaultTriggers();
+                return ScheduledTask.GetDefaultTriggers().ToList();
             }
             }
         }
         }
 
 

+ 1 - 0
MediaBrowser.Common/MediaBrowser.Common.csproj

@@ -90,6 +90,7 @@
     <Compile Include="Security\IRequiresRegistration.cs" />
     <Compile Include="Security\IRequiresRegistration.cs" />
     <Compile Include="Security\ISecurityManager.cs" />
     <Compile Include="Security\ISecurityManager.cs" />
     <Compile Include="Security\PaymentRequiredException.cs" />
     <Compile Include="Security\PaymentRequiredException.cs" />
+    <Compile Include="Threading\PeriodicTimer.cs" />
     <Compile Include="Updates\IInstallationManager.cs" />
     <Compile Include="Updates\IInstallationManager.cs" />
     <Compile Include="Updates\InstallationEventArgs.cs" />
     <Compile Include="Updates\InstallationEventArgs.cs" />
     <Compile Include="Updates\InstallationFailedEventArgs.cs" />
     <Compile Include="Updates\InstallationFailedEventArgs.cs" />

+ 72 - 0
MediaBrowser.Common/Threading/PeriodicTimer.cs

@@ -0,0 +1,72 @@
+using System;
+using System.Threading;
+using Microsoft.Win32;
+
+namespace MediaBrowser.Common.Threading
+{
+    public class PeriodicTimer : IDisposable
+    {
+        public Action<object> Callback { get; set; }
+        private Timer _timer;
+        private readonly object _state;
+        private readonly object _timerLock = new object();
+        private readonly TimeSpan _period;
+
+        public PeriodicTimer(Action<object> callback, object state, TimeSpan dueTime, TimeSpan period)
+        {
+            if (callback == null)
+            {
+                throw new ArgumentNullException("callback");
+            }
+
+            Callback = callback;
+            _period = period;
+            _state = state;
+
+            StartTimer(dueTime);
+        }
+
+        void SystemEvents_PowerModeChanged(object sender, PowerModeChangedEventArgs e)
+        {
+            if (e.Mode == PowerModes.Resume)
+            {
+                DisposeTimer();
+                StartTimer(Timeout.InfiniteTimeSpan);
+            }
+        }
+
+        private void TimerCallback(object state)
+        {
+            Callback(state);
+        }
+
+        private void StartTimer(TimeSpan dueTime)
+        {
+            lock (_timerLock)
+            {
+                _timer = new Timer(TimerCallback, _state, dueTime, _period);
+
+                SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged;
+            }
+        }
+
+        private void DisposeTimer()
+        {
+            SystemEvents.PowerModeChanged -= SystemEvents_PowerModeChanged;
+            
+            lock (_timerLock)
+            {
+                if (_timer != null)
+                {
+                    _timer.Dispose();
+                    _timer = null;
+                }
+            }
+        }
+
+        public void Dispose()
+        {
+            DisposeTimer();
+        }
+    }
+}

+ 59 - 2
MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs

@@ -1,17 +1,21 @@
-using MediaBrowser.Controller.Providers;
+using System;
+using MediaBrowser.Controller.Providers;
 using MediaBrowser.Model.Configuration;
 using MediaBrowser.Model.Configuration;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Users;
 using MediaBrowser.Model.Users;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
 using System.Runtime.Serialization;
 using System.Runtime.Serialization;
+using System.Threading;
+using System.Threading.Tasks;
+using MediaBrowser.Controller.Library;
 
 
 namespace MediaBrowser.Controller.Entities.Audio
 namespace MediaBrowser.Controller.Entities.Audio
 {
 {
     /// <summary>
     /// <summary>
     /// Class MusicAlbum
     /// Class MusicAlbum
     /// </summary>
     /// </summary>
-    public class MusicAlbum : Folder, IHasAlbumArtist, IHasArtist, IHasMusicGenres, IHasLookupInfo<AlbumInfo>
+    public class MusicAlbum : Folder, IHasAlbumArtist, IHasArtist, IHasMusicGenres, IHasLookupInfo<AlbumInfo>, IMetadataContainer
     {
     {
         public MusicAlbum()
         public MusicAlbum()
         {
         {
@@ -139,5 +143,58 @@ namespace MediaBrowser.Controller.Entities.Audio
 
 
             return id;
             return id;
         }
         }
+
+        public async Task RefreshAllMetadata(MetadataRefreshOptions refreshOptions, IProgress<double> progress, CancellationToken cancellationToken)
+        {
+            var items = GetRecursiveChildren().ToList();
+
+            var songs = items.OfType<Audio>().ToList();
+
+            var others = items.Except(songs).ToList();
+
+            var totalItems = songs.Count + others.Count;
+            var numComplete = 0;
+
+            var childUpdateType = ItemUpdateType.None;
+
+            // Refresh songs
+            foreach (var item in songs)
+            {
+                cancellationToken.ThrowIfCancellationRequested();
+
+                var updateType = await item.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);
+                childUpdateType = childUpdateType | updateType;
+
+                numComplete++;
+                double percent = numComplete;
+                percent /= totalItems;
+                progress.Report(percent * 100);
+            }
+
+            var parentRefreshOptions = refreshOptions;
+            if (childUpdateType > ItemUpdateType.None)
+            {
+                parentRefreshOptions = new MetadataRefreshOptions(refreshOptions);
+                parentRefreshOptions.MetadataRefreshMode = MetadataRefreshMode.FullRefresh;
+            }
+
+            // Refresh current item
+            await RefreshMetadata(parentRefreshOptions, cancellationToken).ConfigureAwait(false);
+
+            // Refresh all non-songs
+            foreach (var item in others)
+            {
+                cancellationToken.ThrowIfCancellationRequested();
+
+                var updateType = await item.RefreshMetadata(parentRefreshOptions, cancellationToken).ConfigureAwait(false);
+
+                numComplete++;
+                double percent = numComplete;
+                percent /= totalItems;
+                progress.Report(percent * 100);
+            }
+
+            progress.Report(100);
+        }
     }
     }
 }
 }

+ 2 - 2
MediaBrowser.Controller/Entities/User.cs

@@ -109,7 +109,7 @@ namespace MediaBrowser.Controller.Entities
         /// <value>The last activity date.</value>
         /// <value>The last activity date.</value>
         public DateTime? LastActivityDate { get; set; }
         public DateTime? LastActivityDate { get; set; }
 
 
-        private UserConfiguration _config;
+        private volatile UserConfiguration _config;
         private readonly object _configSyncLock = new object();
         private readonly object _configSyncLock = new object();
         [IgnoreDataMember]
         [IgnoreDataMember]
         public UserConfiguration Configuration
         public UserConfiguration Configuration
@@ -132,7 +132,7 @@ namespace MediaBrowser.Controller.Entities
             set { _config = value; }
             set { _config = value; }
         }
         }
 
 
-        private UserPolicy _policy;
+        private volatile UserPolicy _policy;
         private readonly object _policySyncLock = new object();
         private readonly object _policySyncLock = new object();
         [IgnoreDataMember]
         [IgnoreDataMember]
         public UserPolicy Policy
         public UserPolicy Policy

+ 1 - 0
MediaBrowser.Controller/MediaBrowser.Controller.csproj

@@ -265,6 +265,7 @@
     <Compile Include="Playlists\IPlaylistManager.cs" />
     <Compile Include="Playlists\IPlaylistManager.cs" />
     <Compile Include="Playlists\Playlist.cs" />
     <Compile Include="Playlists\Playlist.cs" />
     <Compile Include="Plugins\ILocalizablePlugin.cs" />
     <Compile Include="Plugins\ILocalizablePlugin.cs" />
+    <Compile Include="Power\IPowerManagement.cs" />
     <Compile Include="Providers\AlbumInfo.cs" />
     <Compile Include="Providers\AlbumInfo.cs" />
     <Compile Include="Providers\ArtistInfo.cs" />
     <Compile Include="Providers\ArtistInfo.cs" />
     <Compile Include="Providers\BookInfo.cs" />
     <Compile Include="Providers\BookInfo.cs" />

+ 13 - 0
MediaBrowser.Controller/Power/IPowerManagement.cs

@@ -0,0 +1,13 @@
+using System;
+
+namespace MediaBrowser.Controller.Power
+{
+    public interface IPowerManagement
+    {
+        /// <summary>
+        /// Schedules the wake.
+        /// </summary>
+        /// <param name="utcTime">The UTC time.</param>
+        void ScheduleWake(DateTime utcTime);
+    }
+}

+ 42 - 37
MediaBrowser.Dlna/PlayTo/Device.cs

@@ -22,14 +22,26 @@ namespace MediaBrowser.Dlna.PlayTo
         #region Fields & Properties
         #region Fields & Properties
 
 
         private Timer _timer;
         private Timer _timer;
-        private Timer _volumeTimer;
 
 
         public DeviceInfo Properties { get; set; }
         public DeviceInfo Properties { get; set; }
 
 
         private int _muteVol;
         private int _muteVol;
         public bool IsMuted { get; set; }
         public bool IsMuted { get; set; }
 
 
-        public int Volume { get; set; }
+        private int _volume;
+
+        public int Volume
+        {
+            get
+            {
+                RefreshVolumeIfNeeded();
+                return _volume;
+            }
+            set
+            {
+                _volume = value;
+            }
+        }
 
 
         public TimeSpan? Duration { get; set; }
         public TimeSpan? Duration { get; set; }
 
 
@@ -93,11 +105,6 @@ namespace MediaBrowser.Dlna.PlayTo
             return 1000;
             return 1000;
         }
         }
 
 
-        private int GetVolumeTimerIntervalMs()
-        {
-            return 5000;
-        }
-
         private int GetInactiveTimerIntervalMs()
         private int GetInactiveTimerIntervalMs()
         {
         {
             return 20000;
             return 20000;
@@ -107,11 +114,37 @@ namespace MediaBrowser.Dlna.PlayTo
         {
         {
             _timer = new Timer(TimerCallback, null, GetPlaybackTimerIntervalMs(), GetInactiveTimerIntervalMs());
             _timer = new Timer(TimerCallback, null, GetPlaybackTimerIntervalMs(), GetInactiveTimerIntervalMs());
 
 
-            _volumeTimer = new Timer(VolumeTimerCallback, null, Timeout.Infinite, Timeout.Infinite);
-
             _timerActive = false;
             _timerActive = false;
         }
         }
 
 
+        private DateTime _lastVolumeRefresh;
+        private void RefreshVolumeIfNeeded()
+        {
+            if (!_timerActive)
+            {
+                return;
+            }
+
+            if (DateTime.UtcNow >= _lastVolumeRefresh.AddSeconds(5))
+            {
+                _lastVolumeRefresh = DateTime.UtcNow;
+                RefreshVolume();
+            }
+        }
+
+        private async void RefreshVolume()
+        {
+            try
+            {
+                await GetVolume().ConfigureAwait(false);
+                await GetMute().ConfigureAwait(false);
+            }
+            catch (Exception ex)
+            {
+                _logger.ErrorException("Error updating device volume info for {0}", ex, Properties.Name);
+            }
+        }
+
         private readonly object _timerLock = new object();
         private readonly object _timerLock = new object();
         private bool _timerActive;
         private bool _timerActive;
         private void RestartTimer()
         private void RestartTimer()
@@ -124,7 +157,6 @@ namespace MediaBrowser.Dlna.PlayTo
                     {
                     {
                         _logger.Debug("RestartTimer");
                         _logger.Debug("RestartTimer");
                         _timer.Change(10, GetPlaybackTimerIntervalMs());
                         _timer.Change(10, GetPlaybackTimerIntervalMs());
-                        _volumeTimer.Change(100, GetVolumeTimerIntervalMs());
                     }
                     }
 
 
                     _timerActive = true;
                     _timerActive = true;
@@ -150,10 +182,6 @@ namespace MediaBrowser.Dlna.PlayTo
                         {
                         {
                             _timer.Change(interval, interval);
                             _timer.Change(interval, interval);
                         }
                         }
-                        if (_volumeTimer != null)
-                        {
-                            _volumeTimer.Change(Timeout.Infinite, Timeout.Infinite);
-                        }
                     }
                     }
 
 
                     _timerActive = false;
                     _timerActive = false;
@@ -440,19 +468,6 @@ namespace MediaBrowser.Dlna.PlayTo
             }
             }
         }
         }
 
 
-        private async void VolumeTimerCallback(object sender)
-        {
-            try
-            {
-                await GetVolume().ConfigureAwait(false);
-                await GetMute().ConfigureAwait(false);
-            }
-            catch (Exception ex)
-            {
-                _logger.ErrorException("Error updating device volume info for {0}", ex, Properties.Name);
-            }
-        }
-
         private async Task GetVolume()
         private async Task GetVolume()
         {
         {
             var command = RendererCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetVolume");
             var command = RendererCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetVolume");
@@ -1012,7 +1027,6 @@ namespace MediaBrowser.Dlna.PlayTo
                 _disposed = true;
                 _disposed = true;
 
 
                 DisposeTimer();
                 DisposeTimer();
-                DisposeVolumeTimer();
             }
             }
         }
         }
 
 
@@ -1025,15 +1039,6 @@ namespace MediaBrowser.Dlna.PlayTo
             }
             }
         }
         }
 
 
-        private void DisposeVolumeTimer()
-        {
-            if (_volumeTimer != null)
-            {
-                _volumeTimer.Dispose();
-                _volumeTimer = null;
-            }
-        }
-
         #endregion
         #endregion
 
 
         public override string ToString()
         public override string ToString()

+ 18 - 31
MediaBrowser.Dlna/PlayTo/PlayToController.cs

@@ -37,11 +37,28 @@ namespace MediaBrowser.Dlna.PlayTo
         private readonly IDeviceDiscovery _deviceDiscovery;
         private readonly IDeviceDiscovery _deviceDiscovery;
         private readonly string _serverAddress;
         private readonly string _serverAddress;
         private readonly string _accessToken;
         private readonly string _accessToken;
+        private readonly DateTime _creationTime;
 
 
         public bool IsSessionActive
         public bool IsSessionActive
         {
         {
             get
             get
             {
             {
+                var lastDateKnownActivity = new[] { _creationTime, _device.DateLastActivity }.Max();
+
+                if (DateTime.UtcNow >= lastDateKnownActivity.AddSeconds(120))
+                {
+                    try
+                    {
+                        // Session is inactive, mark it for Disposal and don't start the elapsed timer.
+                        _sessionManager.ReportSessionEnded(_session.Id);
+                    }
+                    catch (Exception ex)
+                    {
+                        _logger.ErrorException("Error in ReportSessionEnded", ex);
+                    }
+                    return false;
+                }
+
                 return _device != null;
                 return _device != null;
             }
             }
         }
         }
@@ -55,8 +72,6 @@ namespace MediaBrowser.Dlna.PlayTo
             get { return IsSessionActive; }
             get { return IsSessionActive; }
         }
         }
 
 
-        private Timer _updateTimer;
-
         public PlayToController(SessionInfo session, ISessionManager sessionManager, ILibraryManager libraryManager, ILogger logger, IDlnaManager dlnaManager, IUserManager userManager, IImageProcessor imageProcessor, string serverAddress, string accessToken, IDeviceDiscovery deviceDiscovery, IUserDataManager userDataManager, ILocalizationManager localization, IMediaSourceManager mediaSourceManager)
         public PlayToController(SessionInfo session, ISessionManager sessionManager, ILibraryManager libraryManager, ILogger logger, IDlnaManager dlnaManager, IUserManager userManager, IImageProcessor imageProcessor, string serverAddress, string accessToken, IDeviceDiscovery deviceDiscovery, IUserDataManager userDataManager, ILocalizationManager localization, IMediaSourceManager mediaSourceManager)
         {
         {
             _session = session;
             _session = session;
@@ -72,6 +87,7 @@ namespace MediaBrowser.Dlna.PlayTo
             _mediaSourceManager = mediaSourceManager;
             _mediaSourceManager = mediaSourceManager;
             _accessToken = accessToken;
             _accessToken = accessToken;
             _logger = logger;
             _logger = logger;
+            _creationTime = DateTime.UtcNow;
         }
         }
 
 
         public void Init(Device device)
         public void Init(Device device)
@@ -84,8 +100,6 @@ namespace MediaBrowser.Dlna.PlayTo
             _device.Start();
             _device.Start();
 
 
             _deviceDiscovery.DeviceLeft += _deviceDiscovery_DeviceLeft;
             _deviceDiscovery.DeviceLeft += _deviceDiscovery_DeviceLeft;
-
-            _updateTimer = new Timer(updateTimer_Elapsed, null, 60000, 60000);
         }
         }
 
 
         void _deviceDiscovery_DeviceLeft(object sender, SsdpMessageEventArgs e)
         void _deviceDiscovery_DeviceLeft(object sender, SsdpMessageEventArgs e)
@@ -117,22 +131,6 @@ namespace MediaBrowser.Dlna.PlayTo
             }
             }
         }
         }
 
 
-        private void updateTimer_Elapsed(object state)
-        {
-            if (DateTime.UtcNow >= _device.DateLastActivity.AddSeconds(120))
-            {
-                try
-                {
-                    // Session is inactive, mark it for Disposal and don't start the elapsed timer.
-                    _sessionManager.ReportSessionEnded(_session.Id);
-                }
-                catch (Exception ex)
-                {
-                    _logger.ErrorException("Error in ReportSessionEnded", ex);
-                }
-            }
-        }
-
         async void _device_MediaChanged(object sender, MediaChangedEventArgs e)
         async void _device_MediaChanged(object sender, MediaChangedEventArgs e)
         {
         {
             try
             try
@@ -634,21 +632,10 @@ namespace MediaBrowser.Dlna.PlayTo
                 _device.MediaChanged -= _device_MediaChanged;
                 _device.MediaChanged -= _device_MediaChanged;
                 _deviceDiscovery.DeviceLeft -= _deviceDiscovery_DeviceLeft;
                 _deviceDiscovery.DeviceLeft -= _deviceDiscovery_DeviceLeft;
 
 
-                DisposeUpdateTimer();
-
                 _device.Dispose();
                 _device.Dispose();
             }
             }
         }
         }
 
 
-        private void DisposeUpdateTimer()
-        {
-            if (_updateTimer != null)
-            {
-                _updateTimer.Dispose();
-                _updateTimer = null;
-            }
-        }
-
         private readonly CultureInfo _usCulture = new CultureInfo("en-US");
         private readonly CultureInfo _usCulture = new CultureInfo("en-US");
 
 
         public Task SendGeneralCommand(GeneralCommand command, CancellationToken cancellationToken)
         public Task SendGeneralCommand(GeneralCommand command, CancellationToken cancellationToken)

+ 7 - 17
MediaBrowser.Dlna/PlayTo/PlayToManager.cs

@@ -36,7 +36,7 @@ namespace MediaBrowser.Dlna.PlayTo
         private readonly IMediaSourceManager _mediaSourceManager;
         private readonly IMediaSourceManager _mediaSourceManager;
 
 
         private readonly List<string> _nonRendererUrls = new List<string>();
         private readonly List<string> _nonRendererUrls = new List<string>();
-        private Timer _clearNonRenderersTimer;
+        private DateTime _lastRendererClear;
 
 
         public PlayToManager(ILogger logger, ISessionManager sessionManager, ILibraryManager libraryManager, IUserManager userManager, IDlnaManager dlnaManager, IServerApplicationHost appHost, IImageProcessor imageProcessor, IDeviceDiscovery deviceDiscovery, IHttpClient httpClient, IServerConfigurationManager config, IUserDataManager userDataManager, ILocalizationManager localization, IMediaSourceManager mediaSourceManager)
         public PlayToManager(ILogger logger, ISessionManager sessionManager, ILibraryManager libraryManager, IUserManager userManager, IDlnaManager dlnaManager, IServerApplicationHost appHost, IImageProcessor imageProcessor, IDeviceDiscovery deviceDiscovery, IHttpClient httpClient, IServerConfigurationManager config, IUserDataManager userDataManager, ILocalizationManager localization, IMediaSourceManager mediaSourceManager)
         {
         {
@@ -57,19 +57,9 @@ namespace MediaBrowser.Dlna.PlayTo
 
 
         public void Start()
         public void Start()
         {
         {
-            _clearNonRenderersTimer = new Timer(OnClearUrlTimerCallback, null, TimeSpan.FromMinutes(10), TimeSpan.FromMinutes(10));
-
             _deviceDiscovery.DeviceDiscovered += _deviceDiscovery_DeviceDiscovered;
             _deviceDiscovery.DeviceDiscovered += _deviceDiscovery_DeviceDiscovered;
         }
         }
 
 
-        private void OnClearUrlTimerCallback(object state)
-        {
-            lock (_nonRendererUrls)
-            {
-                _nonRendererUrls.Clear();
-            }
-        }
-
         async void _deviceDiscovery_DeviceDiscovered(object sender, SsdpMessageEventArgs e)
         async void _deviceDiscovery_DeviceDiscovered(object sender, SsdpMessageEventArgs e)
         {
         {
             string usn;
             string usn;
@@ -99,6 +89,12 @@ namespace MediaBrowser.Dlna.PlayTo
 
 
                 lock (_nonRendererUrls)
                 lock (_nonRendererUrls)
                 {
                 {
+                    if ((DateTime.UtcNow - _lastRendererClear).TotalMinutes >= 10)
+                    {
+                        _nonRendererUrls.Clear();
+                        _lastRendererClear = DateTime.UtcNow;
+                    }
+
                     if (_nonRendererUrls.Contains(location, StringComparer.OrdinalIgnoreCase))
                     if (_nonRendererUrls.Contains(location, StringComparer.OrdinalIgnoreCase))
                     {
                     {
                         return;
                         return;
@@ -181,12 +177,6 @@ namespace MediaBrowser.Dlna.PlayTo
         public void Dispose()
         public void Dispose()
         {
         {
             _deviceDiscovery.DeviceDiscovered -= _deviceDiscovery_DeviceDiscovered;
             _deviceDiscovery.DeviceDiscovered -= _deviceDiscovery_DeviceDiscovered;
-
-            if (_clearNonRenderersTimer != null)
-            {
-                _clearNonRenderersTimer.Dispose();
-                _clearNonRenderersTimer = null;
-            }
         }
         }
     }
     }
 }
 }

+ 5 - 5
MediaBrowser.Dlna/Profiles/WdtvLiveProfile.cs

@@ -58,7 +58,7 @@ namespace MediaBrowser.Dlna.Profiles
                     Container = "avi",
                     Container = "avi",
                     Type = DlnaProfileType.Video,
                     Type = DlnaProfileType.Video,
                     VideoCodec = "mpeg1video,mpeg2video,mpeg4,h264,vc1",
                     VideoCodec = "mpeg1video,mpeg2video,mpeg4,h264,vc1",
-                    AudioCodec = "ac3,dca,mp2,mp3,pcm"
+                    AudioCodec = "ac3,dca,mp2,mp3,pcm,dca"
                 },
                 },
 
 
                 new DirectPlayProfile
                 new DirectPlayProfile
@@ -66,7 +66,7 @@ namespace MediaBrowser.Dlna.Profiles
                     Container = "mpeg",
                     Container = "mpeg",
                     Type = DlnaProfileType.Video,
                     Type = DlnaProfileType.Video,
                     VideoCodec = "mpeg1video,mpeg2video",
                     VideoCodec = "mpeg1video,mpeg2video",
-                    AudioCodec = "ac3,dca,mp2,mp3,pcm"
+                    AudioCodec = "ac3,dca,mp2,mp3,pcm,dca"
                 },
                 },
 
 
                 new DirectPlayProfile
                 new DirectPlayProfile
@@ -74,7 +74,7 @@ namespace MediaBrowser.Dlna.Profiles
                     Container = "mkv",
                     Container = "mkv",
                     Type = DlnaProfileType.Video,
                     Type = DlnaProfileType.Video,
                     VideoCodec = "mpeg1video,mpeg2video,mpeg4,h264,vc1",
                     VideoCodec = "mpeg1video,mpeg2video,mpeg4,h264,vc1",
-                    AudioCodec = "ac3,dca,aac,mp2,mp3,pcm"
+                    AudioCodec = "ac3,dca,aac,mp2,mp3,pcm,dca"
                 },
                 },
 
 
                 new DirectPlayProfile
                 new DirectPlayProfile
@@ -82,7 +82,7 @@ namespace MediaBrowser.Dlna.Profiles
                     Container = "ts",
                     Container = "ts",
                     Type = DlnaProfileType.Video,
                     Type = DlnaProfileType.Video,
                     VideoCodec = "mpeg1video,mpeg2video,h264,vc1",
                     VideoCodec = "mpeg1video,mpeg2video,h264,vc1",
-                    AudioCodec = "ac3,dca,mp2,mp3,aac"
+                    AudioCodec = "ac3,dca,mp2,mp3,aac,dca"
                 },
                 },
 
 
                 new DirectPlayProfile
                 new DirectPlayProfile
@@ -90,7 +90,7 @@ namespace MediaBrowser.Dlna.Profiles
                     Container = "mp4,mov",
                     Container = "mp4,mov",
                     Type = DlnaProfileType.Video,
                     Type = DlnaProfileType.Video,
                     VideoCodec = "h264,mpeg4",
                     VideoCodec = "h264,mpeg4",
-                    AudioCodec = "ac3,aac,mp2,mp3"
+                    AudioCodec = "ac3,aac,mp2,mp3,dca"
                 },
                 },
 
 
                 new DirectPlayProfile
                 new DirectPlayProfile

+ 5 - 5
MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml

@@ -37,11 +37,11 @@
   <IgnoreTranscodeByteRangeRequests>true</IgnoreTranscodeByteRangeRequests>
   <IgnoreTranscodeByteRangeRequests>true</IgnoreTranscodeByteRangeRequests>
   <XmlRootAttributes />
   <XmlRootAttributes />
   <DirectPlayProfiles>
   <DirectPlayProfiles>
-    <DirectPlayProfile container="avi" audioCodec="ac3,dca,mp2,mp3,pcm" videoCodec="mpeg1video,mpeg2video,mpeg4,h264,vc1" type="Video" />
-    <DirectPlayProfile container="mpeg" audioCodec="ac3,dca,mp2,mp3,pcm" videoCodec="mpeg1video,mpeg2video" type="Video" />
-    <DirectPlayProfile container="mkv" audioCodec="ac3,dca,aac,mp2,mp3,pcm" videoCodec="mpeg1video,mpeg2video,mpeg4,h264,vc1" type="Video" />
-    <DirectPlayProfile container="ts" audioCodec="ac3,dca,mp2,mp3,aac" videoCodec="mpeg1video,mpeg2video,h264,vc1" type="Video" />
-    <DirectPlayProfile container="mp4,mov" audioCodec="ac3,aac,mp2,mp3" videoCodec="h264,mpeg4" type="Video" />
+    <DirectPlayProfile container="avi" audioCodec="ac3,dca,mp2,mp3,pcm,dca" videoCodec="mpeg1video,mpeg2video,mpeg4,h264,vc1" type="Video" />
+    <DirectPlayProfile container="mpeg" audioCodec="ac3,dca,mp2,mp3,pcm,dca" videoCodec="mpeg1video,mpeg2video" type="Video" />
+    <DirectPlayProfile container="mkv" audioCodec="ac3,dca,aac,mp2,mp3,pcm,dca" videoCodec="mpeg1video,mpeg2video,mpeg4,h264,vc1" type="Video" />
+    <DirectPlayProfile container="ts" audioCodec="ac3,dca,mp2,mp3,aac,dca" videoCodec="mpeg1video,mpeg2video,h264,vc1" type="Video" />
+    <DirectPlayProfile container="mp4,mov" audioCodec="ac3,aac,mp2,mp3,dca" videoCodec="h264,mpeg4" type="Video" />
     <DirectPlayProfile container="asf" audioCodec="wmav2,wmapro" videoCodec="vc1" type="Video" />
     <DirectPlayProfile container="asf" audioCodec="wmav2,wmapro" videoCodec="vc1" type="Video" />
     <DirectPlayProfile container="asf" audioCodec="mp2,ac3" videoCodec="mpeg2video" type="Video" />
     <DirectPlayProfile container="asf" audioCodec="mp2,ac3" videoCodec="mpeg2video" type="Video" />
     <DirectPlayProfile container="mp3" audioCodec="mp2,mp3" type="Audio" />
     <DirectPlayProfile container="mp3" audioCodec="mp2,mp3" type="Audio" />

+ 15 - 115
MediaBrowser.Dlna/Ssdp/SsdpHandler.cs

@@ -33,12 +33,8 @@ namespace MediaBrowser.Dlna.Ssdp
         private readonly IPAddress _ssdpIp = IPAddress.Parse(SSDPAddr);
         private readonly IPAddress _ssdpIp = IPAddress.Parse(SSDPAddr);
         private readonly IPEndPoint _ssdpEndp = new IPEndPoint(IPAddress.Parse(SSDPAddr), SSDPPort);
         private readonly IPEndPoint _ssdpEndp = new IPEndPoint(IPAddress.Parse(SSDPAddr), SSDPPort);
 
 
-        private Timer _queueTimer;
         private Timer _notificationTimer;
         private Timer _notificationTimer;
 
 
-        private readonly AutoResetEvent _datagramPosted = new AutoResetEvent(false);
-        private readonly ConcurrentQueue<Datagram> _messageQueue = new ConcurrentQueue<Datagram>();
-
         private bool _isDisposed;
         private bool _isDisposed;
         private readonly ConcurrentDictionary<Guid, List<UpnpDevice>> _devices = new ConcurrentDictionary<Guid, List<UpnpDevice>>();
         private readonly ConcurrentDictionary<Guid, List<UpnpDevice>> _devices = new ConcurrentDictionary<Guid, List<UpnpDevice>>();
 
 
@@ -121,9 +117,13 @@ namespace MediaBrowser.Dlna.Ssdp
 
 
         public void Start()
         public void Start()
         {
         {
-            RestartSocketListener();
+            DisposeSocket();
+            StopAliveNotifier();
 
 
+            RestartSocketListener();
             ReloadAliveNotifier();
             ReloadAliveNotifier();
+
+            SystemEvents.PowerModeChanged -= SystemEvents_PowerModeChanged;
             SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged;
             SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged;
         }
         }
 
 
@@ -131,7 +131,7 @@ namespace MediaBrowser.Dlna.Ssdp
         {
         {
             if (e.Mode == PowerModes.Resume)
             if (e.Mode == PowerModes.Resume)
             {
             {
-                NotifyAll();
+                Start();
             }
             }
         }
         }
 
 
@@ -154,7 +154,7 @@ namespace MediaBrowser.Dlna.Ssdp
             SendDatagram("M-SEARCH * HTTP/1.1", values, _ssdpEndp, localIp, true, 2);
             SendDatagram("M-SEARCH * HTTP/1.1", values, _ssdpEndp, localIp, true, 2);
         }
         }
 
 
-        public void SendDatagram(string header,
+        public async void SendDatagram(string header,
             Dictionary<string, string> values,
             Dictionary<string, string> values,
             EndPoint endpoint,
             EndPoint endpoint,
             EndPoint localAddress,
             EndPoint localAddress,
@@ -162,28 +162,18 @@ namespace MediaBrowser.Dlna.Ssdp
             int sendCount)
             int sendCount)
         {
         {
             var msg = new SsdpMessageBuilder().BuildMessage(header, values);
             var msg = new SsdpMessageBuilder().BuildMessage(header, values);
-            var queued = false;
 
 
             var enableDebugLogging = _config.GetDlnaConfiguration().EnableDebugLogging;
             var enableDebugLogging = _config.GetDlnaConfiguration().EnableDebugLogging;
 
 
             for (var i = 0; i < sendCount; i++)
             for (var i = 0; i < sendCount; i++)
             {
             {
-                var dgram = new Datagram(endpoint, localAddress, _logger, msg, isBroadcast, enableDebugLogging);
-
-                if (_messageQueue.Count == 0)
+                if (i > 0)
                 {
                 {
-                    dgram.Send();
+                    await Task.Delay(500).ConfigureAwait(false);
                 }
                 }
-                else
-                {
-                    _messageQueue.Enqueue(dgram);
-                    queued = true;
-                }
-            }
 
 
-            if (queued)
-            {
-                StartQueueTimer();
+                var dgram = new Datagram(endpoint, localAddress, _logger, msg, isBroadcast, enableDebugLogging);
+                dgram.Send();
             }
             }
         }
         }
 
 
@@ -254,47 +244,10 @@ namespace MediaBrowser.Dlna.Ssdp
             }
             }
         }
         }
 
 
-        private readonly object _queueTimerSyncLock = new object();
-        private void StartQueueTimer()
-        {
-            lock (_queueTimerSyncLock)
-            {
-                if (_queueTimer == null)
-                {
-                    _queueTimer = new Timer(QueueTimerCallback, null, 500, Timeout.Infinite);
-                }
-                else
-                {
-                    _queueTimer.Change(500, Timeout.Infinite);
-                }
-            }
-        }
-
-        private void QueueTimerCallback(object state)
-        {
-            Datagram msg;
-            while (_messageQueue.TryDequeue(out msg))
-            {
-                msg.Send();
-            }
-
-            _datagramPosted.Set();
-
-            if (_messageQueue.Count > 0)
-            {
-                StartQueueTimer();
-            }
-            else
-            {
-                DisposeQueueTimer();
-            }
-        }
-
         private void RestartSocketListener()
         private void RestartSocketListener()
         {
         {
             if (_isDisposed)
             if (_isDisposed)
             {
             {
-                StopSocketRetryTimer();
                 return;
                 return;
             }
             }
 
 
@@ -304,8 +257,6 @@ namespace MediaBrowser.Dlna.Ssdp
 
 
                 _logger.Info("MultiCast socket created");
                 _logger.Info("MultiCast socket created");
 
 
-                StopSocketRetryTimer();
-
                 Receive();
                 Receive();
             }
             }
             catch (Exception ex)
             catch (Exception ex)
@@ -315,31 +266,6 @@ namespace MediaBrowser.Dlna.Ssdp
             }
             }
         }
         }
 
 
-        private Timer _socketRetryTimer;
-        private readonly object _socketRetryLock = new object();
-        private void StartSocketRetryTimer()
-        {
-            lock (_socketRetryLock)
-            {
-                if (_socketRetryTimer == null)
-                {
-                    _socketRetryTimer = new Timer(s => RestartSocketListener(), null, TimeSpan.FromSeconds(30), TimeSpan.FromSeconds(30));
-                }
-            }
-        }
-
-        private void StopSocketRetryTimer()
-        {
-            lock (_socketRetryLock)
-            {
-                if (_socketRetryTimer != null)
-                {
-                    _socketRetryTimer.Dispose();
-                    _socketRetryTimer = null;
-                }
-            }
-        }
-
         private void Receive()
         private void Receive()
         {
         {
             try
             try
@@ -448,16 +374,9 @@ namespace MediaBrowser.Dlna.Ssdp
             SystemEvents.PowerModeChanged -= SystemEvents_PowerModeChanged;
             SystemEvents.PowerModeChanged -= SystemEvents_PowerModeChanged;
 
 
             _isDisposed = true;
             _isDisposed = true;
-            while (_messageQueue.Count != 0)
-            {
-                _datagramPosted.WaitOne();
-            }
 
 
             DisposeSocket();
             DisposeSocket();
-            DisposeQueueTimer();
-            DisposeNotificationTimer();
-
-            _datagramPosted.Dispose();
+            StopAliveNotifier();
         }
         }
 
 
         private void DisposeSocket()
         private void DisposeSocket()
@@ -470,18 +389,6 @@ namespace MediaBrowser.Dlna.Ssdp
             }
             }
         }
         }
 
 
-        private void DisposeQueueTimer()
-        {
-            lock (_queueTimerSyncLock)
-            {
-                if (_queueTimer != null)
-                {
-                    _queueTimer.Dispose();
-                    _queueTimer = null;
-                }
-            }
-        }
-
         private Socket CreateMulticastSocket()
         private Socket CreateMulticastSocket()
         {
         {
             var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
             var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
@@ -534,14 +441,7 @@ namespace MediaBrowser.Dlna.Ssdp
 
 
         public void RegisterNotification(Guid uuid, Uri descriptionUri, IPAddress address, IEnumerable<string> services)
         public void RegisterNotification(Guid uuid, Uri descriptionUri, IPAddress address, IEnumerable<string> services)
         {
         {
-            List<UpnpDevice> list;
-            lock (_devices)
-            {
-                if (!_devices.TryGetValue(uuid, out list))
-                {
-                    _devices.TryAdd(uuid, list = new List<UpnpDevice>());
-                }
-            }
+            var list = _devices.GetOrAdd(uuid, new List<UpnpDevice>());
 
 
             list.AddRange(services.Select(i => new UpnpDevice(uuid, i, descriptionUri, address)));
             list.AddRange(services.Select(i => new UpnpDevice(uuid, i, descriptionUri, address)));
 
 
@@ -572,7 +472,7 @@ namespace MediaBrowser.Dlna.Ssdp
 
 
             if (!config.BlastAliveMessages)
             if (!config.BlastAliveMessages)
             {
             {
-                DisposeNotificationTimer();
+                StopAliveNotifier();
                 return;
                 return;
             }
             }
 
 
@@ -599,7 +499,7 @@ namespace MediaBrowser.Dlna.Ssdp
             }
             }
         }
         }
 
 
-        private void DisposeNotificationTimer()
+        private void StopAliveNotifier()
         {
         {
             lock (_notificationTimerSyncLock)
             lock (_notificationTimerSyncLock)
             {
             {

+ 18 - 9
MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs

@@ -292,16 +292,25 @@ namespace MediaBrowser.MediaEncoding.Encoder
                 return false;
                 return false;
             }
             }
 
 
-            // If the video codec is not some form of mpeg, then take a shortcut and limit this to containers that are likely to have interlaced content
-            if ((videoStream.Codec ?? string.Empty).IndexOf("mpeg", StringComparison.OrdinalIgnoreCase) == -1)
+            var formats = (video.Container ?? string.Empty).Split(',').ToList();
+            var enableInterlacedDection = formats.Contains("vob", StringComparer.OrdinalIgnoreCase) &&
+                                          formats.Contains("m2ts", StringComparer.OrdinalIgnoreCase) &&
+                                          formats.Contains("ts", StringComparer.OrdinalIgnoreCase) &&
+                                          formats.Contains("mpegts", StringComparer.OrdinalIgnoreCase) &&
+                                          formats.Contains("wtv", StringComparer.OrdinalIgnoreCase);
+            
+            // If it's mpeg based, assume true
+            if ((videoStream.Codec ?? string.Empty).IndexOf("mpeg", StringComparison.OrdinalIgnoreCase) != -1)
             {
             {
-                var formats = (video.Container ?? string.Empty).Split(',').ToList();
-
-                if (!formats.Contains("vob", StringComparer.OrdinalIgnoreCase) &&
-                    !formats.Contains("m2ts", StringComparer.OrdinalIgnoreCase) &&
-                    !formats.Contains("ts", StringComparer.OrdinalIgnoreCase) &&
-                    !formats.Contains("mpegts", StringComparer.OrdinalIgnoreCase) &&
-                    !formats.Contains("wtv", StringComparer.OrdinalIgnoreCase))
+                if (enableInterlacedDection)
+                {
+                    return true;
+                }
+            }
+            else
+            {
+                // If the video codec is not some form of mpeg, then take a shortcut and limit this to containers that are likely to have interlaced content
+                if (!enableInterlacedDection)
                 {
                 {
                     return false;
                     return false;
                 }
                 }

+ 2 - 3
MediaBrowser.Model/Configuration/ServerConfiguration.cs

@@ -164,7 +164,7 @@ namespace MediaBrowser.Model.Configuration
         /// different directories and files.
         /// different directories and files.
         /// </summary>
         /// </summary>
         /// <value>The file watcher delay.</value>
         /// <value>The file watcher delay.</value>
-        public int RealtimeLibraryMonitorDelay { get; set; }
+        public int LibraryMonitorDelay { get; set; }
 
 
         /// <summary>
         /// <summary>
         /// Gets or sets a value indicating whether [enable dashboard response caching].
         /// Gets or sets a value indicating whether [enable dashboard response caching].
@@ -181,7 +181,6 @@ namespace MediaBrowser.Model.Configuration
         public string DashboardSourcePath { get; set; }
         public string DashboardSourcePath { get; set; }
 
 
         public bool MergeMetadataAndImagesByName { get; set; }
         public bool MergeMetadataAndImagesByName { get; set; }
-        public bool EnableStandaloneMetadata { get; set; }
 
 
         /// <summary>
         /// <summary>
         /// Gets or sets the image saving convention.
         /// Gets or sets the image saving convention.
@@ -256,7 +255,7 @@ namespace MediaBrowser.Model.Configuration
             MinResumeDurationSeconds = 300;
             MinResumeDurationSeconds = 300;
 
 
             EnableLibraryMonitor = AutoOnOff.Auto;
             EnableLibraryMonitor = AutoOnOff.Auto;
-            RealtimeLibraryMonitorDelay = 40;
+            LibraryMonitorDelay = 60;
 
 
             EnableInternetProviders = true;
             EnableInternetProviders = true;
             FindInternetTrailers = true;
             FindInternetTrailers = true;

+ 1 - 0
MediaBrowser.Model/Configuration/UserConfiguration.cs

@@ -48,6 +48,7 @@ namespace MediaBrowser.Model.Configuration
         public string[] PlainFolderViews { get; set; }
         public string[] PlainFolderViews { get; set; }
 
 
         public bool HidePlayedInLatest { get; set; }
         public bool HidePlayedInLatest { get; set; }
+        public bool DisplayChannelsInline { get; set; }
 
 
         /// <summary>
         /// <summary>
         /// Initializes a new instance of the <see cref="UserConfiguration" /> class.
         /// Initializes a new instance of the <see cref="UserConfiguration" /> class.

+ 11 - 3
MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs

@@ -213,7 +213,7 @@ namespace MediaBrowser.Providers.MediaInfo
             }
             }
 
 
             var chapters = mediaInfo.Chapters ?? new List<ChapterInfo>();
             var chapters = mediaInfo.Chapters ?? new List<ChapterInfo>();
-            if (video.VideoType == VideoType.BluRay || (video.IsoType.HasValue && video.IsoType.Value == IsoType.BluRay))
+            if (blurayInfo != null)
             {
             {
                 FetchBdInfo(video, chapters, mediaStreams, blurayInfo);
                 FetchBdInfo(video, chapters, mediaStreams, blurayInfo);
             }
             }
@@ -360,7 +360,15 @@ namespace MediaBrowser.Providers.MediaInfo
         /// <returns>VideoStream.</returns>
         /// <returns>VideoStream.</returns>
         private BlurayDiscInfo GetBDInfo(string path)
         private BlurayDiscInfo GetBDInfo(string path)
         {
         {
-            return _blurayExaminer.GetDiscInfo(path);
+            try
+            {
+                return _blurayExaminer.GetDiscInfo(path);
+            }
+            catch (Exception ex)
+            {
+                _logger.ErrorException("Error getting BDInfo", ex);
+                return null;
+            }
         }
         }
 
 
         private void FetchEmbeddedInfo(Video video, Model.MediaInfo.MediaInfo data, MetadataRefreshOptions options)
         private void FetchEmbeddedInfo(Video video, Model.MediaInfo.MediaInfo data, MetadataRefreshOptions options)
@@ -628,7 +636,7 @@ namespace MediaBrowser.Providers.MediaInfo
                 FetchFromDvdLib(item, mount);
                 FetchFromDvdLib(item, mount);
             }
             }
 
 
-            if (item.VideoType == VideoType.BluRay || (item.IsoType.HasValue && item.IsoType.Value == IsoType.BluRay))
+            if (blurayDiscInfo != null)
             {
             {
                 item.PlayableStreamFileNames = blurayDiscInfo.Files.ToList();
                 item.PlayableStreamFileNames = blurayDiscInfo.Files.ToList();
             }
             }

+ 7 - 11
MediaBrowser.Providers/People/MovieDbPersonProvider.cs

@@ -38,7 +38,7 @@ namespace MediaBrowser.Providers.People
 
 
         private int _requestCount;
         private int _requestCount;
         private readonly object _requestCountLock = new object();
         private readonly object _requestCountLock = new object();
-        private Timer _requestCountReset;
+        private DateTime _lastRequestCountReset;
 
 
         public MovieDbPersonProvider(IFileSystem fileSystem, IServerConfigurationManager configurationManager, IJsonSerializer jsonSerializer, IHttpClient httpClient, ILogger logger)
         public MovieDbPersonProvider(IFileSystem fileSystem, IServerConfigurationManager configurationManager, IJsonSerializer jsonSerializer, IHttpClient httpClient, ILogger logger)
         {
         {
@@ -48,16 +48,6 @@ namespace MediaBrowser.Providers.People
             _httpClient = httpClient;
             _httpClient = httpClient;
             _logger = logger;
             _logger = logger;
             Current = this;
             Current = this;
-
-            _requestCountReset = new Timer(OnRequestThrottleTimerFired, null, TimeSpan.FromHours(1), TimeSpan.FromHours(1));
-        }
-
-        private void OnRequestThrottleTimerFired(object state)
-        {
-            lock (_requestCountLock)
-            {
-                _requestCount = 0;
-            }
         }
         }
 
 
         public string Name
         public string Name
@@ -101,6 +91,12 @@ namespace MediaBrowser.Providers.People
             {
             {
                 lock (_requestCountLock)
                 lock (_requestCountLock)
                 {
                 {
+                    if ((DateTime.UtcNow - _lastRequestCountReset).TotalHours >= 1)
+                    {
+                        _requestCount = 0;
+                        _lastRequestCountReset = DateTime.UtcNow;
+                    }
+
                     var requestCount = _requestCount;
                     var requestCount = _requestCount;
 
 
                     if (requestCount >= 5)
                     if (requestCount >= 5)

+ 2 - 2
MediaBrowser.Providers/TV/TvdbSeriesProvider.cs

@@ -238,7 +238,7 @@ namespace MediaBrowser.Providers.TV
                 throw new ArgumentNullException("seriesId");
                 throw new ArgumentNullException("seriesId");
             }
             }
 
 
-            var url = string.Format(SeriesGetZip, TVUtils.TvdbApiKey, seriesId, preferredMetadataLanguage);
+            var url = string.Format(SeriesGetZip, TVUtils.TvdbApiKey, seriesId, NormalizeLanguage(preferredMetadataLanguage));
 
 
             using (var zipStream = await _httpClient.Get(new HttpRequestOptions
             using (var zipStream = await _httpClient.Get(new HttpRequestOptions
             {
             {
@@ -268,7 +268,7 @@ namespace MediaBrowser.Providers.TV
                 await SanitizeXmlFile(file).ConfigureAwait(false);
                 await SanitizeXmlFile(file).ConfigureAwait(false);
             }
             }
 
 
-            var downloadLangaugeXmlFile = Path.Combine(seriesDataPath, preferredMetadataLanguage + ".xml");
+            var downloadLangaugeXmlFile = Path.Combine(seriesDataPath, NormalizeLanguage(preferredMetadataLanguage) + ".xml");
             var saveAsLanguageXmlFile = Path.Combine(seriesDataPath, saveAsMetadataLanguage + ".xml");
             var saveAsLanguageXmlFile = Path.Combine(seriesDataPath, saveAsMetadataLanguage + ".xml");
 
 
             if (!string.Equals(downloadLangaugeXmlFile, saveAsLanguageXmlFile, StringComparison.OrdinalIgnoreCase))
             if (!string.Equals(downloadLangaugeXmlFile, saveAsLanguageXmlFile, StringComparison.OrdinalIgnoreCase))

+ 3 - 58
MediaBrowser.Server.Implementations/Channels/ChannelManager.cs

@@ -29,7 +29,7 @@ using CommonIO;
 
 
 namespace MediaBrowser.Server.Implementations.Channels
 namespace MediaBrowser.Server.Implementations.Channels
 {
 {
-    public class ChannelManager : IChannelManager, IDisposable
+    public class ChannelManager : IChannelManager
     {
     {
         private IChannel[] _channels;
         private IChannel[] _channels;
 
 
@@ -47,11 +47,6 @@ namespace MediaBrowser.Server.Implementations.Channels
         private readonly ILocalizationManager _localization;
         private readonly ILocalizationManager _localization;
         private readonly ConcurrentDictionary<Guid, bool> _refreshedItems = new ConcurrentDictionary<Guid, bool>();
         private readonly ConcurrentDictionary<Guid, bool> _refreshedItems = new ConcurrentDictionary<Guid, bool>();
 
 
-        private readonly ConcurrentDictionary<string, int> _downloadCounts = new ConcurrentDictionary<string, int>();
-
-        private Timer _refreshTimer;
-        private Timer _clearDownloadCountsTimer;
-
         public ChannelManager(IUserManager userManager, IDtoService dtoService, ILibraryManager libraryManager, ILogger logger, IServerConfigurationManager config, IFileSystem fileSystem, IUserDataManager userDataManager, IJsonSerializer jsonSerializer, ILocalizationManager localization, IHttpClient httpClient, IProviderManager providerManager)
         public ChannelManager(IUserManager userManager, IDtoService dtoService, ILibraryManager libraryManager, ILogger logger, IServerConfigurationManager config, IFileSystem fileSystem, IUserDataManager userDataManager, IJsonSerializer jsonSerializer, ILocalizationManager localization, IHttpClient httpClient, IProviderManager providerManager)
         {
         {
             _userManager = userManager;
             _userManager = userManager;
@@ -65,9 +60,6 @@ namespace MediaBrowser.Server.Implementations.Channels
             _localization = localization;
             _localization = localization;
             _httpClient = httpClient;
             _httpClient = httpClient;
             _providerManager = providerManager;
             _providerManager = providerManager;
-
-            _refreshTimer = new Timer(s => _refreshedItems.Clear(), null, TimeSpan.FromHours(3), TimeSpan.FromHours(3));
-            _clearDownloadCountsTimer = new Timer(s => _downloadCounts.Clear(), null, TimeSpan.FromHours(24), TimeSpan.FromHours(24));
         }
         }
 
 
         private TimeSpan CacheLength
         private TimeSpan CacheLength
@@ -206,6 +198,8 @@ namespace MediaBrowser.Server.Implementations.Channels
 
 
         public async Task RefreshChannels(IProgress<double> progress, CancellationToken cancellationToken)
         public async Task RefreshChannels(IProgress<double> progress, CancellationToken cancellationToken)
         {
         {
+            _refreshedItems.Clear();
+
             var allChannelsList = GetAllChannels().ToList();
             var allChannelsList = GetAllChannels().ToList();
 
 
             var numComplete = 0;
             var numComplete = 0;
@@ -1471,12 +1465,6 @@ namespace MediaBrowser.Server.Implementations.Channels
 
 
             var limit = features.DailyDownloadLimit;
             var limit = features.DailyDownloadLimit;
 
 
-            if (!ValidateDownloadLimit(host, limit))
-            {
-                _logger.Error(string.Format("Download limit has been reached for {0}", channel.Name));
-                throw new ChannelDownloadException(string.Format("Download limit has been reached for {0}", channel.Name));
-            }
-
             foreach (var header in source.RequiredHttpHeaders)
             foreach (var header in source.RequiredHttpHeaders)
             {
             {
                 options.RequestHeaders[header.Key] = header.Value;
                 options.RequestHeaders[header.Key] = header.Value;
@@ -1495,8 +1483,6 @@ namespace MediaBrowser.Server.Implementations.Channels
                 };
                 };
             }
             }
 
 
-            IncrementDownloadCount(host, limit);
-
             if (string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase) && response.ContentType.StartsWith("video/", StringComparison.OrdinalIgnoreCase))
             if (string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase) && response.ContentType.StartsWith("video/", StringComparison.OrdinalIgnoreCase))
             {
             {
                 var extension = response.ContentType.Split('/')
                 var extension = response.ContentType.Split('/')
@@ -1531,46 +1517,5 @@ namespace MediaBrowser.Server.Implementations.Channels
 
 
             }
             }
         }
         }
-
-        private void IncrementDownloadCount(string key, int? limit)
-        {
-            if (!limit.HasValue)
-            {
-                return;
-            }
-
-            int current;
-            _downloadCounts.TryGetValue(key, out current);
-
-            current++;
-            _downloadCounts.AddOrUpdate(key, current, (k, v) => current);
-        }
-
-        private bool ValidateDownloadLimit(string key, int? limit)
-        {
-            if (!limit.HasValue)
-            {
-                return true;
-            }
-
-            int current;
-            _downloadCounts.TryGetValue(key, out current);
-
-            return current < limit.Value;
-        }
-
-        public void Dispose()
-        {
-            if (_clearDownloadCountsTimer != null)
-            {
-                _clearDownloadCountsTimer.Dispose();
-                _clearDownloadCountsTimer = null;
-            }
-            if (_refreshTimer != null)
-            {
-                _refreshTimer.Dispose();
-                _refreshTimer = null;
-            }
-        }
     }
     }
 }
 }

+ 2 - 10
MediaBrowser.Server.Implementations/Configuration/ServerConfigurationManager.cs

@@ -121,20 +121,12 @@ namespace MediaBrowser.Server.Implementations.Configuration
 
 
             ((ServerApplicationPaths)ApplicationPaths).InternalMetadataPath = metadataPath;
             ((ServerApplicationPaths)ApplicationPaths).InternalMetadataPath = metadataPath;
 
 
-            if (Configuration.MergeMetadataAndImagesByName)
-            {
-                ((ServerApplicationPaths)ApplicationPaths).ItemsByNamePath = ((ServerApplicationPaths)ApplicationPaths).InternalMetadataPath;
-            }
+            ((ServerApplicationPaths)ApplicationPaths).ItemsByNamePath = ((ServerApplicationPaths)ApplicationPaths).InternalMetadataPath;
         }
         }
 
 
         private string GetInternalMetadataPath()
         private string GetInternalMetadataPath()
         {
         {
-            if (Configuration.EnableStandaloneMetadata)
-            {
-                return Path.Combine(ApplicationPaths.ProgramDataPath, "metadata");
-            }
-
-            return null;
+            return Path.Combine(ApplicationPaths.ProgramDataPath, "metadata");
         }
         }
 
 
         /// <summary>
         /// <summary>

+ 3 - 2
MediaBrowser.Server.Implementations/Connect/ConnectEntryPoint.cs

@@ -13,12 +13,13 @@ using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using CommonIO;
 using CommonIO;
 using MediaBrowser.Common.IO;
 using MediaBrowser.Common.IO;
+using MediaBrowser.Common.Threading;
 
 
 namespace MediaBrowser.Server.Implementations.Connect
 namespace MediaBrowser.Server.Implementations.Connect
 {
 {
     public class ConnectEntryPoint : IServerEntryPoint
     public class ConnectEntryPoint : IServerEntryPoint
     {
     {
-        private Timer _timer;
+        private PeriodicTimer _timer;
         private readonly IHttpClient _httpClient;
         private readonly IHttpClient _httpClient;
         private readonly IApplicationPaths _appPaths;
         private readonly IApplicationPaths _appPaths;
         private readonly ILogger _logger;
         private readonly ILogger _logger;
@@ -43,7 +44,7 @@ namespace MediaBrowser.Server.Implementations.Connect
         {
         {
             Task.Run(() => LoadCachedAddress());
             Task.Run(() => LoadCachedAddress());
 
 
-            _timer = new Timer(TimerCallback, null, TimeSpan.FromSeconds(5), TimeSpan.FromHours(3));
+            _timer = new PeriodicTimer(TimerCallback, null, TimeSpan.FromSeconds(5), TimeSpan.FromHours(3));
         }
         }
 
 
         private readonly string[] _ipLookups = { "http://bot.whatismyipaddress.com", "https://connect.emby.media/service/ip" };
         private readonly string[] _ipLookups = { "http://bot.whatismyipaddress.com", "https://connect.emby.media/service/ip" };

+ 5 - 8
MediaBrowser.Server.Implementations/Devices/DeviceRepository.cs

@@ -25,7 +25,7 @@ namespace MediaBrowser.Server.Implementations.Devices
         private readonly ILogger _logger;
         private readonly ILogger _logger;
         private readonly IFileSystem _fileSystem;
         private readonly IFileSystem _fileSystem;
 
 
-        private ConcurrentBag<DeviceInfo> _devices;
+        private List<DeviceInfo> _devices;
 
 
         public DeviceRepository(IApplicationPaths appPaths, IJsonSerializer json, ILogger logger, IFileSystem fileSystem)
         public DeviceRepository(IApplicationPaths appPaths, IJsonSerializer json, ILogger logger, IFileSystem fileSystem)
         {
         {
@@ -93,17 +93,14 @@ namespace MediaBrowser.Server.Implementations.Devices
 
 
         public IEnumerable<DeviceInfo> GetDevices()
         public IEnumerable<DeviceInfo> GetDevices()
         {
         {
-            if (_devices == null)
+            lock (_syncLock)
             {
             {
-                lock (_syncLock)
+                if (_devices == null)
                 {
                 {
-                    if (_devices == null)
-                    {
-                        _devices = new ConcurrentBag<DeviceInfo>(LoadDevices());
-                    }
+                    _devices = LoadDevices().ToList();
                 }
                 }
+                return _devices.ToList();
             }
             }
-            return _devices.ToList();
         }
         }
 
 
         private IEnumerable<DeviceInfo> LoadDevices()
         private IEnumerable<DeviceInfo> LoadDevices()

+ 3 - 2
MediaBrowser.Server.Implementations/EntryPoints/ExternalPortForwarding.cs

@@ -11,6 +11,7 @@ using System.IO;
 using System.Net;
 using System.Net;
 using System.Text;
 using System.Text;
 using System.Threading;
 using System.Threading;
+using MediaBrowser.Common.Threading;
 
 
 namespace MediaBrowser.Server.Implementations.EntryPoints
 namespace MediaBrowser.Server.Implementations.EntryPoints
 {
 {
@@ -21,7 +22,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
         private readonly IServerConfigurationManager _config;
         private readonly IServerConfigurationManager _config;
         private readonly ISsdpHandler _ssdp;
         private readonly ISsdpHandler _ssdp;
 
 
-        private Timer _timer;
+        private PeriodicTimer _timer;
         private bool _isStarted;
         private bool _isStarted;
 
 
         public ExternalPortForwarding(ILogManager logmanager, IServerApplicationHost appHost, IServerConfigurationManager config, ISsdpHandler ssdp)
         public ExternalPortForwarding(ILogManager logmanager, IServerApplicationHost appHost, IServerConfigurationManager config, ISsdpHandler ssdp)
@@ -95,7 +96,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
             NatUtility.UnhandledException += NatUtility_UnhandledException;
             NatUtility.UnhandledException += NatUtility_UnhandledException;
             NatUtility.StartDiscovery();
             NatUtility.StartDiscovery();
 
 
-            _timer = new Timer(s => _createdRules = new List<string>(), null, TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(5));
+            _timer = new PeriodicTimer(s => _createdRules = new List<string>(), null, TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(5));
 
 
             _ssdp.MessageReceived += _ssdp_MessageReceived;
             _ssdp.MessageReceived += _ssdp_MessageReceived;
 
 

+ 3 - 2
MediaBrowser.Server.Implementations/EntryPoints/LoadRegistrations.cs

@@ -4,6 +4,7 @@ using MediaBrowser.Model.Logging;
 using System;
 using System;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
+using MediaBrowser.Common.Threading;
 
 
 namespace MediaBrowser.Server.Implementations.EntryPoints
 namespace MediaBrowser.Server.Implementations.EntryPoints
 {
 {
@@ -22,7 +23,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
         /// </summary>
         /// </summary>
         private readonly ILogger _logger;
         private readonly ILogger _logger;
 
 
-        private Timer _timer;
+        private PeriodicTimer _timer;
 
 
         /// <summary>
         /// <summary>
         /// Initializes a new instance of the <see cref="LoadRegistrations" /> class.
         /// Initializes a new instance of the <see cref="LoadRegistrations" /> class.
@@ -41,7 +42,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
         /// </summary>
         /// </summary>
         public void Run()
         public void Run()
         {
         {
-            _timer = new Timer(s => LoadAllRegistrations(), null, TimeSpan.FromMilliseconds(100), TimeSpan.FromHours(12));
+            _timer = new PeriodicTimer(s => LoadAllRegistrations(), null, TimeSpan.FromMilliseconds(100), TimeSpan.FromHours(12));
         }
         }
 
 
         private async Task LoadAllRegistrations()
         private async Task LoadAllRegistrations()

+ 5 - 11
MediaBrowser.Server.Implementations/EntryPoints/UsageEntryPoint.cs

@@ -9,6 +9,7 @@ using System;
 using System.Collections.Concurrent;
 using System.Collections.Concurrent;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Threading;
 using System.Threading;
+using System.Threading.Tasks;
 
 
 namespace MediaBrowser.Server.Implementations.EntryPoints
 namespace MediaBrowser.Server.Implementations.EntryPoints
 {
 {
@@ -23,7 +24,6 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
         private readonly ISessionManager _sessionManager;
         private readonly ISessionManager _sessionManager;
         private readonly IUserManager _userManager;
         private readonly IUserManager _userManager;
 
 
-        private Timer _timer;
         private readonly TimeSpan _frequency = TimeSpan.FromHours(24);
         private readonly TimeSpan _frequency = TimeSpan.FromHours(24);
 
 
         private readonly ConcurrentDictionary<Guid, ClientInfo> _apps = new ConcurrentDictionary<Guid, ClientInfo>();
         private readonly ConcurrentDictionary<Guid, ClientInfo> _apps = new ConcurrentDictionary<Guid, ClientInfo>();
@@ -95,16 +95,16 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
             return info;
             return info;
         }
         }
 
 
-        public void Run()
+        public async void Run()
         {
         {
-            _timer = new Timer(OnTimerFired, null, TimeSpan.FromMilliseconds(5000), _frequency);
+            await Task.Delay(5000).ConfigureAwait(false);
+            OnTimerFired();
         }
         }
 
 
         /// <summary>
         /// <summary>
         /// Called when [timer fired].
         /// Called when [timer fired].
         /// </summary>
         /// </summary>
-        /// <param name="state">The state.</param>
-        private async void OnTimerFired(object state)
+        private async void OnTimerFired()
         {
         {
             try
             try
             {
             {
@@ -121,12 +121,6 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
         public void Dispose()
         public void Dispose()
         {
         {
             _sessionManager.SessionStarted -= _sessionManager_SessionStarted;
             _sessionManager.SessionStarted -= _sessionManager_SessionStarted;
-
-            if (_timer != null)
-            {
-                _timer.Dispose();
-                _timer = null;
-            }
         }
         }
     }
     }
 }
 }

+ 119 - 114
MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs

@@ -76,50 +76,50 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
             {
             {
                 var seasonNumber = episodeInfo.SeasonNumber;
                 var seasonNumber = episodeInfo.SeasonNumber;
 
 
-				result.ExtractedSeasonNumber = seasonNumber;
-
-				// Passing in true will include a few extra regex's
-				var episodeNumber = episodeInfo.EpisodeNumber;
-
-				result.ExtractedEpisodeNumber = episodeNumber;
-
-				var premiereDate = episodeInfo.IsByDate ? 
-					new DateTime(episodeInfo.Year.Value, episodeInfo.Month.Value, episodeInfo.Day.Value) : 
-					(DateTime?)null;
-
-				if (episodeInfo.IsByDate || (seasonNumber.HasValue && episodeNumber.HasValue))
-				{
-					if (episodeInfo.IsByDate) 
-					{
-						_logger.Debug("Extracted information from {0}. Series name {1}, Date {2}", path, seriesName, premiereDate.Value);
-					} 
-					else 
-					{
-						_logger.Debug("Extracted information from {0}. Series name {1}, Season {2}, Episode {3}", path, seriesName, seasonNumber, episodeNumber);
-					}
-
-					var endingEpisodeNumber = episodeInfo.EndingEpsiodeNumber;
-
-					result.ExtractedEndingEpisodeNumber = endingEpisodeNumber;
-
-					await OrganizeEpisode(path, 
-						seriesName, 
-						seasonNumber, 
-						episodeNumber, 
-						endingEpisodeNumber, 
-						premiereDate,
-						options, 
-						overwriteExisting, 
-						result, 
-						cancellationToken).ConfigureAwait(false);
-				}
-				else
-				{
-					var msg = string.Format("Unable to determine episode number from {0}", path);
-					result.Status = FileSortingStatus.Failure;
-					result.StatusMessage = msg;
-					_logger.Warn(msg);
-				}
+                result.ExtractedSeasonNumber = seasonNumber;
+
+                // Passing in true will include a few extra regex's
+                var episodeNumber = episodeInfo.EpisodeNumber;
+
+                result.ExtractedEpisodeNumber = episodeNumber;
+
+                var premiereDate = episodeInfo.IsByDate ?
+                    new DateTime(episodeInfo.Year.Value, episodeInfo.Month.Value, episodeInfo.Day.Value) :
+                    (DateTime?)null;
+
+                if (episodeInfo.IsByDate || (seasonNumber.HasValue && episodeNumber.HasValue))
+                {
+                    if (episodeInfo.IsByDate)
+                    {
+                        _logger.Debug("Extracted information from {0}. Series name {1}, Date {2}", path, seriesName, premiereDate.Value);
+                    }
+                    else
+                    {
+                        _logger.Debug("Extracted information from {0}. Series name {1}, Season {2}, Episode {3}", path, seriesName, seasonNumber, episodeNumber);
+                    }
+
+                    var endingEpisodeNumber = episodeInfo.EndingEpsiodeNumber;
+
+                    result.ExtractedEndingEpisodeNumber = endingEpisodeNumber;
+
+                    await OrganizeEpisode(path,
+                        seriesName,
+                        seasonNumber,
+                        episodeNumber,
+                        endingEpisodeNumber,
+                        premiereDate,
+                        options,
+                        overwriteExisting,
+                        result,
+                        cancellationToken).ConfigureAwait(false);
+                }
+                else
+                {
+                    var msg = string.Format("Unable to determine episode number from {0}", path);
+                    result.Status = FileSortingStatus.Failure;
+                    result.StatusMessage = msg;
+                    _logger.Warn(msg);
+                }
             }
             }
             else
             else
             {
             {
@@ -151,32 +151,32 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
 
 
             var series = (Series)_libraryManager.GetItemById(new Guid(request.SeriesId));
             var series = (Series)_libraryManager.GetItemById(new Guid(request.SeriesId));
 
 
-            await OrganizeEpisode(result.OriginalPath, 
-				series, 
-				request.SeasonNumber, 
-				request.EpisodeNumber, 
-				request.EndingEpisodeNumber, 
-				null,
-				options, 
-				true, 
-				result, 
-				cancellationToken).ConfigureAwait(false);
+            await OrganizeEpisode(result.OriginalPath,
+                series,
+                request.SeasonNumber,
+                request.EpisodeNumber,
+                request.EndingEpisodeNumber,
+                null,
+                options,
+                true,
+                result,
+                cancellationToken).ConfigureAwait(false);
 
 
             await _organizationService.SaveResult(result, CancellationToken.None).ConfigureAwait(false);
             await _organizationService.SaveResult(result, CancellationToken.None).ConfigureAwait(false);
 
 
             return result;
             return result;
         }
         }
 
 
-        private Task OrganizeEpisode(string sourcePath, 
-			string seriesName, 
-			int? seasonNumber, 
-			int? episodeNumber, 
-			int? endingEpiosdeNumber, 
-			DateTime? premiereDate,
-			TvFileOrganizationOptions options, 
-			bool overwriteExisting, 
-			FileOrganizationResult result, 
-			CancellationToken cancellationToken)
+        private Task OrganizeEpisode(string sourcePath,
+            string seriesName,
+            int? seasonNumber,
+            int? episodeNumber,
+            int? endingEpiosdeNumber,
+            DateTime? premiereDate,
+            TvFileOrganizationOptions options,
+            bool overwriteExisting,
+            FileOrganizationResult result,
+            CancellationToken cancellationToken)
         {
         {
             var series = GetMatchingSeries(seriesName, result);
             var series = GetMatchingSeries(seriesName, result);
 
 
@@ -189,33 +189,33 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
                 return Task.FromResult(true);
                 return Task.FromResult(true);
             }
             }
 
 
-            return OrganizeEpisode(sourcePath, 
-				series, 
-				seasonNumber, 
-				episodeNumber, 
-				endingEpiosdeNumber,
-				premiereDate,
-				options, 
-				overwriteExisting, 
-				result, 
-				cancellationToken);
+            return OrganizeEpisode(sourcePath,
+                series,
+                seasonNumber,
+                episodeNumber,
+                endingEpiosdeNumber,
+                premiereDate,
+                options,
+                overwriteExisting,
+                result,
+                cancellationToken);
         }
         }
 
 
-        private async Task OrganizeEpisode(string sourcePath, 
-			Series series, 
-			int? seasonNumber, 
-			int? episodeNumber, 
-			int? endingEpiosdeNumber, 
-			DateTime? premiereDate,
-			TvFileOrganizationOptions options, 
-			bool overwriteExisting, 
-			FileOrganizationResult result, 
-			CancellationToken cancellationToken)
+        private async Task OrganizeEpisode(string sourcePath,
+            Series series,
+            int? seasonNumber,
+            int? episodeNumber,
+            int? endingEpiosdeNumber,
+            DateTime? premiereDate,
+            TvFileOrganizationOptions options,
+            bool overwriteExisting,
+            FileOrganizationResult result,
+            CancellationToken cancellationToken)
         {
         {
             _logger.Info("Sorting file {0} into series {1}", sourcePath, series.Path);
             _logger.Info("Sorting file {0} into series {1}", sourcePath, series.Path);
 
 
             // Proceed to sort the file
             // Proceed to sort the file
-			var newPath = await GetNewPath(sourcePath, series, seasonNumber, episodeNumber, endingEpiosdeNumber, premiereDate, options, cancellationToken).ConfigureAwait(false);
+            var newPath = await GetNewPath(sourcePath, series, seasonNumber, episodeNumber, endingEpiosdeNumber, premiereDate, options, cancellationToken).ConfigureAwait(false);
 
 
             if (string.IsNullOrEmpty(newPath))
             if (string.IsNullOrEmpty(newPath))
             {
             {
@@ -324,17 +324,17 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
             }
             }
         }
         }
 
 
-        private List<string> GetOtherDuplicatePaths(string targetPath, 
-			Series series, 
-			int? seasonNumber, 
-			int? episodeNumber, 
-			int? endingEpisodeNumber)
+        private List<string> GetOtherDuplicatePaths(string targetPath,
+            Series series,
+            int? seasonNumber,
+            int? episodeNumber,
+            int? endingEpisodeNumber)
         {
         {
-			// TODO: Support date-naming?
-			if (!seasonNumber.HasValue || episodeNumber.HasValue) 
-			{
-				return new List<string> ();
-			}
+            // TODO: Support date-naming?
+            if (!seasonNumber.HasValue || episodeNumber.HasValue)
+            {
+                return new List<string>();
+            }
 
 
             var episodePaths = series.GetRecursiveChildren()
             var episodePaths = series.GetRecursiveChildren()
                 .OfType<Episode>()
                 .OfType<Episode>()
@@ -462,16 +462,18 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
         /// <param name="seasonNumber">The season number.</param>
         /// <param name="seasonNumber">The season number.</param>
         /// <param name="episodeNumber">The episode number.</param>
         /// <param name="episodeNumber">The episode number.</param>
         /// <param name="endingEpisodeNumber">The ending episode number.</param>
         /// <param name="endingEpisodeNumber">The ending episode number.</param>
+        /// <param name="premiereDate">The premiere date.</param>
         /// <param name="options">The options.</param>
         /// <param name="options">The options.</param>
+        /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>System.String.</returns>
         /// <returns>System.String.</returns>
-        private async Task<string> GetNewPath(string sourcePath, 
-			Series series, 
-			int? seasonNumber, 
-			int? episodeNumber, 
-			int? endingEpisodeNumber, 
-			DateTime? premiereDate,
-			TvFileOrganizationOptions options, 
-			CancellationToken cancellationToken)
+        private async Task<string> GetNewPath(string sourcePath,
+            Series series,
+            int? seasonNumber,
+            int? episodeNumber,
+            int? endingEpisodeNumber,
+            DateTime? premiereDate,
+            TvFileOrganizationOptions options,
+            CancellationToken cancellationToken)
         {
         {
             var episodeInfo = new EpisodeInfo
             var episodeInfo = new EpisodeInfo
             {
             {
@@ -481,7 +483,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
                 MetadataLanguage = series.GetPreferredMetadataLanguage(),
                 MetadataLanguage = series.GetPreferredMetadataLanguage(),
                 ParentIndexNumber = seasonNumber,
                 ParentIndexNumber = seasonNumber,
                 SeriesProviderIds = series.ProviderIds,
                 SeriesProviderIds = series.ProviderIds,
-				PremiereDate = premiereDate
+                PremiereDate = premiereDate
             };
             };
 
 
             var searchResults = await _providerManager.GetRemoteSearchResults<Episode, EpisodeInfo>(new RemoteSearchQuery<EpisodeInfo>
             var searchResults = await _providerManager.GetRemoteSearchResults<Episode, EpisodeInfo>(new RemoteSearchQuery<EpisodeInfo>
@@ -491,22 +493,25 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
             }, cancellationToken).ConfigureAwait(false);
             }, cancellationToken).ConfigureAwait(false);
 
 
             var episode = searchResults.FirstOrDefault();
             var episode = searchResults.FirstOrDefault();
-            
-            string episodeName = string.Empty;
 
 
             if (episode == null)
             if (episode == null)
             {
             {
                 var msg = string.Format("No provider metadata found for {0} season {1} episode {2}", series.Name, seasonNumber, episodeNumber);
                 var msg = string.Format("No provider metadata found for {0} season {1} episode {2}", series.Name, seasonNumber, episodeNumber);
                 _logger.Warn(msg);
                 _logger.Warn(msg);
-                //throw new Exception(msg);
+                return null;
             }
             }
-            else
-            {
-                episodeName = episode.Name;
-			}
 
 
-			seasonNumber = seasonNumber ?? episode.ParentIndexNumber;
-			episodeNumber = episodeNumber ?? episode.IndexNumber;
+            var episodeName = episode.Name;
+
+            //if (string.IsNullOrWhiteSpace(episodeName))
+            //{
+            //    var msg = string.Format("No provider metadata found for {0} season {1} episode {2}", series.Name, seasonNumber, episodeNumber);
+            //    _logger.Warn(msg);
+            //    return null;
+            //}
+
+            seasonNumber = seasonNumber ?? episode.ParentIndexNumber;
+            episodeNumber = episodeNumber ?? episode.IndexNumber;
 
 
             var newPath = GetSeasonFolderPath(series, seasonNumber.Value, options);
             var newPath = GetSeasonFolderPath(series, seasonNumber.Value, options);
 
 
@@ -579,7 +584,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
         {
         {
             seriesName = _fileSystem.GetValidFilename(seriesName).Trim();
             seriesName = _fileSystem.GetValidFilename(seriesName).Trim();
 
 
-            if (string.IsNullOrEmpty(episodeTitle))
+            if (string.IsNullOrWhiteSpace(episodeTitle))
             {
             {
                 episodeTitle = string.Empty;
                 episodeTitle = string.Empty;
             }
             }

+ 31 - 3
MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs

@@ -256,6 +256,25 @@ namespace MediaBrowser.Server.Implementations.HttpServer
             }
             }
         }
         }
 
 
+        private readonly Dictionary<string, int> _skipLogExtensions = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase)
+        {
+            {".js", 0},
+            {".css", 0},
+            {".woff", 0},
+            {".woff2", 0},
+            {".ttf", 0},
+            {".html", 0}
+        };
+
+        private bool EnableLogging(string url)
+        {
+            var parts = url.Split(new[] { '?' }, 2);
+
+            var extension = Path.GetExtension(parts[0]);
+
+            return string.IsNullOrWhiteSpace(extension) || !_skipLogExtensions.ContainsKey(extension);
+        }
+
         /// <summary>
         /// <summary>
         /// Overridable method that can be used to implement a custom hnandler
         /// Overridable method that can be used to implement a custom hnandler
         /// </summary>
         /// </summary>
@@ -271,6 +290,14 @@ namespace MediaBrowser.Server.Implementations.HttpServer
             var operationName = httpReq.OperationName;
             var operationName = httpReq.OperationName;
             var localPath = url.LocalPath;
             var localPath = url.LocalPath;
 
 
+            var urlString = url.OriginalString;
+            var enableLog = EnableLogging(urlString);
+
+            if (enableLog)
+            {
+                LoggerUtils.LogRequest(_logger, urlString, httpReq.HttpMethod, httpReq.UserAgent);
+            }
+            
             if (string.Equals(localPath, "/mediabrowser/", StringComparison.OrdinalIgnoreCase) ||
             if (string.Equals(localPath, "/mediabrowser/", StringComparison.OrdinalIgnoreCase) ||
                 string.Equals(localPath, "/emby/", StringComparison.OrdinalIgnoreCase))
                 string.Equals(localPath, "/emby/", StringComparison.OrdinalIgnoreCase))
             {
             {
@@ -333,15 +360,16 @@ namespace MediaBrowser.Server.Implementations.HttpServer
                 task.ContinueWith(x => httpRes.Close(), TaskContinuationOptions.OnlyOnRanToCompletion | TaskContinuationOptions.AttachedToParent);
                 task.ContinueWith(x => httpRes.Close(), TaskContinuationOptions.OnlyOnRanToCompletion | TaskContinuationOptions.AttachedToParent);
                 //Matches Exceptions handled in HttpListenerBase.InitTask()
                 //Matches Exceptions handled in HttpListenerBase.InitTask()
 
 
-                var urlString = url.ToString();
-
                 task.ContinueWith(x =>
                 task.ContinueWith(x =>
                 {
                 {
                     var statusCode = httpRes.StatusCode;
                     var statusCode = httpRes.StatusCode;
 
 
                     var duration = DateTime.Now - date;
                     var duration = DateTime.Now - date;
 
 
-                    LoggerUtils.LogResponse(_logger, statusCode, urlString, remoteIp, duration);
+                    if (enableLog)
+                    {
+                        LoggerUtils.LogResponse(_logger, statusCode, urlString, remoteIp, duration);
+                    }
 
 
                 }, TaskContinuationOptions.None);
                 }, TaskContinuationOptions.None);
                 return task;
                 return task;

+ 19 - 0
MediaBrowser.Server.Implementations/HttpServer/LoggerUtils.cs

@@ -1,11 +1,30 @@
 using MediaBrowser.Model.Logging;
 using MediaBrowser.Model.Logging;
 using System;
 using System;
 using System.Globalization;
 using System.Globalization;
+using System.IO;
+using SocketHttpListener.Net;
 
 
 namespace MediaBrowser.Server.Implementations.HttpServer
 namespace MediaBrowser.Server.Implementations.HttpServer
 {
 {
     public static class LoggerUtils
     public static class LoggerUtils
     {
     {
+        /// <summary>
+        /// Logs the request.
+        /// </summary>
+        /// <param name="logger">The logger.</param>
+        /// <param name="request">The request.</param>
+        public static void LogRequest(ILogger logger, HttpListenerRequest request)
+        {
+            var url = request.Url.ToString();
+
+            logger.Info("{0} {1}. UserAgent: {2}", (request.IsWebSocketRequest ? "WS" : "HTTP " + request.HttpMethod), url, request.UserAgent ?? string.Empty);
+        }
+
+        public static void LogRequest(ILogger logger, string url, string method, string userAgent)
+        {
+            logger.Info("{0} {1}. UserAgent: {2}", ("HTTP " + method), url, userAgent ?? string.Empty);
+        }
+
         /// <summary>
         /// <summary>
         /// Logs the response.
         /// Logs the response.
         /// </summary>
         /// </summary>

+ 2 - 40
MediaBrowser.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpListener.cs

@@ -78,10 +78,10 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp
         {
         {
             var request = context.Request;
             var request = context.Request;
 
 
-            LogRequest(_logger, request);
-
             if (request.IsWebSocketRequest)
             if (request.IsWebSocketRequest)
             {
             {
+                LoggerUtils.LogRequest(_logger, request);
+
                 ProcessWebSocketRequest(context);
                 ProcessWebSocketRequest(context);
                 return Task.FromResult(true);
                 return Task.FromResult(true);
             }
             }
@@ -156,44 +156,6 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp
             return req;
             return req;
         }
         }
 
 
-        /// <summary>
-        /// Logs the request.
-        /// </summary>
-        /// <param name="logger">The logger.</param>
-        /// <param name="request">The request.</param>
-        private static void LogRequest(ILogger logger, HttpListenerRequest request)
-        {
-            var url = request.Url.ToString();
-            var extension = Path.GetExtension(url);
-
-            if (string.Equals(extension, ".js", StringComparison.OrdinalIgnoreCase))
-            {
-                return;
-            }
-            if (string.Equals(extension, ".css", StringComparison.OrdinalIgnoreCase))
-            {
-                return;
-            }
-            if (string.Equals(extension, ".woff", StringComparison.OrdinalIgnoreCase))
-            {
-                return;
-            }
-            if (string.Equals(extension, ".woff2", StringComparison.OrdinalIgnoreCase))
-            {
-                return;
-            }
-            if (string.Equals(extension, ".ttf", StringComparison.OrdinalIgnoreCase))
-            {
-                return;
-            }
-            if (string.Equals(extension, ".html", StringComparison.OrdinalIgnoreCase))
-            {
-                return;
-            }
-
-            logger.Info("{0} {1}. UserAgent: {2}", (request.IsWebSocketRequest ? "WS" : "HTTP " + request.HttpMethod), url, request.UserAgent ?? string.Empty);
-        }
-
         private void HandleError(Exception ex, HttpListenerContext context)
         private void HandleError(Exception ex, HttpListenerContext context)
         {
         {
             var httpReq = GetRequest(context);
             var httpReq = GetRequest(context);

+ 9 - 3
MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs

@@ -471,11 +471,11 @@ namespace MediaBrowser.Server.Implementations.IO
             {
             {
                 if (_updateTimer == null)
                 if (_updateTimer == null)
                 {
                 {
-                    _updateTimer = new Timer(TimerStopped, null, TimeSpan.FromSeconds(ConfigurationManager.Configuration.RealtimeLibraryMonitorDelay), TimeSpan.FromMilliseconds(-1));
+                    _updateTimer = new Timer(TimerStopped, null, TimeSpan.FromSeconds(ConfigurationManager.Configuration.LibraryMonitorDelay), TimeSpan.FromMilliseconds(-1));
                 }
                 }
                 else
                 else
                 {
                 {
-                    _updateTimer.Change(TimeSpan.FromSeconds(ConfigurationManager.Configuration.RealtimeLibraryMonitorDelay), TimeSpan.FromMilliseconds(-1));
+                    _updateTimer.Change(TimeSpan.FromSeconds(ConfigurationManager.Configuration.LibraryMonitorDelay), TimeSpan.FromMilliseconds(-1));
                 }
                 }
             }
             }
         }
         }
@@ -513,12 +513,18 @@ namespace MediaBrowser.Server.Implementations.IO
 
 
         private bool IsFileLocked(string path)
         private bool IsFileLocked(string path)
         {
         {
+            if (Environment.OSVersion.Platform != PlatformID.Win32NT)
+            {
+                // Causing lockups on linux
+                return false;
+            }
+
             try
             try
             {
             {
                 var data = _fileSystem.GetFileSystemInfo(path);
                 var data = _fileSystem.GetFileSystemInfo(path);
 
 
                 if (!data.Exists
                 if (!data.Exists
-                    || data.Attributes.HasFlag(FileAttributes.Directory)
+                    || data.IsDirectory
 
 
                     // Opening a writable stream will fail with readonly files
                     // Opening a writable stream will fail with readonly files
                     || data.Attributes.HasFlag(FileAttributes.ReadOnly))
                     || data.Attributes.HasFlag(FileAttributes.ReadOnly))

+ 2 - 2
MediaBrowser.Server.Implementations/Library/LibraryManager.cs

@@ -222,7 +222,7 @@ namespace MediaBrowser.Server.Implementations.Library
         /// <summary>
         /// <summary>
         /// The _root folder
         /// The _root folder
         /// </summary>
         /// </summary>
-        private AggregateFolder _rootFolder;
+        private volatile AggregateFolder _rootFolder;
         /// <summary>
         /// <summary>
         /// The _root folder sync lock
         /// The _root folder sync lock
         /// </summary>
         /// </summary>
@@ -743,7 +743,7 @@ namespace MediaBrowser.Server.Implementations.Library
             return rootFolder;
             return rootFolder;
         }
         }
 
 
-        private UserRootFolder _userRootFolder;
+        private volatile UserRootFolder _userRootFolder;
         private readonly object _syncLock = new object();
         private readonly object _syncLock = new object();
         public Folder GetUserRootFolder()
         public Folder GetUserRootFolder()
         {
         {

+ 11 - 2
MediaBrowser.Server.Implementations/Library/Resolvers/PhotoResolver.cs

@@ -51,8 +51,17 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers
         {
         {
             var filename = Path.GetFileNameWithoutExtension(path) ?? string.Empty;
             var filename = Path.GetFileNameWithoutExtension(path) ?? string.Empty;
 
 
-            return !IgnoreFiles.Contains(filename, StringComparer.OrdinalIgnoreCase)
-                && imageProcessor.SupportedInputFormats.Contains((Path.GetExtension(path) ?? string.Empty).TrimStart('.'), StringComparer.OrdinalIgnoreCase);
+            if (IgnoreFiles.Contains(filename, StringComparer.OrdinalIgnoreCase))
+            {
+                return false;
+            }
+
+            if (IgnoreFiles.Any(i => filename.IndexOf("-" + i, StringComparison.OrdinalIgnoreCase) != -1))
+            {
+                return false;
+            }
+
+            return imageProcessor.SupportedInputFormats.Contains((Path.GetExtension(path) ?? string.Empty).TrimStart('.'), StringComparer.OrdinalIgnoreCase);
         }
         }
 
 
     }
     }

+ 8 - 1
MediaBrowser.Server.Implementations/Library/UserViewManager.cs

@@ -163,7 +163,14 @@ namespace MediaBrowser.Server.Implementations.Library
 
 
                 var channels = channelResult.Items;
                 var channels = channelResult.Items;
 
 
-                list.AddRange(channels);
+                if (user.Configuration.DisplayChannelsInline && channels.Length > 0)
+                {
+                    list.Add(await _channelManager.GetInternalChannelFolder(cancellationToken).ConfigureAwait(false));
+                }
+                else
+                {
+                    list.AddRange(channels);
+                }
 
 
                 if (_liveTvManager.GetEnabledUsers().Select(i => i.Id.ToString("N")).Contains(query.UserId))
                 if (_liveTvManager.GetEnabledUsers().Select(i => i.Id.ToString("N")).Contains(query.UserId))
                 {
                 {

+ 80 - 20
MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs

@@ -22,12 +22,15 @@ using MediaBrowser.Server.Implementations.FileOrganization;
 using System;
 using System;
 using System.Collections.Concurrent;
 using System.Collections.Concurrent;
 using System.Collections.Generic;
 using System.Collections.Generic;
+using System.Globalization;
 using System.IO;
 using System.IO;
 using System.Linq;
 using System.Linq;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using CommonIO;
 using CommonIO;
 using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.Extensions;
+using MediaBrowser.Controller.Power;
+using Microsoft.Win32;
 
 
 namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
 namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
 {
 {
@@ -55,7 +58,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
 
 
         public static EmbyTV Current;
         public static EmbyTV Current;
 
 
-        public EmbyTV(IApplicationHost appHost, ILogger logger, IJsonSerializer jsonSerializer, IHttpClient httpClient, IServerConfigurationManager config, ILiveTvManager liveTvManager, IFileSystem fileSystem, ISecurityManager security, ILibraryManager libraryManager, ILibraryMonitor libraryMonitor, IProviderManager providerManager, IFileOrganizationService organizationService, IMediaEncoder mediaEncoder)
+        public EmbyTV(IApplicationHost appHost, ILogger logger, IJsonSerializer jsonSerializer, IHttpClient httpClient, IServerConfigurationManager config, ILiveTvManager liveTvManager, IFileSystem fileSystem, ISecurityManager security, ILibraryManager libraryManager, ILibraryMonitor libraryMonitor, IProviderManager providerManager, IFileOrganizationService organizationService, IMediaEncoder mediaEncoder, IPowerManagement powerManagement)
         {
         {
             Current = this;
             Current = this;
 
 
@@ -75,13 +78,26 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
 
 
             _recordingProvider = new ItemDataProvider<RecordingInfo>(fileSystem, jsonSerializer, _logger, Path.Combine(DataPath, "recordings"), (r1, r2) => string.Equals(r1.Id, r2.Id, StringComparison.OrdinalIgnoreCase));
             _recordingProvider = new ItemDataProvider<RecordingInfo>(fileSystem, jsonSerializer, _logger, Path.Combine(DataPath, "recordings"), (r1, r2) => string.Equals(r1.Id, r2.Id, StringComparison.OrdinalIgnoreCase));
             _seriesTimerProvider = new SeriesTimerManager(fileSystem, jsonSerializer, _logger, Path.Combine(DataPath, "seriestimers"));
             _seriesTimerProvider = new SeriesTimerManager(fileSystem, jsonSerializer, _logger, Path.Combine(DataPath, "seriestimers"));
-            _timerProvider = new TimerManager(fileSystem, jsonSerializer, _logger, Path.Combine(DataPath, "timers"));
+            _timerProvider = new TimerManager(fileSystem, jsonSerializer, _logger, Path.Combine(DataPath, "timers"), powerManagement, _logger);
             _timerProvider.TimerFired += _timerProvider_TimerFired;
             _timerProvider.TimerFired += _timerProvider_TimerFired;
         }
         }
 
 
         public void Start()
         public void Start()
         {
         {
             _timerProvider.RestartTimers();
             _timerProvider.RestartTimers();
+
+            SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged;
+
+        }
+
+        void SystemEvents_PowerModeChanged(object sender, PowerModeChangedEventArgs e)
+        {
+            _logger.Info("Power mode changed to {0}", e.Mode);
+
+            if (e.Mode == PowerModes.Resume)
+            {
+                _timerProvider.RestartTimers();
+            }
         }
         }
 
 
         public event EventHandler DataSourceChanged;
         public event EventHandler DataSourceChanged;
@@ -155,7 +171,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
                 {
                 {
                     epgData = GetEpgDataForChannel(timer.ChannelId);
                     epgData = GetEpgDataForChannel(timer.ChannelId);
                 }
                 }
-                await UpdateTimersForSeriesTimer(epgData, timer).ConfigureAwait(false);
+                await UpdateTimersForSeriesTimer(epgData, timer, false).ConfigureAwait(false);
             }
             }
 
 
             var timers = await GetTimersAsync(cancellationToken).ConfigureAwait(false);
             var timers = await GetTimersAsync(cancellationToken).ConfigureAwait(false);
@@ -223,7 +239,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
 
 
         public Task CancelSeriesTimerAsync(string timerId, CancellationToken cancellationToken)
         public Task CancelSeriesTimerAsync(string timerId, CancellationToken cancellationToken)
         {
         {
-            var timers = _timerProvider.GetAll().Where(i => string.Equals(i.SeriesTimerId, timerId, StringComparison.OrdinalIgnoreCase));
+            var timers = _timerProvider
+                .GetAll()
+                .Where(i => string.Equals(i.SeriesTimerId, timerId, StringComparison.OrdinalIgnoreCase))
+                .ToList();
+
             foreach (var timer in timers)
             foreach (var timer in timers)
             {
             {
                 CancelTimerInternal(timer.Id);
                 CancelTimerInternal(timer.Id);
@@ -332,25 +352,44 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
             }
             }
 
 
             _seriesTimerProvider.Add(info);
             _seriesTimerProvider.Add(info);
-            await UpdateTimersForSeriesTimer(epgData, info).ConfigureAwait(false);
+            await UpdateTimersForSeriesTimer(epgData, info, false).ConfigureAwait(false);
         }
         }
 
 
         public async Task UpdateSeriesTimerAsync(SeriesTimerInfo info, CancellationToken cancellationToken)
         public async Task UpdateSeriesTimerAsync(SeriesTimerInfo info, CancellationToken cancellationToken)
         {
         {
-            _seriesTimerProvider.Update(info);
-            List<ProgramInfo> epgData;
-            if (info.RecordAnyChannel)
-            {
-                var channels = await GetChannelsAsync(true, CancellationToken.None).ConfigureAwait(false);
-                var channelIds = channels.Select(i => i.Id).ToList();
-                epgData = GetEpgDataForChannels(channelIds);
-            }
-            else
+            var instance = _seriesTimerProvider.GetAll().FirstOrDefault(i => string.Equals(i.Id, info.Id, StringComparison.OrdinalIgnoreCase));
+
+            if (instance != null)
             {
             {
-                epgData = GetEpgDataForChannel(info.ChannelId);
-            }
+                instance.ChannelId = info.ChannelId;
+                instance.Days = info.Days;
+                instance.EndDate = info.EndDate;
+                instance.IsPostPaddingRequired = info.IsPostPaddingRequired;
+                instance.IsPrePaddingRequired = info.IsPrePaddingRequired;
+                instance.PostPaddingSeconds = info.PostPaddingSeconds;
+                instance.PrePaddingSeconds = info.PrePaddingSeconds;
+                instance.Priority = info.Priority;
+                instance.RecordAnyChannel = info.RecordAnyChannel;
+                instance.RecordAnyTime = info.RecordAnyTime;
+                instance.RecordNewOnly = info.RecordNewOnly;
+                instance.StartDate = info.StartDate;
 
 
-            await UpdateTimersForSeriesTimer(epgData, info).ConfigureAwait(false);
+                _seriesTimerProvider.Update(instance);
+
+                List<ProgramInfo> epgData;
+                if (instance.RecordAnyChannel)
+                {
+                    var channels = await GetChannelsAsync(true, CancellationToken.None).ConfigureAwait(false);
+                    var channelIds = channels.Select(i => i.Id).ToList();
+                    epgData = GetEpgDataForChannels(channelIds);
+                }
+                else
+                {
+                    epgData = GetEpgDataForChannel(instance.ChannelId);
+                }
+
+                await UpdateTimersForSeriesTimer(epgData, instance, true).ConfigureAwait(false);
+            }
         }
         }
 
 
         public Task UpdateTimerAsync(TimerInfo info, CancellationToken cancellationToken)
         public Task UpdateTimerAsync(TimerInfo info, CancellationToken cancellationToken)
@@ -603,6 +642,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
                 {
                 {
                     await RecordStream(timer, recordingEndDate, cancellationTokenSource.Token).ConfigureAwait(false);
                     await RecordStream(timer, recordingEndDate, cancellationTokenSource.Token).ConfigureAwait(false);
                 }
                 }
+                else
+                {
+                    _logger.Info("Skipping RecordStream because it's already in progress.");
+                }
             }
             }
             catch (OperationCanceledException)
             catch (OperationCanceledException)
             {
             {
@@ -721,7 +764,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
                     recording.DateLastUpdated = DateTime.UtcNow;
                     recording.DateLastUpdated = DateTime.UtcNow;
                     _recordingProvider.AddOrUpdate(recording);
                     _recordingProvider.AddOrUpdate(recording);
 
 
-                    _logger.Info("Beginning recording.");
+                    _logger.Info("Beginning recording. Will record for {0} minutes.", duration.TotalMinutes.ToString(CultureInfo.InvariantCulture));
 
 
                     httpRequestOptions.BufferContent = false;
                     httpRequestOptions.BufferContent = false;
                     var durationToken = new CancellationTokenSource(duration);
                     var durationToken = new CancellationTokenSource(duration);
@@ -836,7 +879,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
             return _config.GetConfiguration<LiveTvOptions>("livetv");
             return _config.GetConfiguration<LiveTvOptions>("livetv");
         }
         }
 
 
-        private async Task UpdateTimersForSeriesTimer(List<ProgramInfo> epgData, SeriesTimerInfo seriesTimer)
+        private async Task UpdateTimersForSeriesTimer(List<ProgramInfo> epgData, SeriesTimerInfo seriesTimer, bool deleteInvalidTimers)
         {
         {
             var newTimers = GetTimersForSeries(seriesTimer, epgData, _recordingProvider.GetAll()).ToList();
             var newTimers = GetTimersForSeries(seriesTimer, epgData, _recordingProvider.GetAll()).ToList();
 
 
@@ -849,12 +892,29 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
                     _timerProvider.AddOrUpdate(timer);
                     _timerProvider.AddOrUpdate(timer);
                 }
                 }
             }
             }
+
+            if (deleteInvalidTimers)
+            {
+                var allTimers = GetTimersForSeries(seriesTimer, epgData, new List<RecordingInfo>())
+                    .Select(i => i.Id)
+                    .ToList();
+
+                var deletes = _timerProvider.GetAll()
+                    .Where(i => string.Equals(i.SeriesTimerId, seriesTimer.Id, StringComparison.OrdinalIgnoreCase))
+                    .Where(i => !allTimers.Contains(i.Id, StringComparer.OrdinalIgnoreCase) && i.StartDate > DateTime.UtcNow)
+                    .ToList();
+
+                foreach (var timer in deletes)
+                {
+                    await CancelTimerAsync(timer.Id, CancellationToken.None).ConfigureAwait(false);
+                }
+            }
         }
         }
 
 
         private IEnumerable<TimerInfo> GetTimersForSeries(SeriesTimerInfo seriesTimer, IEnumerable<ProgramInfo> allPrograms, IReadOnlyList<RecordingInfo> currentRecordings)
         private IEnumerable<TimerInfo> GetTimersForSeries(SeriesTimerInfo seriesTimer, IEnumerable<ProgramInfo> allPrograms, IReadOnlyList<RecordingInfo> currentRecordings)
         {
         {
             // Exclude programs that have already ended
             // Exclude programs that have already ended
-            allPrograms = allPrograms.Where(i => i.EndDate > DateTime.UtcNow);
+            allPrograms = allPrograms.Where(i => i.EndDate > DateTime.UtcNow && i.StartDate > DateTime.UtcNow);
 
 
             allPrograms = GetProgramsForSeries(seriesTimer, allPrograms);
             allPrograms = GetProgramsForSeries(seriesTimer, allPrograms);
 
 

+ 1 - 1
MediaBrowser.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs

@@ -13,7 +13,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
         where T : class
         where T : class
     {
     {
         private readonly object _fileDataLock = new object();
         private readonly object _fileDataLock = new object();
-        private List<T> _items;
+        private volatile List<T> _items;
         private readonly IJsonSerializer _jsonSerializer;
         private readonly IJsonSerializer _jsonSerializer;
         protected readonly ILogger Logger;
         protected readonly ILogger Logger;
         private readonly string _dataPath;
         private readonly string _dataPath;

+ 34 - 3
MediaBrowser.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs

@@ -5,22 +5,27 @@ using MediaBrowser.Model.Logging;
 using MediaBrowser.Model.Serialization;
 using MediaBrowser.Model.Serialization;
 using System;
 using System;
 using System.Collections.Concurrent;
 using System.Collections.Concurrent;
+using System.Globalization;
 using System.Linq;
 using System.Linq;
 using System.Threading;
 using System.Threading;
 using CommonIO;
 using CommonIO;
-using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.Power;
 
 
 namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
 namespace MediaBrowser.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 IPowerManagement _powerManagement;
+        private readonly ILogger _logger;
 
 
         public event EventHandler<GenericEventArgs<TimerInfo>> TimerFired;
         public event EventHandler<GenericEventArgs<TimerInfo>> TimerFired;
 
 
-        public TimerManager(IFileSystem fileSystem, IJsonSerializer jsonSerializer, ILogger logger, string dataPath)
+        public TimerManager(IFileSystem fileSystem, IJsonSerializer jsonSerializer, ILogger logger, string dataPath, IPowerManagement powerManagement, ILogger logger1)
             : base(fileSystem, jsonSerializer, logger, dataPath, (r1, r2) => string.Equals(r1.Id, r2.Id, StringComparison.OrdinalIgnoreCase))
             : base(fileSystem, jsonSerializer, logger, dataPath, (r1, r2) => string.Equals(r1.Id, r2.Id, StringComparison.OrdinalIgnoreCase))
         {
         {
+            _powerManagement = powerManagement;
+            _logger = logger1;
         }
         }
 
 
         public void RestartTimers()
         public void RestartTimers()
@@ -58,6 +63,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
             {
             {
                 var timespan = RecordingHelper.GetStartTime(item) - DateTime.UtcNow;
                 var timespan = RecordingHelper.GetStartTime(item) - DateTime.UtcNow;
                 timer.Change(timespan, TimeSpan.Zero);
                 timer.Change(timespan, TimeSpan.Zero);
+                ScheduleWake(item);
             }
             }
             else
             else
             {
             {
@@ -74,6 +80,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
 
 
             base.Add(item);
             base.Add(item);
             AddTimer(item);
             AddTimer(item);
+            ScheduleWake(item);
         }
         }
 
 
         private void AddTimer(TimerInfo item)
         private void AddTimer(TimerInfo item)
@@ -91,15 +98,39 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
             StartTimer(item, timerLength);
             StartTimer(item, timerLength);
         }
         }
 
 
+        private void ScheduleWake(TimerInfo info)
+        {
+            var startDate = RecordingHelper.GetStartTime(info).AddMinutes(-5);
+
+            try
+            {
+                _powerManagement.ScheduleWake(startDate);
+                _logger.Info("Scheduled system wake timer at {0} (UTC)", startDate);
+            }
+            catch (NotImplementedException)
+            {
+
+            }
+            catch (Exception ex)
+            {
+                _logger.ErrorException("Error scheduling wake timer", ex);
+            }
+        }
+
         public void StartTimer(TimerInfo item, TimeSpan length)
         public void StartTimer(TimerInfo item, TimeSpan length)
         {
         {
             StopTimer(item);
             StopTimer(item);
 
 
             var timer = new Timer(TimerCallback, item.Id, length, TimeSpan.Zero);
             var timer = new Timer(TimerCallback, item.Id, length, TimeSpan.Zero);
 
 
-            if (!_timers.TryAdd(item.Id, timer))
+            if (_timers.TryAdd(item.Id, timer))
+            {
+                _logger.Info("Creating recording timer for {0}, {1}. Timer will fire in {2} minutes", item.Id, item.Name, length.TotalMinutes.ToString(CultureInfo.InvariantCulture));
+            }
+            else
             {
             {
                 timer.Dispose();
                 timer.Dispose();
+                _logger.Warn("Timer already exists for item {0}", item.Id);
             }
             }
         }
         }
 
 

+ 1 - 0
MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs

@@ -1954,6 +1954,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
 
 
             await service.CreateTimerAsync(info, cancellationToken).ConfigureAwait(false);
             await service.CreateTimerAsync(info, cancellationToken).ConfigureAwait(false);
             _lastRecordingRefreshTime = DateTime.MinValue;
             _lastRecordingRefreshTime = DateTime.MinValue;
+            _logger.Info("New recording scheduled");
         }
         }
 
 
         public async Task CreateSeriesTimer(SeriesTimerInfoDto timer, CancellationToken cancellationToken)
         public async Task CreateSeriesTimer(SeriesTimerInfoDto timer, CancellationToken cancellationToken)

+ 11 - 11
MediaBrowser.Server.Implementations/Localization/Core/ca.json

@@ -1,6 +1,6 @@
 {
 {
-    "AppDeviceValues": "App: {0}, Device: {1}",
-    "UserDownloadingItemWithValues": "{0} is downloading {1}",
+    "AppDeviceValues": "App: {0}, Dispositiu: {1}",
+    "UserDownloadingItemWithValues": "{0} est\u00e0 descarregant {1}",
     "FolderTypeMixed": "Mixed content",
     "FolderTypeMixed": "Mixed content",
     "FolderTypeMovies": "Pel\u00b7l\u00edcules",
     "FolderTypeMovies": "Pel\u00b7l\u00edcules",
     "FolderTypeMusic": "M\u00fasica",
     "FolderTypeMusic": "M\u00fasica",
@@ -12,10 +12,10 @@
     "FolderTypeBooks": "Llibres",
     "FolderTypeBooks": "Llibres",
     "FolderTypeTvShows": "TV",
     "FolderTypeTvShows": "TV",
     "FolderTypeInherit": "Heretat",
     "FolderTypeInherit": "Heretat",
-    "HeaderCastCrew": "Repartiment i equip",
+    "HeaderCastCrew": "Repartiment i Equip",
     "HeaderPeople": "People",
     "HeaderPeople": "People",
     "ValueSpecialEpisodeName": "Special - {0}",
     "ValueSpecialEpisodeName": "Special - {0}",
-    "LabelChapterName": "Chapter {0}",
+    "LabelChapterName": "Cap\u00edtol {0}",
     "NameSeasonNumber": "Temporada {0}",
     "NameSeasonNumber": "Temporada {0}",
     "LabelExit": "Sortir",
     "LabelExit": "Sortir",
     "LabelVisitCommunity": "Visita la comunitat",
     "LabelVisitCommunity": "Visita la comunitat",
@@ -77,7 +77,7 @@
     "ViewTypeMovieMovies": "Pel\u00b7l\u00edcules",
     "ViewTypeMovieMovies": "Pel\u00b7l\u00edcules",
     "ViewTypeMovieCollections": "Col\u00b7leccions",
     "ViewTypeMovieCollections": "Col\u00b7leccions",
     "ViewTypeMovieFavorites": "Preferides",
     "ViewTypeMovieFavorites": "Preferides",
-    "ViewTypeMovieGenres": "Genres",
+    "ViewTypeMovieGenres": "G\u00e8neres",
     "ViewTypeMusicLatest": "Novetats",
     "ViewTypeMusicLatest": "Novetats",
     "ViewTypeMusicPlaylists": "Llistes de reproducci\u00f3",
     "ViewTypeMusicPlaylists": "Llistes de reproducci\u00f3",
     "ViewTypeMusicAlbums": "\u00c0lbums",
     "ViewTypeMusicAlbums": "\u00c0lbums",
@@ -89,7 +89,7 @@
     "ViewTypeMusicFavoriteArtists": "Artistes Preferits",
     "ViewTypeMusicFavoriteArtists": "Artistes Preferits",
     "ViewTypeMusicFavoriteSongs": "Can\u00e7ons Preferides",
     "ViewTypeMusicFavoriteSongs": "Can\u00e7ons Preferides",
     "ViewTypeFolders": "Directoris",
     "ViewTypeFolders": "Directoris",
-    "ViewTypeLiveTvRecordingGroups": "Recordings",
+    "ViewTypeLiveTvRecordingGroups": "Enregistraments",
     "ViewTypeLiveTvChannels": "Canals",
     "ViewTypeLiveTvChannels": "Canals",
     "ScheduledTaskFailedWithName": "{0} failed",
     "ScheduledTaskFailedWithName": "{0} failed",
     "LabelRunningTimeValue": "Running time: {0}",
     "LabelRunningTimeValue": "Running time: {0}",
@@ -103,7 +103,7 @@
     "LabelIpAddressValue": "Ip address: {0}",
     "LabelIpAddressValue": "Ip address: {0}",
     "DeviceOnlineWithName": "{0} is connected",
     "DeviceOnlineWithName": "{0} is connected",
     "UserOnlineFromDevice": "{0} is online from {1}",
     "UserOnlineFromDevice": "{0} is online from {1}",
-    "ProviderValue": "Provider: {0}",
+    "ProviderValue": "Prove\u00efdor: {0}",
     "SubtitlesDownloadedForItem": "Subtitles downloaded for {0}",
     "SubtitlesDownloadedForItem": "Subtitles downloaded for {0}",
     "UserConfigurationUpdatedWithName": "User configuration has been updated for {0}",
     "UserConfigurationUpdatedWithName": "User configuration has been updated for {0}",
     "UserCreatedWithName": "User {0} has been created",
     "UserCreatedWithName": "User {0} has been created",
@@ -113,12 +113,12 @@
     "MessageNamedServerConfigurationUpdatedWithValue": "Server configuration section {0} has been updated",
     "MessageNamedServerConfigurationUpdatedWithValue": "Server configuration section {0} has been updated",
     "MessageApplicationUpdated": "Emby Server has been updated",
     "MessageApplicationUpdated": "Emby Server has been updated",
     "FailedLoginAttemptWithUserName": "Failed login attempt from {0}",
     "FailedLoginAttemptWithUserName": "Failed login attempt from {0}",
-    "AuthenticationSucceededWithUserName": "{0} successfully authenticated",
+    "AuthenticationSucceededWithUserName": "{0} autenticat correctament",
     "DeviceOfflineWithName": "{0} has disconnected",
     "DeviceOfflineWithName": "{0} has disconnected",
     "UserLockedOutWithName": "User {0} has been locked out",
     "UserLockedOutWithName": "User {0} has been locked out",
     "UserOfflineFromDevice": "{0} has disconnected from {1}",
     "UserOfflineFromDevice": "{0} has disconnected from {1}",
-    "UserStartedPlayingItemWithValues": "{0} has started playing {1}",
-    "UserStoppedPlayingItemWithValues": "{0} has stopped playing {1}",
+    "UserStartedPlayingItemWithValues": "{0} ha comen\u00e7at a reproduir {1}",
+    "UserStoppedPlayingItemWithValues": "{0} ha parat de reproduir {1}",
     "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}",
     "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}",
     "HeaderUnidentified": "Unidentified",
     "HeaderUnidentified": "Unidentified",
     "HeaderImagePrimary": "Primary",
     "HeaderImagePrimary": "Primary",
@@ -164,7 +164,7 @@
     "HeaderTracks": "Tracks",
     "HeaderTracks": "Tracks",
     "HeaderMusicArtist": "M\u00fasic",
     "HeaderMusicArtist": "M\u00fasic",
     "HeaderLocked": "Locked",
     "HeaderLocked": "Locked",
-    "HeaderStudios": "Studios",
+    "HeaderStudios": "Estudis",
     "HeaderActor": "Actors",
     "HeaderActor": "Actors",
     "HeaderComposer": "Compositors",
     "HeaderComposer": "Compositors",
     "HeaderDirector": "Directors",
     "HeaderDirector": "Directors",

+ 2 - 2
MediaBrowser.Server.Implementations/Localization/Core/nl.json

@@ -63,7 +63,7 @@
     "ViewTypeLatestGames": "Nieuwste games",
     "ViewTypeLatestGames": "Nieuwste games",
     "ViewTypeRecentlyPlayedGames": "Recent gespeelt",
     "ViewTypeRecentlyPlayedGames": "Recent gespeelt",
     "ViewTypeGameFavorites": "Favorieten",
     "ViewTypeGameFavorites": "Favorieten",
-    "ViewTypeGameSystems": "Gam systemen",
+    "ViewTypeGameSystems": "Game systemen",
     "ViewTypeGameGenres": "Genres",
     "ViewTypeGameGenres": "Genres",
     "ViewTypeTvResume": "Hervatten",
     "ViewTypeTvResume": "Hervatten",
     "ViewTypeTvNextUp": "Volgende",
     "ViewTypeTvNextUp": "Volgende",
@@ -147,7 +147,7 @@
     "HeaderCommunityRating": "Gemeenschap cijfer",
     "HeaderCommunityRating": "Gemeenschap cijfer",
     "HeaderTrailers": "Trailers",
     "HeaderTrailers": "Trailers",
     "HeaderSpecials": "Specials",
     "HeaderSpecials": "Specials",
-    "HeaderGameSystems": "Spel systemen",
+    "HeaderGameSystems": "Game systemen",
     "HeaderPlayers": "Spelers:",
     "HeaderPlayers": "Spelers:",
     "HeaderAlbumArtists": "Album artiesten",
     "HeaderAlbumArtists": "Album artiesten",
     "HeaderAlbums": "Albums",
     "HeaderAlbums": "Albums",

+ 2 - 2
MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj

@@ -48,9 +48,9 @@
     <Reference Include="Interfaces.IO">
     <Reference Include="Interfaces.IO">
       <HintPath>..\packages\Interfaces.IO.1.0.0.5\lib\portable-net45+sl4+wp71+win8+wpa81\Interfaces.IO.dll</HintPath>
       <HintPath>..\packages\Interfaces.IO.1.0.0.5\lib\portable-net45+sl4+wp71+win8+wpa81\Interfaces.IO.dll</HintPath>
     </Reference>
     </Reference>
-    <Reference Include="MediaBrowser.Naming, Version=1.0.5818.23111, Culture=neutral, processorArchitecture=MSIL">
+    <Reference Include="MediaBrowser.Naming, Version=1.0.5869.26812, Culture=neutral, processorArchitecture=MSIL">
       <SpecificVersion>False</SpecificVersion>
       <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\MediaBrowser.Naming.1.0.0.41\lib\portable-net45+sl4+wp71+win8+wpa81\MediaBrowser.Naming.dll</HintPath>
+      <HintPath>..\packages\MediaBrowser.Naming.1.0.0.44\lib\portable-net45+sl4+wp71+win8+wpa81\MediaBrowser.Naming.dll</HintPath>
     </Reference>
     </Reference>
     <Reference Include="MoreLinq">
     <Reference Include="MoreLinq">
       <HintPath>..\packages\morelinq.1.4.0\lib\net35\MoreLinq.dll</HintPath>
       <HintPath>..\packages\morelinq.1.4.0\lib\net35\MoreLinq.dll</HintPath>

+ 3 - 3
MediaBrowser.Server.Implementations/News/NewsEntryPoint.cs

@@ -1,6 +1,5 @@
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.IO;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Notifications;
 using MediaBrowser.Controller.Notifications;
@@ -17,12 +16,13 @@ using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using System.Xml;
 using System.Xml;
 using CommonIO;
 using CommonIO;
+using MediaBrowser.Common.Threading;
 
 
 namespace MediaBrowser.Server.Implementations.News
 namespace MediaBrowser.Server.Implementations.News
 {
 {
     public class NewsEntryPoint : IServerEntryPoint
     public class NewsEntryPoint : IServerEntryPoint
     {
     {
-        private Timer _timer;
+        private PeriodicTimer _timer;
         private readonly IHttpClient _httpClient;
         private readonly IHttpClient _httpClient;
         private readonly IApplicationPaths _appPaths;
         private readonly IApplicationPaths _appPaths;
         private readonly IFileSystem _fileSystem;
         private readonly IFileSystem _fileSystem;
@@ -47,7 +47,7 @@ namespace MediaBrowser.Server.Implementations.News
 
 
         public void Run()
         public void Run()
         {
         {
-            _timer = new Timer(OnTimerFired, null, TimeSpan.FromMilliseconds(500), _frequency);
+            _timer = new PeriodicTimer(OnTimerFired, null, TimeSpan.FromMilliseconds(500), _frequency);
         }
         }
 
 
         /// <summary>
         /// <summary>

+ 1 - 1
MediaBrowser.Server.Implementations/packages.config

@@ -2,7 +2,7 @@
 <packages>
 <packages>
   <package id="CommonIO" version="1.0.0.7" targetFramework="net45" />
   <package id="CommonIO" version="1.0.0.7" targetFramework="net45" />
   <package id="Interfaces.IO" version="1.0.0.5" targetFramework="net45" />
   <package id="Interfaces.IO" version="1.0.0.5" targetFramework="net45" />
-  <package id="MediaBrowser.Naming" version="1.0.0.41" targetFramework="net45" />
+  <package id="MediaBrowser.Naming" version="1.0.0.44" targetFramework="net45" />
   <package id="Mono.Nat" version="1.2.24.0" targetFramework="net45" />
   <package id="Mono.Nat" version="1.2.24.0" targetFramework="net45" />
   <package id="morelinq" version="1.4.0" targetFramework="net45" />
   <package id="morelinq" version="1.4.0" targetFramework="net45" />
   <package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" />
   <package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" />

+ 14 - 0
MediaBrowser.Server.Mono/Native/BaseMonoApp.cs

@@ -8,6 +8,7 @@ using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Reflection;
 using System.Reflection;
 using System.Text.RegularExpressions;
 using System.Text.RegularExpressions;
+using MediaBrowser.Controller.Power;
 
 
 namespace MediaBrowser.Server.Mono.Native
 namespace MediaBrowser.Server.Mono.Native
 {
 {
@@ -203,5 +204,18 @@ namespace MediaBrowser.Server.Mono.Native
             public string sysname = string.Empty;
             public string sysname = string.Empty;
             public string machine = string.Empty;
             public string machine = string.Empty;
         }
         }
+
+        public IPowerManagement GetPowerManagement()
+        {
+            return new NullPowerManagement();
+        }
+    }
+
+    public class NullPowerManagement : IPowerManagement
+    {
+        public void ScheduleWake(DateTime utcTime)
+        {
+            throw new NotImplementedException();
+        }
     }
     }
 }
 }

+ 23 - 17
MediaBrowser.Server.Startup.Common/ApplicationHost.cs

@@ -210,7 +210,6 @@ namespace MediaBrowser.Server.Startup.Common
         private readonly string _releaseAssetFilename;
         private readonly string _releaseAssetFilename;
 
 
         internal INativeApp NativeApp { get; set; }
         internal INativeApp NativeApp { get; set; }
-        private Timer _ipAddressCacheTimer;
 
 
         /// <summary>
         /// <summary>
         /// Initializes a new instance of the <see cref="ApplicationHost" /> class.
         /// Initializes a new instance of the <see cref="ApplicationHost" /> class.
@@ -234,8 +233,6 @@ namespace MediaBrowser.Server.Startup.Common
             NativeApp = nativeApp;
             NativeApp = nativeApp;
 
 
             SetBaseExceptionMessage();
             SetBaseExceptionMessage();
-
-            _ipAddressCacheTimer = new Timer(OnCacheClearTimerFired, null, TimeSpan.FromMinutes(3), TimeSpan.FromMinutes(3));
         }
         }
 
 
         private Version _version;
         private Version _version;
@@ -533,6 +530,8 @@ namespace MediaBrowser.Server.Startup.Common
             EncodingManager = new EncodingManager(FileSystemManager, Logger, MediaEncoder, ChapterManager);
             EncodingManager = new EncodingManager(FileSystemManager, Logger, MediaEncoder, ChapterManager);
             RegisterSingleInstance(EncodingManager);
             RegisterSingleInstance(EncodingManager);
 
 
+            RegisterSingleInstance(NativeApp.GetPowerManagement());
+
             var sharingRepo = new SharingRepository(LogManager, ApplicationPaths);
             var sharingRepo = new SharingRepository(LogManager, ApplicationPaths);
             await sharingRepo.Initialize().ConfigureAwait(false);
             await sharingRepo.Initialize().ConfigureAwait(false);
             RegisterSingleInstance<ISharingManager>(new SharingManager(sharingRepo, ServerConfigurationManager, LibraryManager, this));
             RegisterSingleInstance<ISharingManager>(new SharingManager(sharingRepo, ServerConfigurationManager, LibraryManager, this));
@@ -970,10 +969,10 @@ namespace MediaBrowser.Server.Startup.Common
         {
         {
             get
             get
             {
             {
-				if (!ServerConfigurationManager.Configuration.EnableAutoUpdate) 
-				{
-					return false;
-				}
+                if (!ServerConfigurationManager.Configuration.EnableAutoUpdate)
+                {
+                    return false;
+                }
 #if DEBUG
 #if DEBUG
                 return false;
                 return false;
 #endif
 #endif
@@ -1157,7 +1156,12 @@ namespace MediaBrowser.Server.Startup.Common
         }
         }
 
 
         private readonly ConcurrentDictionary<string, bool> _validAddressResults = new ConcurrentDictionary<string, bool>(StringComparer.OrdinalIgnoreCase);
         private readonly ConcurrentDictionary<string, bool> _validAddressResults = new ConcurrentDictionary<string, bool>(StringComparer.OrdinalIgnoreCase);
+        private DateTime _lastAddressCacheClear;
         private bool IsIpAddressValid(IPAddress address)
         private bool IsIpAddressValid(IPAddress address)
+        {
+            return IsIpAddressValidInternal(address).Result;
+        }
+        private async Task<bool> IsIpAddressValidInternal(IPAddress address)
         {
         {
             if (IPAddress.IsLoopback(address))
             if (IPAddress.IsLoopback(address))
             {
             {
@@ -1167,6 +1171,12 @@ namespace MediaBrowser.Server.Startup.Common
             var apiUrl = GetLocalApiUrl(address.ToString());
             var apiUrl = GetLocalApiUrl(address.ToString());
             apiUrl += "/system/ping";
             apiUrl += "/system/ping";
 
 
+            if ((DateTime.UtcNow - _lastAddressCacheClear).TotalMinutes >= 5)
+            {
+                _lastAddressCacheClear = DateTime.UtcNow;
+                _validAddressResults.Clear();
+            }
+
             bool cachedResult;
             bool cachedResult;
             if (_validAddressResults.TryGetValue(apiUrl, out cachedResult))
             if (_validAddressResults.TryGetValue(apiUrl, out cachedResult))
             {
             {
@@ -1175,14 +1185,15 @@ namespace MediaBrowser.Server.Startup.Common
 
 
             try
             try
             {
             {
-                using (var response = HttpClient.SendAsync(new HttpRequestOptions
+                using (var response = await HttpClient.SendAsync(new HttpRequestOptions
                 {
                 {
                     Url = apiUrl,
                     Url = apiUrl,
                     LogErrorResponseBody = false,
                     LogErrorResponseBody = false,
                     LogErrors = false,
                     LogErrors = false,
-                    LogRequest = false
+                    LogRequest = false,
+                    TimeoutMs = 30000
 
 
-                }, "POST").Result)
+                }, "POST").ConfigureAwait(false))
                 {
                 {
                     using (var reader = new StreamReader(response.Content))
                     using (var reader = new StreamReader(response.Content))
                     {
                     {
@@ -1190,25 +1201,20 @@ namespace MediaBrowser.Server.Startup.Common
                         var valid = string.Equals(Name, result, StringComparison.OrdinalIgnoreCase);
                         var valid = string.Equals(Name, result, StringComparison.OrdinalIgnoreCase);
 
 
                         _validAddressResults.AddOrUpdate(apiUrl, valid, (k, v) => valid);
                         _validAddressResults.AddOrUpdate(apiUrl, valid, (k, v) => valid);
-                        Logger.Debug("Ping test result to {0}. Success: {1}", apiUrl, valid);
+                        //Logger.Debug("Ping test result to {0}. Success: {1}", apiUrl, valid);
                         return valid;
                         return valid;
                     }
                     }
                 }
                 }
             }
             }
             catch
             catch
             {
             {
-                Logger.Debug("Ping test result to {0}. Success: {1}", apiUrl, false);
+                //Logger.Debug("Ping test result to {0}. Success: {1}", apiUrl, false);
 
 
                 _validAddressResults.AddOrUpdate(apiUrl, false, (k, v) => false);
                 _validAddressResults.AddOrUpdate(apiUrl, false, (k, v) => false);
                 return false;
                 return false;
             }
             }
         }
         }
 
 
-        private void OnCacheClearTimerFired(object state)
-        {
-            _validAddressResults.Clear();
-        }
-
         public string FriendlyName
         public string FriendlyName
         {
         {
             get
             get

+ 3 - 3
MediaBrowser.Server.Startup.Common/EntryPoints/KeepServerAwake.cs

@@ -4,7 +4,7 @@ using MediaBrowser.Controller.Session;
 using MediaBrowser.Model.Logging;
 using MediaBrowser.Model.Logging;
 using System;
 using System;
 using System.Linq;
 using System.Linq;
-using System.Threading;
+using MediaBrowser.Common.Threading;
 
 
 namespace MediaBrowser.Server.Startup.Common.EntryPoints
 namespace MediaBrowser.Server.Startup.Common.EntryPoints
 {
 {
@@ -12,7 +12,7 @@ namespace MediaBrowser.Server.Startup.Common.EntryPoints
     {
     {
         private readonly ISessionManager _sessionManager;
         private readonly ISessionManager _sessionManager;
         private readonly ILogger _logger;
         private readonly ILogger _logger;
-        private Timer _timer;
+        private PeriodicTimer _timer;
         private readonly IServerApplicationHost _appHost;
         private readonly IServerApplicationHost _appHost;
 
 
         public KeepServerAwake(ISessionManager sessionManager, ILogger logger, IServerApplicationHost appHost)
         public KeepServerAwake(ISessionManager sessionManager, ILogger logger, IServerApplicationHost appHost)
@@ -24,7 +24,7 @@ namespace MediaBrowser.Server.Startup.Common.EntryPoints
 
 
         public void Run()
         public void Run()
         {
         {
-            _timer = new Timer(obj =>
+            _timer = new PeriodicTimer(obj =>
             {
             {
                 var now = DateTime.UtcNow;
                 var now = DateTime.UtcNow;
                 if (_sessionManager.Sessions.Any(i => (now - i.LastActivityDate).TotalMinutes < 15))
                 if (_sessionManager.Sessions.Any(i => (now - i.LastActivityDate).TotalMinutes < 15))

+ 7 - 0
MediaBrowser.Server.Startup.Common/INativeApp.cs

@@ -2,6 +2,7 @@
 using MediaBrowser.Model.Logging;
 using MediaBrowser.Model.Logging;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Reflection;
 using System.Reflection;
+using MediaBrowser.Controller.Power;
 
 
 namespace MediaBrowser.Server.Startup.Common
 namespace MediaBrowser.Server.Startup.Common
 {
 {
@@ -90,5 +91,11 @@ namespace MediaBrowser.Server.Startup.Common
         /// Prevents the system stand by.
         /// Prevents the system stand by.
         /// </summary>
         /// </summary>
         void PreventSystemStandby();
         void PreventSystemStandby();
+
+        /// <summary>
+        /// Gets the power management.
+        /// </summary>
+        /// <returns>IPowerManagement.</returns>
+        IPowerManagement GetPowerManagement();
     }
     }
 }
 }

+ 1 - 1
MediaBrowser.ServerApplication/MainStartup.cs

@@ -218,7 +218,7 @@ namespace MediaBrowser.ServerApplication
             var fileSystem = new WindowsFileSystem(new PatternsLogger(logManager.GetLogger("FileSystem")));
             var fileSystem = new WindowsFileSystem(new PatternsLogger(logManager.GetLogger("FileSystem")));
             fileSystem.AddShortcutHandler(new MbLinkShortcutHandler(fileSystem));
             fileSystem.AddShortcutHandler(new MbLinkShortcutHandler(fileSystem));
 
 
-            var nativeApp = new WindowsApp(fileSystem)
+            var nativeApp = new WindowsApp(fileSystem, _logger)
             {
             {
                 IsRunningAsService = runService
                 IsRunningAsService = runService
             };
             };

+ 1 - 0
MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj

@@ -120,6 +120,7 @@
     <Compile Include="Native\Standby.cs" />
     <Compile Include="Native\Standby.cs" />
     <Compile Include="Native\ServerAuthorization.cs" />
     <Compile Include="Native\ServerAuthorization.cs" />
     <Compile Include="Native\WindowsApp.cs" />
     <Compile Include="Native\WindowsApp.cs" />
+    <Compile Include="Native\WindowsPowerManagement.cs" />
     <Compile Include="Networking\CertificateGenerator.cs" />
     <Compile Include="Networking\CertificateGenerator.cs" />
     <Compile Include="Networking\NativeMethods.cs" />
     <Compile Include="Networking\NativeMethods.cs" />
     <Compile Include="Networking\NetworkManager.cs" />
     <Compile Include="Networking\NetworkManager.cs" />

+ 9 - 1
MediaBrowser.ServerApplication/Native/WindowsApp.cs

@@ -6,16 +6,19 @@ using MediaBrowser.ServerApplication.Networking;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Reflection;
 using System.Reflection;
 using CommonIO;
 using CommonIO;
+using MediaBrowser.Controller.Power;
 
 
 namespace MediaBrowser.ServerApplication.Native
 namespace MediaBrowser.ServerApplication.Native
 {
 {
     public class WindowsApp : INativeApp
     public class WindowsApp : INativeApp
     {
     {
         private readonly IFileSystem _fileSystem;
         private readonly IFileSystem _fileSystem;
+        private readonly ILogger _logger;
 
 
-        public WindowsApp(IFileSystem fileSystem)
+        public WindowsApp(IFileSystem fileSystem, ILogger logger)
         {
         {
             _fileSystem = fileSystem;
             _fileSystem = fileSystem;
+            _logger = logger;
         }
         }
 
 
         public List<Assembly> GetAssembliesWithParts()
         public List<Assembly> GetAssembliesWithParts()
@@ -117,5 +120,10 @@ namespace MediaBrowser.ServerApplication.Native
         {
         {
             Standby.PreventSystemStandby();
             Standby.PreventSystemStandby();
         }
         }
+
+        public IPowerManagement GetPowerManagement()
+        {
+            return new WindowsPowerManagement(_logger);
+        }
     }
     }
 }
 }

+ 94 - 0
MediaBrowser.ServerApplication/Native/WindowsPowerManagement.cs

@@ -0,0 +1,94 @@
+using System;
+using System.ComponentModel;
+using System.Runtime.InteropServices;
+using System.Threading;
+using MediaBrowser.Controller.Power;
+using MediaBrowser.Model.Logging;
+using Microsoft.Win32.SafeHandles;
+
+namespace MediaBrowser.ServerApplication.Native
+{
+    public class WindowsPowerManagement : IPowerManagement
+    {
+        [DllImport("kernel32.dll")]
+        public static extern SafeWaitHandle CreateWaitableTimer(IntPtr lpTimerAttributes,
+                                                                  bool bManualReset,
+                                                                string lpTimerName);
+
+        [DllImport("kernel32.dll", SetLastError = true)]
+        [return: MarshalAs(UnmanagedType.Bool)]
+        public static extern bool SetWaitableTimer(SafeWaitHandle hTimer,
+                                                    [In] ref long pDueTime,
+                                                              int lPeriod,
+                                                           IntPtr pfnCompletionRoutine,
+                                                           IntPtr lpArgToCompletionRoutine,
+                                                             bool fResume);
+
+        private BackgroundWorker _bgWorker;
+        private readonly ILogger _logger;
+        private readonly object _initLock = new object();
+
+        public WindowsPowerManagement(ILogger logger)
+        {
+            _logger = logger;
+        }
+
+        public void ScheduleWake(DateTime utcTime)
+        {
+            //Initialize();
+            //_bgWorker.RunWorkerAsync(utcTime.ToFileTime());
+            throw new NotImplementedException();
+        }
+
+        private void Initialize()
+        {
+            lock (_initLock)
+            {
+                if (_bgWorker == null)
+                {
+                    _bgWorker = new BackgroundWorker();
+
+                    _bgWorker.DoWork += bgWorker_DoWork;
+                    _bgWorker.RunWorkerCompleted += bgWorker_RunWorkerCompleted;
+                }
+            }
+        }
+
+        void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
+        {
+            //if (Woken != null)
+            //{
+            //    Woken(this, new EventArgs());
+            //}
+        }
+
+        private void bgWorker_DoWork(object sender, DoWorkEventArgs e)
+        {
+            try
+            {
+                long waketime = (long)e.Argument;
+
+                using (SafeWaitHandle handle = CreateWaitableTimer(IntPtr.Zero, true, GetType().Assembly.GetName().Name + "Timer"))
+                {
+                    if (SetWaitableTimer(handle, ref waketime, 0, IntPtr.Zero, IntPtr.Zero, true))
+                    {
+                        using (EventWaitHandle wh = new EventWaitHandle(false,
+                                                               EventResetMode.AutoReset))
+                        {
+                            wh.SafeWaitHandle = handle;
+                            wh.WaitOne();
+                        }
+                    }
+                    else
+                    {
+                        throw new Win32Exception(Marshal.GetLastWin32Error());
+                    }
+                }
+            }
+            catch (Exception ex)
+            {
+                _logger.ErrorException("Error scheduling wake timer", ex);
+            }
+        }
+    }
+}

+ 1 - 2
MediaBrowser.WebDashboard/Api/DashboardService.cs

@@ -354,8 +354,7 @@ namespace MediaBrowser.WebDashboard.Api
             DeleteFoldersByName(Path.Combine(bowerPath, "jstree"), "src");
             DeleteFoldersByName(Path.Combine(bowerPath, "jstree"), "src");
             DeleteFoldersByName(Path.Combine(bowerPath, "Sortable"), "meteor");
             DeleteFoldersByName(Path.Combine(bowerPath, "Sortable"), "meteor");
             DeleteFoldersByName(Path.Combine(bowerPath, "Sortable"), "st");
             DeleteFoldersByName(Path.Combine(bowerPath, "Sortable"), "st");
-            DeleteFoldersByName(Path.Combine(bowerPath, "swipebox"), "lib");
-            DeleteFoldersByName(Path.Combine(bowerPath, "swipebox"), "scss");
+            DeleteFoldersByName(Path.Combine(bowerPath, "Swiper"), "src");
 
 
             if (string.Equals(mode, "cordova", StringComparison.OrdinalIgnoreCase))
             if (string.Equals(mode, "cordova", StringComparison.OrdinalIgnoreCase))
             {
             {

+ 0 - 6
MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj

@@ -158,9 +158,6 @@
     <Content Include="dashboard-ui\components\metadataeditor\metadataeditor.template.html">
     <Content Include="dashboard-ui\components\metadataeditor\metadataeditor.template.html">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
     </Content>
-    <Content Include="dashboard-ui\components\paperdialoghelper.js">
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </Content>
     <Content Include="dashboard-ui\components\playlisteditor\playlisteditor.js">
     <Content Include="dashboard-ui\components\playlisteditor\playlisteditor.js">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
     </Content>
@@ -311,9 +308,6 @@
     <Content Include="dashboard-ui\scripts\supporterkeypage.js">
     <Content Include="dashboard-ui\scripts\supporterkeypage.js">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
     </Content>
-    <Content Include="dashboard-ui\components\testermessage.js">
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </Content>
     <Content Include="dashboard-ui\scripts\wizardlivetvguide.js">
     <Content Include="dashboard-ui\scripts\wizardlivetvguide.js">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
     </Content>

+ 11 - 4
MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs

@@ -215,7 +215,11 @@ namespace MediaBrowser.XbmcMetadata.Parsers
                         if (!string.IsNullOrWhiteSpace(val))
                         if (!string.IsNullOrWhiteSpace(val))
                         {
                         {
                             DateTime added;
                             DateTime added;
-                            if (DateTime.TryParse(val, out added))
+                            if (DateTime.TryParseExact(val, BaseNfoSaver.DateAddedFormat, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out added))
+                            {
+                                item.EndDate = added.ToUniversalTime();
+                            }
+                            else if (DateTime.TryParse(val, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out added))
                             {
                             {
                                 item.DateCreated = added.ToUniversalTime();
                                 item.DateCreated = added.ToUniversalTime();
                             }
                             }
@@ -627,7 +631,10 @@ namespace MediaBrowser.XbmcMetadata.Parsers
                         {
                         {
                             var person = GetPersonFromXmlNode(subtree);
                             var person = GetPersonFromXmlNode(subtree);
 
 
-                            itemResult.AddPerson(person);
+                            if (!string.IsNullOrWhiteSpace(person.Name))
+                            {
+                                itemResult.AddPerson(person);
+                            }
                         }
                         }
                         break;
                         break;
                     }
                     }
@@ -976,11 +983,11 @@ namespace MediaBrowser.XbmcMetadata.Parsers
                         if (!string.IsNullOrWhiteSpace(val) && !string.IsNullOrWhiteSpace(userDataUserId))
                         if (!string.IsNullOrWhiteSpace(val) && !string.IsNullOrWhiteSpace(userDataUserId))
                         {
                         {
                             DateTime parsedValue;
                             DateTime parsedValue;
-                            if (DateTime.TryParseExact(val, "yyyy-MM-dd HH:mm:ss", _usCulture, DateTimeStyles.None, out parsedValue))
+                            if (DateTime.TryParseExact(val, "yyyy-MM-dd HH:mm:ss", _usCulture, DateTimeStyles.AssumeLocal, out parsedValue))
                             {
                             {
                                 var userData = GetOrAdd(itemResult, userDataUserId);
                                 var userData = GetOrAdd(itemResult, userDataUserId);
 
 
-                                userData.LastPlayedDate = parsedValue;
+                                userData.LastPlayedDate = parsedValue.ToUniversalTime();
                             }
                             }
                         }
                         }
                         break;
                         break;

+ 4 - 2
MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs

@@ -416,6 +416,8 @@ namespace MediaBrowser.XbmcMetadata.Savers
             writer.WriteEndElement();
             writer.WriteEndElement();
         }
         }
 
 
+        public const string DateAddedFormat = "yyyy-MM-dd HH:mm:ss";
+
         /// <summary>
         /// <summary>
         /// Adds the common nodes.
         /// Adds the common nodes.
         /// </summary>
         /// </summary>
@@ -472,7 +474,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
                 writer.WriteElementString("type", item.DisplayMediaType);
                 writer.WriteElementString("type", item.DisplayMediaType);
             }
             }
 
 
-            writer.WriteElementString("dateadded", item.DateCreated.ToString("yyyy-MM-dd HH:mm:ss"));
+            writer.WriteElementString("dateadded", item.DateCreated.ToLocalTime().ToString(DateAddedFormat));
 
 
             writer.WriteElementString("title", item.Name ?? string.Empty);
             writer.WriteElementString("title", item.Name ?? string.Empty);
             writer.WriteElementString("originaltitle", item.Name ?? string.Empty);
             writer.WriteElementString("originaltitle", item.Name ?? string.Empty);
@@ -949,7 +951,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
 
 
                 if (userdata.LastPlayedDate.HasValue)
                 if (userdata.LastPlayedDate.HasValue)
                 {
                 {
-                    writer.WriteElementString("lastplayed", userdata.LastPlayedDate.Value.ToString("yyyy-MM-dd HH:mm:ss").ToLower());
+                    writer.WriteElementString("lastplayed", userdata.LastPlayedDate.Value.ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss").ToLower());
                 }
                 }
 
 
                 writer.WriteStartElement("resume");
                 writer.WriteStartElement("resume");