Merge remote-tracking branch 'upstream/master' into package-install-repo

This commit is contained in:
crobibero
2020-09-01 18:24:42 -06:00
580 changed files with 9651 additions and 10726 deletions

View File

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

View File

@@ -37,6 +37,7 @@ using Emby.Server.Implementations.LiveTv;
using Emby.Server.Implementations.Localization;
using Emby.Server.Implementations.Net;
using Emby.Server.Implementations.Playlists;
using Emby.Server.Implementations.QuickConnect;
using Emby.Server.Implementations.ScheduledTasks;
using Emby.Server.Implementations.Security;
using Emby.Server.Implementations.Serialization;
@@ -53,7 +54,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;
@@ -72,6 +72,7 @@ using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Playlists;
using MediaBrowser.Controller.Plugins;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.QuickConnect;
using MediaBrowser.Controller.Resolvers;
using MediaBrowser.Controller.Security;
using MediaBrowser.Controller.Session;
@@ -98,6 +99,7 @@ using MediaBrowser.Providers.Plugins.TheTvdb;
using MediaBrowser.Providers.Subtitles;
using MediaBrowser.XbmcMetadata.Providers;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Prometheus.DotNetRuntime;
@@ -173,6 +175,8 @@ namespace Emby.Server.Implementations
/// </summary>
protected ILogger<ApplicationHost> Logger { get; }
protected IServiceCollection ServiceCollection { get; }
private IPlugin[] _plugins;
/// <summary>
@@ -238,9 +242,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 +470,7 @@ namespace Emby.Server.Implementations
}
/// <inheritdoc/>
public void Init(IServiceCollection serviceCollection)
public void Init()
{
HttpPort = ServerConfigurationManager.Configuration.HttpServerPortNumber;
HttpsPort = ServerConfigurationManager.Configuration.HttpsPortNumber;
@@ -493,7 +499,7 @@ namespace Emby.Server.Implementations
DiscoverTypes();
RegisterServices(serviceCollection);
RegisterServices();
}
public Task ExecuteHttpHandlerAsync(HttpContext context, Func<Task> next)
@@ -502,136 +508,140 @@ 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<IQuickConnect, QuickConnectManager>();
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.AddSingleton<TranscodingJobHelper>();
ServiceCollection.AddScoped<MediaInfoHelper>();
ServiceCollection.AddScoped<AudioHelper>();
ServiceCollection.AddScoped<DynamicHlsHelper>();
}
/// <summary>
@@ -831,6 +841,8 @@ namespace Emby.Server.Implementations
{
hasPluginConfiguration.SetStartupInfo(s => Directory.CreateDirectory(s));
}
plugin.RegisterServices(ServiceCollection);
}
catch (Exception ex)
{
@@ -1385,6 +1397,20 @@ namespace Emby.Server.Implementations
_plugins = list.ToArray();
}
public IEnumerable<Assembly> GetApiPluginAssemblies()
{
var assemblies = _allConcreteTypes
.Where(i => typeof(ControllerBase).IsAssignableFrom(i))
.Select(i => i.Assembly)
.Distinct();
foreach (var assembly in assemblies)
{
Logger.LogDebug("Found API endpoints in plugin {name}", assembly.FullName);
yield return assembly;
}
}
public virtual void LaunchUrl(string url)
{
if (!CanLaunchWebBrowser)

View File

@@ -746,12 +746,21 @@ namespace Emby.Server.Implementations.Channels
// null if came from cache
if (itemsResult != null)
{
var internalItems = itemsResult.Items
.Select(i => GetChannelItemEntity(i, channelProvider, channel.Id, parentItem, cancellationToken))
.ToArray();
var items = itemsResult.Items;
var itemsLen = items.Count;
var internalItems = new Guid[itemsLen];
for (int i = 0; i < itemsLen; i++)
{
internalItems[i] = (await GetChannelItemEntityAsync(
items[i],
channelProvider,
channel.Id,
parentItem,
cancellationToken).ConfigureAwait(false)).Id;
}
var existingIds = _libraryManager.GetItemIds(query);
var deadIds = existingIds.Except(internalItems.Select(i => i.Id))
var deadIds = existingIds.Except(internalItems)
.ToArray();
foreach (var deadId in deadIds)
@@ -963,7 +972,7 @@ namespace Emby.Server.Implementations.Channels
return item;
}
private BaseItem GetChannelItemEntity(ChannelItemInfo info, IChannel channelProvider, Guid internalChannelId, BaseItem parentFolder, CancellationToken cancellationToken)
private async Task<BaseItem> GetChannelItemEntityAsync(ChannelItemInfo info, IChannel channelProvider, Guid internalChannelId, BaseItem parentFolder, CancellationToken cancellationToken)
{
var parentFolderId = parentFolder.Id;
@@ -1165,7 +1174,7 @@ namespace Emby.Server.Implementations.Channels
}
else if (forceUpdate)
{
item.UpdateToRepository(ItemUpdateType.None, cancellationToken);
await item.UpdateToRepositoryAsync(ItemUpdateType.None, cancellationToken).ConfigureAwait(false);
}
if ((isNew || forceUpdate) && info.Type == ChannelItemType.Media)

View File

@@ -132,7 +132,7 @@ namespace Emby.Server.Implementations.Collections
}
/// <inheritdoc />
public BoxSet CreateCollection(CollectionCreationOptions options)
public async Task<BoxSet> CreateCollectionAsync(CollectionCreationOptions options)
{
var name = options.Name;
@@ -141,7 +141,7 @@ namespace Emby.Server.Implementations.Collections
// This could cause it to get re-resolved as a plain folder
var folderName = _fileSystem.GetValidFilename(name) + " [boxset]";
var parentFolder = GetCollectionsFolder(true).GetAwaiter().GetResult();
var parentFolder = await GetCollectionsFolder(true).ConfigureAwait(false);
if (parentFolder == null)
{
@@ -169,12 +169,16 @@ namespace Emby.Server.Implementations.Collections
if (options.ItemIdList.Length > 0)
{
AddToCollection(collection.Id, options.ItemIdList, false, new MetadataRefreshOptions(new DirectoryService(_fileSystem))
{
// The initial adding of items is going to create a local metadata file
// This will cause internet metadata to be skipped as a result
MetadataRefreshMode = MetadataRefreshMode.FullRefresh
});
await AddToCollectionAsync(
collection.Id,
options.ItemIdList.Select(x => new Guid(x)),
false,
new MetadataRefreshOptions(new DirectoryService(_fileSystem))
{
// The initial adding of items is going to create a local metadata file
// This will cause internet metadata to be skipped as a result
MetadataRefreshMode = MetadataRefreshMode.FullRefresh
}).ConfigureAwait(false);
}
else
{
@@ -197,18 +201,10 @@ namespace Emby.Server.Implementations.Collections
}
/// <inheritdoc />
public void AddToCollection(Guid collectionId, IEnumerable<string> ids)
{
AddToCollection(collectionId, ids, true, new MetadataRefreshOptions(new DirectoryService(_fileSystem)));
}
public Task AddToCollectionAsync(Guid collectionId, IEnumerable<Guid> ids)
=> AddToCollectionAsync(collectionId, ids, true, new MetadataRefreshOptions(new DirectoryService(_fileSystem)));
/// <inheritdoc />
public void AddToCollection(Guid collectionId, IEnumerable<Guid> ids)
{
AddToCollection(collectionId, ids.Select(i => i.ToString("N", CultureInfo.InvariantCulture)), true, new MetadataRefreshOptions(new DirectoryService(_fileSystem)));
}
private void AddToCollection(Guid collectionId, IEnumerable<string> ids, bool fireEvent, MetadataRefreshOptions refreshOptions)
private async Task AddToCollectionAsync(Guid collectionId, IEnumerable<Guid> ids, bool fireEvent, MetadataRefreshOptions refreshOptions)
{
var collection = _libraryManager.GetItemById(collectionId) as BoxSet;
if (collection == null)
@@ -224,15 +220,14 @@ namespace Emby.Server.Implementations.Collections
foreach (var id in ids)
{
var guidId = new Guid(id);
var item = _libraryManager.GetItemById(guidId);
var item = _libraryManager.GetItemById(id);
if (item == null)
{
throw new ArgumentException("No item exists with the supplied Id");
}
if (!currentLinkedChildrenIds.Contains(guidId))
if (!currentLinkedChildrenIds.Contains(id))
{
itemList.Add(item);
@@ -249,7 +244,7 @@ namespace Emby.Server.Implementations.Collections
collection.UpdateRatingToItems(linkedChildrenList);
collection.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None);
await collection.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
refreshOptions.ForceSave = true;
_providerManager.QueueRefresh(collection.Id, refreshOptions, RefreshPriority.High);
@@ -266,13 +261,7 @@ namespace Emby.Server.Implementations.Collections
}
/// <inheritdoc />
public void RemoveFromCollection(Guid collectionId, IEnumerable<string> itemIds)
{
RemoveFromCollection(collectionId, itemIds.Select(i => new Guid(i)));
}
/// <inheritdoc />
public void RemoveFromCollection(Guid collectionId, IEnumerable<Guid> itemIds)
public async Task RemoveFromCollectionAsync(Guid collectionId, IEnumerable<Guid> itemIds)
{
var collection = _libraryManager.GetItemById(collectionId) as BoxSet;
@@ -309,7 +298,7 @@ namespace Emby.Server.Implementations.Collections
collection.LinkedChildren = collection.LinkedChildren.Except(list).ToArray();
}
collection.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None);
await collection.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
_providerManager.QueueRefresh(
collection.Id,
new MetadataRefreshOptions(new DirectoryService(_fileSystem))

View File

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

View File

@@ -4308,7 +4308,7 @@ namespace Emby.Server.Implementations.Data
whereClauses.Add("ProductionYear=@Years");
if (statement != null)
{
statement.TryBind("@Years", query.Years[0].ToString());
statement.TryBind("@Years", query.Years[0].ToString(CultureInfo.InvariantCulture));
}
}
else if (query.Years.Length > 1)
@@ -4560,13 +4560,13 @@ namespace Emby.Server.Implementations.Data
if (query.AncestorIds.Length > 1)
{
var inClause = string.Join(",", query.AncestorIds.Select(i => "'" + i.ToString("N", CultureInfo.InvariantCulture) + "'"));
whereClauses.Add(string.Format("Guid in (select itemId from AncestorIds where AncestorIdText in ({0}))", inClause));
whereClauses.Add(string.Format(CultureInfo.InvariantCulture, "Guid in (select itemId from AncestorIds where AncestorIdText in ({0}))", inClause));
}
if (!string.IsNullOrWhiteSpace(query.AncestorWithPresentationUniqueKey))
{
var inClause = "select guid from TypedBaseItems where PresentationUniqueKey=@AncestorWithPresentationUniqueKey";
whereClauses.Add(string.Format("Guid in (select itemId from AncestorIds where AncestorId in ({0}))", inClause));
whereClauses.Add(string.Format(CultureInfo.InvariantCulture, "Guid in (select itemId from AncestorIds where AncestorId in ({0}))", inClause));
if (statement != null)
{
statement.TryBind("@AncestorWithPresentationUniqueKey", query.AncestorWithPresentationUniqueKey);
@@ -5170,7 +5170,10 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
insertText.Append(',');
}
insertText.AppendFormat("(@ItemId, @AncestorId{0}, @AncestorIdText{0})", i.ToString(CultureInfo.InvariantCulture));
insertText.AppendFormat(
CultureInfo.InvariantCulture,
"(@ItemId, @AncestorId{0}, @AncestorIdText{0})",
i.ToString(CultureInfo.InvariantCulture));
}
using (var statement = PrepareStatement(db, insertText.ToString()))

View File

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

View File

@@ -22,7 +22,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="IPNetwork2" Version="2.5.211" />
<PackageReference Include="IPNetwork2" Version="2.5.224" />
<PackageReference Include="Jellyfin.XmlTv" Version="10.6.2" />
<PackageReference Include="Microsoft.AspNetCore.Hosting" Version="2.2.7" />
<PackageReference Include="Microsoft.AspNetCore.Hosting.Abstractions" Version="2.2.0" />
@@ -37,11 +37,11 @@
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.6" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="3.1.6" />
<PackageReference Include="Mono.Nat" Version="2.0.2" />
<PackageReference Include="prometheus-net.DotNetRuntime" Version="3.3.1" />
<PackageReference Include="prometheus-net.DotNetRuntime" Version="3.4.0" />
<PackageReference Include="ServiceStack.Text.Core" Version="5.9.2" />
<PackageReference Include="sharpcompress" Version="0.26.0" />
<PackageReference Include="SQLitePCL.pretty.netstandard" Version="2.1.0" />
<PackageReference Include="DotNet.Glob" Version="3.0.9" />
<PackageReference Include="DotNet.Glob" Version="3.1.0" />
</ItemGroup>
<ItemGroup>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -179,7 +179,7 @@ namespace Emby.Server.Implementations.HttpServer
return;
}
WebSocketMessage<object> stub;
WebSocketMessage<object>? stub;
try
{
@@ -209,6 +209,12 @@ namespace Emby.Server.Implementations.HttpServer
return;
}
if (stub == null)
{
_logger.LogError("Error processing web socket message");
return;
}
// Tell the PipeReader how much of the buffer we have consumed
reader.AdvanceTo(buffer.End);

View File

@@ -6,12 +6,11 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Emby.Server.Implementations.Library;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Plugins;
using MediaBrowser.Model.IO;
using Emby.Server.Implementations.Library;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.IO
@@ -38,6 +37,8 @@ namespace Emby.Server.Implementations.IO
/// </summary>
private readonly ConcurrentDictionary<string, string> _tempIgnoredPaths = new ConcurrentDictionary<string, string>(StringComparer.OrdinalIgnoreCase);
private bool _disposed = false;
/// <summary>
/// Add the path to our temporary ignore list. Use when writing to a path within our listening scope.
/// </summary>
@@ -492,8 +493,6 @@ namespace Emby.Server.Implementations.IO
}
}
private bool _disposed = false;
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
@@ -522,24 +521,4 @@ namespace Emby.Server.Implementations.IO
_disposed = true;
}
}
public class LibraryMonitorStartup : IServerEntryPoint
{
private readonly ILibraryMonitor _monitor;
public LibraryMonitorStartup(ILibraryMonitor monitor)
{
_monitor = monitor;
}
public Task RunAsync()
{
_monitor.Start();
return Task.CompletedTask;
}
public void Dispose()
{
}
}
}

