Merge pull request #3910 from barronpm/event-rewrite-1

Event Rewrite (Part 1)
pull/3998/head
Joshua M. Boniface 4 years ago committed by GitHub
commit cf6ef9958d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -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;

@ -2,7 +2,7 @@
namespace Emby.Dlna
{
public interface IConnectionManager : IEventManager, IUpnpService
public interface IConnectionManager : IDlnaEventManager, IUpnpService
{
}
}

@ -2,7 +2,7 @@
namespace Emby.Dlna
{
public interface IContentDirectory : IEventManager, IUpnpService
public interface IContentDirectory : IDlnaEventManager, IUpnpService
{
}
}

@ -2,7 +2,7 @@
namespace Emby.Dlna
{
public interface IEventManager
public interface IDlnaEventManager
{
/// <summary>
/// Cancels the event subscription.

@ -2,7 +2,7 @@
namespace Emby.Dlna
{
public interface IMediaReceiverRegistrar : IEventManager, IUpnpService
public interface IMediaReceiverRegistrar : IDlnaEventManager, IUpnpService
{
}
}

@ -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;

@ -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;

@ -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; }

@ -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;

@ -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;

@ -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));
}
}
}

@ -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)
{

@ -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;

@ -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;

@ -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;

@ -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

@ -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);
}

@ -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;
}
}
}
}

@ -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;

@ -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;

@ -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;

@ -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;

@ -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;

@ -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;

@ -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)

@ -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;

@ -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);
}
}
}

@ -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

@ -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;

@ -1,19 +1,13 @@
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>
@ -22,5 +16,11 @@ namespace MediaBrowser.Model.Events
{
Argument = arg;
}
/// <summary>
/// Gets the argument.
/// </summary>
/// <value>The argument.</value>
public T Argument { get; }
}
}

@ -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
{
}
}

@ -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)
{
}
}
}

@ -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)
{
}
}
}

@ -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)
{
}
}
}

@ -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)
{
}
}
}

@ -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)
{
}
}
}

@ -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

@ -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;
}
}
}

@ -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);
}
}
}

@ -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);
}
}
}

@ -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;
}
}
}

@ -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;
}
}
}

@ -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);
}
}
}

@ -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);
}
}
}

@ -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);
}
}
}

@ -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));
}
}
}

@ -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);
}
}
}

@ -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);
}
}
}

@ -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);
}
}
}

@ -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);
}
}
}

@ -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);
}
}
}

@ -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);
}
}
}

@ -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);
}
}
}

@ -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);
}
}
}

@ -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);
}
}
}

@ -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);
}
}
}

@ -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);
}
}
}

@ -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);
}
}
}

@ -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);
}
}
}

@ -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);
}
}
}

@ -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);
}
}
}

@ -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);
}
}
}

@ -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());
}
}
}
}
}

@ -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>();
}
}
}

@ -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
{

@ -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;
@ -77,21 +83,9 @@ namespace Jellyfin.Server.Implementations.Users
_defaultPasswordResetProvider = _passwordResetProviders.OfType<DefaultPasswordResetProvider>().First();
}
/// <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
{
@ -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,

@ -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 />

@ -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();

@ -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.

@ -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
{
@ -81,6 +82,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)
{

@ -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

@ -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; }

@ -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;

@ -0,0 +1,20 @@
using System;
using System.Threading.Tasks;
namespace MediaBrowser.Controller.Events
{
/// <summary>
/// An interface representing a type that consumes events of type <c>T</c>.
/// </summary>
/// <typeparam name="T">The type of events this consumes.</typeparam>
public interface IEventConsumer<in T>
where T : EventArgs
{
/// <summary>
/// A method that is called when an event of type <c>T</c> is fired.
/// </summary>
/// <param name="eventArgs">The event.</param>
/// <returns>A task representing the consumption of the event.</returns>
Task OnEvent(T eventArgs);
}
}

@ -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;
}
}

@ -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)
{
}
}
}

@ -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)
{
}
}
}

@ -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)
{
}
}
}

@ -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)
{
}
}
}

@ -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)
{
}
}
}

@ -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)
{
}
}
}

@ -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)
{
}
}
}

@ -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; }
@ -57,6 +51,14 @@ namespace MediaBrowser.Controller
/// <value>The name of the friendly.</value>
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.

@ -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
@ -21,26 +21,6 @@ namespace MediaBrowser.Controller.Library
/// </summary>
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>

@ -0,0 +1,9 @@
namespace MediaBrowser.Controller.Library
{
/// <summary>
/// An event that occurs when playback is started.
/// </summary>
public class PlaybackStartEventArgs : PlaybackProgressEventArgs
{
}
}

@ -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;

@ -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;

@ -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

@ -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;

@ -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; }
}
}

@ -0,0 +1,26 @@
using System;
using MediaBrowser.Controller.Entities;
namespace MediaBrowser.Controller.Subtitles
{
/// <summary>
/// An event that occurs when subtitle downloading fails.
/// </summary>
public class SubtitleDownloadFailureEventArgs : EventArgs
{
/// <summary>
/// Gets or sets the item.
/// </summary>
public BaseItem Item { get; set; }
/// <summary>
/// Gets or sets the provider.
/// </summary>
public string Provider { get; set; }
/// <summary>
/// Gets or sets the exception.
/// </summary>
public Exception Exception { get; set; }
}
}

@ -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

@ -1,7 +1,7 @@
#pragma warning disable CS1591
using System;
using MediaBrowser.Model.Events;
using Jellyfin.Data.Events;
namespace MediaBrowser.Model.Dlna
{

@ -1,6 +1,6 @@
#nullable disable
using System;
using MediaBrowser.Model.Events;
using Jellyfin.Data.Events;
namespace MediaBrowser.Model.Tasks
{

@ -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
{

@ -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;

@ -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);

Loading…
Cancel
Save