diff options
| author | Luke Pulverenti <luke.pulverenti@gmail.com> | 2016-02-01 14:24:15 -0500 |
|---|---|---|
| committer | Luke Pulverenti <luke.pulverenti@gmail.com> | 2016-02-01 14:24:15 -0500 |
| commit | 37352785acef4db99301c1e88624cf48133ec979 (patch) | |
| tree | 15638d697cc56a0a774bf261de9814c9034f8e42 | |
| parent | f14e9b8d3af98f6ffbe243b105f96f46e8cf3b06 (diff) | |
| parent | f5ebeddbf5104092ce584486689af6640125054f (diff) | |
Merge branch 'beta'
61 files changed, 835 insertions, 606 deletions
diff --git a/MediaBrowser.Api/EnvironmentService.cs b/MediaBrowser.Api/EnvironmentService.cs index 8fdfea6b4..4e88e946f 100644 --- a/MediaBrowser.Api/EnvironmentService.cs +++ b/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.Model.IO; using MediaBrowser.Model.Net; @@ -46,6 +45,11 @@ namespace MediaBrowser.Api /// <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")] public bool IncludeHidden { get; set; } + + public GetDirectoryContents() + { + IncludeHidden = true; + } } [Route("/Environment/NetworkShares", "GET", Summary = "Gets shares from a network device")] diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 9e2e399d7..bb7f6bb1e 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -2192,7 +2192,7 @@ namespace MediaBrowser.Api.Playback { if (string.Equals(state.OutputContainer, "mkv", StringComparison.OrdinalIgnoreCase)) { - inputModifier += " -noaccurate_seek"; + //inputModifier += " -noaccurate_seek"; } } diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs index 3b577ac01..2bf61f90b 100644 --- a/MediaBrowser.Api/Playback/MediaInfoService.cs +++ b/MediaBrowser.Api/Playback/MediaInfoService.cs @@ -141,10 +141,10 @@ namespace MediaBrowser.Api.Playback 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; } diff --git a/MediaBrowser.Api/StartupWizardService.cs b/MediaBrowser.Api/StartupWizardService.cs index afe3da710..b05a1bd20 100644 --- a/MediaBrowser.Api/StartupWizardService.cs +++ b/MediaBrowser.Api/StartupWizardService.cs @@ -66,13 +66,12 @@ namespace MediaBrowser.Api { _config.Configuration.IsStartupWizardCompleted = true; _config.Configuration.EnableLocalizedGuids = true; - _config.Configuration.MergeMetadataAndImagesByName = true; - _config.Configuration.EnableStandaloneMetadata = true; _config.Configuration.EnableLibraryMetadataSubFolder = true; _config.Configuration.EnableCustomPathSubFolders = true; _config.Configuration.DisableStartupScan = true; _config.Configuration.EnableUserViews = true; _config.Configuration.EnableDateLastRefresh = true; + _config.Configuration.MergeMetadataAndImagesByName = true; _config.SaveConfiguration(); } diff --git a/MediaBrowser.Api/UserLibrary/PlaystateService.cs b/MediaBrowser.Api/UserLibrary/PlaystateService.cs index 7002a3703..08c6b0ba4 100644 --- a/MediaBrowser.Api/UserLibrary/PlaystateService.cs +++ b/MediaBrowser.Api/UserLibrary/PlaystateService.cs @@ -370,6 +370,8 @@ namespace MediaBrowser.Api.UserLibrary public void Post(ReportPlaybackStopped request) { + Logger.Debug("ReportPlaybackStopped PlaySessionId: {0}", request.PlaySessionId ?? string.Empty); + if (!string.IsNullOrWhiteSpace(request.PlaySessionId)) { ApiEntryPoint.Instance.KillTranscodingJobs(AuthorizationContext.GetAuthorizationInfo(Request).DeviceId, request.PlaySessionId, s => true); diff --git a/MediaBrowser.Common.Implementations/Networking/BaseNetworkManager.cs b/MediaBrowser.Common.Implementations/Networking/BaseNetworkManager.cs index bc3dc360f..ff11c889a 100644 --- a/MediaBrowser.Common.Implementations/Networking/BaseNetworkManager.cs +++ b/MediaBrowser.Common.Implementations/Networking/BaseNetworkManager.cs @@ -6,7 +6,6 @@ using System.Linq; using System.Net; using System.Net.NetworkInformation; using System.Net.Sockets; -using System.Threading; using MoreLinq; namespace MediaBrowser.Common.Implementations.Networking @@ -14,22 +13,11 @@ namespace MediaBrowser.Common.Implementations.Networking public abstract class BaseNetworkManager { protected ILogger Logger { get; private set; } - private Timer _clearCacheTimer; + private DateTime _lastRefresh; protected BaseNetworkManager(ILogger 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; @@ -41,15 +29,21 @@ namespace MediaBrowser.Common.Implementations.Networking /// <returns>IPAddress.</returns> public IEnumerable<IPAddress> GetLocalIpAddresses() { - if (_localIpAddresses == null) + const int cacheMinutes = 3; + var forceRefresh = (DateTime.UtcNow - _lastRefresh).TotalMinutes >= cacheMinutes; + + if (_localIpAddresses == null || forceRefresh) { lock (_localIpAddressSyncLock) { - if (_localIpAddresses == null) + forceRefresh = (DateTime.UtcNow - _lastRefresh).TotalMinutes >= cacheMinutes; + + if (_localIpAddresses == null || forceRefresh) { var addresses = GetLocalIpAddressesInternal().ToList(); _localIpAddresses = addresses; + _lastRefresh = DateTime.UtcNow; return addresses; } diff --git a/MediaBrowser.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs b/MediaBrowser.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs index 95f29915d..a4ccbb6f8 100644 --- a/MediaBrowser.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs +++ b/MediaBrowser.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs @@ -233,7 +233,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks /// <summary> /// The _triggers /// </summary> - private IEnumerable<ITaskTrigger> _triggers; + private volatile List<ITaskTrigger> _triggers; /// <summary> /// The _triggers sync lock /// </summary> @@ -532,7 +532,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks /// Loads the triggers. /// </summary> /// <returns>IEnumerable{BaseTaskTrigger}.</returns> - private IEnumerable<ITaskTrigger> LoadTriggers() + private List<ITaskTrigger> LoadTriggers() { try { @@ -543,12 +543,12 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks catch (FileNotFoundException) { // File doesn't exist. No biggie. Return defaults. - return ScheduledTask.GetDefaultTriggers(); + return ScheduledTask.GetDefaultTriggers().ToList(); } catch (DirectoryNotFoundException) { // File doesn't exist. No biggie. Return defaults. - return ScheduledTask.GetDefaultTriggers(); + return ScheduledTask.GetDefaultTriggers().ToList(); } } diff --git a/MediaBrowser.Common/MediaBrowser.Common.csproj b/MediaBrowser.Common/MediaBrowser.Common.csproj index ccdb319fe..17f211d84 100644 --- a/MediaBrowser.Common/MediaBrowser.Common.csproj +++ b/MediaBrowser.Common/MediaBrowser.Common.csproj @@ -90,6 +90,7 @@ <Compile Include="Security\IRequiresRegistration.cs" /> <Compile Include="Security\ISecurityManager.cs" /> <Compile Include="Security\PaymentRequiredException.cs" /> + <Compile Include="Threading\PeriodicTimer.cs" /> <Compile Include="Updates\IInstallationManager.cs" /> <Compile Include="Updates\InstallationEventArgs.cs" /> <Compile Include="Updates\InstallationFailedEventArgs.cs" /> diff --git a/MediaBrowser.Common/Threading/PeriodicTimer.cs b/MediaBrowser.Common/Threading/PeriodicTimer.cs new file mode 100644 index 000000000..75ccada4e --- /dev/null +++ b/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(); + } + } +} diff --git a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs index 98d1eb4ce..654c9abd3 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs +++ b/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.Entities; using MediaBrowser.Model.Users; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Controller.Library; namespace MediaBrowser.Controller.Entities.Audio { /// <summary> /// Class MusicAlbum /// </summary> - public class MusicAlbum : Folder, IHasAlbumArtist, IHasArtist, IHasMusicGenres, IHasLookupInfo<AlbumInfo> + public class MusicAlbum : Folder, IHasAlbumArtist, IHasArtist, IHasMusicGenres, IHasLookupInfo<AlbumInfo>, IMetadataContainer { public MusicAlbum() { @@ -139,5 +143,58 @@ namespace MediaBrowser.Controller.Entities.Audio 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); + } } } diff --git a/MediaBrowser.Controller/Entities/User.cs b/MediaBrowser.Controller/Entities/User.cs index a9e314ede..be8521a5c 100644 --- a/MediaBrowser.Controller/Entities/User.cs +++ b/MediaBrowser.Controller/Entities/User.cs @@ -109,7 +109,7 @@ namespace MediaBrowser.Controller.Entities /// <value>The last activity date.</value> public DateTime? LastActivityDate { get; set; } - private UserConfiguration _config; + private volatile UserConfiguration _config; private readonly object _configSyncLock = new object(); [IgnoreDataMember] public UserConfiguration Configuration @@ -132,7 +132,7 @@ namespace MediaBrowser.Controller.Entities set { _config = value; } } - private UserPolicy _policy; + private volatile UserPolicy _policy; private readonly object _policySyncLock = new object(); [IgnoreDataMember] public UserPolicy Policy diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 2227df3f0..471aa38d4 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -265,6 +265,7 @@ <Compile Include="Playlists\IPlaylistManager.cs" /> <Compile Include="Playlists\Playlist.cs" /> <Compile Include="Plugins\ILocalizablePlugin.cs" /> + <Compile Include="Power\IPowerManagement.cs" /> <Compile Include="Providers\AlbumInfo.cs" /> <Compile Include="Providers\ArtistInfo.cs" /> <Compile Include="Providers\BookInfo.cs" /> diff --git a/MediaBrowser.Controller/Power/IPowerManagement.cs b/MediaBrowser.Controller/Power/IPowerManagement.cs new file mode 100644 index 000000000..faa289695 --- /dev/null +++ b/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); + } +} diff --git a/MediaBrowser.Dlna/PlayTo/Device.cs b/MediaBrowser.Dlna/PlayTo/Device.cs index 222a52736..1ec7a4ce0 100644 --- a/MediaBrowser.Dlna/PlayTo/Device.cs +++ b/MediaBrowser.Dlna/PlayTo/Device.cs @@ -22,14 +22,26 @@ namespace MediaBrowser.Dlna.PlayTo #region Fields & Properties private Timer _timer; - private Timer _volumeTimer; public DeviceInfo Properties { get; set; } private int _muteVol; 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; } @@ -93,11 +105,6 @@ namespace MediaBrowser.Dlna.PlayTo return 1000; } - private int GetVolumeTimerIntervalMs() - { - return 5000; - } - private int GetInactiveTimerIntervalMs() { return 20000; @@ -107,11 +114,37 @@ namespace MediaBrowser.Dlna.PlayTo { _timer = new Timer(TimerCallback, null, GetPlaybackTimerIntervalMs(), GetInactiveTimerIntervalMs()); - _volumeTimer = new Timer(VolumeTimerCallback, null, Timeout.Infinite, Timeout.Infinite); - _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 bool _timerActive; private void RestartTimer() @@ -124,7 +157,6 @@ namespace MediaBrowser.Dlna.PlayTo { _logger.Debug("RestartTimer"); _timer.Change(10, GetPlaybackTimerIntervalMs()); - _volumeTimer.Change(100, GetVolumeTimerIntervalMs()); } _timerActive = true; @@ -150,10 +182,6 @@ namespace MediaBrowser.Dlna.PlayTo { _timer.Change(interval, interval); } - if (_volumeTimer != null) - { - _volumeTimer.Change(Timeout.Infinite, Timeout.Infinite); - } } _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() { var command = RendererCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetVolume"); @@ -1012,7 +1027,6 @@ namespace MediaBrowser.Dlna.PlayTo _disposed = true; DisposeTimer(); - DisposeVolumeTimer(); } } @@ -1025,15 +1039,6 @@ namespace MediaBrowser.Dlna.PlayTo } } - private void DisposeVolumeTimer() - { - if (_volumeTimer != null) - { - _volumeTimer.Dispose(); - _volumeTimer = null; - } - } - #endregion public override string ToString() diff --git a/MediaBrowser.Dlna/PlayTo/PlayToController.cs b/MediaBrowser.Dlna/PlayTo/PlayToController.cs index cb3629678..7e021b877 100644 --- a/MediaBrowser.Dlna/PlayTo/PlayToController.cs +++ b/MediaBrowser.Dlna/PlayTo/PlayToController.cs @@ -37,11 +37,28 @@ namespace MediaBrowser.Dlna.PlayTo private readonly IDeviceDiscovery _deviceDiscovery; private readonly string _serverAddress; private readonly string _accessToken; + private readonly DateTime _creationTime; public bool IsSessionActive { 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; } } @@ -55,8 +72,6 @@ namespace MediaBrowser.Dlna.PlayTo 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) { _session = session; @@ -72,6 +87,7 @@ namespace MediaBrowser.Dlna.PlayTo _mediaSourceManager = mediaSourceManager; _accessToken = accessToken; _logger = logger; + _creationTime = DateTime.UtcNow; } public void Init(Device device) @@ -84,8 +100,6 @@ namespace MediaBrowser.Dlna.PlayTo _device.Start(); _deviceDiscovery.DeviceLeft += _deviceDiscovery_DeviceLeft; - - _updateTimer = new Timer(updateTimer_Elapsed, null, 60000, 60000); } 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) { try @@ -634,21 +632,10 @@ namespace MediaBrowser.Dlna.PlayTo _device.MediaChanged -= _device_MediaChanged; _deviceDiscovery.DeviceLeft -= _deviceDiscovery_DeviceLeft; - DisposeUpdateTimer(); - _device.Dispose(); } } - private void DisposeUpdateTimer() - { - if (_updateTimer != null) - { - _updateTimer.Dispose(); - _updateTimer = null; - } - } - private readonly CultureInfo _usCulture = new CultureInfo("en-US"); public Task SendGeneralCommand(GeneralCommand command, CancellationToken cancellationToken) diff --git a/MediaBrowser.Dlna/PlayTo/PlayToManager.cs b/MediaBrowser.Dlna/PlayTo/PlayToManager.cs index 6c79007c4..94f8104be 100644 --- a/MediaBrowser.Dlna/PlayTo/PlayToManager.cs +++ b/MediaBrowser.Dlna/PlayTo/PlayToManager.cs @@ -36,7 +36,7 @@ namespace MediaBrowser.Dlna.PlayTo private readonly IMediaSourceManager _mediaSourceManager; 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) { @@ -57,19 +57,9 @@ namespace MediaBrowser.Dlna.PlayTo public void Start() { - _clearNonRenderersTimer = new Timer(OnClearUrlTimerCallback, null, TimeSpan.FromMinutes(10), TimeSpan.FromMinutes(10)); - _deviceDiscovery.DeviceDiscovered += _deviceDiscovery_DeviceDiscovered; } - private void OnClearUrlTimerCallback(object state) - { - lock (_nonRendererUrls) - { - _nonRendererUrls.Clear(); - } - } - async void _deviceDiscovery_DeviceDiscovered(object sender, SsdpMessageEventArgs e) { string usn; @@ -99,6 +89,12 @@ namespace MediaBrowser.Dlna.PlayTo lock (_nonRendererUrls) { + if ((DateTime.UtcNow - _lastRendererClear).TotalMinutes >= 10) + { + _nonRendererUrls.Clear(); + _lastRendererClear = DateTime.UtcNow; + } + if (_nonRendererUrls.Contains(location, StringComparer.OrdinalIgnoreCase)) { return; @@ -181,12 +177,6 @@ namespace MediaBrowser.Dlna.PlayTo public void Dispose() { _deviceDiscovery.DeviceDiscovered -= _deviceDiscovery_DeviceDiscovered; - - if (_clearNonRenderersTimer != null) - { - _clearNonRenderersTimer.Dispose(); - _clearNonRenderersTimer = null; - } } } } diff --git a/MediaBrowser.Dlna/Profiles/WdtvLiveProfile.cs b/MediaBrowser.Dlna/Profiles/WdtvLiveProfile.cs index 5bfd37726..98e780a25 100644 --- a/MediaBrowser.Dlna/Profiles/WdtvLiveProfile.cs +++ b/MediaBrowser.Dlna/Profiles/WdtvLiveProfile.cs @@ -58,7 +58,7 @@ namespace MediaBrowser.Dlna.Profiles Container = "avi", Type = DlnaProfileType.Video, VideoCodec = "mpeg1video,mpeg2video,mpeg4,h264,vc1", - AudioCodec = "ac3,dca,mp2,mp3,pcm" + AudioCodec = "ac3,dca,mp2,mp3,pcm,dca" }, new DirectPlayProfile @@ -66,7 +66,7 @@ namespace MediaBrowser.Dlna.Profiles Container = "mpeg", Type = DlnaProfileType.Video, VideoCodec = "mpeg1video,mpeg2video", - AudioCodec = "ac3,dca,mp2,mp3,pcm" + AudioCodec = "ac3,dca,mp2,mp3,pcm,dca" }, new DirectPlayProfile @@ -74,7 +74,7 @@ namespace MediaBrowser.Dlna.Profiles Container = "mkv", Type = DlnaProfileType.Video, VideoCodec = "mpeg1video,mpeg2video,mpeg4,h264,vc1", - AudioCodec = "ac3,dca,aac,mp2,mp3,pcm" + AudioCodec = "ac3,dca,aac,mp2,mp3,pcm,dca" }, new DirectPlayProfile @@ -82,7 +82,7 @@ namespace MediaBrowser.Dlna.Profiles Container = "ts", Type = DlnaProfileType.Video, VideoCodec = "mpeg1video,mpeg2video,h264,vc1", - AudioCodec = "ac3,dca,mp2,mp3,aac" + AudioCodec = "ac3,dca,mp2,mp3,aac,dca" }, new DirectPlayProfile @@ -90,7 +90,7 @@ namespace MediaBrowser.Dlna.Profiles Container = "mp4,mov", Type = DlnaProfileType.Video, VideoCodec = "h264,mpeg4", - AudioCodec = "ac3,aac,mp2,mp3" + AudioCodec = "ac3,aac,mp2,mp3,dca" }, new DirectPlayProfile diff --git a/MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml b/MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml index 10b642bba..e17fc087b 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml @@ -37,11 +37,11 @@ <IgnoreTranscodeByteRangeRequests>true</IgnoreTranscodeByteRangeRequests> <XmlRootAttributes /> <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="mp2,ac3" videoCodec="mpeg2video" type="Video" /> <DirectPlayProfile container="mp3" audioCodec="mp2,mp3" type="Audio" /> diff --git a/MediaBrowser.Dlna/Ssdp/SsdpHandler.cs b/MediaBrowser.Dlna/Ssdp/SsdpHandler.cs index 32c369a8f..f800a12c1 100644 --- a/MediaBrowser.Dlna/Ssdp/SsdpHandler.cs +++ b/MediaBrowser.Dlna/Ssdp/SsdpHandler.cs @@ -33,12 +33,8 @@ namespace MediaBrowser.Dlna.Ssdp private readonly IPAddress _ssdpIp = IPAddress.Parse(SSDPAddr); private readonly IPEndPoint _ssdpEndp = new IPEndPoint(IPAddress.Parse(SSDPAddr), SSDPPort); - private Timer _queueTimer; private Timer _notificationTimer; - private readonly AutoResetEvent _datagramPosted = new AutoResetEvent(false); - private readonly ConcurrentQueue<Datagram> _messageQueue = new ConcurrentQueue<Datagram>(); - private bool _isDisposed; private readonly ConcurrentDictionary<Guid, List<UpnpDevice>> _devices = new ConcurrentDictionary<Guid, List<UpnpDevice>>(); @@ -121,9 +117,13 @@ namespace MediaBrowser.Dlna.Ssdp public void Start() { - RestartSocketListener(); + DisposeSocket(); + StopAliveNotifier(); + RestartSocketListener(); ReloadAliveNotifier(); + + SystemEvents.PowerModeChanged -= SystemEvents_PowerModeChanged; SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged; } @@ -131,7 +131,7 @@ namespace MediaBrowser.Dlna.Ssdp { 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); } - public void SendDatagram(string header, + public async void SendDatagram(string header, Dictionary<string, string> values, EndPoint endpoint, EndPoint localAddress, @@ -162,28 +162,18 @@ namespace MediaBrowser.Dlna.Ssdp int sendCount) { var msg = new SsdpMessageBuilder().BuildMessage(header, values); - var queued = false; var enableDebugLogging = _config.GetDlnaConfiguration().EnableDebugLogging; 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() { if (_isDisposed) { - StopSocketRetryTimer(); return; } @@ -304,8 +257,6 @@ namespace MediaBrowser.Dlna.Ssdp _logger.Info("MultiCast socket created"); - StopSocketRetryTimer(); - Receive(); } 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() { try @@ -448,16 +374,9 @@ namespace MediaBrowser.Dlna.Ssdp SystemEvents.PowerModeChanged -= SystemEvents_PowerModeChanged; _isDisposed = true; - while (_messageQueue.Count != 0) - { - _datagramPosted.WaitOne(); - } DisposeSocket(); - DisposeQueueTimer(); - DisposeNotificationTimer(); - - _datagramPosted.Dispose(); + StopAliveNotifier(); } 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() { 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) { - 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))); @@ -572,7 +472,7 @@ namespace MediaBrowser.Dlna.Ssdp if (!config.BlastAliveMessages) { - DisposeNotificationTimer(); + StopAliveNotifier(); return; } @@ -599,7 +499,7 @@ namespace MediaBrowser.Dlna.Ssdp } } - private void DisposeNotificationTimer() + private void StopAliveNotifier() { lock (_notificationTimerSyncLock) { diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index ba0790bf3..2a3a416ad 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -292,16 +292,25 @@ namespace MediaBrowser.MediaEncoding.Encoder 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; } diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs index fb22637f9..152bdd993 100644 --- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs @@ -164,7 +164,7 @@ namespace MediaBrowser.Model.Configuration /// different directories and files. /// </summary> /// <value>The file watcher delay.</value> - public int RealtimeLibraryMonitorDelay { get; set; } + public int LibraryMonitorDelay { get; set; } /// <summary> /// 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 bool MergeMetadataAndImagesByName { get; set; } - public bool EnableStandaloneMetadata { get; set; } /// <summary> /// Gets or sets the image saving convention. @@ -256,7 +255,7 @@ namespace MediaBrowser.Model.Configuration MinResumeDurationSeconds = 300; EnableLibraryMonitor = AutoOnOff.Auto; - RealtimeLibraryMonitorDelay = 40; + LibraryMonitorDelay = 60; EnableInternetProviders = true; FindInternetTrailers = true; diff --git a/MediaBrowser.Model/Configuration/UserConfiguration.cs b/MediaBrowser.Model/Configuration/UserConfiguration.cs index ab3a861a3..819948909 100644 --- a/MediaBrowser.Model/Configuration/UserConfiguration.cs +++ b/MediaBrowser.Model/Configuration/UserConfiguration.cs @@ -48,6 +48,7 @@ namespace MediaBrowser.Model.Configuration public string[] PlainFolderViews { get; set; } public bool HidePlayedInLatest { get; set; } + public bool DisplayChannelsInline { get; set; } /// <summary> /// Initializes a new instance of the <see cref="UserConfiguration" /> class. diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs index efa6ff0cf..8094aa58d 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs @@ -213,7 +213,7 @@ namespace MediaBrowser.Providers.MediaInfo } 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); } @@ -360,7 +360,15 @@ namespace MediaBrowser.Providers.MediaInfo /// <returns>VideoStream.</returns> 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) @@ -628,7 +636,7 @@ namespace MediaBrowser.Providers.MediaInfo FetchFromDvdLib(item, mount); } - if (item.VideoType == VideoType.BluRay || (item.IsoType.HasValue && item.IsoType.Value == IsoType.BluRay)) + if (blurayDiscInfo != null) { item.PlayableStreamFileNames = blurayDiscInfo.Files.ToList(); } diff --git a/MediaBrowser.Providers/People/MovieDbPersonProvider.cs b/MediaBrowser.Providers/People/MovieDbPersonProvider.cs index 5c5919709..14304c2eb 100644 --- a/MediaBrowser.Providers/People/MovieDbPersonProvider.cs +++ b/MediaBrowser.Providers/People/MovieDbPersonProvider.cs @@ -38,7 +38,7 @@ namespace MediaBrowser.Providers.People private int _requestCount; private readonly object _requestCountLock = new object(); - private Timer _requestCountReset; + private DateTime _lastRequestCountReset; public MovieDbPersonProvider(IFileSystem fileSystem, IServerConfigurationManager configurationManager, IJsonSerializer jsonSerializer, IHttpClient httpClient, ILogger logger) { @@ -48,16 +48,6 @@ namespace MediaBrowser.Providers.People _httpClient = httpClient; _logger = logger; 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 @@ -101,6 +91,12 @@ namespace MediaBrowser.Providers.People { lock (_requestCountLock) { + if ((DateTime.UtcNow - _lastRequestCountReset).TotalHours >= 1) + { + _requestCount = 0; + _lastRequestCountReset = DateTime.UtcNow; + } + var requestCount = _requestCount; if (requestCount >= 5) diff --git a/MediaBrowser.Providers/TV/TvdbSeriesProvider.cs b/MediaBrowser.Providers/TV/TvdbSeriesProvider.cs index 313ca9074..4a7ae57fb 100644 --- a/MediaBrowser.Providers/TV/TvdbSeriesProvider.cs +++ b/MediaBrowser.Providers/TV/TvdbSeriesProvider.cs @@ -238,7 +238,7 @@ namespace MediaBrowser.Providers.TV 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 { @@ -268,7 +268,7 @@ namespace MediaBrowser.Providers.TV 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"); if (!string.Equals(downloadLangaugeXmlFile, saveAsLanguageXmlFile, StringComparison.OrdinalIgnoreCase)) diff --git a/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs b/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs index b115c3bfd..284556c72 100644 --- a/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs +++ b/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs @@ -29,7 +29,7 @@ using CommonIO; namespace MediaBrowser.Server.Implementations.Channels { - public class ChannelManager : IChannelManager, IDisposable + public class ChannelManager : IChannelManager { private IChannel[] _channels; @@ -47,11 +47,6 @@ namespace MediaBrowser.Server.Implementations.Channels private readonly ILocalizationManager _localization; 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) { _userManager = userManager; @@ -65,9 +60,6 @@ namespace MediaBrowser.Server.Implementations.Channels _localization = localization; _httpClient = httpClient; _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 @@ -206,6 +198,8 @@ namespace MediaBrowser.Server.Implementations.Channels public async Task RefreshChannels(IProgress<double> progress, CancellationToken cancellationToken) { + _refreshedItems.Clear(); + var allChannelsList = GetAllChannels().ToList(); var numComplete = 0; @@ -1471,12 +1465,6 @@ namespace MediaBrowser.Server.Implementations.Channels 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) { 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)) { 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; - } - } } } diff --git a/MediaBrowser.Server.Implementations/Configuration/ServerConfigurationManager.cs b/MediaBrowser.Server.Implementations/Configuration/ServerConfigurationManager.cs index a7d3854e7..d7df37332 100644 --- a/MediaBrowser.Server.Implementations/Configuration/ServerConfigurationManager.cs +++ b/MediaBrowser.Server.Implementations/Configuration/ServerConfigurationManager.cs @@ -121,20 +121,12 @@ namespace MediaBrowser.Server.Implementations.Configuration ((ServerApplicationPaths)ApplicationPaths).InternalMetadataPath = metadataPath; - if (Configuration.MergeMetadataAndImagesByName) - { - ((ServerApplicationPaths)ApplicationPaths).ItemsByNamePath = ((ServerApplicationPaths)ApplicationPaths).InternalMetadataPath; - } + ((ServerApplicationPaths)ApplicationPaths).ItemsByNamePath = ((ServerApplicationPaths)ApplicationPaths).InternalMetadataPath; } private string GetInternalMetadataPath() { - if (Configuration.EnableStandaloneMetadata) - { - return Path.Combine(ApplicationPaths.ProgramDataPath, "metadata"); - } - - return null; + return Path.Combine(ApplicationPaths.ProgramDataPath, "metadata"); } /// <summary> diff --git a/MediaBrowser.Server.Implementations/Connect/ConnectEntryPoint.cs b/MediaBrowser.Server.Implementations/Connect/ConnectEntryPoint.cs index 6dab136a5..12de5f6ef 100644 --- a/MediaBrowser.Server.Implementations/Connect/ConnectEntryPoint.cs +++ b/MediaBrowser.Server.Implementations/Connect/ConnectEntryPoint.cs @@ -13,12 +13,13 @@ using System.Threading; using System.Threading.Tasks; using CommonIO; using MediaBrowser.Common.IO; +using MediaBrowser.Common.Threading; namespace MediaBrowser.Server.Implementations.Connect { public class ConnectEntryPoint : IServerEntryPoint { - private Timer _timer; + private PeriodicTimer _timer; private readonly IHttpClient _httpClient; private readonly IApplicationPaths _appPaths; private readonly ILogger _logger; @@ -43,7 +44,7 @@ namespace MediaBrowser.Server.Implementations.Connect { 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" }; diff --git a/MediaBrowser.Server.Implementations/Devices/DeviceRepository.cs b/MediaBrowser.Server.Implementations/Devices/DeviceRepository.cs index 43b1e693c..9e4a45253 100644 --- a/MediaBrowser.Server.Implementations/Devices/DeviceRepository.cs +++ b/MediaBrowser.Server.Implementations/Devices/DeviceRepository.cs @@ -25,7 +25,7 @@ namespace MediaBrowser.Server.Implementations.Devices private readonly ILogger _logger; private readonly IFileSystem _fileSystem; - private ConcurrentBag<DeviceInfo> _devices; + private List<DeviceInfo> _devices; public DeviceRepository(IApplicationPaths appPaths, IJsonSerializer json, ILogger logger, IFileSystem fileSystem) { @@ -93,17 +93,14 @@ namespace MediaBrowser.Server.Implementations.Devices 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() diff --git a/MediaBrowser.Server.Implementations/EntryPoints/ExternalPortForwarding.cs b/MediaBrowser.Server.Implementations/EntryPoints/ExternalPortForwarding.cs index a2ffa9aff..2b2c338dd 100644 --- a/MediaBrowser.Server.Implementations/EntryPoints/ExternalPortForwarding.cs +++ b/MediaBrowser.Server.Implementations/EntryPoints/ExternalPortForwarding.cs @@ -11,6 +11,7 @@ using System.IO; using System.Net; using System.Text; using System.Threading; +using MediaBrowser.Common.Threading; namespace MediaBrowser.Server.Implementations.EntryPoints { @@ -21,7 +22,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints private readonly IServerConfigurationManager _config; private readonly ISsdpHandler _ssdp; - private Timer _timer; + private PeriodicTimer _timer; private bool _isStarted; public ExternalPortForwarding(ILogManager logmanager, IServerApplicationHost appHost, IServerConfigurationManager config, ISsdpHandler ssdp) @@ -95,7 +96,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints NatUtility.UnhandledException += NatUtility_UnhandledException; 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; diff --git a/MediaBrowser.Server.Implementations/EntryPoints/LoadRegistrations.cs b/MediaBrowser.Server.Implementations/EntryPoints/LoadRegistrations.cs index 701cf21fb..efda36821 100644 --- a/MediaBrowser.Server.Implementations/EntryPoints/LoadRegistrations.cs +++ b/MediaBrowser.Server.Implementations/EntryPoints/LoadRegistrations.cs @@ -4,6 +4,7 @@ using MediaBrowser.Model.Logging; using System; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Common.Threading; namespace MediaBrowser.Server.Implementations.EntryPoints { @@ -22,7 +23,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints /// </summary> private readonly ILogger _logger; - private Timer _timer; + private PeriodicTimer _timer; /// <summary> /// Initializes a new instance of the <see cref="LoadRegistrations" /> class. @@ -41,7 +42,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints /// </summary> 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() diff --git a/MediaBrowser.Server.Implementations/EntryPoints/UsageEntryPoint.cs b/MediaBrowser.Server.Implementations/EntryPoints/UsageEntryPoint.cs index c3b9c0d4d..d8aef909b 100644 --- a/MediaBrowser.Server.Implementations/EntryPoints/UsageEntryPoint.cs +++ b/MediaBrowser.Server.Implementations/EntryPoints/UsageEntryPoint.cs @@ -9,6 +9,7 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Threading; +using System.Threading.Tasks; namespace MediaBrowser.Server.Implementations.EntryPoints { @@ -23,7 +24,6 @@ namespace MediaBrowser.Server.Implementations.EntryPoints private readonly ISessionManager _sessionManager; private readonly IUserManager _userManager; - private Timer _timer; private readonly TimeSpan _frequency = TimeSpan.FromHours(24); private readonly ConcurrentDictionary<Guid, ClientInfo> _apps = new ConcurrentDictionary<Guid, ClientInfo>(); @@ -95,16 +95,16 @@ namespace MediaBrowser.Server.Implementations.EntryPoints 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> /// Called when [timer fired]. /// </summary> - /// <param name="state">The state.</param> - private async void OnTimerFired(object state) + private async void OnTimerFired() { try { @@ -121,12 +121,6 @@ namespace MediaBrowser.Server.Implementations.EntryPoints public void Dispose() { _sessionManager.SessionStarted -= _sessionManager_SessionStarted; - - if (_timer != null) - { - _timer.Dispose(); - _timer = null; - } } } } diff --git a/MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs b/MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs index 26392f5a9..73cc5ab01 100644 --- a/MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs +++ b/MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs @@ -76,50 +76,50 @@ namespace MediaBrowser.Server.Implementations.FileOrganization { 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 { @@ -151,32 +151,32 @@ namespace MediaBrowser.Server.Implementations.FileOrganization 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); 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); @@ -189,33 +189,33 @@ namespace MediaBrowser.Server.Implementations.FileOrganization 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); // 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)) { @@ -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() .OfType<Episode>() @@ -462,16 +462,18 @@ namespace MediaBrowser.Server.Implementations.FileOrganization /// <param name="seasonNumber">The season number.</param> /// <param name="episodeNumber">The 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="cancellationToken">The cancellation token.</param> /// <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 { @@ -481,7 +483,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization MetadataLanguage = series.GetPreferredMetadataLanguage(), ParentIndexNumber = seasonNumber, SeriesProviderIds = series.ProviderIds, - PremiereDate = premiereDate + PremiereDate = premiereDate }; var searchResults = await _providerManager.GetRemoteSearchResults<Episode, EpisodeInfo>(new RemoteSearchQuery<EpisodeInfo> @@ -491,22 +493,25 @@ namespace MediaBrowser.Server.Implementations.FileOrganization }, cancellationToken).ConfigureAwait(false); var episode = searchResults.FirstOrDefault(); - - string episodeName = string.Empty; if (episode == null) { var msg = string.Format("No provider metadata found for {0} season {1} episode {2}", series.Name, seasonNumber, episodeNumber); _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); @@ -579,7 +584,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization { seriesName = _fileSystem.GetValidFilename(seriesName).Trim(); - if (string.IsNullOrEmpty(episodeTitle)) + if (string.IsNullOrWhiteSpace(episodeTitle)) { episodeTitle = string.Empty; } diff --git a/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs b/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs index 6a23a8497..038116703 100644 --- a/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs +++ b/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> /// Overridable method that can be used to implement a custom hnandler /// </summary> @@ -271,6 +290,14 @@ namespace MediaBrowser.Server.Implementations.HttpServer var operationName = httpReq.OperationName; 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) || string.Equals(localPath, "/emby/", StringComparison.OrdinalIgnoreCase)) { @@ -333,15 +360,16 @@ namespace MediaBrowser.Server.Implementations.HttpServer task.ContinueWith(x => httpRes.Close(), TaskContinuationOptions.OnlyOnRanToCompletion | TaskContinuationOptions.AttachedToParent); //Matches Exceptions handled in HttpListenerBase.InitTask() - var urlString = url.ToString(); - task.ContinueWith(x => { var statusCode = httpRes.StatusCode; var duration = DateTime.Now - date; - LoggerUtils.LogResponse(_logger, statusCode, urlString, remoteIp, duration); + if (enableLog) + { + LoggerUtils.LogResponse(_logger, statusCode, urlString, remoteIp, duration); + } }, TaskContinuationOptions.None); return task; diff --git a/MediaBrowser.Server.Implementations/HttpServer/LoggerUtils.cs b/MediaBrowser.Server.Implementations/HttpServer/LoggerUtils.cs index fae702023..0b8caaa6e 100644 --- a/MediaBrowser.Server.Implementations/HttpServer/LoggerUtils.cs +++ b/MediaBrowser.Server.Implementations/HttpServer/LoggerUtils.cs @@ -1,12 +1,31 @@ using MediaBrowser.Model.Logging; using System; using System.Globalization; +using System.IO; +using SocketHttpListener.Net; namespace MediaBrowser.Server.Implementations.HttpServer { 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> /// Logs the response. /// </summary> /// <param name="logger">The logger.</param> diff --git a/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpListener.cs b/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpListener.cs index a91b1e3ed..a029e0955 100644 --- a/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpListener.cs +++ b/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpListener.cs @@ -78,10 +78,10 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp { var request = context.Request; - LogRequest(_logger, request); - if (request.IsWebSocketRequest) { + LoggerUtils.LogRequest(_logger, request); + ProcessWebSocketRequest(context); return Task.FromResult(true); } @@ -156,44 +156,6 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp 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) { var httpReq = GetRequest(context); diff --git a/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs b/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs index 85ea8ec57..184b72d8f 100644 --- a/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs +++ b/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs @@ -471,11 +471,11 @@ namespace MediaBrowser.Server.Implementations.IO { 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 { - _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) { + if (Environment.OSVersion.Platform != PlatformID.Win32NT) + { + // Causing lockups on linux + return false; + } + try { var data = _fileSystem.GetFileSystemInfo(path); if (!data.Exists - || data.Attributes.HasFlag(FileAttributes.Directory) + || data.IsDirectory // Opening a writable stream will fail with readonly files || data.Attributes.HasFlag(FileAttributes.ReadOnly)) diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs index 0cb5174c9..b0b2680ca 100644 --- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs +++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs @@ -222,7 +222,7 @@ namespace MediaBrowser.Server.Implementations.Library /// <summary> /// The _root folder /// </summary> - private AggregateFolder _rootFolder; + private volatile AggregateFolder _rootFolder; /// <summary> /// The _root folder sync lock /// </summary> @@ -743,7 +743,7 @@ namespace MediaBrowser.Server.Implementations.Library return rootFolder; } - private UserRootFolder _userRootFolder; + private volatile UserRootFolder _userRootFolder; private readonly object _syncLock = new object(); public Folder GetUserRootFolder() { diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/PhotoResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/PhotoResolver.cs index 407aac53d..8beb03b71 100644 --- a/MediaBrowser.Server.Implementations/Library/Resolvers/PhotoResolver.cs +++ b/MediaBrowser.Server.Implementations/Library/Resolvers/PhotoResolver.cs @@ -51,8 +51,17 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers { 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); } } diff --git a/MediaBrowser.Server.Implementations/Library/UserViewManager.cs b/MediaBrowser.Server.Implementations/Library/UserViewManager.cs index 30a720a62..0df4742bd 100644 --- a/MediaBrowser.Server.Implementations/Library/UserViewManager.cs +++ b/MediaBrowser.Server.Implementations/Library/UserViewManager.cs @@ -163,7 +163,14 @@ namespace MediaBrowser.Server.Implementations.Library 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)) { diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index 356d0d83d..cd91684ce 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -22,12 +22,15 @@ using MediaBrowser.Server.Implementations.FileOrganization; using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; using CommonIO; using MediaBrowser.Common.Extensions; +using MediaBrowser.Controller.Power; +using Microsoft.Win32; namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV { @@ -55,7 +58,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV 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; @@ -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)); _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; } public void Start() { _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; @@ -155,7 +171,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV { epgData = GetEpgDataForChannel(timer.ChannelId); } - await UpdateTimersForSeriesTimer(epgData, timer).ConfigureAwait(false); + await UpdateTimersForSeriesTimer(epgData, timer, false).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) { - 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) { CancelTimerInternal(timer.Id); @@ -332,25 +352,44 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV } _seriesTimerProvider.Add(info); - await UpdateTimersForSeriesTimer(epgData, info).ConfigureAwait(false); + await UpdateTimersForSeriesTimer(epgData, info, false).ConfigureAwait(false); } 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) @@ -603,6 +642,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV { await RecordStream(timer, recordingEndDate, cancellationTokenSource.Token).ConfigureAwait(false); } + else + { + _logger.Info("Skipping RecordStream because it's already in progress."); + } } catch (OperationCanceledException) { @@ -721,7 +764,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV recording.DateLastUpdated = DateTime.UtcNow; _recordingProvider.AddOrUpdate(recording); - _logger.Info("Beginning recording."); + _logger.Info("Beginning recording. Will record for {0} minutes.", duration.TotalMinutes.ToString(CultureInfo.InvariantCulture)); httpRequestOptions.BufferContent = false; var durationToken = new CancellationTokenSource(duration); @@ -836,7 +879,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV 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(); @@ -849,12 +892,29 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV _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) { // 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); diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs index f46daa6d5..b29a7562c 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs @@ -13,7 +13,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV where T : class { private readonly object _fileDataLock = new object(); - private List<T> _items; + private volatile List<T> _items; private readonly IJsonSerializer _jsonSerializer; protected readonly ILogger Logger; private readonly string _dataPath; diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs index 80bb671fa..5d462f106 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs @@ -5,22 +5,27 @@ using MediaBrowser.Model.Logging; using MediaBrowser.Model.Serialization; using System; using System.Collections.Concurrent; +using System.Globalization; using System.Linq; using System.Threading; using CommonIO; -using MediaBrowser.Common.IO; +using MediaBrowser.Controller.Power; namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV { public class TimerManager : ItemDataProvider<TimerInfo> { 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 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)) { + _powerManagement = powerManagement; + _logger = logger1; } public void RestartTimers() @@ -58,6 +63,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV { var timespan = RecordingHelper.GetStartTime(item) - DateTime.UtcNow; timer.Change(timespan, TimeSpan.Zero); + ScheduleWake(item); } else { @@ -74,6 +80,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV base.Add(item); AddTimer(item); + ScheduleWake(item); } private void AddTimer(TimerInfo item) @@ -91,15 +98,39 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV 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) { StopTimer(item); 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(); + _logger.Warn("Timer already exists for item {0}", item.Id); } } diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs index e09e06bd4..8bf1d27b8 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs @@ -1954,6 +1954,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv await service.CreateTimerAsync(info, cancellationToken).ConfigureAwait(false); _lastRecordingRefreshTime = DateTime.MinValue; + _logger.Info("New recording scheduled"); } public async Task CreateSeriesTimer(SeriesTimerInfoDto timer, CancellationToken cancellationToken) diff --git a/MediaBrowser.Server.Implementations/Localization/Core/ca.json b/MediaBrowser.Server.Implementations/Localization/Core/ca.json index 40dfcd2c3..83d72030f 100644 --- a/MediaBrowser.Server.Implementations/Localization/Core/ca.json +++ b/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", "FolderTypeMovies": "Pel\u00b7l\u00edcules", "FolderTypeMusic": "M\u00fasica", @@ -12,10 +12,10 @@ "FolderTypeBooks": "Llibres", "FolderTypeTvShows": "TV", "FolderTypeInherit": "Heretat", - "HeaderCastCrew": "Repartiment i equip", + "HeaderCastCrew": "Repartiment i Equip", "HeaderPeople": "People", "ValueSpecialEpisodeName": "Special - {0}", - "LabelChapterName": "Chapter {0}", + "LabelChapterName": "Cap\u00edtol {0}", "NameSeasonNumber": "Temporada {0}", "LabelExit": "Sortir", "LabelVisitCommunity": "Visita la comunitat", @@ -77,7 +77,7 @@ "ViewTypeMovieMovies": "Pel\u00b7l\u00edcules", "ViewTypeMovieCollections": "Col\u00b7leccions", "ViewTypeMovieFavorites": "Preferides", - "ViewTypeMovieGenres": "Genres", + "ViewTypeMovieGenres": "G\u00e8neres", "ViewTypeMusicLatest": "Novetats", "ViewTypeMusicPlaylists": "Llistes de reproducci\u00f3", "ViewTypeMusicAlbums": "\u00c0lbums", @@ -89,7 +89,7 @@ "ViewTypeMusicFavoriteArtists": "Artistes Preferits", "ViewTypeMusicFavoriteSongs": "Can\u00e7ons Preferides", "ViewTypeFolders": "Directoris", - "ViewTypeLiveTvRecordingGroups": "Recordings", + "ViewTypeLiveTvRecordingGroups": "Enregistraments", "ViewTypeLiveTvChannels": "Canals", "ScheduledTaskFailedWithName": "{0} failed", "LabelRunningTimeValue": "Running time: {0}", @@ -103,7 +103,7 @@ "LabelIpAddressValue": "Ip address: {0}", "DeviceOnlineWithName": "{0} is connected", "UserOnlineFromDevice": "{0} is online from {1}", - "ProviderValue": "Provider: {0}", + "ProviderValue": "Prove\u00efdor: {0}", "SubtitlesDownloadedForItem": "Subtitles downloaded for {0}", "UserConfigurationUpdatedWithName": "User configuration has been updated for {0}", "UserCreatedWithName": "User {0} has been created", @@ -113,12 +113,12 @@ "MessageNamedServerConfigurationUpdatedWithValue": "Server configuration section {0} has been updated", "MessageApplicationUpdated": "Emby Server has been updated", "FailedLoginAttemptWithUserName": "Failed login attempt from {0}", - "AuthenticationSucceededWithUserName": "{0} successfully authenticated", + "AuthenticationSucceededWithUserName": "{0} autenticat correctament", "DeviceOfflineWithName": "{0} has disconnected", "UserLockedOutWithName": "User {0} has been locked out", "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}", "HeaderUnidentified": "Unidentified", "HeaderImagePrimary": "Primary", @@ -164,7 +164,7 @@ "HeaderTracks": "Tracks", "HeaderMusicArtist": "M\u00fasic", "HeaderLocked": "Locked", - "HeaderStudios": "Studios", + "HeaderStudios": "Estudis", "HeaderActor": "Actors", "HeaderComposer": "Compositors", "HeaderDirector": "Directors", diff --git a/MediaBrowser.Server.Implementations/Localization/Core/nl.json b/MediaBrowser.Server.Implementations/Localization/Core/nl.json index b32ebefc3..fd0c586a2 100644 --- a/MediaBrowser.Server.Implementations/Localization/Core/nl.json +++ b/MediaBrowser.Server.Implementations/Localization/Core/nl.json @@ -63,7 +63,7 @@ "ViewTypeLatestGames": "Nieuwste games", "ViewTypeRecentlyPlayedGames": "Recent gespeelt", "ViewTypeGameFavorites": "Favorieten", - "ViewTypeGameSystems": "Gam systemen", + "ViewTypeGameSystems": "Game systemen", "ViewTypeGameGenres": "Genres", "ViewTypeTvResume": "Hervatten", "ViewTypeTvNextUp": "Volgende", @@ -147,7 +147,7 @@ "HeaderCommunityRating": "Gemeenschap cijfer", "HeaderTrailers": "Trailers", "HeaderSpecials": "Specials", - "HeaderGameSystems": "Spel systemen", + "HeaderGameSystems": "Game systemen", "HeaderPlayers": "Spelers:", "HeaderAlbumArtists": "Album artiesten", "HeaderAlbums": "Albums", diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj index e5c94a01b..c54ea42a5 100644 --- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj +++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj @@ -48,9 +48,9 @@ <Reference Include="Interfaces.IO"> <HintPath>..\packages\Interfaces.IO.1.0.0.5\lib\portable-net45+sl4+wp71+win8+wpa81\Interfaces.IO.dll</HintPath> </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> - <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 Include="MoreLinq"> <HintPath>..\packages\morelinq.1.4.0\lib\net35\MoreLinq.dll</HintPath> diff --git a/MediaBrowser.Server.Implementations/News/NewsEntryPoint.cs b/MediaBrowser.Server.Implementations/News/NewsEntryPoint.cs index e8f910f81..969541fbc 100644 --- a/MediaBrowser.Server.Implementations/News/NewsEntryPoint.cs +++ b/MediaBrowser.Server.Implementations/News/NewsEntryPoint.cs @@ -1,6 +1,5 @@ using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.IO; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Notifications; @@ -17,12 +16,13 @@ using System.Threading; using System.Threading.Tasks; using System.Xml; using CommonIO; +using MediaBrowser.Common.Threading; namespace MediaBrowser.Server.Implementations.News { public class NewsEntryPoint : IServerEntryPoint { - private Timer _timer; + private PeriodicTimer _timer; private readonly IHttpClient _httpClient; private readonly IApplicationPaths _appPaths; private readonly IFileSystem _fileSystem; @@ -47,7 +47,7 @@ namespace MediaBrowser.Server.Implementations.News public void Run() { - _timer = new Timer(OnTimerFired, null, TimeSpan.FromMilliseconds(500), _frequency); + _timer = new PeriodicTimer(OnTimerFired, null, TimeSpan.FromMilliseconds(500), _frequency); } /// <summary> diff --git a/MediaBrowser.Server.Implementations/packages.config b/MediaBrowser.Server.Implementations/packages.config index 5c04f2782..b7f5533ce 100644 --- a/MediaBrowser.Server.Implementations/packages.config +++ b/MediaBrowser.Server.Implementations/packages.config @@ -2,7 +2,7 @@ <packages>
<package id="CommonIO" version="1.0.0.7" 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="morelinq" version="1.4.0" targetFramework="net45" />
<package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" />
diff --git a/MediaBrowser.Server.Mono/Native/BaseMonoApp.cs b/MediaBrowser.Server.Mono/Native/BaseMonoApp.cs index 31d4592c8..e57a651c0 100644 --- a/MediaBrowser.Server.Mono/Native/BaseMonoApp.cs +++ b/MediaBrowser.Server.Mono/Native/BaseMonoApp.cs @@ -8,6 +8,7 @@ using System; using System.Collections.Generic; using System.Reflection; using System.Text.RegularExpressions; +using MediaBrowser.Controller.Power; namespace MediaBrowser.Server.Mono.Native { @@ -203,5 +204,18 @@ namespace MediaBrowser.Server.Mono.Native public string sysname = 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(); + } } } diff --git a/MediaBrowser.Server.Startup.Common/ApplicationHost.cs b/MediaBrowser.Server.Startup.Common/ApplicationHost.cs index 21731a3a6..2d625f810 100644 --- a/MediaBrowser.Server.Startup.Common/ApplicationHost.cs +++ b/MediaBrowser.Server.Startup.Common/ApplicationHost.cs @@ -210,7 +210,6 @@ namespace MediaBrowser.Server.Startup.Common private readonly string _releaseAssetFilename; internal INativeApp NativeApp { get; set; } - private Timer _ipAddressCacheTimer; /// <summary> /// Initializes a new instance of the <see cref="ApplicationHost" /> class. @@ -234,8 +233,6 @@ namespace MediaBrowser.Server.Startup.Common NativeApp = nativeApp; SetBaseExceptionMessage(); - - _ipAddressCacheTimer = new Timer(OnCacheClearTimerFired, null, TimeSpan.FromMinutes(3), TimeSpan.FromMinutes(3)); } private Version _version; @@ -533,6 +530,8 @@ namespace MediaBrowser.Server.Startup.Common EncodingManager = new EncodingManager(FileSystemManager, Logger, MediaEncoder, ChapterManager); RegisterSingleInstance(EncodingManager); + RegisterSingleInstance(NativeApp.GetPowerManagement()); + var sharingRepo = new SharingRepository(LogManager, ApplicationPaths); await sharingRepo.Initialize().ConfigureAwait(false); RegisterSingleInstance<ISharingManager>(new SharingManager(sharingRepo, ServerConfigurationManager, LibraryManager, this)); @@ -970,10 +969,10 @@ namespace MediaBrowser.Server.Startup.Common { get { - if (!ServerConfigurationManager.Configuration.EnableAutoUpdate) - { - return false; - } + if (!ServerConfigurationManager.Configuration.EnableAutoUpdate) + { + return false; + } #if DEBUG return false; #endif @@ -1157,8 +1156,13 @@ namespace MediaBrowser.Server.Startup.Common } private readonly ConcurrentDictionary<string, bool> _validAddressResults = new ConcurrentDictionary<string, bool>(StringComparer.OrdinalIgnoreCase); + private DateTime _lastAddressCacheClear; private bool IsIpAddressValid(IPAddress address) { + return IsIpAddressValidInternal(address).Result; + } + private async Task<bool> IsIpAddressValidInternal(IPAddress address) + { if (IPAddress.IsLoopback(address)) { return true; @@ -1167,6 +1171,12 @@ namespace MediaBrowser.Server.Startup.Common var apiUrl = GetLocalApiUrl(address.ToString()); apiUrl += "/system/ping"; + if ((DateTime.UtcNow - _lastAddressCacheClear).TotalMinutes >= 5) + { + _lastAddressCacheClear = DateTime.UtcNow; + _validAddressResults.Clear(); + } + bool cachedResult; if (_validAddressResults.TryGetValue(apiUrl, out cachedResult)) { @@ -1175,14 +1185,15 @@ namespace MediaBrowser.Server.Startup.Common try { - using (var response = HttpClient.SendAsync(new HttpRequestOptions + using (var response = await HttpClient.SendAsync(new HttpRequestOptions { Url = apiUrl, LogErrorResponseBody = false, LogErrors = false, - LogRequest = false + LogRequest = false, + TimeoutMs = 30000 - }, "POST").Result) + }, "POST").ConfigureAwait(false)) { using (var reader = new StreamReader(response.Content)) { @@ -1190,25 +1201,20 @@ namespace MediaBrowser.Server.Startup.Common var valid = string.Equals(Name, result, StringComparison.OrdinalIgnoreCase); _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; } } } 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); return false; } } - private void OnCacheClearTimerFired(object state) - { - _validAddressResults.Clear(); - } - public string FriendlyName { get diff --git a/MediaBrowser.Server.Startup.Common/EntryPoints/KeepServerAwake.cs b/MediaBrowser.Server.Startup.Common/EntryPoints/KeepServerAwake.cs index ba335868d..20d4c6b2a 100644 --- a/MediaBrowser.Server.Startup.Common/EntryPoints/KeepServerAwake.cs +++ b/MediaBrowser.Server.Startup.Common/EntryPoints/KeepServerAwake.cs @@ -4,7 +4,7 @@ using MediaBrowser.Controller.Session; using MediaBrowser.Model.Logging; using System; using System.Linq; -using System.Threading; +using MediaBrowser.Common.Threading; namespace MediaBrowser.Server.Startup.Common.EntryPoints { @@ -12,7 +12,7 @@ namespace MediaBrowser.Server.Startup.Common.EntryPoints { private readonly ISessionManager _sessionManager; private readonly ILogger _logger; - private Timer _timer; + private PeriodicTimer _timer; private readonly IServerApplicationHost _appHost; public KeepServerAwake(ISessionManager sessionManager, ILogger logger, IServerApplicationHost appHost) @@ -24,7 +24,7 @@ namespace MediaBrowser.Server.Startup.Common.EntryPoints public void Run() { - _timer = new Timer(obj => + _timer = new PeriodicTimer(obj => { var now = DateTime.UtcNow; if (_sessionManager.Sessions.Any(i => (now - i.LastActivityDate).TotalMinutes < 15)) diff --git a/MediaBrowser.Server.Startup.Common/INativeApp.cs b/MediaBrowser.Server.Startup.Common/INativeApp.cs index 597caf34c..75b38d0c4 100644 --- a/MediaBrowser.Server.Startup.Common/INativeApp.cs +++ b/MediaBrowser.Server.Startup.Common/INativeApp.cs @@ -2,6 +2,7 @@ using MediaBrowser.Model.Logging; using System.Collections.Generic; using System.Reflection; +using MediaBrowser.Controller.Power; namespace MediaBrowser.Server.Startup.Common { @@ -90,5 +91,11 @@ namespace MediaBrowser.Server.Startup.Common /// Prevents the system stand by. /// </summary> void PreventSystemStandby(); + + /// <summary> + /// Gets the power management. + /// </summary> + /// <returns>IPowerManagement.</returns> + IPowerManagement GetPowerManagement(); } } diff --git a/MediaBrowser.ServerApplication/MainStartup.cs b/MediaBrowser.ServerApplication/MainStartup.cs index 62cdbd05f..cf174c2d3 100644 --- a/MediaBrowser.ServerApplication/MainStartup.cs +++ b/MediaBrowser.ServerApplication/MainStartup.cs @@ -218,7 +218,7 @@ namespace MediaBrowser.ServerApplication var fileSystem = new WindowsFileSystem(new PatternsLogger(logManager.GetLogger("FileSystem"))); fileSystem.AddShortcutHandler(new MbLinkShortcutHandler(fileSystem)); - var nativeApp = new WindowsApp(fileSystem) + var nativeApp = new WindowsApp(fileSystem, _logger) { IsRunningAsService = runService }; diff --git a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj index c62983192..80e56d6e1 100644 --- a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj +++ b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj @@ -120,6 +120,7 @@ <Compile Include="Native\Standby.cs" /> <Compile Include="Native\ServerAuthorization.cs" /> <Compile Include="Native\WindowsApp.cs" /> + <Compile Include="Native\WindowsPowerManagement.cs" /> <Compile Include="Networking\CertificateGenerator.cs" /> <Compile Include="Networking\NativeMethods.cs" /> <Compile Include="Networking\NetworkManager.cs" /> diff --git a/MediaBrowser.ServerApplication/Native/WindowsApp.cs b/MediaBrowser.ServerApplication/Native/WindowsApp.cs index ceab5379d..fe2fe6de6 100644 --- a/MediaBrowser.ServerApplication/Native/WindowsApp.cs +++ b/MediaBrowser.ServerApplication/Native/WindowsApp.cs @@ -6,16 +6,19 @@ using MediaBrowser.ServerApplication.Networking; using System.Collections.Generic; using System.Reflection; using CommonIO; +using MediaBrowser.Controller.Power; namespace MediaBrowser.ServerApplication.Native { public class WindowsApp : INativeApp { private readonly IFileSystem _fileSystem; + private readonly ILogger _logger; - public WindowsApp(IFileSystem fileSystem) + public WindowsApp(IFileSystem fileSystem, ILogger logger) { _fileSystem = fileSystem; + _logger = logger; } public List<Assembly> GetAssembliesWithParts() @@ -117,5 +120,10 @@ namespace MediaBrowser.ServerApplication.Native { Standby.PreventSystemStandby(); } + + public IPowerManagement GetPowerManagement() + { + return new WindowsPowerManagement(_logger); + } } } diff --git a/MediaBrowser.ServerApplication/Native/WindowsPowerManagement.cs b/MediaBrowser.ServerApplication/Native/WindowsPowerManagement.cs new file mode 100644 index 000000000..866272639 --- /dev/null +++ b/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); + } + } + } +} diff --git a/MediaBrowser.WebDashboard/Api/DashboardService.cs b/MediaBrowser.WebDashboard/Api/DashboardService.cs index 5ad40e4c7..12cd5c385 100644 --- a/MediaBrowser.WebDashboard/Api/DashboardService.cs +++ b/MediaBrowser.WebDashboard/Api/DashboardService.cs @@ -354,8 +354,7 @@ namespace MediaBrowser.WebDashboard.Api DeleteFoldersByName(Path.Combine(bowerPath, "jstree"), "src"); DeleteFoldersByName(Path.Combine(bowerPath, "Sortable"), "meteor"); 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)) { diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj index cc153a010..165792291 100644 --- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj +++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj @@ -158,9 +158,6 @@ <Content Include="dashboard-ui\components\metadataeditor\metadataeditor.template.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\components\paperdialoghelper.js">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
<Content Include="dashboard-ui\components\playlisteditor\playlisteditor.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -311,9 +308,6 @@ <Content Include="dashboard-ui\scripts\supporterkeypage.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\components\testermessage.js">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
<Content Include="dashboard-ui\scripts\wizardlivetvguide.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
diff --git a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs index c5184ec3d..40b974ee7 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs @@ -215,7 +215,11 @@ namespace MediaBrowser.XbmcMetadata.Parsers if (!string.IsNullOrWhiteSpace(val)) { 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(); } @@ -627,7 +631,10 @@ namespace MediaBrowser.XbmcMetadata.Parsers { var person = GetPersonFromXmlNode(subtree); - itemResult.AddPerson(person); + if (!string.IsNullOrWhiteSpace(person.Name)) + { + itemResult.AddPerson(person); + } } break; } @@ -976,11 +983,11 @@ namespace MediaBrowser.XbmcMetadata.Parsers if (!string.IsNullOrWhiteSpace(val) && !string.IsNullOrWhiteSpace(userDataUserId)) { 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); - userData.LastPlayedDate = parsedValue; + userData.LastPlayedDate = parsedValue.ToUniversalTime(); } } break; diff --git a/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs index 0da53f575..c114f9dd9 100644 --- a/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs +++ b/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs @@ -416,6 +416,8 @@ namespace MediaBrowser.XbmcMetadata.Savers writer.WriteEndElement(); } + public const string DateAddedFormat = "yyyy-MM-dd HH:mm:ss"; + /// <summary> /// Adds the common nodes. /// </summary> @@ -472,7 +474,7 @@ namespace MediaBrowser.XbmcMetadata.Savers 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("originaltitle", item.Name ?? string.Empty); @@ -949,7 +951,7 @@ namespace MediaBrowser.XbmcMetadata.Savers 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"); |