View File

@@ -0,0 +1,35 @@
using System.Threading.Tasks;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Plugins;
namespace Emby.Server.Implementations.IO
{
/// <summary>
/// <see cref="IServerEntryPoint" /> which is responsible for starting the library monitor.
/// </summary>
public sealed class LibraryMonitorStartup : IServerEntryPoint
{
private readonly ILibraryMonitor _monitor;
/// <summary>
/// Initializes a new instance of the <see cref="LibraryMonitorStartup"/> class.
/// </summary>
/// <param name="monitor">The library monitor.</param>
public LibraryMonitorStartup(ILibraryMonitor monitor)
{
_monitor = monitor;
}
/// <inheritdoc />
public Task RunAsync()
{
_monitor.Start();
return Task.CompletedTask;
}
/// <inheritdoc />
public void Dispose()
{
}
}
}

View File

@@ -729,7 +729,7 @@ namespace Emby.Server.Implementations.Library
Directory.CreateDirectory(rootFolderPath);
var rootFolder = GetItemById(GetNewItemId(rootFolderPath, typeof(AggregateFolder))) as AggregateFolder ??
((Folder) ResolvePath(_fileSystem.GetDirectoryInfo(rootFolderPath)))
((Folder)ResolvePath(_fileSystem.GetDirectoryInfo(rootFolderPath)))
.DeepCopy<Folder, AggregateFolder>();
// In case program data folder was moved
@@ -771,7 +771,7 @@ namespace Emby.Server.Implementations.Library
if (folder.ParentId != rootFolder.Id)
{
folder.ParentId = rootFolder.Id;
folder.UpdateToRepository(ItemUpdateType.MetadataImport, CancellationToken.None);
folder.UpdateToRepositoryAsync(ItemUpdateType.MetadataImport, CancellationToken.None).GetAwaiter().GetResult();
}
rootFolder.AddVirtualChild(folder);
@@ -1868,7 +1868,8 @@ namespace Emby.Server.Implementations.Library
return image.Path != null && !image.IsLocalFile;
}
public void UpdateImages(BaseItem item, bool forceUpdate = false)
/// <inheritdoc />
public async Task UpdateImagesAsync(BaseItem item, bool forceUpdate = false)
{
if (item == null)
{
@@ -1891,7 +1892,7 @@ namespace Emby.Server.Implementations.Library
try
{
var index = item.GetImageIndex(img);
image = ConvertImageToLocal(item, img, index).ConfigureAwait(false).GetAwaiter().GetResult();
image = await ConvertImageToLocal(item, img, index).ConfigureAwait(false);
}
catch (ArgumentException)
{
@@ -1913,7 +1914,7 @@ namespace Emby.Server.Implementations.Library
}
catch (Exception ex)
{
_logger.LogError(ex, "Cannnot get image dimensions for {0}", image.Path);
_logger.LogError(ex, "Cannot get image dimensions for {0}", image.Path);
image.Width = 0;
image.Height = 0;
continue;
@@ -1943,10 +1944,8 @@ namespace Emby.Server.Implementations.Library
RegisterItem(item);
}
/// <summary>
/// Updates the item.
/// </summary>
public void UpdateItems(IReadOnlyList<BaseItem> items, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken)
/// <inheritdoc />
public async Task UpdateItemsAsync(IReadOnlyList<BaseItem> items, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken)
{
foreach (var item in items)
{
@@ -1957,7 +1956,7 @@ namespace Emby.Server.Implementations.Library
item.DateLastSaved = DateTime.UtcNow;
UpdateImages(item, updateReason >= ItemUpdateType.ImageUpdate);
await UpdateImagesAsync(item, updateReason >= ItemUpdateType.ImageUpdate).ConfigureAwait(false);
}
_itemRepository.SaveItems(items, cancellationToken);
@@ -1991,17 +1990,9 @@ namespace Emby.Server.Implementations.Library
}
}
/// <summary>
/// Updates the item.
/// </summary>
/// <param name="item">The item.</param>
/// <param name="parent">The parent item.</param>
/// <param name="updateReason">The update reason.</param>
/// <param name="cancellationToken">The cancellation token.</param>
public void UpdateItem(BaseItem item, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken)
{
UpdateItems(new[] { item }, parent, updateReason, cancellationToken);
}
/// <inheritdoc />
public Task UpdateItemAsync(BaseItem item, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken)
=> UpdateItemsAsync(new[] { item }, parent, updateReason, cancellationToken);
/// <summary>
/// Reports the item removed.
@@ -2233,7 +2224,7 @@ namespace Emby.Server.Implementations.Library
if (refresh)
{
item.UpdateToRepository(ItemUpdateType.MetadataImport, CancellationToken.None);
item.UpdateToRepositoryAsync(ItemUpdateType.MetadataImport, CancellationToken.None).GetAwaiter().GetResult();
ProviderManager.QueueRefresh(item.Id, new MetadataRefreshOptions(new DirectoryService(_fileSystem)), RefreshPriority.Normal);
}
@@ -2420,7 +2411,7 @@ namespace Emby.Server.Implementations.Library
if (!string.Equals(viewType, item.ViewType, StringComparison.OrdinalIgnoreCase))
{
item.ViewType = viewType;
item.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None);
item.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).GetAwaiter().GetResult();
}
var refresh = isNew || DateTime.UtcNow - item.DateLastRefreshed >= _viewRefreshInterval;
@@ -2902,7 +2893,7 @@ namespace Emby.Server.Implementations.Library
await ProviderManager.SaveImage(item, url, image.Type, imageIndex, CancellationToken.None).ConfigureAwait(false);
item.UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None);
await item.UpdateToRepositoryAsync(ItemUpdateType.ImageUpdate, CancellationToken.None).ConfigureAwait(false);
return item.GetImageInfo(image.Type, imageIndex);
}
@@ -2920,7 +2911,7 @@ namespace Emby.Server.Implementations.Library
// Remove this image to prevent it from retrying over and over
item.RemoveImage(image);
item.UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None);
await item.UpdateToRepositoryAsync(ItemUpdateType.ImageUpdate, CancellationToken.None).ConfigureAwait(false);
throw new InvalidOperationException();
}

