Merge branch 'beta'
This commit is contained in:
commit
37352785ac
|
@ -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")]
|
||||
|
|
|
@ -2192,7 +2192,7 @@ namespace MediaBrowser.Api.Playback
|
|||
{
|
||||
if (string.Equals(state.OutputContainer, "mkv", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
inputModifier += " -noaccurate_seek";
|
||||
//inputModifier += " -noaccurate_seek";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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" />
|
||||
|
|
72
MediaBrowser.Common/Threading/PeriodicTimer.cs
Normal file
72
MediaBrowser.Common/Threading/PeriodicTimer.cs
Normal file
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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" />
|
||||
|
|
13
MediaBrowser.Controller/Power/IPowerManagement.cs
Normal file
13
MediaBrowser.Controller/Power/IPowerManagement.cs
Normal file
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -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++)
|
||||
{
|
||||
if (i > 0)
|
||||
{
|
||||
await Task.Delay(500).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
var dgram = new Datagram(endpoint, localAddress, _logger, msg, isBroadcast, enableDebugLogging);
|
||||
|
||||
if (_messageQueue.Count == 0)
|
||||
{
|
||||
dgram.Send();
|
||||
}
|
||||
else
|
||||
{
|
||||
_messageQueue.Enqueue(dgram);
|
||||
queued = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (queued)
|
||||
{
|
||||
StartQueueTimer();
|
||||
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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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" };
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -76,50 +76,50 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
|
|||
{
|
||||
var seasonNumber = episodeInfo.SeasonNumber;
|
||||
|
||||
result.ExtractedSeasonNumber = seasonNumber;
|
||||
result.ExtractedSeasonNumber = seasonNumber;
|
||||
|
||||
// Passing in true will include a few extra regex's
|
||||
var episodeNumber = episodeInfo.EpisodeNumber;
|
||||
// Passing in true will include a few extra regex's
|
||||
var episodeNumber = episodeInfo.EpisodeNumber;
|
||||
|
||||
result.ExtractedEpisodeNumber = episodeNumber;
|
||||
result.ExtractedEpisodeNumber = episodeNumber;
|
||||
|
||||
var premiereDate = episodeInfo.IsByDate ?
|
||||
new DateTime(episodeInfo.Year.Value, episodeInfo.Month.Value, episodeInfo.Day.Value) :
|
||||
(DateTime?)null;
|
||||
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);
|
||||
}
|
||||
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;
|
||||
var endingEpisodeNumber = episodeInfo.EndingEpsiodeNumber;
|
||||
|
||||
result.ExtractedEndingEpisodeNumber = endingEpisodeNumber;
|
||||
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);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -1,11 +1,30 @@
|
|||
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>
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
{
|
||||
|
|
|
@ -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
|
||||
{
|
||||
epgData = GetEpgDataForChannel(info.ChannelId);
|
||||
}
|
||||
var instance = _seriesTimerProvider.GetAll().FirstOrDefault(i => string.Equals(i.Id, info.Id, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
await UpdateTimersForSeriesTimer(epgData, info).ConfigureAwait(false);
|
||||
if (instance != null)
|
||||
{
|
||||
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;
|
||||
|
||||
_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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,7 +1156,12 @@ 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))
|
||||
{
|
||||
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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))
|
||||
{
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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");
|
||||
|
|
Loading…
Reference in New Issue
Block a user