aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Emby.Dlna/Eventing/DlnaEventManager.cs (renamed from Emby.Dlna/Eventing/EventManager.cs)4
-rw-r--r--Emby.Dlna/IConnectionManager.cs2
-rw-r--r--Emby.Dlna/IContentDirectory.cs2
-rw-r--r--Emby.Dlna/IDlnaEventManager.cs (renamed from Emby.Dlna/IEventManager.cs)2
-rw-r--r--Emby.Dlna/IMediaReceiverRegistrar.cs2
-rw-r--r--Emby.Dlna/PlayTo/PlayToController.cs2
-rw-r--r--Emby.Dlna/PlayTo/PlayToManager.cs2
-rw-r--r--Emby.Dlna/Service/BaseService.cs6
-rw-r--r--Emby.Dlna/Ssdp/DeviceDiscovery.cs2
-rw-r--r--Emby.Notifications/NotificationEntryPoint.cs2
-rw-r--r--Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs590
-rw-r--r--Emby.Server.Implementations/ApplicationHost.cs159
-rw-r--r--Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs2
-rw-r--r--Emby.Server.Implementations/Devices/DeviceManager.cs2
-rw-r--r--Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs2
-rw-r--r--Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs2
-rw-r--r--Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs9
-rw-r--r--Emby.Server.Implementations/EntryPoints/ServerEventNotifier.cs210
-rw-r--r--Emby.Server.Implementations/HttpServer/HttpListenerHost.cs2
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs2
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs2
-rw-r--r--Emby.Server.Implementations/LiveTv/LiveTvManager.cs2
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs2
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/TaskManager.cs2
-rw-r--r--Emby.Server.Implementations/Session/SessionManager.cs117
-rw-r--r--Emby.Server.Implementations/Session/SessionWebSocketListener.cs2
-rw-r--r--Jellyfin.Api/Controllers/DlnaServerController.cs8
-rw-r--r--Jellyfin.Api/WebSocketListeners/ActivityLogWebSocketListener.cs2
-rw-r--r--Jellyfin.Api/WebSocketListeners/ScheduledTasksWebSocketListener.cs2
-rw-r--r--Jellyfin.Data/Events/GenericEventArgs.cs (renamed from MediaBrowser.Model/Events/GenericEventArgs.cs)16
-rw-r--r--Jellyfin.Data/Events/System/PendingRestartEventArgs.cs11
-rw-r--r--Jellyfin.Data/Events/Users/UserCreatedEventArgs.cs18
-rw-r--r--Jellyfin.Data/Events/Users/UserDeletedEventArgs.cs18
-rw-r--r--Jellyfin.Data/Events/Users/UserLockedOutEventArgs.cs18
-rw-r--r--Jellyfin.Data/Events/Users/UserPasswordChangedEventArgs.cs18
-rw-r--r--Jellyfin.Data/Events/Users/UserUpdatedEventArgs.cs18
-rw-r--r--Jellyfin.Server.Implementations/Activity/ActivityManager.cs2
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Library/SubtitleDownloadFailureLogger.cs102
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Security/AuthenticationFailedLogger.cs52
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Security/AuthenticationSucceededLogger.cs49
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStartLogger.cs104
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStopLogger.cs106
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Session/SessionEndedLogger.cs54
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Session/SessionStartedLogger.cs54
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/System/PendingRestartNotifier.cs31
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/System/TaskCompletedLogger.cs158
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/System/TaskCompletedNotifier.cs31
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstallationCancelledNotifier.cs31
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstallationFailedLogger.cs51
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstallationFailedNotifier.cs31
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstalledLogger.cs50
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstalledNotifier.cs31
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstallingNotifier.cs31
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginUninstalledLogger.cs45
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginUninstalledNotifier.cs31
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginUpdatedLogger.cs51
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Users/UserCreatedLogger.cs43
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Users/UserDeletedLogger.cs44
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Users/UserDeletedNotifier.cs38
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Users/UserLockedOutLogger.cs47
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Users/UserPasswordChangedLogger.cs43
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Users/UserUpdatedNotifier.cs41
-rw-r--r--Jellyfin.Server.Implementations/Events/EventManager.cs60
-rw-r--r--Jellyfin.Server.Implementations/Events/EventingServiceCollectionExtensions.cs72
-rw-r--r--Jellyfin.Server.Implementations/Users/DeviceAccessEntryPoint.cs2
-rw-r--r--Jellyfin.Server.Implementations/Users/UserManager.cs31
-rw-r--r--Jellyfin.Server/CoreAppHost.cs27
-rw-r--r--Jellyfin.Server/Program.cs7
-rw-r--r--MediaBrowser.Common/IApplicationHost.cs3
-rw-r--r--MediaBrowser.Common/Plugins/BasePlugin.cs11
-rw-r--r--MediaBrowser.Common/Plugins/IPlugin.cs13
-rw-r--r--MediaBrowser.Common/Updates/InstallationEventArgs.cs3
-rw-r--r--MediaBrowser.Controller/Devices/IDeviceManager.cs2
-rw-r--r--MediaBrowser.Controller/Events/IEventConsumer.cs11
-rw-r--r--MediaBrowser.Controller/Events/IEventManager.cs28
-rw-r--r--MediaBrowser.Controller/Events/Session/SessionEndedEventArgs.cs19
-rw-r--r--MediaBrowser.Controller/Events/Session/SessionStartedEventArgs.cs19
-rw-r--r--MediaBrowser.Controller/Events/Updates/PluginInstallationCancelledEventArgs.cs19
-rw-r--r--MediaBrowser.Controller/Events/Updates/PluginInstalledEventArgs.cs19
-rw-r--r--MediaBrowser.Controller/Events/Updates/PluginInstallingEventArgs.cs19
-rw-r--r--MediaBrowser.Controller/Events/Updates/PluginUninstalledEventArgs.cs19
-rw-r--r--MediaBrowser.Controller/Events/Updates/PluginUpdatedEventArgs.cs19
-rw-r--r--MediaBrowser.Controller/IServerApplicationHost.cs16
-rw-r--r--MediaBrowser.Controller/Library/IUserManager.cs22
-rw-r--r--MediaBrowser.Controller/Library/PlaybackStartEventArgs.cs6
-rw-r--r--MediaBrowser.Controller/LiveTv/ILiveTvManager.cs2
-rw-r--r--MediaBrowser.Controller/Net/IHttpServer.cs2
-rw-r--r--MediaBrowser.Controller/Providers/IProviderManager.cs2
-rw-r--r--MediaBrowser.Controller/Session/ISessionManager.cs2
-rw-r--r--MediaBrowser.Controller/Subtitles/SubtitleDownloadEventArgs.cs29
-rw-r--r--MediaBrowser.Controller/Subtitles/SubtitleDownloadFailureEventArgs.cs14
-rw-r--r--MediaBrowser.Model/Activity/IActivityManager.cs2
-rw-r--r--MediaBrowser.Model/Dlna/IDeviceDiscovery.cs2
-rw-r--r--MediaBrowser.Model/Tasks/IScheduledTaskWorker.cs2
-rw-r--r--MediaBrowser.Model/Tasks/ITaskManager.cs2
-rw-r--r--MediaBrowser.Providers/Manager/ProviderManager.cs2
-rw-r--r--tests/MediaBrowser.Api.Tests/JellyfinApplicationFactory.cs7
97 files changed, 2046 insertions, 1082 deletions
diff --git a/Emby.Dlna/Eventing/EventManager.cs b/Emby.Dlna/Eventing/DlnaEventManager.cs
index cc8e0ae06..b66e966df 100644
--- a/Emby.Dlna/Eventing/EventManager.cs
+++ b/Emby.Dlna/Eventing/DlnaEventManager.cs
@@ -14,7 +14,7 @@ using Microsoft.Extensions.Logging;
namespace Emby.Dlna.Eventing
{
- public class EventManager : IEventManager
+ public class DlnaEventManager : IDlnaEventManager
{
private readonly ConcurrentDictionary<string, EventSubscription> _subscriptions =
new ConcurrentDictionary<string, EventSubscription>(StringComparer.OrdinalIgnoreCase);
@@ -24,7 +24,7 @@ namespace Emby.Dlna.Eventing
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
- public EventManager(ILogger logger, IHttpClient httpClient)
+ public DlnaEventManager(ILogger logger, IHttpClient httpClient)
{
_httpClient = httpClient;
_logger = logger;
diff --git a/Emby.Dlna/IConnectionManager.cs b/Emby.Dlna/IConnectionManager.cs
index 7b4a33a98..9f643a9e6 100644
--- a/Emby.Dlna/IConnectionManager.cs
+++ b/Emby.Dlna/IConnectionManager.cs
@@ -2,7 +2,7 @@
namespace Emby.Dlna
{
- public interface IConnectionManager : IEventManager, IUpnpService
+ public interface IConnectionManager : IDlnaEventManager, IUpnpService
{
}
}
diff --git a/Emby.Dlna/IContentDirectory.cs b/Emby.Dlna/IContentDirectory.cs
index 83ef09c66..10f4d6386 100644
--- a/Emby.Dlna/IContentDirectory.cs
+++ b/Emby.Dlna/IContentDirectory.cs
@@ -2,7 +2,7 @@
namespace Emby.Dlna
{
- public interface IContentDirectory : IEventManager, IUpnpService
+ public interface IContentDirectory : IDlnaEventManager, IUpnpService
{
}
}
diff --git a/Emby.Dlna/IEventManager.cs b/Emby.Dlna/IDlnaEventManager.cs
index de87b0709..33cf0896b 100644
--- a/Emby.Dlna/IEventManager.cs
+++ b/Emby.Dlna/IDlnaEventManager.cs
@@ -2,7 +2,7 @@
namespace Emby.Dlna
{
- public interface IEventManager
+ public interface IDlnaEventManager
{
/// <summary>
/// Cancels the event subscription.
diff --git a/Emby.Dlna/IMediaReceiverRegistrar.cs b/Emby.Dlna/IMediaReceiverRegistrar.cs
index b0376b6a9..43e934b53 100644
--- a/Emby.Dlna/IMediaReceiverRegistrar.cs
+++ b/Emby.Dlna/IMediaReceiverRegistrar.cs
@@ -2,7 +2,7 @@
namespace Emby.Dlna
{
- public interface IMediaReceiverRegistrar : IEventManager, IUpnpService
+ public interface IMediaReceiverRegistrar : IDlnaEventManager, IUpnpService
{
}
}
diff --git a/Emby.Dlna/PlayTo/PlayToController.cs b/Emby.Dlna/PlayTo/PlayToController.cs
index d9e10f459..328759c5b 100644
--- a/Emby.Dlna/PlayTo/PlayToController.cs
+++ b/Emby.Dlna/PlayTo/PlayToController.cs
@@ -8,6 +8,7 @@ using System.Threading;
using System.Threading.Tasks;
using Emby.Dlna.Didl;
using Jellyfin.Data.Entities;
+using Jellyfin.Data.Events;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Drawing;
@@ -18,7 +19,6 @@ using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Events;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Session;
using Microsoft.AspNetCore.WebUtilities;
diff --git a/Emby.Dlna/PlayTo/PlayToManager.cs b/Emby.Dlna/PlayTo/PlayToManager.cs
index 54f4fee05..ff801f263 100644
--- a/Emby.Dlna/PlayTo/PlayToManager.cs
+++ b/Emby.Dlna/PlayTo/PlayToManager.cs
@@ -6,6 +6,7 @@ using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Data.Events;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller;
@@ -16,7 +17,6 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Dlna;
-using MediaBrowser.Model.Events;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Session;
using Microsoft.Extensions.Logging;
diff --git a/Emby.Dlna/Service/BaseService.cs b/Emby.Dlna/Service/BaseService.cs
index 3ad1ea9e0..40d069e7c 100644
--- a/Emby.Dlna/Service/BaseService.cs
+++ b/Emby.Dlna/Service/BaseService.cs
@@ -6,17 +6,17 @@ using Microsoft.Extensions.Logging;
namespace Emby.Dlna.Service
{
- public class BaseService : IEventManager
+ public class BaseService : IDlnaEventManager
{
protected BaseService(ILogger<BaseService> logger, IHttpClient httpClient)
{
Logger = logger;
HttpClient = httpClient;
- EventManager = new EventManager(logger, HttpClient);
+ EventManager = new DlnaEventManager(logger, HttpClient);
}
- protected IEventManager EventManager { get; }
+ protected IDlnaEventManager EventManager { get; }
protected IHttpClient HttpClient { get; }
diff --git a/Emby.Dlna/Ssdp/DeviceDiscovery.cs b/Emby.Dlna/Ssdp/DeviceDiscovery.cs
index 7c4c0f6d9..8c7d961f3 100644
--- a/Emby.Dlna/Ssdp/DeviceDiscovery.cs
+++ b/Emby.Dlna/Ssdp/DeviceDiscovery.cs
@@ -3,9 +3,9 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using Jellyfin.Data.Events;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Model.Dlna;
-using MediaBrowser.Model.Events;
using Rssdp;
using Rssdp.Infrastructure;
diff --git a/Emby.Notifications/NotificationEntryPoint.cs b/Emby.Notifications/NotificationEntryPoint.cs
index b923fd26c..ded22d26c 100644
--- a/Emby.Notifications/NotificationEntryPoint.cs
+++ b/Emby.Notifications/NotificationEntryPoint.cs
@@ -4,6 +4,7 @@ using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Data.Events;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Entities;
@@ -13,7 +14,6 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Notifications;
using MediaBrowser.Controller.Plugins;
using MediaBrowser.Model.Activity;
-using MediaBrowser.Model.Events;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Notifications;
using Microsoft.Extensions.Logging;
diff --git a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs
deleted file mode 100644
index 84bec9201..000000000
--- a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs
+++ /dev/null
@@ -1,590 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using Jellyfin.Data.Entities;
-using MediaBrowser.Common.Plugins;
-using MediaBrowser.Common.Updates;
-using MediaBrowser.Controller.Authentication;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Plugins;
-using MediaBrowser.Controller.Session;
-using MediaBrowser.Controller.Subtitles;
-using MediaBrowser.Model.Activity;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Events;
-using MediaBrowser.Model.Globalization;
-using MediaBrowser.Model.Notifications;
-using MediaBrowser.Model.Tasks;
-using MediaBrowser.Model.Updates;
-using Microsoft.Extensions.Logging;
-
-namespace Emby.Server.Implementations.Activity
-{
- /// <summary>
- /// Entry point for the activity logger.
- /// </summary>
- public sealed class ActivityLogEntryPoint : IServerEntryPoint
- {
- private readonly ILogger<ActivityLogEntryPoint> _logger;
- private readonly IInstallationManager _installationManager;
- private readonly ISessionManager _sessionManager;
- private readonly ITaskManager _taskManager;
- private readonly IActivityManager _activityManager;
- private readonly ILocalizationManager _localization;
- private readonly ISubtitleManager _subManager;
- private readonly IUserManager _userManager;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="ActivityLogEntryPoint"/> class.
- /// </summary>
- /// <param name="logger">The logger.</param>
- /// <param name="sessionManager">The session manager.</param>
- /// <param name="taskManager">The task manager.</param>
- /// <param name="activityManager">The activity manager.</param>
- /// <param name="localization">The localization manager.</param>
- /// <param name="installationManager">The installation manager.</param>
- /// <param name="subManager">The subtitle manager.</param>
- /// <param name="userManager">The user manager.</param>
- public ActivityLogEntryPoint(
- ILogger<ActivityLogEntryPoint> logger,
- ISessionManager sessionManager,
- ITaskManager taskManager,
- IActivityManager activityManager,
- ILocalizationManager localization,
- IInstallationManager installationManager,
- ISubtitleManager subManager,
- IUserManager userManager)
- {
- _logger = logger;
- _sessionManager = sessionManager;
- _taskManager = taskManager;
- _activityManager = activityManager;
- _localization = localization;
- _installationManager = installationManager;
- _subManager = subManager;
- _userManager = userManager;
- }
-
- /// <inheritdoc />
- public Task RunAsync()
- {
- _taskManager.TaskCompleted += OnTaskCompleted;
-
- _installationManager.PluginInstalled += OnPluginInstalled;
- _installationManager.PluginUninstalled += OnPluginUninstalled;
- _installationManager.PluginUpdated += OnPluginUpdated;
- _installationManager.PackageInstallationFailed += OnPackageInstallationFailed;
-
- _sessionManager.SessionStarted += OnSessionStarted;
- _sessionManager.AuthenticationFailed += OnAuthenticationFailed;
- _sessionManager.AuthenticationSucceeded += OnAuthenticationSucceeded;
- _sessionManager.SessionEnded += OnSessionEnded;
- _sessionManager.PlaybackStart += OnPlaybackStart;
- _sessionManager.PlaybackStopped += OnPlaybackStopped;
-
- _subManager.SubtitleDownloadFailure += OnSubtitleDownloadFailure;
-
- _userManager.OnUserCreated += OnUserCreated;
- _userManager.OnUserPasswordChanged += OnUserPasswordChanged;
- _userManager.OnUserDeleted += OnUserDeleted;
- _userManager.OnUserLockedOut += OnUserLockedOut;
-
- return Task.CompletedTask;
- }
-
- private async void OnUserLockedOut(object sender, GenericEventArgs<User> e)
- {
- await CreateLogEntry(new ActivityLog(
- string.Format(
- CultureInfo.InvariantCulture,
- _localization.GetLocalizedString("UserLockedOutWithName"),
- e.Argument.Username),
- NotificationType.UserLockedOut.ToString(),
- e.Argument.Id)
- {
- LogSeverity = LogLevel.Error
- }).ConfigureAwait(false);
- }
-
- private async void OnSubtitleDownloadFailure(object sender, SubtitleDownloadFailureEventArgs e)
- {
- await CreateLogEntry(new ActivityLog(
- string.Format(
- CultureInfo.InvariantCulture,
- _localization.GetLocalizedString("SubtitleDownloadFailureFromForItem"),
- e.Provider,
- Notifications.NotificationEntryPoint.GetItemName(e.Item)),
- "SubtitleDownloadFailure",
- Guid.Empty)
- {
- ItemId = e.Item.Id.ToString("N", CultureInfo.InvariantCulture),
- ShortOverview = e.Exception.Message
- }).ConfigureAwait(false);
- }
-
- private async void OnPlaybackStopped(object sender, PlaybackStopEventArgs e)
- {
- var item = e.MediaInfo;
-
- if (item == null)
- {
- _logger.LogWarning("PlaybackStopped reported with null media info.");
- return;
- }
-
- if (e.Item != null && e.Item.IsThemeMedia)
- {
- // Don't report theme song or local trailer playback
- return;
- }
-
- if (e.Users.Count == 0)
- {
- return;
- }
-
- var user = e.Users[0];
-
- await CreateLogEntry(new ActivityLog(
- string.Format(
- CultureInfo.InvariantCulture,
- _localization.GetLocalizedString("UserStoppedPlayingItemWithValues"),
- user.Username,
- GetItemName(item),
- e.DeviceName),
- GetPlaybackStoppedNotificationType(item.MediaType),
- user.Id))
- .ConfigureAwait(false);
- }
-
- private async void OnPlaybackStart(object sender, PlaybackProgressEventArgs e)
- {
- var item = e.MediaInfo;
-
- if (item == null)
- {
- _logger.LogWarning("PlaybackStart reported with null media info.");
- return;
- }
-
- if (e.Item != null && e.Item.IsThemeMedia)
- {
- // Don't report theme song or local trailer playback
- return;
- }
-
- if (e.Users.Count == 0)
- {
- return;
- }
-
- var user = e.Users.First();
-
- await CreateLogEntry(new ActivityLog(
- string.Format(
- CultureInfo.InvariantCulture,
- _localization.GetLocalizedString("UserStartedPlayingItemWithValues"),
- user.Username,
- GetItemName(item),
- e.DeviceName),
- GetPlaybackNotificationType(item.MediaType),
- user.Id))
- .ConfigureAwait(false);
- }
-
- private static string GetItemName(BaseItemDto item)
- {
- var name = item.Name;
-
- if (!string.IsNullOrEmpty(item.SeriesName))
- {
- name = item.SeriesName + " - " + name;
- }
-
- if (item.Artists != null && item.Artists.Count > 0)
- {
- name = item.Artists[0] + " - " + name;
- }
-
- return name;
- }
-
- private static string GetPlaybackNotificationType(string mediaType)
- {
- if (string.Equals(mediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase))
- {
- return NotificationType.AudioPlayback.ToString();
- }
-
- if (string.Equals(mediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase))
- {
- return NotificationType.VideoPlayback.ToString();
- }
-
- return null;
- }
-
- private static string GetPlaybackStoppedNotificationType(string mediaType)
- {
- if (string.Equals(mediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase))
- {
- return NotificationType.AudioPlaybackStopped.ToString();
- }
-
- if (string.Equals(mediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase))
- {
- return NotificationType.VideoPlaybackStopped.ToString();
- }
-
- return null;
- }
-
- private async void OnSessionEnded(object sender, SessionEventArgs e)
- {
- var session = e.SessionInfo;
-
- if (string.IsNullOrEmpty(session.UserName))
- {
- return;
- }
-
- await CreateLogEntry(new ActivityLog(
- string.Format(
- CultureInfo.InvariantCulture,
- _localization.GetLocalizedString("UserOfflineFromDevice"),
- session.UserName,
- session.DeviceName),
- "SessionEnded",
- session.UserId)
- {
- ShortOverview = string.Format(
- CultureInfo.InvariantCulture,
- _localization.GetLocalizedString("LabelIpAddressValue"),
- session.RemoteEndPoint),
- }).ConfigureAwait(false);
- }
-
- private async void OnAuthenticationSucceeded(object sender, GenericEventArgs<AuthenticationResult> e)
- {
- var user = e.Argument.User;
-
- await CreateLogEntry(new ActivityLog(
- string.Format(
- CultureInfo.InvariantCulture,
- _localization.GetLocalizedString("AuthenticationSucceededWithUserName"),
- user.Name),
- "AuthenticationSucceeded",
- user.Id)
- {
- ShortOverview = string.Format(
- CultureInfo.InvariantCulture,
- _localization.GetLocalizedString("LabelIpAddressValue"),
- e.Argument.SessionInfo.RemoteEndPoint),
- }).ConfigureAwait(false);
- }
-
- private async void OnAuthenticationFailed(object sender, GenericEventArgs<AuthenticationRequest> e)
- {
- await CreateLogEntry(new ActivityLog(
- string.Format(
- CultureInfo.InvariantCulture,
- _localization.GetLocalizedString("FailedLoginAttemptWithUserName"),
- e.Argument.Username),
- "AuthenticationFailed",
- Guid.Empty)
- {
- LogSeverity = LogLevel.Error,
- ShortOverview = string.Format(
- CultureInfo.InvariantCulture,
- _localization.GetLocalizedString("LabelIpAddressValue"),
- e.Argument.RemoteEndPoint),
- }).ConfigureAwait(false);
- }
-
- private async void OnUserDeleted(object sender, GenericEventArgs<User> e)
- {
- await CreateLogEntry(new ActivityLog(
- string.Format(
- CultureInfo.InvariantCulture,
- _localization.GetLocalizedString("UserDeletedWithName"),
- e.Argument.Username),
- "UserDeleted",
- Guid.Empty))
- .ConfigureAwait(false);
- }
-
- private async void OnUserPasswordChanged(object sender, GenericEventArgs<User> e)
- {
- await CreateLogEntry(new ActivityLog(
- string.Format(
- CultureInfo.InvariantCulture,
- _localization.GetLocalizedString("UserPasswordChangedWithName"),
- e.Argument.Username),
- "UserPasswordChanged",
- e.Argument.Id))
- .ConfigureAwait(false);
- }
-
- private async void OnUserCreated(object sender, GenericEventArgs<User> e)
- {
- await CreateLogEntry(new ActivityLog(
- string.Format(
- CultureInfo.InvariantCulture,
- _localization.GetLocalizedString("UserCreatedWithName"),
- e.Argument.Username),
- "UserCreated",
- e.Argument.Id))
- .ConfigureAwait(false);
- }
-
- private async void OnSessionStarted(object sender, SessionEventArgs e)
- {
- var session = e.SessionInfo;
-
- if (string.IsNullOrEmpty(session.UserName))
- {
- return;
- }
-
- await CreateLogEntry(new ActivityLog(
- string.Format(
- CultureInfo.InvariantCulture,
- _localization.GetLocalizedString("UserOnlineFromDevice"),
- session.UserName,
- session.DeviceName),
- "SessionStarted",
- session.UserId)
- {
- ShortOverview = string.Format(
- CultureInfo.InvariantCulture,
- _localization.GetLocalizedString("LabelIpAddressValue"),
- session.RemoteEndPoint)
- }).ConfigureAwait(false);
- }
-
- private async void OnPluginUpdated(object sender, InstallationInfo e)
- {
- await CreateLogEntry(new ActivityLog(
- string.Format(
- CultureInfo.InvariantCulture,
- _localization.GetLocalizedString("PluginUpdatedWithName"),
- e.Name),
- NotificationType.PluginUpdateInstalled.ToString(),
- Guid.Empty)
- {
- ShortOverview = string.Format(
- CultureInfo.InvariantCulture,
- _localization.GetLocalizedString("VersionNumber"),
- e.Version),
- Overview = e.Changelog
- }).ConfigureAwait(false);
- }
-
- private async void OnPluginUninstalled(object sender, IPlugin e)
- {
- await CreateLogEntry(new ActivityLog(
- string.Format(
- CultureInfo.InvariantCulture,
- _localization.GetLocalizedString("PluginUninstalledWithName"),
- e.Name),
- NotificationType.PluginUninstalled.ToString(),
- Guid.Empty))
- .ConfigureAwait(false);
- }
-
- private async void OnPluginInstalled(object sender, InstallationInfo e)
- {
- await CreateLogEntry(new ActivityLog(
- string.Format(
- CultureInfo.InvariantCulture,
- _localization.GetLocalizedString("PluginInstalledWithName"),
- e.Name),
- NotificationType.PluginInstalled.ToString(),
- Guid.Empty)
- {
- ShortOverview = string.Format(
- CultureInfo.InvariantCulture,
- _localization.GetLocalizedString("VersionNumber"),
- e.Version)
- }).ConfigureAwait(false);
- }
-
- private async void OnPackageInstallationFailed(object sender, InstallationFailedEventArgs e)
- {
- var installationInfo = e.InstallationInfo;
-
- await CreateLogEntry(new ActivityLog(
- string.Format(
- CultureInfo.InvariantCulture,
- _localization.GetLocalizedString("NameInstallFailed"),
- installationInfo.Name),
- NotificationType.InstallationFailed.ToString(),
- Guid.Empty)
- {
- ShortOverview = string.Format(
- CultureInfo.InvariantCulture,
- _localization.GetLocalizedString("VersionNumber"),
- installationInfo.Version),
- Overview = e.Exception.Message
- }).ConfigureAwait(false);
- }
-
- private async void OnTaskCompleted(object sender, TaskCompletionEventArgs e)
- {
- var result = e.Result;
- var task = e.Task;
-
- if (task.ScheduledTask is IConfigurableScheduledTask activityTask
- && !activityTask.IsLogged)
- {
- return;
- }
-
- var time = result.EndTimeUtc - result.StartTimeUtc;
- var runningTime = string.Format(
- CultureInfo.InvariantCulture,
- _localization.GetLocalizedString("LabelRunningTimeValue"),
- ToUserFriendlyString(time));
-
- if (result.Status == TaskCompletionStatus.Failed)
- {
- var vals = new List<string>();
-
- if (!string.IsNullOrEmpty(e.Result.ErrorMessage))
- {
- vals.Add(e.Result.ErrorMessage);
- }
-
- if (!string.IsNullOrEmpty(e.Result.LongErrorMessage))
- {
- vals.Add(e.Result.LongErrorMessage);
- }
-
- await CreateLogEntry(new ActivityLog(
- string.Format(CultureInfo.InvariantCulture, _localization.GetLocalizedString("ScheduledTaskFailedWithName"), task.Name),
- NotificationType.TaskFailed.ToString(),
- Guid.Empty)
- {
- LogSeverity = LogLevel.Error,
- Overview = string.Join(Environment.NewLine, vals),
- ShortOverview = runningTime
- }).ConfigureAwait(false);
- }
- }
-
- private async Task CreateLogEntry(ActivityLog entry)
- => await _activityManager.CreateAsync(entry).ConfigureAwait(false);
-
- /// <inheritdoc />
- public void Dispose()
- {
- _taskManager.TaskCompleted -= OnTaskCompleted;
-
- _installationManager.PluginInstalled -= OnPluginInstalled;
- _installationManager.PluginUninstalled -= OnPluginUninstalled;
- _installationManager.PluginUpdated -= OnPluginUpdated;
- _installationManager.PackageInstallationFailed -= OnPackageInstallationFailed;
-
- _sessionManager.SessionStarted -= OnSessionStarted;
- _sessionManager.AuthenticationFailed -= OnAuthenticationFailed;
- _sessionManager.AuthenticationSucceeded -= OnAuthenticationSucceeded;
- _sessionManager.SessionEnded -= OnSessionEnded;
-
- _sessionManager.PlaybackStart -= OnPlaybackStart;
- _sessionManager.PlaybackStopped -= OnPlaybackStopped;
-
- _subManager.SubtitleDownloadFailure -= OnSubtitleDownloadFailure;
-
- _userManager.OnUserCreated -= OnUserCreated;
- _userManager.OnUserPasswordChanged -= OnUserPasswordChanged;
- _userManager.OnUserDeleted -= OnUserDeleted;
- _userManager.OnUserLockedOut -= OnUserLockedOut;
- }
-
- /// <summary>
- /// Constructs a user-friendly string for this TimeSpan instance.
- /// </summary>
- private static string ToUserFriendlyString(TimeSpan span)
- {
- const int DaysInYear = 365;
- const int DaysInMonth = 30;
-
- // Get each non-zero value from TimeSpan component
- var values = new List<string>();
-
- // Number of years
- int days = span.Days;
- if (days >= DaysInYear)
- {
- int years = days / DaysInYear;
- values.Add(CreateValueString(years, "year"));
- days %= DaysInYear;
- }
-
- // Number of months
- if (days >= DaysInMonth)
- {
- int months = days / DaysInMonth;
- values.Add(CreateValueString(months, "month"));
- days = days % DaysInMonth;
- }
-
- // Number of days
- if (days >= 1)
- {
- values.Add(CreateValueString(days, "day"));
- }
-
- // Number of hours
- if (span.Hours >= 1)
- {
- values.Add(CreateValueString(span.Hours, "hour"));
- }
-
- // Number of minutes
- if (span.Minutes >= 1)
- {
- values.Add(CreateValueString(span.Minutes, "minute"));
- }
-
- // Number of seconds (include when 0 if no other components included)
- if (span.Seconds >= 1 || values.Count == 0)
- {
- values.Add(CreateValueString(span.Seconds, "second"));
- }
-
- // Combine values into string
- var builder = new StringBuilder();
- for (int i = 0; i < values.Count; i++)
- {
- if (builder.Length > 0)
- {
- builder.Append(i == values.Count - 1 ? " and " : ", ");
- }
-
- builder.Append(values[i]);
- }
-
- // Return result
- return builder.ToString();
- }
-
- /// <summary>
- /// Constructs a string description of a time-span value.
- /// </summary>
- /// <param name="value">The value of this item.</param>
- /// <param name="description">The name of this item (singular form).</param>
- private static string CreateValueString(int value, string description)
- {
- return string.Format(
- CultureInfo.InvariantCulture,
- "{0:#,##0} {1}",
- value,
- value == 1 ? description : string.Format(CultureInfo.InvariantCulture, "{0}s", description));
- }
- }
-}
diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs
index 99530bfbd..cb63cc85e 100644
--- a/Emby.Server.Implementations/ApplicationHost.cs
+++ b/Emby.Server.Implementations/ApplicationHost.cs
@@ -53,7 +53,6 @@ using MediaBrowser.Common.Net;
using MediaBrowser.Common.Plugins;
using MediaBrowser.Common.Updates;
using MediaBrowser.Controller;
-using MediaBrowser.Controller.Authentication;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Chapters;
using MediaBrowser.Controller.Collections;
@@ -173,6 +172,8 @@ namespace Emby.Server.Implementations
/// </summary>
protected ILogger<ApplicationHost> Logger { get; }
+ protected IServiceCollection ServiceCollection { get; }
+
private IPlugin[] _plugins;
/// <summary>
@@ -238,9 +239,11 @@ namespace Emby.Server.Implementations
ILoggerFactory loggerFactory,
IStartupOptions options,
IFileSystem fileSystem,
- INetworkManager networkManager)
+ INetworkManager networkManager,
+ IServiceCollection serviceCollection)
{
_xmlSerializer = new MyXmlSerializer();
+ ServiceCollection = serviceCollection;
_networkManager = networkManager;
networkManager.LocalSubnetsFn = GetConfiguredLocalSubnets;
@@ -464,7 +467,7 @@ namespace Emby.Server.Implementations
}
/// <inheritdoc/>
- public void Init(IServiceCollection serviceCollection)
+ public void Init()
{
HttpPort = ServerConfigurationManager.Configuration.HttpServerPortNumber;
HttpsPort = ServerConfigurationManager.Configuration.HttpsPortNumber;
@@ -493,7 +496,7 @@ namespace Emby.Server.Implementations
DiscoverTypes();
- RegisterServices(serviceCollection);
+ RegisterServices();
}
public Task ExecuteHttpHandlerAsync(HttpContext context, Func<Task> next)
@@ -502,139 +505,139 @@ namespace Emby.Server.Implementations
/// <summary>
/// Registers services/resources with the service collection that will be available via DI.
/// </summary>
- protected virtual void RegisterServices(IServiceCollection serviceCollection)
+ protected virtual void RegisterServices()
{
- serviceCollection.AddSingleton(_startupOptions);
+ ServiceCollection.AddSingleton(_startupOptions);
- serviceCollection.AddMemoryCache();
+ ServiceCollection.AddMemoryCache();
- serviceCollection.AddSingleton(ConfigurationManager);
- serviceCollection.AddSingleton<IApplicationHost>(this);
+ ServiceCollection.AddSingleton(ConfigurationManager);
+ ServiceCollection.AddSingleton<IApplicationHost>(this);
- serviceCollection.AddSingleton<IApplicationPaths>(ApplicationPaths);
+ ServiceCollection.AddSingleton<IApplicationPaths>(ApplicationPaths);
- serviceCollection.AddSingleton<IJsonSerializer, JsonSerializer>();
+ ServiceCollection.AddSingleton<IJsonSerializer, JsonSerializer>();
- serviceCollection.AddSingleton(_fileSystemManager);
- serviceCollection.AddSingleton<TvdbClientManager>();
+ ServiceCollection.AddSingleton(_fileSystemManager);
+ ServiceCollection.AddSingleton<TvdbClientManager>();
- serviceCollection.AddSingleton<IHttpClient, HttpClientManager.HttpClientManager>();
+ ServiceCollection.AddSingleton<IHttpClient, HttpClientManager.HttpClientManager>();
- serviceCollection.AddSingleton(_networkManager);
+ ServiceCollection.AddSingleton(_networkManager);
- serviceCollection.AddSingleton<IIsoManager, IsoManager>();
+ ServiceCollection.AddSingleton<IIsoManager, IsoManager>();
- serviceCollection.AddSingleton<ITaskManager, TaskManager>();
+ ServiceCollection.AddSingleton<ITaskManager, TaskManager>();
- serviceCollection.AddSingleton(_xmlSerializer);
+ ServiceCollection.AddSingleton(_xmlSerializer);
- serviceCollection.AddSingleton<IStreamHelper, StreamHelper>();
+ ServiceCollection.AddSingleton<IStreamHelper, StreamHelper>();
- serviceCollection.AddSingleton<ICryptoProvider, CryptographyProvider>();
+ ServiceCollection.AddSingleton<ICryptoProvider, CryptographyProvider>();
- serviceCollection.AddSingleton<ISocketFactory, SocketFactory>();
+ ServiceCollection.AddSingleton<ISocketFactory, SocketFactory>();
- serviceCollection.AddSingleton<IInstallationManager, InstallationManager>();
+ ServiceCollection.AddSingleton<IInstallationManager, InstallationManager>();
- serviceCollection.AddSingleton<IZipClient, ZipClient>();
+ ServiceCollection.AddSingleton<IZipClient, ZipClient>();
- serviceCollection.AddSingleton<IHttpResultFactory, HttpResultFactory>();
+ ServiceCollection.AddSingleton<IHttpResultFactory, HttpResultFactory>();
- serviceCollection.AddSingleton<IServerApplicationHost>(this);
- serviceCollection.AddSingleton<IServerApplicationPaths>(ApplicationPaths);
+ ServiceCollection.AddSingleton<IServerApplicationHost>(this);
+ ServiceCollection.AddSingleton<IServerApplicationPaths>(ApplicationPaths);
- serviceCollection.AddSingleton(ServerConfigurationManager);
+ ServiceCollection.AddSingleton(ServerConfigurationManager);
- serviceCollection.AddSingleton<ILocalizationManager, LocalizationManager>();
+ ServiceCollection.AddSingleton<ILocalizationManager, LocalizationManager>();
- serviceCollection.AddSingleton<IBlurayExaminer, BdInfoExaminer>();
+ ServiceCollection.AddSingleton<IBlurayExaminer, BdInfoExaminer>();
- serviceCollection.AddSingleton<IUserDataRepository, SqliteUserDataRepository>();
- serviceCollection.AddSingleton<IUserDataManager, UserDataManager>();
+ ServiceCollection.AddSingleton<IUserDataRepository, SqliteUserDataRepository>();
+ ServiceCollection.AddSingleton<IUserDataManager, UserDataManager>();
- serviceCollection.AddSingleton<IItemRepository, SqliteItemRepository>();
+ ServiceCollection.AddSingleton<IItemRepository, SqliteItemRepository>();
- serviceCollection.AddSingleton<IAuthenticationRepository, AuthenticationRepository>();
+ ServiceCollection.AddSingleton<IAuthenticationRepository, AuthenticationRepository>();
// TODO: Refactor to eliminate the circular dependency here so that Lazy<T> isn't required
- serviceCollection.AddTransient(provider => new Lazy<IDtoService>(provider.GetRequiredService<IDtoService>));
+ ServiceCollection.AddTransient(provider => new Lazy<IDtoService>(provider.GetRequiredService<IDtoService>));
// TODO: Refactor to eliminate the circular dependency here so that Lazy<T> isn't required
- serviceCollection.AddTransient(provider => new Lazy<EncodingHelper>(provider.GetRequiredService<EncodingHelper>));
- serviceCollection.AddSingleton<IMediaEncoder, MediaBrowser.MediaEncoding.Encoder.MediaEncoder>();
+ ServiceCollection.AddTransient(provider => new Lazy<EncodingHelper>(provider.GetRequiredService<EncodingHelper>));
+ ServiceCollection.AddSingleton<IMediaEncoder, MediaBrowser.MediaEncoding.Encoder.MediaEncoder>();
// TODO: Refactor to eliminate the circular dependencies here so that Lazy<T> isn't required
- serviceCollection.AddTransient(provider => new Lazy<ILibraryMonitor>(provider.GetRequiredService<ILibraryMonitor>));
- serviceCollection.AddTransient(provider => new Lazy<IProviderManager>(provider.GetRequiredService<IProviderManager>));
- serviceCollection.AddTransient(provider => new Lazy<IUserViewManager>(provider.GetRequiredService<IUserViewManager>));
- serviceCollection.AddSingleton<ILibraryManager, LibraryManager>();
+ ServiceCollection.AddTransient(provider => new Lazy<ILibraryMonitor>(provider.GetRequiredService<ILibraryMonitor>));
+ ServiceCollection.AddTransient(provider => new Lazy<IProviderManager>(provider.GetRequiredService<IProviderManager>));
+ ServiceCollection.AddTransient(provider => new Lazy<IUserViewManager>(provider.GetRequiredService<IUserViewManager>));
+ ServiceCollection.AddSingleton<ILibraryManager, LibraryManager>();
- serviceCollection.AddSingleton<IMusicManager, MusicManager>();
+ ServiceCollection.AddSingleton<IMusicManager, MusicManager>();
- serviceCollection.AddSingleton<ILibraryMonitor, LibraryMonitor>();
+ ServiceCollection.AddSingleton<ILibraryMonitor, LibraryMonitor>();
- serviceCollection.AddSingleton<ISearchEngine, SearchEngine>();
+ ServiceCollection.AddSingleton<ISearchEngine, SearchEngine>();
- serviceCollection.AddSingleton<ServiceController>();
- serviceCollection.AddSingleton<IHttpServer, HttpListenerHost>();
+ ServiceCollection.AddSingleton<ServiceController>();
+ ServiceCollection.AddSingleton<IHttpServer, HttpListenerHost>();
- serviceCollection.AddSingleton<IImageProcessor, ImageProcessor>();
+ ServiceCollection.AddSingleton<IImageProcessor, ImageProcessor>();
- serviceCollection.AddSingleton<ITVSeriesManager, TVSeriesManager>();
+ ServiceCollection.AddSingleton<ITVSeriesManager, TVSeriesManager>();
- serviceCollection.AddSingleton<IDeviceManager, DeviceManager>();
+ ServiceCollection.AddSingleton<IDeviceManager, DeviceManager>();
- serviceCollection.AddSingleton<IMediaSourceManager, MediaSourceManager>();
+ ServiceCollection.AddSingleton<IMediaSourceManager, MediaSourceManager>();
- serviceCollection.AddSingleton<ISubtitleManager, SubtitleManager>();
+ ServiceCollection.AddSingleton<ISubtitleManager, SubtitleManager>();
- serviceCollection.AddSingleton<IProviderManager, ProviderManager>();
+ ServiceCollection.AddSingleton<IProviderManager, ProviderManager>();
// TODO: Refactor to eliminate the circular dependency here so that Lazy<T> isn't required
- serviceCollection.AddTransient(provider => new Lazy<ILiveTvManager>(provider.GetRequiredService<ILiveTvManager>));
- serviceCollection.AddSingleton<IDtoService, DtoService>();
+ ServiceCollection.AddTransient(provider => new Lazy<ILiveTvManager>(provider.GetRequiredService<ILiveTvManager>));
+ ServiceCollection.AddSingleton<IDtoService, DtoService>();
- serviceCollection.AddSingleton<IChannelManager, ChannelManager>();
+ ServiceCollection.AddSingleton<IChannelManager, ChannelManager>();
- serviceCollection.AddSingleton<ISessionManager, SessionManager>();
+ ServiceCollection.AddSingleton<ISessionManager, SessionManager>();
- serviceCollection.AddSingleton<IDlnaManager, DlnaManager>();
+ ServiceCollection.AddSingleton<IDlnaManager, DlnaManager>();
- serviceCollection.AddSingleton<ICollectionManager, CollectionManager>();
+ ServiceCollection.AddSingleton<ICollectionManager, CollectionManager>();
- serviceCollection.AddSingleton<IPlaylistManager, PlaylistManager>();
+ ServiceCollection.AddSingleton<IPlaylistManager, PlaylistManager>();
- serviceCollection.AddSingleton<ISyncPlayManager, SyncPlayManager>();
+ ServiceCollection.AddSingleton<ISyncPlayManager, SyncPlayManager>();
- serviceCollection.AddSingleton<LiveTvDtoService>();
- serviceCollection.AddSingleton<ILiveTvManager, LiveTvManager>();
+ ServiceCollection.AddSingleton<LiveTvDtoService>();
+ ServiceCollection.AddSingleton<ILiveTvManager, LiveTvManager>();
- serviceCollection.AddSingleton<IUserViewManager, UserViewManager>();
+ ServiceCollection.AddSingleton<IUserViewManager, UserViewManager>();
- serviceCollection.AddSingleton<INotificationManager, NotificationManager>();
+ ServiceCollection.AddSingleton<INotificationManager, NotificationManager>();
- serviceCollection.AddSingleton<IDeviceDiscovery, DeviceDiscovery>();
+ ServiceCollection.AddSingleton<IDeviceDiscovery, DeviceDiscovery>();
- serviceCollection.AddSingleton<IChapterManager, ChapterManager>();
+ ServiceCollection.AddSingleton<IChapterManager, ChapterManager>();
- serviceCollection.AddSingleton<IEncodingManager, MediaEncoder.EncodingManager>();
+ ServiceCollection.AddSingleton<IEncodingManager, MediaEncoder.EncodingManager>();
- serviceCollection.AddSingleton<IAuthorizationContext, AuthorizationContext>();
- serviceCollection.AddSingleton<ISessionContext, SessionContext>();
+ ServiceCollection.AddSingleton<IAuthorizationContext, AuthorizationContext>();
+ ServiceCollection.AddSingleton<ISessionContext, SessionContext>();
- serviceCollection.AddSingleton<IAuthService, AuthService>();
+ ServiceCollection.AddSingleton<IAuthService, AuthService>();
- serviceCollection.AddSingleton<ISubtitleEncoder, MediaBrowser.MediaEncoding.Subtitles.SubtitleEncoder>();
+ ServiceCollection.AddSingleton<ISubtitleEncoder, MediaBrowser.MediaEncoding.Subtitles.SubtitleEncoder>();
- serviceCollection.AddSingleton<IResourceFileManager, ResourceFileManager>();
- serviceCollection.AddSingleton<EncodingHelper>();
+ ServiceCollection.AddSingleton<IResourceFileManager, ResourceFileManager>();
+ ServiceCollection.AddSingleton<EncodingHelper>();
- serviceCollection.AddSingleton<IAttachmentExtractor, MediaBrowser.MediaEncoding.Attachments.AttachmentExtractor>();
+ ServiceCollection.AddSingleton<IAttachmentExtractor, MediaBrowser.MediaEncoding.Attachments.AttachmentExtractor>();
- serviceCollection.AddSingleton<TranscodingJobHelper>();
- serviceCollection.AddScoped<MediaInfoHelper>();
- serviceCollection.AddScoped<AudioHelper>();
- serviceCollection.AddScoped<DynamicHlsHelper>();
+ ServiceCollection.AddSingleton<TranscodingJobHelper>();
+ ServiceCollection.AddScoped<MediaInfoHelper>();
+ ServiceCollection.AddScoped<AudioHelper>();
+ ServiceCollection.AddScoped<DynamicHlsHelper>();
}
/// <summary>
@@ -834,6 +837,8 @@ namespace Emby.Server.Implementations
{
hasPluginConfiguration.SetStartupInfo(s => Directory.CreateDirectory(s));
}
+
+ plugin.RegisterServices(ServiceCollection);
}
catch (Exception ex)
{
diff --git a/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs b/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs
index a15295fca..f05a30a89 100644
--- a/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs
+++ b/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs
@@ -2,11 +2,11 @@ using System;
using System.Globalization;
using System.IO;
using Emby.Server.Implementations.AppBase;
+using Jellyfin.Data.Events;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.Events;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Serialization;
using Microsoft.Extensions.Logging;
diff --git a/Emby.Server.Implementations/Devices/DeviceManager.cs b/Emby.Server.Implementations/Devices/DeviceManager.cs
index cc4b407f5..f98c694c4 100644
--- a/Emby.Server.Implementations/Devices/DeviceManager.cs
+++ b/Emby.Server.Implementations/Devices/DeviceManager.cs
@@ -7,13 +7,13 @@ using System.IO;
using System.Linq;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
+using Jellyfin.Data.Events;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Security;
using MediaBrowser.Model.Devices;
-using MediaBrowser.Model.Events;
using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Session;
diff --git a/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs b/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs
index 9fce49425..2e8cc76d2 100644
--- a/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs
+++ b/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs
@@ -7,11 +7,11 @@ using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Data.Events;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Plugins;
using MediaBrowser.Model.Dlna;
-using MediaBrowser.Model.Events;
using Microsoft.Extensions.Logging;
using Mono.Nat;
diff --git a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
index 1deef7f72..c9d21d963 100644
--- a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
+++ b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
@@ -7,6 +7,7 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Data.Entities;
+using Jellyfin.Data.Events;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
@@ -15,7 +16,6 @@ using MediaBrowser.Controller.Plugins;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Events;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.EntryPoints
diff --git a/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs b/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs
index 632735910..44d2580d6 100644
--- a/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs
+++ b/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs
@@ -5,6 +5,7 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Data.Enums;
+using Jellyfin.Data.Events;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.Plugins;
@@ -43,22 +44,22 @@ namespace Emby.Server.Implementations.EntryPoints
return Task.CompletedTask;
}
- private async void OnLiveTvManagerSeriesTimerCreated(object sender, MediaBrowser.Model.Events.GenericEventArgs<TimerEventInfo> e)
+ private async void OnLiveTvManagerSeriesTimerCreated(object sender, GenericEventArgs<TimerEventInfo> e)
{
await SendMessage("SeriesTimerCreated", e.Argument).ConfigureAwait(false);
}
- private async void OnLiveTvManagerTimerCreated(object sender, MediaBrowser.Model.Events.GenericEventArgs<TimerEventInfo> e)
+ private async void OnLiveTvManagerTimerCreated(object sender, GenericEventArgs<TimerEventInfo> e)
{
await SendMessage("TimerCreated", e.Argument).ConfigureAwait(false);
}
- private async void OnLiveTvManagerSeriesTimerCancelled(object sender, MediaBrowser.Model.Events.GenericEventArgs<TimerEventInfo> e)
+ private async void OnLiveTvManagerSeriesTimerCancelled(object sender, GenericEventArgs<TimerEventInfo> e)
{
await SendMessage("SeriesTimerCancelled", e.Argument).ConfigureAwait(false);
}
- private async void OnLiveTvManagerTimerCancelled(object sender, MediaBrowser.Model.Events.GenericEventArgs<TimerEventInfo> e)
+ private async void OnLiveTvManagerTimerCancelled(object sender, GenericEventArgs<TimerEventInfo> e)
{
await SendMessage("TimerCancelled", e.Argument).ConfigureAwait(false);
}
diff --git a/Emby.Server.Implementations/EntryPoints/ServerEventNotifier.cs b/Emby.Server.Implementations/EntryPoints/ServerEventNotifier.cs
deleted file mode 100644
index 826d4d8dc..000000000
--- a/Emby.Server.Implementations/EntryPoints/ServerEventNotifier.cs
+++ /dev/null
@@ -1,210 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.Threading;
-using System.Threading.Tasks;
-using Jellyfin.Data.Entities;
-using MediaBrowser.Common.Plugins;
-using MediaBrowser.Common.Updates;
-using MediaBrowser.Controller;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Plugins;
-using MediaBrowser.Controller.Session;
-using MediaBrowser.Model.Events;
-using MediaBrowser.Model.Tasks;
-using MediaBrowser.Model.Updates;
-
-namespace Emby.Server.Implementations.EntryPoints
-{
- /// <summary>
- /// Class WebSocketEvents.
- /// </summary>
- public class ServerEventNotifier : IServerEntryPoint
- {
- /// <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;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="ServerEventNotifier"/> class.
- /// </summary>
- /// <param name="appHost">The application host.</param>
- /// <param name="userManager">The user manager.</param>
- /// <param name="installationManager">The installation manager.</param>
- /// <param name="taskManager">The task manager.</param>
- /// <param name="sessionManager">The session manager.</param>
- public ServerEventNotifier(
- IServerApplicationHost appHost,
- IUserManager userManager,
- IInstallationManager installationManager,
- ITaskManager taskManager,
- ISessionManager sessionManager)
- {
- _userManager = userManager;
- _installationManager = installationManager;
- _appHost = appHost;
- _taskManager = taskManager;
- _sessionManager = sessionManager;
- }
-
- /// <inheritdoc />
- public Task RunAsync()
- {
- _userManager.OnUserDeleted += OnUserDeleted;
- _userManager.OnUserUpdated += OnUserUpdated;
-
- _appHost.HasPendingRestartChanged += OnHasPendingRestartChanged;
-
- _installationManager.PluginUninstalled += OnPluginUninstalled;
- _installationManager.PackageInstalling += OnPackageInstalling;
- _installationManager.PackageInstallationCancelled += OnPackageInstallationCancelled;
- _installationManager.PackageInstallationCompleted += OnPackageInstallationCompleted;
- _installationManager.PackageInstallationFailed += OnPackageInstallationFailed;
-
- _taskManager.TaskCompleted += OnTaskCompleted;
-
- return Task.CompletedTask;
- }
-
- private async void OnPackageInstalling(object sender, InstallationInfo e)
- {
- await SendMessageToAdminSessions("PackageInstalling", e).ConfigureAwait(false);
- }
-
- private async void OnPackageInstallationCancelled(object sender, InstallationInfo e)
- {
- await SendMessageToAdminSessions("PackageInstallationCancelled", e).ConfigureAwait(false);
- }
-
- private async void OnPackageInstallationCompleted(object sender, InstallationInfo e)
- {
- await SendMessageToAdminSessions("PackageInstallationCompleted", e).ConfigureAwait(false);
- }
-
- private async void OnPackageInstallationFailed(object sender, InstallationFailedEventArgs e)
- {
- await SendMessageToAdminSessions("PackageInstallationFailed", e.InstallationInfo).ConfigureAwait(false);
- }
-
- private async void OnTaskCompleted(object sender, TaskCompletionEventArgs e)
- {
- await SendMessageToAdminSessions("ScheduledTaskEnded", e.Result).ConfigureAwait(false);
- }
-
- /// <summary>
- /// Installations the manager_ plugin uninstalled.
- /// </summary>
- /// <param name="sender">The sender.</param>
- /// <param name="e">The e.</param>
- private async void OnPluginUninstalled(object sender, IPlugin e)
- {
- await SendMessageToAdminSessions("PluginUninstalled", e).ConfigureAwait(false);
- }
-
- /// <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>
- private async void OnHasPendingRestartChanged(object sender, EventArgs e)
- {
- await _sessionManager.SendRestartRequiredNotification(CancellationToken.None).ConfigureAwait(false);
- }
-
- /// <summary>
- /// Users the manager_ user updated.
- /// </summary>
- /// <param name="sender">The sender.</param>
- /// <param name="e">The e.</param>
- private async void OnUserUpdated(object sender, GenericEventArgs<User> e)
- {
- var dto = _userManager.GetUserDto(e.Argument);
-
- await SendMessageToUserSession(e.Argument, "UserUpdated", dto).ConfigureAwait(false);
- }
-
- /// <summary>
- /// Users the manager_ user deleted.
- /// </summary>
- /// <param name="sender">The sender.</param>
- /// <param name="e">The e.</param>
- private async void OnUserDeleted(object sender, GenericEventArgs<User> e)
- {
- await SendMessageToUserSession(e.Argument, "UserDeleted", e.Argument.Id.ToString("N", CultureInfo.InvariantCulture)).ConfigureAwait(false);
- }
-
- private async Task SendMessageToAdminSessions<T>(string name, T data)
- {
- try
- {
- await _sessionManager.SendMessageToAdminSessions(name, data, CancellationToken.None).ConfigureAwait(false);
- }
- catch (Exception)
- {
- }
- }
-
- private async Task SendMessageToUserSession<T>(User user, string name, T data)
- {
- try
- {
- await _sessionManager.SendMessageToUserSessions(
- new List<Guid> { user.Id },
- name,
- data,
- CancellationToken.None).ConfigureAwait(false);
- }
- catch (Exception)
- {
- }
- }
-
- /// <inheritdoc />
- public void Dispose()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
-
- /// <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.OnUserDeleted -= OnUserDeleted;
- _userManager.OnUserUpdated -= OnUserUpdated;
-
- _installationManager.PluginUninstalled -= OnPluginUninstalled;
- _installationManager.PackageInstalling -= OnPackageInstalling;
- _installationManager.PackageInstallationCancelled -= OnPackageInstallationCancelled;
- _installationManager.PackageInstallationCompleted -= OnPackageInstallationCompleted;
- _installationManager.PackageInstallationFailed -= OnPackageInstallationFailed;
-
- _appHost.HasPendingRestartChanged -= OnHasPendingRestartChanged;
-
- _taskManager.TaskCompleted -= OnTaskCompleted;
- }
- }
- }
-}
diff --git a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
index dafdd5b7b..fe39bb4b2 100644
--- a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
+++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
@@ -12,13 +12,13 @@ using System.Threading;
using System.Threading.Tasks;
using Emby.Server.Implementations.Services;
using Emby.Server.Implementations.SocketSharp;
+using Jellyfin.Data.Events;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Authentication;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Net;
-using MediaBrowser.Model.Events;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Services;
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
index 80e09f0a3..09c52d95b 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
@@ -13,6 +13,7 @@ using System.Threading.Tasks;
using System.Xml;
using Emby.Server.Implementations.Library;
using Jellyfin.Data.Enums;
+using Jellyfin.Data.Events;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
@@ -29,7 +30,6 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Events;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.LiveTv;
using MediaBrowser.Model.MediaInfo;
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs
index 285a59a24..dd479b7d1 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs
@@ -5,8 +5,8 @@ using System.Collections.Concurrent;
using System.Globalization;
using System.Linq;
using System.Threading;
+using Jellyfin.Data.Events;
using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Model.Events;
using MediaBrowser.Model.LiveTv;
using MediaBrowser.Model.Serialization;
using Microsoft.Extensions.Logging;
diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
index 5ed6baeb9..a898a564f 100644
--- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
+++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
@@ -10,6 +10,7 @@ using System.Threading.Tasks;
using Emby.Server.Implementations.Library;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
+using Jellyfin.Data.Events;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Progress;
@@ -24,7 +25,6 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Sorting;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Events;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.LiveTv;
diff --git a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs
index 1ef083d04..bc01f9543 100644
--- a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs
@@ -6,10 +6,10 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Data.Events;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Progress;
-using MediaBrowser.Model.Events;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Tasks;
using Microsoft.Extensions.Logging;
diff --git a/Emby.Server.Implementations/ScheduledTasks/TaskManager.cs b/Emby.Server.Implementations/ScheduledTasks/TaskManager.cs
index 6d2b4ffc8..6f81bf49b 100644
--- a/Emby.Server.Implementations/ScheduledTasks/TaskManager.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/TaskManager.cs
@@ -5,8 +5,8 @@ using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
+using Jellyfin.Data.Events;
using MediaBrowser.Common.Configuration;
-using MediaBrowser.Model.Events;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Tasks;
using Microsoft.Extensions.Logging;
diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs
index 862a7296c..dad414142 100644
--- a/Emby.Server.Implementations/Session/SessionManager.cs
+++ b/Emby.Server.Implementations/Session/SessionManager.cs
@@ -9,6 +9,7 @@ using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
+using Jellyfin.Data.Events;
using MediaBrowser.Common.Events;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller;
@@ -17,6 +18,8 @@ using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Events;
+using MediaBrowser.Controller.Events.Session;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Security;
@@ -24,7 +27,6 @@ using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Devices;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Events;
using MediaBrowser.Model.Library;
using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Session;
@@ -40,25 +42,16 @@ namespace Emby.Server.Implementations.Session
/// </summary>
public class SessionManager : ISessionManager, IDisposable
{
- /// <summary>
- /// The user data repository.
- /// </summary>
private readonly IUserDataManager _userDataManager;
-
- /// <summary>
- /// The logger.
- /// </summary>
private readonly ILogger<SessionManager> _logger;
-
+ private readonly IEventManager _eventManager;
private readonly ILibraryManager _libraryManager;
private readonly IUserManager _userManager;
private readonly IMusicManager _musicManager;
private readonly IDtoService _dtoService;
private readonly IImageProcessor _imageProcessor;
private readonly IMediaSourceManager _mediaSourceManager;
-
private readonly IServerApplicationHost _appHost;
-
private readonly IAuthenticationRepository _authRepo;
private readonly IDeviceManager _deviceManager;
@@ -75,6 +68,7 @@ namespace Emby.Server.Implementations.Session
public SessionManager(
ILogger<SessionManager> logger,
+ IEventManager eventManager,
IUserDataManager userDataManager,
ILibraryManager libraryManager,
IUserManager userManager,
@@ -87,6 +81,7 @@ namespace Emby.Server.Implementations.Session
IMediaSourceManager mediaSourceManager)
{
_logger = logger;
+ _eventManager = eventManager;
_userDataManager = userDataManager;
_libraryManager = libraryManager;
_userManager = userManager;
@@ -209,6 +204,8 @@ namespace Emby.Server.Implementations.Session
}
}
+ _eventManager.Publish(new SessionStartedEventArgs(info));
+
EventHelper.QueueEventIfNotNull(
SessionStarted,
this,
@@ -230,6 +227,8 @@ namespace Emby.Server.Implementations.Session
},
_logger);
+ _eventManager.Publish(new SessionEndedEventArgs(info));
+
info.Dispose();
}
@@ -667,22 +666,26 @@ namespace Emby.Server.Implementations.Session
}
}
+ var eventArgs = new PlaybackProgressEventArgs
+ {
+ Item = libraryItem,
+ Users = users,
+ MediaSourceId = info.MediaSourceId,
+ MediaInfo = info.Item,
+ DeviceName = session.DeviceName,
+ ClientName = session.Client,
+ DeviceId = session.DeviceId,
+ Session = session
+ };
+
+ await _eventManager.PublishAsync(eventArgs).ConfigureAwait(false);
+
// Nothing to save here
// Fire events to inform plugins
EventHelper.QueueEventIfNotNull(
PlaybackStart,
this,
- new PlaybackProgressEventArgs
- {
- Item = libraryItem,
- Users = users,
- MediaSourceId = info.MediaSourceId,
- MediaInfo = info.Item,
- DeviceName = session.DeviceName,
- ClientName = session.Client,
- DeviceId = session.DeviceId,
- Session = session
- },
+ eventArgs,
_logger);
StartIdleCheckTimer();
@@ -750,23 +753,25 @@ namespace Emby.Server.Implementations.Session
}
}
- PlaybackProgress?.Invoke(
- this,
- new PlaybackProgressEventArgs
- {
- Item = libraryItem,
- Users = users,
- PlaybackPositionTicks = session.PlayState.PositionTicks,
- MediaSourceId = session.PlayState.MediaSourceId,
- MediaInfo = info.Item,
- DeviceName = session.DeviceName,
- ClientName = session.Client,
- DeviceId = session.DeviceId,
- IsPaused = info.IsPaused,
- PlaySessionId = info.PlaySessionId,
- IsAutomated = isAutomated,
- Session = session
- });
+ var eventArgs = new PlaybackProgressEventArgs
+ {
+ Item = libraryItem,
+ Users = users,
+ PlaybackPositionTicks = session.PlayState.PositionTicks,
+ MediaSourceId = session.PlayState.MediaSourceId,
+ MediaInfo = info.Item,
+ DeviceName = session.DeviceName,
+ ClientName = session.Client,
+ DeviceId = session.DeviceId,
+ IsPaused = info.IsPaused,
+ PlaySessionId = info.PlaySessionId,
+ IsAutomated = isAutomated,
+ Session = session
+ };
+
+ await _eventManager.PublishAsync(eventArgs).ConfigureAwait(false);
+
+ PlaybackProgress?.Invoke(this, eventArgs);
if (!isAutomated)
{
@@ -943,23 +948,23 @@ namespace Emby.Server.Implementations.Session
}
}
- EventHelper.QueueEventIfNotNull(
- PlaybackStopped,
- this,
- new PlaybackStopEventArgs
- {
- Item = libraryItem,
- Users = users,
- PlaybackPositionTicks = info.PositionTicks,
- PlayedToCompletion = playedToCompletion,
- MediaSourceId = info.MediaSourceId,
- MediaInfo = info.Item,
- DeviceName = session.DeviceName,
- ClientName = session.Client,
- DeviceId = session.DeviceId,
- Session = session
- },
- _logger);
+ var eventArgs = new PlaybackStopEventArgs
+ {
+ Item = libraryItem,
+ Users = users,
+ PlaybackPositionTicks = info.PositionTicks,
+ PlayedToCompletion = playedToCompletion,
+ MediaSourceId = info.MediaSourceId,
+ MediaInfo = info.Item,
+ DeviceName = session.DeviceName,
+ ClientName = session.Client,
+ DeviceId = session.DeviceId,
+ Session = session
+ };
+
+ await _eventManager.PublishAsync(eventArgs).ConfigureAwait(false);
+
+ EventHelper.QueueEventIfNotNull(PlaybackStopped, this, eventArgs, _logger);
}
private bool OnPlaybackStopped(User user, BaseItem item, long? positionTicks, bool playbackFailed)
diff --git a/Emby.Server.Implementations/Session/SessionWebSocketListener.cs b/Emby.Server.Implementations/Session/SessionWebSocketListener.cs
index 8bebd37dc..1da7a6473 100644
--- a/Emby.Server.Implementations/Session/SessionWebSocketListener.cs
+++ b/Emby.Server.Implementations/Session/SessionWebSocketListener.cs
@@ -4,9 +4,9 @@ using System.Linq;
using System.Net.WebSockets;
using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Data.Events;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Session;
-using MediaBrowser.Model.Events;
using MediaBrowser.Model.Net;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
diff --git a/Jellyfin.Api/Controllers/DlnaServerController.cs b/Jellyfin.Api/Controllers/DlnaServerController.cs
index bbaf39eb1..ee87d917b 100644
--- a/Jellyfin.Api/Controllers/DlnaServerController.cs
+++ b/Jellyfin.Api/Controllers/DlnaServerController.cs
@@ -229,7 +229,7 @@ namespace Jellyfin.Api.Controllers
});
}
- private EventSubscriptionResponse ProcessEventRequest(IEventManager eventManager)
+ private EventSubscriptionResponse ProcessEventRequest(IDlnaEventManager dlnaEventManager)
{
var subscriptionId = Request.Headers["SID"];
if (string.Equals(Request.Method, "subscribe", StringComparison.OrdinalIgnoreCase))
@@ -240,17 +240,17 @@ namespace Jellyfin.Api.Controllers
if (string.IsNullOrEmpty(notificationType))
{
- return eventManager.RenewEventSubscription(
+ return dlnaEventManager.RenewEventSubscription(
subscriptionId,
notificationType,
timeoutString,
callback);
}
- return eventManager.CreateEventSubscription(notificationType, timeoutString, callback);
+ return dlnaEventManager.CreateEventSubscription(notificationType, timeoutString, callback);
}
- return eventManager.CancelEventSubscription(subscriptionId);
+ return dlnaEventManager.CancelEventSubscription(subscriptionId);
}
}
}
diff --git a/Jellyfin.Api/WebSocketListeners/ActivityLogWebSocketListener.cs b/Jellyfin.Api/WebSocketListeners/ActivityLogWebSocketListener.cs
index 6395b8d62..849b3b709 100644
--- a/Jellyfin.Api/WebSocketListeners/ActivityLogWebSocketListener.cs
+++ b/Jellyfin.Api/WebSocketListeners/ActivityLogWebSocketListener.cs
@@ -1,8 +1,8 @@
using System;
using System.Threading.Tasks;
+using Jellyfin.Data.Events;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Activity;
-using MediaBrowser.Model.Events;
using Microsoft.Extensions.Logging;
namespace Jellyfin.Api.WebSocketListeners
diff --git a/Jellyfin.Api/WebSocketListeners/ScheduledTasksWebSocketListener.cs b/Jellyfin.Api/WebSocketListeners/ScheduledTasksWebSocketListener.cs
index 12f815ff7..8a966c137 100644
--- a/Jellyfin.Api/WebSocketListeners/ScheduledTasksWebSocketListener.cs
+++ b/Jellyfin.Api/WebSocketListeners/ScheduledTasksWebSocketListener.cs
@@ -1,8 +1,8 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
+using Jellyfin.Data.Events;
using MediaBrowser.Controller.Net;
-using MediaBrowser.Model.Events;
using MediaBrowser.Model.Tasks;
using Microsoft.Extensions.Logging;
diff --git a/MediaBrowser.Model/Events/GenericEventArgs.cs b/Jellyfin.Data/Events/GenericEventArgs.cs
index 44f60f811..7b9a5111e 100644
--- a/MediaBrowser.Model/Events/GenericEventArgs.cs
+++ b/Jellyfin.Data/Events/GenericEventArgs.cs
@@ -1,20 +1,14 @@
using System;
-namespace MediaBrowser.Model.Events
+namespace Jellyfin.Data.Events
{
/// <summary>
/// Provides a generic EventArgs subclass that can hold any kind of object.
/// </summary>
- /// <typeparam name="T"></typeparam>
+ /// <typeparam name="T">The type of this event.</typeparam>
public class GenericEventArgs<T> : EventArgs
{
/// <summary>
- /// Gets or sets the argument.
- /// </summary>
- /// <value>The argument.</value>
- public T Argument { get; set; }
-
- /// <summary>
/// Initializes a new instance of the <see cref="GenericEventArgs{T}"/> class.
/// </summary>
/// <param name="arg">The argument.</param>
@@ -22,5 +16,11 @@ namespace MediaBrowser.Model.Events
{
Argument = arg;
}
+
+ /// <summary>
+ /// Gets the argument.
+ /// </summary>
+ /// <value>The argument.</value>
+ public T Argument { get; }
}
}
diff --git a/Jellyfin.Data/Events/System/PendingRestartEventArgs.cs b/Jellyfin.Data/Events/System/PendingRestartEventArgs.cs
new file mode 100644
index 000000000..2aa40a946
--- /dev/null
+++ b/Jellyfin.Data/Events/System/PendingRestartEventArgs.cs
@@ -0,0 +1,11 @@
+using System;
+
+namespace Jellyfin.Data.Events.System
+{
+ /// <summary>
+ /// An event that occurs when there is a pending restart.
+ /// </summary>
+ public class PendingRestartEventArgs : EventArgs
+ {
+ }
+}
diff --git a/Jellyfin.Data/Events/Users/UserCreatedEventArgs.cs b/Jellyfin.Data/Events/Users/UserCreatedEventArgs.cs
new file mode 100644
index 000000000..66f7c8d4f
--- /dev/null
+++ b/Jellyfin.Data/Events/Users/UserCreatedEventArgs.cs
@@ -0,0 +1,18 @@
+using Jellyfin.Data.Entities;
+
+namespace Jellyfin.Data.Events.Users
+{
+ /// <summary>
+ /// An event that occurs when a user is created.
+ /// </summary>
+ public class UserCreatedEventArgs : GenericEventArgs<User>
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="UserCreatedEventArgs"/> class.
+ /// </summary>
+ /// <param name="arg">The user.</param>
+ public UserCreatedEventArgs(User arg) : base(arg)
+ {
+ }
+ }
+}
diff --git a/Jellyfin.Data/Events/Users/UserDeletedEventArgs.cs b/Jellyfin.Data/Events/Users/UserDeletedEventArgs.cs
new file mode 100644
index 000000000..0b9493375
--- /dev/null
+++ b/Jellyfin.Data/Events/Users/UserDeletedEventArgs.cs
@@ -0,0 +1,18 @@
+using Jellyfin.Data.Entities;
+
+namespace Jellyfin.Data.Events.Users
+{
+ /// <summary>
+ /// An event that occurs when a user is deleted.
+ /// </summary>
+ public class UserDeletedEventArgs : GenericEventArgs<User>
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="UserDeletedEventArgs"/> class.
+ /// </summary>
+ /// <param name="arg">The user.</param>
+ public UserDeletedEventArgs(User arg) : base(arg)
+ {
+ }
+ }
+}
diff --git a/Jellyfin.Data/Events/Users/UserLockedOutEventArgs.cs b/Jellyfin.Data/Events/Users/UserLockedOutEventArgs.cs
new file mode 100644
index 000000000..cca3726dc
--- /dev/null
+++ b/Jellyfin.Data/Events/Users/UserLockedOutEventArgs.cs
@@ -0,0 +1,18 @@
+using Jellyfin.Data.Entities;
+
+namespace Jellyfin.Data.Events.Users
+{
+ /// <summary>
+ /// An event that occurs when a user is locked out.
+ /// </summary>
+ public class UserLockedOutEventArgs : GenericEventArgs<User>
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="UserLockedOutEventArgs"/> class.
+ /// </summary>
+ /// <param name="arg">The user.</param>
+ public UserLockedOutEventArgs(User arg) : base(arg)
+ {
+ }
+ }
+}
diff --git a/Jellyfin.Data/Events/Users/UserPasswordChangedEventArgs.cs b/Jellyfin.Data/Events/Users/UserPasswordChangedEventArgs.cs
new file mode 100644
index 000000000..087ec9ab6
--- /dev/null
+++ b/Jellyfin.Data/Events/Users/UserPasswordChangedEventArgs.cs
@@ -0,0 +1,18 @@
+using Jellyfin.Data.Entities;
+
+namespace Jellyfin.Data.Events.Users
+{
+ /// <summary>
+ /// An event that occurs when a user's password has changed.
+ /// </summary>
+ public class UserPasswordChangedEventArgs : GenericEventArgs<User>
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="UserPasswordChangedEventArgs"/> class.
+ /// </summary>
+ /// <param name="arg">The user.</param>
+ public UserPasswordChangedEventArgs(User arg) : base(arg)
+ {
+ }
+ }
+}
diff --git a/Jellyfin.Data/Events/Users/UserUpdatedEventArgs.cs b/Jellyfin.Data/Events/Users/UserUpdatedEventArgs.cs
new file mode 100644
index 000000000..2b4e49cdf
--- /dev/null
+++ b/Jellyfin.Data/Events/Users/UserUpdatedEventArgs.cs
@@ -0,0 +1,18 @@
+using Jellyfin.Data.Entities;
+
+namespace Jellyfin.Data.Events.Users
+{
+ /// <summary>
+ /// An event that occurs when a user is updated.
+ /// </summary>
+ public class UserUpdatedEventArgs : GenericEventArgs<User>
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="UserUpdatedEventArgs"/> class.
+ /// </summary>
+ /// <param name="arg">The user.</param>
+ public UserUpdatedEventArgs(User arg) : base(arg)
+ {
+ }
+ }
+}
diff --git a/Jellyfin.Server.Implementations/Activity/ActivityManager.cs b/Jellyfin.Server.Implementations/Activity/ActivityManager.cs
index 2deefbe81..09f2611e4 100644
--- a/Jellyfin.Server.Implementations/Activity/ActivityManager.cs
+++ b/Jellyfin.Server.Implementations/Activity/ActivityManager.cs
@@ -2,8 +2,8 @@ using System;
using System.Linq;
using System.Threading.Tasks;
using Jellyfin.Data.Entities;
+using Jellyfin.Data.Events;
using MediaBrowser.Model.Activity;
-using MediaBrowser.Model.Events;
using MediaBrowser.Model.Querying;
namespace Jellyfin.Server.Implementations.Activity
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Library/SubtitleDownloadFailureLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/Library/SubtitleDownloadFailureLogger.cs
new file mode 100644
index 000000000..449f27be2
--- /dev/null
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Library/SubtitleDownloadFailureLogger.cs
@@ -0,0 +1,102 @@
+using System;
+using System.Globalization;
+using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities.Audio;
+using MediaBrowser.Controller.Events;
+using MediaBrowser.Controller.Subtitles;
+using MediaBrowser.Model.Activity;
+using MediaBrowser.Model.Globalization;
+using Episode = MediaBrowser.Controller.Entities.TV.Episode;
+
+namespace Jellyfin.Server.Implementations.Events.Consumers.Library
+{
+ /// <summary>
+ /// Creates an entry in the activity log whenever a subtitle download fails.
+ /// </summary>
+ public class SubtitleDownloadFailureLogger : IEventConsumer<SubtitleDownloadFailureEventArgs>
+ {
+ private readonly ILocalizationManager _localizationManager;
+ private readonly IActivityManager _activityManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="SubtitleDownloadFailureLogger"/> class.
+ /// </summary>
+ /// <param name="localizationManager">The localization manager.</param>
+ /// <param name="activityManager">The activity manager.</param>
+ public SubtitleDownloadFailureLogger(ILocalizationManager localizationManager, IActivityManager activityManager)
+ {
+ _localizationManager = localizationManager;
+ _activityManager = activityManager;
+ }
+
+ /// <inheritdoc />
+ public async Task OnEvent(SubtitleDownloadFailureEventArgs eventArgs)
+ {
+ await _activityManager.CreateAsync(new ActivityLog(
+ string.Format(
+ CultureInfo.InvariantCulture,
+ _localizationManager.GetLocalizedString("SubtitleDownloadFailureFromForItem"),
+ eventArgs.Provider,
+ GetItemName(eventArgs.Item)),
+ "SubtitleDownloadFailure",
+ Guid.Empty)
+ {
+ ItemId = eventArgs.Item.Id.ToString("N", CultureInfo.InvariantCulture),
+ ShortOverview = eventArgs.Exception.Message
+ }).ConfigureAwait(false);
+ }
+
+ private static string GetItemName(BaseItem item)
+ {
+ var name = item.Name;
+ if (item is Episode episode)
+ {
+ if (episode.IndexNumber.HasValue)
+ {
+ name = string.Format(
+ CultureInfo.InvariantCulture,
+ "Ep{0} - {1}",
+ episode.IndexNumber.Value,
+ name);
+ }
+
+ if (episode.ParentIndexNumber.HasValue)
+ {
+ name = string.Format(
+ CultureInfo.InvariantCulture,
+ "S{0}, {1}",
+ episode.ParentIndexNumber.Value,
+ name);
+ }
+ }
+
+ if (item is IHasSeries hasSeries)
+ {
+ name = hasSeries.SeriesName + " - " + name;
+ }
+
+ if (item is IHasAlbumArtist hasAlbumArtist)
+ {
+ var artists = hasAlbumArtist.AlbumArtists;
+
+ if (artists.Count > 0)
+ {
+ name = artists[0] + " - " + name;
+ }
+ }
+ else if (item is IHasArtist hasArtist)
+ {
+ var artists = hasArtist.Artists;
+
+ if (artists.Count > 0)
+ {
+ name = artists[0] + " - " + name;
+ }
+ }
+
+ return name;
+ }
+ }
+}
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Security/AuthenticationFailedLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/Security/AuthenticationFailedLogger.cs
new file mode 100644
index 000000000..f899b4497
--- /dev/null
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Security/AuthenticationFailedLogger.cs
@@ -0,0 +1,52 @@
+using System;
+using System.Globalization;
+using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
+using Jellyfin.Data.Events;
+using MediaBrowser.Controller.Events;
+using MediaBrowser.Controller.Session;
+using MediaBrowser.Model.Activity;
+using MediaBrowser.Model.Globalization;
+using Microsoft.Extensions.Logging;
+
+namespace Jellyfin.Server.Implementations.Events.Consumers.Security
+{
+ /// <summary>
+ /// Creates an entry in the activity log when there is a failed login attempt.
+ /// </summary>
+ public class AuthenticationFailedLogger : IEventConsumer<GenericEventArgs<AuthenticationRequest>>
+ {
+ private readonly ILocalizationManager _localizationManager;
+ private readonly IActivityManager _activityManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="AuthenticationFailedLogger"/> class.
+ /// </summary>
+ /// <param name="localizationManager">The localization manager.</param>
+ /// <param name="activityManager">The activity manager.</param>
+ public AuthenticationFailedLogger(ILocalizationManager localizationManager, IActivityManager activityManager)
+ {
+ _localizationManager = localizationManager;
+ _activityManager = activityManager;
+ }
+
+ /// <inheritdoc />
+ public async Task OnEvent(GenericEventArgs<AuthenticationRequest> eventArgs)
+ {
+ await _activityManager.CreateAsync(new ActivityLog(
+ string.Format(
+ CultureInfo.InvariantCulture,
+ _localizationManager.GetLocalizedString("FailedLoginAttemptWithUserName"),
+ eventArgs.Argument.Username),
+ "AuthenticationFailed",
+ Guid.Empty)
+ {
+ LogSeverity = LogLevel.Error,
+ ShortOverview = string.Format(
+ CultureInfo.InvariantCulture,
+ _localizationManager.GetLocalizedString("LabelIpAddressValue"),
+ eventArgs.Argument.RemoteEndPoint),
+ }).ConfigureAwait(false);
+ }
+ }
+}
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Security/AuthenticationSucceededLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/Security/AuthenticationSucceededLogger.cs
new file mode 100644
index 000000000..2f9f44ed6
--- /dev/null
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Security/AuthenticationSucceededLogger.cs
@@ -0,0 +1,49 @@
+using System.Globalization;
+using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
+using Jellyfin.Data.Events;
+using MediaBrowser.Controller.Authentication;
+using MediaBrowser.Controller.Events;
+using MediaBrowser.Model.Activity;
+using MediaBrowser.Model.Globalization;
+
+namespace Jellyfin.Server.Implementations.Events.Consumers.Security
+{
+ /// <summary>
+ /// Creates an entry in the activity log when there is a successful login attempt.
+ /// </summary>
+ public class AuthenticationSucceededLogger : IEventConsumer<GenericEventArgs<AuthenticationResult>>
+ {
+ private readonly ILocalizationManager _localizationManager;
+ private readonly IActivityManager _activityManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="AuthenticationSucceededLogger"/> class.
+ /// </summary>
+ /// <param name="localizationManager">The localization manager.</param>
+ /// <param name="activityManager">The activity manager.</param>
+ public AuthenticationSucceededLogger(ILocalizationManager localizationManager, IActivityManager activityManager)
+ {
+ _localizationManager = localizationManager;
+ _activityManager = activityManager;
+ }
+
+ /// <inheritdoc />
+ public async Task OnEvent(GenericEventArgs<AuthenticationResult> e)
+ {
+ await _activityManager.CreateAsync(new ActivityLog(
+ string.Format(
+ CultureInfo.InvariantCulture,
+ _localizationManager.GetLocalizedString("AuthenticationSucceededWithUserName"),
+ e.Argument.User.Name),
+ "AuthenticationSucceeded",
+ e.Argument.User.Id)
+ {
+ ShortOverview = string.Format(
+ CultureInfo.InvariantCulture,
+ _localizationManager.GetLocalizedString("LabelIpAddressValue"),
+ e.Argument.SessionInfo.RemoteEndPoint),
+ }).ConfigureAwait(false);
+ }
+ }
+}
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStartLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStartLogger.cs
new file mode 100644
index 000000000..ec4a76e7f
--- /dev/null
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStartLogger.cs
@@ -0,0 +1,104 @@
+using System;
+using System.Globalization;
+using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
+using MediaBrowser.Controller.Events;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Model.Activity;
+using MediaBrowser.Model.Dto;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Globalization;
+using MediaBrowser.Model.Notifications;
+using Microsoft.Extensions.Logging;
+
+namespace Jellyfin.Server.Implementations.Events.Consumers.Session
+{
+ /// <summary>
+ /// Creates an entry in the activity log whenever a user starts playback.
+ /// </summary>
+ public class PlaybackStartLogger : IEventConsumer<PlaybackStartEventArgs>
+ {
+ private readonly ILogger<PlaybackStartLogger> _logger;
+ private readonly ILocalizationManager _localizationManager;
+ private readonly IActivityManager _activityManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PlaybackStartLogger"/> class.
+ /// </summary>
+ /// <param name="logger">The logger.</param>
+ /// <param name="localizationManager">The localization manager.</param>
+ /// <param name="activityManager">The activity manager.</param>
+ public PlaybackStartLogger(ILogger<PlaybackStartLogger> logger, ILocalizationManager localizationManager, IActivityManager activityManager)
+ {
+ _logger = logger;
+ _localizationManager = localizationManager;
+ _activityManager = activityManager;
+ }
+
+ /// <inheritdoc />
+ public async Task OnEvent(PlaybackStartEventArgs eventArgs)
+ {
+ if (eventArgs.MediaInfo == null)
+ {
+ _logger.LogWarning("PlaybackStart reported with null media info.");
+ return;
+ }
+
+ if (eventArgs.Item != null && eventArgs.Item.IsThemeMedia)
+ {
+ // Don't report theme song or local trailer playback
+ return;
+ }
+
+ if (eventArgs.Users.Count == 0)
+ {
+ return;
+ }
+
+ var user = eventArgs.Users[0];
+
+ await _activityManager.CreateAsync(new ActivityLog(
+ string.Format(
+ CultureInfo.InvariantCulture,
+ _localizationManager.GetLocalizedString("UserStartedPlayingItemWithValues"),
+ user.Username,
+ GetItemName(eventArgs.MediaInfo),
+ eventArgs.DeviceName),
+ GetPlaybackNotificationType(eventArgs.MediaInfo.MediaType),
+ user.Id))
+ .ConfigureAwait(false);
+ }
+
+ private static string GetItemName(BaseItemDto item)
+ {
+ var name = item.Name;
+
+ if (!string.IsNullOrEmpty(item.SeriesName))
+ {
+ name = item.SeriesName + " - " + name;
+ }
+
+ if (item.Artists != null && item.Artists.Count > 0)
+ {
+ name = item.Artists[0] + " - " + name;
+ }
+
+ return name;
+ }
+
+ private static string GetPlaybackNotificationType(string mediaType)
+ {
+ if (string.Equals(mediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase))
+ {
+ return NotificationType.AudioPlayback.ToString();
+ }
+
+ if (string.Equals(mediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase))
+ {
+ return NotificationType.VideoPlayback.ToString();
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStopLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStopLogger.cs
new file mode 100644
index 000000000..51a882c14
--- /dev/null
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStopLogger.cs
@@ -0,0 +1,106 @@
+using System;
+using System.Globalization;
+using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
+using MediaBrowser.Controller.Events;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Model.Activity;
+using MediaBrowser.Model.Dto;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Globalization;
+using MediaBrowser.Model.Notifications;
+using Microsoft.Extensions.Logging;
+
+namespace Jellyfin.Server.Implementations.Events.Consumers.Session
+{
+ /// <summary>
+ /// Creates an activity log entry whenever a user stops playback.
+ /// </summary>
+ public class PlaybackStopLogger : IEventConsumer<PlaybackStopEventArgs>
+ {
+ private readonly ILogger<PlaybackStopLogger> _logger;
+ private readonly ILocalizationManager _localizationManager;
+ private readonly IActivityManager _activityManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PlaybackStopLogger"/> class.
+ /// </summary>
+ /// <param name="logger">The logger.</param>
+ /// <param name="localizationManager">The localization manager.</param>
+ /// <param name="activityManager">The activity manager.</param>
+ public PlaybackStopLogger(ILogger<PlaybackStopLogger> logger, ILocalizationManager localizationManager, IActivityManager activityManager)
+ {
+ _logger = logger;
+ _localizationManager = localizationManager;
+ _activityManager = activityManager;
+ }
+
+ /// <inheritdoc />
+ public async Task OnEvent(PlaybackStopEventArgs eventArgs)
+ {
+ var item = eventArgs.MediaInfo;
+
+ if (item == null)
+ {
+ _logger.LogWarning("PlaybackStopped reported with null media info.");
+ return;
+ }
+
+ if (eventArgs.Item != null && eventArgs.Item.IsThemeMedia)
+ {
+ // Don't report theme song or local trailer playback
+ return;
+ }
+
+ if (eventArgs.Users.Count == 0)
+ {
+ return;
+ }
+
+ var user = eventArgs.Users[0];
+
+ await _activityManager.CreateAsync(new ActivityLog(
+ string.Format(
+ CultureInfo.InvariantCulture,
+ _localizationManager.GetLocalizedString("UserStoppedPlayingItemWithValues"),
+ user.Username,
+ GetItemName(item),
+ eventArgs.DeviceName),
+ GetPlaybackStoppedNotificationType(item.MediaType),
+ user.Id))
+ .ConfigureAwait(false);
+ }
+
+ private static string GetItemName(BaseItemDto item)
+ {
+ var name = item.Name;
+
+ if (!string.IsNullOrEmpty(item.SeriesName))
+ {
+ name = item.SeriesName + " - " + name;
+ }
+
+ if (item.Artists != null && item.Artists.Count > 0)
+ {
+ name = item.Artists[0] + " - " + name;
+ }
+
+ return name;
+ }
+
+ private static string GetPlaybackStoppedNotificationType(string mediaType)
+ {
+ if (string.Equals(mediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase))
+ {
+ return NotificationType.AudioPlaybackStopped.ToString();
+ }
+
+ if (string.Equals(mediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase))
+ {
+ return NotificationType.VideoPlaybackStopped.ToString();
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Session/SessionEndedLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/Session/SessionEndedLogger.cs
new file mode 100644
index 000000000..cf20946ec
--- /dev/null
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Session/SessionEndedLogger.cs
@@ -0,0 +1,54 @@
+using System.Globalization;
+using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
+using MediaBrowser.Controller.Events;
+using MediaBrowser.Controller.Events.Session;
+using MediaBrowser.Model.Activity;
+using MediaBrowser.Model.Globalization;
+
+namespace Jellyfin.Server.Implementations.Events.Consumers.Session
+{
+ /// <summary>
+ /// Creates an entry in the activity log whenever a session ends.
+ /// </summary>
+ public class SessionEndedLogger : IEventConsumer<SessionEndedEventArgs>
+ {
+ private readonly ILocalizationManager _localizationManager;
+ private readonly IActivityManager _activityManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="SessionEndedLogger"/> class.
+ /// </summary>
+ /// <param name="localizationManager">The localization manager.</param>
+ /// <param name="activityManager">The activity manager.</param>
+ public SessionEndedLogger(ILocalizationManager localizationManager, IActivityManager activityManager)
+ {
+ _localizationManager = localizationManager;
+ _activityManager = activityManager;
+ }
+
+ /// <inheritdoc />
+ public async Task OnEvent(SessionEndedEventArgs eventArgs)
+ {
+ if (string.IsNullOrEmpty(eventArgs.Argument.UserName))
+ {
+ return;
+ }
+
+ await _activityManager.CreateAsync(new ActivityLog(
+ string.Format(
+ CultureInfo.InvariantCulture,
+ _localizationManager.GetLocalizedString("UserOfflineFromDevice"),
+ eventArgs.Argument.UserName,
+ eventArgs.Argument.DeviceName),
+ "SessionEnded",
+ eventArgs.Argument.UserId)
+ {
+ ShortOverview = string.Format(
+ CultureInfo.InvariantCulture,
+ _localizationManager.GetLocalizedString("LabelIpAddressValue"),
+ eventArgs.Argument.RemoteEndPoint),
+ }).ConfigureAwait(false);
+ }
+ }
+}
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Session/SessionStartedLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/Session/SessionStartedLogger.cs
new file mode 100644
index 000000000..6a0f29b09
--- /dev/null
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Session/SessionStartedLogger.cs
@@ -0,0 +1,54 @@
+using System.Globalization;
+using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
+using MediaBrowser.Controller.Events;
+using MediaBrowser.Controller.Events.Session;
+using MediaBrowser.Model.Activity;
+using MediaBrowser.Model.Globalization;
+
+namespace Jellyfin.Server.Implementations.Events.Consumers.Session
+{
+ /// <summary>
+ /// Creates an entry in the activity log when a session is started.
+ /// </summary>
+ public class SessionStartedLogger : IEventConsumer<SessionStartedEventArgs>
+ {
+ private readonly ILocalizationManager _localizationManager;
+ private readonly IActivityManager _activityManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="SessionStartedLogger"/> class.
+ /// </summary>
+ /// <param name="localizationManager">The localization manager.</param>
+ /// <param name="activityManager">The activity manager.</param>
+ public SessionStartedLogger(ILocalizationManager localizationManager, IActivityManager activityManager)
+ {
+ _localizationManager = localizationManager;
+ _activityManager = activityManager;
+ }
+
+ /// <inheritdoc />
+ public async Task OnEvent(SessionStartedEventArgs eventArgs)
+ {
+ if (string.IsNullOrEmpty(eventArgs.Argument.UserName))
+ {
+ return;
+ }
+
+ await _activityManager.CreateAsync(new ActivityLog(
+ string.Format(
+ CultureInfo.InvariantCulture,
+ _localizationManager.GetLocalizedString("UserOnlineFromDevice"),
+ eventArgs.Argument.UserName,
+ eventArgs.Argument.DeviceName),
+ "SessionStarted",
+ eventArgs.Argument.UserId)
+ {
+ ShortOverview = string.Format(
+ CultureInfo.InvariantCulture,
+ _localizationManager.GetLocalizedString("LabelIpAddressValue"),
+ eventArgs.Argument.RemoteEndPoint)
+ }).ConfigureAwait(false);
+ }
+ }
+}
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/System/PendingRestartNotifier.cs b/Jellyfin.Server.Implementations/Events/Consumers/System/PendingRestartNotifier.cs
new file mode 100644
index 000000000..2fa38dd71
--- /dev/null
+++ b/Jellyfin.Server.Implementations/Events/Consumers/System/PendingRestartNotifier.cs
@@ -0,0 +1,31 @@
+using System.Threading;
+using System.Threading.Tasks;
+using Jellyfin.Data.Events.System;
+using MediaBrowser.Controller.Events;
+using MediaBrowser.Controller.Session;
+
+namespace Jellyfin.Server.Implementations.Events.Consumers.System
+{
+ /// <summary>
+ /// Notifies users when there is a pending restart.
+ /// </summary>
+ public class PendingRestartNotifier : IEventConsumer<PendingRestartEventArgs>
+ {
+ private readonly ISessionManager _sessionManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PendingRestartNotifier"/> class.
+ /// </summary>
+ /// <param name="sessionManager">The session manager.</param>
+ public PendingRestartNotifier(ISessionManager sessionManager)
+ {
+ _sessionManager = sessionManager;
+ }
+
+ /// <inheritdoc />
+ public async Task OnEvent(PendingRestartEventArgs eventArgs)
+ {
+ await _sessionManager.SendRestartRequiredNotification(CancellationToken.None).ConfigureAwait(false);
+ }
+ }
+}
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/System/TaskCompletedLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/System/TaskCompletedLogger.cs
new file mode 100644
index 000000000..05201a346
--- /dev/null
+++ b/Jellyfin.Server.Implementations/Events/Consumers/System/TaskCompletedLogger.cs
@@ -0,0 +1,158 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Text;
+using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
+using MediaBrowser.Controller.Events;
+using MediaBrowser.Model.Activity;
+using MediaBrowser.Model.Globalization;
+using MediaBrowser.Model.Notifications;
+using MediaBrowser.Model.Tasks;
+using Microsoft.Extensions.Logging;
+
+namespace Jellyfin.Server.Implementations.Events.Consumers.System
+{
+ /// <summary>
+ /// Creates an activity log entry whenever a task is completed.
+ /// </summary>
+ public class TaskCompletedLogger : IEventConsumer<TaskCompletionEventArgs>
+ {
+ private readonly ILocalizationManager _localizationManager;
+ private readonly IActivityManager _activityManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="TaskCompletedLogger"/> class.
+ /// </summary>
+ /// <param name="localizationManager">The localization manager.</param>
+ /// <param name="activityManager">The activity manager.</param>
+ public TaskCompletedLogger(ILocalizationManager localizationManager, IActivityManager activityManager)
+ {
+ _localizationManager = localizationManager;
+ _activityManager = activityManager;
+ }
+
+ /// <inheritdoc />
+ public async Task OnEvent(TaskCompletionEventArgs e)
+ {
+ var result = e.Result;
+ var task = e.Task;
+
+ if (task.ScheduledTask is IConfigurableScheduledTask activityTask
+ && !activityTask.IsLogged)
+ {
+ return;
+ }
+
+ var time = result.EndTimeUtc - result.StartTimeUtc;
+ var runningTime = string.Format(
+ CultureInfo.InvariantCulture,
+ _localizationManager.GetLocalizedString("LabelRunningTimeValue"),
+ ToUserFriendlyString(time));
+
+ if (result.Status == TaskCompletionStatus.Failed)
+ {
+ var vals = new List<string>();
+
+ if (!string.IsNullOrEmpty(e.Result.ErrorMessage))
+ {
+ vals.Add(e.Result.ErrorMessage);
+ }
+
+ if (!string.IsNullOrEmpty(e.Result.LongErrorMessage))
+ {
+ vals.Add(e.Result.LongErrorMessage);
+ }
+
+ await _activityManager.CreateAsync(new ActivityLog(
+ string.Format(CultureInfo.InvariantCulture, _localizationManager.GetLocalizedString("ScheduledTaskFailedWithName"), task.Name),
+ NotificationType.TaskFailed.ToString(),
+ Guid.Empty)
+ {
+ LogSeverity = LogLevel.Error,
+ Overview = string.Join(Environment.NewLine, vals),
+ ShortOverview = runningTime
+ }).ConfigureAwait(false);
+ }
+ }
+
+ private static string ToUserFriendlyString(TimeSpan span)
+ {
+ const int DaysInYear = 365;
+ const int DaysInMonth = 30;
+
+ // Get each non-zero value from TimeSpan component
+ var values = new List<string>();
+
+ // Number of years
+ int days = span.Days;
+ if (days >= DaysInYear)
+ {
+ int years = days / DaysInYear;
+ values.Add(CreateValueString(years, "year"));
+ days %= DaysInYear;
+ }
+
+ // Number of months
+ if (days >= DaysInMonth)
+ {
+ int months = days / DaysInMonth;
+ values.Add(CreateValueString(months, "month"));
+ days = days % DaysInMonth;
+ }
+
+ // Number of days
+ if (days >= 1)
+ {
+ values.Add(CreateValueString(days, "day"));
+ }
+
+ // Number of hours
+ if (span.Hours >= 1)
+ {
+ values.Add(CreateValueString(span.Hours, "hour"));
+ }
+
+ // Number of minutes
+ if (span.Minutes >= 1)
+ {
+ values.Add(CreateValueString(span.Minutes, "minute"));
+ }
+
+ // Number of seconds (include when 0 if no other components included)
+ if (span.Seconds >= 1 || values.Count == 0)
+ {
+ values.Add(CreateValueString(span.Seconds, "second"));
+ }
+
+ // Combine values into string
+ var builder = new StringBuilder();
+ for (int i = 0; i < values.Count; i++)
+ {
+ if (builder.Length > 0)
+ {
+ builder.Append(i == values.Count - 1 ? " and " : ", ");
+ }
+
+ builder.Append(values[i]);
+ }
+
+ // Return result
+ return builder.ToString();
+ }
+
+ /// <summary>
+ /// Constructs a string description of a time-span value.
+ /// </summary>
+ /// <param name="value">The value of this item.</param>
+ /// <param name="description">The name of this item (singular form).</param>
+ private static string CreateValueString(int value, string description)
+ {
+ return string.Format(
+ CultureInfo.InvariantCulture,
+ "{0:#,##0} {1}",
+ value,
+ value == 1 ? description : string.Format(CultureInfo.InvariantCulture, "{0}s", description));
+ }
+ }
+}
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/System/TaskCompletedNotifier.cs b/Jellyfin.Server.Implementations/Events/Consumers/System/TaskCompletedNotifier.cs
new file mode 100644
index 000000000..80ed56cd8
--- /dev/null
+++ b/Jellyfin.Server.Implementations/Events/Consumers/System/TaskCompletedNotifier.cs
@@ -0,0 +1,31 @@
+using System.Threading;
+using System.Threading.Tasks;
+using MediaBrowser.Controller.Events;
+using MediaBrowser.Controller.Session;
+using MediaBrowser.Model.Tasks;
+
+namespace Jellyfin.Server.Implementations.Events.Consumers.System
+{
+ /// <summary>
+ /// Notifies admin users when a task is completed.
+ /// </summary>
+ public class TaskCompletedNotifier : IEventConsumer<TaskCompletionEventArgs>
+ {
+ private readonly ISessionManager _sessionManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="TaskCompletedNotifier"/> class.
+ /// </summary>
+ /// <param name="sessionManager">The session manager.</param>
+ public TaskCompletedNotifier(ISessionManager sessionManager)
+ {
+ _sessionManager = sessionManager;
+ }
+
+ /// <inheritdoc />
+ public async Task OnEvent(TaskCompletionEventArgs eventArgs)
+ {
+ await _sessionManager.SendMessageToAdminSessions("ScheduledTaskEnded", eventArgs.Result, CancellationToken.None).ConfigureAwait(false);
+ }
+ }
+}
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstallationCancelledNotifier.cs b/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstallationCancelledNotifier.cs
new file mode 100644
index 000000000..1c600683a
--- /dev/null
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstallationCancelledNotifier.cs
@@ -0,0 +1,31 @@
+using System.Threading;
+using System.Threading.Tasks;
+using MediaBrowser.Controller.Events;
+using MediaBrowser.Controller.Events.Updates;
+using MediaBrowser.Controller.Session;
+
+namespace Jellyfin.Server.Implementations.Events.Consumers.Updates
+{
+ /// <summary>
+ /// Notifies admin users when a plugin installation is cancelled.
+ /// </summary>
+ public class PluginInstallationCancelledNotifier : IEventConsumer<PluginInstallationCancelledEventArgs>
+ {
+ private readonly ISessionManager _sessionManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PluginInstallationCancelledNotifier"/> class.
+ /// </summary>
+ /// <param name="sessionManager">The session manager.</param>
+ public PluginInstallationCancelledNotifier(ISessionManager sessionManager)
+ {
+ _sessionManager = sessionManager;
+ }
+
+ /// <inheritdoc />
+ public async Task OnEvent(PluginInstallationCancelledEventArgs eventArgs)
+ {
+ await _sessionManager.SendMessageToAdminSessions("PackageInstallationCancelled", eventArgs.Argument, CancellationToken.None).ConfigureAwait(false);
+ }
+ }
+}
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstallationFailedLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstallationFailedLogger.cs
new file mode 100644
index 000000000..d71c298c5
--- /dev/null
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstallationFailedLogger.cs
@@ -0,0 +1,51 @@
+using System;
+using System.Globalization;
+using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
+using MediaBrowser.Common.Updates;
+using MediaBrowser.Controller.Events;
+using MediaBrowser.Model.Activity;
+using MediaBrowser.Model.Globalization;
+using MediaBrowser.Model.Notifications;
+
+namespace Jellyfin.Server.Implementations.Events.Consumers.Updates
+{
+ /// <summary>
+ /// Creates an entry in the activity log when a package installation fails.
+ /// </summary>
+ public class PluginInstallationFailedLogger : IEventConsumer<InstallationFailedEventArgs>
+ {
+ private readonly ILocalizationManager _localizationManager;
+ private readonly IActivityManager _activityManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PluginInstallationFailedLogger"/> class.
+ /// </summary>
+ /// <param name="localizationManager">The localization manager.</param>
+ /// <param name="activityManager">The activity manager.</param>
+ public PluginInstallationFailedLogger(ILocalizationManager localizationManager, IActivityManager activityManager)
+ {
+ _localizationManager = localizationManager;
+ _activityManager = activityManager;
+ }
+
+ /// <inheritdoc />
+ public async Task OnEvent(InstallationFailedEventArgs eventArgs)
+ {
+ await _activityManager.CreateAsync(new ActivityLog(
+ string.Format(
+ CultureInfo.InvariantCulture,
+ _localizationManager.GetLocalizedString("NameInstallFailed"),
+ eventArgs.InstallationInfo.Name),
+ NotificationType.InstallationFailed.ToString(),
+ Guid.Empty)
+ {
+ ShortOverview = string.Format(
+ CultureInfo.InvariantCulture,
+ _localizationManager.GetLocalizedString("VersionNumber"),
+ eventArgs.InstallationInfo.Version),
+ Overview = eventArgs.Exception.Message
+ }).ConfigureAwait(false);
+ }
+ }
+}
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstallationFailedNotifier.cs b/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstallationFailedNotifier.cs
new file mode 100644
index 000000000..ea0c878d4
--- /dev/null
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstallationFailedNotifier.cs
@@ -0,0 +1,31 @@
+using System.Threading;
+using System.Threading.Tasks;
+using MediaBrowser.Common.Updates;
+using MediaBrowser.Controller.Events;
+using MediaBrowser.Controller.Session;
+
+namespace Jellyfin.Server.Implementations.Events.Consumers.Updates
+{
+ /// <summary>
+ /// Notifies admin users when a plugin installation fails.
+ /// </summary>
+ public class PluginInstallationFailedNotifier : IEventConsumer<InstallationFailedEventArgs>
+ {
+ private readonly ISessionManager _sessionManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PluginInstallationFailedNotifier"/> class.
+ /// </summary>
+ /// <param name="sessionManager">The session manager.</param>
+ public PluginInstallationFailedNotifier(ISessionManager sessionManager)
+ {
+ _sessionManager = sessionManager;
+ }
+
+ /// <inheritdoc />
+ public async Task OnEvent(InstallationFailedEventArgs eventArgs)
+ {
+ await _sessionManager.SendMessageToAdminSessions("PackageInstallationFailed", eventArgs.InstallationInfo, CancellationToken.None).ConfigureAwait(false);
+ }
+ }
+}
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstalledLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstalledLogger.cs
new file mode 100644
index 000000000..8837172db
--- /dev/null
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstalledLogger.cs
@@ -0,0 +1,50 @@
+using System;
+using System.Globalization;
+using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
+using MediaBrowser.Controller.Events;
+using MediaBrowser.Controller.Events.Updates;
+using MediaBrowser.Model.Activity;
+using MediaBrowser.Model.Globalization;
+using MediaBrowser.Model.Notifications;
+
+namespace Jellyfin.Server.Implementations.Events.Consumers.Updates
+{
+ /// <summary>
+ /// Creates an entry in the activity log when a plugin is installed.
+ /// </summary>
+ public class PluginInstalledLogger : IEventConsumer<PluginInstalledEventArgs>
+ {
+ private readonly ILocalizationManager _localizationManager;
+ private readonly IActivityManager _activityManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PluginInstalledLogger"/> class.
+ /// </summary>
+ /// <param name="localizationManager">The localization manager.</param>
+ /// <param name="activityManager">The activity manager.</param>
+ public PluginInstalledLogger(ILocalizationManager localizationManager, IActivityManager activityManager)
+ {
+ _localizationManager = localizationManager;
+ _activityManager = activityManager;
+ }
+
+ /// <inheritdoc />
+ public async Task OnEvent(PluginInstalledEventArgs eventArgs)
+ {
+ await _activityManager.CreateAsync(new ActivityLog(
+ string.Format(
+ CultureInfo.InvariantCulture,
+ _localizationManager.GetLocalizedString("PluginInstalledWithName"),
+ eventArgs.Argument.Name),
+ NotificationType.PluginInstalled.ToString(),
+ Guid.Empty)
+ {
+ ShortOverview = string.Format(
+ CultureInfo.InvariantCulture,
+ _localizationManager.GetLocalizedString("VersionNumber"),
+ eventArgs.Argument.Version)
+ }).ConfigureAwait(false);
+ }
+ }
+}
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstalledNotifier.cs b/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstalledNotifier.cs
new file mode 100644
index 000000000..3dda5a04c
--- /dev/null
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstalledNotifier.cs
@@ -0,0 +1,31 @@
+using System.Threading;
+using System.Threading.Tasks;
+using MediaBrowser.Controller.Events;
+using MediaBrowser.Controller.Events.Updates;
+using MediaBrowser.Controller.Session;
+
+namespace Jellyfin.Server.Implementations.Events.Consumers.Updates
+{
+ /// <summary>
+ /// Notifies admin users when a plugin is installed.
+ /// </summary>
+ public class PluginInstalledNotifier : IEventConsumer<PluginInstalledEventArgs>
+ {
+ private readonly ISessionManager _sessionManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PluginInstalledNotifier"/> class.
+ /// </summary>
+ /// <param name="sessionManager">The session manager.</param>
+ public PluginInstalledNotifier(ISessionManager sessionManager)
+ {
+ _sessionManager = sessionManager;
+ }
+
+ /// <inheritdoc />
+ public async Task OnEvent(PluginInstalledEventArgs eventArgs)
+ {
+ await _sessionManager.SendMessageToAdminSessions("PackageInstallationCompleted", eventArgs.Argument, CancellationToken.None).ConfigureAwait(false);
+ }
+ }
+}
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstallingNotifier.cs b/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstallingNotifier.cs
new file mode 100644
index 000000000..f691d11a7
--- /dev/null
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstallingNotifier.cs
@@ -0,0 +1,31 @@
+using System.Threading;
+using System.Threading.Tasks;
+using MediaBrowser.Controller.Events;
+using MediaBrowser.Controller.Events.Updates;
+using MediaBrowser.Controller.Session;
+
+namespace Jellyfin.Server.Implementations.Events.Consumers.Updates
+{
+ /// <summary>
+ /// Notifies admin users when a plugin is being installed.
+ /// </summary>
+ public class PluginInstallingNotifier : IEventConsumer<PluginInstallingEventArgs>
+ {
+ private readonly ISessionManager _sessionManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PluginInstallingNotifier"/> class.
+ /// </summary>
+ /// <param name="sessionManager">The session manager.</param>
+ public PluginInstallingNotifier(ISessionManager sessionManager)
+ {
+ _sessionManager = sessionManager;
+ }
+
+ /// <inheritdoc />
+ public async Task OnEvent(PluginInstallingEventArgs eventArgs)
+ {
+ await _sessionManager.SendMessageToAdminSessions("PackageInstalling", eventArgs.Argument, CancellationToken.None).ConfigureAwait(false);
+ }
+ }
+}
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginUninstalledLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginUninstalledLogger.cs
new file mode 100644
index 000000000..91a30069e
--- /dev/null
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginUninstalledLogger.cs
@@ -0,0 +1,45 @@
+using System;
+using System.Globalization;
+using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
+using MediaBrowser.Controller.Events;
+using MediaBrowser.Controller.Events.Updates;
+using MediaBrowser.Model.Activity;
+using MediaBrowser.Model.Globalization;
+using MediaBrowser.Model.Notifications;
+
+namespace Jellyfin.Server.Implementations.Events.Consumers.Updates
+{
+ /// <summary>
+ /// Creates an entry in the activity log when a plugin is uninstalled.
+ /// </summary>
+ public class PluginUninstalledLogger : IEventConsumer<PluginUninstalledEventArgs>
+ {
+ private readonly ILocalizationManager _localizationManager;
+ private readonly IActivityManager _activityManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PluginUninstalledLogger"/> class.
+ /// </summary>
+ /// <param name="localizationManager">The localization manager.</param>
+ /// <param name="activityManager">The activity manager.</param>
+ public PluginUninstalledLogger(ILocalizationManager localizationManager, IActivityManager activityManager)
+ {
+ _localizationManager = localizationManager;
+ _activityManager = activityManager;
+ }
+
+ /// <inheritdoc />
+ public async Task OnEvent(PluginUninstalledEventArgs e)
+ {
+ await _activityManager.CreateAsync(new ActivityLog(
+ string.Format(
+ CultureInfo.InvariantCulture,
+ _localizationManager.GetLocalizedString("PluginUninstalledWithName"),
+ e.Argument.Name),
+ NotificationType.PluginUninstalled.ToString(),
+ Guid.Empty))
+ .ConfigureAwait(false);
+ }
+ }
+}
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginUninstalledNotifier.cs b/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginUninstalledNotifier.cs
new file mode 100644
index 000000000..709692f6b
--- /dev/null
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginUninstalledNotifier.cs
@@ -0,0 +1,31 @@
+using System.Threading;
+using System.Threading.Tasks;
+using MediaBrowser.Controller.Events;
+using MediaBrowser.Controller.Events.Updates;
+using MediaBrowser.Controller.Session;
+
+namespace Jellyfin.Server.Implementations.Events.Consumers.Updates
+{
+ /// <summary>
+ /// Notifies admin users when a plugin is uninstalled.
+ /// </summary>
+ public class PluginUninstalledNotifier : IEventConsumer<PluginUninstalledEventArgs>
+ {
+ private readonly ISessionManager _sessionManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PluginUninstalledNotifier"/> class.
+ /// </summary>
+ /// <param name="sessionManager">The session manager.</param>
+ public PluginUninstalledNotifier(ISessionManager sessionManager)
+ {
+ _sessionManager = sessionManager;
+ }
+
+ /// <inheritdoc />
+ public async Task OnEvent(PluginUninstalledEventArgs eventArgs)
+ {
+ await _sessionManager.SendMessageToAdminSessions("PluginUninstalled", eventArgs.Argument, CancellationToken.None).ConfigureAwait(false);
+ }
+ }
+}
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginUpdatedLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginUpdatedLogger.cs
new file mode 100644
index 000000000..9ce16f774
--- /dev/null
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginUpdatedLogger.cs
@@ -0,0 +1,51 @@
+using System;
+using System.Globalization;
+using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
+using MediaBrowser.Controller.Events;
+using MediaBrowser.Controller.Events.Updates;
+using MediaBrowser.Model.Activity;
+using MediaBrowser.Model.Globalization;
+using MediaBrowser.Model.Notifications;
+
+namespace Jellyfin.Server.Implementations.Events.Consumers.Updates
+{
+ /// <summary>
+ /// Creates an entry in the activity log when a plugin is updated.
+ /// </summary>
+ public class PluginUpdatedLogger : IEventConsumer<PluginUpdatedEventArgs>
+ {
+ private readonly ILocalizationManager _localizationManager;
+ private readonly IActivityManager _activityManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PluginUpdatedLogger"/> class.
+ /// </summary>
+ /// <param name="localizationManager">The localization manager.</param>
+ /// <param name="activityManager">The activity manager.</param>
+ public PluginUpdatedLogger(ILocalizationManager localizationManager, IActivityManager activityManager)
+ {
+ _localizationManager = localizationManager;
+ _activityManager = activityManager;
+ }
+
+ /// <inheritdoc />
+ public async Task OnEvent(PluginUpdatedEventArgs eventArgs)
+ {
+ await _activityManager.CreateAsync(new ActivityLog(
+ string.Format(
+ CultureInfo.InvariantCulture,
+ _localizationManager.GetLocalizedString("PluginUpdatedWithName"),
+ eventArgs.Argument.Name),
+ NotificationType.PluginUpdateInstalled.ToString(),
+ Guid.Empty)
+ {
+ ShortOverview = string.Format(
+ CultureInfo.InvariantCulture,
+ _localizationManager.GetLocalizedString("VersionNumber"),
+ eventArgs.Argument.Version),
+ Overview = eventArgs.Argument.Changelog
+ }).ConfigureAwait(false);
+ }
+ }
+}
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Users/UserCreatedLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/Users/UserCreatedLogger.cs
new file mode 100644
index 000000000..dc855cc36
--- /dev/null
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Users/UserCreatedLogger.cs
@@ -0,0 +1,43 @@
+using System.Globalization;
+using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
+using Jellyfin.Data.Events.Users;
+using MediaBrowser.Controller.Events;
+using MediaBrowser.Model.Activity;
+using MediaBrowser.Model.Globalization;
+
+namespace Jellyfin.Server.Implementations.Events.Consumers.Users
+{
+ /// <summary>
+ /// Creates an entry in the activity log when a user is created.
+ /// </summary>
+ public class UserCreatedLogger : IEventConsumer<UserCreatedEventArgs>
+ {
+ private readonly ILocalizationManager _localizationManager;
+ private readonly IActivityManager _activityManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="UserCreatedLogger"/> class.
+ /// </summary>
+ /// <param name="localizationManager">The localization manager.</param>
+ /// <param name="activityManager">The activity manager.</param>
+ public UserCreatedLogger(ILocalizationManager localizationManager, IActivityManager activityManager)
+ {
+ _localizationManager = localizationManager;
+ _activityManager = activityManager;
+ }
+
+ /// <inheritdoc />
+ public async Task OnEvent(UserCreatedEventArgs eventArgs)
+ {
+ await _activityManager.CreateAsync(new ActivityLog(
+ string.Format(
+ CultureInfo.InvariantCulture,
+ _localizationManager.GetLocalizedString("UserCreatedWithName"),
+ eventArgs.Argument.Username),
+ "UserCreated",
+ eventArgs.Argument.Id))
+ .ConfigureAwait(false);
+ }
+ }
+}
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Users/UserDeletedLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/Users/UserDeletedLogger.cs
new file mode 100644
index 000000000..c68a62c81
--- /dev/null
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Users/UserDeletedLogger.cs
@@ -0,0 +1,44 @@
+using System;
+using System.Globalization;
+using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
+using Jellyfin.Data.Events.Users;
+using MediaBrowser.Controller.Events;
+using MediaBrowser.Model.Activity;
+using MediaBrowser.Model.Globalization;
+
+namespace Jellyfin.Server.Implementations.Events.Consumers.Users
+{
+ /// <summary>
+ /// Adds an entry to the activity log when a user is deleted.
+ /// </summary>
+ public class UserDeletedLogger : IEventConsumer<UserDeletedEventArgs>
+ {
+ private readonly ILocalizationManager _localizationManager;
+ private readonly IActivityManager _activityManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="UserDeletedLogger"/> class.
+ /// </summary>
+ /// <param name="localizationManager">The localization manager.</param>
+ /// <param name="activityManager">The activity manager.</param>
+ public UserDeletedLogger(ILocalizationManager localizationManager, IActivityManager activityManager)
+ {
+ _localizationManager = localizationManager;
+ _activityManager = activityManager;
+ }
+
+ /// <inheritdoc />
+ public async Task OnEvent(UserDeletedEventArgs eventArgs)
+ {
+ await _activityManager.CreateAsync(new ActivityLog(
+ string.Format(
+ CultureInfo.InvariantCulture,
+ _localizationManager.GetLocalizedString("UserDeletedWithName"),
+ eventArgs.Argument.Username),
+ "UserDeleted",
+ Guid.Empty))
+ .ConfigureAwait(false);
+ }
+ }
+}
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Users/UserDeletedNotifier.cs b/Jellyfin.Server.Implementations/Events/Consumers/Users/UserDeletedNotifier.cs
new file mode 100644
index 000000000..10367a939
--- /dev/null
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Users/UserDeletedNotifier.cs
@@ -0,0 +1,38 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Threading;
+using System.Threading.Tasks;
+using Jellyfin.Data.Events.Users;
+using MediaBrowser.Controller.Events;
+using MediaBrowser.Controller.Session;
+
+namespace Jellyfin.Server.Implementations.Events.Consumers.Users
+{
+ /// <summary>
+ /// Notifies the user's sessions when a user is deleted.
+ /// </summary>
+ public class UserDeletedNotifier : IEventConsumer<UserDeletedEventArgs>
+ {
+ private readonly ISessionManager _sessionManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="UserDeletedNotifier"/> class.
+ /// </summary>
+ /// <param name="sessionManager">The session manager.</param>
+ public UserDeletedNotifier(ISessionManager sessionManager)
+ {
+ _sessionManager = sessionManager;
+ }
+
+ /// <inheritdoc />
+ public async Task OnEvent(UserDeletedEventArgs eventArgs)
+ {
+ await _sessionManager.SendMessageToUserSessions(
+ new List<Guid> { eventArgs.Argument.Id },
+ "UserDeleted",
+ eventArgs.Argument.Id.ToString("N", CultureInfo.InvariantCulture),
+ CancellationToken.None).ConfigureAwait(false);
+ }
+ }
+}
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Users/UserLockedOutLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/Users/UserLockedOutLogger.cs
new file mode 100644
index 000000000..a31f222ee
--- /dev/null
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Users/UserLockedOutLogger.cs
@@ -0,0 +1,47 @@
+using System.Globalization;
+using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
+using Jellyfin.Data.Events.Users;
+using MediaBrowser.Controller.Events;
+using MediaBrowser.Model.Activity;
+using MediaBrowser.Model.Globalization;
+using MediaBrowser.Model.Notifications;
+using Microsoft.Extensions.Logging;
+
+namespace Jellyfin.Server.Implementations.Events.Consumers.Users
+{
+ /// <summary>
+ /// Creates an entry in the activity log when a user is locked out.
+ /// </summary>
+ public class UserLockedOutLogger : IEventConsumer<UserLockedOutEventArgs>
+ {
+ private readonly ILocalizationManager _localizationManager;
+ private readonly IActivityManager _activityManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="UserLockedOutLogger"/> class.
+ /// </summary>
+ /// <param name="localizationManager">The localization manager.</param>
+ /// <param name="activityManager">The activity manager.</param>
+ public UserLockedOutLogger(ILocalizationManager localizationManager, IActivityManager activityManager)
+ {
+ _localizationManager = localizationManager;
+ _activityManager = activityManager;
+ }
+
+ /// <inheritdoc />
+ public async Task OnEvent(UserLockedOutEventArgs eventArgs)
+ {
+ await _activityManager.CreateAsync(new ActivityLog(
+ string.Format(
+ CultureInfo.InvariantCulture,
+ _localizationManager.GetLocalizedString("UserLockedOutWithName"),
+ eventArgs.Argument.Username),
+ NotificationType.UserLockedOut.ToString(),
+ eventArgs.Argument.Id)
+ {
+ LogSeverity = LogLevel.Error
+ }).ConfigureAwait(false);
+ }
+ }
+}
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Users/UserPasswordChangedLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/Users/UserPasswordChangedLogger.cs
new file mode 100644
index 000000000..dc8ecbf48
--- /dev/null
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Users/UserPasswordChangedLogger.cs
@@ -0,0 +1,43 @@
+using System.Globalization;
+using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
+using Jellyfin.Data.Events.Users;
+using MediaBrowser.Controller.Events;
+using MediaBrowser.Model.Activity;
+using MediaBrowser.Model.Globalization;
+
+namespace Jellyfin.Server.Implementations.Events.Consumers.Users
+{
+ /// <summary>
+ /// Creates an entry in the activity log when a user's password is changed.
+ /// </summary>
+ public class UserPasswordChangedLogger : IEventConsumer<UserPasswordChangedEventArgs>
+ {
+ private readonly ILocalizationManager _localizationManager;
+ private readonly IActivityManager _activityManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="UserPasswordChangedLogger"/> class.
+ /// </summary>
+ /// <param name="localizationManager">The localization manager.</param>
+ /// <param name="activityManager">The activity manager.</param>
+ public UserPasswordChangedLogger(ILocalizationManager localizationManager, IActivityManager activityManager)
+ {
+ _localizationManager = localizationManager;
+ _activityManager = activityManager;
+ }
+
+ /// <inheritdoc />
+ public async Task OnEvent(UserPasswordChangedEventArgs eventArgs)
+ {
+ await _activityManager.CreateAsync(new ActivityLog(
+ string.Format(
+ CultureInfo.InvariantCulture,
+ _localizationManager.GetLocalizedString("UserPasswordChangedWithName"),
+ eventArgs.Argument.Username),
+ "UserPasswordChanged",
+ eventArgs.Argument.Id))
+ .ConfigureAwait(false);
+ }
+ }
+}
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Users/UserUpdatedNotifier.cs b/Jellyfin.Server.Implementations/Events/Consumers/Users/UserUpdatedNotifier.cs
new file mode 100644
index 000000000..6081dd044
--- /dev/null
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Users/UserUpdatedNotifier.cs
@@ -0,0 +1,41 @@
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using Jellyfin.Data.Events.Users;
+using MediaBrowser.Controller.Events;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Session;
+
+namespace Jellyfin.Server.Implementations.Events.Consumers.Users
+{
+ /// <summary>
+ /// Notifies a user when their account has been updated.
+ /// </summary>
+ public class UserUpdatedNotifier : IEventConsumer<UserUpdatedEventArgs>
+ {
+ private readonly IUserManager _userManager;
+ private readonly ISessionManager _sessionManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="UserUpdatedNotifier"/> class.
+ /// </summary>
+ /// <param name="userManager">The user manager.</param>
+ /// <param name="sessionManager">The session manager.</param>
+ public UserUpdatedNotifier(IUserManager userManager, ISessionManager sessionManager)
+ {
+ _userManager = userManager;
+ _sessionManager = sessionManager;
+ }
+
+ /// <inheritdoc />
+ public async Task OnEvent(UserUpdatedEventArgs e)
+ {
+ await _sessionManager.SendMessageToUserSessions(
+ new List<Guid> { e.Argument.Id },
+ "UserUpdated",
+ _userManager.GetUserDto(e.Argument),
+ CancellationToken.None).ConfigureAwait(false);
+ }
+ }
+}
diff --git a/Jellyfin.Server.Implementations/Events/EventManager.cs b/Jellyfin.Server.Implementations/Events/EventManager.cs
new file mode 100644
index 000000000..707002442
--- /dev/null
+++ b/Jellyfin.Server.Implementations/Events/EventManager.cs
@@ -0,0 +1,60 @@
+using System;
+using System.Threading.Tasks;
+using MediaBrowser.Controller;
+using MediaBrowser.Controller.Events;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+
+namespace Jellyfin.Server.Implementations.Events
+{
+ /// <summary>
+ /// Handles the firing of events.
+ /// </summary>
+ public class EventManager : IEventManager
+ {
+ private readonly ILogger<EventManager> _logger;
+ private readonly IServerApplicationHost _appHost;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="EventManager"/> class.
+ /// </summary>
+ /// <param name="logger">The logger.</param>
+ /// <param name="appHost">The application host.</param>
+ public EventManager(ILogger<EventManager> logger, IServerApplicationHost appHost)
+ {
+ _logger = logger;
+ _appHost = appHost;
+ }
+
+ /// <inheritdoc />
+ public void Publish<T>(T eventArgs)
+ where T : EventArgs
+ {
+ Task.WaitAll(PublishInternal(eventArgs));
+ }
+
+ /// <inheritdoc />
+ public async Task PublishAsync<T>(T eventArgs)
+ where T : EventArgs
+ {
+ await PublishInternal(eventArgs).ConfigureAwait(false);
+ }
+
+ private async Task PublishInternal<T>(T eventArgs)
+ where T : EventArgs
+ {
+ using var scope = _appHost.ServiceProvider.CreateScope();
+ foreach (var service in scope.ServiceProvider.GetServices<IEventConsumer<T>>())
+ {
+ try
+ {
+ await service.OnEvent(eventArgs).ConfigureAwait(false);
+ }
+ catch (Exception e)
+ {
+ _logger.LogError(e, "Uncaught exception in EventConsumer {type}: ", service.GetType());
+ }
+ }
+ }
+ }
+}
diff --git a/Jellyfin.Server.Implementations/Events/EventingServiceCollectionExtensions.cs b/Jellyfin.Server.Implementations/Events/EventingServiceCollectionExtensions.cs
new file mode 100644
index 000000000..5d558189b
--- /dev/null
+++ b/Jellyfin.Server.Implementations/Events/EventingServiceCollectionExtensions.cs
@@ -0,0 +1,72 @@
+using Jellyfin.Data.Events;
+using Jellyfin.Data.Events.System;
+using Jellyfin.Data.Events.Users;
+using Jellyfin.Server.Implementations.Events.Consumers.Library;
+using Jellyfin.Server.Implementations.Events.Consumers.Security;
+using Jellyfin.Server.Implementations.Events.Consumers.Session;
+using Jellyfin.Server.Implementations.Events.Consumers.System;
+using Jellyfin.Server.Implementations.Events.Consumers.Updates;
+using Jellyfin.Server.Implementations.Events.Consumers.Users;
+using MediaBrowser.Common.Updates;
+using MediaBrowser.Controller.Authentication;
+using MediaBrowser.Controller.Events;
+using MediaBrowser.Controller.Events.Session;
+using MediaBrowser.Controller.Events.Updates;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Session;
+using MediaBrowser.Controller.Subtitles;
+using MediaBrowser.Model.Tasks;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Jellyfin.Server.Implementations.Events
+{
+ /// <summary>
+ /// A class containing extensions to <see cref="IServiceCollection"/> for eventing.
+ /// </summary>
+ public static class EventingServiceCollectionExtensions
+ {
+ /// <summary>
+ /// Adds the event services to the service collection.
+ /// </summary>
+ /// <param name="collection">The service collection.</param>
+ public static void AddEventServices(this IServiceCollection collection)
+ {
+ // Library consumers
+ collection.AddScoped<IEventConsumer<SubtitleDownloadFailureEventArgs>, SubtitleDownloadFailureLogger>();
+
+ // Security consumers
+ collection.AddScoped<IEventConsumer<GenericEventArgs<AuthenticationRequest>>, AuthenticationFailedLogger>();
+ collection.AddScoped<IEventConsumer<GenericEventArgs<AuthenticationResult>>, AuthenticationSucceededLogger>();
+
+ // Session consumers
+ collection.AddScoped<IEventConsumer<PlaybackStartEventArgs>, PlaybackStartLogger>();
+ collection.AddScoped<IEventConsumer<PlaybackStopEventArgs>, PlaybackStopLogger>();
+ collection.AddScoped<IEventConsumer<SessionEndedEventArgs>, SessionEndedLogger>();
+ collection.AddScoped<IEventConsumer<SessionStartedEventArgs>, SessionStartedLogger>();
+
+ // System consumers
+ collection.AddScoped<IEventConsumer<PendingRestartEventArgs>, PendingRestartNotifier>();
+ collection.AddScoped<IEventConsumer<TaskCompletionEventArgs>, TaskCompletedLogger>();
+ collection.AddScoped<IEventConsumer<TaskCompletionEventArgs>, TaskCompletedNotifier>();
+
+ // Update consumers
+ collection.AddScoped<IEventConsumer<PluginInstallationCancelledEventArgs>, PluginInstallationCancelledNotifier>();
+ collection.AddScoped<IEventConsumer<InstallationFailedEventArgs>, PluginInstallationFailedLogger>();
+ collection.AddScoped<IEventConsumer<InstallationFailedEventArgs>, PluginInstallationFailedNotifier>();
+ collection.AddScoped<IEventConsumer<PluginInstalledEventArgs>, PluginInstalledLogger>();
+ collection.AddScoped<IEventConsumer<PluginInstalledEventArgs>, PluginInstalledNotifier>();
+ collection.AddScoped<IEventConsumer<PluginInstallingEventArgs>, PluginInstallingNotifier>();
+ collection.AddScoped<IEventConsumer<PluginUninstalledEventArgs>, PluginUninstalledLogger>();
+ collection.AddScoped<IEventConsumer<PluginUninstalledEventArgs>, PluginUninstalledNotifier>();
+ collection.AddScoped<IEventConsumer<PluginUpdatedEventArgs>, PluginUpdatedLogger>();
+
+ // User consumers
+ collection.AddScoped<IEventConsumer<UserCreatedEventArgs>, UserCreatedLogger>();
+ collection.AddScoped<IEventConsumer<UserDeletedEventArgs>, UserDeletedLogger>();
+ collection.AddScoped<IEventConsumer<UserDeletedEventArgs>, UserDeletedNotifier>();
+ collection.AddScoped<IEventConsumer<UserLockedOutEventArgs>, UserLockedOutLogger>();
+ collection.AddScoped<IEventConsumer<UserPasswordChangedEventArgs>, UserPasswordChangedLogger>();
+ collection.AddScoped<IEventConsumer<UserUpdatedEventArgs>, UserUpdatedNotifier>();
+ }
+ }
+}
diff --git a/Jellyfin.Server.Implementations/Users/DeviceAccessEntryPoint.cs b/Jellyfin.Server.Implementations/Users/DeviceAccessEntryPoint.cs
index 140853e52..1fb89c4a6 100644
--- a/Jellyfin.Server.Implementations/Users/DeviceAccessEntryPoint.cs
+++ b/Jellyfin.Server.Implementations/Users/DeviceAccessEntryPoint.cs
@@ -4,12 +4,12 @@
using System.Threading.Tasks;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
+using Jellyfin.Data.Events;
using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Plugins;
using MediaBrowser.Controller.Security;
using MediaBrowser.Controller.Session;
-using MediaBrowser.Model.Events;
namespace Jellyfin.Server.Implementations.Users
{
diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs
index 11402ee05..3e8edeb44 100644
--- a/Jellyfin.Server.Implementations/Users/UserManager.cs
+++ b/Jellyfin.Server.Implementations/Users/UserManager.cs
@@ -10,18 +10,20 @@ using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
+using Jellyfin.Data.Events;
+using Jellyfin.Data.Events.Users;
using MediaBrowser.Common;
using MediaBrowser.Common.Cryptography;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Authentication;
using MediaBrowser.Controller.Drawing;
+using MediaBrowser.Controller.Events;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Cryptography;
using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Events;
using MediaBrowser.Model.Users;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
@@ -34,6 +36,7 @@ namespace Jellyfin.Server.Implementations.Users
public class UserManager : IUserManager
{
private readonly JellyfinDbProvider _dbProvider;
+ private readonly IEventManager _eventManager;
private readonly ICryptoProvider _cryptoProvider;
private readonly INetworkManager _networkManager;
private readonly IApplicationHost _appHost;
@@ -49,6 +52,7 @@ namespace Jellyfin.Server.Implementations.Users
/// Initializes a new instance of the <see cref="UserManager"/> class.
/// </summary>
/// <param name="dbProvider">The database provider.</param>
+ /// <param name="eventManager">The event manager.</param>
/// <param name="cryptoProvider">The cryptography provider.</param>
/// <param name="networkManager">The network manager.</param>
/// <param name="appHost">The application host.</param>
@@ -56,6 +60,7 @@ namespace Jellyfin.Server.Implementations.Users
/// <param name="logger">The logger.</param>
public UserManager(
JellyfinDbProvider dbProvider,
+ IEventManager eventManager,
ICryptoProvider cryptoProvider,
INetworkManager networkManager,
IApplicationHost appHost,
@@ -63,6 +68,7 @@ namespace Jellyfin.Server.Implementations.Users
ILogger<UserManager> logger)
{
_dbProvider = dbProvider;
+ _eventManager = eventManager;
_cryptoProvider = cryptoProvider;
_networkManager = networkManager;
_appHost = appHost;
@@ -78,21 +84,9 @@ namespace Jellyfin.Server.Implementations.Users
}
/// <inheritdoc/>
- public event EventHandler<GenericEventArgs<User>>? OnUserPasswordChanged;
-
- /// <inheritdoc/>
public event EventHandler<GenericEventArgs<User>>? OnUserUpdated;
/// <inheritdoc/>
- public event EventHandler<GenericEventArgs<User>>? OnUserCreated;
-
- /// <inheritdoc/>
- public event EventHandler<GenericEventArgs<User>>? OnUserDeleted;
-
- /// <inheritdoc/>
- public event EventHandler<GenericEventArgs<User>>? OnUserLockedOut;
-
- /// <inheritdoc/>
public IEnumerable<User> Users
{
get
@@ -234,7 +228,7 @@ namespace Jellyfin.Server.Implementations.Users
dbContext.Users.Add(newUser);
await dbContext.SaveChangesAsync().ConfigureAwait(false);
- OnUserCreated?.Invoke(this, new GenericEventArgs<User>(newUser));
+ await _eventManager.PublishAsync(new UserCreatedEventArgs(newUser)).ConfigureAwait(false);
return newUser;
}
@@ -293,7 +287,8 @@ namespace Jellyfin.Server.Implementations.Users
dbContext.RemoveRange(user.AccessSchedules);
dbContext.Users.Remove(user);
dbContext.SaveChanges();
- OnUserDeleted?.Invoke(this, new GenericEventArgs<User>(user));
+
+ _eventManager.Publish(new UserDeletedEventArgs(user));
}
/// <inheritdoc/>
@@ -319,7 +314,7 @@ namespace Jellyfin.Server.Implementations.Users
await GetAuthenticationProvider(user).ChangePassword(user, newPassword).ConfigureAwait(false);
await UpdateUserAsync(user).ConfigureAwait(false);
- OnUserPasswordChanged?.Invoke(this, new GenericEventArgs<User>(user));
+ await _eventManager.PublishAsync(new UserPasswordChangedEventArgs(user)).ConfigureAwait(false);
}
/// <inheritdoc/>
@@ -338,7 +333,7 @@ namespace Jellyfin.Server.Implementations.Users
user.EasyPassword = newPasswordSha1;
UpdateUser(user);
- OnUserPasswordChanged?.Invoke(this, new GenericEventArgs<User>(user));
+ _eventManager.Publish(new UserPasswordChangedEventArgs(user));
}
/// <inheritdoc/>
@@ -901,7 +896,7 @@ namespace Jellyfin.Server.Implementations.Users
if (maxInvalidLogins.HasValue && user.InvalidLoginAttemptCount >= maxInvalidLogins)
{
user.SetPermission(PermissionKind.IsDisabled, true);
- OnUserLockedOut?.Invoke(this, new GenericEventArgs<User>(user));
+ await _eventManager.PublishAsync(new UserLockedOutEventArgs(user)).ConfigureAwait(false);
_logger.LogWarning(
"Disabling user {Username} due to {Attempts} unsuccessful login attempts.",
user.Username,
diff --git a/Jellyfin.Server/CoreAppHost.cs b/Jellyfin.Server/CoreAppHost.cs
index 29a59e1c8..755844dd9 100644
--- a/Jellyfin.Server/CoreAppHost.cs
+++ b/Jellyfin.Server/CoreAppHost.cs
@@ -1,20 +1,20 @@
using System;
using System.Collections.Generic;
-using System.IO;
using System.Reflection;
using Emby.Drawing;
using Emby.Server.Implementations;
using Jellyfin.Drawing.Skia;
using Jellyfin.Server.Implementations;
using Jellyfin.Server.Implementations.Activity;
+using Jellyfin.Server.Implementations.Events;
using Jellyfin.Server.Implementations.Users;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Drawing;
+using MediaBrowser.Controller.Events;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Activity;
using MediaBrowser.Model.IO;
-using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
@@ -33,30 +33,33 @@ namespace Jellyfin.Server
/// <param name="options">The <see cref="StartupOptions" /> to be used by the <see cref="CoreAppHost" />.</param>
/// <param name="fileSystem">The <see cref="IFileSystem" /> to be used by the <see cref="CoreAppHost" />.</param>
/// <param name="networkManager">The <see cref="INetworkManager" /> to be used by the <see cref="CoreAppHost" />.</param>
+ /// <param name="collection">The <see cref="IServiceCollection"/> to be used by the <see cref="CoreAppHost"/>.</param>
public CoreAppHost(
IServerApplicationPaths applicationPaths,
ILoggerFactory loggerFactory,
IStartupOptions options,
IFileSystem fileSystem,
- INetworkManager networkManager)
+ INetworkManager networkManager,
+ IServiceCollection collection)
: base(
applicationPaths,
loggerFactory,
options,
fileSystem,
- networkManager)
+ networkManager,
+ collection)
{
}
/// <inheritdoc/>
- protected override void RegisterServices(IServiceCollection serviceCollection)
+ protected override void RegisterServices()
{
// Register an image encoder
bool useSkiaEncoder = SkiaEncoder.IsNativeLibAvailable();
Type imageEncoderType = useSkiaEncoder
? typeof(SkiaEncoder)
: typeof(NullImageEncoder);
- serviceCollection.AddSingleton(typeof(IImageEncoder), imageEncoderType);
+ ServiceCollection.AddSingleton(typeof(IImageEncoder), imageEncoderType);
// Log a warning if the Skia encoder could not be used
if (!useSkiaEncoder)
@@ -71,13 +74,15 @@ namespace Jellyfin.Server
// .UseSqlite($"Filename={Path.Combine(ApplicationPaths.DataPath, "jellyfin.db")}"),
// ServiceLifetime.Transient);
- serviceCollection.AddSingleton<JellyfinDbProvider>();
+ ServiceCollection.AddEventServices();
+ ServiceCollection.AddSingleton<IEventManager, EventManager>();
+ ServiceCollection.AddSingleton<JellyfinDbProvider>();
- serviceCollection.AddSingleton<IActivityManager, ActivityManager>();
- serviceCollection.AddSingleton<IUserManager, UserManager>();
- serviceCollection.AddSingleton<IDisplayPreferencesManager, DisplayPreferencesManager>();
+ ServiceCollection.AddSingleton<IActivityManager, ActivityManager>();
+ ServiceCollection.AddSingleton<IUserManager, UserManager>();
+ ServiceCollection.AddSingleton<IDisplayPreferencesManager, DisplayPreferencesManager>();
- base.RegisterServices(serviceCollection);
+ base.RegisterServices();
}
/// <inheritdoc />
diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs
index f6ac4e2a3..14cc5f4c2 100644
--- a/Jellyfin.Server/Program.cs
+++ b/Jellyfin.Server/Program.cs
@@ -154,13 +154,15 @@ namespace Jellyfin.Server
ApplicationHost.LogEnvironmentInfo(_logger, appPaths);
PerformStaticInitialization();
+ var serviceCollection = new ServiceCollection();
var appHost = new CoreAppHost(
appPaths,
_loggerFactory,
options,
new ManagedFileSystem(_loggerFactory.CreateLogger<ManagedFileSystem>(), appPaths),
- new NetworkManager(_loggerFactory.CreateLogger<NetworkManager>()));
+ new NetworkManager(_loggerFactory.CreateLogger<NetworkManager>()),
+ serviceCollection);
try
{
@@ -178,8 +180,7 @@ namespace Jellyfin.Server
}
}
- ServiceCollection serviceCollection = new ServiceCollection();
- appHost.Init(serviceCollection);
+ appHost.Init();
var webHost = new WebHostBuilder().ConfigureWebHostBuilder(appHost, serviceCollection, options, startupConfig, appPaths).Build();
diff --git a/MediaBrowser.Common/IApplicationHost.cs b/MediaBrowser.Common/IApplicationHost.cs
index e8d9282e4..7a76be722 100644
--- a/MediaBrowser.Common/IApplicationHost.cs
+++ b/MediaBrowser.Common/IApplicationHost.cs
@@ -116,8 +116,7 @@ namespace MediaBrowser.Common
/// <summary>
/// Initializes this instance.
/// </summary>
- /// <param name="serviceCollection">The service collection.</param>
- void Init(IServiceCollection serviceCollection);
+ void Init();
/// <summary>
/// Creates the instance.
diff --git a/MediaBrowser.Common/Plugins/BasePlugin.cs b/MediaBrowser.Common/Plugins/BasePlugin.cs
index f10a1918f..4b2918d08 100644
--- a/MediaBrowser.Common/Plugins/BasePlugin.cs
+++ b/MediaBrowser.Common/Plugins/BasePlugin.cs
@@ -6,6 +6,7 @@ using System.Reflection;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Model.Plugins;
using MediaBrowser.Model.Serialization;
+using Microsoft.Extensions.DependencyInjection;
namespace MediaBrowser.Common.Plugins
{
@@ -82,6 +83,16 @@ namespace MediaBrowser.Common.Plugins
}
/// <inheritdoc />
+ public virtual void RegisterServices(IServiceCollection serviceCollection)
+ {
+ }
+
+ /// <inheritdoc />
+ public virtual void UnregisterServices(IServiceCollection serviceCollection)
+ {
+ }
+
+ /// <inheritdoc />
public void SetAttributes(string assemblyFilePath, string dataFolderPath, Version assemblyVersion)
{
AssemblyFilePath = assemblyFilePath;
diff --git a/MediaBrowser.Common/Plugins/IPlugin.cs b/MediaBrowser.Common/Plugins/IPlugin.cs
index 7bd37d210..1844eb124 100644
--- a/MediaBrowser.Common/Plugins/IPlugin.cs
+++ b/MediaBrowser.Common/Plugins/IPlugin.cs
@@ -2,6 +2,7 @@
using System;
using MediaBrowser.Model.Plugins;
+using Microsoft.Extensions.DependencyInjection;
namespace MediaBrowser.Common.Plugins
{
@@ -61,6 +62,18 @@ namespace MediaBrowser.Common.Plugins
/// Called when just before the plugin is uninstalled from the server.
/// </summary>
void OnUninstalling();
+
+ /// <summary>
+ /// Registers the plugin's services to the service collection.
+ /// </summary>
+ /// <param name="serviceCollection">The service collection.</param>
+ void RegisterServices(IServiceCollection serviceCollection);
+
+ /// <summary>
+ /// Unregisters the plugin's services from the service collection.
+ /// </summary>
+ /// <param name="serviceCollection">The service collection.</param>
+ void UnregisterServices(IServiceCollection serviceCollection);
}
public interface IHasPluginConfiguration
diff --git a/MediaBrowser.Common/Updates/InstallationEventArgs.cs b/MediaBrowser.Common/Updates/InstallationEventArgs.cs
index 11eb2ad34..61178f631 100644
--- a/MediaBrowser.Common/Updates/InstallationEventArgs.cs
+++ b/MediaBrowser.Common/Updates/InstallationEventArgs.cs
@@ -1,10 +1,11 @@
#pragma warning disable CS1591
+using System;
using MediaBrowser.Model.Updates;
namespace MediaBrowser.Common.Updates
{
- public class InstallationEventArgs
+ public class InstallationEventArgs : EventArgs
{
public InstallationInfo InstallationInfo { get; set; }
diff --git a/MediaBrowser.Controller/Devices/IDeviceManager.cs b/MediaBrowser.Controller/Devices/IDeviceManager.cs
index 55e022f15..8f0872dba 100644
--- a/MediaBrowser.Controller/Devices/IDeviceManager.cs
+++ b/MediaBrowser.Controller/Devices/IDeviceManager.cs
@@ -2,8 +2,8 @@
using System;
using Jellyfin.Data.Entities;
+using Jellyfin.Data.Events;
using MediaBrowser.Model.Devices;
-using MediaBrowser.Model.Events;
using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Session;
diff --git a/MediaBrowser.Controller/Events/IEventConsumer.cs b/MediaBrowser.Controller/Events/IEventConsumer.cs
new file mode 100644
index 000000000..3cefe2f9c
--- /dev/null
+++ b/MediaBrowser.Controller/Events/IEventConsumer.cs
@@ -0,0 +1,11 @@
+using System;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Controller.Events
+{
+ public interface IEventConsumer<in T>
+ where T : EventArgs
+ {
+ Task OnEvent(T eventArgs);
+ }
+}
diff --git a/MediaBrowser.Controller/Events/IEventManager.cs b/MediaBrowser.Controller/Events/IEventManager.cs
new file mode 100644
index 000000000..a1f40b3a6
--- /dev/null
+++ b/MediaBrowser.Controller/Events/IEventManager.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Controller.Events
+{
+ /// <summary>
+ /// An interface that handles eventing.
+ /// </summary>
+ public interface IEventManager
+ {
+ /// <summary>
+ /// Publishes an event.
+ /// </summary>
+ /// <param name="eventArgs">the event arguments.</param>
+ /// <typeparam name="T">The type of event.</typeparam>
+ void Publish<T>(T eventArgs)
+ where T : EventArgs;
+
+ /// <summary>
+ /// Publishes an event asynchronously.
+ /// </summary>
+ /// <param name="eventArgs">The event arguments.</param>
+ /// <typeparam name="T">The type of event.</typeparam>
+ /// <returns>A task representing the publishing of the event.</returns>
+ Task PublishAsync<T>(T eventArgs)
+ where T : EventArgs;
+ }
+}
diff --git a/MediaBrowser.Controller/Events/Session/SessionEndedEventArgs.cs b/MediaBrowser.Controller/Events/Session/SessionEndedEventArgs.cs
new file mode 100644
index 000000000..46d7e5a17
--- /dev/null
+++ b/MediaBrowser.Controller/Events/Session/SessionEndedEventArgs.cs
@@ -0,0 +1,19 @@
+using Jellyfin.Data.Events;
+using MediaBrowser.Controller.Session;
+
+namespace MediaBrowser.Controller.Events.Session
+{
+ /// <summary>
+ /// An event that fires when a session is ended.
+ /// </summary>
+ public class SessionEndedEventArgs : GenericEventArgs<SessionInfo>
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="SessionEndedEventArgs"/> class.
+ /// </summary>
+ /// <param name="arg">The session info.</param>
+ public SessionEndedEventArgs(SessionInfo arg) : base(arg)
+ {
+ }
+ }
+}
diff --git a/MediaBrowser.Controller/Events/Session/SessionStartedEventArgs.cs b/MediaBrowser.Controller/Events/Session/SessionStartedEventArgs.cs
new file mode 100644
index 000000000..aab19cc46
--- /dev/null
+++ b/MediaBrowser.Controller/Events/Session/SessionStartedEventArgs.cs
@@ -0,0 +1,19 @@
+using Jellyfin.Data.Events;
+using MediaBrowser.Controller.Session;
+
+namespace MediaBrowser.Controller.Events.Session
+{
+ /// <summary>
+ /// An event that fires when a session is started.
+ /// </summary>
+ public class SessionStartedEventArgs : GenericEventArgs<SessionInfo>
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="SessionStartedEventArgs"/> class.
+ /// </summary>
+ /// <param name="arg">The session info.</param>
+ public SessionStartedEventArgs(SessionInfo arg) : base(arg)
+ {
+ }
+ }
+}
diff --git a/MediaBrowser.Controller/Events/Updates/PluginInstallationCancelledEventArgs.cs b/MediaBrowser.Controller/Events/Updates/PluginInstallationCancelledEventArgs.cs
new file mode 100644
index 000000000..b06046c05
--- /dev/null
+++ b/MediaBrowser.Controller/Events/Updates/PluginInstallationCancelledEventArgs.cs
@@ -0,0 +1,19 @@
+using Jellyfin.Data.Events;
+using MediaBrowser.Model.Updates;
+
+namespace MediaBrowser.Controller.Events.Updates
+{
+ /// <summary>
+ /// An event that occurs when a plugin installation is cancelled.
+ /// </summary>
+ public class PluginInstallationCancelledEventArgs : GenericEventArgs<InstallationInfo>
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PluginInstallationCancelledEventArgs"/> class.
+ /// </summary>
+ /// <param name="arg">The installation info.</param>
+ public PluginInstallationCancelledEventArgs(InstallationInfo arg) : base(arg)
+ {
+ }
+ }
+}
diff --git a/MediaBrowser.Controller/Events/Updates/PluginInstalledEventArgs.cs b/MediaBrowser.Controller/Events/Updates/PluginInstalledEventArgs.cs
new file mode 100644
index 000000000..dfadc9f61
--- /dev/null
+++ b/MediaBrowser.Controller/Events/Updates/PluginInstalledEventArgs.cs
@@ -0,0 +1,19 @@
+using Jellyfin.Data.Events;
+using MediaBrowser.Model.Updates;
+
+namespace MediaBrowser.Controller.Events.Updates
+{
+ /// <summary>
+ /// An event that occurs when a plugin is installed.
+ /// </summary>
+ public class PluginInstalledEventArgs : GenericEventArgs<InstallationInfo>
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PluginInstalledEventArgs"/> class.
+ /// </summary>
+ /// <param name="arg">The installation info.</param>
+ public PluginInstalledEventArgs(InstallationInfo arg) : base(arg)
+ {
+ }
+ }
+}
diff --git a/MediaBrowser.Controller/Events/Updates/PluginInstallingEventArgs.cs b/MediaBrowser.Controller/Events/Updates/PluginInstallingEventArgs.cs
new file mode 100644
index 000000000..045a60027
--- /dev/null
+++ b/MediaBrowser.Controller/Events/Updates/PluginInstallingEventArgs.cs
@@ -0,0 +1,19 @@
+using Jellyfin.Data.Events;
+using MediaBrowser.Model.Updates;
+
+namespace MediaBrowser.Controller.Events.Updates
+{
+ /// <summary>
+ /// An event that occurs when a plugin is installing.
+ /// </summary>
+ public class PluginInstallingEventArgs : GenericEventArgs<InstallationInfo>
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PluginInstallingEventArgs"/> class.
+ /// </summary>
+ /// <param name="arg">The installation info.</param>
+ public PluginInstallingEventArgs(InstallationInfo arg) : base(arg)
+ {
+ }
+ }
+}
diff --git a/MediaBrowser.Controller/Events/Updates/PluginUninstalledEventArgs.cs b/MediaBrowser.Controller/Events/Updates/PluginUninstalledEventArgs.cs
new file mode 100644
index 000000000..7510b62b8
--- /dev/null
+++ b/MediaBrowser.Controller/Events/Updates/PluginUninstalledEventArgs.cs
@@ -0,0 +1,19 @@
+using Jellyfin.Data.Events;
+using MediaBrowser.Common.Plugins;
+
+namespace MediaBrowser.Controller.Events.Updates
+{
+ /// <summary>
+ /// An event that occurs when a plugin is uninstalled.
+ /// </summary>
+ public class PluginUninstalledEventArgs : GenericEventArgs<IPlugin>
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PluginUninstalledEventArgs"/> class.
+ /// </summary>
+ /// <param name="arg">The plugin.</param>
+ public PluginUninstalledEventArgs(IPlugin arg) : base(arg)
+ {
+ }
+ }
+}
diff --git a/MediaBrowser.Controller/Events/Updates/PluginUpdatedEventArgs.cs b/MediaBrowser.Controller/Events/Updates/PluginUpdatedEventArgs.cs
new file mode 100644
index 000000000..661ca066a
--- /dev/null
+++ b/MediaBrowser.Controller/Events/Updates/PluginUpdatedEventArgs.cs
@@ -0,0 +1,19 @@
+using Jellyfin.Data.Events;
+using MediaBrowser.Model.Updates;
+
+namespace MediaBrowser.Controller.Events.Updates
+{
+ /// <summary>
+ /// An event that occurs when a plugin is updated.
+ /// </summary>
+ public class PluginUpdatedEventArgs : GenericEventArgs<InstallationInfo>
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PluginUpdatedEventArgs"/> class.
+ /// </summary>
+ /// <param name="arg">The installation info.</param>
+ public PluginUpdatedEventArgs(InstallationInfo arg) : base(arg)
+ {
+ }
+ }
+}
diff --git a/MediaBrowser.Controller/IServerApplicationHost.cs b/MediaBrowser.Controller/IServerApplicationHost.cs
index 2db631157..39b896c0f 100644
--- a/MediaBrowser.Controller/IServerApplicationHost.cs
+++ b/MediaBrowser.Controller/IServerApplicationHost.cs
@@ -18,13 +18,7 @@ namespace MediaBrowser.Controller
{
event EventHandler HasUpdateAvailableChanged;
- /// <summary>
- /// Gets the system info.
- /// </summary>
- /// <returns>SystemInfo.</returns>
- Task<SystemInfo> GetSystemInfo(CancellationToken cancellationToken);
-
- Task<PublicSystemInfo> GetPublicSystemInfo(CancellationToken cancellationToken);
+ IServiceProvider ServiceProvider { get; }
bool CanLaunchWebBrowser { get; }
@@ -58,6 +52,14 @@ namespace MediaBrowser.Controller
string FriendlyName { get; }
/// <summary>
+ /// Gets the system info.
+ /// </summary>
+ /// <returns>SystemInfo.</returns>
+ Task<SystemInfo> GetSystemInfo(CancellationToken cancellationToken);
+
+ Task<PublicSystemInfo> GetPublicSystemInfo(CancellationToken cancellationToken);
+
+ /// <summary>
/// Gets all the local IP addresses of this API instance. Each address is validated by sending a 'ping' request
/// to the API that should exist at the address.
/// </summary>
diff --git a/MediaBrowser.Controller/Library/IUserManager.cs b/MediaBrowser.Controller/Library/IUserManager.cs
index ffd9739f6..6a4f5cf67 100644
--- a/MediaBrowser.Controller/Library/IUserManager.cs
+++ b/MediaBrowser.Controller/Library/IUserManager.cs
@@ -4,9 +4,9 @@ using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Jellyfin.Data.Entities;
+using Jellyfin.Data.Events;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Events;
using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.Library
@@ -22,26 +22,6 @@ namespace MediaBrowser.Controller.Library
event EventHandler<GenericEventArgs<User>> OnUserUpdated;
/// <summary>
- /// Occurs when a user is created.
- /// </summary>
- event EventHandler<GenericEventArgs<User>> OnUserCreated;
-
- /// <summary>
- /// Occurs when a user is deleted.
- /// </summary>
- event EventHandler<GenericEventArgs<User>> OnUserDeleted;
-
- /// <summary>
- /// Occurs when a user's password is changed.
- /// </summary>
- event EventHandler<GenericEventArgs<User>> OnUserPasswordChanged;
-
- /// <summary>
- /// Occurs when a user is locked out.
- /// </summary>
- event EventHandler<GenericEventArgs<User>> OnUserLockedOut;
-
- /// <summary>
/// Gets the users.
/// </summary>
/// <value>The users.</value>
diff --git a/MediaBrowser.Controller/Library/PlaybackStartEventArgs.cs b/MediaBrowser.Controller/Library/PlaybackStartEventArgs.cs
new file mode 100644
index 000000000..3aa9c2895
--- /dev/null
+++ b/MediaBrowser.Controller/Library/PlaybackStartEventArgs.cs
@@ -0,0 +1,6 @@
+namespace MediaBrowser.Controller.Library
+{
+ public class PlaybackStartEventArgs : PlaybackProgressEventArgs
+ {
+ }
+}
diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs
index 079442215..55c330931 100644
--- a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs
+++ b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs
@@ -5,11 +5,11 @@ using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Data.Entities;
+using Jellyfin.Data.Events;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Events;
using MediaBrowser.Model.LiveTv;
using MediaBrowser.Model.Querying;
diff --git a/MediaBrowser.Controller/Net/IHttpServer.cs b/MediaBrowser.Controller/Net/IHttpServer.cs
index e6609fae3..b04ebda8c 100644
--- a/MediaBrowser.Controller/Net/IHttpServer.cs
+++ b/MediaBrowser.Controller/Net/IHttpServer.cs
@@ -1,7 +1,7 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
-using MediaBrowser.Model.Events;
+using Jellyfin.Data.Events;
using MediaBrowser.Model.Services;
using Microsoft.AspNetCore.Http;
diff --git a/MediaBrowser.Controller/Providers/IProviderManager.cs b/MediaBrowser.Controller/Providers/IProviderManager.cs
index e470d77b6..996ec27c0 100644
--- a/MediaBrowser.Controller/Providers/IProviderManager.cs
+++ b/MediaBrowser.Controller/Providers/IProviderManager.cs
@@ -7,12 +7,12 @@ using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Data.Entities;
+using Jellyfin.Data.Events;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Events;
using MediaBrowser.Model.Providers;
namespace MediaBrowser.Controller.Providers
diff --git a/MediaBrowser.Controller/Session/ISessionManager.cs b/MediaBrowser.Controller/Session/ISessionManager.cs
index 1a1e200fa..9237d21df 100644
--- a/MediaBrowser.Controller/Session/ISessionManager.cs
+++ b/MediaBrowser.Controller/Session/ISessionManager.cs
@@ -4,11 +4,11 @@ using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Data.Events;
using MediaBrowser.Controller.Authentication;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Security;
using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Events;
using MediaBrowser.Model.Session;
using MediaBrowser.Model.SyncPlay;
diff --git a/MediaBrowser.Controller/Subtitles/SubtitleDownloadEventArgs.cs b/MediaBrowser.Controller/Subtitles/SubtitleDownloadEventArgs.cs
deleted file mode 100644
index b1d74517e..000000000
--- a/MediaBrowser.Controller/Subtitles/SubtitleDownloadEventArgs.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-#pragma warning disable CS1591
-
-using System;
-using MediaBrowser.Controller.Entities;
-
-namespace MediaBrowser.Controller.Subtitles
-{
- public class SubtitleDownloadEventArgs
- {
- public BaseItem Item { get; set; }
-
- public string Format { get; set; }
-
- public string Language { get; set; }
-
- public bool IsForced { get; set; }
-
- public string Provider { get; set; }
- }
-
- public class SubtitleDownloadFailureEventArgs
- {
- public BaseItem Item { get; set; }
-
- public string Provider { get; set; }
-
- public Exception Exception { get; set; }
- }
-}
diff --git a/MediaBrowser.Controller/Subtitles/SubtitleDownloadFailureEventArgs.cs b/MediaBrowser.Controller/Subtitles/SubtitleDownloadFailureEventArgs.cs
new file mode 100644
index 000000000..7687bf5b6
--- /dev/null
+++ b/MediaBrowser.Controller/Subtitles/SubtitleDownloadFailureEventArgs.cs
@@ -0,0 +1,14 @@
+using System;
+using MediaBrowser.Controller.Entities;
+
+namespace MediaBrowser.Controller.Subtitles
+{
+ public class SubtitleDownloadFailureEventArgs : EventArgs
+ {
+ public BaseItem Item { get; set; }
+
+ public string Provider { get; set; }
+
+ public Exception Exception { get; set; }
+ }
+}
diff --git a/MediaBrowser.Model/Activity/IActivityManager.cs b/MediaBrowser.Model/Activity/IActivityManager.cs
index 9dab5e77b..2362f7e92 100644
--- a/MediaBrowser.Model/Activity/IActivityManager.cs
+++ b/MediaBrowser.Model/Activity/IActivityManager.cs
@@ -4,7 +4,7 @@ using System;
using System.Linq;
using System.Threading.Tasks;
using Jellyfin.Data.Entities;
-using MediaBrowser.Model.Events;
+using Jellyfin.Data.Events;
using MediaBrowser.Model.Querying;
namespace MediaBrowser.Model.Activity
diff --git a/MediaBrowser.Model/Dlna/IDeviceDiscovery.cs b/MediaBrowser.Model/Dlna/IDeviceDiscovery.cs
index 76c9a4b04..05209e53d 100644
--- a/MediaBrowser.Model/Dlna/IDeviceDiscovery.cs
+++ b/MediaBrowser.Model/Dlna/IDeviceDiscovery.cs
@@ -1,7 +1,7 @@
#pragma warning disable CS1591
using System;
-using MediaBrowser.Model.Events;
+using Jellyfin.Data.Events;
namespace MediaBrowser.Model.Dlna
{
diff --git a/MediaBrowser.Model/Tasks/IScheduledTaskWorker.cs b/MediaBrowser.Model/Tasks/IScheduledTaskWorker.cs
index b08acba2c..2f05e08c5 100644
--- a/MediaBrowser.Model/Tasks/IScheduledTaskWorker.cs
+++ b/MediaBrowser.Model/Tasks/IScheduledTaskWorker.cs
@@ -1,6 +1,6 @@
#nullable disable
using System;
-using MediaBrowser.Model.Events;
+using Jellyfin.Data.Events;
namespace MediaBrowser.Model.Tasks
{
diff --git a/MediaBrowser.Model/Tasks/ITaskManager.cs b/MediaBrowser.Model/Tasks/ITaskManager.cs
index 363773ff7..02b29074e 100644
--- a/MediaBrowser.Model/Tasks/ITaskManager.cs
+++ b/MediaBrowser.Model/Tasks/ITaskManager.cs
@@ -3,7 +3,7 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
-using MediaBrowser.Model.Events;
+using Jellyfin.Data.Events;
namespace MediaBrowser.Model.Tasks
{
diff --git a/MediaBrowser.Providers/Manager/ProviderManager.cs b/MediaBrowser.Providers/Manager/ProviderManager.cs
index d9a84be5c..155e8cb8a 100644
--- a/MediaBrowser.Providers/Manager/ProviderManager.cs
+++ b/MediaBrowser.Providers/Manager/ProviderManager.cs
@@ -10,6 +10,7 @@ using System.Net.Mime;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Data.Entities;
+using Jellyfin.Data.Events;
using MediaBrowser.Common.Net;
using MediaBrowser.Common.Progress;
using MediaBrowser.Controller;
@@ -23,7 +24,6 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Subtitles;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Events;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Net;
using MediaBrowser.Model.Providers;
diff --git a/tests/MediaBrowser.Api.Tests/JellyfinApplicationFactory.cs b/tests/MediaBrowser.Api.Tests/JellyfinApplicationFactory.cs
index c39ed07de..2029f88e9 100644
--- a/tests/MediaBrowser.Api.Tests/JellyfinApplicationFactory.cs
+++ b/tests/MediaBrowser.Api.Tests/JellyfinApplicationFactory.cs
@@ -72,6 +72,7 @@ namespace MediaBrowser.Api.Tests
var startupConfig = Program.CreateAppConfiguration(commandLineOpts, appPaths);
ILoggerFactory loggerFactory = new SerilogLoggerFactory();
+ var serviceCollection = new ServiceCollection();
_disposableComponents.Add(loggerFactory);
// Create the app host and initialize it
@@ -80,10 +81,10 @@ namespace MediaBrowser.Api.Tests
loggerFactory,
commandLineOpts,
new ManagedFileSystem(loggerFactory.CreateLogger<ManagedFileSystem>(), appPaths),
- new NetworkManager(loggerFactory.CreateLogger<NetworkManager>()));
+ new NetworkManager(loggerFactory.CreateLogger<NetworkManager>()),
+ serviceCollection);
_disposableComponents.Add(appHost);
- var serviceCollection = new ServiceCollection();
- appHost.Init(serviceCollection);
+ appHost.Init();
// Configure the web host builder
Program.ConfigureWebHostBuilder(builder, appHost, serviceCollection, commandLineOpts, startupConfig, appPaths);