View File

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

View File

@@ -230,7 +230,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
if (filters.Count > 0)
{
output += string.Format(" -vf \"{0}\"", string.Join(",", filters.ToArray()));
output += string.Format(CultureInfo.InvariantCulture, " -vf \"{0}\"", string.Join(",", filters.ToArray()));
}
return output;

View File

@@ -5,7 +5,7 @@ using MediaBrowser.Controller.Plugins;
namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
public class EntryPoint : IServerEntryPoint
public sealed class EntryPoint : IServerEntryPoint
{
/// <inheritdoc />
public Task RunAsync()

View File

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

View File

@@ -929,7 +929,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
private static string NormalizeName(string value)
{
return value.Replace(" ", string.Empty).Replace("-", string.Empty);
return value.Replace(" ", string.Empty, StringComparison.Ordinal).Replace("-", string.Empty, StringComparison.Ordinal);
}
public class ScheduleDirect

View File

@@ -237,7 +237,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
&& !programInfo.IsRepeat
&& (programInfo.EpisodeNumber ?? 0) == 0)
{
programInfo.ShowId = programInfo.ShowId + programInfo.StartDate.Ticks.ToString(CultureInfo.InvariantCulture);
programInfo.ShowId += programInfo.StartDate.Ticks.ToString(CultureInfo.InvariantCulture);
}
}
else
@@ -246,7 +246,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
}
// Construct an id from the channel and start date
programInfo.Id = string.Format("{0}_{1:O}", program.ChannelId, program.StartDate);
programInfo.Id = string.Format(CultureInfo.InvariantCulture, "{0}_{1:O}", program.ChannelId, program.StartDate);
if (programInfo.IsMovie)
{
@@ -296,7 +296,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings
Name = c.DisplayName,
ImageUrl = c.Icon != null && !string.IsNullOrEmpty(c.Icon.Source) ? c.Icon.Source : null,
Number = string.IsNullOrWhiteSpace(c.Number) ? c.Id : c.Number
}).ToList();
}
}

View File

