mirror of
https://github.com/jellyfin/jellyfin.git
synced 2026-01-15 23:58:57 +00:00
move classes to portable server project
This commit is contained in:
@@ -51,6 +51,15 @@
|
||||
<Compile Include="Collections\CollectionManager.cs" />
|
||||
<Compile Include="Devices\DeviceManager.cs" />
|
||||
<Compile Include="Dto\DtoService.cs" />
|
||||
<Compile Include="EntryPoints\AutomaticRestartEntryPoint.cs" />
|
||||
<Compile Include="EntryPoints\LibraryChangedNotifier.cs" />
|
||||
<Compile Include="EntryPoints\LoadRegistrations.cs" />
|
||||
<Compile Include="EntryPoints\RecordingNotifier.cs" />
|
||||
<Compile Include="EntryPoints\RefreshUsersMetadata.cs" />
|
||||
<Compile Include="EntryPoints\ServerEventNotifier.cs" />
|
||||
<Compile Include="EntryPoints\UsageEntryPoint.cs" />
|
||||
<Compile Include="EntryPoints\UsageReporter.cs" />
|
||||
<Compile Include="EntryPoints\UserDataChangeNotifier.cs" />
|
||||
<Compile Include="FileOrganization\EpisodeFileOrganizer.cs" />
|
||||
<Compile Include="FileOrganization\Extensions.cs" />
|
||||
<Compile Include="FileOrganization\FileOrganizationNotifier.cs" />
|
||||
@@ -100,6 +109,8 @@
|
||||
<Compile Include="Library\Validators\StudiosValidator.cs" />
|
||||
<Compile Include="Library\Validators\YearsPostScanTask.cs" />
|
||||
<Compile Include="Logging\PatternsLogger.cs" />
|
||||
<Compile Include="MediaEncoder\EncodingManager.cs" />
|
||||
<Compile Include="News\NewsEntryPoint.cs" />
|
||||
<Compile Include="News\NewsService.cs" />
|
||||
<Compile Include="Notifications\CoreNotificationTypes.cs" />
|
||||
<Compile Include="Notifications\IConfigurableNotificationService.cs" />
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Plugins;
|
||||
using MediaBrowser.Controller.Session;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using MediaBrowser.Model.Tasks;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Controller.LiveTv;
|
||||
using MediaBrowser.Model.LiveTv;
|
||||
using MediaBrowser.Model.Threading;
|
||||
|
||||
namespace Emby.Server.Implementations.EntryPoints
|
||||
{
|
||||
public class AutomaticRestartEntryPoint : IServerEntryPoint
|
||||
{
|
||||
private readonly IServerApplicationHost _appHost;
|
||||
private readonly ILogger _logger;
|
||||
private readonly ITaskManager _iTaskManager;
|
||||
private readonly ISessionManager _sessionManager;
|
||||
private readonly IServerConfigurationManager _config;
|
||||
private readonly ILiveTvManager _liveTvManager;
|
||||
private readonly ITimerFactory _timerFactory;
|
||||
|
||||
private ITimer _timer;
|
||||
|
||||
public AutomaticRestartEntryPoint(IServerApplicationHost appHost, ILogger logger, ITaskManager iTaskManager, ISessionManager sessionManager, IServerConfigurationManager config, ILiveTvManager liveTvManager, ITimerFactory timerFactory)
|
||||
{
|
||||
_appHost = appHost;
|
||||
_logger = logger;
|
||||
_iTaskManager = iTaskManager;
|
||||
_sessionManager = sessionManager;
|
||||
_config = config;
|
||||
_liveTvManager = liveTvManager;
|
||||
_timerFactory = timerFactory;
|
||||
}
|
||||
|
||||
public void Run()
|
||||
{
|
||||
if (_appHost.CanSelfRestart)
|
||||
{
|
||||
_appHost.HasPendingRestartChanged += _appHost_HasPendingRestartChanged;
|
||||
}
|
||||
}
|
||||
|
||||
void _appHost_HasPendingRestartChanged(object sender, EventArgs e)
|
||||
{
|
||||
DisposeTimer();
|
||||
|
||||
if (_appHost.HasPendingRestart)
|
||||
{
|
||||
_timer = _timerFactory.Create(TimerCallback, null, TimeSpan.FromMinutes(10), TimeSpan.FromMinutes(10));
|
||||
}
|
||||
}
|
||||
|
||||
private async void TimerCallback(object state)
|
||||
{
|
||||
if (_config.Configuration.EnableAutomaticRestart)
|
||||
{
|
||||
var isIdle = await IsIdle().ConfigureAwait(false);
|
||||
|
||||
if (isIdle)
|
||||
{
|
||||
DisposeTimer();
|
||||
|
||||
try
|
||||
{
|
||||
_appHost.Restart();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.ErrorException("Error restarting server", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<bool> IsIdle()
|
||||
{
|
||||
if (_iTaskManager.ScheduledTasks.Any(i => i.State != TaskState.Idle))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_liveTvManager.Services.Count == 1)
|
||||
{
|
||||
try
|
||||
{
|
||||
var timers = await _liveTvManager.GetTimers(new TimerQuery(), CancellationToken.None).ConfigureAwait(false);
|
||||
if (timers.Items.Any(i => i.Status == RecordingStatus.InProgress))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.ErrorException("Error getting timers", ex);
|
||||
}
|
||||
}
|
||||
|
||||
var now = DateTime.UtcNow;
|
||||
|
||||
return !_sessionManager.Sessions.Any(i => (now - i.LastActivityDate).TotalMinutes < 30);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_appHost.HasPendingRestartChanged -= _appHost_HasPendingRestartChanged;
|
||||
|
||||
DisposeTimer();
|
||||
}
|
||||
|
||||
private void DisposeTimer()
|
||||
{
|
||||
if (_timer != null)
|
||||
{
|
||||
_timer.Dispose();
|
||||
_timer = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,343 @@
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.Plugins;
|
||||
using MediaBrowser.Controller.Session;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using MediaBrowser.Controller.Entities.Audio;
|
||||
using MediaBrowser.Model.Extensions;
|
||||
using MediaBrowser.Model.Threading;
|
||||
|
||||
namespace Emby.Server.Implementations.EntryPoints
|
||||
{
|
||||
public class LibraryChangedNotifier : IServerEntryPoint
|
||||
{
|
||||
/// <summary>
|
||||
/// The _library manager
|
||||
/// </summary>
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
|
||||
private readonly ISessionManager _sessionManager;
|
||||
private readonly IUserManager _userManager;
|
||||
private readonly ILogger _logger;
|
||||
private readonly ITimerFactory _timerFactory;
|
||||
|
||||
/// <summary>
|
||||
/// The _library changed sync lock
|
||||
/// </summary>
|
||||
private readonly object _libraryChangedSyncLock = new object();
|
||||
|
||||
private readonly List<Folder> _foldersAddedTo = new List<Folder>();
|
||||
private readonly List<Folder> _foldersRemovedFrom = new List<Folder>();
|
||||
|
||||
private readonly List<BaseItem> _itemsAdded = new List<BaseItem>();
|
||||
private readonly List<BaseItem> _itemsRemoved = new List<BaseItem>();
|
||||
private readonly List<BaseItem> _itemsUpdated = new List<BaseItem>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the library update timer.
|
||||
/// </summary>
|
||||
/// <value>The library update timer.</value>
|
||||
private ITimer LibraryUpdateTimer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The library update duration
|
||||
/// </summary>
|
||||
private const int LibraryUpdateDuration = 5000;
|
||||
|
||||
public LibraryChangedNotifier(ILibraryManager libraryManager, ISessionManager sessionManager, IUserManager userManager, ILogger logger, ITimerFactory timerFactory)
|
||||
{
|
||||
_libraryManager = libraryManager;
|
||||
_sessionManager = sessionManager;
|
||||
_userManager = userManager;
|
||||
_logger = logger;
|
||||
_timerFactory = timerFactory;
|
||||
}
|
||||
|
||||
public void Run()
|
||||
{
|
||||
_libraryManager.ItemAdded += libraryManager_ItemAdded;
|
||||
_libraryManager.ItemUpdated += libraryManager_ItemUpdated;
|
||||
_libraryManager.ItemRemoved += libraryManager_ItemRemoved;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles the ItemAdded event of the libraryManager control.
|
||||
/// </summary>
|
||||
/// <param name="sender">The source of the event.</param>
|
||||
/// <param name="e">The <see cref="ItemChangeEventArgs"/> instance containing the event data.</param>
|
||||
void libraryManager_ItemAdded(object sender, ItemChangeEventArgs e)
|
||||
{
|
||||
if (!FilterItem(e.Item))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
lock (_libraryChangedSyncLock)
|
||||
{
|
||||
if (LibraryUpdateTimer == null)
|
||||
{
|
||||
LibraryUpdateTimer = _timerFactory.Create(LibraryUpdateTimerCallback, null, LibraryUpdateDuration,
|
||||
Timeout.Infinite);
|
||||
}
|
||||
else
|
||||
{
|
||||
LibraryUpdateTimer.Change(LibraryUpdateDuration, Timeout.Infinite);
|
||||
}
|
||||
|
||||
if (e.Item.Parent != null)
|
||||
{
|
||||
_foldersAddedTo.Add(e.Item.Parent);
|
||||
}
|
||||
|
||||
_itemsAdded.Add(e.Item);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles the ItemUpdated event of the libraryManager control.
|
||||
/// </summary>
|
||||
/// <param name="sender">The source of the event.</param>
|
||||
/// <param name="e">The <see cref="ItemChangeEventArgs"/> instance containing the event data.</param>
|
||||
void libraryManager_ItemUpdated(object sender, ItemChangeEventArgs e)
|
||||
{
|
||||
if (!FilterItem(e.Item))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
lock (_libraryChangedSyncLock)
|
||||
{
|
||||
if (LibraryUpdateTimer == null)
|
||||
{
|
||||
LibraryUpdateTimer = _timerFactory.Create(LibraryUpdateTimerCallback, null, LibraryUpdateDuration,
|
||||
Timeout.Infinite);
|
||||
}
|
||||
else
|
||||
{
|
||||
LibraryUpdateTimer.Change(LibraryUpdateDuration, Timeout.Infinite);
|
||||
}
|
||||
|
||||
_itemsUpdated.Add(e.Item);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles the ItemRemoved event of the libraryManager control.
|
||||
/// </summary>
|
||||
/// <param name="sender">The source of the event.</param>
|
||||
/// <param name="e">The <see cref="ItemChangeEventArgs"/> instance containing the event data.</param>
|
||||
void libraryManager_ItemRemoved(object sender, ItemChangeEventArgs e)
|
||||
{
|
||||
if (!FilterItem(e.Item))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
lock (_libraryChangedSyncLock)
|
||||
{
|
||||
if (LibraryUpdateTimer == null)
|
||||
{
|
||||
LibraryUpdateTimer = _timerFactory.Create(LibraryUpdateTimerCallback, null, LibraryUpdateDuration,
|
||||
Timeout.Infinite);
|
||||
}
|
||||
else
|
||||
{
|
||||
LibraryUpdateTimer.Change(LibraryUpdateDuration, Timeout.Infinite);
|
||||
}
|
||||
|
||||
if (e.Item.Parent != null)
|
||||
{
|
||||
_foldersRemovedFrom.Add(e.Item.Parent);
|
||||
}
|
||||
|
||||
_itemsRemoved.Add(e.Item);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Libraries the update timer callback.
|
||||
/// </summary>
|
||||
/// <param name="state">The state.</param>
|
||||
private void LibraryUpdateTimerCallback(object state)
|
||||
{
|
||||
lock (_libraryChangedSyncLock)
|
||||
{
|
||||
// Remove dupes in case some were saved multiple times
|
||||
var foldersAddedTo = _foldersAddedTo.DistinctBy(i => i.Id).ToList();
|
||||
|
||||
var foldersRemovedFrom = _foldersRemovedFrom.DistinctBy(i => i.Id).ToList();
|
||||
|
||||
var itemsUpdated = _itemsUpdated
|
||||
.Where(i => !_itemsAdded.Contains(i))
|
||||
.DistinctBy(i => i.Id)
|
||||
.ToList();
|
||||
|
||||
SendChangeNotifications(_itemsAdded.ToList(), itemsUpdated, _itemsRemoved.ToList(), foldersAddedTo, foldersRemovedFrom, CancellationToken.None);
|
||||
|
||||
if (LibraryUpdateTimer != null)
|
||||
{
|
||||
LibraryUpdateTimer.Dispose();
|
||||
LibraryUpdateTimer = null;
|
||||
}
|
||||
|
||||
_itemsAdded.Clear();
|
||||
_itemsRemoved.Clear();
|
||||
_itemsUpdated.Clear();
|
||||
_foldersAddedTo.Clear();
|
||||
_foldersRemovedFrom.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends the change notifications.
|
||||
/// </summary>
|
||||
/// <param name="itemsAdded">The items added.</param>
|
||||
/// <param name="itemsUpdated">The items updated.</param>
|
||||
/// <param name="itemsRemoved">The items removed.</param>
|
||||
/// <param name="foldersAddedTo">The folders added to.</param>
|
||||
/// <param name="foldersRemovedFrom">The folders removed from.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
private async void SendChangeNotifications(List<BaseItem> itemsAdded, List<BaseItem> itemsUpdated, List<BaseItem> itemsRemoved, List<Folder> foldersAddedTo, List<Folder> foldersRemovedFrom, CancellationToken cancellationToken)
|
||||
{
|
||||
foreach (var user in _userManager.Users.ToList())
|
||||
{
|
||||
var id = user.Id;
|
||||
var userSessions = _sessionManager.Sessions
|
||||
.Where(u => u.UserId.HasValue && u.UserId.Value == id && u.SessionController != null && u.IsActive)
|
||||
.ToList();
|
||||
|
||||
if (userSessions.Count > 0)
|
||||
{
|
||||
LibraryUpdateInfo info;
|
||||
|
||||
try
|
||||
{
|
||||
info = GetLibraryUpdateInfo(itemsAdded, itemsUpdated, itemsRemoved, foldersAddedTo,
|
||||
foldersRemovedFrom, id);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.ErrorException("Error in GetLibraryUpdateInfo", ex);
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var userSession in userSessions)
|
||||
{
|
||||
try
|
||||
{
|
||||
await userSession.SessionController.SendLibraryUpdateInfo(info, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.ErrorException("Error sending LibraryChanged message", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the library update info.
|
||||
/// </summary>
|
||||
/// <param name="itemsAdded">The items added.</param>
|
||||
/// <param name="itemsUpdated">The items updated.</param>
|
||||
/// <param name="itemsRemoved">The items removed.</param>
|
||||
/// <param name="foldersAddedTo">The folders added to.</param>
|
||||
/// <param name="foldersRemovedFrom">The folders removed from.</param>
|
||||
/// <param name="userId">The user id.</param>
|
||||
/// <returns>LibraryUpdateInfo.</returns>
|
||||
private LibraryUpdateInfo GetLibraryUpdateInfo(IEnumerable<BaseItem> itemsAdded, IEnumerable<BaseItem> itemsUpdated, IEnumerable<BaseItem> itemsRemoved, IEnumerable<Folder> foldersAddedTo, IEnumerable<Folder> foldersRemovedFrom, Guid userId)
|
||||
{
|
||||
var user = _userManager.GetUserById(userId);
|
||||
|
||||
return new LibraryUpdateInfo
|
||||
{
|
||||
ItemsAdded = itemsAdded.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)).Select(i => i.Id.ToString("N")).Distinct().ToList(),
|
||||
|
||||
ItemsUpdated = itemsUpdated.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)).Select(i => i.Id.ToString("N")).Distinct().ToList(),
|
||||
|
||||
ItemsRemoved = itemsRemoved.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user, true)).Select(i => i.Id.ToString("N")).Distinct().ToList(),
|
||||
|
||||
FoldersAddedTo = foldersAddedTo.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)).Select(i => i.Id.ToString("N")).Distinct().ToList(),
|
||||
|
||||
FoldersRemovedFrom = foldersRemovedFrom.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)).Select(i => i.Id.ToString("N")).Distinct().ToList()
|
||||
};
|
||||
}
|
||||
|
||||
private bool FilterItem(BaseItem item)
|
||||
{
|
||||
if (!item.IsFolder && item.LocationType == LocationType.Virtual)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (item is IItemByName && !(item is MusicArtist))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return item.SourceType == SourceType.Library;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Translates the physical item to user library.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="item">The item.</param>
|
||||
/// <param name="user">The user.</param>
|
||||
/// <param name="includeIfNotFound">if set to <c>true</c> [include if not found].</param>
|
||||
/// <returns>IEnumerable{``0}.</returns>
|
||||
private IEnumerable<T> TranslatePhysicalItemToUserLibrary<T>(T item, User user, bool includeIfNotFound = false)
|
||||
where T : BaseItem
|
||||
{
|
||||
// If the physical root changed, return the user root
|
||||
if (item is AggregateFolder)
|
||||
{
|
||||
return new[] { user.RootFolder as T };
|
||||
}
|
||||
|
||||
// Return it only if it's in the user's library
|
||||
if (includeIfNotFound || item.IsVisibleStandalone(user))
|
||||
{
|
||||
return new[] { item };
|
||||
}
|
||||
|
||||
return new T[] { };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases unmanaged and - optionally - managed resources.
|
||||
/// </summary>
|
||||
/// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
|
||||
protected virtual void Dispose(bool dispose)
|
||||
{
|
||||
if (dispose)
|
||||
{
|
||||
if (LibraryUpdateTimer != null)
|
||||
{
|
||||
LibraryUpdateTimer.Dispose();
|
||||
LibraryUpdateTimer = null;
|
||||
}
|
||||
|
||||
_libraryManager.ItemAdded -= libraryManager_ItemAdded;
|
||||
_libraryManager.ItemUpdated -= libraryManager_ItemUpdated;
|
||||
_libraryManager.ItemRemoved -= libraryManager_ItemRemoved;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
73
Emby.Server.Implementations/EntryPoints/LoadRegistrations.cs
Normal file
73
Emby.Server.Implementations/EntryPoints/LoadRegistrations.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
using MediaBrowser.Common.Security;
|
||||
using MediaBrowser.Controller.Plugins;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Model.Threading;
|
||||
|
||||
namespace Emby.Server.Implementations.EntryPoints
|
||||
{
|
||||
/// <summary>
|
||||
/// Class LoadRegistrations
|
||||
/// </summary>
|
||||
public class LoadRegistrations : IServerEntryPoint
|
||||
{
|
||||
/// <summary>
|
||||
/// The _security manager
|
||||
/// </summary>
|
||||
private readonly ISecurityManager _securityManager;
|
||||
|
||||
/// <summary>
|
||||
/// The _logger
|
||||
/// </summary>
|
||||
private readonly ILogger _logger;
|
||||
|
||||
private ITimer _timer;
|
||||
private readonly ITimerFactory _timerFactory;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LoadRegistrations" /> class.
|
||||
/// </summary>
|
||||
/// <param name="securityManager">The security manager.</param>
|
||||
/// <param name="logManager">The log manager.</param>
|
||||
public LoadRegistrations(ISecurityManager securityManager, ILogManager logManager, ITimerFactory timerFactory)
|
||||
{
|
||||
_securityManager = securityManager;
|
||||
_timerFactory = timerFactory;
|
||||
|
||||
_logger = logManager.GetLogger("Registration Loader");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs this instance.
|
||||
/// </summary>
|
||||
public void Run()
|
||||
{
|
||||
_timer = _timerFactory.Create(s => LoadAllRegistrations(), null, TimeSpan.FromMilliseconds(100), TimeSpan.FromHours(12));
|
||||
}
|
||||
|
||||
private async Task LoadAllRegistrations()
|
||||
{
|
||||
try
|
||||
{
|
||||
await _securityManager.LoadAllRegistrationInfo().ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.ErrorException("Error loading registration info", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (_timer != null)
|
||||
{
|
||||
_timer.Dispose();
|
||||
_timer = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
77
Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs
Normal file
77
Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs
Normal file
@@ -0,0 +1,77 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.LiveTv;
|
||||
using MediaBrowser.Controller.Plugins;
|
||||
using MediaBrowser.Controller.Session;
|
||||
using MediaBrowser.Model.Logging;
|
||||
|
||||
namespace Emby.Server.Implementations.EntryPoints
|
||||
{
|
||||
public class RecordingNotifier : IServerEntryPoint
|
||||
{
|
||||
private readonly ILiveTvManager _liveTvManager;
|
||||
private readonly ISessionManager _sessionManager;
|
||||
private readonly IUserManager _userManager;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public RecordingNotifier(ISessionManager sessionManager, IUserManager userManager, ILogger logger, ILiveTvManager liveTvManager)
|
||||
{
|
||||
_sessionManager = sessionManager;
|
||||
_userManager = userManager;
|
||||
_logger = logger;
|
||||
_liveTvManager = liveTvManager;
|
||||
}
|
||||
|
||||
public void Run()
|
||||
{
|
||||
_liveTvManager.TimerCancelled += _liveTvManager_TimerCancelled;
|
||||
_liveTvManager.SeriesTimerCancelled += _liveTvManager_SeriesTimerCancelled;
|
||||
_liveTvManager.TimerCreated += _liveTvManager_TimerCreated;
|
||||
_liveTvManager.SeriesTimerCreated += _liveTvManager_SeriesTimerCreated;
|
||||
}
|
||||
|
||||
private void _liveTvManager_SeriesTimerCreated(object sender, MediaBrowser.Model.Events.GenericEventArgs<TimerEventInfo> e)
|
||||
{
|
||||
SendMessage("SeriesTimerCreated", e.Argument);
|
||||
}
|
||||
|
||||
private void _liveTvManager_TimerCreated(object sender, MediaBrowser.Model.Events.GenericEventArgs<TimerEventInfo> e)
|
||||
{
|
||||
SendMessage("TimerCreated", e.Argument);
|
||||
}
|
||||
|
||||
private void _liveTvManager_SeriesTimerCancelled(object sender, MediaBrowser.Model.Events.GenericEventArgs<TimerEventInfo> e)
|
||||
{
|
||||
SendMessage("SeriesTimerCancelled", e.Argument);
|
||||
}
|
||||
|
||||
private void _liveTvManager_TimerCancelled(object sender, MediaBrowser.Model.Events.GenericEventArgs<TimerEventInfo> e)
|
||||
{
|
||||
SendMessage("TimerCancelled", e.Argument);
|
||||
}
|
||||
|
||||
private async void SendMessage(string name, TimerEventInfo info)
|
||||
{
|
||||
var users = _userManager.Users.Where(i => i.Policy.EnableLiveTvAccess).Select(i => i.Id.ToString("N")).ToList();
|
||||
|
||||
try
|
||||
{
|
||||
await _sessionManager.SendMessageToUserSessions<TimerEventInfo>(users, name, info, CancellationToken.None);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.ErrorException("Error sending message", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_liveTvManager.TimerCancelled -= _liveTvManager_TimerCancelled;
|
||||
_liveTvManager.SeriesTimerCancelled -= _liveTvManager_SeriesTimerCancelled;
|
||||
_liveTvManager.TimerCreated -= _liveTvManager_TimerCreated;
|
||||
_liveTvManager.SeriesTimerCreated -= _liveTvManager_SeriesTimerCreated;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.Plugins;
|
||||
using System.Threading;
|
||||
|
||||
namespace Emby.Server.Implementations.EntryPoints
|
||||
{
|
||||
/// <summary>
|
||||
/// Class RefreshUsersMetadata
|
||||
/// </summary>
|
||||
public class RefreshUsersMetadata : IServerEntryPoint
|
||||
{
|
||||
/// <summary>
|
||||
/// The _user manager
|
||||
/// </summary>
|
||||
private readonly IUserManager _userManager;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RefreshUsersMetadata" /> class.
|
||||
/// </summary>
|
||||
/// <param name="userManager">The user manager.</param>
|
||||
public RefreshUsersMetadata(IUserManager userManager)
|
||||
{
|
||||
_userManager = userManager;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs this instance.
|
||||
/// </summary>
|
||||
public async void Run()
|
||||
{
|
||||
await _userManager.RefreshUsersMetadata(CancellationToken.None).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
203
Emby.Server.Implementations/EntryPoints/ServerEventNotifier.cs
Normal file
203
Emby.Server.Implementations/EntryPoints/ServerEventNotifier.cs
Normal file
@@ -0,0 +1,203 @@
|
||||
using MediaBrowser.Common.Plugins;
|
||||
using MediaBrowser.Common.Updates;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.Net;
|
||||
using MediaBrowser.Controller.Plugins;
|
||||
using MediaBrowser.Controller.Session;
|
||||
using MediaBrowser.Controller.Sync;
|
||||
using MediaBrowser.Model.Events;
|
||||
using MediaBrowser.Model.Sync;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using MediaBrowser.Model.Tasks;
|
||||
|
||||
namespace Emby.Server.Implementations.EntryPoints
|
||||
{
|
||||
/// <summary>
|
||||
/// Class WebSocketEvents
|
||||
/// </summary>
|
||||
public class ServerEventNotifier : IServerEntryPoint
|
||||
{
|
||||
/// <summary>
|
||||
/// The _server manager
|
||||
/// </summary>
|
||||
private readonly IServerManager _serverManager;
|
||||
|
||||
/// <summary>
|
||||
/// The _user manager
|
||||
/// </summary>
|
||||
private readonly IUserManager _userManager;
|
||||
|
||||
/// <summary>
|
||||
/// The _installation manager
|
||||
/// </summary>
|
||||
private readonly IInstallationManager _installationManager;
|
||||
|
||||
/// <summary>
|
||||
/// The _kernel
|
||||
/// </summary>
|
||||
private readonly IServerApplicationHost _appHost;
|
||||
|
||||
/// <summary>
|
||||
/// The _task manager
|
||||
/// </summary>
|
||||
private readonly ITaskManager _taskManager;
|
||||
|
||||
private readonly ISessionManager _sessionManager;
|
||||
private readonly ISyncManager _syncManager;
|
||||
|
||||
public ServerEventNotifier(IServerManager serverManager, IServerApplicationHost appHost, IUserManager userManager, IInstallationManager installationManager, ITaskManager taskManager, ISessionManager sessionManager, ISyncManager syncManager)
|
||||
{
|
||||
_serverManager = serverManager;
|
||||
_userManager = userManager;
|
||||
_installationManager = installationManager;
|
||||
_appHost = appHost;
|
||||
_taskManager = taskManager;
|
||||
_sessionManager = sessionManager;
|
||||
_syncManager = syncManager;
|
||||
}
|
||||
|
||||
public void Run()
|
||||
{
|
||||
_userManager.UserDeleted += userManager_UserDeleted;
|
||||
_userManager.UserUpdated += userManager_UserUpdated;
|
||||
_userManager.UserConfigurationUpdated += _userManager_UserConfigurationUpdated;
|
||||
|
||||
_appHost.HasPendingRestartChanged += kernel_HasPendingRestartChanged;
|
||||
|
||||
_installationManager.PluginUninstalled += InstallationManager_PluginUninstalled;
|
||||
_installationManager.PackageInstalling += _installationManager_PackageInstalling;
|
||||
_installationManager.PackageInstallationCancelled += _installationManager_PackageInstallationCancelled;
|
||||
_installationManager.PackageInstallationCompleted += _installationManager_PackageInstallationCompleted;
|
||||
_installationManager.PackageInstallationFailed += _installationManager_PackageInstallationFailed;
|
||||
|
||||
_taskManager.TaskCompleted += _taskManager_TaskCompleted;
|
||||
_syncManager.SyncJobCreated += _syncManager_SyncJobCreated;
|
||||
_syncManager.SyncJobCancelled += _syncManager_SyncJobCancelled;
|
||||
}
|
||||
|
||||
void _syncManager_SyncJobCancelled(object sender, GenericEventArgs<SyncJob> e)
|
||||
{
|
||||
_sessionManager.SendMessageToUserDeviceSessions(e.Argument.TargetId, "SyncJobCancelled", e.Argument, CancellationToken.None);
|
||||
}
|
||||
|
||||
void _syncManager_SyncJobCreated(object sender, GenericEventArgs<SyncJobCreationResult> e)
|
||||
{
|
||||
_sessionManager.SendMessageToUserDeviceSessions(e.Argument.Job.TargetId, "SyncJobCreated", e.Argument, CancellationToken.None);
|
||||
}
|
||||
|
||||
void _installationManager_PackageInstalling(object sender, InstallationEventArgs e)
|
||||
{
|
||||
_serverManager.SendWebSocketMessage("PackageInstalling", e.InstallationInfo);
|
||||
}
|
||||
|
||||
void _installationManager_PackageInstallationCancelled(object sender, InstallationEventArgs e)
|
||||
{
|
||||
_serverManager.SendWebSocketMessage("PackageInstallationCancelled", e.InstallationInfo);
|
||||
}
|
||||
|
||||
void _installationManager_PackageInstallationCompleted(object sender, InstallationEventArgs e)
|
||||
{
|
||||
_serverManager.SendWebSocketMessage("PackageInstallationCompleted", e.InstallationInfo);
|
||||
}
|
||||
|
||||
void _installationManager_PackageInstallationFailed(object sender, InstallationFailedEventArgs e)
|
||||
{
|
||||
_serverManager.SendWebSocketMessage("PackageInstallationFailed", e.InstallationInfo);
|
||||
}
|
||||
|
||||
void _taskManager_TaskCompleted(object sender, TaskCompletionEventArgs e)
|
||||
{
|
||||
_serverManager.SendWebSocketMessage("ScheduledTaskEnded", e.Result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Installations the manager_ plugin uninstalled.
|
||||
/// </summary>
|
||||
/// <param name="sender">The sender.</param>
|
||||
/// <param name="e">The e.</param>
|
||||
void InstallationManager_PluginUninstalled(object sender, GenericEventArgs<IPlugin> e)
|
||||
{
|
||||
_serverManager.SendWebSocketMessage("PluginUninstalled", e.Argument.GetPluginInfo());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles the HasPendingRestartChanged event of the kernel control.
|
||||
/// </summary>
|
||||
/// <param name="sender">The source of the event.</param>
|
||||
/// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param>
|
||||
void kernel_HasPendingRestartChanged(object sender, EventArgs e)
|
||||
{
|
||||
_sessionManager.SendRestartRequiredNotification(CancellationToken.None);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Users the manager_ user updated.
|
||||
/// </summary>
|
||||
/// <param name="sender">The sender.</param>
|
||||
/// <param name="e">The e.</param>
|
||||
void userManager_UserUpdated(object sender, GenericEventArgs<User> e)
|
||||
{
|
||||
var dto = _userManager.GetUserDto(e.Argument);
|
||||
|
||||
SendMessageToUserSession(e.Argument, "UserUpdated", dto);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Users the manager_ user deleted.
|
||||
/// </summary>
|
||||
/// <param name="sender">The sender.</param>
|
||||
/// <param name="e">The e.</param>
|
||||
void userManager_UserDeleted(object sender, GenericEventArgs<User> e)
|
||||
{
|
||||
SendMessageToUserSession(e.Argument, "UserDeleted", e.Argument.Id.ToString("N"));
|
||||
}
|
||||
|
||||
void _userManager_UserConfigurationUpdated(object sender, GenericEventArgs<User> e)
|
||||
{
|
||||
var dto = _userManager.GetUserDto(e.Argument);
|
||||
|
||||
SendMessageToUserSession(e.Argument, "UserConfigurationUpdated", dto);
|
||||
}
|
||||
|
||||
private async void SendMessageToUserSession<T>(User user, string name, T data)
|
||||
{
|
||||
await _sessionManager.SendMessageToUserSessions(new List<string> { user.Id.ToString("N") }, name, data, CancellationToken.None);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases unmanaged and - optionally - managed resources.
|
||||
/// </summary>
|
||||
/// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
|
||||
protected virtual void Dispose(bool dispose)
|
||||
{
|
||||
if (dispose)
|
||||
{
|
||||
_userManager.UserDeleted -= userManager_UserDeleted;
|
||||
_userManager.UserUpdated -= userManager_UserUpdated;
|
||||
_userManager.UserConfigurationUpdated -= _userManager_UserConfigurationUpdated;
|
||||
|
||||
_installationManager.PluginUninstalled -= InstallationManager_PluginUninstalled;
|
||||
_installationManager.PackageInstalling -= _installationManager_PackageInstalling;
|
||||
_installationManager.PackageInstallationCancelled -= _installationManager_PackageInstallationCancelled;
|
||||
_installationManager.PackageInstallationCompleted -= _installationManager_PackageInstallationCompleted;
|
||||
_installationManager.PackageInstallationFailed -= _installationManager_PackageInstallationFailed;
|
||||
|
||||
_appHost.HasPendingRestartChanged -= kernel_HasPendingRestartChanged;
|
||||
_syncManager.SyncJobCreated -= _syncManager_SyncJobCreated;
|
||||
_syncManager.SyncJobCancelled -= _syncManager_SyncJobCancelled;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
133
Emby.Server.Implementations/EntryPoints/UsageEntryPoint.cs
Normal file
133
Emby.Server.Implementations/EntryPoints/UsageEntryPoint.cs
Normal file
@@ -0,0 +1,133 @@
|
||||
using MediaBrowser.Common;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.Plugins;
|
||||
using MediaBrowser.Controller.Session;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
|
||||
namespace Emby.Server.Implementations.EntryPoints
|
||||
{
|
||||
/// <summary>
|
||||
/// Class UsageEntryPoint
|
||||
/// </summary>
|
||||
public class UsageEntryPoint : IServerEntryPoint
|
||||
{
|
||||
private readonly IApplicationHost _applicationHost;
|
||||
private readonly IHttpClient _httpClient;
|
||||
private readonly ILogger _logger;
|
||||
private readonly ISessionManager _sessionManager;
|
||||
private readonly IUserManager _userManager;
|
||||
private readonly IServerConfigurationManager _config;
|
||||
|
||||
private readonly ConcurrentDictionary<Guid, ClientInfo> _apps = new ConcurrentDictionary<Guid, ClientInfo>();
|
||||
|
||||
public UsageEntryPoint(ILogger logger, IApplicationHost applicationHost, IHttpClient httpClient, ISessionManager sessionManager, IUserManager userManager, IServerConfigurationManager config)
|
||||
{
|
||||
_logger = logger;
|
||||
_applicationHost = applicationHost;
|
||||
_httpClient = httpClient;
|
||||
_sessionManager = sessionManager;
|
||||
_userManager = userManager;
|
||||
_config = config;
|
||||
|
||||
_sessionManager.SessionStarted += _sessionManager_SessionStarted;
|
||||
}
|
||||
|
||||
void _sessionManager_SessionStarted(object sender, SessionEventArgs e)
|
||||
{
|
||||
var session = e.SessionInfo;
|
||||
|
||||
if (!string.IsNullOrEmpty(session.Client) &&
|
||||
!string.IsNullOrEmpty(session.DeviceName) &&
|
||||
!string.IsNullOrEmpty(session.DeviceId) &&
|
||||
!string.IsNullOrEmpty(session.ApplicationVersion))
|
||||
{
|
||||
var keys = new List<string>
|
||||
{
|
||||
session.Client,
|
||||
session.DeviceName,
|
||||
session.DeviceId,
|
||||
session.ApplicationVersion
|
||||
};
|
||||
|
||||
var key = string.Join("_", keys.ToArray()).GetMD5();
|
||||
|
||||
_apps.GetOrAdd(key, guid => GetNewClientInfo(session));
|
||||
}
|
||||
}
|
||||
|
||||
private async void ReportNewSession(ClientInfo client)
|
||||
{
|
||||
if (!_config.Configuration.EnableAnonymousUsageReporting)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await new UsageReporter(_applicationHost, _httpClient, _userManager, _logger)
|
||||
.ReportAppUsage(client, CancellationToken.None)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.ErrorException("Error sending anonymous usage statistics.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private ClientInfo GetNewClientInfo(SessionInfo session)
|
||||
{
|
||||
var info = new ClientInfo
|
||||
{
|
||||
AppName = session.Client,
|
||||
AppVersion = session.ApplicationVersion,
|
||||
DeviceName = session.DeviceName,
|
||||
DeviceId = session.DeviceId
|
||||
};
|
||||
|
||||
ReportNewSession(info);
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
public async void Run()
|
||||
{
|
||||
await Task.Delay(5000).ConfigureAwait(false);
|
||||
OnTimerFired();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when [timer fired].
|
||||
/// </summary>
|
||||
private async void OnTimerFired()
|
||||
{
|
||||
if (!_config.Configuration.EnableAnonymousUsageReporting)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await new UsageReporter(_applicationHost, _httpClient, _userManager, _logger)
|
||||
.ReportServerUsage(CancellationToken.None)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.ErrorException("Error sending anonymous usage statistics.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_sessionManager.SessionStarted -= _sessionManager_SessionStarted;
|
||||
}
|
||||
}
|
||||
}
|
||||
138
Emby.Server.Implementations/EntryPoints/UsageReporter.cs
Normal file
138
Emby.Server.Implementations/EntryPoints/UsageReporter.cs
Normal file
@@ -0,0 +1,138 @@
|
||||
using MediaBrowser.Common;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Model.Connect;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Model.Logging;
|
||||
|
||||
namespace Emby.Server.Implementations.EntryPoints
|
||||
{
|
||||
public class UsageReporter
|
||||
{
|
||||
private readonly IApplicationHost _applicationHost;
|
||||
private readonly IHttpClient _httpClient;
|
||||
private readonly IUserManager _userManager;
|
||||
private readonly ILogger _logger;
|
||||
private const string MbAdminUrl = "https://www.mb3admin.com/admin/";
|
||||
|
||||
public UsageReporter(IApplicationHost applicationHost, IHttpClient httpClient, IUserManager userManager, ILogger logger)
|
||||
{
|
||||
_applicationHost = applicationHost;
|
||||
_httpClient = httpClient;
|
||||
_userManager = userManager;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task ReportServerUsage(CancellationToken cancellationToken)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
var data = new Dictionary<string, string>
|
||||
{
|
||||
{ "feature", _applicationHost.Name },
|
||||
{ "mac", _applicationHost.SystemId },
|
||||
{ "serverid", _applicationHost.SystemId },
|
||||
{ "deviceid", _applicationHost.SystemId },
|
||||
{ "ver", _applicationHost.ApplicationVersion.ToString() },
|
||||
{ "platform", _applicationHost.OperatingSystemDisplayName },
|
||||
{ "isservice", _applicationHost.IsRunningAsService.ToString().ToLower()}
|
||||
};
|
||||
|
||||
var users = _userManager.Users.ToList();
|
||||
|
||||
data["localusers"] = users.Count(i => !i.ConnectLinkType.HasValue).ToString(CultureInfo.InvariantCulture);
|
||||
data["guests"] = users.Count(i => i.ConnectLinkType.HasValue && i.ConnectLinkType.Value == UserLinkType.Guest).ToString(CultureInfo.InvariantCulture);
|
||||
data["linkedusers"] = users.Count(i => i.ConnectLinkType.HasValue && i.ConnectLinkType.Value == UserLinkType.LinkedUser).ToString(CultureInfo.InvariantCulture);
|
||||
|
||||
data["plugins"] = string.Join(",", _applicationHost.Plugins.Select(i => i.Id).ToArray());
|
||||
|
||||
var logErrors = false;
|
||||
#if DEBUG
|
||||
logErrors = true;
|
||||
#endif
|
||||
var options = new HttpRequestOptions
|
||||
{
|
||||
Url = MbAdminUrl + "service/registration/ping",
|
||||
CancellationToken = cancellationToken,
|
||||
|
||||
// Seeing block length errors
|
||||
EnableHttpCompression = false,
|
||||
|
||||
LogRequest = false,
|
||||
LogErrors = logErrors,
|
||||
BufferContent = false
|
||||
};
|
||||
|
||||
options.SetPostData(data);
|
||||
|
||||
using (var response = await _httpClient.SendAsync(options, "POST").ConfigureAwait(false))
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public async Task ReportAppUsage(ClientInfo app, CancellationToken cancellationToken)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(app.DeviceId))
|
||||
{
|
||||
throw new ArgumentException("Client info must have a device Id");
|
||||
}
|
||||
|
||||
_logger.Info("App Activity: app: {0}, version: {1}, deviceId: {2}, deviceName: {3}",
|
||||
app.AppName ?? "Unknown App",
|
||||
app.AppVersion ?? "Unknown",
|
||||
app.DeviceId,
|
||||
app.DeviceName ?? "Unknown");
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
var data = new Dictionary<string, string>
|
||||
{
|
||||
{ "feature", app.AppName ?? "Unknown App" },
|
||||
{ "serverid", _applicationHost.SystemId },
|
||||
{ "deviceid", app.DeviceId },
|
||||
{ "mac", app.DeviceId },
|
||||
{ "ver", app.AppVersion ?? "Unknown" },
|
||||
{ "platform", app.DeviceName },
|
||||
};
|
||||
|
||||
var logErrors = false;
|
||||
|
||||
#if DEBUG
|
||||
logErrors = true;
|
||||
#endif
|
||||
var options = new HttpRequestOptions
|
||||
{
|
||||
Url = MbAdminUrl + "service/registration/ping",
|
||||
CancellationToken = cancellationToken,
|
||||
|
||||
// Seeing block length errors
|
||||
EnableHttpCompression = false,
|
||||
|
||||
LogRequest = false,
|
||||
LogErrors = logErrors,
|
||||
BufferContent = false
|
||||
};
|
||||
|
||||
options.SetPostData(data);
|
||||
|
||||
using (var response = await _httpClient.SendAsync(options, "POST").ConfigureAwait(false))
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class ClientInfo
|
||||
{
|
||||
public string AppName { get; set; }
|
||||
public string AppVersion { get; set; }
|
||||
public string DeviceName { get; set; }
|
||||
public string DeviceId { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.Plugins;
|
||||
using MediaBrowser.Controller.Session;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using MediaBrowser.Model.Session;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Model.Extensions;
|
||||
using MediaBrowser.Model.Threading;
|
||||
|
||||
namespace Emby.Server.Implementations.EntryPoints
|
||||
{
|
||||
class UserDataChangeNotifier : IServerEntryPoint
|
||||
{
|
||||
private readonly ISessionManager _sessionManager;
|
||||
private readonly ILogger _logger;
|
||||
private readonly IUserDataManager _userDataManager;
|
||||
private readonly IUserManager _userManager;
|
||||
|
||||
private readonly object _syncLock = new object();
|
||||
private ITimer UpdateTimer { get; set; }
|
||||
private readonly ITimerFactory _timerFactory;
|
||||
private const int UpdateDuration = 500;
|
||||
|
||||
private readonly Dictionary<Guid, List<IHasUserData>> _changedItems = new Dictionary<Guid, List<IHasUserData>>();
|
||||
|
||||
public UserDataChangeNotifier(IUserDataManager userDataManager, ISessionManager sessionManager, ILogger logger, IUserManager userManager, ITimerFactory timerFactory)
|
||||
{
|
||||
_userDataManager = userDataManager;
|
||||
_sessionManager = sessionManager;
|
||||
_logger = logger;
|
||||
_userManager = userManager;
|
||||
_timerFactory = timerFactory;
|
||||
}
|
||||
|
||||
public void Run()
|
||||
{
|
||||
_userDataManager.UserDataSaved += _userDataManager_UserDataSaved;
|
||||
}
|
||||
|
||||
void _userDataManager_UserDataSaved(object sender, UserDataSaveEventArgs e)
|
||||
{
|
||||
if (e.SaveReason == UserDataSaveReason.PlaybackProgress)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
lock (_syncLock)
|
||||
{
|
||||
if (UpdateTimer == null)
|
||||
{
|
||||
UpdateTimer = _timerFactory.Create(UpdateTimerCallback, null, UpdateDuration,
|
||||
Timeout.Infinite);
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateTimer.Change(UpdateDuration, Timeout.Infinite);
|
||||
}
|
||||
|
||||
List<IHasUserData> keys;
|
||||
|
||||
if (!_changedItems.TryGetValue(e.UserId, out keys))
|
||||
{
|
||||
keys = new List<IHasUserData>();
|
||||
_changedItems[e.UserId] = keys;
|
||||
}
|
||||
|
||||
keys.Add(e.Item);
|
||||
|
||||
var baseItem = e.Item as BaseItem;
|
||||
|
||||
// Go up one level for indicators
|
||||
if (baseItem != null)
|
||||
{
|
||||
var parent = baseItem.GetParent();
|
||||
|
||||
if (parent != null)
|
||||
{
|
||||
keys.Add(parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateTimerCallback(object state)
|
||||
{
|
||||
lock (_syncLock)
|
||||
{
|
||||
// Remove dupes in case some were saved multiple times
|
||||
var changes = _changedItems.ToList();
|
||||
_changedItems.Clear();
|
||||
|
||||
var task = SendNotifications(changes, CancellationToken.None);
|
||||
|
||||
if (UpdateTimer != null)
|
||||
{
|
||||
UpdateTimer.Dispose();
|
||||
UpdateTimer = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SendNotifications(IEnumerable<KeyValuePair<Guid, List<IHasUserData>>> changes, CancellationToken cancellationToken)
|
||||
{
|
||||
foreach (var pair in changes)
|
||||
{
|
||||
var userId = pair.Key;
|
||||
var userSessions = _sessionManager.Sessions
|
||||
.Where(u => u.ContainsUser(userId) && u.SessionController != null && u.IsActive)
|
||||
.ToList();
|
||||
|
||||
if (userSessions.Count > 0)
|
||||
{
|
||||
var user = _userManager.GetUserById(userId);
|
||||
|
||||
var dtoList = pair.Value
|
||||
.DistinctBy(i => i.Id)
|
||||
.Select(i =>
|
||||
{
|
||||
var dto = _userDataManager.GetUserDataDto(i, user).Result;
|
||||
dto.ItemId = i.Id.ToString("N");
|
||||
return dto;
|
||||
})
|
||||
.ToList();
|
||||
|
||||
var info = new UserDataChangeInfo
|
||||
{
|
||||
UserId = userId.ToString("N"),
|
||||
|
||||
UserDataList = dtoList
|
||||
};
|
||||
|
||||
foreach (var userSession in userSessions)
|
||||
{
|
||||
try
|
||||
{
|
||||
await userSession.SessionController.SendUserDataChangeInfo(info, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.ErrorException("Error sending UserDataChanged message", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (UpdateTimer != null)
|
||||
{
|
||||
UpdateTimer.Dispose();
|
||||
UpdateTimer = null;
|
||||
}
|
||||
|
||||
_userDataManager.UserDataSaved -= _userDataManager_UserDataSaved;
|
||||
}
|
||||
}
|
||||
}
|
||||
236
Emby.Server.Implementations/MediaEncoder/EncodingManager.cs
Normal file
236
Emby.Server.Implementations/MediaEncoder/EncodingManager.cs
Normal file
@@ -0,0 +1,236 @@
|
||||
using MediaBrowser.Controller.Chapters;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.Movies;
|
||||
using MediaBrowser.Controller.Entities.TV;
|
||||
using MediaBrowser.Controller.MediaEncoding;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using MediaBrowser.Model.MediaInfo;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Common.IO;
|
||||
using MediaBrowser.Controller.IO;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Controller.Library;
|
||||
|
||||
namespace Emby.Server.Implementations.MediaEncoder
|
||||
{
|
||||
public class EncodingManager : IEncodingManager
|
||||
{
|
||||
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
|
||||
private readonly IFileSystem _fileSystem;
|
||||
private readonly ILogger _logger;
|
||||
private readonly IMediaEncoder _encoder;
|
||||
private readonly IChapterManager _chapterManager;
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
|
||||
public EncodingManager(IFileSystem fileSystem,
|
||||
ILogger logger,
|
||||
IMediaEncoder encoder,
|
||||
IChapterManager chapterManager, ILibraryManager libraryManager)
|
||||
{
|
||||
_fileSystem = fileSystem;
|
||||
_logger = logger;
|
||||
_encoder = encoder;
|
||||
_chapterManager = chapterManager;
|
||||
_libraryManager = libraryManager;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the chapter images data path.
|
||||
/// </summary>
|
||||
/// <value>The chapter images data path.</value>
|
||||
private string GetChapterImagesPath(IHasImages item)
|
||||
{
|
||||
return Path.Combine(item.GetInternalMetadataPath(), "chapters");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether [is eligible for chapter image extraction] [the specified video].
|
||||
/// </summary>
|
||||
/// <param name="video">The video.</param>
|
||||
/// <returns><c>true</c> if [is eligible for chapter image extraction] [the specified video]; otherwise, <c>false</c>.</returns>
|
||||
private bool IsEligibleForChapterImageExtraction(Video video)
|
||||
{
|
||||
if (video.IsPlaceHolder)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var libraryOptions = _libraryManager.GetLibraryOptions(video);
|
||||
if (libraryOptions != null)
|
||||
{
|
||||
if (!libraryOptions.EnableChapterImageExtraction)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Can't extract images if there are no video streams
|
||||
return video.DefaultVideoStreamIndex.HasValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The first chapter ticks
|
||||
/// </summary>
|
||||
private static readonly long FirstChapterTicks = TimeSpan.FromSeconds(15).Ticks;
|
||||
|
||||
public async Task<bool> RefreshChapterImages(ChapterImageRefreshOptions options, CancellationToken cancellationToken)
|
||||
{
|
||||
var extractImages = options.ExtractImages;
|
||||
var video = options.Video;
|
||||
var chapters = options.Chapters;
|
||||
var saveChapters = options.SaveChapters;
|
||||
|
||||
if (!IsEligibleForChapterImageExtraction(video))
|
||||
{
|
||||
extractImages = false;
|
||||
}
|
||||
|
||||
var success = true;
|
||||
var changesMade = false;
|
||||
|
||||
var runtimeTicks = video.RunTimeTicks ?? 0;
|
||||
|
||||
var currentImages = GetSavedChapterImages(video);
|
||||
|
||||
foreach (var chapter in chapters)
|
||||
{
|
||||
if (chapter.StartPositionTicks >= runtimeTicks)
|
||||
{
|
||||
_logger.Info("Stopping chapter extraction for {0} because a chapter was found with a position greater than the runtime.", video.Name);
|
||||
break;
|
||||
}
|
||||
|
||||
var path = GetChapterImagePath(video, chapter.StartPositionTicks);
|
||||
|
||||
if (!currentImages.Contains(path, StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
if (extractImages)
|
||||
{
|
||||
if (video.VideoType == VideoType.HdDvd || video.VideoType == VideoType.Iso)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (video.VideoType == VideoType.BluRay || video.VideoType == VideoType.Dvd)
|
||||
{
|
||||
if (video.PlayableStreamFileNames.Count != 1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Add some time for the first chapter to make sure we don't end up with a black image
|
||||
var time = chapter.StartPositionTicks == 0 ? TimeSpan.FromTicks(Math.Min(FirstChapterTicks, video.RunTimeTicks ?? 0)) : TimeSpan.FromTicks(chapter.StartPositionTicks);
|
||||
|
||||
var protocol = MediaProtocol.File;
|
||||
|
||||
var inputPath = MediaEncoderHelpers.GetInputArgument(_fileSystem, video.Path, protocol, null, video.PlayableStreamFileNames);
|
||||
|
||||
_fileSystem.CreateDirectory(Path.GetDirectoryName(path));
|
||||
|
||||
var container = video.Container;
|
||||
|
||||
var tempFile = await _encoder.ExtractVideoImage(inputPath, container, protocol, video.Video3DFormat, time, cancellationToken).ConfigureAwait(false);
|
||||
_fileSystem.CopyFile(tempFile, path, true);
|
||||
|
||||
try
|
||||
{
|
||||
_fileSystem.DeleteFile(tempFile);
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
chapter.ImagePath = path;
|
||||
chapter.ImageDateModified = _fileSystem.GetLastWriteTimeUtc(path);
|
||||
changesMade = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.ErrorException("Error extracting chapter images for {0}", ex, string.Join(",", video.Path));
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(chapter.ImagePath))
|
||||
{
|
||||
chapter.ImagePath = null;
|
||||
changesMade = true;
|
||||
}
|
||||
}
|
||||
else if (!string.Equals(path, chapter.ImagePath, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
chapter.ImagePath = path;
|
||||
chapter.ImageDateModified = _fileSystem.GetLastWriteTimeUtc(path);
|
||||
changesMade = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (saveChapters && changesMade)
|
||||
{
|
||||
await _chapterManager.SaveChapters(video.Id.ToString(), chapters, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
DeleteDeadImages(currentImages, chapters);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
private string GetChapterImagePath(Video video, long chapterPositionTicks)
|
||||
{
|
||||
var filename = video.DateModified.Ticks.ToString(_usCulture) + "_" + chapterPositionTicks.ToString(_usCulture) + ".jpg";
|
||||
|
||||
return Path.Combine(GetChapterImagesPath(video), filename);
|
||||
}
|
||||
|
||||
private List<string> GetSavedChapterImages(Video video)
|
||||
{
|
||||
var path = GetChapterImagesPath(video);
|
||||
|
||||
try
|
||||
{
|
||||
return _fileSystem.GetFilePaths(path)
|
||||
.ToList();
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
return new List<string>();
|
||||
}
|
||||
}
|
||||
|
||||
private void DeleteDeadImages(IEnumerable<string> images, IEnumerable<ChapterInfo> chapters)
|
||||
{
|
||||
var deadImages = images
|
||||
.Except(chapters.Select(i => i.ImagePath).Where(i => !string.IsNullOrEmpty(i)), StringComparer.OrdinalIgnoreCase)
|
||||
.Where(i => BaseItem.SupportedImageExtensions.Contains(Path.GetExtension(i), StringComparer.OrdinalIgnoreCase))
|
||||
.ToList();
|
||||
|
||||
foreach (var image in deadImages)
|
||||
{
|
||||
_logger.Debug("Deleting dead chapter image {0}", image);
|
||||
|
||||
try
|
||||
{
|
||||
_fileSystem.DeleteFile(image);
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
_logger.ErrorException("Error deleting {0}.", ex, image);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
260
Emby.Server.Implementations/News/NewsEntryPoint.cs
Normal file
260
Emby.Server.Implementations/News/NewsEntryPoint.cs
Normal file
@@ -0,0 +1,260 @@
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.Notifications;
|
||||
using MediaBrowser.Controller.Plugins;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using MediaBrowser.Model.News;
|
||||
using MediaBrowser.Model.Notifications;
|
||||
using MediaBrowser.Model.Serialization;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.Threading;
|
||||
|
||||
namespace Emby.Server.Implementations.News
|
||||
{
|
||||
public class NewsEntryPoint : IServerEntryPoint
|
||||
{
|
||||
private ITimer _timer;
|
||||
private readonly IHttpClient _httpClient;
|
||||
private readonly IApplicationPaths _appPaths;
|
||||
private readonly IFileSystem _fileSystem;
|
||||
private readonly ILogger _logger;
|
||||
private readonly IJsonSerializer _json;
|
||||
|
||||
private readonly INotificationManager _notifications;
|
||||
private readonly IUserManager _userManager;
|
||||
|
||||
private readonly TimeSpan _frequency = TimeSpan.FromHours(24);
|
||||
private readonly ITimerFactory _timerFactory;
|
||||
|
||||
public NewsEntryPoint(IHttpClient httpClient, IApplicationPaths appPaths, IFileSystem fileSystem, ILogger logger, IJsonSerializer json, INotificationManager notifications, IUserManager userManager, ITimerFactory timerFactory)
|
||||
{
|
||||
_httpClient = httpClient;
|
||||
_appPaths = appPaths;
|
||||
_fileSystem = fileSystem;
|
||||
_logger = logger;
|
||||
_json = json;
|
||||
_notifications = notifications;
|
||||
_userManager = userManager;
|
||||
_timerFactory = timerFactory;
|
||||
}
|
||||
|
||||
public void Run()
|
||||
{
|
||||
_timer = _timerFactory.Create(OnTimerFired, null, TimeSpan.FromMilliseconds(500), _frequency);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when [timer fired].
|
||||
/// </summary>
|
||||
/// <param name="state">The state.</param>
|
||||
private async void OnTimerFired(object state)
|
||||
{
|
||||
var path = Path.Combine(_appPaths.CachePath, "news.json");
|
||||
|
||||
try
|
||||
{
|
||||
await DownloadNews(path).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.ErrorException("Error downloading news", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task DownloadNews(string path)
|
||||
{
|
||||
DateTime? lastUpdate = null;
|
||||
|
||||
if (_fileSystem.FileExists(path))
|
||||
{
|
||||
lastUpdate = _fileSystem.GetLastWriteTimeUtc(path);
|
||||
}
|
||||
|
||||
var requestOptions = new HttpRequestOptions
|
||||
{
|
||||
Url = "http://emby.media/community/index.php?/blog/rss/1-media-browser-developers-blog",
|
||||
Progress = new Progress<double>(),
|
||||
UserAgent = "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.42 Safari/537.36",
|
||||
BufferContent = false
|
||||
};
|
||||
|
||||
using (var stream = await _httpClient.Get(requestOptions).ConfigureAwait(false))
|
||||
{
|
||||
using (var reader = XmlReader.Create(stream))
|
||||
{
|
||||
var news = ParseRssItems(reader).ToList();
|
||||
|
||||
_json.SerializeToFile(news, path);
|
||||
|
||||
await CreateNotifications(news, lastUpdate, CancellationToken.None).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Task CreateNotifications(List<NewsItem> items, DateTime? lastUpdate, CancellationToken cancellationToken)
|
||||
{
|
||||
if (lastUpdate.HasValue)
|
||||
{
|
||||
items = items.Where(i => i.Date.ToUniversalTime() >= lastUpdate.Value)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
var tasks = items.Select(i => _notifications.SendNotification(new NotificationRequest
|
||||
{
|
||||
Date = i.Date,
|
||||
Name = i.Title,
|
||||
Description = i.Description,
|
||||
Url = i.Link,
|
||||
UserIds = _userManager.Users.Select(u => u.Id.ToString("N")).ToList()
|
||||
|
||||
}, cancellationToken));
|
||||
|
||||
return Task.WhenAll(tasks);
|
||||
}
|
||||
|
||||
private IEnumerable<NewsItem> ParseRssItems(XmlReader reader)
|
||||
{
|
||||
reader.MoveToContent();
|
||||
reader.Read();
|
||||
|
||||
while (!reader.EOF)
|
||||
{
|
||||
if (reader.NodeType == XmlNodeType.Element)
|
||||
{
|
||||
switch (reader.Name)
|
||||
{
|
||||
case "channel":
|
||||
{
|
||||
using (var subReader = reader.ReadSubtree())
|
||||
{
|
||||
return ParseFromChannelNode(subReader);
|
||||
}
|
||||
}
|
||||
default:
|
||||
{
|
||||
reader.Skip();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
reader.Read();
|
||||
}
|
||||
}
|
||||
|
||||
return new List<NewsItem>();
|
||||
}
|
||||
|
||||
private IEnumerable<NewsItem> ParseFromChannelNode(XmlReader reader)
|
||||
{
|
||||
var list = new List<NewsItem>();
|
||||
|
||||
reader.MoveToContent();
|
||||
reader.Read();
|
||||
|
||||
while (!reader.EOF)
|
||||
{
|
||||
if (reader.NodeType == XmlNodeType.Element)
|
||||
{
|
||||
switch (reader.Name)
|
||||
{
|
||||
case "item":
|
||||
{
|
||||
using (var subReader = reader.ReadSubtree())
|
||||
{
|
||||
list.Add(ParseItem(subReader));
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
reader.Skip();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
reader.Read();
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
private NewsItem ParseItem(XmlReader reader)
|
||||
{
|
||||
var item = new NewsItem();
|
||||
|
||||
reader.MoveToContent();
|
||||
reader.Read();
|
||||
|
||||
while (!reader.EOF)
|
||||
{
|
||||
if (reader.NodeType == XmlNodeType.Element)
|
||||
{
|
||||
switch (reader.Name)
|
||||
{
|
||||
case "title":
|
||||
{
|
||||
item.Title = reader.ReadElementContentAsString();
|
||||
break;
|
||||
}
|
||||
case "link":
|
||||
{
|
||||
item.Link = reader.ReadElementContentAsString();
|
||||
break;
|
||||
}
|
||||
case "description":
|
||||
{
|
||||
item.DescriptionHtml = reader.ReadElementContentAsString();
|
||||
item.Description = item.DescriptionHtml.StripHtml();
|
||||
break;
|
||||
}
|
||||
case "pubDate":
|
||||
{
|
||||
var date = reader.ReadElementContentAsString();
|
||||
DateTime parsedDate;
|
||||
|
||||
if (DateTime.TryParse(date, out parsedDate))
|
||||
{
|
||||
item.Date = parsedDate;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
reader.Skip();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
reader.Read();
|
||||
}
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_timer != null)
|
||||
{
|
||||
_timer.Dispose();
|
||||
_timer = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,7 @@
|
||||
{
|
||||
{
|
||||
"supports": {},
|
||||
"dependencies": {
|
||||
"MediaBrowser.Naming": "1.0.0.59",
|
||||
"Patterns.Logging": "1.0.0.6"
|
||||
"MediaBrowser.Naming": "1.0.0.59"
|
||||
},
|
||||
"frameworks": {
|
||||
".NETPortable,Version=v4.5,Profile=Profile7": {}
|
||||
|
||||
Reference in New Issue
Block a user