@@ -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;
@@ -41,6 +41,7 @@ namespace Emby.Server.Implementations.LiveTv
/// </summary>
public class LiveTvManager : ILiveTvManager, IDisposable
{
private const int MaxGuideDays = 14;
private const string ExternalServiceTag = "ExternalServiceId";
private const string EtagKey = "ProgramEtag";
@@ -421,7 +422,7 @@ namespace Emby.Server.Implementations.LiveTv
}
}
private LiveTvChannel GetChannel(ChannelInfo channelInfo, string serviceName, BaseItem parentFolder, CancellationToken cancellationToken)
private async Task<LiveTvChannel> GetChannelAsync(ChannelInfo channelInfo, string serviceName, BaseItem parentFolder, CancellationToken cancellationToken)
{
var parentFolderId = parentFolder.Id;
var isNew = false;
@@ -511,7 +512,7 @@ namespace Emby.Server.Implementations.LiveTv
}
else if (forceUpdate)
{
_libraryManager.UpdateItem(item, parentFolder, ItemUpdateType.MetadataImport, cancellationToken);
await _libraryManager.UpdateItemAsync(item, parentFolder, ItemUpdateType.MetadataImport, cancellationToken).ConfigureAwait(false);
}
return item;
@@ -560,7 +561,7 @@ namespace Emby.Server.Implementations.LiveTv
item.Audio = info.Audio;
item.ChannelId = channel.Id;
item.CommunityRating = item.CommunityRating ?? info.CommunityRating;
item.CommunityRating ??= info.CommunityRating;
if ((item.CommunityRating ?? 0).Equals(0))
{
item.CommunityRating = null;
@@ -645,8 +646,8 @@ namespace Emby.Server.Implementations.LiveTv
item.IsSeries = isSeries;
item.Name = info.Name;
item.OfficialRating = item.OfficialRating ?? info.OfficialRating;
item.Overview = item.Overview ?? info.Overview;
item.OfficialRating ??= info.OfficialRating;
item.Overview ??= info.Overview;
item.RunTimeTicks = (info.EndDate - info.StartDate).Ticks;
item.ProviderIds = info.ProviderIds;
@@ -683,19 +684,23 @@ namespace Emby.Server.Implementations.LiveTv
{
if (!string.IsNullOrWhiteSpace(info.ImagePath))
{
item.SetImage(new ItemImageInfo
{
Path = info.ImagePath,
Type = ImageType.Primary
}, 0);
item.SetImage(
new ItemImageInfo
{
Path = info.ImagePath,
Type = ImageType.Primary
},
0);
}
else if (!string.IsNullOrWhiteSpace(info.ImageUrl))
{
item.SetImage(new ItemImageInfo
{
Path = info.ImageUrl,
Type = ImageType.Primary
}, 0);
item.SetImage(
new ItemImageInfo
{
Path = info.ImageUrl,
Type = ImageType.Primary
},
0);
}
}
@@ -703,11 +708,13 @@ namespace Emby.Server.Implementations.LiveTv
{
if (!string.IsNullOrWhiteSpace(info.ThumbImageUrl))
{
item.SetImage(new ItemImageInfo
{
Path = info.ThumbImageUrl,
Type = ImageType.Thumb
}, 0);
item.SetImage(
new ItemImageInfo
{
Path = info.ThumbImageUrl,
Type = ImageType.Thumb
},
0);
}
}
@@ -715,11 +722,13 @@ namespace Emby.Server.Implementations.LiveTv
{
if (!string.IsNullOrWhiteSpace(info.LogoImageUrl))
{
item.SetImage(new ItemImageInfo
{
Path = info.LogoImageUrl,
Type = ImageType.Logo
}, 0);
item.SetImage(
new ItemImageInfo
{
Path = info.LogoImageUrl,
Type = ImageType.Logo
},
0);
}
}
@@ -727,11 +736,13 @@ namespace Emby.Server.Implementations.LiveTv
{
if (!string.IsNullOrWhiteSpace(info.BackdropImageUrl))
{
item.SetImage(new ItemImageInfo
{
Path = info.BackdropImageUrl,
Type = ImageType.Backdrop
}, 0);
item.SetImage(
new ItemImageInfo
{
Path = info.BackdropImageUrl,
Type = ImageType.Backdrop
},
0);
}
}
@@ -786,7 +797,6 @@ namespace Emby.Server.Implementations.LiveTv
if (query.OrderBy.Count == 0)
{
// Unless something else was specified, order by start date to take advantage of a specialized index
query.OrderBy = new[]
{
@@ -824,7 +834,7 @@ namespace Emby.Server.Implementations.LiveTv
if (!string.IsNullOrWhiteSpace(query.SeriesTimerId))
{
var seriesTimers = await GetSeriesTimersInternal(new SeriesTimerQuery { }, cancellationToken).ConfigureAwait(false);
var seriesTimers = await GetSeriesTimersInternal(new SeriesTimerQuery(), cancellationToken).ConfigureAwait(false);
var seriesTimer = seriesTimers.Items.FirstOrDefault(i => string.Equals(_tvDtoService.GetInternalSeriesTimerId(i.Id).ToString("N", CultureInfo.InvariantCulture), query.SeriesTimerId, StringComparison.OrdinalIgnoreCase));
if (seriesTimer != null)
{
@@ -847,13 +857,11 @@ namespace Emby.Server.Implementations.LiveTv
var returnArray = _dtoService.GetBaseItemDtos(queryResult.Items, options, user);
var result = new QueryResult<BaseItemDto>
return new QueryResult<BaseItemDto>
{
Items = returnArray,
TotalRecordCount = queryResult.TotalRecordCount
};
return result;
}
public QueryResult<BaseItem> GetRecommendedProgramsInternal(InternalItemsQuery query, DtoOptions options, CancellationToken cancellationToken)
@@ -1121,7 +1129,7 @@ namespace Emby.Server.Implementations.LiveTv
try
{
var item = GetChannel(channelInfo.Item2, channelInfo.Item1, parentFolder, cancellationToken);
var item = await GetChannelAsync(channelInfo.Item2, channelInfo.Item1, parentFolder, cancellationToken).ConfigureAwait(false);
list.Add(item);
}
@@ -1138,7 +1146,7 @@ namespace Emby.Server.Implementations.LiveTv
double percent = numComplete;
percent /= allChannelsList.Count;
progress.Report(5 * percent + 10);
progress.Report((5 * percent) + 10);
}
progress.Report(15);
@@ -1173,7 +1181,6 @@ namespace Emby.Server.Implementations.LiveTv
var existingPrograms = _libraryManager.GetItemList(new InternalItemsQuery
{
IncludeItemTypes = new string[] { typeof(LiveTvProgram).Name },
ChannelIds = new Guid[] { currentChannel.Id },
DtoOptions = new DtoOptions(true)
@@ -1214,7 +1221,11 @@ namespace Emby.Server.Implementations.LiveTv
if (updatedPrograms.Count > 0)
{
_libraryManager.UpdateItems(updatedPrograms, currentChannel, ItemUpdateType.MetadataImport, cancellationToken);
await _libraryManager.UpdateItemsAsync(
updatedPrograms,
currentChannel,
ItemUpdateType.MetadataImport,
cancellationToken).ConfigureAwait(false);
}
currentChannel.IsMovie = isMovie;
@@ -1227,7 +1238,7 @@ namespace Emby.Server.Implementations.LiveTv
currentChannel.AddTag("Kids");
}
currentChannel.UpdateToRepository(ItemUpdateType.MetadataImport, cancellationToken);
await currentChannel.UpdateToRepositoryAsync(ItemUpdateType.MetadataImport, cancellationToken).ConfigureAwait(false);
await currentChannel.RefreshMetadata(
new MetadataRefreshOptions(new DirectoryService(_fileSystem))
{
@@ -1298,8 +1309,6 @@ namespace Emby.Server.Implementations.LiveTv
}
}
private const int MaxGuideDays = 14;
private double GetGuideDays()
{
var config = GetConfiguration();
@@ -1712,7 +1721,7 @@ namespace Emby.Server.Implementations.LiveTv
if (timer == null)
{
throw new ResourceNotFoundException(string.Format("Timer with Id {0} not found", id));
throw new ResourceNotFoundException(string.Format(CultureInfo.InvariantCulture, "Timer with Id {0} not found", id));
}
var service = GetService(timer.ServiceName);
@@ -1731,7 +1740,7 @@ namespace Emby.Server.Implementations.LiveTv
if (timer == null)
{
throw new ResourceNotFoundException(string.Format("SeriesTimer with Id {0} not found", id));
throw new ResourceNotFoundException(string.Format(CultureInfo.InvariantCulture, "SeriesTimer with Id {0} not found", id));
}
var service = GetService(timer.ServiceName);
@@ -1743,10 +1752,12 @@ namespace Emby.Server.Implementations.LiveTv
public async Task<TimerInfoDto> GetTimer(string id, CancellationToken cancellationToken)
{
var results = await GetTimers(new TimerQuery
{
Id = id
}, cancellationToken).ConfigureAwait(false);
var results = await GetTimers(
new TimerQuery
{
Id = id
},
cancellationToken).ConfigureAwait(false);
return results.Items.FirstOrDefault(i => string.Equals(i.Id, id, StringComparison.OrdinalIgnoreCase));
}
@@ -1794,10 +1805,7 @@ namespace Emby.Server.Implementations.LiveTv
}
var returnArray = timers
.Select(i =>
{
return i.Item1;
})
.Select(i => i.Item1)
.ToArray();
return new QueryResult<SeriesTimerInfo>
@@ -1968,7 +1976,7 @@ namespace Emby.Server.Implementations.LiveTv
if (service == null)
{
service = _services.First();
service = _services[0];
}
var info = await service.GetNewTimerDefaultsAsync(cancellationToken, programInfo).ConfigureAwait(false);
@@ -1994,9 +2002,7 @@ namespace Emby.Server.Implementations.LiveTv
{
var info = await GetNewTimerDefaultsInternal(cancellationToken).ConfigureAwait(false);
var obj = _tvDtoService.GetSeriesTimerInfoDto(info.Item1, info.Item2, null);
return obj;
return _tvDtoService.GetSeriesTimerInfoDto(info.Item1, info.Item2, null);
}
public async Task<SeriesTimerInfoDto> GetNewTimerDefaults(string programId, CancellationToken cancellationToken)
@@ -2125,6 +2131,7 @@ namespace Emby.Server.Implementations.LiveTv
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private bool _disposed = false;
@@ -2447,8 +2454,7 @@ namespace Emby.Server.Implementations.LiveTv
.SelectMany(i => i.Locations)
.Distinct(StringComparer.OrdinalIgnoreCase)
.Select(i => _libraryManager.FindByPath(i, true))
.Where(i => i != null)
.Where(i => i.IsVisibleStandalone(user))
.Where(i => i != null && i.IsVisibleStandalone(user))
.SelectMany(i => _libraryManager.GetCollectionFolders(i))
.GroupBy(x => x.Id)
.Select(x => x.First())

View File

@@ -37,6 +37,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
private readonly INetworkManager _networkManager;
private readonly IStreamHelper _streamHelper;
private readonly Dictionary<string, DiscoverResponse> _modelCache = new Dictionary<string, DiscoverResponse>();
public HdHomerunHost(
IServerConfigurationManager config,
ILogger<HdHomerunHost> logger,
@@ -114,7 +116,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
}).Cast<ChannelInfo>().ToList();
}
private readonly Dictionary<string, DiscoverResponse> _modelCache = new Dictionary<string, DiscoverResponse>();
private async Task<DiscoverResponse> GetModelInfo(TunerHostInfo info, bool throwAllExceptions, CancellationToken cancellationToken)
{
var cacheKey = info.Id;
@@ -157,10 +158,10 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
{
if (!throwAllExceptions && ex.StatusCode.HasValue && ex.StatusCode.Value == HttpStatusCode.NotFound)
{
var defaultValue = "HDHR";
const string DefaultValue = "HDHR";
var response = new DiscoverResponse
{
ModelNumber = defaultValue
ModelNumber = DefaultValue
};
if (!string.IsNullOrEmpty(cacheKey))
{
@@ -182,12 +183,14 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
{
var model = await GetModelInfo(info, false, cancellationToken).ConfigureAwait(false);
using (var response = await _httpClient.SendAsync(new HttpRequestOptions()
{
Url = string.Format("{0}/tuners.html", GetApiUrl(info)),
CancellationToken = cancellationToken,
BufferContent = false
}, HttpMethod.Get).ConfigureAwait(false))
using (var response = await _httpClient.SendAsync(
new HttpRequestOptions()
{
Url = string.Format(CultureInfo.InvariantCulture, "{0}/tuners.html", GetApiUrl(info)),
CancellationToken = cancellationToken,
BufferContent = false
},
HttpMethod.Get).ConfigureAwait(false))
using (var stream = response.Content)
using (var sr = new StreamReader(stream, System.Text.Encoding.UTF8))
{
@@ -730,7 +733,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
// Need a way to set the Receive timeout on the socket otherwise this might never timeout?
try
{
await udpClient.SendToAsync(discBytes, 0, discBytes.Length, new IPEndPoint(IPAddress.Parse("255.255.255.255"), 65001), cancellationToken);
await udpClient.SendToAsync(discBytes, 0, discBytes.Length, new IPEndPoint(IPAddress.Parse("255.255.255.255"), 65001), cancellationToken).ConfigureAwait(false);
var receiveBuffer = new byte[8192];
while (!cancellationToken.IsCancellationRequested)

View File

@@ -17,5 +17,8 @@
"Genres": "Géneros",
"Folders": "Carpetas",
"Favorites": "Favoritos",
"FailedLoginAttemptWithUserName": "Intento de inicio de sesión fallido de {0}"
"FailedLoginAttemptWithUserName": "Intento de inicio de sesión fallido de {0}",
"HeaderFavoriteSongs": "Canciones Favoritas",
"HeaderFavoriteEpisodes": "Episodios Favoritos",
"HeaderFavoriteArtists": "Artistas Favoritos"
}

View File

@@ -1,7 +1,7 @@
{
"Albums": "Album",
"AuthenticationSucceededWithUserName": "{0} berhasil diautentikasi",
"AppDeviceValues": "Aplikasi: {0}, Alat: {1}",
"AppDeviceValues": "Aplikasi : {0}, Alat : {1}",
"LabelRunningTimeValue": "Waktu berjalan: {0}",
"MessageApplicationUpdatedTo": "Jellyfin Server sudah diperbarui ke {0}",
"MessageApplicationUpdated": "Jellyfin Server sudah diperbarui",
@@ -22,7 +22,7 @@
"HeaderContinueWatching": "Lanjutkan Menonton",
"HeaderCameraUploads": "Unggahan Kamera",
"HeaderAlbumArtists": "Album Artis",
"Genres": "Genre",
"Genres": "Aliran",
"Folders": "Folder",
"Favorites": "Favorit",
"Collections": "Koleksi",

View File

@@ -21,7 +21,7 @@
"HeaderFavoriteAlbums": "Избранные альбомы",
"HeaderFavoriteArtists": "Избранные исполнители",
"HeaderFavoriteEpisodes": "Избранные эпизоды",
"HeaderFavoriteShows": "Избранные передачи",
"HeaderFavoriteShows": "Избранные сериалы",
"HeaderFavoriteSongs": "Избранные композиции",
"HeaderLiveTV": "Эфир",
"HeaderNextUp": "Очередное",

View File

@@ -69,5 +69,8 @@
"AppDeviceValues": "App: {0}, อุปกรณ์: {1}",
"Albums": "อัลบั้ม",
"ScheduledTaskStartedWithName": "{0} เริ่มต้น",
"ScheduledTaskFailedWithName": "{0} ล้มเหลว"
"ScheduledTaskFailedWithName": "{0} ล้มเหลว",
"Songs": "เพลง",
"Shows": "แสดง",
"ServerNameNeedsToBeRestarted": "{0} ต้องการรีสตาร์ท"
}

View File

@@ -152,10 +152,10 @@ namespace Emby.Server.Implementations.Playlists
if (options.ItemIdList.Length > 0)
{
AddToPlaylistInternal(playlist.Id.ToString("N", CultureInfo.InvariantCulture), options.ItemIdList, user, new DtoOptions(false)
await AddToPlaylistInternal(playlist.Id, options.ItemIdList, user, new DtoOptions(false)
{
EnableImages = true
});
}).ConfigureAwait(false);
}
return new PlaylistCreationResult(playlist.Id.ToString("N", CultureInfo.InvariantCulture));
@@ -184,17 +184,17 @@ namespace Emby.Server.Implementations.Playlists
return Playlist.GetPlaylistItems(playlistMediaType, items, user, options);
}
public void AddToPlaylist(string playlistId, ICollection<Guid> itemIds, Guid userId)
public Task AddToPlaylistAsync(Guid playlistId, ICollection<Guid> itemIds, Guid userId)
{
var user = userId.Equals(Guid.Empty) ? null : _userManager.GetUserById(userId);
AddToPlaylistInternal(playlistId, itemIds, user, new DtoOptions(false)
return AddToPlaylistInternal(playlistId, itemIds, user, new DtoOptions(false)
{
EnableImages = true
});
}
private void AddToPlaylistInternal(string playlistId, ICollection<Guid> newItemIds, User user, DtoOptions options)
private async Task AddToPlaylistInternal(Guid playlistId, ICollection<Guid> newItemIds, User user, DtoOptions options)
{
// Retrieve the existing playlist
var playlist = _libraryManager.GetItemById(playlistId) as Playlist
@@ -238,7 +238,7 @@ namespace Emby.Server.Implementations.Playlists
// Update the playlist in the repository
playlist.LinkedChildren = newLinkedChildren;
playlist.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None);
await playlist.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
// Update the playlist on disk
if (playlist.IsFile)
@@ -256,7 +256,7 @@ namespace Emby.Server.Implementations.Playlists
RefreshPriority.High);
}
public void RemoveFromPlaylist(string playlistId, IEnumerable<string> entryIds)
public async Task RemoveFromPlaylistAsync(string playlistId, IEnumerable<string> entryIds)
{
if (!(_libraryManager.GetItemById(playlistId) is Playlist playlist))
{
@@ -273,7 +273,7 @@ namespace Emby.Server.Implementations.Playlists
.Select(i => i.Item1)
.ToArray();
playlist.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None);
await playlist.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
if (playlist.IsFile)
{
@@ -289,7 +289,7 @@ namespace Emby.Server.Implementations.Playlists
RefreshPriority.High);
}
public void MoveItem(string playlistId, string entryId, int newIndex)
public async Task MoveItemAsync(string playlistId, string entryId, int newIndex)
{
if (!(_libraryManager.GetItemById(playlistId) is Playlist playlist))
{
@@ -322,7 +322,7 @@ namespace Emby.Server.Implementations.Playlists
playlist.LinkedChildren = newList.ToArray();
playlist.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None);
await playlist.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
if (playlist.IsFile)
{

View File

@@ -0,0 +1,285 @@
using System;
using System.Collections.Concurrent;
using System.Globalization;
using System.Linq;
using System.Security.Cryptography;
using MediaBrowser.Common;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Authentication;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.QuickConnect;
using MediaBrowser.Controller.Security;
using MediaBrowser.Model.QuickConnect;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.QuickConnect
{
/// <summary>
/// Quick connect implementation.
/// </summary>
public class QuickConnectManager : IQuickConnect, IDisposable
{
private readonly RNGCryptoServiceProvider _rng = new RNGCryptoServiceProvider();
private readonly ConcurrentDictionary<string, QuickConnectResult> _currentRequests = new ConcurrentDictionary<string, QuickConnectResult>();
private readonly IServerConfigurationManager _config;
private readonly ILogger<QuickConnectManager> _logger;
private readonly IAuthenticationRepository _authenticationRepository;
private readonly IAuthorizationContext _authContext;
private readonly IServerApplicationHost _appHost;
/// <summary>
/// Initializes a new instance of the <see cref="QuickConnectManager"/> class.
/// Should only be called at server startup when a singleton is created.
/// </summary>
/// <param name="config">Configuration.</param>
/// <param name="logger">Logger.</param>
/// <param name="appHost">Application host.</param>
/// <param name="authContext">Authentication context.</param>
/// <param name="authenticationRepository">Authentication repository.</param>
public QuickConnectManager(
IServerConfigurationManager config,
ILogger<QuickConnectManager> logger,
IServerApplicationHost appHost,
IAuthorizationContext authContext,
IAuthenticationRepository authenticationRepository)
{
_config = config;
_logger = logger;
_appHost = appHost;
_authContext = authContext;
_authenticationRepository = authenticationRepository;
ReloadConfiguration();
}
/// <inheritdoc/>
public int CodeLength { get; set; } = 6;
/// <inheritdoc/>
public string TokenName { get; set; } = "QuickConnect";
/// <inheritdoc/>
public QuickConnectState State { get; private set; } = QuickConnectState.Unavailable;
/// <inheritdoc/>
public int Timeout { get; set; } = 5;
private DateTime DateActivated { get; set; }
/// <inheritdoc/>
public void AssertActive()
{
if (State != QuickConnectState.Active)
{
throw new ArgumentException("Quick connect is not active on this server");
}
}
/// <inheritdoc/>
public void Activate()
{
DateActivated = DateTime.UtcNow;
SetState(QuickConnectState.Active);
}
/// <inheritdoc/>
public void SetState(QuickConnectState newState)
{
_logger.LogDebug("Changed quick connect state from {State} to {newState}", State, newState);
ExpireRequests(true);
State = newState;
_config.Configuration.QuickConnectAvailable = newState == QuickConnectState.Available || newState == QuickConnectState.Active;
_config.SaveConfiguration();
_logger.LogDebug("Configuration saved");
}
/// <inheritdoc/>
public QuickConnectResult TryConnect()
{
ExpireRequests();
if (State != QuickConnectState.Active)
{
_logger.LogDebug("Refusing quick connect initiation request, current state is {State}", State);
throw new AuthenticationException("Quick connect is not active on this server");
}
var code = GenerateCode();
var result = new QuickConnectResult()
{
Secret = GenerateSecureRandom(),
DateAdded = DateTime.UtcNow,
Code = code
};
_currentRequests[code] = result;
return result;
}
/// <inheritdoc/>
public QuickConnectResult CheckRequestStatus(string secret)
{
ExpireRequests();
AssertActive();
string code = _currentRequests.Where(x => x.Value.Secret == secret).Select(x => x.Value.Code).DefaultIfEmpty(string.Empty).First();
if (!_currentRequests.TryGetValue(code, out QuickConnectResult result))
{
throw new ResourceNotFoundException("Unable to find request with provided secret");
}
return result;
}
/// <inheritdoc/>
public string GenerateCode()
{
Span<byte> raw = stackalloc byte[4];
int min = (int)Math.Pow(10, CodeLength - 1);
int max = (int)Math.Pow(10, CodeLength);
uint scale = uint.MaxValue;
while (scale == uint.MaxValue)
{
_rng.GetBytes(raw);
scale = BitConverter.ToUInt32(raw);
}
int code = (int)(min + ((max - min) * (scale / (double)uint.MaxValue)));
return code.ToString(CultureInfo.InvariantCulture);
}
/// <inheritdoc/>
public bool AuthorizeRequest(Guid userId, string code)
{
ExpireRequests();
AssertActive();
if (!_currentRequests.TryGetValue(code, out QuickConnectResult result))
{
throw new ResourceNotFoundException("Unable to find request");
}
if (result.Authenticated)
{
throw new InvalidOperationException("Request is already authorized");
}
result.Authentication = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture);
// Change the time on the request so it expires one minute into the future. It can't expire immediately as otherwise some clients wouldn't ever see that they have been authenticated.
var added = result.DateAdded ?? DateTime.UtcNow.Subtract(TimeSpan.FromMinutes(Timeout));
result.DateAdded = added.Subtract(TimeSpan.FromMinutes(Timeout - 1));
_authenticationRepository.Create(new AuthenticationInfo
{
AppName = TokenName,
AccessToken = result.Authentication,
DateCreated = DateTime.UtcNow,
DeviceId = _appHost.SystemId,
DeviceName = _appHost.FriendlyName,
AppVersion = _appHost.ApplicationVersionString,
UserId = userId
});
_logger.LogDebug("Authorizing device with code {Code} to login as user {userId}", code, userId);
return true;
}
/// <inheritdoc/>
public int DeleteAllDevices(Guid user)
{
var raw = _authenticationRepository.Get(new AuthenticationInfoQuery()
{
DeviceId = _appHost.SystemId,
UserId = user
});
var tokens = raw.Items.Where(x => x.AppName.StartsWith(TokenName, StringComparison.Ordinal));
var removed = 0;
foreach (var token in tokens)
{
_authenticationRepository.Delete(token);
_logger.LogDebug("Deleted token {AccessToken}", token.AccessToken);
removed++;
}
return removed;
}
/// <summary>
/// Dispose.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Dispose.
/// </summary>
/// <param name="disposing">Dispose unmanaged resources.</param>
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
_rng?.Dispose();
}
}
private string GenerateSecureRandom(int length = 32)
{
Span<byte> bytes = stackalloc byte[length];
_rng.GetBytes(bytes);
return Hex.Encode(bytes);
}
/// <inheritdoc/>
public void ExpireRequests(bool expireAll = false)
{
// Check if quick connect should be deactivated
if (State == QuickConnectState.Active && DateTime.UtcNow > DateActivated.AddMinutes(Timeout) && !expireAll)
{
_logger.LogDebug("Quick connect time expired, deactivating");
SetState(QuickConnectState.Available);
expireAll = true;
}
// Expire stale connection requests
var code = string.Empty;
var values = _currentRequests.Values.ToList();
for (int i = 0; i < values.Count; i++)
{
var added = values[i].DateAdded ?? DateTime.UnixEpoch;
if (DateTime.UtcNow > added.AddMinutes(Timeout) || expireAll)
{
code = values[i].Code;
_logger.LogDebug("Removing expired request {code}", code);
if (!_currentRequests.TryRemove(code, out _))
{
_logger.LogWarning("Request {code} already expired", code);
}
}
}
}
private void ReloadConfiguration()
{
State = _config.Configuration.QuickConnectAvailable ? QuickConnectState.Available : QuickConnectState.Unavailable;
}
}
}

View File

@@ -6,11 +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.IO;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Tasks;
using Microsoft.Extensions.Logging;
@@ -22,37 +21,53 @@ namespace Emby.Server.Implementations.ScheduledTasks
/// </summary>
public class ScheduledTaskWorker : IScheduledTaskWorker
{
public event EventHandler<GenericEventArgs<double>> TaskProgress;
/// <summary>
/// Gets the scheduled task.
/// </summary>
/// <value>The scheduled task.</value>
public IScheduledTask ScheduledTask { get; private set; }
/// <summary>
/// Gets or sets the json serializer.
/// </summary>
/// <value>The json serializer.</value>
private IJsonSerializer JsonSerializer { get; set; }
private readonly IJsonSerializer _jsonSerializer;
/// <summary>
/// Gets or sets the application paths.
/// </summary>
/// <value>The application paths.</value>
private IApplicationPaths ApplicationPaths { get; set; }
private readonly IApplicationPaths _applicationPaths;
/// <summary>
/// Gets the logger.
/// Gets or sets the logger.
/// </summary>
/// <value>The logger.</value>
private ILogger Logger { get; set; }
private readonly ILogger _logger;
/// <summary>
/// Gets the task manager.
/// Gets or sets the task manager.
/// </summary>
/// <value>The task manager.</value>
private ITaskManager TaskManager { get; set; }
private readonly ITaskManager _taskManager;
/// <summary>
/// The _last execution result sync lock.
/// </summary>
private readonly object _lastExecutionResultSyncLock = new object();
private bool _readFromFile = false;
/// <summary>
/// The _last execution result.
/// </summary>
private TaskResult _lastExecutionResult;
private Task _currentTask;
/// <summary>
/// The _triggers.
/// </summary>
private Tuple<TaskTriggerInfo, ITaskTrigger>[] _triggers;
/// <summary>
/// The _id.
/// </summary>
private string _id;
/// <summary>
/// Initializes a new instance of the <see cref="ScheduledTaskWorker" /> class.
@@ -71,7 +86,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
/// or
/// jsonSerializer
/// or
/// logger
/// logger.
/// </exception>
public ScheduledTaskWorker(IScheduledTask scheduledTask, IApplicationPaths applicationPaths, ITaskManager taskManager, IJsonSerializer jsonSerializer, ILogger logger)
{
@@ -101,23 +116,22 @@ namespace Emby.Server.Implementations.ScheduledTasks
}
ScheduledTask = scheduledTask;
ApplicationPaths = applicationPaths;
TaskManager = taskManager;
JsonSerializer = jsonSerializer;
Logger = logger;
_applicationPaths = applicationPaths;
_taskManager = taskManager;
_jsonSerializer = jsonSerializer;
_logger = logger;
InitTriggerEvents();
}
private bool _readFromFile = false;
public event EventHandler<GenericEventArgs<double>> TaskProgress;
/// <summary>
/// The _last execution result.
/// Gets the scheduled task.
/// </summary>
private TaskResult _lastExecutionResult;
/// <summary>
/// The _last execution result sync lock.
/// </summary>
private readonly object _lastExecutionResultSyncLock = new object();
/// <value>The scheduled task.</value>
public IScheduledTask ScheduledTask { get; private set; }
/// <summary>
/// Gets the last execution result.
/// </summary>
@@ -136,11 +150,11 @@ namespace Emby.Server.Implementations.ScheduledTasks
{
try
{
_lastExecutionResult = JsonSerializer.DeserializeFromFile<TaskResult>(path);
_lastExecutionResult = _jsonSerializer.DeserializeFromFile<TaskResult>(path);
}
catch (Exception ex)
{
Logger.LogError(ex, "Error deserializing {File}", path);
_logger.LogError(ex, "Error deserializing {File}", path);
}
}
@@ -160,7 +174,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
lock (_lastExecutionResultSyncLock)
{
JsonSerializer.SerializeToFile(value, path);
_jsonSerializer.SerializeToFile(value, path);
}
}
}
@@ -184,7 +198,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
public string Category => ScheduledTask.Category;
/// <summary>
/// Gets the current cancellation token.
/// Gets or sets the current cancellation token.
/// </summary>
/// <value>The current cancellation token source.</value>
private CancellationTokenSource CurrentCancellationTokenSource { get; set; }
@@ -221,12 +235,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
public double? CurrentProgress { get; private set; }
/// <summary>
/// The _triggers.
/// </summary>
private Tuple<TaskTriggerInfo, ITaskTrigger>[] _triggers;
/// <summary>
/// Gets the triggers that define when the task will run.
/// Gets or sets the triggers that define when the task will run.
/// </summary>
/// <value>The triggers.</value>
private Tuple<TaskTriggerInfo, ITaskTrigger>[] InternalTriggers
@@ -255,7 +264,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
/// Gets the triggers that define when the task will run.
/// </summary>
/// <value>The triggers.</value>
/// <exception cref="ArgumentNullException">value</exception>
/// <exception cref="ArgumentNullException"><c>value</c> is <c>null</c>.</exception>
public TaskTriggerInfo[] Triggers
{
get
@@ -280,11 +289,6 @@ namespace Emby.Server.Implementations.ScheduledTasks
}
}
/// <summary>
/// The _id.
/// </summary>
private string _id;
/// <summary>
/// Gets the unique id.
/// </summary>
@@ -325,9 +329,9 @@ namespace Emby.Server.Implementations.ScheduledTasks
trigger.Stop();
trigger.Triggered -= trigger_Triggered;
trigger.Triggered += trigger_Triggered;
trigger.Start(LastExecutionResult, Logger, Name, isApplicationStartup);
trigger.Triggered -= OnTriggerTriggered;
trigger.Triggered += OnTriggerTriggered;
trigger.Start(LastExecutionResult, _logger, Name, isApplicationStartup);
}
}
@@ -336,7 +340,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param>
async void trigger_Triggered(object sender, EventArgs e)
private async void OnTriggerTriggered(object sender, EventArgs e)
{
var trigger = (ITaskTrigger)sender;
@@ -347,19 +351,17 @@ namespace Emby.Server.Implementations.ScheduledTasks
return;
}
Logger.LogInformation("{0} fired for task: {1}", trigger.GetType().Name, Name);
_logger.LogInformation("{0} fired for task: {1}", trigger.GetType().Name, Name);
trigger.Stop();
TaskManager.QueueScheduledTask(ScheduledTask, trigger.TaskOptions);
_taskManager.QueueScheduledTask(ScheduledTask, trigger.TaskOptions);
await Task.Delay(1000).ConfigureAwait(false);
trigger.Start(LastExecutionResult, Logger, Name, false);
trigger.Start(LastExecutionResult, _logger, Name, false);
}
private Task _currentTask;
/// <summary>
/// Executes the task.
/// </summary>
@@ -395,9 +397,9 @@ namespace Emby.Server.Implementations.ScheduledTasks
CurrentCancellationTokenSource = new CancellationTokenSource();
Logger.LogInformation("Executing {0}", Name);
_logger.LogInformation("Executing {0}", Name);
((TaskManager)TaskManager).OnTaskExecuting(this);
((TaskManager)_taskManager).OnTaskExecuting(this);
progress.ProgressChanged += OnProgressChanged;
@@ -423,7 +425,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
}
catch (Exception ex)
{
Logger.LogError(ex, "Error");
_logger.LogError(ex, "Error");
failureException = ex;
@@ -476,7 +478,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
{
if (State == TaskState.Running)
{
Logger.LogInformation("Attempting to cancel Scheduled Task {0}", Name);
_logger.LogInformation("Attempting to cancel Scheduled Task {0}", Name);
CurrentCancellationTokenSource.Cancel();
}
}
@@ -487,7 +489,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
/// <returns>System.String.</returns>
private string GetScheduledTasksConfigurationDirectory()
{
return Path.Combine(ApplicationPaths.ConfigurationDirectoryPath, "ScheduledTasks");
return Path.Combine(_applicationPaths.ConfigurationDirectoryPath, "ScheduledTasks");
}
/// <summary>
@@ -496,7 +498,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
/// <returns>System.String.</returns>
private string GetScheduledTasksDataDirectory()
{
return Path.Combine(ApplicationPaths.DataPath, "ScheduledTasks");
return Path.Combine(_applicationPaths.DataPath, "ScheduledTasks");
}
/// <summary>
@@ -535,7 +537,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
TaskTriggerInfo[] list = null;
if (File.Exists(path))
{
list = JsonSerializer.DeserializeFromFile<TaskTriggerInfo[]>(path);
list = _jsonSerializer.DeserializeFromFile<TaskTriggerInfo[]>(path);
}
// Return defaults if file doesn't exist.
@@ -571,7 +573,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
Directory.CreateDirectory(Path.GetDirectoryName(path));
JsonSerializer.SerializeToFile(triggers, path);
_jsonSerializer.SerializeToFile(triggers, path);
}
/// <summary>
@@ -585,7 +587,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
{
var elapsedTime = endTime - startTime;
Logger.LogInformation("{0} {1} after {2} minute(s) and {3} seconds", Name, status, Math.Truncate(elapsedTime.TotalMinutes), elapsedTime.Seconds);
_logger.LogInformation("{0} {1} after {2} minute(s) and {3} seconds", Name, status, Math.Truncate(elapsedTime.TotalMinutes), elapsedTime.Seconds);
var result = new TaskResult
{
@@ -606,7 +608,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
LastExecutionResult = result;
((TaskManager)TaskManager).OnTaskCompleted(this, result);
((TaskManager)_taskManager).OnTaskCompleted(this, result);
}
/// <summary>
@@ -615,6 +617,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
@@ -635,12 +638,12 @@ namespace Emby.Server.Implementations.ScheduledTasks
{
try
{
Logger.LogInformation(Name + ": Cancelling");
_logger.LogInformation(Name + ": Cancelling");
token.Cancel();
}
catch (Exception ex)
{
Logger.LogError(ex, "Error calling CancellationToken.Cancel();");
_logger.LogError(ex, "Error calling CancellationToken.Cancel();");
}
}
@@ -649,21 +652,21 @@ namespace Emby.Server.Implementations.ScheduledTasks
{
try
{
Logger.LogInformation(Name + ": Waiting on Task");
_logger.LogInformation(Name + ": Waiting on Task");
var exited = Task.WaitAll(new[] { task }, 2000);
if (exited)
{
Logger.LogInformation(Name + ": Task exited");
_logger.LogInformation(Name + ": Task exited");
}
else
{
Logger.LogInformation(Name + ": Timed out waiting for task to stop");
_logger.LogInformation(Name + ": Timed out waiting for task to stop");
}
}
catch (Exception ex)
{
Logger.LogError(ex, "Error calling Task.WaitAll();");
_logger.LogError(ex, "Error calling Task.WaitAll();");
}
}
@@ -671,12 +674,12 @@ namespace Emby.Server.Implementations.ScheduledTasks
{
try
{
Logger.LogDebug(Name + ": Disposing CancellationToken");
_logger.LogDebug(Name + ": Disposing CancellationToken");
token.Dispose();
}
catch (Exception ex)
{
Logger.LogError(ex, "Error calling CancellationToken.Dispose();");
_logger.LogError(ex, "Error calling CancellationToken.Dispose();");
}
}
@@ -692,8 +695,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
/// </summary>
/// <param name="info">The info.</param>
/// <returns>BaseTaskTrigger.</returns>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentException">Invalid trigger type: + info.Type</exception>
/// <exception cref="ArgumentException">Invalid trigger type: + info.Type.</exception>
private ITaskTrigger GetTrigger(TaskTriggerInfo info)
{
var options = new TaskOptions
@@ -765,7 +767,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
foreach (var triggerInfo in InternalTriggers)
{
var trigger = triggerInfo.Item2;
trigger.Triggered -= trigger_Triggered;
trigger.Triggered -= OnTriggerTriggered;
trigger.Stop();
}
}

View File

@@ -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;
@@ -207,6 +207,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>

View File

@@ -1,12 +1,13 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Tasks;
using MediaBrowser.Model.Globalization;
namespace Emby.Server.Implementations.ScheduledTasks.Tasks
{
@@ -15,12 +16,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
/// </summary>
public class DeleteLogFileTask : IScheduledTask, IConfigurableScheduledTask
{
/// <summary>
/// Gets or sets the configuration manager.
/// </summary>
/// <value>The configuration manager.</value>
private IConfigurationManager ConfigurationManager { get; set; }
private readonly IConfigurationManager _configurationManager;
private readonly IFileSystem _fileSystem;
private readonly ILocalizationManager _localization;
@@ -32,18 +28,43 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
/// <param name="localization">The localization manager.</param>
public DeleteLogFileTask(IConfigurationManager configurationManager, IFileSystem fileSystem, ILocalizationManager localization)
{
ConfigurationManager = configurationManager;
_configurationManager = configurationManager;
_fileSystem = fileSystem;
_localization = localization;
}
/// <inheritdoc />
public string Name => _localization.GetLocalizedString("TaskCleanLogs");
/// <inheritdoc />
public string Description => string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("TaskCleanLogsDescription"),
_configurationManager.CommonConfiguration.LogFileRetentionDays);
/// <inheritdoc />
public string Category => _localization.GetLocalizedString("TasksMaintenanceCategory");
/// <inheritdoc />
public string Key => "CleanLogFiles";
/// <inheritdoc />
public bool IsHidden => false;
/// <inheritdoc />
public bool IsEnabled => true;
/// <inheritdoc />
public bool IsLogged => true;
/// <summary>
/// Creates the triggers that define when the task will run.
/// </summary>
/// <returns>IEnumerable{BaseTaskTrigger}.</returns>
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
{
return new[] {
return new[]
{
new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(24).Ticks}
};
}
@@ -57,10 +78,10 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
public Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
{
// Delete log files more than n days old
var minDateModified = DateTime.UtcNow.AddDays(-ConfigurationManager.CommonConfiguration.LogFileRetentionDays);
var minDateModified = DateTime.UtcNow.AddDays(-_configurationManager.CommonConfiguration.LogFileRetentionDays);
// Only delete the .txt log files, the *.log files created by serilog get managed by itself
var filesToDelete = _fileSystem.GetFiles(ConfigurationManager.CommonApplicationPaths.LogDirectoryPath, new[] { ".txt" }, true, true)
var filesToDelete = _fileSystem.GetFiles(_configurationManager.CommonApplicationPaths.LogDirectoryPath, new[] { ".txt" }, true, true)
.Where(f => _fileSystem.GetLastWriteTimeUtc(f) < minDateModified)
.ToList();
@@ -83,26 +104,5 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
return Task.CompletedTask;
}
/// <inheritdoc />
public string Name => _localization.GetLocalizedString("TaskCleanLogs");
/// <inheritdoc />
public string Description => string.Format(_localization.GetLocalizedString("TaskCleanLogsDescription"), ConfigurationManager.CommonConfiguration.LogFileRetentionDays);
/// <inheritdoc />
public string Category => _localization.GetLocalizedString("TasksMaintenanceCategory");
/// <inheritdoc />
public string Key => "CleanLogFiles";
/// <inheritdoc />
public bool IsHidden => false;
/// <inheritdoc />
public bool IsEnabled => true;
/// <inheritdoc />
public bool IsLogged => true;
}
}

View File

@@ -80,8 +80,8 @@ namespace Emby.Server.Implementations.Services
public static List<string> GetFirstMatchWildCardHashKeys(string[] pathPartsForMatching)
{
const string hashPrefix = WildCard + PathSeperator;
return GetPotentialMatchesWithPrefix(hashPrefix, pathPartsForMatching);
const string HashPrefix = WildCard + PathSeperator;
return GetPotentialMatchesWithPrefix(HashPrefix, pathPartsForMatching);
}
private static List<string> GetPotentialMatchesWithPrefix(string hashPrefix, string[] pathPartsForMatching)
@@ -92,7 +92,7 @@ namespace Emby.Server.Implementations.Services
{
list.Add(hashPrefix + part);
if (part.IndexOf(ComponentSeperator) == -1)
if (part.IndexOf(ComponentSeperator, StringComparison.Ordinal) == -1)
{
continue;
}
@@ -130,7 +130,7 @@ namespace Emby.Server.Implementations.Services
}
if (component.IndexOf(VariablePrefix, StringComparison.OrdinalIgnoreCase) != -1
&& component.IndexOf(ComponentSeperator) != -1)
&& component.IndexOf(ComponentSeperator, StringComparison.Ordinal) != -1)
{
hasSeparators.Add(true);
componentsList.AddRange(component.Split(ComponentSeperator));

View File

@@ -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)
@@ -1424,6 +1429,24 @@ namespace Emby.Server.Implementations.Session
return AuthenticateNewSessionInternal(request, false);
}
public Task<AuthenticationResult> AuthenticateQuickConnect(AuthenticationRequest request, string token)
{
var result = _authRepo.Get(new AuthenticationInfoQuery()
{
AccessToken = token,
DeviceId = _appHost.SystemId,
Limit = 1
});
if (result.TotalRecordCount == 0)
{
throw new SecurityException("Unknown quick connect token");
}
request.UserId = result.Items[0].UserId;
return AuthenticateNewSessionInternal(request, false);
}
private async Task<AuthenticationResult> AuthenticateNewSessionInternal(AuthenticationRequest request, bool enforcePassword)
{
CheckDisposed();

View File

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