aboutsummaryrefslogtreecommitdiff
path: root/Emby.Server.Implementations
diff options
context:
space:
mode:
Diffstat (limited to 'Emby.Server.Implementations')
-rw-r--r--Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs94
-rw-r--r--Emby.Server.Implementations/Activity/ActivityManager.cs26
-rw-r--r--Emby.Server.Implementations/Activity/ActivityRepository.cs196
-rw-r--r--Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs12
-rw-r--r--Emby.Server.Implementations/AppBase/ConfigurationHelper.cs26
-rw-r--r--Emby.Server.Implementations/ApplicationHost.cs725
-rw-r--r--Emby.Server.Implementations/Archiving/ZipClient.cs130
-rw-r--r--Emby.Server.Implementations/Branding/BrandingConfigurationFactory.cs7
-rw-r--r--Emby.Server.Implementations/Browser/BrowserLauncher.cs35
-rw-r--r--Emby.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs16
-rw-r--r--Emby.Server.Implementations/Channels/ChannelImageProvider.cs19
-rw-r--r--Emby.Server.Implementations/Channels/ChannelManager.cs332
-rw-r--r--Emby.Server.Implementations/Channels/ChannelPostScanTask.cs22
-rw-r--r--Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs31
-rw-r--r--Emby.Server.Implementations/Collections/CollectionImageProvider.cs25
-rw-r--r--Emby.Server.Implementations/Collections/CollectionManager.cs58
-rw-r--r--Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs19
-rw-r--r--Emby.Server.Implementations/ConfigurationOptions.cs18
-rw-r--r--Emby.Server.Implementations/Cryptography/CryptographyProvider.cs41
-rw-r--r--Emby.Server.Implementations/Data/BaseSqliteRepository.cs1
-rw-r--r--Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs1
-rw-r--r--Emby.Server.Implementations/Data/ManagedConnection.cs1
-rw-r--r--Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs1
-rw-r--r--Emby.Server.Implementations/Data/SqliteExtensions.cs30
-rw-r--r--Emby.Server.Implementations/Data/SqliteItemRepository.cs507
-rw-r--r--Emby.Server.Implementations/Data/SqliteUserDataRepository.cs1
-rw-r--r--Emby.Server.Implementations/Data/SqliteUserRepository.cs3
-rw-r--r--Emby.Server.Implementations/Devices/DeviceId.cs1
-rw-r--r--Emby.Server.Implementations/Devices/DeviceManager.cs7
-rw-r--r--Emby.Server.Implementations/Diagnostics/CommonProcess.cs153
-rw-r--r--Emby.Server.Implementations/Diagnostics/ProcessFactory.cs15
-rw-r--r--Emby.Server.Implementations/Dto/DtoService.cs50
-rw-r--r--Emby.Server.Implementations/Emby.Server.Implementations.csproj18
-rw-r--r--Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs94
-rw-r--r--Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs1
-rw-r--r--Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs1
-rw-r--r--Emby.Server.Implementations/EntryPoints/StartupWizard.cs50
-rw-r--r--Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs1
-rw-r--r--Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs24
-rw-r--r--Emby.Server.Implementations/HttpServer/FileWriter.cs3
-rw-r--r--Emby.Server.Implementations/HttpServer/HttpListenerHost.cs150
-rw-r--r--Emby.Server.Implementations/HttpServer/HttpResultFactory.cs15
-rw-r--r--Emby.Server.Implementations/HttpServer/IHttpListener.cs1
-rw-r--r--Emby.Server.Implementations/HttpServer/RangeRequestWriter.cs1
-rw-r--r--Emby.Server.Implementations/HttpServer/ResponseFilter.cs4
-rw-r--r--Emby.Server.Implementations/HttpServer/Security/AuthService.cs38
-rw-r--r--Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs1
-rw-r--r--Emby.Server.Implementations/HttpServer/Security/SessionContext.cs1
-rw-r--r--Emby.Server.Implementations/IO/ExtendedFileSystemInfo.cs1
-rw-r--r--Emby.Server.Implementations/IO/FileRefresher.cs1
-rw-r--r--Emby.Server.Implementations/IO/LibraryMonitor.cs59
-rw-r--r--Emby.Server.Implementations/IO/ManagedFileSystem.cs11
-rw-r--r--Emby.Server.Implementations/IO/MbLinkShortcutHandler.cs1
-rw-r--r--Emby.Server.Implementations/IO/StreamHelper.cs1
-rw-r--r--Emby.Server.Implementations/IStartupOptions.cs17
-rw-r--r--Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs1
-rw-r--r--Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs2
-rw-r--r--Emby.Server.Implementations/Library/ExclusiveLiveStream.cs1
-rw-r--r--Emby.Server.Implementations/Library/LibraryManager.cs236
-rw-r--r--Emby.Server.Implementations/Library/LiveStreamHelper.cs1
-rw-r--r--Emby.Server.Implementations/Library/MediaSourceManager.cs23
-rw-r--r--Emby.Server.Implementations/Library/MediaStreamSelector.cs7
-rw-r--r--Emby.Server.Implementations/Library/MusicManager.cs1
-rw-r--r--Emby.Server.Implementations/Library/PathExtensions.cs24
-rw-r--r--Emby.Server.Implementations/Library/ResolverHelper.cs4
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs1
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs1
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs1
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs1
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs2
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/SpecialFolderResolver.cs1
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs2
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/VideoResolver.cs1
-rw-r--r--Emby.Server.Implementations/Library/SearchEngine.cs8
-rw-r--r--Emby.Server.Implementations/Library/UserDataManager.cs38
-rw-r--r--Emby.Server.Implementations/Library/UserManager.cs65
-rw-r--r--Emby.Server.Implementations/Library/UserViewManager.cs1
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs3
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs37
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs73
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs1
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/EpgChannelData.cs1
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/IRecorder.cs1
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs1
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs1
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs1
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs1
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs5
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs3
-rw-r--r--Emby.Server.Implementations/LiveTv/LiveTvConfigurationFactory.cs1
-rw-r--r--Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs27
-rw-r--r--Emby.Server.Implementations/LiveTv/LiveTvManager.cs48
-rw-r--r--Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs1
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs1
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs1
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs1
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs5
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs3
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs1
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs1
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs5
-rw-r--r--Emby.Server.Implementations/Localization/Core/af.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/ar.json39
-rw-r--r--Emby.Server.Implementations/Localization/Core/bg-BG.json53
-rw-r--r--Emby.Server.Implementations/Localization/Core/bn.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/ca.json11
-rw-r--r--Emby.Server.Implementations/Localization/Core/cs.json27
-rw-r--r--Emby.Server.Implementations/Localization/Core/da.json31
-rw-r--r--Emby.Server.Implementations/Localization/Core/de.json33
-rw-r--r--Emby.Server.Implementations/Localization/Core/el.json27
-rw-r--r--Emby.Server.Implementations/Localization/Core/en-GB.json25
-rw-r--r--Emby.Server.Implementations/Localization/Core/en-US.json25
-rw-r--r--Emby.Server.Implementations/Localization/Core/es-AR.json67
-rw-r--r--Emby.Server.Implementations/Localization/Core/es-MX.json25
-rw-r--r--Emby.Server.Implementations/Localization/Core/es.json25
-rw-r--r--Emby.Server.Implementations/Localization/Core/es_DO.json2
-rw-r--r--Emby.Server.Implementations/Localization/Core/fa.json109
-rw-r--r--Emby.Server.Implementations/Localization/Core/fi.json65
-rw-r--r--Emby.Server.Implementations/Localization/Core/fil.json11
-rw-r--r--Emby.Server.Implementations/Localization/Core/fr-CA.json5
-rw-r--r--Emby.Server.Implementations/Localization/Core/fr.json35
-rw-r--r--Emby.Server.Implementations/Localization/Core/gsw.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/he.json12
-rw-r--r--Emby.Server.Implementations/Localization/Core/hr.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/hu.json29
-rw-r--r--Emby.Server.Implementations/Localization/Core/id.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/is.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/it.json29
-rw-r--r--Emby.Server.Implementations/Localization/Core/ja.json25
-rw-r--r--Emby.Server.Implementations/Localization/Core/kk.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/ko.json25
-rw-r--r--Emby.Server.Implementations/Localization/Core/lt-LT.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/lv.json25
-rw-r--r--Emby.Server.Implementations/Localization/Core/mk.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/mr.json61
-rw-r--r--Emby.Server.Implementations/Localization/Core/ms.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/nb.json8
-rw-r--r--Emby.Server.Implementations/Localization/Core/nl.json31
-rw-r--r--Emby.Server.Implementations/Localization/Core/nn.json22
-rw-r--r--Emby.Server.Implementations/Localization/Core/pl.json25
-rw-r--r--Emby.Server.Implementations/Localization/Core/pt-BR.json25
-rw-r--r--Emby.Server.Implementations/Localization/Core/pt-PT.json27
-rw-r--r--Emby.Server.Implementations/Localization/Core/pt.json15
-rw-r--r--Emby.Server.Implementations/Localization/Core/ro.json25
-rw-r--r--Emby.Server.Implementations/Localization/Core/ru.json67
-rw-r--r--Emby.Server.Implementations/Localization/Core/sk.json25
-rw-r--r--Emby.Server.Implementations/Localization/Core/sl-SI.json3
-rw-r--r--Emby.Server.Implementations/Localization/Core/sr.json27
-rw-r--r--Emby.Server.Implementations/Localization/Core/sv.json29
-rw-r--r--Emby.Server.Implementations/Localization/Core/tr.json31
-rw-r--r--Emby.Server.Implementations/Localization/Core/ur_PK.json117
-rw-r--r--Emby.Server.Implementations/Localization/Core/zh-CN.json29
-rw-r--r--Emby.Server.Implementations/Localization/Core/zh-HK.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/zh-TW.json37
-rw-r--r--Emby.Server.Implementations/Localization/LocalizationManager.cs3
-rw-r--r--Emby.Server.Implementations/MediaEncoder/EncodingManager.cs40
-rw-r--r--Emby.Server.Implementations/Networking/NetworkManager.cs2
-rw-r--r--Emby.Server.Implementations/Playlists/PlaylistManager.cs57
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/TaskManager.cs20
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs18
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs12
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs11
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs12
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/PeopleValidationTask.cs11
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs16
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/RefreshMediaLibraryTask.cs14
-rw-r--r--Emby.Server.Implementations/Security/AuthenticationRepository.cs4
-rw-r--r--Emby.Server.Implementations/ServerApplicationPaths.cs12
-rw-r--r--Emby.Server.Implementations/Services/ServiceController.cs20
-rw-r--r--Emby.Server.Implementations/Services/StringMapTypeDeserializer.cs16
-rw-r--r--Emby.Server.Implementations/Services/SwaggerService.cs4
-rw-r--r--Emby.Server.Implementations/Services/UrlExtensions.cs20
-rw-r--r--Emby.Server.Implementations/Session/SessionManager.cs22
-rw-r--r--Emby.Server.Implementations/SocketSharp/WebSocketSharpRequest.cs16
-rw-r--r--Emby.Server.Implementations/Updates/InstallationManager.cs141
175 files changed, 2951 insertions, 2637 deletions
diff --git a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs
index 4664eadd3..4685a03ac 100644
--- a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs
+++ b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs
@@ -1,6 +1,3 @@
-#pragma warning disable CS1591
-#pragma warning disable SA1600
-
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -9,7 +6,6 @@ using System.Text;
using System.Threading.Tasks;
using MediaBrowser.Common.Plugins;
using MediaBrowser.Common.Updates;
-using MediaBrowser.Controller;
using MediaBrowser.Controller.Authentication;
using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Entities;
@@ -29,6 +25,9 @@ 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 _logger;
@@ -44,16 +43,15 @@ namespace Emby.Server.Implementations.Activity
/// <summary>
/// Initializes a new instance of the <see cref="ActivityLogEntryPoint"/> class.
/// </summary>
- /// <param name="logger"></param>
- /// <param name="sessionManager"></param>
- /// <param name="deviceManager"></param>
- /// <param name="taskManager"></param>
- /// <param name="activityManager"></param>
- /// <param name="localization"></param>
- /// <param name="installationManager"></param>
- /// <param name="subManager"></param>
- /// <param name="userManager"></param>
- /// <param name="appHost"></param>
+ /// <param name="logger">The logger.</param>
+ /// <param name="sessionManager">The session manager.</param>
+ /// <param name="deviceManager">The device 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,
@@ -76,6 +74,7 @@ namespace Emby.Server.Implementations.Activity
_userManager = userManager;
}
+ /// <inheritdoc />
public Task RunAsync()
{
_taskManager.TaskCompleted += OnTaskCompleted;
@@ -138,7 +137,7 @@ namespace Emby.Server.Implementations.Activity
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("SubtitleDownloadFailureFromForItem"),
e.Provider,
- Emby.Notifications.NotificationEntryPoint.GetItemName(e.Item)),
+ Notifications.NotificationEntryPoint.GetItemName(e.Item)),
Type = "SubtitleDownloadFailure",
ItemId = e.Item.Id.ToString("N", CultureInfo.InvariantCulture),
ShortOverview = e.Exception.Message
@@ -170,7 +169,12 @@ namespace Emby.Server.Implementations.Activity
CreateLogEntry(new ActivityLogEntry
{
- Name = string.Format(_localization.GetLocalizedString("UserStoppedPlayingItemWithValues"), user.Name, GetItemName(item), e.DeviceName),
+ Name = string.Format(
+ CultureInfo.InvariantCulture,
+ _localization.GetLocalizedString("UserStoppedPlayingItemWithValues"),
+ user.Name,
+ GetItemName(item),
+ e.DeviceName),
Type = GetPlaybackStoppedNotificationType(item.MediaType),
UserId = user.Id
});
@@ -261,31 +265,20 @@ namespace Emby.Server.Implementations.Activity
private void OnSessionEnded(object sender, SessionEventArgs e)
{
- string name;
var session = e.SessionInfo;
if (string.IsNullOrEmpty(session.UserName))
{
- name = string.Format(
- CultureInfo.InvariantCulture,
- _localization.GetLocalizedString("DeviceOfflineWithName"),
- session.DeviceName);
-
- // Causing too much spam for now
return;
}
- else
+
+ CreateLogEntry(new ActivityLogEntry
{
- name = string.Format(
+ Name = string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("UserOfflineFromDevice"),
session.UserName,
- session.DeviceName);
- }
-
- CreateLogEntry(new ActivityLogEntry
- {
- Name = name,
+ session.DeviceName),
Type = "SessionEnded",
ShortOverview = string.Format(
CultureInfo.InvariantCulture,
@@ -384,31 +377,20 @@ namespace Emby.Server.Implementations.Activity
private void OnSessionStarted(object sender, SessionEventArgs e)
{
- string name;
var session = e.SessionInfo;
if (string.IsNullOrEmpty(session.UserName))
{
- name = string.Format(
- CultureInfo.InvariantCulture,
- _localization.GetLocalizedString("DeviceOnlineWithName"),
- session.DeviceName);
-
- // Causing too much spam for now
return;
}
- else
+
+ CreateLogEntry(new ActivityLogEntry
{
- name = string.Format(
+ Name = string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("UserOnlineFromDevice"),
session.UserName,
- session.DeviceName);
- }
-
- CreateLogEntry(new ActivityLogEntry
- {
- Name = name,
+ session.DeviceName),
Type = "SessionStarted",
ShortOverview = string.Format(
CultureInfo.InvariantCulture,
@@ -418,7 +400,7 @@ namespace Emby.Server.Implementations.Activity
});
}
- private void OnPluginUpdated(object sender, GenericEventArgs<(IPlugin, PackageVersionInfo)> e)
+ private void OnPluginUpdated(object sender, GenericEventArgs<(IPlugin, VersionInfo)> e)
{
CreateLogEntry(new ActivityLogEntry
{
@@ -430,8 +412,8 @@ namespace Emby.Server.Implementations.Activity
ShortOverview = string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("VersionNumber"),
- e.Argument.Item2.versionStr),
- Overview = e.Argument.Item2.description
+ e.Argument.Item2.version),
+ Overview = e.Argument.Item2.changelog
});
}
@@ -447,7 +429,7 @@ namespace Emby.Server.Implementations.Activity
});
}
- private void OnPluginInstalled(object sender, GenericEventArgs<PackageVersionInfo> e)
+ private void OnPluginInstalled(object sender, GenericEventArgs<VersionInfo> e)
{
CreateLogEntry(new ActivityLogEntry
{
@@ -459,7 +441,7 @@ namespace Emby.Server.Implementations.Activity
ShortOverview = string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("VersionNumber"),
- e.Argument.versionStr)
+ e.Argument.version)
});
}
@@ -487,8 +469,8 @@ namespace Emby.Server.Implementations.Activity
var result = e.Result;
var task = e.Task;
- var activityTask = task.ScheduledTask as IConfigurableScheduledTask;
- if (activityTask != null && !activityTask.IsLogged)
+ if (task.ScheduledTask is IConfigurableScheduledTask activityTask
+ && !activityTask.IsLogged)
{
return;
}
@@ -562,7 +544,7 @@ namespace Emby.Server.Implementations.Activity
/// <summary>
/// Constructs a user-friendly string for this TimeSpan instance.
/// </summary>
- public static string ToUserFriendlyString(TimeSpan span)
+ private static string ToUserFriendlyString(TimeSpan span)
{
const int DaysInYear = 365;
const int DaysInMonth = 30;
@@ -576,7 +558,7 @@ namespace Emby.Server.Implementations.Activity
{
int years = days / DaysInYear;
values.Add(CreateValueString(years, "year"));
- days = days % DaysInYear;
+ days %= DaysInYear;
}
// Number of months
@@ -584,7 +566,7 @@ namespace Emby.Server.Implementations.Activity
{
int months = days / DaysInMonth;
values.Add(CreateValueString(months, "month"));
- days = days % DaysInMonth;
+ days %= DaysInMonth;
}
// Number of days
diff --git a/Emby.Server.Implementations/Activity/ActivityManager.cs b/Emby.Server.Implementations/Activity/ActivityManager.cs
index 6712c4782..81bebae3d 100644
--- a/Emby.Server.Implementations/Activity/ActivityManager.cs
+++ b/Emby.Server.Implementations/Activity/ActivityManager.cs
@@ -1,33 +1,33 @@
-#pragma warning disable CS1591
-#pragma warning disable SA1600
-
using System;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Activity;
using MediaBrowser.Model.Events;
using MediaBrowser.Model.Querying;
-using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.Activity
{
+ /// <summary>
+ /// The activity log manager.
+ /// </summary>
public class ActivityManager : IActivityManager
{
- public event EventHandler<GenericEventArgs<ActivityLogEntry>> EntryCreated;
-
private readonly IActivityRepository _repo;
- private readonly ILogger _logger;
private readonly IUserManager _userManager;
- public ActivityManager(
- ILoggerFactory loggerFactory,
- IActivityRepository repo,
- IUserManager userManager)
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ActivityManager"/> class.
+ /// </summary>
+ /// <param name="repo">The activity repository.</param>
+ /// <param name="userManager">The user manager.</param>
+ public ActivityManager(IActivityRepository repo, IUserManager userManager)
{
- _logger = loggerFactory.CreateLogger(nameof(ActivityManager));
_repo = repo;
_userManager = userManager;
}
+ /// <inheritdoc />
+ public event EventHandler<GenericEventArgs<ActivityLogEntry>> EntryCreated;
+
public void Create(ActivityLogEntry entry)
{
entry.Date = DateTime.UtcNow;
@@ -37,6 +37,7 @@ namespace Emby.Server.Implementations.Activity
EntryCreated?.Invoke(this, new GenericEventArgs<ActivityLogEntry>(entry));
}
+ /// <inheritdoc />
public QueryResult<ActivityLogEntry> GetActivityLogEntries(DateTime? minDate, bool? hasUserId, int? startIndex, int? limit)
{
var result = _repo.GetActivityLogEntries(minDate, hasUserId, startIndex, limit);
@@ -60,6 +61,7 @@ namespace Emby.Server.Implementations.Activity
return result;
}
+ /// <inheritdoc />
public QueryResult<ActivityLogEntry> GetActivityLogEntries(DateTime? minDate, int? startIndex, int? limit)
{
return GetActivityLogEntries(minDate, null, startIndex, limit);
diff --git a/Emby.Server.Implementations/Activity/ActivityRepository.cs b/Emby.Server.Implementations/Activity/ActivityRepository.cs
index 633343bb6..22796ba3f 100644
--- a/Emby.Server.Implementations/Activity/ActivityRepository.cs
+++ b/Emby.Server.Implementations/Activity/ActivityRepository.cs
@@ -1,6 +1,3 @@
-#pragma warning disable CS1591
-#pragma warning disable SA1600
-
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -16,18 +13,31 @@ using SQLitePCL.pretty;
namespace Emby.Server.Implementations.Activity
{
+ /// <summary>
+ /// The activity log repository.
+ /// </summary>
public class ActivityRepository : BaseSqliteRepository, IActivityRepository
{
- private static readonly CultureInfo _usCulture = CultureInfo.ReadOnly(new CultureInfo("en-US"));
+ private const string BaseActivitySelectText = "select Id, Name, Overview, ShortOverview, Type, ItemId, UserId, DateCreated, LogSeverity from ActivityLog";
+
private readonly IFileSystem _fileSystem;
- public ActivityRepository(ILoggerFactory loggerFactory, IServerApplicationPaths appPaths, IFileSystem fileSystem)
- : base(loggerFactory.CreateLogger(nameof(ActivityRepository)))
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ActivityRepository"/> class.
+ /// </summary>
+ /// <param name="logger">The logger.</param>
+ /// <param name="appPaths">The server application paths.</param>
+ /// <param name="fileSystem">The filesystem.</param>
+ public ActivityRepository(ILogger<ActivityRepository> logger, IServerApplicationPaths appPaths, IFileSystem fileSystem)
+ : base(logger)
{
DbFilePath = Path.Combine(appPaths.DataPath, "activitylog.db");
_fileSystem = fileSystem;
}
+ /// <summary>
+ /// Initializes the <see cref="ActivityRepository"/>.
+ /// </summary>
public void Initialize()
{
try
@@ -46,16 +56,14 @@ namespace Emby.Server.Implementations.Activity
private void InitializeInternal()
{
- using (var connection = GetConnection())
+ using var connection = GetConnection();
+ connection.RunQueries(new[]
{
- connection.RunQueries(new[]
- {
- "create table if not exists ActivityLog (Id INTEGER PRIMARY KEY, Name TEXT NOT NULL, Overview TEXT, ShortOverview TEXT, Type TEXT NOT NULL, ItemId TEXT, UserId TEXT, DateCreated DATETIME NOT NULL, LogSeverity TEXT NOT NULL)",
- "drop index if exists idx_ActivityLogEntries"
- });
+ "create table if not exists ActivityLog (Id INTEGER PRIMARY KEY, Name TEXT NOT NULL, Overview TEXT, ShortOverview TEXT, Type TEXT NOT NULL, ItemId TEXT, UserId TEXT, DateCreated DATETIME NOT NULL, LogSeverity TEXT NOT NULL)",
+ "drop index if exists idx_ActivityLogEntries"
+ });
- TryMigrate(connection);
- }
+ TryMigrate(connection);
}
private void TryMigrate(ManagedConnection connection)
@@ -77,8 +85,7 @@ namespace Emby.Server.Implementations.Activity
}
}
- private const string BaseActivitySelectText = "select Id, Name, Overview, ShortOverview, Type, ItemId, UserId, DateCreated, LogSeverity from ActivityLog";
-
+ /// <inheritdoc />
public void Create(ActivityLogEntry entry)
{
if (entry == null)
@@ -86,37 +93,38 @@ namespace Emby.Server.Implementations.Activity
throw new ArgumentNullException(nameof(entry));
}
- using (var connection = GetConnection())
+ using var connection = GetConnection();
+ connection.RunInTransaction(db =>
{
- connection.RunInTransaction(db =>
- {
- using (var statement = db.PrepareStatement("insert into ActivityLog (Name, Overview, ShortOverview, Type, ItemId, UserId, DateCreated, LogSeverity) values (@Name, @Overview, @ShortOverview, @Type, @ItemId, @UserId, @DateCreated, @LogSeverity)"))
- {
- statement.TryBind("@Name", entry.Name);
+ using var statement = db.PrepareStatement("insert into ActivityLog (Name, Overview, ShortOverview, Type, ItemId, UserId, DateCreated, LogSeverity) values (@Name, @Overview, @ShortOverview, @Type, @ItemId, @UserId, @DateCreated, @LogSeverity)");
+ statement.TryBind("@Name", entry.Name);
- statement.TryBind("@Overview", entry.Overview);
- statement.TryBind("@ShortOverview", entry.ShortOverview);
- statement.TryBind("@Type", entry.Type);
- statement.TryBind("@ItemId", entry.ItemId);
+ statement.TryBind("@Overview", entry.Overview);
+ statement.TryBind("@ShortOverview", entry.ShortOverview);
+ statement.TryBind("@Type", entry.Type);
+ statement.TryBind("@ItemId", entry.ItemId);
- if (entry.UserId.Equals(Guid.Empty))
- {
- statement.TryBindNull("@UserId");
- }
- else
- {
- statement.TryBind("@UserId", entry.UserId.ToString("N", CultureInfo.InvariantCulture));
- }
+ if (entry.UserId.Equals(Guid.Empty))
+ {
+ statement.TryBindNull("@UserId");
+ }
+ else
+ {
+ statement.TryBind("@UserId", entry.UserId.ToString("N", CultureInfo.InvariantCulture));
+ }
- statement.TryBind("@DateCreated", entry.Date.ToDateTimeParamValue());
- statement.TryBind("@LogSeverity", entry.Severity.ToString());
+ statement.TryBind("@DateCreated", entry.Date.ToDateTimeParamValue());
+ statement.TryBind("@LogSeverity", entry.Severity.ToString());
- statement.MoveNext();
- }
- }, TransactionMode);
- }
+ statement.MoveNext();
+ }, TransactionMode);
}
+ /// <summary>
+ /// Adds the provided <see cref="ActivityLogEntry"/> to this repository.
+ /// </summary>
+ /// <param name="entry">The activity log entry.</param>
+ /// <exception cref="ArgumentNullException">If entry is null.</exception>
public void Update(ActivityLogEntry entry)
{
if (entry == null)
@@ -124,38 +132,35 @@ namespace Emby.Server.Implementations.Activity
throw new ArgumentNullException(nameof(entry));
}
- using (var connection = GetConnection())
+ using var connection = GetConnection();
+ connection.RunInTransaction(db =>
{
- connection.RunInTransaction(db =>
- {
- using (var statement = db.PrepareStatement("Update ActivityLog set Name=@Name,Overview=@Overview,ShortOverview=@ShortOverview,Type=@Type,ItemId=@ItemId,UserId=@UserId,DateCreated=@DateCreated,LogSeverity=@LogSeverity where Id=@Id"))
- {
- statement.TryBind("@Id", entry.Id);
+ using var statement = db.PrepareStatement("Update ActivityLog set Name=@Name,Overview=@Overview,ShortOverview=@ShortOverview,Type=@Type,ItemId=@ItemId,UserId=@UserId,DateCreated=@DateCreated,LogSeverity=@LogSeverity where Id=@Id");
+ statement.TryBind("@Id", entry.Id);
- statement.TryBind("@Name", entry.Name);
- statement.TryBind("@Overview", entry.Overview);
- statement.TryBind("@ShortOverview", entry.ShortOverview);
- statement.TryBind("@Type", entry.Type);
- statement.TryBind("@ItemId", entry.ItemId);
+ statement.TryBind("@Name", entry.Name);
+ statement.TryBind("@Overview", entry.Overview);
+ statement.TryBind("@ShortOverview", entry.ShortOverview);
+ statement.TryBind("@Type", entry.Type);
+ statement.TryBind("@ItemId", entry.ItemId);
- if (entry.UserId.Equals(Guid.Empty))
- {
- statement.TryBindNull("@UserId");
- }
- else
- {
- statement.TryBind("@UserId", entry.UserId.ToString("N", CultureInfo.InvariantCulture));
- }
+ if (entry.UserId.Equals(Guid.Empty))
+ {
+ statement.TryBindNull("@UserId");
+ }
+ else
+ {
+ statement.TryBind("@UserId", entry.UserId.ToString("N", CultureInfo.InvariantCulture));
+ }
- statement.TryBind("@DateCreated", entry.Date.ToDateTimeParamValue());
- statement.TryBind("@LogSeverity", entry.Severity.ToString());
+ statement.TryBind("@DateCreated", entry.Date.ToDateTimeParamValue());
+ statement.TryBind("@LogSeverity", entry.Severity.ToString());
- statement.MoveNext();
- }
- }, TransactionMode);
- }
+ statement.MoveNext();
+ }, TransactionMode);
}
+ /// <inheritdoc />
public QueryResult<ActivityLogEntry> GetActivityLogEntries(DateTime? minDate, bool? hasUserId, int? startIndex, int? limit)
{
var commandText = BaseActivitySelectText;
@@ -165,16 +170,10 @@ namespace Emby.Server.Implementations.Activity
{
whereClauses.Add("DateCreated>=@DateCreated");
}
+
if (hasUserId.HasValue)
{
- if (hasUserId.Value)
- {
- whereClauses.Add("UserId not null");
- }
- else
- {
- whereClauses.Add("UserId is null");
- }
+ whereClauses.Add(hasUserId.Value ? "UserId not null" : "UserId is null");
}
var whereTextWithoutPaging = whereClauses.Count == 0 ?
@@ -205,7 +204,7 @@ namespace Emby.Server.Implementations.Activity
if (limit.HasValue)
{
- commandText += " LIMIT " + limit.Value.ToString(_usCulture);
+ commandText += " LIMIT " + limit.Value.ToString(CultureInfo.InvariantCulture);
}
var statementTexts = new[]
@@ -217,38 +216,33 @@ namespace Emby.Server.Implementations.Activity
var list = new List<ActivityLogEntry>();
var result = new QueryResult<ActivityLogEntry>();
- using (var connection = GetConnection(true))
- {
- connection.RunInTransaction(
- db =>
- {
- var statements = PrepareAll(db, statementTexts).ToList();
+ using var connection = GetConnection(true);
+ connection.RunInTransaction(
+ db =>
+ {
+ var statements = PrepareAll(db, statementTexts).ToList();
- using (var statement = statements[0])
+ using (var statement = statements[0])
+ {
+ if (minDate.HasValue)
{
- if (minDate.HasValue)
- {
- statement.TryBind("@DateCreated", minDate.Value.ToDateTimeParamValue());
- }
-
- foreach (var row in statement.ExecuteQuery())
- {
- list.Add(GetEntry(row));
- }
+ statement.TryBind("@DateCreated", minDate.Value.ToDateTimeParamValue());
}
- using (var statement = statements[1])
- {
- if (minDate.HasValue)
- {
- statement.TryBind("@DateCreated", minDate.Value.ToDateTimeParamValue());
- }
+ list.AddRange(statement.ExecuteQuery().Select(GetEntry));
+ }
- result.TotalRecordCount = statement.ExecuteQuery().SelectScalarInt().First();
+ using (var statement = statements[1])
+ {
+ if (minDate.HasValue)
+ {
+ statement.TryBind("@DateCreated", minDate.Value.ToDateTimeParamValue());
}
- },
- ReadTransactionMode);
- }
+
+ result.TotalRecordCount = statement.ExecuteQuery().SelectScalarInt().First();
+ }
+ },
+ ReadTransactionMode);
result.Items = list;
return result;
@@ -305,7 +299,7 @@ namespace Emby.Server.Implementations.Activity
index++;
if (reader[index].SQLiteType != SQLiteType.Null)
{
- info.Severity = (LogLevel)Enum.Parse(typeof(LogLevel), reader[index].ToString(), true);
+ info.Severity = Enum.Parse<LogLevel>(reader[index].ToString(), true);
}
return info;
diff --git a/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs b/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs
index c3cdcc222..2adc1d6c3 100644
--- a/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs
+++ b/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs
@@ -5,7 +5,7 @@ using MediaBrowser.Common.Configuration;
namespace Emby.Server.Implementations.AppBase
{
/// <summary>
- /// Provides a base class to hold common application paths used by both the Ui and Server.
+ /// Provides a base class to hold common application paths used by both the UI and Server.
/// This can be subclassed to add application-specific paths.
/// </summary>
public abstract class BaseApplicationPaths : IApplicationPaths
@@ -15,6 +15,11 @@ namespace Emby.Server.Implementations.AppBase
/// <summary>
/// Initializes a new instance of the <see cref="BaseApplicationPaths"/> class.
/// </summary>
+ /// <param name="programDataPath">The program data path.</param>
+ /// <param name="logDirectoryPath">The log directory path.</param>
+ /// <param name="configurationDirectoryPath">The configuration directory path.</param>
+ /// <param name="cacheDirectoryPath">The cache directory path.</param>
+ /// <param name="webDirectoryPath">The web directory path.</param>
protected BaseApplicationPaths(
string programDataPath,
string logDirectoryPath,
@@ -37,10 +42,7 @@ namespace Emby.Server.Implementations.AppBase
/// <value>The program data path.</value>
public string ProgramDataPath { get; }
- /// <summary>
- /// Gets the path to the web UI resources folder.
- /// </summary>
- /// <value>The web UI resources path.</value>
+ /// <inheritdoc/>
public string WebPath { get; }
/// <summary>
diff --git a/Emby.Server.Implementations/AppBase/ConfigurationHelper.cs b/Emby.Server.Implementations/AppBase/ConfigurationHelper.cs
index 854d7b4cb..0b681fddf 100644
--- a/Emby.Server.Implementations/AppBase/ConfigurationHelper.cs
+++ b/Emby.Server.Implementations/AppBase/ConfigurationHelper.cs
@@ -36,24 +36,22 @@ namespace Emby.Server.Implementations.AppBase
configuration = Activator.CreateInstance(type);
}
- using (var stream = new MemoryStream())
- {
- xmlSerializer.SerializeToStream(configuration, stream);
-
- // Take the object we just got and serialize it back to bytes
- var newBytes = stream.ToArray();
+ using var stream = new MemoryStream();
+ xmlSerializer.SerializeToStream(configuration, stream);
- // If the file didn't exist before, or if something has changed, re-save
- if (buffer == null || !buffer.SequenceEqual(newBytes))
- {
- Directory.CreateDirectory(Path.GetDirectoryName(path));
+ // Take the object we just got and serialize it back to bytes
+ var newBytes = stream.ToArray();
- // Save it after load in case we got new items
- File.WriteAllBytes(path, newBytes);
- }
+ // If the file didn't exist before, or if something has changed, re-save
+ if (buffer == null || !buffer.SequenceEqual(newBytes))
+ {
+ Directory.CreateDirectory(Path.GetDirectoryName(path));
- return configuration;
+ // Save it after load in case we got new items
+ File.WriteAllBytes(path, newBytes);
}
+
+ return configuration;
}
}
}
diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs
index d51e74a4d..ffc916b98 100644
--- a/Emby.Server.Implementations/ApplicationHost.cs
+++ b/Emby.Server.Implementations/ApplicationHost.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Collections.Concurrent;
@@ -31,7 +30,6 @@ using Emby.Server.Implementations.Configuration;
using Emby.Server.Implementations.Cryptography;
using Emby.Server.Implementations.Data;
using Emby.Server.Implementations.Devices;
-using Emby.Server.Implementations.Diagnostics;
using Emby.Server.Implementations.Dto;
using Emby.Server.Implementations.HttpServer;
using Emby.Server.Implementations.HttpServer.Security;
@@ -44,6 +42,7 @@ using Emby.Server.Implementations.Playlists;
using Emby.Server.Implementations.ScheduledTasks;
using Emby.Server.Implementations.Security;
using Emby.Server.Implementations.Serialization;
+using Emby.Server.Implementations.Services;
using Emby.Server.Implementations.Session;
using Emby.Server.Implementations.SocketSharp;
using Emby.Server.Implementations.TV;
@@ -86,9 +85,7 @@ using MediaBrowser.MediaEncoding.BdInfo;
using MediaBrowser.Model.Activity;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Cryptography;
-using MediaBrowser.Model.Diagnostics;
using MediaBrowser.Model.Dlna;
-using MediaBrowser.Model.Events;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.MediaInfo;
@@ -100,16 +97,16 @@ using MediaBrowser.Model.Tasks;
using MediaBrowser.Model.Updates;
using MediaBrowser.Providers.Chapters;
using MediaBrowser.Providers.Manager;
+using MediaBrowser.Providers.Plugins.TheTvdb;
using MediaBrowser.Providers.Subtitles;
-using MediaBrowser.Providers.TV.TheTVDB;
using MediaBrowser.WebDashboard.Api;
using MediaBrowser.XbmcMetadata.Providers;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions;
-using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using OperatingSystem = MediaBrowser.Common.System.OperatingSystem;
+using Prometheus.DotNetRuntime;
namespace Emby.Server.Implementations
{
@@ -118,14 +115,25 @@ namespace Emby.Server.Implementations
/// </summary>
public abstract class ApplicationHost : IServerApplicationHost, IDisposable
{
- private SqliteUserRepository _userRepository;
- private SqliteDisplayPreferencesRepository _displayPreferencesRepository;
+ /// <summary>
+ /// The environment variable prefixes to log at server startup.
+ /// </summary>
+ private static readonly string[] _relevantEnvVarPrefixes = { "JELLYFIN_", "DOTNET_", "ASPNETCORE_" };
+
+ private readonly IFileSystem _fileSystemManager;
+ private readonly INetworkManager _networkManager;
+ private readonly IXmlSerializer _xmlSerializer;
+ private readonly IStartupOptions _startupOptions;
+
+ private IMediaEncoder _mediaEncoder;
+ private ISessionManager _sessionManager;
+ private IHttpServer _httpServer;
+ private IHttpClient _httpClient;
/// <summary>
/// Gets a value indicating whether this instance can self restart.
/// </summary>
- /// <value><c>true</c> if this instance can self restart; otherwise, <c>false</c>.</value>
- public abstract bool CanSelfRestart { get; }
+ public bool CanSelfRestart => _startupOptions.RestartPath != null;
public virtual bool CanLaunchWebBrowser
{
@@ -136,7 +144,7 @@ namespace Emby.Server.Implementations
return false;
}
- if (StartupOptions.IsService)
+ if (_startupOptions.IsService)
{
return false;
}
@@ -206,21 +214,6 @@ namespace Emby.Server.Implementations
/// <value>The configuration manager.</value>
protected IConfigurationManager ConfigurationManager { get; set; }
- public IFileSystem FileSystemManager { get; set; }
-
- /// <inheritdoc />
- public PackageVersionClass SystemUpdateLevel
- {
- get
- {
-#if BETA
- return PackageVersionClass.Beta;
-#else
- return PackageVersionClass.Release;
-#endif
- }
- }
-
/// <summary>
/// Gets or sets the service provider.
/// </summary>
@@ -237,125 +230,12 @@ namespace Emby.Server.Implementations
public int HttpsPort { get; private set; }
/// <summary>
- /// Gets the content root for the webhost.
- /// </summary>
- public string ContentRoot { get; private set; }
-
- /// <summary>
/// Gets the server configuration manager.
/// </summary>
/// <value>The server configuration manager.</value>
public IServerConfigurationManager ServerConfigurationManager => (IServerConfigurationManager)ConfigurationManager;
/// <summary>
- /// Gets or sets the user manager.
- /// </summary>
- /// <value>The user manager.</value>
- public IUserManager UserManager { get; set; }
-
- /// <summary>
- /// Gets or sets the library manager.
- /// </summary>
- /// <value>The library manager.</value>
- internal ILibraryManager LibraryManager { get; set; }
-
- /// <summary>
- /// Gets or sets the directory watchers.
- /// </summary>
- /// <value>The directory watchers.</value>
- private ILibraryMonitor LibraryMonitor { get; set; }
-
- /// <summary>
- /// Gets or sets the provider manager.
- /// </summary>
- /// <value>The provider manager.</value>
- private IProviderManager ProviderManager { get; set; }
-
- /// <summary>
- /// Gets or sets the HTTP server.
- /// </summary>
- /// <value>The HTTP server.</value>
- private IHttpServer HttpServer { get; set; }
-
- private IDtoService DtoService { get; set; }
-
- public IImageProcessor ImageProcessor { get; set; }
-
- /// <summary>
- /// Gets or sets the media encoder.
- /// </summary>
- /// <value>The media encoder.</value>
- private IMediaEncoder MediaEncoder { get; set; }
-
- private ISubtitleEncoder SubtitleEncoder { get; set; }
-
- private ISessionManager SessionManager { get; set; }
-
- private ILiveTvManager LiveTvManager { get; set; }
-
- public LocalizationManager LocalizationManager { get; set; }
-
- private IEncodingManager EncodingManager { get; set; }
-
- private IChannelManager ChannelManager { get; set; }
-
- /// <summary>
- /// Gets or sets the user data repository.
- /// </summary>
- /// <value>The user data repository.</value>
- private IUserDataManager UserDataManager { get; set; }
-
- internal SqliteItemRepository ItemRepository { get; set; }
-
- private INotificationManager NotificationManager { get; set; }
-
- private ISubtitleManager SubtitleManager { get; set; }
-
- private IChapterManager ChapterManager { get; set; }
-
- private IDeviceManager DeviceManager { get; set; }
-
- internal IUserViewManager UserViewManager { get; set; }
-
- private IAuthenticationRepository AuthenticationRepository { get; set; }
-
- private ITVSeriesManager TVSeriesManager { get; set; }
-
- private ICollectionManager CollectionManager { get; set; }
-
- private IMediaSourceManager MediaSourceManager { get; set; }
-
- private readonly IConfiguration _configuration;
-
- /// <summary>
- /// Gets the installation manager.
- /// </summary>
- /// <value>The installation manager.</value>
- protected IInstallationManager InstallationManager { get; private set; }
-
- protected IAuthService AuthService { get; private set; }
-
- public IStartupOptions StartupOptions { get; }
-
- internal IImageEncoder ImageEncoder { get; private set; }
-
- protected IProcessFactory ProcessFactory { get; private set; }
-
- protected readonly IXmlSerializer XmlSerializer;
-
- protected ISocketFactory SocketFactory { get; private set; }
-
- protected ITaskManager TaskManager { get; private set; }
-
- public IHttpClient HttpClient { get; private set; }
-
- protected INetworkManager NetworkManager { get; set; }
-
- public IJsonSerializer JsonSerializer { get; private set; }
-
- protected IIsoManager IsoManager { get; private set; }
-
- /// <summary>
/// Initializes a new instance of the <see cref="ApplicationHost" /> class.
/// </summary>
public ApplicationHost(
@@ -363,32 +243,39 @@ namespace Emby.Server.Implementations
ILoggerFactory loggerFactory,
IStartupOptions options,
IFileSystem fileSystem,
- IImageEncoder imageEncoder,
- INetworkManager networkManager,
- IConfiguration configuration)
+ INetworkManager networkManager)
{
- _configuration = configuration;
-
- XmlSerializer = new MyXmlSerializer();
+ _xmlSerializer = new MyXmlSerializer();
- NetworkManager = networkManager;
+ _networkManager = networkManager;
networkManager.LocalSubnetsFn = GetConfiguredLocalSubnets;
ApplicationPaths = applicationPaths;
LoggerFactory = loggerFactory;
- FileSystemManager = fileSystem;
+ _fileSystemManager = fileSystem;
- ConfigurationManager = new ServerConfigurationManager(ApplicationPaths, LoggerFactory, XmlSerializer, FileSystemManager);
+ ConfigurationManager = new ServerConfigurationManager(ApplicationPaths, LoggerFactory, _xmlSerializer, _fileSystemManager);
- Logger = LoggerFactory.CreateLogger("App");
+ Logger = LoggerFactory.CreateLogger<ApplicationHost>();
- StartupOptions = options;
+ _startupOptions = options;
- ImageEncoder = imageEncoder;
+ // Initialize runtime stat collection
+ if (ServerConfigurationManager.Configuration.EnableMetrics)
+ {
+ DotNetRuntimeStatsBuilder.Default().StartCollecting();
+ }
fileSystem.AddShortcutHandler(new MbLinkShortcutHandler(fileSystem));
- NetworkManager.NetworkChanged += OnNetworkChanged;
+ _networkManager.NetworkChanged += OnNetworkChanged;
+
+ CertificateInfo = new CertificateInfo
+ {
+ Path = ServerConfigurationManager.Configuration.CertificatePath,
+ Password = ServerConfigurationManager.Configuration.CertificatePassword
+ };
+ Certificate = GetCertificate(CertificateInfo);
}
public string ExpandVirtualPath(string path)
@@ -456,10 +343,7 @@ namespace Emby.Server.Implementations
}
}
- /// <summary>
- /// Gets the name.
- /// </summary>
- /// <value>The name.</value>
+ /// <inheritdoc/>
public string Name => ApplicationProductName;
/// <summary>
@@ -549,7 +433,7 @@ namespace Emby.Server.Implementations
ConfigurationManager.ConfigurationUpdated += OnConfigurationUpdated;
- MediaEncoder.SetFFmpegPath();
+ _mediaEncoder.SetFFmpegPath();
Logger.LogInformation("ServerId: {0}", SystemId);
@@ -561,7 +445,7 @@ namespace Emby.Server.Implementations
Logger.LogInformation("Executed all pre-startup entry points in {Elapsed:g}", stopWatch.Elapsed);
Logger.LogInformation("Core startup complete");
- HttpServer.GlobalResponse = null;
+ _httpServer.GlobalResponse = null;
stopWatch.Restart();
await Task.WhenAll(StartEntryPoints(entryPoints, false)).ConfigureAwait(false);
@@ -584,7 +468,8 @@ namespace Emby.Server.Implementations
}
}
- public async Task InitAsync(IServiceCollection serviceCollection)
+ /// <inheritdoc/>
+ public void Init(IServiceCollection serviceCollection)
{
HttpPort = ServerConfigurationManager.Configuration.HttpServerPortNumber;
HttpsPort = ServerConfigurationManager.Configuration.HttpsPortNumber;
@@ -596,8 +481,6 @@ namespace Emby.Server.Implementations
HttpsPort = ServerConfiguration.DefaultHttpsPort;
}
- JsonSerializer = new JsonSerializer();
-
if (Plugins != null)
{
var pluginBuilder = new StringBuilder();
@@ -617,13 +500,7 @@ namespace Emby.Server.Implementations
DiscoverTypes();
- await RegisterResources(serviceCollection).ConfigureAwait(false);
-
- ContentRoot = ServerConfigurationManager.Configuration.DashboardSourcePath;
- if (string.IsNullOrEmpty(ContentRoot))
- {
- ContentRoot = ServerConfigurationManager.ApplicationPaths.WebPath;
- }
+ RegisterServices(serviceCollection);
}
public async Task ExecuteWebsocketHandlerAsync(HttpContext context, Func<Task> next)
@@ -634,7 +511,7 @@ namespace Emby.Server.Implementations
return;
}
- await HttpServer.ProcessWebSocketRequest(context).ConfigureAwait(false);
+ await _httpServer.ProcessWebSocketRequest(context).ConfigureAwait(false);
}
public async Task ExecuteHttpHandlerAsync(HttpContext context, Func<Task> next)
@@ -650,14 +527,16 @@ namespace Emby.Server.Implementations
var localPath = context.Request.Path.ToString();
var req = new WebSocketSharpRequest(request, response, request.Path, LoggerFactory.CreateLogger<WebSocketSharpRequest>());
- await HttpServer.RequestHandler(req, request.GetDisplayUrl(), request.Host.ToString(), localPath, context.RequestAborted).ConfigureAwait(false);
+ await _httpServer.RequestHandler(req, request.GetDisplayUrl(), request.Host.ToString(), localPath, context.RequestAborted).ConfigureAwait(false);
}
/// <summary>
- /// Registers resources that classes will depend on
+ /// Registers services/resources with the service collection that will be available via DI.
/// </summary>
- protected async Task RegisterResources(IServiceCollection serviceCollection)
+ protected virtual void RegisterServices(IServiceCollection serviceCollection)
{
+ serviceCollection.AddSingleton(_startupOptions);
+
serviceCollection.AddMemoryCache();
serviceCollection.AddSingleton(ConfigurationManager);
@@ -665,236 +544,169 @@ namespace Emby.Server.Implementations
serviceCollection.AddSingleton<IApplicationPaths>(ApplicationPaths);
- serviceCollection.AddSingleton<IConfiguration>(_configuration);
-
- serviceCollection.AddSingleton(JsonSerializer);
-
- // TODO: Support for injecting ILogger should be deprecated in favour of ILogger<T> and this removed
- serviceCollection.AddSingleton<ILogger>(Logger);
+ serviceCollection.AddSingleton<IJsonSerializer, JsonSerializer>();
- serviceCollection.AddSingleton(FileSystemManager);
- serviceCollection.AddSingleton<TvDbClientManager>();
+ // TODO: Remove support for injecting ILogger completely
+ serviceCollection.AddSingleton((provider) =>
+ {
+ Logger.LogWarning("Injecting ILogger directly is deprecated and should be replaced with ILogger<T>");
+ return Logger;
+ });
- HttpClient = new HttpClientManager.HttpClientManager(
- ApplicationPaths,
- LoggerFactory.CreateLogger<HttpClientManager.HttpClientManager>(),
- FileSystemManager,
- () => ApplicationUserAgent);
- serviceCollection.AddSingleton(HttpClient);
+ serviceCollection.AddSingleton(_fileSystemManager);
+ serviceCollection.AddSingleton<TvdbClientManager>();
- serviceCollection.AddSingleton(NetworkManager);
+ serviceCollection.AddSingleton<IHttpClient, HttpClientManager.HttpClientManager>();
- IsoManager = new IsoManager();
- serviceCollection.AddSingleton(IsoManager);
+ serviceCollection.AddSingleton(_networkManager);
- TaskManager = new TaskManager(ApplicationPaths, JsonSerializer, LoggerFactory, FileSystemManager);
- serviceCollection.AddSingleton(TaskManager);
+ serviceCollection.AddSingleton<IIsoManager, IsoManager>();
- serviceCollection.AddSingleton(XmlSerializer);
+ serviceCollection.AddSingleton<ITaskManager, TaskManager>();
- ProcessFactory = new ProcessFactory();
- serviceCollection.AddSingleton(ProcessFactory);
+ serviceCollection.AddSingleton(_xmlSerializer);
- serviceCollection.AddSingleton(typeof(IStreamHelper), typeof(StreamHelper));
+ serviceCollection.AddSingleton<IStreamHelper, StreamHelper>();
- var cryptoProvider = new CryptographyProvider();
- serviceCollection.AddSingleton<ICryptoProvider>(cryptoProvider);
+ serviceCollection.AddSingleton<ICryptoProvider, CryptographyProvider>();
- SocketFactory = new SocketFactory();
- serviceCollection.AddSingleton(SocketFactory);
+ serviceCollection.AddSingleton<ISocketFactory, SocketFactory>();
- serviceCollection.AddSingleton(typeof(IInstallationManager), typeof(InstallationManager));
+ serviceCollection.AddSingleton<IInstallationManager, InstallationManager>();
- serviceCollection.AddSingleton(typeof(IZipClient), typeof(ZipClient));
+ serviceCollection.AddSingleton<IZipClient, ZipClient>();
- serviceCollection.AddSingleton(typeof(IHttpResultFactory), typeof(HttpResultFactory));
+ serviceCollection.AddSingleton<IHttpResultFactory, HttpResultFactory>();
serviceCollection.AddSingleton<IServerApplicationHost>(this);
serviceCollection.AddSingleton<IServerApplicationPaths>(ApplicationPaths);
serviceCollection.AddSingleton(ServerConfigurationManager);
- LocalizationManager = new LocalizationManager(ServerConfigurationManager, JsonSerializer, LoggerFactory.CreateLogger<LocalizationManager>());
- await LocalizationManager.LoadAll().ConfigureAwait(false);
- serviceCollection.AddSingleton<ILocalizationManager>(LocalizationManager);
+ serviceCollection.AddSingleton<ILocalizationManager, LocalizationManager>();
- serviceCollection.AddSingleton<IBlurayExaminer>(new BdInfoExaminer(FileSystemManager));
+ serviceCollection.AddSingleton<IBlurayExaminer, BdInfoExaminer>();
- UserDataManager = new UserDataManager(LoggerFactory, ServerConfigurationManager, () => UserManager);
- serviceCollection.AddSingleton(UserDataManager);
+ serviceCollection.AddSingleton<IUserDataRepository, SqliteUserDataRepository>();
+ serviceCollection.AddSingleton<IUserDataManager, UserDataManager>();
- _displayPreferencesRepository = new SqliteDisplayPreferencesRepository(
- LoggerFactory.CreateLogger<SqliteDisplayPreferencesRepository>(),
- ApplicationPaths,
- FileSystemManager);
- serviceCollection.AddSingleton<IDisplayPreferencesRepository>(_displayPreferencesRepository);
+ serviceCollection.AddSingleton<IDisplayPreferencesRepository, SqliteDisplayPreferencesRepository>();
- ItemRepository = new SqliteItemRepository(ServerConfigurationManager, this, LoggerFactory.CreateLogger<SqliteItemRepository>(), LocalizationManager);
- serviceCollection.AddSingleton<IItemRepository>(ItemRepository);
+ serviceCollection.AddSingleton<IItemRepository, SqliteItemRepository>();
- AuthenticationRepository = GetAuthenticationRepository();
- serviceCollection.AddSingleton(AuthenticationRepository);
+ serviceCollection.AddSingleton<IAuthenticationRepository, AuthenticationRepository>();
- _userRepository = GetUserRepository();
+ serviceCollection.AddSingleton<IUserRepository, SqliteUserRepository>();
- UserManager = new UserManager(
- LoggerFactory.CreateLogger<UserManager>(),
- _userRepository,
- XmlSerializer,
- NetworkManager,
- () => ImageProcessor,
- () => DtoService,
- this,
- JsonSerializer,
- FileSystemManager,
- cryptoProvider);
+ // 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.AddSingleton<IUserManager, UserManager>();
- serviceCollection.AddSingleton(UserManager);
+ // TODO: Refactor to eliminate the circular dependency here so that Lazy<T> isn't required
+ // TODO: Add StartupOptions.FFmpegPath to IConfiguration and remove this custom activation
+ serviceCollection.AddTransient(provider => new Lazy<EncodingHelper>(provider.GetRequiredService<EncodingHelper>));
+ serviceCollection.AddSingleton<IMediaEncoder>(provider =>
+ ActivatorUtilities.CreateInstance<MediaBrowser.MediaEncoding.Encoder.MediaEncoder>(provider, _startupOptions.FFmpegPath ?? string.Empty));
- MediaEncoder = new MediaBrowser.MediaEncoding.Encoder.MediaEncoder(
- LoggerFactory.CreateLogger<MediaBrowser.MediaEncoding.Encoder.MediaEncoder>(),
- ServerConfigurationManager,
- FileSystemManager,
- ProcessFactory,
- LocalizationManager,
- () => SubtitleEncoder,
- _configuration,
- StartupOptions.FFmpegPath);
- serviceCollection.AddSingleton(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>();
- LibraryManager = new LibraryManager(this, LoggerFactory, TaskManager, UserManager, ServerConfigurationManager, UserDataManager, () => LibraryMonitor, FileSystemManager, () => ProviderManager, () => UserViewManager, MediaEncoder);
- serviceCollection.AddSingleton(LibraryManager);
+ serviceCollection.AddSingleton<IMusicManager, MusicManager>();
- var musicManager = new MusicManager(LibraryManager);
- serviceCollection.AddSingleton<IMusicManager>(musicManager);
+ serviceCollection.AddSingleton<ILibraryMonitor, LibraryMonitor>();
- LibraryMonitor = new LibraryMonitor(LoggerFactory, LibraryManager, ServerConfigurationManager, FileSystemManager);
- serviceCollection.AddSingleton(LibraryMonitor);
+ serviceCollection.AddSingleton<ISearchEngine, SearchEngine>();
- serviceCollection.AddSingleton<ISearchEngine>(new SearchEngine(LoggerFactory, LibraryManager, UserManager));
+ serviceCollection.AddSingleton<ServiceController>();
+ serviceCollection.AddSingleton<IHttpListener, WebSocketSharpListener>();
+ serviceCollection.AddSingleton<IHttpServer, HttpListenerHost>();
- CertificateInfo = GetCertificateInfo(true);
- Certificate = GetCertificate(CertificateInfo);
-
- HttpServer = new HttpListenerHost(
- this,
- LoggerFactory.CreateLogger<HttpListenerHost>(),
- ServerConfigurationManager,
- _configuration,
- NetworkManager,
- JsonSerializer,
- XmlSerializer,
- CreateHttpListener())
- {
- GlobalResponse = LocalizationManager.GetLocalizedString("StartupEmbyServerIsLoading")
- };
-
- serviceCollection.AddSingleton(HttpServer);
+ serviceCollection.AddSingleton<IImageProcessor, ImageProcessor>();
- ImageProcessor = new ImageProcessor(LoggerFactory.CreateLogger<ImageProcessor>(), ServerConfigurationManager.ApplicationPaths, FileSystemManager, ImageEncoder, () => LibraryManager, () => MediaEncoder);
- serviceCollection.AddSingleton(ImageProcessor);
+ serviceCollection.AddSingleton<ITVSeriesManager, TVSeriesManager>();
- TVSeriesManager = new TVSeriesManager(UserManager, UserDataManager, LibraryManager, ServerConfigurationManager);
- serviceCollection.AddSingleton(TVSeriesManager);
+ serviceCollection.AddSingleton<IDeviceManager, DeviceManager>();
- DeviceManager = new DeviceManager(AuthenticationRepository, JsonSerializer, LibraryManager, LocalizationManager, UserManager, FileSystemManager, LibraryMonitor, ServerConfigurationManager);
- serviceCollection.AddSingleton(DeviceManager);
+ serviceCollection.AddSingleton<IMediaSourceManager, MediaSourceManager>();
- MediaSourceManager = new MediaSourceManager(ItemRepository, ApplicationPaths, LocalizationManager, UserManager, LibraryManager, LoggerFactory, JsonSerializer, FileSystemManager, UserDataManager, () => MediaEncoder);
- serviceCollection.AddSingleton(MediaSourceManager);
+ serviceCollection.AddSingleton<ISubtitleManager, SubtitleManager>();
- SubtitleManager = new SubtitleManager(LoggerFactory, FileSystemManager, LibraryMonitor, MediaSourceManager, LocalizationManager);
- serviceCollection.AddSingleton(SubtitleManager);
+ serviceCollection.AddSingleton<IProviderManager, ProviderManager>();
- ProviderManager = new ProviderManager(HttpClient, SubtitleManager, ServerConfigurationManager, LibraryMonitor, LoggerFactory, FileSystemManager, ApplicationPaths, () => LibraryManager, JsonSerializer);
- serviceCollection.AddSingleton(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>();
- DtoService = new DtoService(LoggerFactory, LibraryManager, UserDataManager, ItemRepository, ImageProcessor, ProviderManager, this, () => MediaSourceManager, () => LiveTvManager);
- serviceCollection.AddSingleton(DtoService);
+ serviceCollection.AddSingleton<IChannelManager, ChannelManager>();
- ChannelManager = new ChannelManager(UserManager, DtoService, LibraryManager, LoggerFactory, ServerConfigurationManager, FileSystemManager, UserDataManager, JsonSerializer, ProviderManager);
- serviceCollection.AddSingleton(ChannelManager);
+ serviceCollection.AddSingleton<ISessionManager, SessionManager>();
- SessionManager = new SessionManager(
- LoggerFactory.CreateLogger<SessionManager>(),
- UserDataManager,
- LibraryManager,
- UserManager,
- musicManager,
- DtoService,
- ImageProcessor,
- this,
- AuthenticationRepository,
- DeviceManager,
- MediaSourceManager);
- serviceCollection.AddSingleton(SessionManager);
+ serviceCollection.AddSingleton<IDlnaManager, DlnaManager>();
- serviceCollection.AddSingleton<IDlnaManager>(
- new DlnaManager(XmlSerializer, FileSystemManager, ApplicationPaths, LoggerFactory, JsonSerializer, this));
+ serviceCollection.AddSingleton<ICollectionManager, CollectionManager>();
- CollectionManager = new CollectionManager(LibraryManager, ApplicationPaths, LocalizationManager, FileSystemManager, LibraryMonitor, LoggerFactory, ProviderManager);
- serviceCollection.AddSingleton(CollectionManager);
+ serviceCollection.AddSingleton<IPlaylistManager, PlaylistManager>();
- serviceCollection.AddSingleton(typeof(IPlaylistManager), typeof(PlaylistManager));
+ serviceCollection.AddSingleton<LiveTvDtoService>();
+ serviceCollection.AddSingleton<ILiveTvManager, LiveTvManager>();
- LiveTvManager = new LiveTvManager(this, ServerConfigurationManager, LoggerFactory, ItemRepository, ImageProcessor, UserDataManager, DtoService, UserManager, LibraryManager, TaskManager, LocalizationManager, JsonSerializer, FileSystemManager, () => ChannelManager);
- serviceCollection.AddSingleton(LiveTvManager);
+ serviceCollection.AddSingleton<IUserViewManager, UserViewManager>();
- UserViewManager = new UserViewManager(LibraryManager, LocalizationManager, UserManager, ChannelManager, LiveTvManager, ServerConfigurationManager);
- serviceCollection.AddSingleton(UserViewManager);
+ serviceCollection.AddSingleton<INotificationManager, NotificationManager>();
- NotificationManager = new NotificationManager(
- LoggerFactory.CreateLogger<NotificationManager>(),
- UserManager,
- ServerConfigurationManager);
- serviceCollection.AddSingleton(NotificationManager);
+ serviceCollection.AddSingleton<IDeviceDiscovery, DeviceDiscovery>();
- serviceCollection.AddSingleton<IDeviceDiscovery>(new DeviceDiscovery(ServerConfigurationManager));
+ serviceCollection.AddSingleton<IChapterManager, ChapterManager>();
- ChapterManager = new ChapterManager(LibraryManager, LoggerFactory, ServerConfigurationManager, ItemRepository);
- serviceCollection.AddSingleton(ChapterManager);
+ serviceCollection.AddSingleton<IEncodingManager, MediaEncoder.EncodingManager>();
- EncodingManager = new MediaEncoder.EncodingManager(FileSystemManager, LoggerFactory, MediaEncoder, ChapterManager, LibraryManager);
- serviceCollection.AddSingleton(EncodingManager);
+ serviceCollection.AddSingleton<IActivityRepository, ActivityRepository>();
+ serviceCollection.AddSingleton<IActivityManager, ActivityManager>();
- var activityLogRepo = GetActivityLogRepository();
- serviceCollection.AddSingleton(activityLogRepo);
- serviceCollection.AddSingleton<IActivityManager>(new ActivityManager(LoggerFactory, activityLogRepo, UserManager));
+ serviceCollection.AddSingleton<IAuthorizationContext, AuthorizationContext>();
+ serviceCollection.AddSingleton<ISessionContext, SessionContext>();
- var authContext = new AuthorizationContext(AuthenticationRepository, UserManager);
- serviceCollection.AddSingleton<IAuthorizationContext>(authContext);
- serviceCollection.AddSingleton<ISessionContext>(new SessionContext(UserManager, authContext, SessionManager));
+ serviceCollection.AddSingleton<IAuthService, AuthService>();
- AuthService = new AuthService(LoggerFactory.CreateLogger<AuthService>(), authContext, ServerConfigurationManager, SessionManager, NetworkManager);
- serviceCollection.AddSingleton(AuthService);
+ serviceCollection.AddSingleton<ISubtitleEncoder, MediaBrowser.MediaEncoding.Subtitles.SubtitleEncoder>();
- SubtitleEncoder = new MediaBrowser.MediaEncoding.Subtitles.SubtitleEncoder(
- LibraryManager,
- LoggerFactory.CreateLogger<MediaBrowser.MediaEncoding.Subtitles.SubtitleEncoder>(),
- ApplicationPaths,
- FileSystemManager,
- MediaEncoder,
- HttpClient,
- MediaSourceManager,
- ProcessFactory);
- serviceCollection.AddSingleton(SubtitleEncoder);
-
- serviceCollection.AddSingleton(typeof(IResourceFileManager), typeof(ResourceFileManager));
+ serviceCollection.AddSingleton<IResourceFileManager, ResourceFileManager>();
serviceCollection.AddSingleton<EncodingHelper>();
- serviceCollection.AddSingleton(typeof(IAttachmentExtractor), typeof(MediaBrowser.MediaEncoding.Attachments.AttachmentExtractor));
+ serviceCollection.AddSingleton<IAttachmentExtractor, MediaBrowser.MediaEncoding.Attachments.AttachmentExtractor>();
+ }
+
+ /// <summary>
+ /// Create services registered with the service container that need to be initialized at application startup.
+ /// </summary>
+ /// <returns>A task representing the service initialization operation.</returns>
+ public async Task InitializeServices()
+ {
+ var localizationManager = (LocalizationManager)Resolve<ILocalizationManager>();
+ await localizationManager.LoadAll().ConfigureAwait(false);
- _displayPreferencesRepository.Initialize();
+ _mediaEncoder = Resolve<IMediaEncoder>();
+ _sessionManager = Resolve<ISessionManager>();
+ _httpServer = Resolve<IHttpServer>();
+ _httpClient = Resolve<IHttpClient>();
- var userDataRepo = new SqliteUserDataRepository(LoggerFactory.CreateLogger<SqliteUserDataRepository>(), ApplicationPaths);
+ ((SqliteDisplayPreferencesRepository)Resolve<IDisplayPreferencesRepository>()).Initialize();
+ ((AuthenticationRepository)Resolve<IAuthenticationRepository>()).Initialize();
+ ((SqliteUserRepository)Resolve<IUserRepository>()).Initialize();
+ ((ActivityRepository)Resolve<IActivityRepository>()).Initialize();
SetStaticProperties();
- ((UserManager)UserManager).Initialize();
+ var userManager = (UserManager)Resolve<IUserManager>();
+ userManager.Initialize();
+
+ var userDataRepo = (SqliteUserDataRepository)Resolve<IUserDataRepository>();
+ ((SqliteItemRepository)Resolve<IItemRepository>()).Initialize(userDataRepo, userManager);
- ((UserDataManager)UserDataManager).Repository = userDataRepo;
- ItemRepository.Initialize(userDataRepo, UserManager);
- ((LibraryManager)LibraryManager).ItemRepository = ItemRepository;
+ FindParts();
}
public static void LogEnvironmentInfo(ILogger logger, IApplicationPaths appPaths)
@@ -904,6 +716,18 @@ namespace Emby.Server.Implementations
.GetCommandLineArgs()
.Distinct();
+ // Get all relevant environment variables
+ var allEnvVars = Environment.GetEnvironmentVariables();
+ var relevantEnvVars = new Dictionary<object, object>();
+ foreach (var key in allEnvVars.Keys)
+ {
+ if (_relevantEnvVarPrefixes.Any(prefix => key.ToString().StartsWith(prefix, StringComparison.OrdinalIgnoreCase)))
+ {
+ relevantEnvVars.Add(key, allEnvVars[key]);
+ }
+ }
+
+ logger.LogInformation("Environment Variables: {EnvVars}", relevantEnvVars);
logger.LogInformation("Arguments: {Args}", commandLineArgs);
logger.LogInformation("Operating system: {OS}", OperatingSystem.Name);
logger.LogInformation("Architecture: {Architecture}", RuntimeInformation.OSArchitecture);
@@ -952,110 +776,37 @@ namespace Emby.Server.Implementations
}
/// <summary>
- /// Gets the user repository.
- /// </summary>
- /// <returns><see cref="Task{SqliteUserRepository}" />.</returns>
- private SqliteUserRepository GetUserRepository()
- {
- var repo = new SqliteUserRepository(
- LoggerFactory.CreateLogger<SqliteUserRepository>(),
- ApplicationPaths);
-
- repo.Initialize();
-
- return repo;
- }
-
- private IAuthenticationRepository GetAuthenticationRepository()
- {
- var repo = new AuthenticationRepository(LoggerFactory, ServerConfigurationManager);
-
- repo.Initialize();
-
- return repo;
- }
-
- private IActivityRepository GetActivityLogRepository()
- {
- var repo = new ActivityRepository(LoggerFactory, ServerConfigurationManager.ApplicationPaths, FileSystemManager);
-
- repo.Initialize();
-
- return repo;
- }
-
- /// <summary>
/// Dirty hacks.
/// </summary>
private void SetStaticProperties()
{
- ItemRepository.ImageProcessor = ImageProcessor;
-
// For now there's no real way to inject these properly
- BaseItem.Logger = LoggerFactory.CreateLogger("BaseItem");
+ BaseItem.Logger = Resolve<ILogger<BaseItem>>();
BaseItem.ConfigurationManager = ServerConfigurationManager;
- BaseItem.LibraryManager = LibraryManager;
- BaseItem.ProviderManager = ProviderManager;
- BaseItem.LocalizationManager = LocalizationManager;
- BaseItem.ItemRepository = ItemRepository;
- User.UserManager = UserManager;
- BaseItem.FileSystem = FileSystemManager;
- BaseItem.UserDataManager = UserDataManager;
- BaseItem.ChannelManager = ChannelManager;
- Video.LiveTvManager = LiveTvManager;
- Folder.UserViewManager = UserViewManager;
- UserView.TVSeriesManager = TVSeriesManager;
- UserView.CollectionManager = CollectionManager;
- BaseItem.MediaSourceManager = MediaSourceManager;
- CollectionFolder.XmlSerializer = XmlSerializer;
- CollectionFolder.JsonSerializer = JsonSerializer;
+ BaseItem.LibraryManager = Resolve<ILibraryManager>();
+ BaseItem.ProviderManager = Resolve<IProviderManager>();
+ BaseItem.LocalizationManager = Resolve<ILocalizationManager>();
+ BaseItem.ItemRepository = Resolve<IItemRepository>();
+ User.UserManager = Resolve<IUserManager>();
+ BaseItem.FileSystem = _fileSystemManager;
+ BaseItem.UserDataManager = Resolve<IUserDataManager>();
+ BaseItem.ChannelManager = Resolve<IChannelManager>();
+ Video.LiveTvManager = Resolve<ILiveTvManager>();
+ Folder.UserViewManager = Resolve<IUserViewManager>();
+ UserView.TVSeriesManager = Resolve<ITVSeriesManager>();
+ UserView.CollectionManager = Resolve<ICollectionManager>();
+ BaseItem.MediaSourceManager = Resolve<IMediaSourceManager>();
+ CollectionFolder.XmlSerializer = _xmlSerializer;
+ CollectionFolder.JsonSerializer = Resolve<IJsonSerializer>();
CollectionFolder.ApplicationHost = this;
- AuthenticatedAttribute.AuthService = AuthService;
- }
-
- private async void PluginInstalled(object sender, GenericEventArgs<PackageVersionInfo> args)
- {
- string dir = Path.Combine(ApplicationPaths.PluginsPath, args.Argument.name);
- var types = Directory.EnumerateFiles(dir, "*.dll", SearchOption.AllDirectories)
- .Select(Assembly.LoadFrom)
- .SelectMany(x => x.ExportedTypes)
- .Where(x => x.IsClass && !x.IsAbstract && !x.IsInterface && !x.IsGenericType)
- .ToArray();
-
- int oldLen = _allConcreteTypes.Length;
- Array.Resize(ref _allConcreteTypes, oldLen + types.Length);
- types.CopyTo(_allConcreteTypes, oldLen);
-
- var plugins = types.Where(x => x.IsAssignableFrom(typeof(IPlugin)))
- .Select(CreateInstanceSafe)
- .Where(x => x != null)
- .Cast<IPlugin>()
- .Select(LoadPlugin)
- .Where(x => x != null)
- .ToArray();
-
- oldLen = _plugins.Length;
- Array.Resize(ref _plugins, oldLen + plugins.Length);
- plugins.CopyTo(_plugins, oldLen);
-
- var entries = types.Where(x => x.IsAssignableFrom(typeof(IServerEntryPoint)))
- .Select(CreateInstanceSafe)
- .Where(x => x != null)
- .Cast<IServerEntryPoint>()
- .ToList();
-
- await Task.WhenAll(StartEntryPoints(entries, true)).ConfigureAwait(false);
- await Task.WhenAll(StartEntryPoints(entries, false)).ConfigureAwait(false);
+ AuthenticatedAttribute.AuthService = Resolve<IAuthService>();
}
/// <summary>
- /// Finds the parts.
+ /// Finds plugin components and register them with the appropriate services.
/// </summary>
- public void FindParts()
+ private void FindParts()
{
- InstallationManager = ServiceProvider.GetService<IInstallationManager>();
- InstallationManager.PluginInstalled += PluginInstalled;
-
if (!ServerConfigurationManager.Configuration.IsPortAuthorized)
{
ServerConfigurationManager.Configuration.IsPortAuthorized = true;
@@ -1068,34 +819,34 @@ namespace Emby.Server.Implementations
.Where(i => i != null)
.ToArray();
- HttpServer.Init(GetExports<IService>(false), GetExports<IWebSocketListener>(), GetUrlPrefixes());
+ _httpServer.Init(GetExportTypes<IService>(), GetExports<IWebSocketListener>(), GetUrlPrefixes());
- LibraryManager.AddParts(
+ Resolve<ILibraryManager>().AddParts(
GetExports<IResolverIgnoreRule>(),
GetExports<IItemResolver>(),
GetExports<IIntroProvider>(),
GetExports<IBaseItemComparer>(),
GetExports<ILibraryPostScanTask>());
- ProviderManager.AddParts(
+ Resolve<IProviderManager>().AddParts(
GetExports<IImageProvider>(),
GetExports<IMetadataService>(),
GetExports<IMetadataProvider>(),
GetExports<IMetadataSaver>(),
GetExports<IExternalId>());
- LiveTvManager.AddParts(GetExports<ILiveTvService>(), GetExports<ITunerHost>(), GetExports<IListingsProvider>());
+ Resolve<ILiveTvManager>().AddParts(GetExports<ILiveTvService>(), GetExports<ITunerHost>(), GetExports<IListingsProvider>());
- SubtitleManager.AddParts(GetExports<ISubtitleProvider>());
+ Resolve<ISubtitleManager>().AddParts(GetExports<ISubtitleProvider>());
- ChannelManager.AddParts(GetExports<IChannel>());
+ Resolve<IChannelManager>().AddParts(GetExports<IChannel>());
- MediaSourceManager.AddParts(GetExports<IMediaSourceProvider>());
+ Resolve<IMediaSourceManager>().AddParts(GetExports<IMediaSourceProvider>());
- NotificationManager.AddParts(GetExports<INotificationService>(), GetExports<INotificationTypeFactory>());
- UserManager.AddParts(GetExports<IAuthenticationProvider>(), GetExports<IPasswordResetProvider>());
+ Resolve<INotificationManager>().AddParts(GetExports<INotificationService>(), GetExports<INotificationTypeFactory>());
+ Resolve<IUserManager>().AddParts(GetExports<IAuthenticationProvider>(), GetExports<IPasswordResetProvider>());
- IsoManager.AddParts(GetExports<IIsoMounter>());
+ Resolve<IIsoManager>().AddParts(GetExports<IIsoMounter>());
}
private IPlugin LoadPlugin(IPlugin plugin)
@@ -1162,7 +913,7 @@ namespace Emby.Server.Implementations
{
exportedTypes = ass.GetExportedTypes();
}
- catch (TypeLoadException ex)
+ catch (FileNotFoundException ex)
{
Logger.LogError(ex, "Error getting exported types from {Assembly}", ass.FullName);
continue;
@@ -1202,18 +953,6 @@ namespace Emby.Server.Implementations
});
}
- protected IHttpListener CreateHttpListener() => new WebSocketSharpListener(LoggerFactory.CreateLogger<WebSocketSharpListener>());
-
- private CertificateInfo GetCertificateInfo(bool generateCertificate)
- {
- // Custom cert
- return new CertificateInfo
- {
- Path = ServerConfigurationManager.Configuration.CertificatePath,
- Password = ServerConfigurationManager.Configuration.CertificatePassword
- };
- }
-
/// <summary>
/// Called when [configuration updated].
/// </summary>
@@ -1240,14 +979,13 @@ namespace Emby.Server.Implementations
}
}
- if (!HttpServer.UrlPrefixes.SequenceEqual(GetUrlPrefixes(), StringComparer.OrdinalIgnoreCase))
+ if (!_httpServer.UrlPrefixes.SequenceEqual(GetUrlPrefixes(), StringComparer.OrdinalIgnoreCase))
{
requiresRestart = true;
}
var currentCertPath = CertificateInfo?.Path;
- var newCertInfo = GetCertificateInfo(false);
- var newCertPath = newCertInfo?.Path;
+ var newCertPath = ServerConfigurationManager.Configuration.CertificatePath;
if (!string.Equals(currentCertPath, newCertPath, StringComparison.OrdinalIgnoreCase))
{
@@ -1300,7 +1038,7 @@ namespace Emby.Server.Implementations
{
try
{
- await SessionManager.SendServerRestartNotification(CancellationToken.None).ConfigureAwait(false);
+ await _sessionManager.SendServerRestartNotification(CancellationToken.None).ConfigureAwait(false);
}
catch (Exception ex)
{
@@ -1404,7 +1142,7 @@ namespace Emby.Server.Implementations
IsShuttingDown = IsShuttingDown,
Version = ApplicationVersionString,
WebSocketPortNumber = HttpPort,
- CompletedInstallations = InstallationManager.CompletedInstallations.ToArray(),
+ CompletedInstallations = Resolve<IInstallationManager>().CompletedInstallations.ToArray(),
Id = SystemId,
ProgramDataPath = ApplicationPaths.ProgramDataPath,
WebPath = ApplicationPaths.WebPath,
@@ -1424,15 +1162,14 @@ namespace Emby.Server.Implementations
ServerName = FriendlyName,
LocalAddress = localAddress,
SupportsLibraryMonitor = true,
- EncoderLocation = MediaEncoder.EncoderLocation,
+ EncoderLocation = _mediaEncoder.EncoderLocation,
SystemArchitecture = RuntimeInformation.OSArchitecture,
- SystemUpdateLevel = SystemUpdateLevel,
- PackageName = StartupOptions.PackageName
+ PackageName = _startupOptions.PackageName
};
}
public IEnumerable<WakeOnLanInfo> GetWakeOnLanInfo()
- => NetworkManager.GetMacAddresses()
+ => _networkManager.GetMacAddresses()
.Select(i => new WakeOnLanInfo(i))
.ToList();
@@ -1455,7 +1192,7 @@ namespace Emby.Server.Implementations
public bool SupportsHttps => Certificate != null || ServerConfigurationManager.Configuration.IsBehindProxy;
- public async Task<string> GetLocalApiUrl(CancellationToken cancellationToken)
+ public async Task<string> GetLocalApiUrl(CancellationToken cancellationToken, bool forceHttp = false)
{
try
{
@@ -1464,7 +1201,7 @@ namespace Emby.Server.Implementations
foreach (var address in addresses)
{
- return GetLocalApiUrl(address);
+ return GetLocalApiUrl(address, forceHttp);
}
return null;
@@ -1494,7 +1231,7 @@ namespace Emby.Server.Implementations
}
/// <inheritdoc />
- public string GetLocalApiUrl(IPAddress ipAddress)
+ public string GetLocalApiUrl(IPAddress ipAddress, bool forceHttp = false)
{
if (ipAddress.AddressFamily == AddressFamily.InterNetworkV6)
{
@@ -1504,28 +1241,21 @@ namespace Emby.Server.Implementations
str.CopyTo(span.Slice(1));
span[^1] = ']';
- return GetLocalApiUrl(span);
+ return GetLocalApiUrl(span, forceHttp);
}
- return GetLocalApiUrl(ipAddress.ToString());
+ return GetLocalApiUrl(ipAddress.ToString(), forceHttp);
}
/// <inheritdoc />
- public string GetLocalApiUrl(ReadOnlySpan<char> host)
+ public string GetLocalApiUrl(ReadOnlySpan<char> host, bool forceHttp = false)
{
var url = new StringBuilder(64);
- if (EnableHttps)
- {
- url.Append("https://");
- }
- else
- {
- url.Append("http://");
- }
-
- url.Append(host)
+ bool useHttps = EnableHttps && !forceHttp;
+ url.Append(useHttps ? "https://" : "http://")
+ .Append(host)
.Append(':')
- .Append(HttpPort);
+ .Append(useHttps ? HttpsPort : HttpPort);
string baseUrl = ServerConfigurationManager.Configuration.BaseUrl;
if (baseUrl.Length != 0)
@@ -1552,7 +1282,7 @@ namespace Emby.Server.Implementations
if (addresses.Count == 0)
{
- addresses.AddRange(NetworkManager.GetLocalIpAddresses(ServerConfigurationManager.Configuration.IgnoreVirtualInterfaces));
+ addresses.AddRange(_networkManager.GetLocalIpAddresses(ServerConfigurationManager.Configuration.IgnoreVirtualInterfaces));
}
var resultList = new List<IPAddress>();
@@ -1619,7 +1349,7 @@ namespace Emby.Server.Implementations
try
{
- using (var response = await HttpClient.SendAsync(
+ using (var response = await _httpClient.SendAsync(
new HttpRequestOptions
{
Url = apiUrl,
@@ -1672,7 +1402,7 @@ namespace Emby.Server.Implementations
try
{
- await SessionManager.SendServerShutdownNotification(CancellationToken.None).ConfigureAwait(false);
+ await _sessionManager.SendServerShutdownNotification(CancellationToken.None).ConfigureAwait(false);
}
catch (Exception ex)
{
@@ -1722,15 +1452,17 @@ namespace Emby.Server.Implementations
throw new NotSupportedException();
}
- var process = ProcessFactory.Create(new ProcessOptions
+ var process = new Process
{
- FileName = url,
- EnableRaisingEvents = true,
- UseShellExecute = true,
- ErrorDialog = false
- });
-
- process.Exited += ProcessExited;
+ StartInfo = new ProcessStartInfo
+ {
+ FileName = url,
+ UseShellExecute = true,
+ ErrorDialog = false
+ },
+ EnableRaisingEvents = true
+ };
+ process.Exited += (sender, args) => ((Process)sender).Dispose();
try
{
@@ -1743,11 +1475,6 @@ namespace Emby.Server.Implementations
}
}
- private static void ProcessExited(object sender, EventArgs e)
- {
- ((IProcess)sender).Dispose();
- }
-
public virtual void EnableLoopback(string appName)
{
}
@@ -1796,14 +1523,8 @@ namespace Emby.Server.Implementations
Logger.LogError(ex, "Error disposing {Type}", part.GetType().Name);
}
}
-
- _userRepository?.Dispose();
- _displayPreferencesRepository.Dispose();
}
- _userRepository = null;
- _displayPreferencesRepository = null;
-
_disposed = true;
}
}
diff --git a/Emby.Server.Implementations/Archiving/ZipClient.cs b/Emby.Server.Implementations/Archiving/ZipClient.cs
index 4a6e5cfd7..591ae547d 100644
--- a/Emby.Server.Implementations/Archiving/ZipClient.cs
+++ b/Emby.Server.Implementations/Archiving/ZipClient.cs
@@ -22,10 +22,8 @@ namespace Emby.Server.Implementations.Archiving
/// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param>
public void ExtractAll(string sourceFile, string targetPath, bool overwriteExistingFiles)
{
- using (var fileStream = File.OpenRead(sourceFile))
- {
- ExtractAll(fileStream, targetPath, overwriteExistingFiles);
- }
+ using var fileStream = File.OpenRead(sourceFile);
+ ExtractAll(fileStream, targetPath, overwriteExistingFiles);
}
/// <summary>
@@ -36,67 +34,61 @@ namespace Emby.Server.Implementations.Archiving
/// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param>
public void ExtractAll(Stream source, string targetPath, bool overwriteExistingFiles)
{
- using (var reader = ReaderFactory.Open(source))
+ using var reader = ReaderFactory.Open(source);
+ var options = new ExtractionOptions
{
- var options = new ExtractionOptions();
- options.ExtractFullPath = true;
-
- if (overwriteExistingFiles)
- {
- options.Overwrite = true;
- }
+ ExtractFullPath = true
+ };
- reader.WriteAllToDirectory(targetPath, options);
+ if (overwriteExistingFiles)
+ {
+ options.Overwrite = true;
}
+
+ reader.WriteAllToDirectory(targetPath, options);
}
+ /// <inheritdoc />
public void ExtractAllFromZip(Stream source, string targetPath, bool overwriteExistingFiles)
{
- using (var reader = ZipReader.Open(source))
+ using var reader = ZipReader.Open(source);
+ var options = new ExtractionOptions
{
- var options = new ExtractionOptions();
- options.ExtractFullPath = true;
+ ExtractFullPath = true,
+ Overwrite = overwriteExistingFiles
+ };
- if (overwriteExistingFiles)
- {
- options.Overwrite = true;
- }
-
- reader.WriteAllToDirectory(targetPath, options);
- }
+ reader.WriteAllToDirectory(targetPath, options);
}
+ /// <inheritdoc />
public void ExtractAllFromGz(Stream source, string targetPath, bool overwriteExistingFiles)
{
- using (var reader = GZipReader.Open(source))
+ using var reader = GZipReader.Open(source);
+ var options = new ExtractionOptions
{
- var options = new ExtractionOptions();
- options.ExtractFullPath = true;
+ ExtractFullPath = true,
+ Overwrite = overwriteExistingFiles
+ };
- if (overwriteExistingFiles)
- {
- options.Overwrite = true;
- }
-
- reader.WriteAllToDirectory(targetPath, options);
- }
+ reader.WriteAllToDirectory(targetPath, options);
}
+ /// <inheritdoc />
public void ExtractFirstFileFromGz(Stream source, string targetPath, string defaultFileName)
{
- using (var reader = GZipReader.Open(source))
+ using var reader = GZipReader.Open(source);
+ if (reader.MoveToNextEntry())
{
- if (reader.MoveToNextEntry())
+ var entry = reader.Entry;
+
+ var filename = entry.Key;
+ if (string.IsNullOrWhiteSpace(filename))
{
- var entry = reader.Entry;
-
- var filename = entry.Key;
- if (string.IsNullOrWhiteSpace(filename))
- {
- filename = defaultFileName;
- }
- reader.WriteEntryToFile(Path.Combine(targetPath, filename));
+ filename = defaultFileName;
}
+
+ reader.WriteEntryToFile(Path.Combine(targetPath, filename));
}
}
@@ -108,10 +100,8 @@ namespace Emby.Server.Implementations.Archiving
/// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param>
public void ExtractAllFrom7z(string sourceFile, string targetPath, bool overwriteExistingFiles)
{
- using (var fileStream = File.OpenRead(sourceFile))
- {
- ExtractAllFrom7z(fileStream, targetPath, overwriteExistingFiles);
- }
+ using var fileStream = File.OpenRead(sourceFile);
+ ExtractAllFrom7z(fileStream, targetPath, overwriteExistingFiles);
}
/// <summary>
@@ -122,21 +112,15 @@ namespace Emby.Server.Implementations.Archiving
/// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param>
public void ExtractAllFrom7z(Stream source, string targetPath, bool overwriteExistingFiles)
{
- using (var archive = SevenZipArchive.Open(source))
+ using var archive = SevenZipArchive.Open(source);
+ using var reader = archive.ExtractAllEntries();
+ var options = new ExtractionOptions
{
- using (var reader = archive.ExtractAllEntries())
- {
- var options = new ExtractionOptions();
- options.ExtractFullPath = true;
-
- if (overwriteExistingFiles)
- {
- options.Overwrite = true;
- }
+ ExtractFullPath = true,
+ Overwrite = overwriteExistingFiles
+ };
- reader.WriteAllToDirectory(targetPath, options);
- }
- }
+ reader.WriteAllToDirectory(targetPath, options);
}
/// <summary>
@@ -147,10 +131,8 @@ namespace Emby.Server.Implementations.Archiving
/// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param>
public void ExtractAllFromTar(string sourceFile, string targetPath, bool overwriteExistingFiles)
{
- using (var fileStream = File.OpenRead(sourceFile))
- {
- ExtractAllFromTar(fileStream, targetPath, overwriteExistingFiles);
- }
+ using var fileStream = File.OpenRead(sourceFile);
+ ExtractAllFromTar(fileStream, targetPath, overwriteExistingFiles);
}
/// <summary>
@@ -161,21 +143,15 @@ namespace Emby.Server.Implementations.Archiving
/// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param>
public void ExtractAllFromTar(Stream source, string targetPath, bool overwriteExistingFiles)
{
- using (var archive = TarArchive.Open(source))
+ using var archive = TarArchive.Open(source);
+ using var reader = archive.ExtractAllEntries();
+ var options = new ExtractionOptions
{
- using (var reader = archive.ExtractAllEntries())
- {
- var options = new ExtractionOptions();
- options.ExtractFullPath = true;
+ ExtractFullPath = true,
+ Overwrite = overwriteExistingFiles
+ };
- if (overwriteExistingFiles)
- {
- options.Overwrite = true;
- }
-
- reader.WriteAllToDirectory(targetPath, options);
- }
- }
+ reader.WriteAllToDirectory(targetPath, options);
}
}
}
diff --git a/Emby.Server.Implementations/Branding/BrandingConfigurationFactory.cs b/Emby.Server.Implementations/Branding/BrandingConfigurationFactory.cs
index 15aee63a0..7ae26bd8b 100644
--- a/Emby.Server.Implementations/Branding/BrandingConfigurationFactory.cs
+++ b/Emby.Server.Implementations/Branding/BrandingConfigurationFactory.cs
@@ -1,14 +1,15 @@
-#pragma warning disable CS1591
-#pragma warning disable SA1600
-
using System.Collections.Generic;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Model.Branding;
namespace Emby.Server.Implementations.Branding
{
+ /// <summary>
+ /// A configuration factory for <see cref="BrandingOptions"/>.
+ /// </summary>
public class BrandingConfigurationFactory : IConfigurationFactory
{
+ /// <inheritdoc />
public IEnumerable<ConfigurationStore> GetConfigurations()
{
return new[]
diff --git a/Emby.Server.Implementations/Browser/BrowserLauncher.cs b/Emby.Server.Implementations/Browser/BrowserLauncher.cs
index f5da0d018..96096e142 100644
--- a/Emby.Server.Implementations/Browser/BrowserLauncher.cs
+++ b/Emby.Server.Implementations/Browser/BrowserLauncher.cs
@@ -1,51 +1,48 @@
using System;
using MediaBrowser.Controller;
+using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.Browser
{
/// <summary>
- /// Class BrowserLauncher.
+ /// Assists in opening application URLs in an external browser.
/// </summary>
public static class BrowserLauncher
{
/// <summary>
- /// Opens the dashboard page.
+ /// Opens the home page of the web client.
/// </summary>
- /// <param name="page">The page.</param>
/// <param name="appHost">The app host.</param>
- private static void OpenDashboardPage(string page, IServerApplicationHost appHost)
+ public static void OpenWebApp(IServerApplicationHost appHost)
{
- var url = appHost.GetLocalApiUrl("localhost") + "/web/" + page;
-
- OpenUrl(appHost, url);
+ TryOpenUrl(appHost, "/web/index.html");
}
/// <summary>
- /// Opens the web client.
+ /// Opens the swagger API page.
/// </summary>
/// <param name="appHost">The app host.</param>
- public static void OpenWebApp(IServerApplicationHost appHost)
+ public static void OpenSwaggerPage(IServerApplicationHost appHost)
{
- OpenDashboardPage("index.html", appHost);
+ TryOpenUrl(appHost, "/swagger/index.html");
}
/// <summary>
- /// Opens the URL.
+ /// Opens the specified URL in an external browser window. Any exceptions will be logged, but ignored.
/// </summary>
- /// <param name="appHost">The application host instance.</param>
+ /// <param name="appHost">The application host.</param>
/// <param name="url">The URL.</param>
- private static void OpenUrl(IServerApplicationHost appHost, string url)
+ private static void TryOpenUrl(IServerApplicationHost appHost, string url)
{
try
{
- appHost.LaunchUrl(url);
- }
- catch (NotSupportedException)
- {
-
+ string baseUrl = appHost.GetLocalApiUrl("localhost");
+ appHost.LaunchUrl(baseUrl + url);
}
- catch (Exception)
+ catch (Exception ex)
{
+ var logger = appHost.Resolve<ILogger>();
+ logger?.LogError(ex, "Failed to open browser window with URL {URL}", url);
}
}
}
diff --git a/Emby.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs b/Emby.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs
index aae416b37..3e149cc82 100644
--- a/Emby.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs
+++ b/Emby.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs
@@ -1,8 +1,6 @@
-#pragma warning disable CS1591
-#pragma warning disable SA1600
-
using System;
using System.Collections.Generic;
+using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.Channels;
@@ -12,6 +10,9 @@ using MediaBrowser.Model.Dto;
namespace Emby.Server.Implementations.Channels
{
+ /// <summary>
+ /// A media source provider for channels.
+ /// </summary>
public class ChannelDynamicMediaSourceProvider : IMediaSourceProvider
{
private readonly ChannelManager _channelManager;
@@ -28,12 +29,9 @@ namespace Emby.Server.Implementations.Channels
/// <inheritdoc />
public Task<IEnumerable<MediaSourceInfo>> GetMediaSources(BaseItem item, CancellationToken cancellationToken)
{
- if (item.SourceType == SourceType.Channel)
- {
- return _channelManager.GetDynamicMediaSources(item, cancellationToken);
- }
-
- return Task.FromResult<IEnumerable<MediaSourceInfo>>(new List<MediaSourceInfo>());
+ return item.SourceType == SourceType.Channel
+ ? _channelManager.GetDynamicMediaSources(item, cancellationToken)
+ : Task.FromResult(Enumerable.Empty<MediaSourceInfo>());
}
/// <inheritdoc />
diff --git a/Emby.Server.Implementations/Channels/ChannelImageProvider.cs b/Emby.Server.Implementations/Channels/ChannelImageProvider.cs
index fe64f1b15..25cbfcf14 100644
--- a/Emby.Server.Implementations/Channels/ChannelImageProvider.cs
+++ b/Emby.Server.Implementations/Channels/ChannelImageProvider.cs
@@ -1,6 +1,3 @@
-#pragma warning disable CS1591
-#pragma warning disable SA1600
-
using System.Collections.Generic;
using System.Linq;
using System.Threading;
@@ -12,20 +9,32 @@ using MediaBrowser.Model.Entities;
namespace Emby.Server.Implementations.Channels
{
+ /// <summary>
+ /// An image provider for channels.
+ /// </summary>
public class ChannelImageProvider : IDynamicImageProvider, IHasItemChangeMonitor
{
private readonly IChannelManager _channelManager;
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ChannelImageProvider"/> class.
+ /// </summary>
+ /// <param name="channelManager">The channel manager.</param>
public ChannelImageProvider(IChannelManager channelManager)
{
_channelManager = channelManager;
}
+ /// <inheritdoc />
+ public string Name => "Channel Image Provider";
+
+ /// <inheritdoc />
public IEnumerable<ImageType> GetSupportedImages(BaseItem item)
{
return GetChannel(item).GetSupportedChannelImages();
}
+ /// <inheritdoc />
public Task<DynamicImageResponse> GetImage(BaseItem item, ImageType type, CancellationToken cancellationToken)
{
var channel = GetChannel(item);
@@ -33,8 +42,7 @@ namespace Emby.Server.Implementations.Channels
return channel.GetChannelImage(type, cancellationToken);
}
- public string Name => "Channel Image Provider";
-
+ /// <inheritdoc />
public bool Supports(BaseItem item)
{
return item is Channel;
@@ -47,6 +55,7 @@ namespace Emby.Server.Implementations.Channels
return ((ChannelManager)_channelManager).GetChannelProvider(channel);
}
+ /// <inheritdoc />
public bool HasChanged(BaseItem item, IDirectoryService directoryService)
{
return GetSupportedImages(item).Any(i => !item.HasImage(i));
diff --git a/Emby.Server.Implementations/Channels/ChannelManager.cs b/Emby.Server.Implementations/Channels/ChannelManager.cs
index de2e123af..138832fb8 100644
--- a/Emby.Server.Implementations/Channels/ChannelManager.cs
+++ b/Emby.Server.Implementations/Channels/ChannelManager.cs
@@ -1,6 +1,3 @@
-#pragma warning disable CS1591
-#pragma warning disable SA1600
-
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
@@ -30,10 +27,11 @@ using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.Channels
{
+ /// <summary>
+ /// The LiveTV channel manager.
+ /// </summary>
public class ChannelManager : IChannelManager
{
- internal IChannel[] Channels { get; private set; }
-
private readonly IUserManager _userManager;
private readonly IUserDataManager _userDataManager;
private readonly IDtoService _dtoService;
@@ -44,11 +42,28 @@ namespace Emby.Server.Implementations.Channels
private readonly IJsonSerializer _jsonSerializer;
private readonly IProviderManager _providerManager;
+ private readonly ConcurrentDictionary<string, Tuple<DateTime, List<MediaSourceInfo>>> _channelItemMediaInfo =
+ new ConcurrentDictionary<string, Tuple<DateTime, List<MediaSourceInfo>>>();
+
+ private readonly SemaphoreSlim _resourcePool = new SemaphoreSlim(1, 1);
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ChannelManager"/> class.
+ /// </summary>
+ /// <param name="userManager">The user manager.</param>
+ /// <param name="dtoService">The dto service.</param>
+ /// <param name="libraryManager">The library manager.</param>
+ /// <param name="loggerFactory">The logger factory.</param>
+ /// <param name="config">The server configuration manager.</param>
+ /// <param name="fileSystem">The filesystem.</param>
+ /// <param name="userDataManager">The user data manager.</param>
+ /// <param name="jsonSerializer">The JSON serializer.</param>
+ /// <param name="providerManager">The provider manager.</param>
public ChannelManager(
IUserManager userManager,
IDtoService dtoService,
ILibraryManager libraryManager,
- ILoggerFactory loggerFactory,
+ ILogger<ChannelManager> logger,
IServerConfigurationManager config,
IFileSystem fileSystem,
IUserDataManager userDataManager,
@@ -58,7 +73,7 @@ namespace Emby.Server.Implementations.Channels
_userManager = userManager;
_dtoService = dtoService;
_libraryManager = libraryManager;
- _logger = loggerFactory.CreateLogger(nameof(ChannelManager));
+ _logger = logger;
_config = config;
_fileSystem = fileSystem;
_userDataManager = userDataManager;
@@ -66,13 +81,17 @@ namespace Emby.Server.Implementations.Channels
_providerManager = providerManager;
}
+ internal IChannel[] Channels { get; private set; }
+
private static TimeSpan CacheLength => TimeSpan.FromHours(3);
+ /// <inheritdoc />
public void AddParts(IEnumerable<IChannel> channels)
{
Channels = channels.ToArray();
}
+ /// <inheritdoc />
public bool EnableMediaSourceDisplay(BaseItem item)
{
var internalChannel = _libraryManager.GetItemById(item.ChannelId);
@@ -81,15 +100,16 @@ namespace Emby.Server.Implementations.Channels
return !(channel is IDisableMediaSourceDisplay);
}
+ /// <inheritdoc />
public bool CanDelete(BaseItem item)
{
var internalChannel = _libraryManager.GetItemById(item.ChannelId);
var channel = Channels.FirstOrDefault(i => GetInternalChannelId(i.Name).Equals(internalChannel.Id));
- var supportsDelete = channel as ISupportsDelete;
- return supportsDelete != null && supportsDelete.CanDelete(item);
+ return channel is ISupportsDelete supportsDelete && supportsDelete.CanDelete(item);
}
+ /// <inheritdoc />
public bool EnableMediaProbe(BaseItem item)
{
var internalChannel = _libraryManager.GetItemById(item.ChannelId);
@@ -98,6 +118,7 @@ namespace Emby.Server.Implementations.Channels
return channel is ISupportsMediaProbe;
}
+ /// <inheritdoc />
public Task DeleteItem(BaseItem item)
{
var internalChannel = _libraryManager.GetItemById(item.ChannelId);
@@ -124,11 +145,16 @@ namespace Emby.Server.Implementations.Channels
.OrderBy(i => i.Name);
}
+ /// <summary>
+ /// Get the installed channel IDs.
+ /// </summary>
+ /// <returns>An <see cref="IEnumerable{T}"/> containing installed channel IDs.</returns>
public IEnumerable<Guid> GetInstalledChannelIds()
{
return GetAllChannels().Select(i => GetInternalChannelId(i.Name));
}
+ /// <inheritdoc />
public QueryResult<Channel> GetChannelsInternal(ChannelQuery query)
{
var user = query.UserId.Equals(Guid.Empty)
@@ -147,15 +173,13 @@ namespace Emby.Server.Implementations.Channels
{
try
{
- var hasAttributes = GetChannelProvider(i) as IHasFolderAttributes;
-
- return (hasAttributes != null && hasAttributes.Attributes.Contains("Recordings", StringComparer.OrdinalIgnoreCase)) == val;
+ return (GetChannelProvider(i) is IHasFolderAttributes hasAttributes
+ && hasAttributes.Attributes.Contains("Recordings", StringComparer.OrdinalIgnoreCase)) == val;
}
catch
{
return false;
}
-
}).ToList();
}
@@ -172,7 +196,6 @@ namespace Emby.Server.Implementations.Channels
{
return false;
}
-
}).ToList();
}
@@ -189,9 +212,9 @@ namespace Emby.Server.Implementations.Channels
{
return false;
}
-
}).ToList();
}
+
if (query.IsFavorite.HasValue)
{
var val = query.IsFavorite.Value;
@@ -216,7 +239,6 @@ namespace Emby.Server.Implementations.Channels
{
return false;
}
-
}).ToList();
}
@@ -227,6 +249,7 @@ namespace Emby.Server.Implementations.Channels
{
all = all.Skip(query.StartIndex.Value).ToList();
}
+
if (query.Limit.HasValue)
{
all = all.Take(query.Limit.Value).ToList();
@@ -249,6 +272,7 @@ namespace Emby.Server.Implementations.Channels
};
}
+ /// <inheritdoc />
public QueryResult<BaseItemDto> GetChannels(ChannelQuery query)
{
var user = query.UserId.Equals(Guid.Empty)
@@ -257,11 +281,9 @@ namespace Emby.Server.Implementations.Channels
var internalResult = GetChannelsInternal(query);
- var dtoOptions = new DtoOptions()
- {
- };
+ var dtoOptions = new DtoOptions();
- //TODO Fix The co-variant conversion (internalResult.Items) between Folder[] and BaseItem[], this can generate runtime issues.
+ // TODO Fix The co-variant conversion (internalResult.Items) between Folder[] and BaseItem[], this can generate runtime issues.
var returnItems = _dtoService.GetBaseItemDtos(internalResult.Items, dtoOptions, user);
var result = new QueryResult<BaseItemDto>
@@ -273,6 +295,12 @@ namespace Emby.Server.Implementations.Channels
return result;
}
+ /// <summary>
+ /// Refreshes the associated channels.
+ /// </summary>
+ /// <param name="progress">The progress.</param>
+ /// <param name="cancellationToken">A cancellation token that can be used to cancel the operation.</param>
+ /// <returns>The completed task.</returns>
public async Task RefreshChannels(IProgress<double> progress, CancellationToken cancellationToken)
{
var allChannelsList = GetAllChannels().ToList();
@@ -306,14 +334,7 @@ namespace Emby.Server.Implementations.Channels
private Channel GetChannelEntity(IChannel channel)
{
- var item = GetChannel(GetInternalChannelId(channel.Name));
-
- if (item == null)
- {
- item = GetChannel(channel, CancellationToken.None).Result;
- }
-
- return item;
+ return GetChannel(GetInternalChannelId(channel.Name)) ?? GetChannel(channel, CancellationToken.None).Result;
}
private List<MediaSourceInfo> GetSavedMediaSources(BaseItem item)
@@ -342,8 +363,8 @@ namespace Emby.Server.Implementations.Channels
}
catch
{
-
}
+
return;
}
@@ -352,6 +373,7 @@ namespace Emby.Server.Implementations.Channels
_jsonSerializer.SerializeToFile(mediaSources, path);
}
+ /// <inheritdoc />
public IEnumerable<MediaSourceInfo> GetStaticMediaSources(BaseItem item, CancellationToken cancellationToken)
{
IEnumerable<MediaSourceInfo> results = GetSavedMediaSources(item);
@@ -361,16 +383,20 @@ namespace Emby.Server.Implementations.Channels
.ToList();
}
+ /// <summary>
+ /// Gets the dynamic media sources based on the provided item.
+ /// </summary>
+ /// <param name="item">The item.</param>
+ /// <param name="cancellationToken">A cancellation token that can be used to cancel the operation.</param>
+ /// <returns>The task representing the operation to get the media sources.</returns>
public async Task<IEnumerable<MediaSourceInfo>> GetDynamicMediaSources(BaseItem item, CancellationToken cancellationToken)
{
var channel = GetChannel(item.ChannelId);
var channelPlugin = GetChannelProvider(channel);
- var requiresCallback = channelPlugin as IRequiresMediaInfoCallback;
-
IEnumerable<MediaSourceInfo> results;
- if (requiresCallback != null)
+ if (channelPlugin is IRequiresMediaInfoCallback requiresCallback)
{
results = await GetChannelItemMediaSourcesInternal(requiresCallback, item.ExternalId, cancellationToken)
.ConfigureAwait(false);
@@ -385,9 +411,6 @@ namespace Emby.Server.Implementations.Channels
.ToList();
}
- private readonly ConcurrentDictionary<string, Tuple<DateTime, List<MediaSourceInfo>>> _channelItemMediaInfo =
- new ConcurrentDictionary<string, Tuple<DateTime, List<MediaSourceInfo>>>();
-
private async Task<IEnumerable<MediaSourceInfo>> GetChannelItemMediaSourcesInternal(IRequiresMediaInfoCallback channel, string id, CancellationToken cancellationToken)
{
if (_channelItemMediaInfo.TryGetValue(id, out Tuple<DateTime, List<MediaSourceInfo>> cachedInfo))
@@ -410,7 +433,7 @@ namespace Emby.Server.Implementations.Channels
private static MediaSourceInfo NormalizeMediaSource(BaseItem item, MediaSourceInfo info)
{
- info.RunTimeTicks = info.RunTimeTicks ?? item.RunTimeTicks;
+ info.RunTimeTicks ??= item.RunTimeTicks;
return info;
}
@@ -445,18 +468,21 @@ namespace Emby.Server.Implementations.Channels
{
isNew = true;
}
+
item.Path = path;
if (!item.ChannelId.Equals(id))
{
forceUpdate = true;
}
+
item.ChannelId = id;
if (item.ParentId != parentFolderId)
{
forceUpdate = true;
}
+
item.ParentId = parentFolderId;
item.OfficialRating = GetOfficialRating(channelInfo.ParentalRating);
@@ -473,51 +499,56 @@ namespace Emby.Server.Implementations.Channels
_libraryManager.CreateItem(item, null);
}
- await item.RefreshMetadata(new MetadataRefreshOptions(new DirectoryService(_fileSystem))
- {
- ForceSave = !isNew && forceUpdate
- }, cancellationToken).ConfigureAwait(false);
+ await item.RefreshMetadata(
+ new MetadataRefreshOptions(new DirectoryService(_fileSystem))
+ {
+ ForceSave = !isNew && forceUpdate
+ },
+ cancellationToken).ConfigureAwait(false);
return item;
}
private static string GetOfficialRating(ChannelParentalRating rating)
{
- switch (rating)
- {
- case ChannelParentalRating.Adult:
- return "XXX";
- case ChannelParentalRating.UsR:
- return "R";
- case ChannelParentalRating.UsPG13:
- return "PG-13";
- case ChannelParentalRating.UsPG:
- return "PG";
- default:
- return null;
- }
+ return rating switch
+ {
+ ChannelParentalRating.Adult => "XXX",
+ ChannelParentalRating.UsR => "R",
+ ChannelParentalRating.UsPG13 => "PG-13",
+ ChannelParentalRating.UsPG => "PG",
+ _ => null
+ };
}
+ /// <summary>
+ /// Gets a channel with the provided Guid.
+ /// </summary>
+ /// <param name="id">The Guid.</param>
+ /// <returns>The corresponding channel.</returns>
public Channel GetChannel(Guid id)
{
return _libraryManager.GetItemById(id) as Channel;
}
+ /// <inheritdoc />
public Channel GetChannel(string id)
{
return _libraryManager.GetItemById(id) as Channel;
}
+ /// <inheritdoc />
public ChannelFeatures[] GetAllChannelFeatures()
{
- return _libraryManager.GetItemIds(new InternalItemsQuery
- {
- IncludeItemTypes = new[] { typeof(Channel).Name },
- OrderBy = new[] { (ItemSortBy.SortName, SortOrder.Ascending) }
-
- }).Select(i => GetChannelFeatures(i.ToString("N", CultureInfo.InvariantCulture))).ToArray();
+ return _libraryManager.GetItemIds(
+ new InternalItemsQuery
+ {
+ IncludeItemTypes = new[] { typeof(Channel).Name },
+ OrderBy = new[] { (ItemSortBy.SortName, SortOrder.Ascending) }
+ }).Select(i => GetChannelFeatures(i.ToString("N", CultureInfo.InvariantCulture))).ToArray();
}
+ /// <inheritdoc />
public ChannelFeatures GetChannelFeatures(string id)
{
if (string.IsNullOrEmpty(id))
@@ -531,15 +562,27 @@ namespace Emby.Server.Implementations.Channels
return GetChannelFeaturesDto(channel, channelProvider, channelProvider.GetChannelFeatures());
}
+ /// <summary>
+ /// Checks whether the provided Guid supports external transfer.
+ /// </summary>
+ /// <param name="channelId">The Guid.</param>
+ /// <returns>Whether or not the provided Guid supports external transfer.</returns>
public bool SupportsExternalTransfer(Guid channelId)
{
- //var channel = GetChannel(channelId);
var channelProvider = GetChannelProvider(channelId);
return channelProvider.GetChannelFeatures().SupportsContentDownloading;
}
- public ChannelFeatures GetChannelFeaturesDto(Channel channel,
+ /// <summary>
+ /// Gets the provided channel's supported features.
+ /// </summary>
+ /// <param name="channel">The channel.</param>
+ /// <param name="provider">The provider.</param>
+ /// <param name="features">The features.</param>
+ /// <returns>The supported features.</returns>
+ public ChannelFeatures GetChannelFeaturesDto(
+ Channel channel,
IChannel provider,
InternalChannelFeatures features)
{
@@ -568,9 +611,11 @@ namespace Emby.Server.Implementations.Channels
{
throw new ArgumentNullException(nameof(name));
}
+
return _libraryManager.GetNewItemId("Channel " + name, typeof(Channel));
}
+ /// <inheritdoc />
public async Task<QueryResult<BaseItemDto>> GetLatestChannelItems(InternalItemsQuery query, CancellationToken cancellationToken)
{
var internalResult = await GetLatestChannelItemsInternal(query, cancellationToken).ConfigureAwait(false);
@@ -589,6 +634,7 @@ namespace Emby.Server.Implementations.Channels
return result;
}
+ /// <inheritdoc />
public async Task<QueryResult<BaseItem>> GetLatestChannelItemsInternal(InternalItemsQuery query, CancellationToken cancellationToken)
{
var channels = GetAllChannels().Where(i => i is ISupportsLatestMedia).ToArray();
@@ -615,7 +661,7 @@ namespace Emby.Server.Implementations.Channels
query.IsFolder = false;
// hack for trailers, figure out a better way later
- var sortByPremiereDate = channels.Length == 1 && channels[0].GetType().Name.IndexOf("Trailer") != -1;
+ var sortByPremiereDate = channels.Length == 1 && channels[0].GetType().Name.Contains("Trailer", StringComparison.Ordinal);
if (sortByPremiereDate)
{
@@ -641,10 +687,12 @@ namespace Emby.Server.Implementations.Channels
{
var internalChannel = await GetChannel(channel, cancellationToken).ConfigureAwait(false);
- var query = new InternalItemsQuery();
- query.Parent = internalChannel;
- query.EnableTotalRecordCount = false;
- query.ChannelIds = new Guid[] { internalChannel.Id };
+ var query = new InternalItemsQuery
+ {
+ Parent = internalChannel,
+ EnableTotalRecordCount = false,
+ ChannelIds = new Guid[] { internalChannel.Id }
+ };
var result = await GetChannelItemsInternal(query, new SimpleProgress<double>(), cancellationToken).ConfigureAwait(false);
@@ -652,17 +700,20 @@ namespace Emby.Server.Implementations.Channels
{
if (item is Folder folder)
{
- await GetChannelItemsInternal(new InternalItemsQuery
- {
- Parent = folder,
- EnableTotalRecordCount = false,
- ChannelIds = new Guid[] { internalChannel.Id }
-
- }, new SimpleProgress<double>(), cancellationToken).ConfigureAwait(false);
+ await GetChannelItemsInternal(
+ new InternalItemsQuery
+ {
+ Parent = folder,
+ EnableTotalRecordCount = false,
+ ChannelIds = new Guid[] { internalChannel.Id }
+ },
+ new SimpleProgress<double>(),
+ cancellationToken).ConfigureAwait(false);
}
}
}
+ /// <inheritdoc />
public async Task<QueryResult<BaseItem>> GetChannelItemsInternal(InternalItemsQuery query, IProgress<double> progress, CancellationToken cancellationToken)
{
// Get the internal channel entity
@@ -673,7 +724,8 @@ namespace Emby.Server.Implementations.Channels
var parentItem = query.ParentId == Guid.Empty ? channel : _libraryManager.GetItemById(query.ParentId);
- var itemsResult = await GetChannelItems(channelProvider,
+ var itemsResult = await GetChannelItems(
+ channelProvider,
query.User,
parentItem is Channel ? null : parentItem.ExternalId,
null,
@@ -685,13 +737,12 @@ namespace Emby.Server.Implementations.Channels
{
query.Parent = channel;
}
+
query.ChannelIds = Array.Empty<Guid>();
// Not yet sure why this is causing a problem
query.GroupByPresentationUniqueKey = false;
- //_logger.LogDebug("GetChannelItemsInternal");
-
// null if came from cache
if (itemsResult != null)
{
@@ -708,12 +759,15 @@ namespace Emby.Server.Implementations.Channels
var deadItem = _libraryManager.GetItemById(deadId);
if (deadItem != null)
{
- _libraryManager.DeleteItem(deadItem, new DeleteOptions
- {
- DeleteFileLocation = false,
- DeleteFromExternalProvider = false
-
- }, parentItem, false);
+ _libraryManager.DeleteItem(
+ deadItem,
+ new DeleteOptions
+ {
+ DeleteFileLocation = false,
+ DeleteFromExternalProvider = false
+ },
+ parentItem,
+ false);
}
}
}
@@ -721,6 +775,7 @@ namespace Emby.Server.Implementations.Channels
return _libraryManager.GetItemsResult(query);
}
+ /// <inheritdoc />
public async Task<QueryResult<BaseItemDto>> GetChannelItems(InternalItemsQuery query, CancellationToken cancellationToken)
{
var internalResult = await GetChannelItemsInternal(query, new SimpleProgress<double>(), cancellationToken).ConfigureAwait(false);
@@ -736,7 +791,6 @@ namespace Emby.Server.Implementations.Channels
return result;
}
- private readonly SemaphoreSlim _resourcePool = new SemaphoreSlim(1, 1);
private async Task<ChannelItemResult> GetChannelItems(IChannel channel,
User user,
string externalFolderId,
@@ -744,7 +798,7 @@ namespace Emby.Server.Implementations.Channels
bool sortDescending,
CancellationToken cancellationToken)
{
- var userId = user == null ? null : user.Id.ToString("N", CultureInfo.InvariantCulture);
+ var userId = user?.Id.ToString("N", CultureInfo.InvariantCulture);
var cacheLength = CacheLength;
var cachePath = GetChannelDataCachePath(channel, userId, externalFolderId, sortField, sortDescending);
@@ -762,11 +816,9 @@ namespace Emby.Server.Implementations.Channels
}
catch (FileNotFoundException)
{
-
}
catch (IOException)
{
-
}
await _resourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
@@ -786,16 +838,14 @@ namespace Emby.Server.Implementations.Channels
}
catch (FileNotFoundException)
{
-
}
catch (IOException)
{
-
}
var query = new InternalChannelItemQuery
{
- UserId = user == null ? Guid.Empty : user.Id,
+ UserId = user?.Id ?? Guid.Empty,
SortBy = sortField,
SortDescending = sortDescending,
FolderId = externalFolderId
@@ -834,7 +884,8 @@ namespace Emby.Server.Implementations.Channels
}
}
- private string GetChannelDataCachePath(IChannel channel,
+ private string GetChannelDataCachePath(
+ IChannel channel,
string userId,
string externalFolderId,
ChannelItemSortField? sortField,
@@ -844,8 +895,7 @@ namespace Emby.Server.Implementations.Channels
var userCacheKey = string.Empty;
- var hasCacheKey = channel as IHasCacheKey;
- if (hasCacheKey != null)
+ if (channel is IHasCacheKey hasCacheKey)
{
userCacheKey = hasCacheKey.GetCacheKey(userId) ?? string.Empty;
}
@@ -859,6 +909,7 @@ namespace Emby.Server.Implementations.Channels
{
filename += "-sortField-" + sortField.Value;
}
+
if (sortDescending)
{
filename += "-sortDescending";
@@ -866,7 +917,8 @@ namespace Emby.Server.Implementations.Channels
filename = filename.GetMD5().ToString("N", CultureInfo.InvariantCulture);
- return Path.Combine(_config.ApplicationPaths.CachePath,
+ return Path.Combine(
+ _config.ApplicationPaths.CachePath,
"channels",
channelId,
version,
@@ -920,60 +972,32 @@ namespace Emby.Server.Implementations.Channels
if (info.Type == ChannelItemType.Folder)
{
- if (info.FolderType == ChannelFolderType.MusicAlbum)
- {
- item = GetItemById<MusicAlbum>(info.Id, channelProvider.Name, out isNew);
- }
- else if (info.FolderType == ChannelFolderType.MusicArtist)
- {
- item = GetItemById<MusicArtist>(info.Id, channelProvider.Name, out isNew);
- }
- else if (info.FolderType == ChannelFolderType.PhotoAlbum)
- {
- item = GetItemById<PhotoAlbum>(info.Id, channelProvider.Name, out isNew);
- }
- else if (info.FolderType == ChannelFolderType.Series)
- {
- item = GetItemById<Series>(info.Id, channelProvider.Name, out isNew);
- }
- else if (info.FolderType == ChannelFolderType.Season)
- {
- item = GetItemById<Season>(info.Id, channelProvider.Name, out isNew);
- }
- else
+ item = info.FolderType switch
{
- item = GetItemById<Folder>(info.Id, channelProvider.Name, out isNew);
- }
+ ChannelFolderType.MusicAlbum => GetItemById<MusicAlbum>(info.Id, channelProvider.Name, out isNew),
+ ChannelFolderType.MusicArtist => GetItemById<MusicArtist>(info.Id, channelProvider.Name, out isNew),
+ ChannelFolderType.PhotoAlbum => GetItemById<PhotoAlbum>(info.Id, channelProvider.Name, out isNew),
+ ChannelFolderType.Series => GetItemById<Series>(info.Id, channelProvider.Name, out isNew),
+ ChannelFolderType.Season => GetItemById<Season>(info.Id, channelProvider.Name, out isNew),
+ _ => GetItemById<Folder>(info.Id, channelProvider.Name, out isNew)
+ };
}
else if (info.MediaType == ChannelMediaType.Audio)
{
- if (info.ContentType == ChannelMediaContentType.Podcast)
- {
- item = GetItemById<AudioBook>(info.Id, channelProvider.Name, out isNew);
- }
- else
- {
- item = GetItemById<Audio>(info.Id, channelProvider.Name, out isNew);
- }
+ item = info.ContentType == ChannelMediaContentType.Podcast
+ ? GetItemById<AudioBook>(info.Id, channelProvider.Name, out isNew)
+ : GetItemById<Audio>(info.Id, channelProvider.Name, out isNew);
}
else
{
- if (info.ContentType == ChannelMediaContentType.Episode)
- {
- item = GetItemById<Episode>(info.Id, channelProvider.Name, out isNew);
- }
- else if (info.ContentType == ChannelMediaContentType.Movie)
- {
- item = GetItemById<Movie>(info.Id, channelProvider.Name, out isNew);
- }
- else if (info.ContentType == ChannelMediaContentType.Trailer || info.ExtraType == ExtraType.Trailer)
+ item = info.ContentType switch
{
- item = GetItemById<Trailer>(info.Id, channelProvider.Name, out isNew);
- }
- else
- {
- item = GetItemById<Video>(info.Id, channelProvider.Name, out isNew);
- }
+ ChannelMediaContentType.Episode => GetItemById<Episode>(info.Id, channelProvider.Name, out isNew),
+ ChannelMediaContentType.Movie => GetItemById<Movie>(info.Id, channelProvider.Name, out isNew),
+ var x when x == ChannelMediaContentType.Trailer || info.ExtraType == ExtraType.Trailer
+ => GetItemById<Trailer>(info.Id, channelProvider.Name, out isNew),
+ _ => GetItemById<Video>(info.Id, channelProvider.Name, out isNew)
+ };
}
var enableMediaProbe = channelProvider is ISupportsMediaProbe;
@@ -982,7 +1006,6 @@ namespace Emby.Server.Implementations.Channels
{
item.RunTimeTicks = null;
}
-
else if (isNew || !enableMediaProbe)
{
item.RunTimeTicks = info.RunTimeTicks;
@@ -1015,26 +1038,24 @@ namespace Emby.Server.Implementations.Channels
}
}
- var hasArtists = item as IHasArtist;
- if (hasArtists != null)
+ if (item is IHasArtist hasArtists)
{
hasArtists.Artists = info.Artists.ToArray();
}
- var hasAlbumArtists = item as IHasAlbumArtist;
- if (hasAlbumArtists != null)
+ if (item is IHasAlbumArtist hasAlbumArtists)
{
hasAlbumArtists.AlbumArtists = info.AlbumArtists.ToArray();
}
- var trailer = item as Trailer;
- if (trailer != null)
+ if (item is Trailer trailer)
{
if (!info.TrailerTypes.SequenceEqual(trailer.TrailerTypes))
{
_logger.LogDebug("Forcing update due to TrailerTypes {0}", item.Name);
forceUpdate = true;
}
+
trailer.TrailerTypes = info.TrailerTypes.ToArray();
}
@@ -1058,6 +1079,7 @@ namespace Emby.Server.Implementations.Channels
forceUpdate = true;
_logger.LogDebug("Forcing update due to ChannelId {0}", item.Name);
}
+
item.ChannelId = internalChannelId;
if (!item.ParentId.Equals(parentFolderId))
@@ -1065,16 +1087,17 @@ namespace Emby.Server.Implementations.Channels
forceUpdate = true;
_logger.LogDebug("Forcing update due to parent folder Id {0}", item.Name);
}
+
item.ParentId = parentFolderId;
- var hasSeries = item as IHasSeries;
- if (hasSeries != null)
+ if (item is IHasSeries hasSeries)
{
if (!string.Equals(hasSeries.SeriesName, info.SeriesName, StringComparison.OrdinalIgnoreCase))
{
forceUpdate = true;
_logger.LogDebug("Forcing update due to SeriesName {0}", item.Name);
}
+
hasSeries.SeriesName = info.SeriesName;
}
@@ -1083,24 +1106,23 @@ namespace Emby.Server.Implementations.Channels
forceUpdate = true;
_logger.LogDebug("Forcing update due to ExternalId {0}", item.Name);
}
+
item.ExternalId = info.Id;
- var channelAudioItem = item as Audio;
- if (channelAudioItem != null)
+ if (item is Audio channelAudioItem)
{
channelAudioItem.ExtraType = info.ExtraType;
var mediaSource = info.MediaSources.FirstOrDefault();
- item.Path = mediaSource == null ? null : mediaSource.Path;
+ item.Path = mediaSource?.Path;
}
- var channelVideoItem = item as Video;
- if (channelVideoItem != null)
+ if (item is Video channelVideoItem)
{
channelVideoItem.ExtraType = info.ExtraType;
var mediaSource = info.MediaSources.FirstOrDefault();
- item.Path = mediaSource == null ? null : mediaSource.Path;
+ item.Path = mediaSource?.Path;
}
if (!string.IsNullOrEmpty(info.ImageUrl) && !item.HasImage(ImageType.Primary))
@@ -1157,7 +1179,7 @@ namespace Emby.Server.Implementations.Channels
}
}
- if (isNew || forceUpdate || item.DateLastRefreshed == default(DateTime))
+ if (isNew || forceUpdate || item.DateLastRefreshed == default)
{
_providerManager.QueueRefresh(item.Id, new MetadataRefreshOptions(new DirectoryService(_fileSystem)), RefreshPriority.Normal);
}
diff --git a/Emby.Server.Implementations/Channels/ChannelPostScanTask.cs b/Emby.Server.Implementations/Channels/ChannelPostScanTask.cs
index 6cbd04fea..eeb49b8fe 100644
--- a/Emby.Server.Implementations/Channels/ChannelPostScanTask.cs
+++ b/Emby.Server.Implementations/Channels/ChannelPostScanTask.cs
@@ -1,6 +1,3 @@
-#pragma warning disable CS1591
-#pragma warning disable SA1600
-
using System;
using System.Linq;
using System.Threading;
@@ -12,21 +9,34 @@ using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.Channels
{
+ /// <summary>
+ /// A task to remove all non-installed channels from the database.
+ /// </summary>
public class ChannelPostScanTask
{
private readonly IChannelManager _channelManager;
- private readonly IUserManager _userManager;
private readonly ILogger _logger;
private readonly ILibraryManager _libraryManager;
- public ChannelPostScanTask(IChannelManager channelManager, IUserManager userManager, ILogger logger, ILibraryManager libraryManager)
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ChannelPostScanTask"/> class.
+ /// </summary>
+ /// <param name="channelManager">The channel manager.</param>
+ /// <param name="logger">The logger.</param>
+ /// <param name="libraryManager">The library manager.</param>
+ public ChannelPostScanTask(IChannelManager channelManager, ILogger logger, ILibraryManager libraryManager)
{
_channelManager = channelManager;
- _userManager = userManager;
_logger = logger;
_libraryManager = libraryManager;
}
+ /// <summary>
+ /// Runs this task.
+ /// </summary>
+ /// <param name="progress">The progress.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>The completed task.</returns>
public Task Run(IProgress<double> progress, CancellationToken cancellationToken)
{
CleanDatabase(cancellationToken);
diff --git a/Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs b/Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs
index 91d72d930..54b621e25 100644
--- a/Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs
+++ b/Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs
@@ -1,6 +1,3 @@
-#pragma warning disable CS1591
-#pragma warning disable SA1600
-
using System;
using System.Collections.Generic;
using System.Threading;
@@ -8,38 +5,49 @@ using System.Threading.Tasks;
using MediaBrowser.Common.Progress;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Library;
+using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Tasks;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.Channels
{
+ /// <summary>
+ /// The "Refresh Channels" scheduled task.
+ /// </summary>
public class RefreshChannelsScheduledTask : IScheduledTask, IConfigurableScheduledTask
{
private readonly IChannelManager _channelManager;
- private readonly IUserManager _userManager;
private readonly ILogger _logger;
private readonly ILibraryManager _libraryManager;
+ private readonly ILocalizationManager _localization;
+ /// <summary>
+ /// Initializes a new instance of the <see cref="RefreshChannelsScheduledTask"/> class.
+ /// </summary>
+ /// <param name="channelManager">The channel manager.</param>
+ /// <param name="logger">The logger.</param>
+ /// <param name="libraryManager">The library manager.</param>
+ /// <param name="localization">The localization manager.</param>
public RefreshChannelsScheduledTask(
IChannelManager channelManager,
- IUserManager userManager,
ILogger<RefreshChannelsScheduledTask> logger,
- ILibraryManager libraryManager)
+ ILibraryManager libraryManager,
+ ILocalizationManager localization)
{
_channelManager = channelManager;
- _userManager = userManager;
_logger = logger;
_libraryManager = libraryManager;
+ _localization = localization;
}
/// <inheritdoc />
- public string Name => "Refresh Channels";
+ public string Name => _localization.GetLocalizedString("TasksRefreshChannels");
/// <inheritdoc />
- public string Description => "Refreshes internet channel information.";
+ public string Description => _localization.GetLocalizedString("TasksRefreshChannelsDescription");
/// <inheritdoc />
- public string Category => "Internet Channels";
+ public string Category => _localization.GetLocalizedString("TasksChannelsCategory");
/// <inheritdoc />
public bool IsHidden => ((ChannelManager)_channelManager).Channels.Length == 0;
@@ -60,7 +68,7 @@ namespace Emby.Server.Implementations.Channels
await manager.RefreshChannels(new SimpleProgress<double>(), cancellationToken).ConfigureAwait(false);
- await new ChannelPostScanTask(_channelManager, _userManager, _logger, _libraryManager).Run(progress, cancellationToken)
+ await new ChannelPostScanTask(_channelManager, _logger, _libraryManager).Run(progress, cancellationToken)
.ConfigureAwait(false);
}
@@ -69,7 +77,6 @@ namespace Emby.Server.Implementations.Channels
{
return new[]
{
-
// Every so often
new TaskTriggerInfo
{
diff --git a/Emby.Server.Implementations/Collections/CollectionImageProvider.cs b/Emby.Server.Implementations/Collections/CollectionImageProvider.cs
index 8b1407984..c69a07e83 100644
--- a/Emby.Server.Implementations/Collections/CollectionImageProvider.cs
+++ b/Emby.Server.Implementations/Collections/CollectionImageProvider.cs
@@ -1,6 +1,3 @@
-#pragma warning disable CS1591
-#pragma warning disable SA1600
-
using System.Collections.Generic;
using System.Linq;
using Emby.Server.Implementations.Images;
@@ -16,8 +13,18 @@ using MediaBrowser.Model.IO;
namespace Emby.Server.Implementations.Collections
{
+ /// <summary>
+ /// A collection image provider.
+ /// </summary>
public class CollectionImageProvider : BaseDynamicImageProvider<BoxSet>
{
+ /// <summary>
+ /// Initializes a new instance of the <see cref="CollectionImageProvider"/> class.
+ /// </summary>
+ /// <param name="fileSystem">The filesystem.</param>
+ /// <param name="providerManager">The provider manager.</param>
+ /// <param name="applicationPaths">The application paths.</param>
+ /// <param name="imageProcessor">The image processor.</param>
public CollectionImageProvider(
IFileSystem fileSystem,
IProviderManager providerManager,
@@ -27,6 +34,7 @@ namespace Emby.Server.Implementations.Collections
{
}
+ /// <inheritdoc />
protected override bool Supports(BaseItem item)
{
// Right now this is the only way to prevent this image from getting created ahead of internet image providers
@@ -38,6 +46,7 @@ namespace Emby.Server.Implementations.Collections
return base.Supports(item);
}
+ /// <inheritdoc />
protected override IReadOnlyList<BaseItem> GetItemsWithImages(BaseItem item)
{
var playlist = (BoxSet)item;
@@ -49,13 +58,10 @@ namespace Emby.Server.Implementations.Collections
var episode = subItem as Episode;
- if (episode != null)
+ var series = episode?.Series;
+ if (series != null && series.HasImage(ImageType.Primary))
{
- var series = episode.Series;
- if (series != null && series.HasImage(ImageType.Primary))
- {
- return series;
- }
+ return series;
}
if (subItem.HasImage(ImageType.Primary))
@@ -81,6 +87,7 @@ namespace Emby.Server.Implementations.Collections
.ToList();
}
+ /// <inheritdoc />
protected override string CreateImage(BaseItem item, IReadOnlyCollection<BaseItem> itemsWithImages, string outputPathWithoutExtension, ImageType imageType, int imageIndex)
{
return CreateSingleImage(itemsWithImages, outputPathWithoutExtension, ImageType.Primary);
diff --git a/Emby.Server.Implementations/Collections/CollectionManager.cs b/Emby.Server.Implementations/Collections/CollectionManager.cs
index 1d477094c..7c518d483 100644
--- a/Emby.Server.Implementations/Collections/CollectionManager.cs
+++ b/Emby.Server.Implementations/Collections/CollectionManager.cs
@@ -1,6 +1,3 @@
-#pragma warning disable CS1591
-#pragma warning disable SA1600
-
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -24,6 +21,9 @@ using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.Collections
{
+ /// <summary>
+ /// The collection manager.
+ /// </summary>
public class CollectionManager : ICollectionManager
{
private readonly ILibraryManager _libraryManager;
@@ -34,6 +34,16 @@ namespace Emby.Server.Implementations.Collections
private readonly ILocalizationManager _localizationManager;
private readonly IApplicationPaths _appPaths;
+ /// <summary>
+ /// Initializes a new instance of the <see cref="CollectionManager"/> class.
+ /// </summary>
+ /// <param name="libraryManager">The library manager.</param>
+ /// <param name="appPaths">The application paths.</param>
+ /// <param name="localizationManager">The localization manager.</param>
+ /// <param name="fileSystem">The filesystem.</param>
+ /// <param name="iLibraryMonitor">The library monitor.</param>
+ /// <param name="loggerFactory">The logger factory.</param>
+ /// <param name="providerManager">The provider manager.</param>
public CollectionManager(
ILibraryManager libraryManager,
IApplicationPaths appPaths,
@@ -52,8 +62,13 @@ namespace Emby.Server.Implementations.Collections
_appPaths = appPaths;
}
+ /// <inheritdoc />
public event EventHandler<CollectionCreatedEventArgs> CollectionCreated;
+
+ /// <inheritdoc />
public event EventHandler<CollectionModifiedEventArgs> ItemsAddedToCollection;
+
+ /// <inheritdoc />
public event EventHandler<CollectionModifiedEventArgs> ItemsRemovedFromCollection;
private IEnumerable<Folder> FindFolders(string path)
@@ -110,11 +125,12 @@ namespace Emby.Server.Implementations.Collections
{
var folder = GetCollectionsFolder(false).Result;
- return folder == null ?
- new List<BoxSet>() :
- folder.GetChildren(user, true).OfType<BoxSet>();
+ return folder == null
+ ? Enumerable.Empty<BoxSet>()
+ : folder.GetChildren(user, true).OfType<BoxSet>();
}
+ /// <inheritdoc />
public BoxSet CreateCollection(CollectionCreationOptions options)
{
var name = options.Name;
@@ -179,11 +195,13 @@ namespace Emby.Server.Implementations.Collections
}
}
+ /// <inheritdoc />
public void AddToCollection(Guid collectionId, IEnumerable<string> ids)
{
AddToCollection(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)));
@@ -192,7 +210,6 @@ namespace Emby.Server.Implementations.Collections
private void AddToCollection(Guid collectionId, IEnumerable<string> ids, bool fireEvent, MetadataRefreshOptions refreshOptions)
{
var collection = _libraryManager.GetItemById(collectionId) as BoxSet;
-
if (collection == null)
{
throw new ArgumentException("No collection exists with the supplied Id");
@@ -247,11 +264,13 @@ 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)
{
var collection = _libraryManager.GetItemById(collectionId) as BoxSet;
@@ -290,10 +309,13 @@ namespace Emby.Server.Implementations.Collections
}
collection.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None);
- _providerManager.QueueRefresh(collection.Id, new MetadataRefreshOptions(new DirectoryService(_fileSystem))
- {
- ForceSave = true
- }, RefreshPriority.High);
+ _providerManager.QueueRefresh(
+ collection.Id,
+ new MetadataRefreshOptions(new DirectoryService(_fileSystem))
+ {
+ ForceSave = true
+ },
+ RefreshPriority.High);
ItemsRemovedFromCollection?.Invoke(this, new CollectionModifiedEventArgs
{
@@ -302,6 +324,7 @@ namespace Emby.Server.Implementations.Collections
});
}
+ /// <inheritdoc />
public IEnumerable<BaseItem> CollapseItemsWithinBoxSets(IEnumerable<BaseItem> items, User user)
{
var results = new Dictionary<Guid, BaseItem>();
@@ -310,9 +333,7 @@ namespace Emby.Server.Implementations.Collections
foreach (var item in items)
{
- var grouping = item as ISupportsBoxSetGrouping;
-
- if (grouping == null)
+ if (!(item is ISupportsBoxSetGrouping))
{
results[item.Id] = item;
}
@@ -342,12 +363,21 @@ namespace Emby.Server.Implementations.Collections
}
}
+ /// <summary>
+ /// The collection manager entry point.
+ /// </summary>
public sealed class CollectionManagerEntryPoint : IServerEntryPoint
{
private readonly CollectionManager _collectionManager;
private readonly IServerConfigurationManager _config;
private readonly ILogger _logger;
+ /// <summary>
+ /// Initializes a new instance of the <see cref="CollectionManagerEntryPoint"/> class.
+ /// </summary>
+ /// <param name="collectionManager">The collection manager.</param>
+ /// <param name="config">The server configuration manager.</param>
+ /// <param name="logger">The logger.</param>
public CollectionManagerEntryPoint(
ICollectionManager collectionManager,
IServerConfigurationManager config,
diff --git a/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs b/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs
index 30b654886..a6eaf2d0a 100644
--- a/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs
+++ b/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs
@@ -67,23 +67,22 @@ namespace Emby.Server.Implementations.Configuration
/// <summary>
/// Updates the metadata path.
/// </summary>
+ /// <exception cref="UnauthorizedAccessException">If the directory does not exist, and the caller does not have the required permission to create it.</exception>
+ /// <exception cref="NotSupportedException">If there is a custom path transcoding path specified, but it is invalid.</exception>
+ /// <exception cref="IOException">If the directory does not exist, and it also could not be created.</exception>
private void UpdateMetadataPath()
{
- if (string.IsNullOrWhiteSpace(Configuration.MetadataPath))
- {
- ((ServerApplicationPaths)ApplicationPaths).InternalMetadataPath = Path.Combine(ApplicationPaths.ProgramDataPath, "metadata");
- }
- else
- {
- ((ServerApplicationPaths)ApplicationPaths).InternalMetadataPath = Configuration.MetadataPath;
- }
+ ((ServerApplicationPaths)ApplicationPaths).InternalMetadataPath = string.IsNullOrWhiteSpace(Configuration.MetadataPath)
+ ? ApplicationPaths.DefaultInternalMetadataPath
+ : Configuration.MetadataPath;
+ Directory.CreateDirectory(ApplicationPaths.InternalMetadataPath);
}
/// <summary>
/// Replaces the configuration.
/// </summary>
/// <param name="newConfiguration">The new configuration.</param>
- /// <exception cref="DirectoryNotFoundException"></exception>
+ /// <exception cref="DirectoryNotFoundException">If the configuration path doesn't exist.</exception>
public override void ReplaceConfiguration(BaseApplicationConfiguration newConfiguration)
{
var newConfig = (ServerConfiguration)newConfiguration;
@@ -132,7 +131,7 @@ namespace Emby.Server.Implementations.Configuration
var newPath = newConfig.MetadataPath;
if (!string.IsNullOrWhiteSpace(newPath)
- && !string.Equals(Configuration.MetadataPath, newPath, StringComparison.Ordinal))
+ && !string.Equals(Configuration.MetadataPath, newPath, StringComparison.Ordinal))
{
// Validate
if (!Directory.Exists(newPath))
diff --git a/Emby.Server.Implementations/ConfigurationOptions.cs b/Emby.Server.Implementations/ConfigurationOptions.cs
index d0f3d6723..db7c35a7c 100644
--- a/Emby.Server.Implementations/ConfigurationOptions.cs
+++ b/Emby.Server.Implementations/ConfigurationOptions.cs
@@ -1,15 +1,27 @@
using System.Collections.Generic;
+using Emby.Server.Implementations.HttpServer;
+using Emby.Server.Implementations.Updates;
+using MediaBrowser.Providers.Music;
using static MediaBrowser.Controller.Extensions.ConfigurationExtensions;
namespace Emby.Server.Implementations
{
+ /// <summary>
+ /// Static class containing the default configuration options for the web server.
+ /// </summary>
public static class ConfigurationOptions
{
- public static Dictionary<string, string> Configuration => new Dictionary<string, string>
+ /// <summary>
+ /// Gets a new copy of the default configuration options.
+ /// </summary>
+ public static Dictionary<string, string> DefaultConfiguration => new Dictionary<string, string>
{
- { "HttpListenerHost:DefaultRedirectPath", "web/index.html" },
+ { HostWebClientKey, bool.TrueString },
+ { HttpListenerHost.DefaultRedirectKey, "web/index.html" },
+ { InstallationManager.PluginManifestUrlKey, "https://repo.jellyfin.org/releases/plugin/manifest-stable.json" },
{ FfmpegProbeSizeKey, "1G" },
- { FfmpegAnalyzeDurationKey, "200M" }
+ { FfmpegAnalyzeDurationKey, "200M" },
+ { PlaylistsAllowDuplicatesKey, bool.TrueString }
};
}
}
diff --git a/Emby.Server.Implementations/Cryptography/CryptographyProvider.cs b/Emby.Server.Implementations/Cryptography/CryptographyProvider.cs
index de83b023d..a037415a9 100644
--- a/Emby.Server.Implementations/Cryptography/CryptographyProvider.cs
+++ b/Emby.Server.Implementations/Cryptography/CryptographyProvider.cs
@@ -31,7 +31,7 @@ namespace Emby.Server.Implementations.Cryptography
private RandomNumberGenerator _randomNumberGenerator;
- private bool _disposed = false;
+ private bool _disposed;
/// <summary>
/// Initializes a new instance of the <see cref="CryptographyProvider"/> class.
@@ -56,15 +56,13 @@ namespace Emby.Server.Implementations.Cryptography
{
// downgrading for now as we need this library to be dotnetstandard compliant
// with this downgrade we'll add a check to make sure we're on the downgrade method at the moment
- if (method == DefaultHashMethod)
+ if (method != DefaultHashMethod)
{
- using (var r = new Rfc2898DeriveBytes(bytes, salt, iterations))
- {
- return r.GetBytes(32);
- }
+ throw new CryptographicException($"Cannot currently use PBKDF2 with requested hash method: {method}");
}
- throw new CryptographicException($"Cannot currently use PBKDF2 with requested hash method: {method}");
+ using var r = new Rfc2898DeriveBytes(bytes, salt, iterations);
+ return r.GetBytes(32);
}
/// <inheritdoc />
@@ -74,25 +72,22 @@ namespace Emby.Server.Implementations.Cryptography
{
return PBKDF2(hashMethod, bytes, salt, DefaultIterations);
}
- else if (_supportedHashMethods.Contains(hashMethod))
+
+ if (!_supportedHashMethods.Contains(hashMethod))
+ {
+ throw new CryptographicException($"Requested hash method is not supported: {hashMethod}");
+ }
+
+ using var h = HashAlgorithm.Create(hashMethod);
+ if (salt.Length == 0)
{
- using (var h = HashAlgorithm.Create(hashMethod))
- {
- if (salt.Length == 0)
- {
- return h.ComputeHash(bytes);
- }
- else
- {
- byte[] salted = new byte[bytes.Length + salt.Length];
- Array.Copy(bytes, salted, bytes.Length);
- Array.Copy(salt, 0, salted, bytes.Length, salt.Length);
- return h.ComputeHash(salted);
- }
- }
+ return h.ComputeHash(bytes);
}
- throw new CryptographicException($"Requested hash method is not supported: {hashMethod}");
+ byte[] salted = new byte[bytes.Length + salt.Length];
+ Array.Copy(bytes, salted, bytes.Length);
+ Array.Copy(salt, 0, salted, bytes.Length, salt.Length);
+ return h.ComputeHash(salted);
}
/// <inheritdoc />
diff --git a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs
index b7f643819..0654132f4 100644
--- a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs
+++ b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs b/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs
index f51dfb725..37c678a5d 100644
--- a/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs
+++ b/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Threading;
diff --git a/Emby.Server.Implementations/Data/ManagedConnection.cs b/Emby.Server.Implementations/Data/ManagedConnection.cs
index 2c2f19cd3..5c094ddd2 100644
--- a/Emby.Server.Implementations/Data/ManagedConnection.cs
+++ b/Emby.Server.Implementations/Data/ManagedConnection.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs b/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs
index 8087419ce..d474f1c6b 100644
--- a/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs
+++ b/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/Data/SqliteExtensions.cs b/Emby.Server.Implementations/Data/SqliteExtensions.cs
index 55c24ccc0..716e5071d 100644
--- a/Emby.Server.Implementations/Data/SqliteExtensions.cs
+++ b/Emby.Server.Implementations/Data/SqliteExtensions.cs
@@ -1,11 +1,8 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Collections.Generic;
using System.Globalization;
-using System.IO;
-using MediaBrowser.Model.Serialization;
using SQLitePCL.pretty;
namespace Emby.Server.Implementations.Data
@@ -110,25 +107,6 @@ namespace Emby.Server.Implementations.Data
return null;
}
- /// <summary>
- /// Serializes to bytes.
- /// </summary>
- /// <returns>System.Byte[][].</returns>
- /// <exception cref="ArgumentNullException">obj</exception>
- public static byte[] SerializeToBytes(this IJsonSerializer json, object obj)
- {
- if (obj == null)
- {
- throw new ArgumentNullException(nameof(obj));
- }
-
- using (var stream = new MemoryStream())
- {
- json.SerializeToStream(obj, stream);
- return stream.ToArray();
- }
- }
-
public static void Attach(SQLiteDatabaseConnection db, string path, string alias)
{
var commandText = string.Format(
@@ -288,7 +266,7 @@ namespace Emby.Server.Implementations.Data
}
}
- public static void TryBind(this IStatement statement, string name, byte[] value)
+ public static void TryBind(this IStatement statement, string name, ReadOnlySpan<byte> value)
{
if (statement.BindParameters.TryGetValue(name, out IBindParameter bindParam))
{
@@ -384,11 +362,11 @@ namespace Emby.Server.Implementations.Data
}
}
- public static IEnumerable<IReadOnlyList<IResultSetValue>> ExecuteQuery(this IStatement This)
+ public static IEnumerable<IReadOnlyList<IResultSetValue>> ExecuteQuery(this IStatement statement)
{
- while (This.MoveNext())
+ while (statement.MoveNext())
{
- yield return This.Current;
+ yield return statement.Current;
}
}
}
diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
index 44f38504a..ca5cd6fdd 100644
--- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs
+++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
@@ -39,12 +39,11 @@ namespace Emby.Server.Implementations.Data
{
private const string ChaptersTableName = "Chapters2";
- /// <summary>
- /// The _app paths
- /// </summary>
private readonly IServerConfigurationManager _config;
private readonly IServerApplicationHost _appHost;
private readonly ILocalizationManager _localization;
+ // TODO: Remove this dependency. GetImageCacheTag() is the only method used and it can be converted to a static helper method
+ private readonly IImageProcessor _imageProcessor;
private readonly TypeMapper _typeMapper;
private readonly JsonSerializerOptions _jsonOptions;
@@ -71,7 +70,8 @@ namespace Emby.Server.Implementations.Data
IServerConfigurationManager config,
IServerApplicationHost appHost,
ILogger<SqliteItemRepository> logger,
- ILocalizationManager localization)
+ ILocalizationManager localization,
+ IImageProcessor imageProcessor)
: base(logger)
{
if (config == null)
@@ -82,6 +82,7 @@ namespace Emby.Server.Implementations.Data
_config = config;
_appHost = appHost;
_localization = localization;
+ _imageProcessor = imageProcessor;
_typeMapper = new TypeMapper();
_jsonOptions = JsonDefaults.GetOptions();
@@ -98,8 +99,6 @@ namespace Emby.Server.Implementations.Data
/// <inheritdoc />
protected override TempStoreMode TempStore => TempStoreMode.Memory;
- public IImageProcessor ImageProcessor { get; set; }
-
/// <summary>
/// Opens the connection to the database
/// </summary>
@@ -454,7 +453,7 @@ namespace Emby.Server.Implementations.Data
private static string GetSaveItemCommandText()
{
- var saveColumns = new []
+ var saveColumns = new[]
{
"guid",
"type",
@@ -560,7 +559,7 @@ namespace Emby.Server.Implementations.Data
throw new ArgumentNullException(nameof(item));
}
- SaveItems(new [] { item }, cancellationToken);
+ SaveItems(new[] { item }, cancellationToken);
}
public void SaveImages(BaseItem item)
@@ -1622,7 +1621,7 @@ namespace Emby.Server.Implementations.Data
{
IEnumerable<MetadataFields> GetLockedFields(string s)
{
- foreach (var i in s.Split(new [] { '|' }, StringSplitOptions.RemoveEmptyEntries))
+ foreach (var i in s.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries))
{
if (Enum.TryParse(i, true, out MetadataFields parsedValue))
{
@@ -1818,7 +1817,7 @@ namespace Emby.Server.Implementations.Data
{
if (!reader.IsDBNull(index))
{
- item.ProductionLocations = reader.GetString(index).Split(new [] { '|' }, StringSplitOptions.RemoveEmptyEntries).ToArray();
+ item.ProductionLocations = reader.GetString(index).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries).ToArray();
}
index++;
}
@@ -1991,7 +1990,14 @@ namespace Emby.Server.Implementations.Data
if (!string.IsNullOrEmpty(chapter.ImagePath))
{
- chapter.ImageTag = ImageProcessor.GetImageCacheTag(item, chapter);
+ try
+ {
+ chapter.ImageTag = _imageProcessor.GetImageCacheTag(item, chapter);
+ }
+ catch (Exception ex)
+ {
+ Logger.LogError(ex, "Failed to create image cache tag.");
+ }
}
}
@@ -2006,7 +2012,7 @@ namespace Emby.Server.Implementations.Data
/// <summary>
/// Saves the chapters.
/// </summary>
- public void SaveChapters(Guid id, List<ChapterInfo> chapters)
+ public void SaveChapters(Guid id, IReadOnlyList<ChapterInfo> chapters)
{
CheckDisposed();
@@ -2035,22 +2041,24 @@ namespace Emby.Server.Implementations.Data
}
}
- private void InsertChapters(byte[] idBlob, List<ChapterInfo> chapters, IDatabaseConnection db)
+ private void InsertChapters(byte[] idBlob, IReadOnlyList<ChapterInfo> chapters, IDatabaseConnection db)
{
var startIndex = 0;
var limit = 100;
var chapterIndex = 0;
+ const string StartInsertText = "insert into " + ChaptersTableName + " (ItemId, ChapterIndex, StartPositionTicks, Name, ImagePath, ImageDateModified) values ";
+ var insertText = new StringBuilder(StartInsertText, 256);
+
while (startIndex < chapters.Count)
{
- var insertText = new StringBuilder("insert into " + ChaptersTableName + " (ItemId, ChapterIndex, StartPositionTicks, Name, ImagePath, ImageDateModified) values ");
-
var endIndex = Math.Min(chapters.Count, startIndex + limit);
for (var i = startIndex; i < endIndex; i++)
{
insertText.AppendFormat("(@ItemId, @ChapterIndex{0}, @StartPositionTicks{0}, @Name{0}, @ImagePath{0}, @ImageDateModified{0}),", i.ToString(CultureInfo.InvariantCulture));
}
+
insertText.Length -= 1; // Remove last ,
using (var statement = PrepareStatement(db, insertText.ToString()))
@@ -2077,6 +2085,7 @@ namespace Emby.Server.Implementations.Data
}
startIndex += limit;
+ insertText.Length = StartInsertText.Length;
}
}
@@ -2897,8 +2906,8 @@ namespace Emby.Server.Implementations.Data
BindSimilarParams(query, statement);
BindSearchParams(query, statement);
- // Running this again will bind the params
- GetWhereClauses(query, statement);
+ // Running this again will bind the params
+ GetWhereClauses(query, statement);
result.TotalRecordCount = statement.ExecuteQuery().SelectScalarInt().First();
}
@@ -2914,29 +2923,30 @@ namespace Emby.Server.Implementations.Data
private string GetOrderByText(InternalItemsQuery query)
{
var orderBy = query.OrderBy;
- if (string.IsNullOrEmpty(query.SearchTerm))
+ bool hasSimilar = query.SimilarTo != null;
+ bool hasSearch = !string.IsNullOrEmpty(query.SearchTerm);
+
+ if (hasSimilar || hasSearch)
{
- int oldLen = orderBy.Count;
- if (oldLen == 0 && query.SimilarTo != null)
+ List<(string, SortOrder)> prepend = new List<(string, SortOrder)>(4);
+ if (hasSearch)
{
- var arr = new (string, SortOrder)[oldLen + 2];
- orderBy.CopyTo(arr, 0);
- arr[oldLen] = ("SimilarityScore", SortOrder.Descending);
- arr[oldLen + 1] = (ItemSortBy.Random, SortOrder.Ascending);
- query.OrderBy = arr;
+ prepend.Add(("SearchScore", SortOrder.Descending));
+ prepend.Add((ItemSortBy.SortName, SortOrder.Ascending));
}
- }
- else
- {
- query.OrderBy = new[]
- {
- ("SearchScore", SortOrder.Descending),
- (ItemSortBy.SortName, SortOrder.Ascending)
- };
- }
+ if (hasSimilar)
+ {
+ prepend.Add(("SimilarityScore", SortOrder.Descending));
+ prepend.Add((ItemSortBy.Random, SortOrder.Ascending));
+ }
- if (orderBy.Count == 0)
+ var arr = new (string, SortOrder)[prepend.Count + orderBy.Count];
+ prepend.CopyTo(arr, 0);
+ orderBy.CopyTo(arr, prepend.Count);
+ orderBy = query.OrderBy = arr;
+ }
+ else if (orderBy.Count == 0)
{
return string.Empty;
}
@@ -3265,8 +3275,8 @@ namespace Emby.Server.Implementations.Data
BindSimilarParams(query, statement);
BindSearchParams(query, statement);
- // Running this again will bind the params
- GetWhereClauses(query, statement);
+ // Running this again will bind the params
+ GetWhereClauses(query, statement);
foreach (var row in statement.ExecuteQuery())
{
@@ -3287,8 +3297,8 @@ namespace Emby.Server.Implementations.Data
BindSimilarParams(query, statement);
BindSearchParams(query, statement);
- // Running this again will bind the params
- GetWhereClauses(query, statement);
+ // Running this again will bind the params
+ GetWhereClauses(query, statement);
result.TotalRecordCount = statement.ExecuteQuery().SelectScalarInt().First();
}
@@ -3311,7 +3321,7 @@ namespace Emby.Server.Implementations.Data
for (int i = 0; i < str.Length; i++)
{
- if (!(char.IsLetter(str[i])) && (!(char.IsNumber(str[i]))))
+ if (!char.IsLetter(str[i]) && !char.IsNumber(str[i]))
{
return false;
}
@@ -3335,7 +3345,7 @@ namespace Emby.Server.Implementations.Data
return IsAlphaNumeric(value);
}
- private List<string> GetWhereClauses(InternalItemsQuery query, IStatement statement, string paramSuffix = "")
+ private List<string> GetWhereClauses(InternalItemsQuery query, IStatement statement)
{
if (query.IsResumable ?? false)
{
@@ -3347,27 +3357,27 @@ namespace Emby.Server.Implementations.Data
if (query.IsHD.HasValue)
{
- var threshold = 1200;
+ const int Threshold = 1200;
if (query.IsHD.Value)
{
- minWidth = threshold;
+ minWidth = Threshold;
}
else
{
- maxWidth = threshold - 1;
+ maxWidth = Threshold - 1;
}
}
if (query.Is4K.HasValue)
{
- var threshold = 3800;
+ const int Threshold = 3800;
if (query.Is4K.Value)
{
- minWidth = threshold;
+ minWidth = Threshold;
}
else
{
- maxWidth = threshold - 1;
+ maxWidth = Threshold - 1;
}
}
@@ -3376,93 +3386,61 @@ namespace Emby.Server.Implementations.Data
if (minWidth.HasValue)
{
whereClauses.Add("Width>=@MinWidth");
- if (statement != null)
- {
- statement.TryBind("@MinWidth", minWidth);
- }
+ statement?.TryBind("@MinWidth", minWidth);
}
+
if (query.MinHeight.HasValue)
{
whereClauses.Add("Height>=@MinHeight");
- if (statement != null)
- {
- statement.TryBind("@MinHeight", query.MinHeight);
- }
+ statement?.TryBind("@MinHeight", query.MinHeight);
}
+
if (maxWidth.HasValue)
{
whereClauses.Add("Width<=@MaxWidth");
- if (statement != null)
- {
- statement.TryBind("@MaxWidth", maxWidth);
- }
+ statement?.TryBind("@MaxWidth", maxWidth);
}
+
if (query.MaxHeight.HasValue)
{
whereClauses.Add("Height<=@MaxHeight");
- if (statement != null)
- {
- statement.TryBind("@MaxHeight", query.MaxHeight);
- }
+ statement?.TryBind("@MaxHeight", query.MaxHeight);
}
if (query.IsLocked.HasValue)
{
whereClauses.Add("IsLocked=@IsLocked");
- if (statement != null)
- {
- statement.TryBind("@IsLocked", query.IsLocked);
- }
+ statement?.TryBind("@IsLocked", query.IsLocked);
}
var tags = query.Tags.ToList();
var excludeTags = query.ExcludeTags.ToList();
- if (query.IsMovie ?? false)
+ if (query.IsMovie == true)
{
- var alternateTypes = new List<string>();
- if (query.IncludeItemTypes.Length == 0 || query.IncludeItemTypes.Contains(typeof(Movie).Name))
+ if (query.IncludeItemTypes.Length == 0
+ || query.IncludeItemTypes.Contains(nameof(Movie))
+ || query.IncludeItemTypes.Contains(nameof(Trailer)))
{
- alternateTypes.Add(typeof(Movie).FullName);
- }
- if (query.IncludeItemTypes.Length == 0 || query.IncludeItemTypes.Contains(typeof(Trailer).Name))
- {
- alternateTypes.Add(typeof(Trailer).FullName);
- }
-
- var programAttribtues = new List<string>();
- if (alternateTypes.Count == 0)
- {
- programAttribtues.Add("IsMovie=@IsMovie");
+ whereClauses.Add("(IsMovie is null OR IsMovie=@IsMovie)");
}
else
{
- programAttribtues.Add("(IsMovie is null OR IsMovie=@IsMovie)");
- }
-
- if (statement != null)
- {
- statement.TryBind("@IsMovie", true);
+ whereClauses.Add("IsMovie=@IsMovie");
}
- whereClauses.Add("(" + string.Join(" OR ", programAttribtues) + ")");
+ statement?.TryBind("@IsMovie", true);
}
else if (query.IsMovie.HasValue)
{
whereClauses.Add("IsMovie=@IsMovie");
- if (statement != null)
- {
- statement.TryBind("@IsMovie", query.IsMovie);
- }
+ statement?.TryBind("@IsMovie", query.IsMovie);
}
if (query.IsSeries.HasValue)
{
whereClauses.Add("IsSeries=@IsSeries");
- if (statement != null)
- {
- statement.TryBind("@IsSeries", query.IsSeries);
- }
+ statement?.TryBind("@IsSeries", query.IsSeries);
}
if (query.IsSports.HasValue)
@@ -3514,10 +3492,7 @@ namespace Emby.Server.Implementations.Data
if (query.IsFolder.HasValue)
{
whereClauses.Add("IsFolder=@IsFolder");
- if (statement != null)
- {
- statement.TryBind("@IsFolder", query.IsFolder);
- }
+ statement?.TryBind("@IsFolder", query.IsFolder);
}
var includeTypes = query.IncludeItemTypes.SelectMany(MapIncludeItemTypes).ToArray();
@@ -3528,10 +3503,7 @@ namespace Emby.Server.Implementations.Data
if (excludeTypes.Length == 1)
{
whereClauses.Add("type<>@type");
- if (statement != null)
- {
- statement.TryBind("@type", excludeTypes[0]);
- }
+ statement?.TryBind("@type", excludeTypes[0]);
}
else if (excludeTypes.Length > 1)
{
@@ -3542,10 +3514,7 @@ namespace Emby.Server.Implementations.Data
else if (includeTypes.Length == 1)
{
whereClauses.Add("type=@type");
- if (statement != null)
- {
- statement.TryBind("@type", includeTypes[0]);
- }
+ statement?.TryBind("@type", includeTypes[0]);
}
else if (includeTypes.Length > 1)
{
@@ -3556,10 +3525,7 @@ namespace Emby.Server.Implementations.Data
if (query.ChannelIds.Length == 1)
{
whereClauses.Add("ChannelId=@ChannelId");
- if (statement != null)
- {
- statement.TryBind("@ChannelId", query.ChannelIds[0].ToString("N", CultureInfo.InvariantCulture));
- }
+ statement?.TryBind("@ChannelId", query.ChannelIds[0].ToString("N", CultureInfo.InvariantCulture));
}
else if (query.ChannelIds.Length > 1)
{
@@ -3570,98 +3536,65 @@ namespace Emby.Server.Implementations.Data
if (!query.ParentId.Equals(Guid.Empty))
{
whereClauses.Add("ParentId=@ParentId");
- if (statement != null)
- {
- statement.TryBind("@ParentId", query.ParentId);
- }
+ statement?.TryBind("@ParentId", query.ParentId);
}
if (!string.IsNullOrWhiteSpace(query.Path))
{
whereClauses.Add("Path=@Path");
- if (statement != null)
- {
- statement.TryBind("@Path", GetPathToSave(query.Path));
- }
+ statement?.TryBind("@Path", GetPathToSave(query.Path));
}
if (!string.IsNullOrWhiteSpace(query.PresentationUniqueKey))
{
whereClauses.Add("PresentationUniqueKey=@PresentationUniqueKey");
- if (statement != null)
- {
- statement.TryBind("@PresentationUniqueKey", query.PresentationUniqueKey);
- }
+ statement?.TryBind("@PresentationUniqueKey", query.PresentationUniqueKey);
}
if (query.MinCommunityRating.HasValue)
{
whereClauses.Add("CommunityRating>=@MinCommunityRating");
- if (statement != null)
- {
- statement.TryBind("@MinCommunityRating", query.MinCommunityRating.Value);
- }
+ statement?.TryBind("@MinCommunityRating", query.MinCommunityRating.Value);
}
if (query.MinIndexNumber.HasValue)
{
whereClauses.Add("IndexNumber>=@MinIndexNumber");
- if (statement != null)
- {
- statement.TryBind("@MinIndexNumber", query.MinIndexNumber.Value);
- }
+ statement?.TryBind("@MinIndexNumber", query.MinIndexNumber.Value);
}
if (query.MinDateCreated.HasValue)
{
whereClauses.Add("DateCreated>=@MinDateCreated");
- if (statement != null)
- {
- statement.TryBind("@MinDateCreated", query.MinDateCreated.Value);
- }
+ statement?.TryBind("@MinDateCreated", query.MinDateCreated.Value);
}
if (query.MinDateLastSaved.HasValue)
{
whereClauses.Add("(DateLastSaved not null and DateLastSaved>=@MinDateLastSavedForUser)");
- if (statement != null)
- {
- statement.TryBind("@MinDateLastSaved", query.MinDateLastSaved.Value);
- }
+ statement?.TryBind("@MinDateLastSaved", query.MinDateLastSaved.Value);
}
if (query.MinDateLastSavedForUser.HasValue)
{
whereClauses.Add("(DateLastSaved not null and DateLastSaved>=@MinDateLastSavedForUser)");
- if (statement != null)
- {
- statement.TryBind("@MinDateLastSavedForUser", query.MinDateLastSavedForUser.Value);
- }
+ statement?.TryBind("@MinDateLastSavedForUser", query.MinDateLastSavedForUser.Value);
}
if (query.IndexNumber.HasValue)
{
whereClauses.Add("IndexNumber=@IndexNumber");
- if (statement != null)
- {
- statement.TryBind("@IndexNumber", query.IndexNumber.Value);
- }
+ statement?.TryBind("@IndexNumber", query.IndexNumber.Value);
}
if (query.ParentIndexNumber.HasValue)
{
whereClauses.Add("ParentIndexNumber=@ParentIndexNumber");
- if (statement != null)
- {
- statement.TryBind("@ParentIndexNumber", query.ParentIndexNumber.Value);
- }
+ statement?.TryBind("@ParentIndexNumber", query.ParentIndexNumber.Value);
}
if (query.ParentIndexNumberNotEquals.HasValue)
{
whereClauses.Add("(ParentIndexNumber<>@ParentIndexNumberNotEquals or ParentIndexNumber is null)");
- if (statement != null)
- {
- statement.TryBind("@ParentIndexNumberNotEquals", query.ParentIndexNumberNotEquals.Value);
- }
+ statement?.TryBind("@ParentIndexNumberNotEquals", query.ParentIndexNumberNotEquals.Value);
}
var minEndDate = query.MinEndDate;
@@ -3682,73 +3615,59 @@ namespace Emby.Server.Implementations.Data
if (minEndDate.HasValue)
{
whereClauses.Add("EndDate>=@MinEndDate");
- if (statement != null)
- {
- statement.TryBind("@MinEndDate", minEndDate.Value);
- }
+ statement?.TryBind("@MinEndDate", minEndDate.Value);
}
if (maxEndDate.HasValue)
{
whereClauses.Add("EndDate<=@MaxEndDate");
- if (statement != null)
- {
- statement.TryBind("@MaxEndDate", maxEndDate.Value);
- }
+ statement?.TryBind("@MaxEndDate", maxEndDate.Value);
}
if (query.MinStartDate.HasValue)
{
whereClauses.Add("StartDate>=@MinStartDate");
- if (statement != null)
- {
- statement.TryBind("@MinStartDate", query.MinStartDate.Value);
- }
+ statement?.TryBind("@MinStartDate", query.MinStartDate.Value);
}
if (query.MaxStartDate.HasValue)
{
whereClauses.Add("StartDate<=@MaxStartDate");
- if (statement != null)
- {
- statement.TryBind("@MaxStartDate", query.MaxStartDate.Value);
- }
+ statement?.TryBind("@MaxStartDate", query.MaxStartDate.Value);
}
if (query.MinPremiereDate.HasValue)
{
whereClauses.Add("PremiereDate>=@MinPremiereDate");
- if (statement != null)
- {
- statement.TryBind("@MinPremiereDate", query.MinPremiereDate.Value);
- }
+ statement?.TryBind("@MinPremiereDate", query.MinPremiereDate.Value);
}
+
if (query.MaxPremiereDate.HasValue)
{
whereClauses.Add("PremiereDate<=@MaxPremiereDate");
- if (statement != null)
- {
- statement.TryBind("@MaxPremiereDate", query.MaxPremiereDate.Value);
- }
+ statement?.TryBind("@MaxPremiereDate", query.MaxPremiereDate.Value);
}
- if (query.TrailerTypes.Length > 0)
+ var trailerTypes = query.TrailerTypes;
+ int trailerTypesLen = trailerTypes.Length;
+ if (trailerTypesLen > 0)
{
- var clauses = new List<string>();
- var index = 0;
- foreach (var type in query.TrailerTypes)
+ const string Or = " OR ";
+ StringBuilder clause = new StringBuilder("(", trailerTypesLen * 32);
+ for (int i = 0; i < trailerTypesLen; i++)
{
- var paramName = "@TrailerTypes" + index;
-
- clauses.Add("TrailerTypes like " + paramName);
- if (statement != null)
- {
- statement.TryBind(paramName, "%" + type + "%");
- }
- index++;
+ var paramName = "@TrailerTypes" + i;
+ clause.Append("TrailerTypes like ")
+ .Append(paramName)
+ .Append(Or);
+ statement?.TryBind(paramName, "%" + trailerTypes[i] + "%");
}
- var clause = "(" + string.Join(" OR ", clauses) + ")";
- whereClauses.Add(clause);
+
+ // Remove last " OR "
+ clause.Length -= Or.Length;
+ clause.Append(')');
+
+ whereClauses.Add(clause.ToString());
}
if (query.IsAiring.HasValue)
@@ -3756,24 +3675,15 @@ namespace Emby.Server.Implementations.Data
if (query.IsAiring.Value)
{
whereClauses.Add("StartDate<=@MaxStartDate");
- if (statement != null)
- {
- statement.TryBind("@MaxStartDate", DateTime.UtcNow);
- }
+ statement?.TryBind("@MaxStartDate", DateTime.UtcNow);
whereClauses.Add("EndDate>=@MinEndDate");
- if (statement != null)
- {
- statement.TryBind("@MinEndDate", DateTime.UtcNow);
- }
+ statement?.TryBind("@MinEndDate", DateTime.UtcNow);
}
else
{
whereClauses.Add("(StartDate>@IsAiringDate OR EndDate < @IsAiringDate)");
- if (statement != null)
- {
- statement.TryBind("@IsAiringDate", DateTime.UtcNow);
- }
+ statement?.TryBind("@IsAiringDate", DateTime.UtcNow);
}
}
@@ -3788,13 +3698,10 @@ namespace Emby.Server.Implementations.Data
var paramName = "@PersonId" + index;
clauses.Add("(guid in (select itemid from People where Name = (select Name from TypedBaseItems where guid=" + paramName + ")))");
-
- if (statement != null)
- {
- statement.TryBind(paramName, personId.ToByteArray());
- }
+ statement?.TryBind(paramName, personId.ToByteArray());
index++;
}
+
var clause = "(" + string.Join(" OR ", clauses) + ")";
whereClauses.Add(clause);
}
@@ -3802,47 +3709,31 @@ namespace Emby.Server.Implementations.Data
if (!string.IsNullOrWhiteSpace(query.Person))
{
whereClauses.Add("Guid in (select ItemId from People where Name=@PersonName)");
- if (statement != null)
- {
- statement.TryBind("@PersonName", query.Person);
- }
+ statement?.TryBind("@PersonName", query.Person);
}
if (!string.IsNullOrWhiteSpace(query.MinSortName))
{
whereClauses.Add("SortName>=@MinSortName");
- if (statement != null)
- {
- statement.TryBind("@MinSortName", query.MinSortName);
- }
+ statement?.TryBind("@MinSortName", query.MinSortName);
}
if (!string.IsNullOrWhiteSpace(query.ExternalSeriesId))
{
whereClauses.Add("ExternalSeriesId=@ExternalSeriesId");
- if (statement != null)
- {
- statement.TryBind("@ExternalSeriesId", query.ExternalSeriesId);
- }
+ statement?.TryBind("@ExternalSeriesId", query.ExternalSeriesId);
}
if (!string.IsNullOrWhiteSpace(query.ExternalId))
{
whereClauses.Add("ExternalId=@ExternalId");
- if (statement != null)
- {
- statement.TryBind("@ExternalId", query.ExternalId);
- }
+ statement?.TryBind("@ExternalId", query.ExternalId);
}
if (!string.IsNullOrWhiteSpace(query.Name))
{
whereClauses.Add("CleanName=@Name");
-
- if (statement != null)
- {
- statement.TryBind("@Name", GetCleanValue(query.Name));
- }
+ statement?.TryBind("@Name", GetCleanValue(query.Name));
}
// These are the same, for now
@@ -3861,28 +3752,21 @@ namespace Emby.Server.Implementations.Data
if (!string.IsNullOrWhiteSpace(query.NameStartsWith))
{
whereClauses.Add("SortName like @NameStartsWith");
- if (statement != null)
- {
- statement.TryBind("@NameStartsWith", query.NameStartsWith + "%");
- }
+ statement?.TryBind("@NameStartsWith", query.NameStartsWith + "%");
}
+
if (!string.IsNullOrWhiteSpace(query.NameStartsWithOrGreater))
{
whereClauses.Add("SortName >= @NameStartsWithOrGreater");
// lowercase this because SortName is stored as lowercase
- if (statement != null)
- {
- statement.TryBind("@NameStartsWithOrGreater", query.NameStartsWithOrGreater.ToLowerInvariant());
- }
+ statement?.TryBind("@NameStartsWithOrGreater", query.NameStartsWithOrGreater.ToLowerInvariant());
}
+
if (!string.IsNullOrWhiteSpace(query.NameLessThan))
{
whereClauses.Add("SortName < @NameLessThan");
// lowercase this because SortName is stored as lowercase
- if (statement != null)
- {
- statement.TryBind("@NameLessThan", query.NameLessThan.ToLowerInvariant());
- }
+ statement?.TryBind("@NameLessThan", query.NameLessThan.ToLowerInvariant());
}
if (query.ImageTypes.Length > 0)
@@ -3898,18 +3782,12 @@ namespace Emby.Server.Implementations.Data
if (query.IsLiked.Value)
{
whereClauses.Add("rating>=@UserRating");
- if (statement != null)
- {
- statement.TryBind("@UserRating", UserItemData.MinLikeValue);
- }
+ statement?.TryBind("@UserRating", UserItemData.MinLikeValue);
}
else
{
whereClauses.Add("(rating is null or rating<@UserRating)");
- if (statement != null)
- {
- statement.TryBind("@UserRating", UserItemData.MinLikeValue);
- }
+ statement?.TryBind("@UserRating", UserItemData.MinLikeValue);
}
}
@@ -3923,10 +3801,8 @@ namespace Emby.Server.Implementations.Data
{
whereClauses.Add("(IsFavorite is null or IsFavorite=@IsFavoriteOrLiked)");
}
- if (statement != null)
- {
- statement.TryBind("@IsFavoriteOrLiked", query.IsFavoriteOrLiked.Value);
- }
+
+ statement?.TryBind("@IsFavoriteOrLiked", query.IsFavoriteOrLiked.Value);
}
if (query.IsFavorite.HasValue)
@@ -3939,10 +3815,8 @@ namespace Emby.Server.Implementations.Data
{
whereClauses.Add("(IsFavorite is null or IsFavorite=@IsFavorite)");
}
- if (statement != null)
- {
- statement.TryBind("@IsFavorite", query.IsFavorite.Value);
- }
+
+ statement?.TryBind("@IsFavorite", query.IsFavorite.Value);
}
if (EnableJoinUserData(query))
@@ -3971,10 +3845,8 @@ namespace Emby.Server.Implementations.Data
{
whereClauses.Add("(played is null or played=@IsPlayed)");
}
- if (statement != null)
- {
- statement.TryBind("@IsPlayed", query.IsPlayed.Value);
- }
+
+ statement?.TryBind("@IsPlayed", query.IsPlayed.Value);
}
}
}
@@ -4006,6 +3878,7 @@ namespace Emby.Server.Implementations.Data
}
index++;
}
+
var clause = "(" + string.Join(" OR ", clauses) + ")";
whereClauses.Add(clause);
}
@@ -4025,6 +3898,7 @@ namespace Emby.Server.Implementations.Data
}
index++;
}
+
var clause = "(" + string.Join(" OR ", clauses) + ")";
whereClauses.Add(clause);
}
@@ -4758,18 +4632,22 @@ namespace Emby.Server.Implementations.Data
{
list.Add(typeof(Person).Name);
}
+
if (IsTypeInQuery(typeof(Genre).Name, query))
{
list.Add(typeof(Genre).Name);
}
+
if (IsTypeInQuery(typeof(MusicGenre).Name, query))
{
list.Add(typeof(MusicGenre).Name);
}
+
if (IsTypeInQuery(typeof(MusicArtist).Name, query))
{
list.Add(typeof(MusicArtist).Name);
}
+
if (IsTypeInQuery(typeof(Studio).Name, query))
{
list.Add(typeof(Studio).Name);
@@ -4843,7 +4721,7 @@ namespace Emby.Server.Implementations.Data
return false;
}
- private static readonly Type[] KnownTypes =
+ private static readonly Type[] _knownTypes =
{
typeof(LiveTvProgram),
typeof(LiveTvChannel),
@@ -4912,7 +4790,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
{
var dict = new Dictionary<string, string[]>(StringComparer.OrdinalIgnoreCase);
- foreach (var t in KnownTypes)
+ foreach (var t in _knownTypes)
{
dict[t.Name] = new[] { t.FullName };
}
@@ -4924,7 +4802,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
}
// Not crazy about having this all the way down here, but at least it's in one place
- readonly Dictionary<string, string[]> _types = GetTypeMapDictionary();
+ private readonly Dictionary<string, string[]> _types = GetTypeMapDictionary();
private string[] MapIncludeItemTypes(string value)
{
@@ -4941,7 +4819,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
return Array.Empty<string>();
}
- public void DeleteItem(Guid id, CancellationToken cancellationToken)
+ public void DeleteItem(Guid id)
{
if (id == Guid.Empty)
{
@@ -4977,7 +4855,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
}
}
- private void ExecuteWithSingleParam(IDatabaseConnection db, string query, byte[] value)
+ private void ExecuteWithSingleParam(IDatabaseConnection db, string query, ReadOnlySpan<byte> value)
{
using (var statement = PrepareStatement(db, query))
{
@@ -5007,6 +4885,11 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
commandText += " order by ListOrder";
+ if (query.Limit > 0)
+ {
+ commandText += " LIMIT " + query.Limit;
+ }
+
using (var connection = GetConnection(true))
{
var list = new List<string>();
@@ -5045,6 +4928,11 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
commandText += " order by ListOrder";
+ if (query.Limit > 0)
+ {
+ commandText += " LIMIT " + query.Limit;
+ }
+
using (var connection = GetConnection(true))
{
var list = new List<PersonInfo>();
@@ -5527,6 +5415,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
{
GetWhereClauses(typeSubQuery, null);
}
+
BindSimilarParams(query, statement);
BindSearchParams(query, statement);
GetWhereClauses(innerQuery, statement);
@@ -5568,7 +5457,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
}
var allTypes = typeString.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries)
- .ToLookup(i => i);
+ .ToLookup(x => x);
foreach (var type in allTypes)
{
@@ -5659,30 +5548,26 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
private void InsertItemValues(byte[] idBlob, List<(int, string)> values, IDatabaseConnection db)
{
+ const int Limit = 100;
var startIndex = 0;
- var limit = 100;
while (startIndex < values.Count)
{
var insertText = new StringBuilder("insert into ItemValues (ItemId, Type, Value, CleanValue) values ");
- var endIndex = Math.Min(values.Count, startIndex + limit);
- var isSubsequentRow = false;
+ var endIndex = Math.Min(values.Count, startIndex + Limit);
for (var i = startIndex; i < endIndex; i++)
{
- if (isSubsequentRow)
- {
- insertText.Append(',');
- }
-
insertText.AppendFormat(
CultureInfo.InvariantCulture,
- "(@ItemId, @Type{0}, @Value{0}, @CleanValue{0})",
+ "(@ItemId, @Type{0}, @Value{0}, @CleanValue{0}),",
i);
- isSubsequentRow = true;
}
+ // Remove last comma
+ insertText.Length--;
+
using (var statement = PrepareStatement(db, insertText.ToString()))
{
statement.TryBind("@ItemId", idBlob);
@@ -5710,7 +5595,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
statement.MoveNext();
}
- startIndex += limit;
+ startIndex += Limit;
}
}
@@ -5745,28 +5630,23 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
private void InsertPeople(byte[] idBlob, List<PersonInfo> people, IDatabaseConnection db)
{
+ const int Limit = 100;
var startIndex = 0;
- var limit = 100;
var listIndex = 0;
while (startIndex < people.Count)
{
var insertText = new StringBuilder("insert into People (ItemId, Name, Role, PersonType, SortOrder, ListOrder) values ");
- var endIndex = Math.Min(people.Count, startIndex + limit);
- var isSubsequentRow = false;
-
+ var endIndex = Math.Min(people.Count, startIndex + Limit);
for (var i = startIndex; i < endIndex; i++)
{
- if (isSubsequentRow)
- {
- insertText.Append(',');
- }
-
- insertText.AppendFormat("(@ItemId, @Name{0}, @Role{0}, @PersonType{0}, @SortOrder{0}, @ListOrder{0})", i.ToString(CultureInfo.InvariantCulture));
- isSubsequentRow = true;
+ insertText.AppendFormat("(@ItemId, @Name{0}, @Role{0}, @PersonType{0}, @SortOrder{0}, @ListOrder{0}),", i.ToString(CultureInfo.InvariantCulture));
}
+ // Remove last comma
+ insertText.Length--;
+
using (var statement = PrepareStatement(db, insertText.ToString()))
{
statement.TryBind("@ItemId", idBlob);
@@ -5790,16 +5670,17 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
statement.MoveNext();
}
- startIndex += limit;
+ startIndex += Limit;
}
}
private PersonInfo GetPerson(IReadOnlyList<IResultSetValue> reader)
{
- var item = new PersonInfo();
-
- item.ItemId = reader.GetGuid(0);
- item.Name = reader.GetString(1);
+ var item = new PersonInfo
+ {
+ ItemId = reader.GetGuid(0),
+ Name = reader.GetString(1)
+ };
if (!reader.IsDBNull(2))
{
@@ -5906,20 +5787,28 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
private void InsertMediaStreams(byte[] idBlob, List<MediaStream> streams, IDatabaseConnection db)
{
+ const int Limit = 10;
var startIndex = 0;
- var limit = 10;
while (startIndex < streams.Count)
{
- var insertText = new StringBuilder(string.Format("insert into mediastreams ({0}) values ", string.Join(",", _mediaStreamSaveColumns)));
+ var insertText = new StringBuilder("insert into mediastreams (");
+ foreach (var column in _mediaStreamSaveColumns)
+ {
+ insertText.Append(column).Append(',');
+ }
- var endIndex = Math.Min(streams.Count, startIndex + limit);
+ // Remove last comma
+ insertText.Length--;
+ insertText.Append(") values ");
+
+ var endIndex = Math.Min(streams.Count, startIndex + Limit);
for (var i = startIndex; i < endIndex; i++)
{
if (i != startIndex)
{
- insertText.Append(",");
+ insertText.Append(',');
}
var index = i.ToString(CultureInfo.InvariantCulture);
@@ -5927,11 +5816,12 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
foreach (var column in _mediaStreamSaveColumns.Skip(1))
{
- insertText.Append("@" + column + index + ",");
+ insertText.Append('@').Append(column).Append(index).Append(',');
}
+
insertText.Length -= 1; // Remove the last comma
- insertText.Append(")");
+ insertText.Append(')');
}
using (var statement = PrepareStatement(db, insertText.ToString()))
@@ -5993,7 +5883,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
statement.MoveNext();
}
- startIndex += limit;
+ startIndex += Limit;
}
}
@@ -6010,7 +5900,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
Index = reader[1].ToInt()
};
- item.Type = (MediaStreamType)Enum.Parse(typeof(MediaStreamType), reader[2].ToString(), true);
+ item.Type = Enum.Parse<MediaStreamType>(reader[2].ToString(), true);
if (reader[3].SQLiteType != SQLiteType.Null)
{
@@ -6158,7 +6048,8 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
item.ColorTransfer = reader[34].ToString();
}
- if (item.Type == MediaStreamType.Subtitle){
+ if (item.Type == MediaStreamType.Subtitle)
+ {
item.localizedUndefined = _localization.GetLocalizedString("Undefined");
item.localizedDefault = _localization.GetLocalizedString("Default");
item.localizedForced = _localization.GetLocalizedString("Forced");
@@ -6287,8 +6178,8 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
statement.TryBind("@Codec" + index, attachment.Codec);
statement.TryBind("@CodecTag" + index, attachment.CodecTag);
statement.TryBind("@Comment" + index, attachment.Comment);
- statement.TryBind("@FileName" + index, attachment.FileName);
- statement.TryBind("@MimeType" + index, attachment.MimeType);
+ statement.TryBind("@Filename" + index, attachment.FileName);
+ statement.TryBind("@MIMEType" + index, attachment.MimeType);
}
statement.Reset();
diff --git a/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs b/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs
index f6c37e4e5..22955850a 100644
--- a/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs
+++ b/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/Data/SqliteUserRepository.cs b/Emby.Server.Implementations/Data/SqliteUserRepository.cs
index c82c93ffc..0c3f26974 100644
--- a/Emby.Server.Implementations/Data/SqliteUserRepository.cs
+++ b/Emby.Server.Implementations/Data/SqliteUserRepository.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Collections.Generic;
@@ -26,7 +25,7 @@ namespace Emby.Server.Implementations.Data
IServerApplicationPaths appPaths)
: base(logger)
{
- _jsonOptions = JsonDefaults.GetOptions();;
+ _jsonOptions = JsonDefaults.GetOptions();
DbFilePath = Path.Combine(appPaths.DataPath, "users.db");
}
diff --git a/Emby.Server.Implementations/Devices/DeviceId.cs b/Emby.Server.Implementations/Devices/DeviceId.cs
index ff75efa59..f0d43e665 100644
--- a/Emby.Server.Implementations/Devices/DeviceId.cs
+++ b/Emby.Server.Implementations/Devices/DeviceId.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Globalization;
diff --git a/Emby.Server.Implementations/Devices/DeviceManager.cs b/Emby.Server.Implementations/Devices/DeviceManager.cs
index 9ac6a1d5e..579cb895e 100644
--- a/Emby.Server.Implementations/Devices/DeviceManager.cs
+++ b/Emby.Server.Implementations/Devices/DeviceManager.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Collections.Generic;
@@ -39,10 +38,11 @@ namespace Emby.Server.Implementations.Devices
private readonly IServerConfigurationManager _config;
private readonly ILibraryManager _libraryManager;
private readonly ILocalizationManager _localizationManager;
-
private readonly IAuthenticationRepository _authRepo;
+ private readonly Dictionary<string, ClientCapabilities> _capabilitiesCache;
public event EventHandler<GenericEventArgs<Tuple<string, DeviceOptions>>> DeviceOptionsUpdated;
+
public event EventHandler<GenericEventArgs<CameraImageUploadInfo>> CameraImageUploaded;
private readonly object _cameraUploadSyncLock = new object();
@@ -66,10 +66,9 @@ namespace Emby.Server.Implementations.Devices
_libraryManager = libraryManager;
_localizationManager = localizationManager;
_authRepo = authRepo;
+ _capabilitiesCache = new Dictionary<string, ClientCapabilities>(StringComparer.OrdinalIgnoreCase);
}
-
- private Dictionary<string, ClientCapabilities> _capabilitiesCache = new Dictionary<string, ClientCapabilities>(StringComparer.OrdinalIgnoreCase);
public void SaveCapabilities(string deviceId, ClientCapabilities capabilities)
{
var path = Path.Combine(GetDevicePath(deviceId), "capabilities.json");
diff --git a/Emby.Server.Implementations/Diagnostics/CommonProcess.cs b/Emby.Server.Implementations/Diagnostics/CommonProcess.cs
deleted file mode 100644
index f8b754151..000000000
--- a/Emby.Server.Implementations/Diagnostics/CommonProcess.cs
+++ /dev/null
@@ -1,153 +0,0 @@
-#pragma warning disable CS1591
-#pragma warning disable SA1600
-
-using System;
-using System.Diagnostics;
-using System.IO;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Model.Diagnostics;
-
-namespace Emby.Server.Implementations.Diagnostics
-{
- public class CommonProcess : IProcess
- {
- private readonly Process _process;
-
- private bool _disposed = false;
- private bool _hasExited;
-
- public CommonProcess(ProcessOptions options)
- {
- StartInfo = options;
-
- var startInfo = new ProcessStartInfo
- {
- Arguments = options.Arguments,
- FileName = options.FileName,
- WorkingDirectory = options.WorkingDirectory,
- UseShellExecute = options.UseShellExecute,
- CreateNoWindow = options.CreateNoWindow,
- RedirectStandardError = options.RedirectStandardError,
- RedirectStandardInput = options.RedirectStandardInput,
- RedirectStandardOutput = options.RedirectStandardOutput,
- ErrorDialog = options.ErrorDialog
- };
-
-
- if (options.IsHidden)
- {
- startInfo.WindowStyle = ProcessWindowStyle.Hidden;
- }
-
- _process = new Process
- {
- StartInfo = startInfo
- };
-
- if (options.EnableRaisingEvents)
- {
- _process.EnableRaisingEvents = true;
- _process.Exited += OnProcessExited;
- }
- }
-
- public event EventHandler Exited;
-
- public ProcessOptions StartInfo { get; }
-
- public StreamWriter StandardInput => _process.StandardInput;
-
- public StreamReader StandardError => _process.StandardError;
-
- public StreamReader StandardOutput => _process.StandardOutput;
-
- public int ExitCode => _process.ExitCode;
-
- private bool HasExited
- {
- get
- {
- if (_hasExited)
- {
- return true;
- }
-
- try
- {
- _hasExited = _process.HasExited;
- }
- catch (InvalidOperationException)
- {
- _hasExited = true;
- }
-
- return _hasExited;
- }
- }
-
- public void Start()
- {
- _process.Start();
- }
-
- public void Kill()
- {
- _process.Kill();
- }
-
- public bool WaitForExit(int timeMs)
- {
- return _process.WaitForExit(timeMs);
- }
-
- public Task<bool> WaitForExitAsync(int timeMs)
- {
- // Note: For this function to work correctly, the option EnableRisingEvents needs to be set to true.
-
- if (HasExited)
- {
- return Task.FromResult(true);
- }
-
- timeMs = Math.Max(0, timeMs);
-
- var tcs = new TaskCompletionSource<bool>();
-
- var cancellationToken = new CancellationTokenSource(timeMs).Token;
-
- _process.Exited += (sender, args) => tcs.TrySetResult(true);
-
- cancellationToken.Register(() => tcs.TrySetResult(HasExited));
-
- return tcs.Task;
- }
-
- public void Dispose()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
-
- protected virtual void Dispose(bool disposing)
- {
- if (_disposed)
- {
- return;
- }
-
- if (disposing)
- {
- _process?.Dispose();
- }
-
- _disposed = true;
- }
-
- private void OnProcessExited(object sender, EventArgs e)
- {
- _hasExited = true;
- Exited?.Invoke(this, e);
- }
- }
-}
diff --git a/Emby.Server.Implementations/Diagnostics/ProcessFactory.cs b/Emby.Server.Implementations/Diagnostics/ProcessFactory.cs
deleted file mode 100644
index 219f73c78..000000000
--- a/Emby.Server.Implementations/Diagnostics/ProcessFactory.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-#pragma warning disable CS1591
-#pragma warning disable SA1600
-
-using MediaBrowser.Model.Diagnostics;
-
-namespace Emby.Server.Implementations.Diagnostics
-{
- public class ProcessFactory : IProcessFactory
- {
- public IProcess Create(ProcessOptions options)
- {
- return new CommonProcess(options);
- }
- }
-}
diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs
index 960f3f2d6..c4b65d265 100644
--- a/Emby.Server.Implementations/Dto/DtoService.cs
+++ b/Emby.Server.Implementations/Dto/DtoService.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Collections.Generic;
@@ -39,21 +38,23 @@ namespace Emby.Server.Implementations.Dto
private readonly IProviderManager _providerManager;
private readonly IApplicationHost _appHost;
- private readonly Func<IMediaSourceManager> _mediaSourceManager;
- private readonly Func<ILiveTvManager> _livetvManager;
+ private readonly IMediaSourceManager _mediaSourceManager;
+ private readonly Lazy<ILiveTvManager> _livetvManagerFactory;
+
+ private ILiveTvManager LivetvManager => _livetvManagerFactory.Value;
public DtoService(
- ILoggerFactory loggerFactory,
+ ILogger<DtoService> logger,
ILibraryManager libraryManager,
IUserDataManager userDataRepository,
IItemRepository itemRepo,
IImageProcessor imageProcessor,
IProviderManager providerManager,
IApplicationHost appHost,
- Func<IMediaSourceManager> mediaSourceManager,
- Func<ILiveTvManager> livetvManager)
+ IMediaSourceManager mediaSourceManager,
+ Lazy<ILiveTvManager> livetvManagerFactory)
{
- _logger = loggerFactory.CreateLogger(nameof(DtoService));
+ _logger = logger;
_libraryManager = libraryManager;
_userDataRepository = userDataRepository;
_itemRepo = itemRepo;
@@ -61,7 +62,7 @@ namespace Emby.Server.Implementations.Dto
_providerManager = providerManager;
_appHost = appHost;
_mediaSourceManager = mediaSourceManager;
- _livetvManager = livetvManager;
+ _livetvManagerFactory = livetvManagerFactory;
}
/// <summary>
@@ -126,12 +127,12 @@ namespace Emby.Server.Implementations.Dto
if (programTuples.Count > 0)
{
- _livetvManager().AddInfoToProgramDto(programTuples, options.Fields, user).GetAwaiter().GetResult();
+ LivetvManager.AddInfoToProgramDto(programTuples, options.Fields, user).GetAwaiter().GetResult();
}
if (channelTuples.Count > 0)
{
- _livetvManager().AddChannelInfo(channelTuples, options, user);
+ LivetvManager.AddChannelInfo(channelTuples, options, user);
}
return returnItems;
@@ -143,12 +144,12 @@ namespace Emby.Server.Implementations.Dto
if (item is LiveTvChannel tvChannel)
{
var list = new List<(BaseItemDto, LiveTvChannel)>(1) { (dto, tvChannel) };
- _livetvManager().AddChannelInfo(list, options, user);
+ LivetvManager.AddChannelInfo(list, options, user);
}
else if (item is LiveTvProgram)
{
var list = new List<(BaseItem, BaseItemDto)>(1) { (item, dto) };
- var task = _livetvManager().AddInfoToProgramDto(list, options.Fields, user);
+ var task = LivetvManager.AddInfoToProgramDto(list, options.Fields, user);
Task.WaitAll(task);
}
@@ -224,7 +225,7 @@ namespace Emby.Server.Implementations.Dto
if (item is IHasMediaSources
&& options.ContainsField(ItemFields.MediaSources))
{
- dto.MediaSources = _mediaSourceManager().GetStaticMediaSources(item, true, user).ToArray();
+ dto.MediaSources = _mediaSourceManager.GetStaticMediaSources(item, true, user).ToArray();
NormalizeMediaSourceContainers(dto);
}
@@ -255,7 +256,7 @@ namespace Emby.Server.Implementations.Dto
dto.Etag = item.GetEtag(user);
}
- var liveTvManager = _livetvManager();
+ var liveTvManager = LivetvManager;
var activeRecording = liveTvManager.GetActiveRecordingInfo(item.Path);
if (activeRecording != null)
{
@@ -1046,7 +1047,7 @@ namespace Emby.Server.Implementations.Dto
}
else
{
- mediaStreams = _mediaSourceManager().GetStaticMediaSources(item, true)[0].MediaStreams.ToArray();
+ mediaStreams = _mediaSourceManager.GetStaticMediaSources(item, true)[0].MediaStreams.ToArray();
}
dto.MediaStreams = mediaStreams;
@@ -1057,30 +1058,19 @@ namespace Emby.Server.Implementations.Dto
if (options.ContainsField(ItemFields.SpecialFeatureCount))
{
- if (allExtras == null)
- {
- allExtras = item.GetExtras().ToArray();
- }
-
+ allExtras = item.GetExtras().ToArray();
dto.SpecialFeatureCount = allExtras.Count(i => i.ExtraType.HasValue && BaseItem.DisplayExtraTypes.Contains(i.ExtraType.Value));
}
if (options.ContainsField(ItemFields.LocalTrailerCount))
{
- int trailerCount = 0;
- if (allExtras == null)
- {
- allExtras = item.GetExtras().ToArray();
- }
-
- trailerCount += allExtras.Count(i => i.ExtraType.HasValue && i.ExtraType.Value == ExtraType.Trailer);
+ allExtras ??= item.GetExtras().ToArray();
+ dto.LocalTrailerCount = allExtras.Count(i => i.ExtraType == ExtraType.Trailer);
if (item is IHasTrailers hasTrailers)
{
- trailerCount += hasTrailers.GetTrailerCount();
+ dto.LocalTrailerCount += hasTrailers.GetTrailerCount();
}
-
- dto.LocalTrailerCount = trailerCount;
}
// Add EpisodeInfo
diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
index f8560ca85..44fc932e3 100644
--- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj
+++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
@@ -1,5 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
+ <!-- ProjectGuid is only included as a requirement for SonarQube analysis -->
+ <PropertyGroup>
+ <ProjectGuid>{E383961B-9356-4D5D-8233-9A1079D03055}</ProjectGuid>
+ </PropertyGroup>
+
<ItemGroup>
<ProjectReference Include="..\Emby.Naming\Emby.Naming.csproj" />
<ProjectReference Include="..\Emby.Notifications\Emby.Notifications.csproj" />
@@ -29,14 +34,15 @@
<PackageReference Include="Microsoft.AspNetCore.ResponseCompression" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.WebSockets" Version="2.2.1" />
- <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.1" />
- <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="3.1.1" />
- <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.1" />
- <PackageReference Include="Mono.Nat" Version="2.0.0" />
+ <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.3" />
+ <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="3.1.3" />
+ <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.3" />
+ <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="3.1.3" />
+ <PackageReference Include="Mono.Nat" Version="2.0.1" />
+ <PackageReference Include="prometheus-net.DotNetRuntime" Version="3.3.1" />
<PackageReference Include="ServiceStack.Text.Core" Version="5.8.0" />
- <PackageReference Include="sharpcompress" Version="0.24.0" />
+ <PackageReference Include="sharpcompress" Version="0.25.0" />
<PackageReference Include="SQLitePCL.pretty.netstandard" Version="2.1.0" />
- <PackageReference Include="System.Interactive.Async" Version="4.0.0" />
</ItemGroup>
<ItemGroup>
diff --git a/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs b/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs
index 4e4ef3be0..37d7fd479 100644
--- a/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs
+++ b/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs
@@ -1,7 +1,7 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
+using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Net;
using System.Text;
@@ -27,10 +27,10 @@ namespace Emby.Server.Implementations.EntryPoints
private readonly IServerConfigurationManager _config;
private readonly IDeviceDiscovery _deviceDiscovery;
- private readonly object _createdRulesLock = new object();
- private List<IPEndPoint> _createdRules = new List<IPEndPoint>();
+ private readonly ConcurrentDictionary<IPEndPoint, byte> _createdRules = new ConcurrentDictionary<IPEndPoint, byte>();
+
private Timer _timer;
- private string _lastConfigIdentifier;
+ private string _configIdentifier;
private bool _disposed = false;
@@ -61,6 +61,7 @@ namespace Emby.Server.Implementations.EntryPoints
return new StringBuilder(32)
.Append(config.EnableUPnP).Append(Separator)
.Append(config.PublicPort).Append(Separator)
+ .Append(config.PublicHttpsPort).Append(Separator)
.Append(_appHost.HttpPort).Append(Separator)
.Append(_appHost.HttpsPort).Append(Separator)
.Append(_appHost.EnableHttps).Append(Separator)
@@ -70,7 +71,10 @@ namespace Emby.Server.Implementations.EntryPoints
private void OnConfigurationUpdated(object sender, EventArgs e)
{
- if (!string.Equals(_lastConfigIdentifier, GetConfigIdentifier(), StringComparison.OrdinalIgnoreCase))
+ var oldConfigIdentifier = _configIdentifier;
+ _configIdentifier = GetConfigIdentifier();
+
+ if (!string.Equals(_configIdentifier, oldConfigIdentifier, StringComparison.OrdinalIgnoreCase))
{
Stop();
Start();
@@ -94,21 +98,19 @@ namespace Emby.Server.Implementations.EntryPoints
return;
}
- _logger.LogDebug("Starting NAT discovery");
+ _logger.LogInformation("Starting NAT discovery");
NatUtility.DeviceFound += OnNatUtilityDeviceFound;
NatUtility.StartDiscovery();
- _timer = new Timer(ClearCreatedRules, null, TimeSpan.FromMinutes(10), TimeSpan.FromMinutes(10));
+ _timer = new Timer((_) => _createdRules.Clear(), null, TimeSpan.FromMinutes(10), TimeSpan.FromMinutes(10));
_deviceDiscovery.DeviceDiscovered += OnDeviceDiscoveryDeviceDiscovered;
-
- _lastConfigIdentifier = GetConfigIdentifier();
}
private void Stop()
{
- _logger.LogDebug("Stopping NAT discovery");
+ _logger.LogInformation("Stopping NAT discovery");
NatUtility.StopDiscovery();
NatUtility.DeviceFound -= OnNatUtilityDeviceFound;
@@ -118,26 +120,16 @@ namespace Emby.Server.Implementations.EntryPoints
_deviceDiscovery.DeviceDiscovered -= OnDeviceDiscoveryDeviceDiscovered;
}
- private void ClearCreatedRules(object state)
- {
- lock (_createdRulesLock)
- {
- _createdRules.Clear();
- }
- }
-
private void OnDeviceDiscoveryDeviceDiscovered(object sender, GenericEventArgs<UpnpDeviceInfo> e)
{
NatUtility.Search(e.Argument.LocalIpAddress, NatProtocol.Upnp);
}
- private void OnNatUtilityDeviceFound(object sender, DeviceEventArgs e)
+ private async void OnNatUtilityDeviceFound(object sender, DeviceEventArgs e)
{
try
{
- var device = e.Device;
-
- CreateRules(device);
+ await CreateRules(e.Device).ConfigureAwait(false);
}
catch (Exception ex)
{
@@ -145,7 +137,7 @@ namespace Emby.Server.Implementations.EntryPoints
}
}
- private async void CreateRules(INatDevice device)
+ private Task CreateRules(INatDevice device)
{
if (_disposed)
{
@@ -154,50 +146,46 @@ namespace Emby.Server.Implementations.EntryPoints
// On some systems the device discovered event seems to fire repeatedly
// This check will help ensure we're not trying to port map the same device over and over
- var address = device.DeviceEndpoint;
-
- lock (_createdRulesLock)
+ if (!_createdRules.TryAdd(device.DeviceEndpoint, 0))
{
- if (!_createdRules.Contains(address))
- {
- _createdRules.Add(address);
- }
- else
- {
- return;
- }
+ return Task.CompletedTask;
}
- try
- {
- await CreatePortMap(device, _appHost.HttpPort, _config.Configuration.PublicPort).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, "Error creating http port map");
- return;
- }
+ return Task.WhenAll(CreatePortMaps(device));
+ }
- try
- {
- await CreatePortMap(device, _appHost.HttpsPort, _config.Configuration.PublicHttpsPort).ConfigureAwait(false);
- }
- catch (Exception ex)
+ private IEnumerable<Task> CreatePortMaps(INatDevice device)
+ {
+ yield return CreatePortMap(device, _appHost.HttpPort, _config.Configuration.PublicPort);
+
+ if (_appHost.EnableHttps)
{
- _logger.LogError(ex, "Error creating https port map");
+ yield return CreatePortMap(device, _appHost.HttpsPort, _config.Configuration.PublicHttpsPort);
}
}
- private Task<Mapping> CreatePortMap(INatDevice device, int privatePort, int publicPort)
+ private async Task CreatePortMap(INatDevice device, int privatePort, int publicPort)
{
_logger.LogDebug(
- "Creating port map on local port {0} to public port {1} with device {2}",
+ "Creating port map on local port {LocalPort} to public port {PublicPort} with device {DeviceEndpoint}",
privatePort,
publicPort,
device.DeviceEndpoint);
- return device.CreatePortMapAsync(
- new Mapping(Protocol.Tcp, privatePort, publicPort, 0, _appHost.Name));
+ try
+ {
+ var mapping = new Mapping(Protocol.Tcp, privatePort, publicPort, 0, _appHost.Name);
+ await device.CreatePortMapAsync(mapping).ConfigureAwait(false);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(
+ ex,
+ "Error creating port map on local port {LocalPort} to public port {PublicPort} with device {DeviceEndpoint}.",
+ privatePort,
+ publicPort,
+ device.DeviceEndpoint);
+ }
}
/// <inheritdoc />
diff --git a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
index b31023cdb..8e3236407 100644
--- a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
+++ b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs b/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs
index eb86ea464..41c0c5115 100644
--- a/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs
+++ b/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Linq;
diff --git a/Emby.Server.Implementations/EntryPoints/StartupWizard.cs b/Emby.Server.Implementations/EntryPoints/StartupWizard.cs
index 5f2d629fe..2e738deeb 100644
--- a/Emby.Server.Implementations/EntryPoints/StartupWizard.cs
+++ b/Emby.Server.Implementations/EntryPoints/StartupWizard.cs
@@ -2,7 +2,9 @@ using System.Threading.Tasks;
using Emby.Server.Implementations.Browser;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Extensions;
using MediaBrowser.Controller.Plugins;
+using Microsoft.Extensions.Configuration;
namespace Emby.Server.Implementations.EntryPoints
{
@@ -11,46 +13,66 @@ namespace Emby.Server.Implementations.EntryPoints
/// </summary>
public sealed class StartupWizard : IServerEntryPoint
{
- /// <summary>
- /// The app host.
- /// </summary>
private readonly IServerApplicationHost _appHost;
+ private readonly IConfiguration _appConfig;
private readonly IServerConfigurationManager _config;
+ private readonly IStartupOptions _startupOptions;
/// <summary>
/// Initializes a new instance of the <see cref="StartupWizard"/> class.
/// </summary>
/// <param name="appHost">The application host.</param>
+ /// <param name="appConfig">The application configuration.</param>
/// <param name="config">The configuration manager.</param>
- public StartupWizard(IServerApplicationHost appHost, IServerConfigurationManager config)
+ /// <param name="startupOptions">The application startup options.</param>
+ public StartupWizard(
+ IServerApplicationHost appHost,
+ IConfiguration appConfig,
+ IServerConfigurationManager config,
+ IStartupOptions startupOptions)
{
_appHost = appHost;
+ _appConfig = appConfig;
_config = config;
+ _startupOptions = startupOptions;
}
/// <inheritdoc />
public Task RunAsync()
{
+ Run();
+ return Task.CompletedTask;
+ }
+
+ private void Run()
+ {
if (!_appHost.CanLaunchWebBrowser)
{
- return Task.CompletedTask;
+ return;
}
- if (!_config.Configuration.IsStartupWizardCompleted)
+ // Always launch the startup wizard if possible when it has not been completed
+ if (!_config.Configuration.IsStartupWizardCompleted && _appConfig.HostWebClient())
{
BrowserLauncher.OpenWebApp(_appHost);
+ return;
}
- else if (_config.Configuration.AutoRunWebApp)
- {
- var options = ((ApplicationHost)_appHost).StartupOptions;
- if (!options.NoAutoRunWebApp)
- {
- BrowserLauncher.OpenWebApp(_appHost);
- }
+ // Do nothing if the web app is configured to not run automatically
+ if (!_config.Configuration.AutoRunWebApp || _startupOptions.NoAutoRunWebApp)
+ {
+ return;
}
- return Task.CompletedTask;
+ // Launch the swagger page if the web client is not hosted, otherwise open the web client
+ if (_appConfig.HostWebClient())
+ {
+ BrowserLauncher.OpenWebApp(_appHost);
+ }
+ else
+ {
+ BrowserLauncher.OpenSwaggerPage(_appHost);
+ }
}
/// <inheritdoc />
diff --git a/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs b/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs
index 026b5dae9..3618b88c5 100644
--- a/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs
+++ b/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs
index 45fa03cdd..d66bb7638 100644
--- a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs
+++ b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs
@@ -6,6 +6,7 @@ using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
+using MediaBrowser.Common;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
@@ -24,7 +25,7 @@ namespace Emby.Server.Implementations.HttpClientManager
private readonly ILogger _logger;
private readonly IApplicationPaths _appPaths;
private readonly IFileSystem _fileSystem;
- private readonly Func<string> _defaultUserAgentFn;
+ private readonly IApplicationHost _appHost;
/// <summary>
/// Holds a dictionary of http clients by host. Use GetHttpClient(host) to retrieve or create a client for web requests.
@@ -40,12 +41,12 @@ namespace Emby.Server.Implementations.HttpClientManager
IApplicationPaths appPaths,
ILogger<HttpClientManager> logger,
IFileSystem fileSystem,
- Func<string> defaultUserAgentFn)
+ IApplicationHost appHost)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_fileSystem = fileSystem;
_appPaths = appPaths ?? throw new ArgumentNullException(nameof(appPaths));
- _defaultUserAgentFn = defaultUserAgentFn;
+ _appHost = appHost;
}
/// <summary>
@@ -91,18 +92,18 @@ namespace Emby.Server.Implementations.HttpClientManager
if (options.EnableDefaultUserAgent
&& !request.Headers.TryGetValues(HeaderNames.UserAgent, out _))
{
- request.Headers.Add(HeaderNames.UserAgent, _defaultUserAgentFn());
+ request.Headers.Add(HeaderNames.UserAgent, _appHost.ApplicationUserAgent);
}
switch (options.DecompressionMethod)
{
- case CompressionMethod.Deflate | CompressionMethod.Gzip:
+ case CompressionMethods.Deflate | CompressionMethods.Gzip:
request.Headers.Add(HeaderNames.AcceptEncoding, new[] { "gzip", "deflate" });
break;
- case CompressionMethod.Deflate:
+ case CompressionMethods.Deflate:
request.Headers.Add(HeaderNames.AcceptEncoding, "deflate");
break;
- case CompressionMethod.Gzip:
+ case CompressionMethods.Gzip:
request.Headers.Add(HeaderNames.AcceptEncoding, "gzip");
break;
default:
@@ -239,15 +240,10 @@ namespace Emby.Server.Implementations.HttpClientManager
var httpWebRequest = GetRequestMessage(options, httpMethod);
- if (options.RequestContentBytes != null
- || !string.IsNullOrEmpty(options.RequestContent)
+ if (!string.IsNullOrEmpty(options.RequestContent)
|| httpMethod == HttpMethod.Post)
{
- if (options.RequestContentBytes != null)
- {
- httpWebRequest.Content = new ByteArrayContent(options.RequestContentBytes);
- }
- else if (options.RequestContent != null)
+ if (options.RequestContent != null)
{
httpWebRequest.Content = new StringContent(
options.RequestContent,
diff --git a/Emby.Server.Implementations/HttpServer/FileWriter.cs b/Emby.Server.Implementations/HttpServer/FileWriter.cs
index d36f230d6..0b61e40b0 100644
--- a/Emby.Server.Implementations/HttpServer/FileWriter.cs
+++ b/Emby.Server.Implementations/HttpServer/FileWriter.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Collections.Generic;
@@ -12,8 +11,8 @@ using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Services;
-using Microsoft.Extensions.Logging;
using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Logging;
using Microsoft.Net.Http.Headers;
namespace Emby.Server.Implementations.HttpServer
diff --git a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
index 85602a67f..211a0c1d9 100644
--- a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
+++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Collections.Generic;
@@ -15,14 +14,17 @@ using Emby.Server.Implementations.Services;
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;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using ServiceStack.Text.Jsv;
@@ -30,6 +32,12 @@ namespace Emby.Server.Implementations.HttpServer
{
public class HttpListenerHost : IHttpServer, IDisposable
{
+ /// <summary>
+ /// The key for a setting that specifies the default redirect path
+ /// to use for requests where the URL base prefix is invalid or missing.
+ /// </summary>
+ public const string DefaultRedirectKey = "HttpListenerHost:DefaultRedirectPath";
+
private readonly ILogger _logger;
private readonly IServerConfigurationManager _config;
private readonly INetworkManager _networkManager;
@@ -42,6 +50,8 @@ namespace Emby.Server.Implementations.HttpServer
private readonly string _baseUrlPrefix;
private readonly Dictionary<Type, Type> _serviceOperationsMap = new Dictionary<Type, Type>();
private readonly List<IWebSocketConnection> _webSocketConnections = new List<IWebSocketConnection>();
+ private readonly IHostEnvironment _hostEnvironment;
+
private IWebSocketListener[] _webSocketListeners = Array.Empty<IWebSocketListener>();
private bool _disposed = false;
@@ -53,23 +63,30 @@ namespace Emby.Server.Implementations.HttpServer
INetworkManager networkManager,
IJsonSerializer jsonSerializer,
IXmlSerializer xmlSerializer,
- IHttpListener socketListener)
+ IHttpListener socketListener,
+ ILocalizationManager localizationManager,
+ ServiceController serviceController,
+ IHostEnvironment hostEnvironment)
{
_appHost = applicationHost;
_logger = logger;
_config = config;
- _defaultRedirectPath = configuration["HttpListenerHost:DefaultRedirectPath"];
+ _defaultRedirectPath = configuration[DefaultRedirectKey];
_baseUrlPrefix = _config.Configuration.BaseUrl;
_networkManager = networkManager;
_jsonSerializer = jsonSerializer;
_xmlSerializer = xmlSerializer;
_socketListener = socketListener;
+ ServiceController = serviceController;
+
_socketListener.WebSocketConnected = OnWebSocketConnected;
+ _hostEnvironment = hostEnvironment;
_funcParseFn = t => s => JsvReader.GetParseFn(t)(s);
Instance = this;
ResponseFilters = Array.Empty<Action<IRequest, HttpResponse, object>>();
+ GlobalResponse = localizationManager.GetLocalizedString("StartupEmbyServerIsLoading");
}
public event EventHandler<GenericEventArgs<IWebSocketConnection>> WebSocketConnected;
@@ -82,7 +99,7 @@ namespace Emby.Server.Implementations.HttpServer
public string GlobalResponse { get; set; }
- public ServiceController ServiceController { get; private set; }
+ public ServiceController ServiceController { get; }
public object CreateInstance(Type type)
{
@@ -214,7 +231,8 @@ namespace Emby.Server.Implementations.HttpServer
switch (ex)
{
case ArgumentException _: return 400;
- case SecurityException _: return 401;
+ case AuthenticationException _: return 401;
+ case SecurityException _: return 403;
case DirectoryNotFoundException _:
case FileNotFoundException _:
case ResourceNotFoundException _: return 404;
@@ -223,55 +241,52 @@ namespace Emby.Server.Implementations.HttpServer
}
}
- private async Task ErrorHandler(Exception ex, IRequest httpReq, bool logExceptionStackTrace)
+ private async Task ErrorHandler(Exception ex, IRequest httpReq, int statusCode, string urlToLog)
{
- try
- {
- ex = GetActualException(ex);
+ bool ignoreStackTrace =
+ ex is SocketException
+ || ex is IOException
+ || ex is OperationCanceledException
+ || ex is SecurityException
+ || ex is AuthenticationException
+ || ex is FileNotFoundException;
- if (logExceptionStackTrace)
- {
- _logger.LogError(ex, "Error processing request");
- }
- else
- {
- _logger.LogError("Error processing request: {Message}", ex.Message);
- }
-
- var httpRes = httpReq.Response;
-
- if (httpRes.HasStarted)
- {
- return;
- }
+ if (ignoreStackTrace)
+ {
+ _logger.LogError("Error processing request: {Message}. URL: {Url}", ex.Message.TrimEnd('.'), urlToLog);
+ }
+ else
+ {
+ _logger.LogError(ex, "Error processing request. URL: {Url}", urlToLog);
+ }
- var statusCode = GetStatusCode(ex);
- httpRes.StatusCode = statusCode;
+ var httpRes = httpReq.Response;
- var errContent = NormalizeExceptionMessage(ex.Message);
- httpRes.ContentType = "text/plain";
- httpRes.ContentLength = errContent.Length;
- await httpRes.WriteAsync(errContent).ConfigureAwait(false);
- }
- catch (Exception errorEx)
+ if (httpRes.HasStarted)
{
- _logger.LogError(errorEx, "Error this.ProcessRequest(context)(Exception while writing error to the response)");
+ return;
}
+
+ httpRes.StatusCode = statusCode;
+
+ var errContent = NormalizeExceptionMessage(ex) ?? string.Empty;
+ httpRes.ContentType = "text/plain";
+ httpRes.ContentLength = errContent.Length;
+ await httpRes.WriteAsync(errContent).ConfigureAwait(false);
}
- private string NormalizeExceptionMessage(string msg)
+ private string NormalizeExceptionMessage(Exception ex)
{
- if (msg == null)
+ // Do not expose the exception message for AuthenticationException
+ if (ex is AuthenticationException)
{
- return string.Empty;
+ return null;
}
// Strip any information we don't want to reveal
-
- msg = msg.Replace(_config.ApplicationPaths.ProgramSystemPath, string.Empty, StringComparison.OrdinalIgnoreCase);
- msg = msg.Replace(_config.ApplicationPaths.ProgramDataPath, string.Empty, StringComparison.OrdinalIgnoreCase);
-
- return msg;
+ return ex.Message
+ ?.Replace(_config.ApplicationPaths.ProgramSystemPath, string.Empty, StringComparison.OrdinalIgnoreCase)
+ .Replace(_config.ApplicationPaths.ProgramDataPath, string.Empty, StringComparison.OrdinalIgnoreCase);
}
/// <summary>
@@ -440,7 +455,7 @@ namespace Emby.Server.Implementations.HttpServer
var stopWatch = new Stopwatch();
stopWatch.Start();
var httpRes = httpReq.Response;
- string urlToLog = null;
+ string urlToLog = GetUrlToLog(urlString);
string remoteIp = httpReq.RemoteIp;
try
@@ -486,8 +501,6 @@ namespace Emby.Server.Implementations.HttpServer
return;
}
- urlToLog = GetUrlToLog(urlString);
-
if (string.Equals(localPath, _baseUrlPrefix + "/", StringComparison.OrdinalIgnoreCase)
|| string.Equals(localPath, _baseUrlPrefix, StringComparison.OrdinalIgnoreCase)
|| string.Equals(localPath, "/", StringComparison.OrdinalIgnoreCase)
@@ -519,22 +532,35 @@ namespace Emby.Server.Implementations.HttpServer
}
else
{
- await ErrorHandler(new FileNotFoundException(), httpReq, false).ConfigureAwait(false);
+ throw new FileNotFoundException();
}
}
- catch (Exception ex) when (ex is SocketException || ex is IOException || ex is OperationCanceledException)
- {
- await ErrorHandler(ex, httpReq, false).ConfigureAwait(false);
- }
- catch (SecurityException ex)
- {
- await ErrorHandler(ex, httpReq, false).ConfigureAwait(false);
- }
- catch (Exception ex)
+ catch (Exception requestEx)
{
- var logException = !string.Equals(ex.GetType().Name, "SocketException", StringComparison.OrdinalIgnoreCase);
+ try
+ {
+ var requestInnerEx = GetActualException(requestEx);
+ var statusCode = GetStatusCode(requestInnerEx);
+
+ // Do not handle 500 server exceptions manually when in development mode
+ // The framework-defined development exception page will be returned instead
+ if (statusCode == 500 && _hostEnvironment.IsDevelopment())
+ {
+ throw;
+ }
- await ErrorHandler(ex, httpReq, logException).ConfigureAwait(false);
+ await ErrorHandler(requestInnerEx, httpReq, statusCode, urlToLog).ConfigureAwait(false);
+ }
+ catch (Exception handlerException)
+ {
+ var aggregateEx = new AggregateException("Error while handling request exception", requestEx, handlerException);
+ _logger.LogError(aggregateEx, "Error while handling exception in response to {Url}", urlToLog);
+
+ if (_hostEnvironment.IsDevelopment())
+ {
+ throw aggregateEx;
+ }
+ }
}
finally
{
@@ -586,17 +612,15 @@ namespace Emby.Server.Implementations.HttpServer
/// <summary>
/// Adds the rest handlers.
/// </summary>
- /// <param name="services">The services.</param>
- /// <param name="listeners"></param>
- /// <param name="urlPrefixes"></param>
- public void Init(IEnumerable<IService> services, IEnumerable<IWebSocketListener> listeners, IEnumerable<string> urlPrefixes)
+ /// <param name="serviceTypes">The service types to register with the <see cref="ServiceController"/>.</param>
+ /// <param name="listeners">The web socket listeners.</param>
+ /// <param name="urlPrefixes">The URL prefixes. See <see cref="UrlPrefixes"/>.</param>
+ public void Init(IEnumerable<Type> serviceTypes, IEnumerable<IWebSocketListener> listeners, IEnumerable<string> urlPrefixes)
{
_webSocketListeners = listeners.ToArray();
UrlPrefixes = urlPrefixes.ToArray();
- ServiceController = new ServiceController();
- var types = services.Select(r => r.GetType());
- ServiceController.Init(this, types);
+ ServiceController.Init(this, serviceTypes);
ResponseFilters = new Action<IRequest, HttpResponse, object>[]
{
diff --git a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs
index 98a4f140e..2e9ecc4ae 100644
--- a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs
+++ b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Collections.Generic;
@@ -29,6 +28,12 @@ namespace Emby.Server.Implementations.HttpServer
/// </summary>
public class HttpResultFactory : IHttpResultFactory
{
+ // Last-Modified and If-Modified-Since must follow strict date format,
+ // see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-Modified-Since
+ private const string HttpDateFormat = "ddd, dd MMM yyyy HH:mm:ss \"GMT\"";
+ // We specifically use en-US culture because both day of week and month names require it
+ private static readonly CultureInfo _enUSculture = new CultureInfo("en-US", false);
+
/// <summary>
/// The logger.
/// </summary>
@@ -421,7 +426,11 @@ namespace Emby.Server.Implementations.HttpServer
if (!noCache)
{
- DateTime.TryParse(requestContext.Headers[HeaderNames.IfModifiedSince], out var ifModifiedSinceHeader);
+ if (!DateTime.TryParseExact(requestContext.Headers[HeaderNames.IfModifiedSince], HttpDateFormat, _enUSculture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out var ifModifiedSinceHeader))
+ {
+ _logger.LogDebug("Failed to parse If-Modified-Since header date: {0}", requestContext.Headers[HeaderNames.IfModifiedSince]);
+ return null;
+ }
if (IsNotModified(ifModifiedSinceHeader, options.CacheDuration, options.DateLastModified))
{
@@ -630,7 +639,7 @@ namespace Emby.Server.Implementations.HttpServer
if (lastModifiedDate.HasValue)
{
- responseHeaders[HeaderNames.LastModified] = lastModifiedDate.Value.ToString(CultureInfo.InvariantCulture);
+ responseHeaders[HeaderNames.LastModified] = lastModifiedDate.Value.ToUniversalTime().ToString(HttpDateFormat, _enUSculture);
}
}
diff --git a/Emby.Server.Implementations/HttpServer/IHttpListener.cs b/Emby.Server.Implementations/HttpServer/IHttpListener.cs
index 1c3496e5d..501593725 100644
--- a/Emby.Server.Implementations/HttpServer/IHttpListener.cs
+++ b/Emby.Server.Implementations/HttpServer/IHttpListener.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Threading;
diff --git a/Emby.Server.Implementations/HttpServer/RangeRequestWriter.cs b/Emby.Server.Implementations/HttpServer/RangeRequestWriter.cs
index 7cb113a58..8b9028f6b 100644
--- a/Emby.Server.Implementations/HttpServer/RangeRequestWriter.cs
+++ b/Emby.Server.Implementations/HttpServer/RangeRequestWriter.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/HttpServer/ResponseFilter.cs b/Emby.Server.Implementations/HttpServer/ResponseFilter.cs
index 5e0466629..4089aa578 100644
--- a/Emby.Server.Implementations/HttpServer/ResponseFilter.cs
+++ b/Emby.Server.Implementations/HttpServer/ResponseFilter.cs
@@ -82,6 +82,10 @@ namespace Emby.Server.Implementations.HttpServer
{
return null;
}
+ else if (inString.Length == 0)
+ {
+ return inString;
+ }
var newString = new StringBuilder(inString.Length);
diff --git a/Emby.Server.Implementations/HttpServer/Security/AuthService.cs b/Emby.Server.Implementations/HttpServer/Security/AuthService.cs
index 03b5b748d..256b24924 100644
--- a/Emby.Server.Implementations/HttpServer/Security/AuthService.cs
+++ b/Emby.Server.Implementations/HttpServer/Security/AuthService.cs
@@ -1,8 +1,8 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Linq;
+using System.Security.Authentication;
using Emby.Server.Implementations.SocketSharp;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
@@ -69,7 +69,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
if (user == null && auth.UserId != Guid.Empty)
{
- throw new SecurityException("User with Id " + auth.UserId + " not found");
+ throw new AuthenticationException("User with Id " + auth.UserId + " not found");
}
if (user != null)
@@ -109,18 +109,12 @@ namespace Emby.Server.Implementations.HttpServer.Security
{
if (user.Policy.IsDisabled)
{
- throw new SecurityException("User account has been disabled.")
- {
- SecurityExceptionType = SecurityExceptionType.Unauthenticated
- };
+ throw new SecurityException("User account has been disabled.");
}
if (!user.Policy.EnableRemoteAccess && !_networkManager.IsInLocalNetwork(request.RemoteIp))
{
- throw new SecurityException("User account has been disabled.")
- {
- SecurityExceptionType = SecurityExceptionType.Unauthenticated
- };
+ throw new SecurityException("User account has been disabled.");
}
if (!user.Policy.IsAdministrator
@@ -129,10 +123,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
{
request.Response.Headers.Add("X-Application-Error-Code", "ParentalControl");
- throw new SecurityException("This user account is not allowed access at this time.")
- {
- SecurityExceptionType = SecurityExceptionType.ParentalControl
- };
+ throw new SecurityException("This user account is not allowed access at this time.");
}
}
@@ -191,10 +182,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
{
if (user == null || !user.Policy.IsAdministrator)
{
- throw new SecurityException("User does not have admin access.")
- {
- SecurityExceptionType = SecurityExceptionType.Unauthenticated
- };
+ throw new SecurityException("User does not have admin access.");
}
}
@@ -202,10 +190,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
{
if (user == null || !user.Policy.EnableContentDeletion)
{
- throw new SecurityException("User does not have delete access.")
- {
- SecurityExceptionType = SecurityExceptionType.Unauthenticated
- };
+ throw new SecurityException("User does not have delete access.");
}
}
@@ -213,10 +198,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
{
if (user == null || !user.Policy.EnableContentDownloading)
{
- throw new SecurityException("User does not have download access.")
- {
- SecurityExceptionType = SecurityExceptionType.Unauthenticated
- };
+ throw new SecurityException("User does not have download access.");
}
}
}
@@ -231,14 +213,14 @@ namespace Emby.Server.Implementations.HttpServer.Security
{
if (string.IsNullOrEmpty(token))
{
- throw new SecurityException("Access token is required.");
+ throw new AuthenticationException("Access token is required.");
}
var info = GetTokenInfo(request);
if (info == null)
{
- throw new SecurityException("Access token is invalid or expired.");
+ throw new AuthenticationException("Access token is invalid or expired.");
}
//if (!string.IsNullOrEmpty(info.UserId))
diff --git a/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs b/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs
index e8884bca0..129faeaab 100644
--- a/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs
+++ b/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs b/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs
index a6a0f5b03..166952c64 100644
--- a/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs
+++ b/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using MediaBrowser.Controller.Entities;
diff --git a/Emby.Server.Implementations/IO/ExtendedFileSystemInfo.cs b/Emby.Server.Implementations/IO/ExtendedFileSystemInfo.cs
index ec26324c3..545d73e05 100644
--- a/Emby.Server.Implementations/IO/ExtendedFileSystemInfo.cs
+++ b/Emby.Server.Implementations/IO/ExtendedFileSystemInfo.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
namespace Emby.Server.Implementations.IO
{
diff --git a/Emby.Server.Implementations/IO/FileRefresher.cs b/Emby.Server.Implementations/IO/FileRefresher.cs
index f37a6af90..ef93779aa 100644
--- a/Emby.Server.Implementations/IO/FileRefresher.cs
+++ b/Emby.Server.Implementations/IO/FileRefresher.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/IO/LibraryMonitor.cs b/Emby.Server.Implementations/IO/LibraryMonitor.cs
index 7777efc3b..5a1eb43bc 100644
--- a/Emby.Server.Implementations/IO/LibraryMonitor.cs
+++ b/Emby.Server.Implementations/IO/LibraryMonitor.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Collections.Concurrent;
@@ -18,6 +17,11 @@ namespace Emby.Server.Implementations.IO
{
public class LibraryMonitor : ILibraryMonitor
{
+ private readonly ILogger _logger;
+ private readonly ILibraryManager _libraryManager;
+ private readonly IServerConfigurationManager _configurationManager;
+ private readonly IFileSystem _fileSystem;
+
/// <summary>
/// The file system watchers.
/// </summary>
@@ -114,34 +118,23 @@ namespace Emby.Server.Implementations.IO
}
catch (Exception ex)
{
- Logger.LogError(ex, "Error in ReportFileSystemChanged for {path}", path);
+ _logger.LogError(ex, "Error in ReportFileSystemChanged for {path}", path);
}
}
}
/// <summary>
- /// Gets or sets the logger.
- /// </summary>
- /// <value>The logger.</value>
- private ILogger Logger { get; set; }
-
- private ILibraryManager LibraryManager { get; set; }
- private IServerConfigurationManager ConfigurationManager { get; set; }
-
- private readonly IFileSystem _fileSystem;
-
- /// <summary>
/// Initializes a new instance of the <see cref="LibraryMonitor" /> class.
/// </summary>
public LibraryMonitor(
- ILoggerFactory loggerFactory,
+ ILogger<LibraryMonitor> logger,
ILibraryManager libraryManager,
IServerConfigurationManager configurationManager,
IFileSystem fileSystem)
{
- LibraryManager = libraryManager;
- Logger = loggerFactory.CreateLogger(GetType().Name);
- ConfigurationManager = configurationManager;
+ _libraryManager = libraryManager;
+ _logger = logger;
+ _configurationManager = configurationManager;
_fileSystem = fileSystem;
}
@@ -152,7 +145,7 @@ namespace Emby.Server.Implementations.IO
return false;
}
- var options = LibraryManager.GetLibraryOptions(item);
+ var options = _libraryManager.GetLibraryOptions(item);
if (options != null)
{
@@ -164,12 +157,12 @@ namespace Emby.Server.Implementations.IO
public void Start()
{
- LibraryManager.ItemAdded += OnLibraryManagerItemAdded;
- LibraryManager.ItemRemoved += OnLibraryManagerItemRemoved;
+ _libraryManager.ItemAdded += OnLibraryManagerItemAdded;
+ _libraryManager.ItemRemoved += OnLibraryManagerItemRemoved;
var pathsToWatch = new List<string>();
- var paths = LibraryManager
+ var paths = _libraryManager
.RootFolder
.Children
.Where(IsLibraryMonitorEnabled)
@@ -262,7 +255,7 @@ namespace Emby.Server.Implementations.IO
if (!Directory.Exists(path))
{
// Seeing a crash in the mono runtime due to an exception being thrown on a different thread
- Logger.LogInformation("Skipping realtime monitor for {Path} because the path does not exist", path);
+ _logger.LogInformation("Skipping realtime monitor for {Path} because the path does not exist", path);
return;
}
@@ -298,7 +291,7 @@ namespace Emby.Server.Implementations.IO
if (_fileSystemWatchers.TryAdd(path, newWatcher))
{
newWatcher.EnableRaisingEvents = true;
- Logger.LogInformation("Watching directory " + path);
+ _logger.LogInformation("Watching directory " + path);
}
else
{
@@ -308,7 +301,7 @@ namespace Emby.Server.Implementations.IO
}
catch (Exception ex)
{
- Logger.LogError(ex, "Error watching path: {path}", path);
+ _logger.LogError(ex, "Error watching path: {path}", path);
}
});
}
@@ -334,7 +327,7 @@ namespace Emby.Server.Implementations.IO
{
using (watcher)
{
- Logger.LogInformation("Stopping directory watching for path {Path}", watcher.Path);
+ _logger.LogInformation("Stopping directory watching for path {Path}", watcher.Path);
watcher.Created -= OnWatcherChanged;
watcher.Deleted -= OnWatcherChanged;
@@ -373,7 +366,7 @@ namespace Emby.Server.Implementations.IO
var ex = e.GetException();
var dw = (FileSystemWatcher)sender;
- Logger.LogError(ex, "Error in Directory watcher for: {Path}", dw.Path);
+ _logger.LogError(ex, "Error in Directory watcher for: {Path}", dw.Path);
DisposeWatcher(dw, true);
}
@@ -391,7 +384,7 @@ namespace Emby.Server.Implementations.IO
}
catch (Exception ex)
{
- Logger.LogError(ex, "Exception in ReportFileSystemChanged. Path: {FullPath}", e.FullPath);
+ _logger.LogError(ex, "Exception in ReportFileSystemChanged. Path: {FullPath}", e.FullPath);
}
}
@@ -417,13 +410,13 @@ namespace Emby.Server.Implementations.IO
{
if (_fileSystem.AreEqual(i, path))
{
- Logger.LogDebug("Ignoring change to {Path}", path);
+ _logger.LogDebug("Ignoring change to {Path}", path);
return true;
}
if (_fileSystem.ContainsSubPath(i, path))
{
- Logger.LogDebug("Ignoring change to {Path}", path);
+ _logger.LogDebug("Ignoring change to {Path}", path);
return true;
}
@@ -431,7 +424,7 @@ namespace Emby.Server.Implementations.IO
var parent = Path.GetDirectoryName(i);
if (!string.IsNullOrEmpty(parent) && _fileSystem.AreEqual(parent, path))
{
- Logger.LogDebug("Ignoring change to {Path}", path);
+ _logger.LogDebug("Ignoring change to {Path}", path);
return true;
}
@@ -486,7 +479,7 @@ namespace Emby.Server.Implementations.IO
}
}
- var newRefresher = new FileRefresher(path, ConfigurationManager, LibraryManager, Logger);
+ var newRefresher = new FileRefresher(path, _configurationManager, _libraryManager, _logger);
newRefresher.Completed += NewRefresher_Completed;
_activeRefreshers.Add(newRefresher);
}
@@ -503,8 +496,8 @@ namespace Emby.Server.Implementations.IO
/// </summary>
public void Stop()
{
- LibraryManager.ItemAdded -= OnLibraryManagerItemAdded;
- LibraryManager.ItemRemoved -= OnLibraryManagerItemRemoved;
+ _libraryManager.ItemAdded -= OnLibraryManagerItemAdded;
+ _libraryManager.ItemRemoved -= OnLibraryManagerItemRemoved;
foreach (var watcher in _fileSystemWatchers.Values.ToList())
{
diff --git a/Emby.Server.Implementations/IO/ManagedFileSystem.cs b/Emby.Server.Implementations/IO/ManagedFileSystem.cs
index da5a4d50e..7461ec4f1 100644
--- a/Emby.Server.Implementations/IO/ManagedFileSystem.cs
+++ b/Emby.Server.Implementations/IO/ManagedFileSystem.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Collections.Generic;
@@ -588,11 +587,11 @@ namespace Emby.Server.Implementations.IO
// some drives on linux have no actual size or are used for other purposes
return DriveInfo.GetDrives().Where(d => d.IsReady && d.TotalSize != 0 && d.DriveType != DriveType.Ram)
.Select(d => new FileSystemMetadata
- {
- Name = d.Name,
- FullName = d.RootDirectory.FullName,
- IsDirectory = true
- }).ToList();
+ {
+ Name = d.Name,
+ FullName = d.RootDirectory.FullName,
+ IsDirectory = true
+ }).ToList();
}
public virtual IEnumerable<FileSystemMetadata> GetDirectories(string path, bool recursive = false)
diff --git a/Emby.Server.Implementations/IO/MbLinkShortcutHandler.cs b/Emby.Server.Implementations/IO/MbLinkShortcutHandler.cs
index 574b63ae6..e6696b8c4 100644
--- a/Emby.Server.Implementations/IO/MbLinkShortcutHandler.cs
+++ b/Emby.Server.Implementations/IO/MbLinkShortcutHandler.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.IO;
diff --git a/Emby.Server.Implementations/IO/StreamHelper.cs b/Emby.Server.Implementations/IO/StreamHelper.cs
index c99018e40..40b397edc 100644
--- a/Emby.Server.Implementations/IO/StreamHelper.cs
+++ b/Emby.Server.Implementations/IO/StreamHelper.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Buffers;
diff --git a/Emby.Server.Implementations/IStartupOptions.cs b/Emby.Server.Implementations/IStartupOptions.cs
index 6e915de3d..16b68170b 100644
--- a/Emby.Server.Implementations/IStartupOptions.cs
+++ b/Emby.Server.Implementations/IStartupOptions.cs
@@ -3,33 +3,38 @@ namespace Emby.Server.Implementations
public interface IStartupOptions
{
/// <summary>
- /// --ffmpeg
+ /// Gets the value of the --ffmpeg command line option.
/// </summary>
string FFmpegPath { get; }
/// <summary>
- /// --service
+ /// Gets the value of the --service command line option.
/// </summary>
bool IsService { get; }
/// <summary>
- /// --noautorunwebapp
+ /// Gets the value of the --noautorunwebapp command line option.
/// </summary>
bool NoAutoRunWebApp { get; }
/// <summary>
- /// --package-name
+ /// Gets the value of the --package-name command line option.
/// </summary>
string PackageName { get; }
/// <summary>
- /// --restartpath
+ /// Gets the value of the --restartpath command line option.
/// </summary>
string RestartPath { get; }
/// <summary>
- /// --restartargs
+ /// Gets the value of the --restartargs command line option.
/// </summary>
string RestartArgs { get; }
+
+ /// <summary>
+ /// Gets the value of the --plugin-manifest-url command line option.
+ /// </summary>
+ string PluginManifestUrl { get; }
}
}
diff --git a/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs b/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs
index acf3a3b23..fd50f156a 100644
--- a/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs
+++ b/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs b/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs
index ab036eca7..52c8facc3 100644
--- a/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs
+++ b/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs
@@ -47,7 +47,7 @@ namespace Emby.Server.Implementations.Library
{
if (resolvedUser == null)
{
- throw new ArgumentNullException(nameof(resolvedUser));
+ throw new AuthenticationException($"Specified user does not exist.");
}
bool success = false;
diff --git a/Emby.Server.Implementations/Library/ExclusiveLiveStream.cs b/Emby.Server.Implementations/Library/ExclusiveLiveStream.cs
index 3eb64c29c..9a7186898 100644
--- a/Emby.Server.Implementations/Library/ExclusiveLiveStream.cs
+++ b/Emby.Server.Implementations/Library/ExclusiveLiveStream.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Globalization;
diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs
index 5f7f5b2f0..0b86b2db7 100644
--- a/Emby.Server.Implementations/Library/LibraryManager.cs
+++ b/Emby.Server.Implementations/Library/LibraryManager.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Collections.Concurrent;
@@ -55,9 +54,29 @@ namespace Emby.Server.Implementations.Library
/// </summary>
public class LibraryManager : ILibraryManager
{
+ private readonly ILogger _logger;
+ private readonly ITaskManager _taskManager;
+ private readonly IUserManager _userManager;
+ private readonly IUserDataManager _userDataRepository;
+ private readonly IServerConfigurationManager _configurationManager;
+ private readonly Lazy<ILibraryMonitor> _libraryMonitorFactory;
+ private readonly Lazy<IProviderManager> _providerManagerFactory;
+ private readonly Lazy<IUserViewManager> _userviewManagerFactory;
+ private readonly IServerApplicationHost _appHost;
+ private readonly IMediaEncoder _mediaEncoder;
+ private readonly IFileSystem _fileSystem;
+ private readonly IItemRepository _itemRepository;
+ private readonly ConcurrentDictionary<Guid, BaseItem> _libraryItemsCache;
+
private NamingOptions _namingOptions;
private string[] _videoFileExtensions;
+ private ILibraryMonitor LibraryMonitor => _libraryMonitorFactory.Value;
+
+ private IProviderManager ProviderManager => _providerManagerFactory.Value;
+
+ private IUserViewManager UserViewManager => _userviewManagerFactory.Value;
+
/// <summary>
/// Gets or sets the postscan tasks.
/// </summary>
@@ -91,12 +110,6 @@ namespace Emby.Server.Implementations.Library
private IBaseItemComparer[] Comparers { get; set; }
/// <summary>
- /// Gets or sets the active item repository
- /// </summary>
- /// <value>The item repository.</value>
- public IItemRepository ItemRepository { get; set; }
-
- /// <summary>
/// Occurs when [item added].
/// </summary>
public event EventHandler<ItemChangeEventArgs> ItemAdded;
@@ -111,90 +124,47 @@ namespace Emby.Server.Implementations.Library
/// </summary>
public event EventHandler<ItemChangeEventArgs> ItemRemoved;
- /// <summary>
- /// The _logger
- /// </summary>
- private readonly ILogger _logger;
-
- /// <summary>
- /// The _task manager
- /// </summary>
- private readonly ITaskManager _taskManager;
-
- /// <summary>
- /// The _user manager
- /// </summary>
- private readonly IUserManager _userManager;
-
- /// <summary>
- /// The _user data repository
- /// </summary>
- private readonly IUserDataManager _userDataRepository;
-
- /// <summary>
- /// Gets or sets the configuration manager.
- /// </summary>
- /// <value>The configuration manager.</value>
- private IServerConfigurationManager ConfigurationManager { get; set; }
-
- private readonly Func<ILibraryMonitor> _libraryMonitorFactory;
- private readonly Func<IProviderManager> _providerManagerFactory;
- private readonly Func<IUserViewManager> _userviewManager;
public bool IsScanRunning { get; private set; }
- private IServerApplicationHost _appHost;
- private readonly IMediaEncoder _mediaEncoder;
-
- /// <summary>
- /// The _library items cache
- /// </summary>
- private readonly ConcurrentDictionary<Guid, BaseItem> _libraryItemsCache;
-
- /// <summary>
- /// Gets the library items cache.
- /// </summary>
- /// <value>The library items cache.</value>
- private ConcurrentDictionary<Guid, BaseItem> LibraryItemsCache => _libraryItemsCache;
-
- private readonly IFileSystem _fileSystem;
-
/// <summary>
/// Initializes a new instance of the <see cref="LibraryManager" /> class.
/// </summary>
/// <param name="appHost">The application host</param>
- /// <param name="loggerFactory">The logger factory.</param>
+ /// <param name="logger">The logger.</param>
/// <param name="taskManager">The task manager.</param>
/// <param name="userManager">The user manager.</param>
/// <param name="configurationManager">The configuration manager.</param>
/// <param name="userDataRepository">The user data repository.</param>
public LibraryManager(
IServerApplicationHost appHost,
- ILoggerFactory loggerFactory,
+ ILogger<LibraryManager> logger,
ITaskManager taskManager,
IUserManager userManager,
IServerConfigurationManager configurationManager,
IUserDataManager userDataRepository,
- Func<ILibraryMonitor> libraryMonitorFactory,
+ Lazy<ILibraryMonitor> libraryMonitorFactory,
IFileSystem fileSystem,
- Func<IProviderManager> providerManagerFactory,
- Func<IUserViewManager> userviewManager,
- IMediaEncoder mediaEncoder)
+ Lazy<IProviderManager> providerManagerFactory,
+ Lazy<IUserViewManager> userviewManagerFactory,
+ IMediaEncoder mediaEncoder,
+ IItemRepository itemRepository)
{
_appHost = appHost;
- _logger = loggerFactory.CreateLogger(nameof(LibraryManager));
+ _logger = logger;
_taskManager = taskManager;
_userManager = userManager;
- ConfigurationManager = configurationManager;
+ _configurationManager = configurationManager;
_userDataRepository = userDataRepository;
_libraryMonitorFactory = libraryMonitorFactory;
_fileSystem = fileSystem;
_providerManagerFactory = providerManagerFactory;
- _userviewManager = userviewManager;
+ _userviewManagerFactory = userviewManagerFactory;
_mediaEncoder = mediaEncoder;
+ _itemRepository = itemRepository;
_libraryItemsCache = new ConcurrentDictionary<Guid, BaseItem>();
- ConfigurationManager.ConfigurationUpdated += ConfigurationUpdated;
+ _configurationManager.ConfigurationUpdated += ConfigurationUpdated;
RecordConfigurationValues(configurationManager.Configuration);
}
@@ -273,7 +243,7 @@ namespace Emby.Server.Implementations.Library
/// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param>
private void ConfigurationUpdated(object sender, EventArgs e)
{
- var config = ConfigurationManager.Configuration;
+ var config = _configurationManager.Configuration;
var wizardChanged = config.IsStartupWizardCompleted != _wizardCompleted;
@@ -307,7 +277,7 @@ namespace Emby.Server.Implementations.Library
}
}
- LibraryItemsCache.AddOrUpdate(item.Id, item, delegate { return item; });
+ _libraryItemsCache.AddOrUpdate(item.Id, item, delegate { return item; });
}
public void DeleteItem(BaseItem item, DeleteOptions options)
@@ -438,10 +408,10 @@ namespace Emby.Server.Implementations.Library
item.SetParent(null);
- ItemRepository.DeleteItem(item.Id, CancellationToken.None);
+ _itemRepository.DeleteItem(item.Id);
foreach (var child in children)
{
- ItemRepository.DeleteItem(child.Id, CancellationToken.None);
+ _itemRepository.DeleteItem(child.Id);
}
_libraryItemsCache.TryRemove(item.Id, out BaseItem removed);
@@ -510,15 +480,15 @@ namespace Emby.Server.Implementations.Library
throw new ArgumentNullException(nameof(type));
}
- if (key.StartsWith(ConfigurationManager.ApplicationPaths.ProgramDataPath, StringComparison.Ordinal))
+ if (key.StartsWith(_configurationManager.ApplicationPaths.ProgramDataPath, StringComparison.Ordinal))
{
// Try to normalize paths located underneath program-data in an attempt to make them more portable
- key = key.Substring(ConfigurationManager.ApplicationPaths.ProgramDataPath.Length)
+ key = key.Substring(_configurationManager.ApplicationPaths.ProgramDataPath.Length)
.TrimStart(new[] { '/', '\\' })
.Replace("/", "\\");
}
- if (forceCaseInsensitive || !ConfigurationManager.Configuration.EnableCaseSensitiveItemIds)
+ if (forceCaseInsensitive || !_configurationManager.Configuration.EnableCaseSensitiveItemIds)
{
key = key.ToLowerInvariant();
}
@@ -551,7 +521,7 @@ namespace Emby.Server.Implementations.Library
collectionType = GetContentTypeOverride(fullPath, true);
}
- var args = new ItemResolveArgs(ConfigurationManager.ApplicationPaths, directoryService)
+ var args = new ItemResolveArgs(_configurationManager.ApplicationPaths, directoryService)
{
Parent = parent,
Path = fullPath,
@@ -721,7 +691,7 @@ namespace Emby.Server.Implementations.Library
/// <exception cref="InvalidOperationException">Cannot create the root folder until plugins have loaded.</exception>
public AggregateFolder CreateRootFolder()
{
- var rootFolderPath = ConfigurationManager.ApplicationPaths.RootFolderPath;
+ var rootFolderPath = _configurationManager.ApplicationPaths.RootFolderPath;
Directory.CreateDirectory(rootFolderPath);
@@ -735,7 +705,7 @@ namespace Emby.Server.Implementations.Library
}
// Add in the plug-in folders
- var path = Path.Combine(ConfigurationManager.ApplicationPaths.DataPath, "playlists");
+ var path = Path.Combine(_configurationManager.ApplicationPaths.DataPath, "playlists");
Directory.CreateDirectory(path);
@@ -787,7 +757,7 @@ namespace Emby.Server.Implementations.Library
{
if (_userRootFolder == null)
{
- var userRootPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath;
+ var userRootPath = _configurationManager.ApplicationPaths.DefaultUserViewsPath;
_logger.LogDebug("Creating userRootPath at {path}", userRootPath);
Directory.CreateDirectory(userRootPath);
@@ -981,7 +951,7 @@ namespace Emby.Server.Implementations.Library
where T : BaseItem, new()
{
var path = getPathFn(name);
- var forceCaseInsensitiveId = ConfigurationManager.Configuration.EnableNormalizedItemByNameIds;
+ var forceCaseInsensitiveId = _configurationManager.Configuration.EnableNormalizedItemByNameIds;
return GetNewItemIdInternal(path, typeof(T), forceCaseInsensitiveId);
}
@@ -995,7 +965,7 @@ namespace Emby.Server.Implementations.Library
public Task ValidatePeople(CancellationToken cancellationToken, IProgress<double> progress)
{
// Ensure the location is available.
- Directory.CreateDirectory(ConfigurationManager.ApplicationPaths.PeoplePath);
+ Directory.CreateDirectory(_configurationManager.ApplicationPaths.PeoplePath);
return new PeopleValidator(this, _logger, _fileSystem).ValidatePeople(cancellationToken, progress);
}
@@ -1032,7 +1002,7 @@ namespace Emby.Server.Implementations.Library
public async Task ValidateMediaLibraryInternal(IProgress<double> progress, CancellationToken cancellationToken)
{
IsScanRunning = true;
- _libraryMonitorFactory().Stop();
+ LibraryMonitor.Stop();
try
{
@@ -1040,7 +1010,7 @@ namespace Emby.Server.Implementations.Library
}
finally
{
- _libraryMonitorFactory().Start();
+ LibraryMonitor.Start();
IsScanRunning = false;
}
}
@@ -1149,7 +1119,7 @@ namespace Emby.Server.Implementations.Library
progress.Report(percent * 100);
}
- ItemRepository.UpdateInheritedValues(cancellationToken);
+ _itemRepository.UpdateInheritedValues(cancellationToken);
progress.Report(100);
}
@@ -1169,9 +1139,9 @@ namespace Emby.Server.Implementations.Library
var topLibraryFolders = GetUserRootFolder().Children.ToList();
_logger.LogDebug("Getting refreshQueue");
- var refreshQueue = includeRefreshState ? _providerManagerFactory().GetRefreshQueue() : null;
+ var refreshQueue = includeRefreshState ? ProviderManager.GetRefreshQueue() : null;
- return _fileSystem.GetDirectoryPaths(ConfigurationManager.ApplicationPaths.DefaultUserViewsPath)
+ return _fileSystem.GetDirectoryPaths(_configurationManager.ApplicationPaths.DefaultUserViewsPath)
.Select(dir => GetVirtualFolderInfo(dir, topLibraryFolders, refreshQueue))
.ToList();
}
@@ -1246,7 +1216,7 @@ namespace Emby.Server.Implementations.Library
throw new ArgumentException("Guid can't be empty", nameof(id));
}
- if (LibraryItemsCache.TryGetValue(id, out BaseItem item))
+ if (_libraryItemsCache.TryGetValue(id, out BaseItem item))
{
return item;
}
@@ -1277,7 +1247,7 @@ namespace Emby.Server.Implementations.Library
AddUserToQuery(query, query.User, allowExternalContent);
}
- return ItemRepository.GetItemList(query);
+ return _itemRepository.GetItemList(query);
}
public List<BaseItem> GetItemList(InternalItemsQuery query)
@@ -1301,7 +1271,7 @@ namespace Emby.Server.Implementations.Library
AddUserToQuery(query, query.User);
}
- return ItemRepository.GetCount(query);
+ return _itemRepository.GetCount(query);
}
public List<BaseItem> GetItemList(InternalItemsQuery query, List<BaseItem> parents)
@@ -1316,7 +1286,7 @@ namespace Emby.Server.Implementations.Library
}
}
- return ItemRepository.GetItemList(query);
+ return _itemRepository.GetItemList(query);
}
public QueryResult<BaseItem> QueryItems(InternalItemsQuery query)
@@ -1328,12 +1298,12 @@ namespace Emby.Server.Implementations.Library
if (query.EnableTotalRecordCount)
{
- return ItemRepository.GetItems(query);
+ return _itemRepository.GetItems(query);
}
return new QueryResult<BaseItem>
{
- Items = ItemRepository.GetItemList(query).ToArray()
+ Items = _itemRepository.GetItemList(query).ToArray()
};
}
@@ -1344,7 +1314,7 @@ namespace Emby.Server.Implementations.Library
AddUserToQuery(query, query.User);
}
- return ItemRepository.GetItemIdsList(query);
+ return _itemRepository.GetItemIdsList(query);
}
public QueryResult<(BaseItem, ItemCounts)> GetStudios(InternalItemsQuery query)
@@ -1355,7 +1325,7 @@ namespace Emby.Server.Implementations.Library
}
SetTopParentOrAncestorIds(query);
- return ItemRepository.GetStudios(query);
+ return _itemRepository.GetStudios(query);
}
public QueryResult<(BaseItem, ItemCounts)> GetGenres(InternalItemsQuery query)
@@ -1366,7 +1336,7 @@ namespace Emby.Server.Implementations.Library
}
SetTopParentOrAncestorIds(query);
- return ItemRepository.GetGenres(query);
+ return _itemRepository.GetGenres(query);
}
public QueryResult<(BaseItem, ItemCounts)> GetMusicGenres(InternalItemsQuery query)
@@ -1377,7 +1347,7 @@ namespace Emby.Server.Implementations.Library
}
SetTopParentOrAncestorIds(query);
- return ItemRepository.GetMusicGenres(query);
+ return _itemRepository.GetMusicGenres(query);
}
public QueryResult<(BaseItem, ItemCounts)> GetAllArtists(InternalItemsQuery query)
@@ -1388,7 +1358,7 @@ namespace Emby.Server.Implementations.Library
}
SetTopParentOrAncestorIds(query);
- return ItemRepository.GetAllArtists(query);
+ return _itemRepository.GetAllArtists(query);
}
public QueryResult<(BaseItem, ItemCounts)> GetArtists(InternalItemsQuery query)
@@ -1399,7 +1369,7 @@ namespace Emby.Server.Implementations.Library
}
SetTopParentOrAncestorIds(query);
- return ItemRepository.GetArtists(query);
+ return _itemRepository.GetArtists(query);
}
private void SetTopParentOrAncestorIds(InternalItemsQuery query)
@@ -1440,7 +1410,7 @@ namespace Emby.Server.Implementations.Library
}
SetTopParentOrAncestorIds(query);
- return ItemRepository.GetAlbumArtists(query);
+ return _itemRepository.GetAlbumArtists(query);
}
public QueryResult<BaseItem> GetItemsResult(InternalItemsQuery query)
@@ -1461,10 +1431,10 @@ namespace Emby.Server.Implementations.Library
if (query.EnableTotalRecordCount)
{
- return ItemRepository.GetItems(query);
+ return _itemRepository.GetItems(query);
}
- var list = ItemRepository.GetItemList(query);
+ var list = _itemRepository.GetItemList(query);
return new QueryResult<BaseItem>
{
@@ -1510,7 +1480,7 @@ namespace Emby.Server.Implementations.Library
string.IsNullOrEmpty(query.SeriesPresentationUniqueKey) &&
query.ItemIds.Length == 0)
{
- var userViews = _userviewManager().GetUserViews(new UserViewQuery
+ var userViews = UserViewManager.GetUserViews(new UserViewQuery
{
UserId = user.Id,
IncludeHidden = true,
@@ -1810,7 +1780,7 @@ namespace Emby.Server.Implementations.Library
// Don't iterate multiple times
var itemsList = items.ToList();
- ItemRepository.SaveItems(itemsList, cancellationToken);
+ _itemRepository.SaveItems(itemsList, cancellationToken);
foreach (var item in itemsList)
{
@@ -1847,7 +1817,7 @@ namespace Emby.Server.Implementations.Library
public void UpdateImages(BaseItem item)
{
- ItemRepository.SaveImages(item);
+ _itemRepository.SaveImages(item);
RegisterItem(item);
}
@@ -1864,7 +1834,7 @@ namespace Emby.Server.Implementations.Library
{
if (item.IsFileProtocol)
{
- _providerManagerFactory().SaveMetadata(item, updateReason);
+ ProviderManager.SaveMetadata(item, updateReason);
}
item.DateLastSaved = DateTime.UtcNow;
@@ -1872,7 +1842,7 @@ namespace Emby.Server.Implementations.Library
RegisterItem(item);
}
- ItemRepository.SaveItems(itemsList, cancellationToken);
+ _itemRepository.SaveItems(itemsList, cancellationToken);
if (ItemUpdated != null)
{
@@ -1948,7 +1918,7 @@ namespace Emby.Server.Implementations.Library
/// <returns>BaseItem.</returns>
public BaseItem RetrieveItem(Guid id)
{
- return ItemRepository.RetrieveItem(id);
+ return _itemRepository.RetrieveItem(id);
}
public List<Folder> GetCollectionFolders(BaseItem item)
@@ -2067,7 +2037,7 @@ namespace Emby.Server.Implementations.Library
private string GetContentTypeOverride(string path, bool inherit)
{
- var nameValuePair = ConfigurationManager.Configuration.ContentTypes
+ var nameValuePair = _configurationManager.Configuration.ContentTypes
.FirstOrDefault(i => _fileSystem.AreEqual(i.Name, path)
|| (inherit && !string.IsNullOrEmpty(i.Name)
&& _fileSystem.ContainsSubPath(i.Name, path)));
@@ -2116,7 +2086,7 @@ namespace Emby.Server.Implementations.Library
string sortName)
{
var path = Path.Combine(
- ConfigurationManager.ApplicationPaths.InternalMetadataPath,
+ _configurationManager.ApplicationPaths.InternalMetadataPath,
"views",
_fileSystem.GetValidFilename(viewType));
@@ -2148,7 +2118,7 @@ namespace Emby.Server.Implementations.Library
if (refresh)
{
item.UpdateToRepository(ItemUpdateType.MetadataImport, CancellationToken.None);
- _providerManagerFactory().QueueRefresh(item.Id, new MetadataRefreshOptions(new DirectoryService(_fileSystem)), RefreshPriority.Normal);
+ ProviderManager.QueueRefresh(item.Id, new MetadataRefreshOptions(new DirectoryService(_fileSystem)), RefreshPriority.Normal);
}
return item;
@@ -2166,7 +2136,7 @@ namespace Emby.Server.Implementations.Library
var id = GetNewItemId(idValues, typeof(UserView));
- var path = Path.Combine(ConfigurationManager.ApplicationPaths.InternalMetadataPath, "views", id.ToString("N", CultureInfo.InvariantCulture));
+ var path = Path.Combine(_configurationManager.ApplicationPaths.InternalMetadataPath, "views", id.ToString("N", CultureInfo.InvariantCulture));
var item = GetItemById(id) as UserView;
@@ -2203,7 +2173,7 @@ namespace Emby.Server.Implementations.Library
if (refresh)
{
- _providerManagerFactory().QueueRefresh(
+ ProviderManager.QueueRefresh(
item.Id,
new MetadataRefreshOptions(new DirectoryService(_fileSystem))
{
@@ -2270,7 +2240,7 @@ namespace Emby.Server.Implementations.Library
if (refresh)
{
- _providerManagerFactory().QueueRefresh(
+ ProviderManager.QueueRefresh(
item.Id,
new MetadataRefreshOptions(new DirectoryService(_fileSystem))
{
@@ -2304,7 +2274,7 @@ namespace Emby.Server.Implementations.Library
var id = GetNewItemId(idValues, typeof(UserView));
- var path = Path.Combine(ConfigurationManager.ApplicationPaths.InternalMetadataPath, "views", id.ToString("N", CultureInfo.InvariantCulture));
+ var path = Path.Combine(_configurationManager.ApplicationPaths.InternalMetadataPath, "views", id.ToString("N", CultureInfo.InvariantCulture));
var item = GetItemById(id) as UserView;
@@ -2347,7 +2317,7 @@ namespace Emby.Server.Implementations.Library
if (refresh)
{
- _providerManagerFactory().QueueRefresh(
+ ProviderManager.QueueRefresh(
item.Id,
new MetadataRefreshOptions(new DirectoryService(_fileSystem))
{
@@ -2365,7 +2335,7 @@ namespace Emby.Server.Implementations.Library
string videoPath,
string[] files)
{
- new SubtitleResolver(BaseItem.LocalizationManager, _fileSystem).AddExternalSubtitleStreams(streams, videoPath, streams.Count, files);
+ new SubtitleResolver(BaseItem.LocalizationManager).AddExternalSubtitleStreams(streams, videoPath, streams.Count, files);
}
/// <inheritdoc />
@@ -2610,14 +2580,12 @@ namespace Emby.Server.Implementations.Library
}).OrderBy(i => i.Path);
}
- private static readonly string[] ExtrasSubfolderNames = new[] { "extras", "specials", "shorts", "scenes", "featurettes", "behind the scenes", "deleted scenes", "interviews" };
-
public IEnumerable<Video> FindExtras(BaseItem owner, List<FileSystemMetadata> fileSystemChildren, IDirectoryService directoryService)
{
var namingOptions = GetNamingOptions();
var files = owner.IsInMixedFolder ? new List<FileSystemMetadata>() : fileSystemChildren.Where(i => i.IsDirectory)
- .Where(i => ExtrasSubfolderNames.Contains(i.Name ?? string.Empty, StringComparer.OrdinalIgnoreCase))
+ .Where(i => BaseItem.AllExtrasTypesFolderNames.Contains(i.Name ?? string.Empty, StringComparer.OrdinalIgnoreCase))
.SelectMany(i => _fileSystem.GetFiles(i.FullName, _videoFileExtensions, false, false))
.ToList();
@@ -2678,8 +2646,8 @@ namespace Emby.Server.Implementations.Library
}
}
- var metadataPath = ConfigurationManager.Configuration.MetadataPath;
- var metadataNetworkPath = ConfigurationManager.Configuration.MetadataNetworkPath;
+ var metadataPath = _configurationManager.Configuration.MetadataPath;
+ var metadataNetworkPath = _configurationManager.Configuration.MetadataNetworkPath;
if (!string.IsNullOrWhiteSpace(metadataPath) && !string.IsNullOrWhiteSpace(metadataNetworkPath))
{
@@ -2690,7 +2658,7 @@ namespace Emby.Server.Implementations.Library
}
}
- foreach (var map in ConfigurationManager.Configuration.PathSubstitutions)
+ foreach (var map in _configurationManager.Configuration.PathSubstitutions)
{
if (!string.IsNullOrWhiteSpace(map.From))
{
@@ -2759,7 +2727,7 @@ namespace Emby.Server.Implementations.Library
public List<PersonInfo> GetPeople(InternalPeopleQuery query)
{
- return ItemRepository.GetPeople(query);
+ return _itemRepository.GetPeople(query);
}
public List<PersonInfo> GetPeople(BaseItem item)
@@ -2782,7 +2750,7 @@ namespace Emby.Server.Implementations.Library
public List<Person> GetPeopleItems(InternalPeopleQuery query)
{
- return ItemRepository.GetPeopleNames(query).Select(i =>
+ return _itemRepository.GetPeopleNames(query).Select(i =>
{
try
{
@@ -2799,7 +2767,7 @@ namespace Emby.Server.Implementations.Library
public List<string> GetPeopleNames(InternalPeopleQuery query)
{
- return ItemRepository.GetPeopleNames(query);
+ return _itemRepository.GetPeopleNames(query);
}
public void UpdatePeople(BaseItem item, List<PersonInfo> people)
@@ -2809,7 +2777,7 @@ namespace Emby.Server.Implementations.Library
return;
}
- ItemRepository.UpdatePeople(item.Id, people);
+ _itemRepository.UpdatePeople(item.Id, people);
}
public async Task<ItemImageInfo> ConvertImageToLocal(BaseItem item, ItemImageInfo image, int imageIndex)
@@ -2820,7 +2788,7 @@ namespace Emby.Server.Implementations.Library
{
_logger.LogDebug("ConvertImageToLocal item {0} - image url: {1}", item.Id, url);
- await _providerManagerFactory().SaveImage(item, url, image.Type, imageIndex, CancellationToken.None).ConfigureAwait(false);
+ await ProviderManager.SaveImage(item, url, image.Type, imageIndex, CancellationToken.None).ConfigureAwait(false);
item.UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None);
@@ -2853,7 +2821,7 @@ namespace Emby.Server.Implementations.Library
name = _fileSystem.GetValidFilename(name);
- var rootFolderPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath;
+ var rootFolderPath = _configurationManager.ApplicationPaths.DefaultUserViewsPath;
var virtualFolderPath = Path.Combine(rootFolderPath, name);
while (Directory.Exists(virtualFolderPath))
@@ -2872,7 +2840,7 @@ namespace Emby.Server.Implementations.Library
}
}
- _libraryMonitorFactory().Stop();
+ LibraryMonitor.Stop();
try
{
@@ -2907,7 +2875,7 @@ namespace Emby.Server.Implementations.Library
{
// Need to add a delay here or directory watchers may still pick up the changes
await Task.Delay(1000).ConfigureAwait(false);
- _libraryMonitorFactory().Start();
+ LibraryMonitor.Start();
}
}
}
@@ -2967,7 +2935,7 @@ namespace Emby.Server.Implementations.Library
throw new FileNotFoundException("The network path does not exist.");
}
- var rootFolderPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath;
+ var rootFolderPath = _configurationManager.ApplicationPaths.DefaultUserViewsPath;
var virtualFolderPath = Path.Combine(rootFolderPath, virtualFolderName);
var shortcutFilename = Path.GetFileNameWithoutExtension(path);
@@ -3010,7 +2978,7 @@ namespace Emby.Server.Implementations.Library
throw new FileNotFoundException("The network path does not exist.");
}
- var rootFolderPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath;
+ var rootFolderPath = _configurationManager.ApplicationPaths.DefaultUserViewsPath;
var virtualFolderPath = Path.Combine(rootFolderPath, virtualFolderName);
var libraryOptions = CollectionFolder.GetLibraryOptions(virtualFolderPath);
@@ -3063,7 +3031,7 @@ namespace Emby.Server.Implementations.Library
throw new ArgumentNullException(nameof(name));
}
- var rootFolderPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath;
+ var rootFolderPath = _configurationManager.ApplicationPaths.DefaultUserViewsPath;
var path = Path.Combine(rootFolderPath, name);
@@ -3072,7 +3040,7 @@ namespace Emby.Server.Implementations.Library
throw new FileNotFoundException("The media folder does not exist");
}
- _libraryMonitorFactory().Stop();
+ LibraryMonitor.Stop();
try
{
@@ -3092,7 +3060,7 @@ namespace Emby.Server.Implementations.Library
{
// Need to add a delay here or directory watchers may still pick up the changes
await Task.Delay(1000).ConfigureAwait(false);
- _libraryMonitorFactory().Start();
+ LibraryMonitor.Start();
}
}
}
@@ -3106,7 +3074,7 @@ namespace Emby.Server.Implementations.Library
var removeList = new List<NameValuePair>();
- foreach (var contentType in ConfigurationManager.Configuration.ContentTypes)
+ foreach (var contentType in _configurationManager.Configuration.ContentTypes)
{
if (string.IsNullOrWhiteSpace(contentType.Name))
{
@@ -3121,11 +3089,11 @@ namespace Emby.Server.Implementations.Library
if (removeList.Count > 0)
{
- ConfigurationManager.Configuration.ContentTypes = ConfigurationManager.Configuration.ContentTypes
+ _configurationManager.Configuration.ContentTypes = _configurationManager.Configuration.ContentTypes
.Except(removeList)
- .ToArray();
+ .ToArray();
- ConfigurationManager.SaveConfiguration();
+ _configurationManager.SaveConfiguration();
}
}
@@ -3136,7 +3104,7 @@ namespace Emby.Server.Implementations.Library
throw new ArgumentNullException(nameof(mediaPath));
}
- var rootFolderPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath;
+ var rootFolderPath = _configurationManager.ApplicationPaths.DefaultUserViewsPath;
var virtualFolderPath = Path.Combine(rootFolderPath, virtualFolderName);
if (!Directory.Exists(virtualFolderPath))
diff --git a/Emby.Server.Implementations/Library/LiveStreamHelper.cs b/Emby.Server.Implementations/Library/LiveStreamHelper.cs
index f28f4a538..ed7d8aa40 100644
--- a/Emby.Server.Implementations/Library/LiveStreamHelper.cs
+++ b/Emby.Server.Implementations/Library/LiveStreamHelper.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs
index e310065b2..01fe98f3a 100644
--- a/Emby.Server.Implementations/Library/MediaSourceManager.cs
+++ b/Emby.Server.Implementations/Library/MediaSourceManager.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Collections.Generic;
@@ -34,13 +33,13 @@ namespace Emby.Server.Implementations.Library
private readonly ILibraryManager _libraryManager;
private readonly IJsonSerializer _jsonSerializer;
private readonly IFileSystem _fileSystem;
-
- private IMediaSourceProvider[] _providers;
private readonly ILogger _logger;
private readonly IUserDataManager _userDataManager;
- private readonly Func<IMediaEncoder> _mediaEncoder;
- private ILocalizationManager _localizationManager;
- private IApplicationPaths _appPaths;
+ private readonly IMediaEncoder _mediaEncoder;
+ private readonly ILocalizationManager _localizationManager;
+ private readonly IApplicationPaths _appPaths;
+
+ private IMediaSourceProvider[] _providers;
public MediaSourceManager(
IItemRepository itemRepo,
@@ -48,16 +47,16 @@ namespace Emby.Server.Implementations.Library
ILocalizationManager localizationManager,
IUserManager userManager,
ILibraryManager libraryManager,
- ILoggerFactory loggerFactory,
+ ILogger<MediaSourceManager> logger,
IJsonSerializer jsonSerializer,
IFileSystem fileSystem,
IUserDataManager userDataManager,
- Func<IMediaEncoder> mediaEncoder)
+ IMediaEncoder mediaEncoder)
{
_itemRepo = itemRepo;
_userManager = userManager;
_libraryManager = libraryManager;
- _logger = loggerFactory.CreateLogger(nameof(MediaSourceManager));
+ _logger = logger;
_jsonSerializer = jsonSerializer;
_fileSystem = fileSystem;
_userDataManager = userDataManager;
@@ -497,7 +496,7 @@ namespace Emby.Server.Implementations.Library
// hack - these two values were taken from LiveTVMediaSourceProvider
string cacheKey = request.OpenToken;
- await new LiveStreamHelper(_mediaEncoder(), _logger, _jsonSerializer, _appPaths)
+ await new LiveStreamHelper(_mediaEncoder, _logger, _jsonSerializer, _appPaths)
.AddMediaInfoWithProbe(mediaSource, isAudio, cacheKey, true, cancellationToken)
.ConfigureAwait(false);
}
@@ -622,7 +621,7 @@ namespace Emby.Server.Implementations.Library
if (liveStreamInfo is IDirectStreamProvider)
{
- var info = await _mediaEncoder().GetMediaInfo(new MediaInfoRequest
+ var info = await _mediaEncoder.GetMediaInfo(new MediaInfoRequest
{
MediaSource = mediaSource,
ExtractChapters = false,
@@ -675,7 +674,7 @@ namespace Emby.Server.Implementations.Library
mediaSource.AnalyzeDurationMs = 3000;
}
- mediaInfo = await _mediaEncoder().GetMediaInfo(new MediaInfoRequest
+ mediaInfo = await _mediaEncoder.GetMediaInfo(new MediaInfoRequest
{
MediaSource = mediaSource,
MediaType = isAudio ? DlnaProfileType.Audio : DlnaProfileType.Video,
diff --git a/Emby.Server.Implementations/Library/MediaStreamSelector.cs b/Emby.Server.Implementations/Library/MediaStreamSelector.cs
index 1652ad974..e27145a1d 100644
--- a/Emby.Server.Implementations/Library/MediaStreamSelector.cs
+++ b/Emby.Server.Implementations/Library/MediaStreamSelector.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Collections.Generic;
@@ -36,7 +35,8 @@ namespace Emby.Server.Implementations.Library
return null;
}
- public static int? GetDefaultSubtitleStreamIndex(List<MediaStream> streams,
+ public static int? GetDefaultSubtitleStreamIndex(
+ List<MediaStream> streams,
string[] preferredLanguages,
SubtitlePlaybackMode mode,
string audioTrackLanguage)
@@ -116,7 +116,8 @@ namespace Emby.Server.Implementations.Library
.ThenBy(i => i.Index);
}
- public static void SetSubtitleStreamScores(List<MediaStream> streams,
+ public static void SetSubtitleStreamScores(
+ List<MediaStream> streams,
string[] preferredLanguages,
SubtitlePlaybackMode mode,
string audioTrackLanguage)
diff --git a/Emby.Server.Implementations/Library/MusicManager.cs b/Emby.Server.Implementations/Library/MusicManager.cs
index 29af6670b..1ec578371 100644
--- a/Emby.Server.Implementations/Library/MusicManager.cs
+++ b/Emby.Server.Implementations/Library/MusicManager.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/Library/PathExtensions.cs b/Emby.Server.Implementations/Library/PathExtensions.cs
index 4fdf73b77..06ff3e611 100644
--- a/Emby.Server.Implementations/Library/PathExtensions.cs
+++ b/Emby.Server.Implementations/Library/PathExtensions.cs
@@ -1,3 +1,5 @@
+#nullable enable
+
using System;
using System.Text.RegularExpressions;
@@ -12,24 +14,24 @@ namespace Emby.Server.Implementations.Library
/// Gets the attribute value.
/// </summary>
/// <param name="str">The STR.</param>
- /// <param name="attrib">The attrib.</param>
+ /// <param name="attribute">The attrib.</param>
/// <returns>System.String.</returns>
- /// <exception cref="ArgumentNullException">attrib</exception>
- public static string GetAttributeValue(this string str, string attrib)
+ /// <exception cref="ArgumentException"><paramref name="str" /> or <paramref name="attribute" /> is empty.</exception>
+ public static string? GetAttributeValue(this string str, string attribute)
{
- if (string.IsNullOrEmpty(str))
+ if (str.Length == 0)
{
- throw new ArgumentNullException(nameof(str));
+ throw new ArgumentException("String can't be empty.", nameof(str));
}
- if (string.IsNullOrEmpty(attrib))
+ if (attribute.Length == 0)
{
- throw new ArgumentNullException(nameof(attrib));
+ throw new ArgumentException("String can't be empty.", nameof(attribute));
}
- string srch = "[" + attrib + "=";
+ string srch = "[" + attribute + "=";
int start = str.IndexOf(srch, StringComparison.OrdinalIgnoreCase);
- if (start > -1)
+ if (start != -1)
{
start += srch.Length;
int end = str.IndexOf(']', start);
@@ -37,9 +39,9 @@ namespace Emby.Server.Implementations.Library
}
// for imdbid we also accept pattern matching
- if (string.Equals(attrib, "imdbid", StringComparison.OrdinalIgnoreCase))
+ if (string.Equals(attribute, "imdbid", StringComparison.OrdinalIgnoreCase))
{
- var m = Regex.Match(str, "tt\\d{7}", RegexOptions.IgnoreCase);
+ var m = Regex.Match(str, "tt([0-9]{7,8})", RegexOptions.IgnoreCase);
return m.Success ? m.Value : null;
}
diff --git a/Emby.Server.Implementations/Library/ResolverHelper.cs b/Emby.Server.Implementations/Library/ResolverHelper.cs
index 96d1bff92..7ca15b4e5 100644
--- a/Emby.Server.Implementations/Library/ResolverHelper.cs
+++ b/Emby.Server.Implementations/Library/ResolverHelper.cs
@@ -9,7 +9,7 @@ using MediaBrowser.Model.IO;
namespace Emby.Server.Implementations.Library
{
/// <summary>
- /// Class ResolverHelper
+ /// Class ResolverHelper.
/// </summary>
public static class ResolverHelper
{
@@ -118,10 +118,12 @@ namespace Emby.Server.Implementations.Library
{
throw new ArgumentNullException(nameof(fileSystem));
}
+
if (item == null)
{
throw new ArgumentNullException(nameof(item));
}
+
if (args == null)
{
throw new ArgumentNullException(nameof(args));
diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs
index 524fb7c10..fefc8e789 100644
--- a/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs
index 848cdb7bd..fb75593bd 100644
--- a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.IO;
diff --git a/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs
index 1e2e0704c..0b93ebeb8 100644
--- a/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.IO;
diff --git a/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs
index 8ad546f8e..bcfcee9c6 100644
--- a/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs b/Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs
index fa6d57466..41561916f 100644
--- a/Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.IO;
using System.Linq;
diff --git a/Emby.Server.Implementations/Library/Resolvers/SpecialFolderResolver.cs b/Emby.Server.Implementations/Library/Resolvers/SpecialFolderResolver.cs
index eca60b133..1030ed39d 100644
--- a/Emby.Server.Implementations/Library/Resolvers/SpecialFolderResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/SpecialFolderResolver.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.IO;
diff --git a/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs b/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs
index f8c26c2e1..dd6bd8ee8 100644
--- a/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Collections.Generic;
@@ -9,7 +8,6 @@ using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Resolvers;
-using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using Microsoft.Extensions.Logging;
diff --git a/Emby.Server.Implementations/Library/Resolvers/VideoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/VideoResolver.cs
index 6404d6476..62268fce9 100644
--- a/Emby.Server.Implementations/Library/Resolvers/VideoResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/VideoResolver.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
diff --git a/Emby.Server.Implementations/Library/SearchEngine.cs b/Emby.Server.Implementations/Library/SearchEngine.cs
index 76ae14720..59a77607d 100644
--- a/Emby.Server.Implementations/Library/SearchEngine.cs
+++ b/Emby.Server.Implementations/Library/SearchEngine.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Collections.Generic;
@@ -18,16 +17,15 @@ namespace Emby.Server.Implementations.Library
{
public class SearchEngine : ISearchEngine
{
+ private readonly ILogger _logger;
private readonly ILibraryManager _libraryManager;
private readonly IUserManager _userManager;
- private readonly ILogger _logger;
- public SearchEngine(ILoggerFactory loggerFactory, ILibraryManager libraryManager, IUserManager userManager)
+ public SearchEngine(ILogger<SearchEngine> logger, ILibraryManager libraryManager, IUserManager userManager)
{
+ _logger = logger;
_libraryManager = libraryManager;
_userManager = userManager;
-
- _logger = loggerFactory.CreateLogger("SearchEngine");
}
public QueryResult<SearchHintInfo> GetSearchHints(SearchQuery query)
diff --git a/Emby.Server.Implementations/Library/UserDataManager.cs b/Emby.Server.Implementations/Library/UserDataManager.cs
index f1fb35d9a..a9772a078 100644
--- a/Emby.Server.Implementations/Library/UserDataManager.cs
+++ b/Emby.Server.Implementations/Library/UserDataManager.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Collections.Concurrent;
@@ -29,25 +28,24 @@ namespace Emby.Server.Implementations.Library
private readonly ILogger _logger;
private readonly IServerConfigurationManager _config;
-
- private Func<IUserManager> _userManager;
-
- public UserDataManager(ILoggerFactory loggerFactory, IServerConfigurationManager config, Func<IUserManager> userManager)
+ private readonly IUserManager _userManager;
+ private readonly IUserDataRepository _repository;
+
+ public UserDataManager(
+ ILogger<UserDataManager> logger,
+ IServerConfigurationManager config,
+ IUserManager userManager,
+ IUserDataRepository repository)
{
+ _logger = logger;
_config = config;
- _logger = loggerFactory.CreateLogger(GetType().Name);
_userManager = userManager;
+ _repository = repository;
}
- /// <summary>
- /// Gets or sets the repository.
- /// </summary>
- /// <value>The repository.</value>
- public IUserDataRepository Repository { get; set; }
-
public void SaveUserData(Guid userId, BaseItem item, UserItemData userData, UserDataSaveReason reason, CancellationToken cancellationToken)
{
- var user = _userManager().GetUserById(userId);
+ var user = _userManager.GetUserById(userId);
SaveUserData(user, item, userData, reason, cancellationToken);
}
@@ -72,7 +70,7 @@ namespace Emby.Server.Implementations.Library
foreach (var key in keys)
{
- Repository.SaveUserData(userId, key, userData, cancellationToken);
+ _repository.SaveUserData(userId, key, userData, cancellationToken);
}
var cacheKey = GetCacheKey(userId, item.Id);
@@ -97,9 +95,9 @@ namespace Emby.Server.Implementations.Library
/// <returns></returns>
public void SaveAllUserData(Guid userId, UserItemData[] userData, CancellationToken cancellationToken)
{
- var user = _userManager().GetUserById(userId);
+ var user = _userManager.GetUserById(userId);
- Repository.SaveAllUserData(user.InternalId, userData, cancellationToken);
+ _repository.SaveAllUserData(user.InternalId, userData, cancellationToken);
}
/// <summary>
@@ -109,14 +107,14 @@ namespace Emby.Server.Implementations.Library
/// <returns></returns>
public List<UserItemData> GetAllUserData(Guid userId)
{
- var user = _userManager().GetUserById(userId);
+ var user = _userManager.GetUserById(userId);
- return Repository.GetAllUserData(user.InternalId);
+ return _repository.GetAllUserData(user.InternalId);
}
public UserItemData GetUserData(Guid userId, Guid itemId, List<string> keys)
{
- var user = _userManager().GetUserById(userId);
+ var user = _userManager.GetUserById(userId);
return GetUserData(user, itemId, keys);
}
@@ -132,7 +130,7 @@ namespace Emby.Server.Implementations.Library
private UserItemData GetUserDataInternal(long internalUserId, List<string> keys)
{
- var userData = Repository.GetUserData(internalUserId, keys);
+ var userData = _repository.GetUserData(internalUserId, keys);
if (userData != null)
{
diff --git a/Emby.Server.Implementations/Library/UserManager.cs b/Emby.Server.Implementations/Library/UserManager.cs
index 6e203f894..d63bc6bda 100644
--- a/Emby.Server.Implementations/Library/UserManager.cs
+++ b/Emby.Server.Implementations/Library/UserManager.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Collections.Concurrent;
@@ -21,6 +20,7 @@ using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Plugins;
using MediaBrowser.Controller.Providers;
@@ -45,22 +45,14 @@ namespace Emby.Server.Implementations.Library
{
private readonly object _policySyncLock = new object();
private readonly object _configSyncLock = new object();
- /// <summary>
- /// The logger.
- /// </summary>
- private readonly ILogger _logger;
- /// <summary>
- /// Gets the active user repository.
- /// </summary>
- /// <value>The user repository.</value>
+ private readonly ILogger _logger;
private readonly IUserRepository _userRepository;
private readonly IXmlSerializer _xmlSerializer;
private readonly IJsonSerializer _jsonSerializer;
private readonly INetworkManager _networkManager;
-
- private readonly Func<IImageProcessor> _imageProcessorFactory;
- private readonly Func<IDtoService> _dtoServiceFactory;
+ private readonly IImageProcessor _imageProcessor;
+ private readonly Lazy<IDtoService> _dtoServiceFactory;
private readonly IServerApplicationHost _appHost;
private readonly IFileSystem _fileSystem;
private readonly ICryptoProvider _cryptoProvider;
@@ -75,13 +67,15 @@ namespace Emby.Server.Implementations.Library
private IPasswordResetProvider[] _passwordResetProviders;
private DefaultPasswordResetProvider _defaultPasswordResetProvider;
+ private IDtoService DtoService => _dtoServiceFactory.Value;
+
public UserManager(
ILogger<UserManager> logger,
IUserRepository userRepository,
IXmlSerializer xmlSerializer,
INetworkManager networkManager,
- Func<IImageProcessor> imageProcessorFactory,
- Func<IDtoService> dtoServiceFactory,
+ IImageProcessor imageProcessor,
+ Lazy<IDtoService> dtoServiceFactory,
IServerApplicationHost appHost,
IJsonSerializer jsonSerializer,
IFileSystem fileSystem,
@@ -91,7 +85,7 @@ namespace Emby.Server.Implementations.Library
_userRepository = userRepository;
_xmlSerializer = xmlSerializer;
_networkManager = networkManager;
- _imageProcessorFactory = imageProcessorFactory;
+ _imageProcessor = imageProcessor;
_dtoServiceFactory = dtoServiceFactory;
_appHost = appHost;
_jsonSerializer = jsonSerializer;
@@ -265,6 +259,7 @@ namespace Emby.Server.Implementations.Library
{
if (string.IsNullOrWhiteSpace(username))
{
+ _logger.LogInformation("Authentication request without username has been denied (IP: {IP}).", remoteEndPoint);
throw new ArgumentNullException(nameof(username));
}
@@ -320,26 +315,26 @@ namespace Emby.Server.Implementations.Library
if (user == null)
{
+ _logger.LogInformation("Authentication request for {UserName} has been denied (IP: {IP}).", username, remoteEndPoint);
throw new AuthenticationException("Invalid username or password entered.");
}
if (user.Policy.IsDisabled)
{
- throw new AuthenticationException(
- string.Format(
- CultureInfo.InvariantCulture,
- "The {0} account is currently disabled. Please consult with your administrator.",
- user.Name));
+ _logger.LogInformation("Authentication request for {UserName} has been denied because this account is currently disabled (IP: {IP}).", username, remoteEndPoint);
+ throw new SecurityException($"The {user.Name} account is currently disabled. Please consult with your administrator.");
}
if (!user.Policy.EnableRemoteAccess && !_networkManager.IsInLocalNetwork(remoteEndPoint))
{
- throw new AuthenticationException("Forbidden.");
+ _logger.LogInformation("Authentication request for {UserName} forbidden: remote access disabled and user not in local network (IP: {IP}).", username, remoteEndPoint);
+ throw new SecurityException("Forbidden.");
}
if (!user.IsParentalScheduleAllowed())
{
- throw new AuthenticationException("User is not allowed access at this time.");
+ _logger.LogInformation("Authentication request for {UserName} is not allowed at this time due parental restrictions (IP: {IP}).", username, remoteEndPoint);
+ throw new SecurityException("User is not allowed access at this time.");
}
// Update LastActivityDate and LastLoginDate, then save
@@ -352,14 +347,14 @@ namespace Emby.Server.Implementations.Library
}
ResetInvalidLoginAttemptCount(user);
+ _logger.LogInformation("Authentication request for {UserName} has succeeded.", user.Name);
}
else
{
IncrementInvalidLoginAttemptCount(user);
+ _logger.LogInformation("Authentication request for {UserName} has been denied (IP: {IP}).", user.Name, remoteEndPoint);
}
- _logger.LogInformation("Authentication request for {0} {1}.", user.Name, success ? "has succeeded" : "has been denied");
-
return success ? user : null;
}
@@ -601,7 +596,7 @@ namespace Emby.Server.Implementations.Library
try
{
- _dtoServiceFactory().AttachPrimaryImageAspectRatio(dto, user);
+ DtoService.AttachPrimaryImageAspectRatio(dto, user);
}
catch (Exception ex)
{
@@ -626,7 +621,7 @@ namespace Emby.Server.Implementations.Library
{
try
{
- return _imageProcessorFactory().GetImageCacheTag(item, image);
+ return _imageProcessor.GetImageCacheTag(item, image);
}
catch (Exception ex)
{
@@ -806,17 +801,17 @@ namespace Emby.Server.Implementations.Library
// Delete user config dir
lock (_configSyncLock)
- lock (_policySyncLock)
- {
- try
- {
- Directory.Delete(user.ConfigurationDirectoryPath, true);
- }
- catch (IOException ex)
+ lock (_policySyncLock)
{
- _logger.LogError(ex, "Error deleting user config dir: {Path}", user.ConfigurationDirectoryPath);
+ try
+ {
+ Directory.Delete(user.ConfigurationDirectoryPath, true);
+ }
+ catch (IOException ex)
+ {
+ _logger.LogError(ex, "Error deleting user config dir: {Path}", user.ConfigurationDirectoryPath);
+ }
}
- }
_users.TryRemove(user.Id, out _);
diff --git a/Emby.Server.Implementations/Library/UserViewManager.cs b/Emby.Server.Implementations/Library/UserViewManager.cs
index 935deb71c..322819b05 100644
--- a/Emby.Server.Implementations/Library/UserViewManager.cs
+++ b/Emby.Server.Implementations/Library/UserViewManager.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs
index 9c4f5fe3d..2e13a3bb3 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.IO;
@@ -73,7 +72,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
UserAgent = "Emby/3.0",
// Shouldn't matter but may cause issues
- DecompressionMethod = CompressionMethod.None
+ DecompressionMethod = CompressionMethods.None
};
using (var response = await _httpClient.SendAsync(httpRequestOptions, HttpMethod.Get).ConfigureAwait(false))
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
index bbef93b8c..33f4ca146 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
@@ -1,9 +1,9 @@
-#pragma warning disable SA1600
#pragma warning disable CS1591
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
+using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
@@ -26,7 +26,6 @@ using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.Diagnostics;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Events;
@@ -62,7 +61,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
private readonly ILibraryManager _libraryManager;
private readonly IProviderManager _providerManager;
private readonly IMediaEncoder _mediaEncoder;
- private readonly IProcessFactory _processFactory;
private readonly IMediaSourceManager _mediaSourceManager;
private readonly IStreamHelper _streamHelper;
@@ -89,8 +87,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
ILibraryManager libraryManager,
ILibraryMonitor libraryMonitor,
IProviderManager providerManager,
- IMediaEncoder mediaEncoder,
- IProcessFactory processFactory)
+ IMediaEncoder mediaEncoder)
{
Current = this;
@@ -103,7 +100,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
_libraryMonitor = libraryMonitor;
_providerManager = providerManager;
_mediaEncoder = mediaEncoder;
- _processFactory = processFactory;
_liveTvManager = (LiveTvManager)liveTvManager;
_jsonSerializer = jsonSerializer;
_mediaSourceManager = mediaSourceManager;
@@ -1063,7 +1059,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
var stream = new MediaSourceInfo
{
- EncoderPath = _appHost.GetLocalApiUrl("127.0.0.1") + "/LiveTv/LiveRecordings/" + info.Id + "/stream",
+ EncoderPath = _appHost.GetLocalApiUrl("127.0.0.1", true) + "/LiveTv/LiveRecordings/" + info.Id + "/stream",
EncoderProtocol = MediaProtocol.Http,
Path = info.Path,
Protocol = MediaProtocol.File,
@@ -1663,7 +1659,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
if (mediaSource.RequiresLooping || !(mediaSource.Container ?? string.Empty).EndsWith("ts", StringComparison.OrdinalIgnoreCase) || (mediaSource.Protocol != MediaProtocol.File && mediaSource.Protocol != MediaProtocol.Http))
{
- return new EncodedRecorder(_logger, _mediaEncoder, _config.ApplicationPaths, _jsonSerializer, _processFactory, _config);
+ return new EncodedRecorder(_logger, _mediaEncoder, _config.ApplicationPaths, _jsonSerializer, _config);
}
return new DirectRecorder(_logger, _httpClient, _streamHelper);
@@ -1684,16 +1680,19 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
try
{
- var process = _processFactory.Create(new ProcessOptions
+ var process = new Process
{
- Arguments = GetPostProcessArguments(path, options.RecordingPostProcessorArguments),
- CreateNoWindow = true,
- EnableRaisingEvents = true,
- ErrorDialog = false,
- FileName = options.RecordingPostProcessor,
- IsHidden = true,
- UseShellExecute = false
- });
+ StartInfo = new ProcessStartInfo
+ {
+ Arguments = GetPostProcessArguments(path, options.RecordingPostProcessorArguments),
+ CreateNoWindow = true,
+ ErrorDialog = false,
+ FileName = options.RecordingPostProcessor,
+ WindowStyle = ProcessWindowStyle.Hidden,
+ UseShellExecute = false
+ },
+ EnableRaisingEvents = true
+ };
_logger.LogInformation("Running recording post processor {0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments);
@@ -1713,11 +1712,9 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
private void Process_Exited(object sender, EventArgs e)
{
- using (var process = (IProcess)sender)
+ using (var process = (Process)sender)
{
_logger.LogInformation("Recording post-processing script completed with exit code {ExitCode}", process.ExitCode);
-
- process.Dispose();
}
}
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
index 8590c56df..bc86cc59a 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
@@ -1,8 +1,8 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Collections.Generic;
+using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Text;
@@ -14,7 +14,6 @@ using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.Diagnostics;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Serialization;
@@ -30,8 +29,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
private bool _hasExited;
private Stream _logFileStream;
private string _targetPath;
- private IProcess _process;
- private readonly IProcessFactory _processFactory;
+ private Process _process;
private readonly IJsonSerializer _json;
private readonly TaskCompletionSource<bool> _taskCompletionSource = new TaskCompletionSource<bool>();
private readonly IServerConfigurationManager _config;
@@ -41,14 +39,12 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
IMediaEncoder mediaEncoder,
IServerApplicationPaths appPaths,
IJsonSerializer json,
- IProcessFactory processFactory,
IServerConfigurationManager config)
{
_logger = logger;
_mediaEncoder = mediaEncoder;
_appPaths = appPaths;
_json = json;
- _processFactory = processFactory;
_config = config;
}
@@ -80,7 +76,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
_targetPath = targetFile;
Directory.CreateDirectory(Path.GetDirectoryName(targetFile));
- var process = _processFactory.Create(new ProcessOptions
+ var processStartInfo = new ProcessStartInfo
{
CreateNoWindow = true,
UseShellExecute = false,
@@ -91,14 +87,11 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
FileName = _mediaEncoder.EncoderPath,
Arguments = GetCommandLineArgs(mediaSource, inputFile, targetFile, duration),
- IsHidden = true,
- ErrorDialog = false,
- EnableRaisingEvents = true
- });
-
- _process = process;
+ WindowStyle = ProcessWindowStyle.Hidden,
+ ErrorDialog = false
+ };
- var commandLineLogMessage = process.StartInfo.FileName + " " + process.StartInfo.Arguments;
+ var commandLineLogMessage = processStartInfo.FileName + " " + processStartInfo.Arguments;
_logger.LogInformation(commandLineLogMessage);
var logFilePath = Path.Combine(_appPaths.LogDirectoryPath, "record-transcode-" + Guid.NewGuid() + ".txt");
@@ -110,16 +103,21 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
var commandLineLogMessageBytes = Encoding.UTF8.GetBytes(_json.SerializeToString(mediaSource) + Environment.NewLine + Environment.NewLine + commandLineLogMessage + Environment.NewLine + Environment.NewLine);
_logFileStream.Write(commandLineLogMessageBytes, 0, commandLineLogMessageBytes.Length);
- process.Exited += (sender, args) => OnFfMpegProcessExited(process, inputFile);
+ _process = new Process
+ {
+ StartInfo = processStartInfo,
+ EnableRaisingEvents = true
+ };
+ _process.Exited += (sender, args) => OnFfMpegProcessExited(_process, inputFile);
- process.Start();
+ _process.Start();
cancellationToken.Register(Stop);
onStarted();
// Important - don't await the log task or we won't be able to kill ffmpeg when the user stops playback
- StartStreamingLog(process.StandardError.BaseStream, _logFileStream);
+ StartStreamingLog(_process.StandardError.BaseStream, _logFileStream);
_logger.LogInformation("ffmpeg recording process started for {0}", _targetPath);
@@ -293,30 +291,33 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
/// <summary>
/// Processes the exited.
/// </summary>
- private void OnFfMpegProcessExited(IProcess process, string inputFile)
+ private void OnFfMpegProcessExited(Process process, string inputFile)
{
- _hasExited = true;
+ using (process)
+ {
+ _hasExited = true;
- _logFileStream?.Dispose();
- _logFileStream = null;
+ _logFileStream?.Dispose();
+ _logFileStream = null;
- var exitCode = process.ExitCode;
+ var exitCode = process.ExitCode;
- _logger.LogInformation("FFMpeg recording exited with code {ExitCode} for {Path}", exitCode, _targetPath);
+ _logger.LogInformation("FFMpeg recording exited with code {ExitCode} for {Path}", exitCode, _targetPath);
- if (exitCode == 0)
- {
- _taskCompletionSource.TrySetResult(true);
- }
- else
- {
- _taskCompletionSource.TrySetException(
- new Exception(
- string.Format(
- CultureInfo.InvariantCulture,
- "Recording for {0} failed. Exit code {1}",
- _targetPath,
- exitCode)));
+ if (exitCode == 0)
+ {
+ _taskCompletionSource.TrySetResult(true);
+ }
+ else
+ {
+ _taskCompletionSource.TrySetException(
+ new Exception(
+ string.Format(
+ CultureInfo.InvariantCulture,
+ "Recording for {0} failed. Exit code {1}",
+ _targetPath,
+ exitCode)));
+ }
}
}
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs
index a716b6240..69a9cb78a 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System.Threading.Tasks;
using MediaBrowser.Controller.Plugins;
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EpgChannelData.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EpgChannelData.cs
index 498aa3c26..463d0ed0a 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/EpgChannelData.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EpgChannelData.cs
@@ -1,4 +1,3 @@
-#pragma warning disable SA1600
#pragma warning disable CS1591
using System;
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/IRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/IRecorder.cs
index d6a1aee38..4712724d6 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/IRecorder.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/IRecorder.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Threading;
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs
index 6d42a58f4..fc543dc55 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs
index 4cb9f6fe8..0b0ff6cb3 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Globalization;
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs
index 9cc53fddc..194e4606d 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using MediaBrowser.Controller.LiveTv;
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs
index 330e881ef..7ebb043d8 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Collections.Concurrent;
diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
index e9d3105bf..89b81fd96 100644
--- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
+++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Collections.Concurrent;
@@ -636,7 +635,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
ListingsProviderInfo providerInfo)
{
// Schedules direct requires that the client support compression and will return a 400 response without it
- options.DecompressionMethod = CompressionMethod.Deflate;
+ options.DecompressionMethod = CompressionMethods.Deflate;
try
{
@@ -666,7 +665,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
ListingsProviderInfo providerInfo)
{
// Schedules direct requires that the client support compression and will return a 400 response without it
- options.DecompressionMethod = CompressionMethod.Deflate;
+ options.DecompressionMethod = CompressionMethods.Deflate;
try
{
diff --git a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs
index c159b60a9..07f8539c5 100644
--- a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs
+++ b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Collections.Generic;
@@ -84,7 +83,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
{
CancellationToken = cancellationToken,
Url = path,
- DecompressionMethod = CompressionMethod.Gzip,
+ DecompressionMethod = CompressionMethods.Gzip,
},
HttpMethod.Get).ConfigureAwait(false))
using (var stream = res.Content)
diff --git a/Emby.Server.Implementations/LiveTv/LiveTvConfigurationFactory.cs b/Emby.Server.Implementations/LiveTv/LiveTvConfigurationFactory.cs
index 222fed9d9..ba916af38 100644
--- a/Emby.Server.Implementations/LiveTv/LiveTvConfigurationFactory.cs
+++ b/Emby.Server.Implementations/LiveTv/LiveTvConfigurationFactory.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System.Collections.Generic;
using MediaBrowser.Common.Configuration;
diff --git a/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs b/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs
index 14b627f82..a59c1090e 100644
--- a/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs
+++ b/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Globalization;
@@ -23,9 +22,12 @@ namespace Emby.Server.Implementations.LiveTv
{
public class LiveTvDtoService
{
+ private const string InternalVersionNumber = "4";
+
+ private const string ServiceName = "Emby";
+
private readonly ILogger _logger;
private readonly IImageProcessor _imageProcessor;
-
private readonly IDtoService _dtoService;
private readonly IApplicationHost _appHost;
private readonly ILibraryManager _libraryManager;
@@ -33,13 +35,13 @@ namespace Emby.Server.Implementations.LiveTv
public LiveTvDtoService(
IDtoService dtoService,
IImageProcessor imageProcessor,
- ILoggerFactory loggerFactory,
+ ILogger<LiveTvDtoService> logger,
IApplicationHost appHost,
ILibraryManager libraryManager)
{
_dtoService = dtoService;
_imageProcessor = imageProcessor;
- _logger = loggerFactory.CreateLogger(nameof(LiveTvDtoService));
+ _logger = logger;
_appHost = appHost;
_libraryManager = libraryManager;
}
@@ -162,7 +164,6 @@ namespace Emby.Server.Implementations.LiveTv
Limit = 1,
ImageTypes = new ImageType[] { ImageType.Thumb },
DtoOptions = new DtoOptions(false)
-
}).FirstOrDefault();
if (librarySeries != null)
@@ -180,6 +181,7 @@ namespace Emby.Server.Implementations.LiveTv
_logger.LogError(ex, "Error");
}
}
+
image = librarySeries.GetImageInfo(ImageType.Backdrop, 0);
if (image != null)
{
@@ -200,13 +202,12 @@ namespace Emby.Server.Implementations.LiveTv
var program = _libraryManager.GetItemList(new InternalItemsQuery
{
- IncludeItemTypes = new string[] { typeof(LiveTvProgram).Name },
+ IncludeItemTypes = new string[] { nameof(LiveTvProgram) },
ExternalSeriesId = programSeriesId,
Limit = 1,
ImageTypes = new ImageType[] { ImageType.Primary },
DtoOptions = new DtoOptions(false),
Name = string.IsNullOrEmpty(programSeriesId) ? seriesName : null
-
}).FirstOrDefault();
if (program != null)
@@ -233,9 +234,10 @@ namespace Emby.Server.Implementations.LiveTv
try
{
dto.ParentBackdropImageTags = new string[]
- {
+ {
_imageProcessor.GetImageCacheTag(program, image)
- };
+ };
+
dto.ParentBackdropItemId = program.Id.ToString("N", CultureInfo.InvariantCulture);
}
catch (Exception ex)
@@ -256,7 +258,6 @@ namespace Emby.Server.Implementations.LiveTv
Limit = 1,
ImageTypes = new ImageType[] { ImageType.Thumb },
DtoOptions = new DtoOptions(false)
-
}).FirstOrDefault();
if (librarySeries != null)
@@ -274,6 +275,7 @@ namespace Emby.Server.Implementations.LiveTv
_logger.LogError(ex, "Error");
}
}
+
image = librarySeries.GetImageInfo(ImageType.Backdrop, 0);
if (image != null)
{
@@ -299,7 +301,6 @@ namespace Emby.Server.Implementations.LiveTv
Limit = 1,
ImageTypes = new ImageType[] { ImageType.Primary },
DtoOptions = new DtoOptions(false)
-
}).FirstOrDefault();
if (program == null)
@@ -312,7 +313,6 @@ namespace Emby.Server.Implementations.LiveTv
ImageTypes = new ImageType[] { ImageType.Primary },
DtoOptions = new DtoOptions(false),
Name = string.IsNullOrEmpty(programSeriesId) ? seriesName : null
-
}).FirstOrDefault();
}
@@ -397,8 +397,6 @@ namespace Emby.Server.Implementations.LiveTv
return null;
}
- private const string InternalVersionNumber = "4";
-
public Guid GetInternalChannelId(string serviceName, string externalId)
{
var name = serviceName + externalId + InternalVersionNumber;
@@ -406,7 +404,6 @@ namespace Emby.Server.Implementations.LiveTv
return _libraryManager.GetNewItemId(name.ToLowerInvariant(), typeof(LiveTvChannel));
}
- private const string ServiceName = "Emby";
public string GetInternalTimerId(string externalId)
{
var name = ServiceName + externalId + InternalVersionNumber;
diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
index f20f6140e..1b10f2d27 100644
--- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
+++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Collections.Generic;
@@ -42,33 +41,32 @@ namespace Emby.Server.Implementations.LiveTv
/// </summary>
public class LiveTvManager : ILiveTvManager, IDisposable
{
+ private const string ExternalServiceTag = "ExternalServiceId";
+
+ private const string EtagKey = "ProgramEtag";
+
private readonly IServerConfigurationManager _config;
private readonly ILogger _logger;
private readonly IItemRepository _itemRepo;
private readonly IUserManager _userManager;
+ private readonly IDtoService _dtoService;
private readonly IUserDataManager _userDataManager;
private readonly ILibraryManager _libraryManager;
private readonly ITaskManager _taskManager;
- private readonly IJsonSerializer _jsonSerializer;
- private readonly Func<IChannelManager> _channelManager;
-
- private readonly IDtoService _dtoService;
private readonly ILocalizationManager _localization;
-
+ private readonly IJsonSerializer _jsonSerializer;
+ private readonly IFileSystem _fileSystem;
+ private readonly IChannelManager _channelManager;
private readonly LiveTvDtoService _tvDtoService;
private ILiveTvService[] _services = Array.Empty<ILiveTvService>();
-
private ITunerHost[] _tunerHosts = Array.Empty<ITunerHost>();
private IListingsProvider[] _listingProviders = Array.Empty<IListingsProvider>();
- private readonly IFileSystem _fileSystem;
public LiveTvManager(
- IServerApplicationHost appHost,
IServerConfigurationManager config,
- ILoggerFactory loggerFactory,
+ ILogger<LiveTvManager> logger,
IItemRepository itemRepo,
- IImageProcessor imageProcessor,
IUserDataManager userDataManager,
IDtoService dtoService,
IUserManager userManager,
@@ -77,10 +75,11 @@ namespace Emby.Server.Implementations.LiveTv
ILocalizationManager localization,
IJsonSerializer jsonSerializer,
IFileSystem fileSystem,
- Func<IChannelManager> channelManager)
+ IChannelManager channelManager,
+ LiveTvDtoService liveTvDtoService)
{
_config = config;
- _logger = loggerFactory.CreateLogger(nameof(LiveTvManager));
+ _logger = logger;
_itemRepo = itemRepo;
_userManager = userManager;
_libraryManager = libraryManager;
@@ -91,8 +90,7 @@ namespace Emby.Server.Implementations.LiveTv
_dtoService = dtoService;
_userDataManager = userDataManager;
_channelManager = channelManager;
-
- _tvDtoService = new LiveTvDtoService(dtoService, imageProcessor, loggerFactory, appHost, _libraryManager);
+ _tvDtoService = liveTvDtoService;
}
public event EventHandler<GenericEventArgs<TimerEventInfo>> SeriesTimerCancelled;
@@ -179,7 +177,6 @@ namespace Emby.Server.Implementations.LiveTv
{
Name = i.Name,
Id = i.Type
-
}).ToList();
}
@@ -262,6 +259,7 @@ namespace Emby.Server.Implementations.LiveTv
var endTime = DateTime.UtcNow;
_logger.LogInformation("Live stream opened after {0}ms", (endTime - startTime).TotalMilliseconds);
}
+
info.RequiresClosing = true;
var idPrefix = service.GetType().FullName.GetMD5().ToString("N", CultureInfo.InvariantCulture) + "_";
@@ -363,30 +361,37 @@ namespace Emby.Server.Implementations.LiveTv
{
stream.BitRate = null;
}
+
if (stream.Channels.HasValue && stream.Channels <= 0)
{
stream.Channels = null;
}
+
if (stream.AverageFrameRate.HasValue && stream.AverageFrameRate <= 0)
{
stream.AverageFrameRate = null;
}
+
if (stream.RealFrameRate.HasValue && stream.RealFrameRate <= 0)
{
stream.RealFrameRate = null;
}
+
if (stream.Width.HasValue && stream.Width <= 0)
{
stream.Width = null;
}
+
if (stream.Height.HasValue && stream.Height <= 0)
{
stream.Height = null;
}
+
if (stream.SampleRate.HasValue && stream.SampleRate <= 0)
{
stream.SampleRate = null;
}
+
if (stream.Level.HasValue && stream.Level <= 0)
{
stream.Level = null;
@@ -428,7 +433,6 @@ namespace Emby.Server.Implementations.LiveTv
}
}
- private const string ExternalServiceTag = "ExternalServiceId";
private LiveTvChannel GetChannel(ChannelInfo channelInfo, string serviceName, BaseItem parentFolder, CancellationToken cancellationToken)
{
var parentFolderId = parentFolder.Id;
@@ -457,6 +461,7 @@ namespace Emby.Server.Implementations.LiveTv
{
isNew = true;
}
+
item.Tags = channelInfo.Tags;
}
@@ -464,6 +469,7 @@ namespace Emby.Server.Implementations.LiveTv
{
isNew = true;
}
+
item.ParentId = parentFolderId;
item.ChannelType = channelInfo.ChannelType;
@@ -473,24 +479,28 @@ namespace Emby.Server.Implementations.LiveTv
{
forceUpdate = true;
}
+
item.SetProviderId(ExternalServiceTag, serviceName);
if (!string.Equals(channelInfo.Id, item.ExternalId, StringComparison.Ordinal))
{
forceUpdate = true;
}
+
item.ExternalId = channelInfo.Id;
if (!string.Equals(channelInfo.Number, item.Number, StringComparison.Ordinal))
{
forceUpdate = true;
}
+
item.Number = channelInfo.Number;
if (!string.Equals(channelInfo.Name, item.Name, StringComparison.Ordinal))
{
forceUpdate = true;
}
+
item.Name = channelInfo.Name;
if (!item.HasImage(ImageType.Primary))
@@ -519,8 +529,6 @@ namespace Emby.Server.Implementations.LiveTv
return item;
}
- private const string EtagKey = "ProgramEtag";
-
private Tuple<LiveTvProgram, bool, bool> GetProgram(ProgramInfo info, Dictionary<Guid, LiveTvProgram> allExistingPrograms, LiveTvChannel channel, ChannelType channelType, string serviceName, CancellationToken cancellationToken)
{
var id = _tvDtoService.GetInternalProgramId(info.Id);
@@ -2483,7 +2491,7 @@ namespace Emby.Server.Implementations.LiveTv
.OrderBy(i => i.SortName)
.ToList();
- folders.AddRange(_channelManager().GetChannelsInternal(new MediaBrowser.Model.Channels.ChannelQuery
+ folders.AddRange(_channelManager.GetChannelsInternal(new MediaBrowser.Model.Channels.ChannelQuery
{
UserId = user.Id,
IsRecordingsFolder = true,
diff --git a/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs b/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs
index 33887bbfd..7f63991d0 100644
--- a/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs
+++ b/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs
index 419ec3635..80ee1ee33 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Collections.Concurrent;
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
index a2d972d19..25b2c674c 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs
index 56864ab11..57c5b7500 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Buffers;
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs
index 77669da39..d89a816b3 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Collections.Generic;
@@ -8,8 +7,8 @@ using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
-using MediaBrowser.Common.Net;
using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.Net;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Dto;
@@ -122,7 +121,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
//OpenedMediaSource.Path = tempFile;
//OpenedMediaSource.ReadAtNativeFramerate = true;
- MediaSource.Path = _appHost.GetLocalApiUrl("127.0.0.1") + "/LiveTv/LiveStreamFiles/" + UniqueId + "/stream.ts";
+ MediaSource.Path = _appHost.GetLocalApiUrl("127.0.0.1", true) + "/LiveTv/LiveStreamFiles/" + UniqueId + "/stream.ts";
MediaSource.Protocol = MediaProtocol.Http;
//OpenedMediaSource.SupportsDirectPlay = false;
//OpenedMediaSource.SupportsDirectStream = true;
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs
index 5354489f9..4e4f1d7f6 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Collections.Generic;
@@ -8,8 +7,8 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using MediaBrowser.Controller.Library;
using MediaBrowser.Common.Configuration;
+using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.LiveTv;
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
index 46c77e7b0..f5dda79db 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
index 511af150b..59451fccd 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs
index 861518387..0e600202a 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Collections.Generic;
@@ -60,7 +59,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
Url = url,
CancellationToken = CancellationToken.None,
BufferContent = false,
- DecompressionMethod = CompressionMethod.None
+ DecompressionMethod = CompressionMethods.None
};
foreach (var header in mediaSource.RequiredHttpHeaders)
@@ -107,7 +106,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
//OpenedMediaSource.Path = tempFile;
//OpenedMediaSource.ReadAtNativeFramerate = true;
- MediaSource.Path = _appHost.GetLocalApiUrl("127.0.0.1") + "/LiveTv/LiveStreamFiles/" + UniqueId + "/stream.ts";
+ MediaSource.Path = _appHost.GetLocalApiUrl("127.0.0.1", true) + "/LiveTv/LiveStreamFiles/" + UniqueId + "/stream.ts";
MediaSource.Protocol = MediaProtocol.Http;
//OpenedMediaSource.Path = TempFilePath;
diff --git a/Emby.Server.Implementations/Localization/Core/af.json b/Emby.Server.Implementations/Localization/Core/af.json
index dcec26801..1363eaf85 100644
--- a/Emby.Server.Implementations/Localization/Core/af.json
+++ b/Emby.Server.Implementations/Localization/Core/af.json
@@ -41,7 +41,6 @@
"User": "Gebruiker",
"TvShows": "TV Programme",
"System": "Stelsel",
- "SubtitlesDownloadedForItem": "Ondertitels afgelaai vir {0}",
"SubtitleDownloadFailureFromForItem": "Ondertitels het misluk om af te laai van {0} vir {1}",
"StartupEmbyServerIsLoading": "Jellyfin Bediener is besig om te laai. Probeer weer in 'n kort tyd.",
"ServerNameNeedsToBeRestarted": "{0} moet herbegin word",
diff --git a/Emby.Server.Implementations/Localization/Core/ar.json b/Emby.Server.Implementations/Localization/Core/ar.json
index 24da1aa56..f313039a6 100644
--- a/Emby.Server.Implementations/Localization/Core/ar.json
+++ b/Emby.Server.Implementations/Localization/Core/ar.json
@@ -1,13 +1,13 @@
{
"Albums": "ألبومات",
"AppDeviceValues": "تطبيق: {0}, جهاز: {1}",
- "Application": "التطبيق",
+ "Application": "تطبيق",
"Artists": "الفنانين",
"AuthenticationSucceededWithUserName": "{0} سجل الدخول بنجاح",
- "Books": "كتب",
+ "Books": "الكتب",
"CameraImageUploadedFrom": "صورة كاميرا جديدة تم رفعها من {0}",
"Channels": "القنوات",
- "ChapterNameValue": "فصل {0}",
+ "ChapterNameValue": "الفصل {0}",
"Collections": "مجموعات",
"DeviceOfflineWithName": "قُطِع الاتصال بـ{0}",
"DeviceOnlineWithName": "{0} متصل",
@@ -51,8 +51,8 @@
"NotificationOptionAudioPlaybackStopped": "تم إيقاف تشغيل المقطع الصوتي",
"NotificationOptionCameraImageUploaded": "تم رفع صورة الكاميرا",
"NotificationOptionInstallationFailed": "فشل في التثبيت",
- "NotificationOptionNewLibraryContent": "أُضِيفَ محتوى جديد",
- "NotificationOptionPluginError": "فشل في الـPlugin",
+ "NotificationOptionNewLibraryContent": "تم إضافة محتوى جديد",
+ "NotificationOptionPluginError": "فشل في البرنامج المضاف",
"NotificationOptionPluginInstalled": "تم تثبيت الملحق",
"NotificationOptionPluginUninstalled": "تمت إزالة الملحق",
"NotificationOptionPluginUpdateInstalled": "تم تثبيت تحديثات الملحق",
@@ -76,7 +76,6 @@
"StartupEmbyServerIsLoading": "سيرفر Jellyfin قيد التشغيل . الرجاء المحاولة بعد قليل.",
"SubtitleDownloadFailureForItem": "عملية إنزال الترجمة فشلت لـ{0}",
"SubtitleDownloadFailureFromForItem": "الترجمات فشلت في التحميل من {0} الى {1}",
- "SubtitlesDownloadedForItem": "تم تحميل الترجمات الى {0}",
"Sync": "مزامنة",
"System": "النظام",
"TvShows": "البرامج التلفزيونية",
@@ -91,7 +90,29 @@
"UserPolicyUpdatedWithName": "تم تحديث سياسة المستخدم {0}",
"UserStartedPlayingItemWithValues": "قام {0} ببدء تشغيل {1} على {2}",
"UserStoppedPlayingItemWithValues": "قام {0} بإيقاف تشغيل {1} على {2}",
- "ValueHasBeenAddedToLibrary": "{0} تم اضافتها الى مكتبة الوسائط",
- "ValueSpecialEpisodeName": "مميز - {0}",
- "VersionNumber": "الإصدار رقم {0}"
+ "ValueHasBeenAddedToLibrary": "تمت اضافت {0} إلى مكتبة الوسائط",
+ "ValueSpecialEpisodeName": "خاص - {0}",
+ "VersionNumber": "النسخة {0}",
+ "TaskCleanCacheDescription": "يحذف ملفات ذاكرة التخزين المؤقت التي لم يعد النظام بحاجة إليها.",
+ "TaskCleanCache": "احذف مجلد ذاكرة التخزين المؤقت",
+ "TasksChannelsCategory": "قنوات الإنترنت",
+ "TasksLibraryCategory": "مكتبة",
+ "TasksMaintenanceCategory": "صيانة",
+ "TaskRefreshLibraryDescription": "يقوم بفصح مكتبة الوسائط الخاصة بك بحثًا عن ملفات جديدة وتحديث البيانات الوصفية.",
+ "TaskRefreshLibrary": "افحص مكتبة الوسائط",
+ "TaskRefreshChapterImagesDescription": "إنشاء صور مصغرة لمقاطع الفيديو ذات فصول.",
+ "TaskRefreshChapterImages": "استخراج صور الفصل",
+ "TasksApplicationCategory": "تطبيق",
+ "TaskDownloadMissingSubtitlesDescription": "ابحث في الإنترنت على الترجمات المفقودة إستنادا على الميتاداتا.",
+ "TaskDownloadMissingSubtitles": "تحميل الترجمات المفقودة",
+ "TaskRefreshChannelsDescription": "تحديث معلومات قنوات الإنترنت.",
+ "TaskRefreshChannels": "إعادة تحديث القنوات",
+ "TaskCleanTranscodeDescription": "حذف ملفات الترميز الأقدم من يوم واحد.",
+ "TaskCleanTranscode": "حذف سجلات الترميز",
+ "TaskUpdatePluginsDescription": "تحميل وتثبيت الإضافات التي تم تفعيل التحديث التلقائي لها.",
+ "TaskUpdatePlugins": "تحديث الإضافات",
+ "TaskRefreshPeopleDescription": "تحديث البيانات الوصفية للممثلين والمخرجين في مكتبة الوسائط الخاصة بك.",
+ "TaskRefreshPeople": "إعادة تحميل الأشخاص",
+ "TaskCleanLogsDescription": "حذف السجلات الأقدم من {0} يوم.",
+ "TaskCleanLogs": "حذف دليل السجل"
}
diff --git a/Emby.Server.Implementations/Localization/Core/bg-BG.json b/Emby.Server.Implementations/Localization/Core/bg-BG.json
index 9441da591..3fc7c7dc0 100644
--- a/Emby.Server.Implementations/Localization/Core/bg-BG.json
+++ b/Emby.Server.Implementations/Localization/Core/bg-BG.json
@@ -1,8 +1,8 @@
{
"Albums": "Албуми",
- "AppDeviceValues": "Програма: {0}, устройство: {1}",
+ "AppDeviceValues": "Програма: {0}, Устройство: {1}",
"Application": "Програма",
- "Artists": "Изпълнители",
+ "Artists": "Артисти",
"AuthenticationSucceededWithUserName": "{0} се удостовери успешно",
"Books": "Книги",
"CameraImageUploadedFrom": "Нова снимка от камера беше качена от {0}",
@@ -31,20 +31,20 @@
"ItemAddedWithName": "{0} е добавено към библиотеката",
"ItemRemovedWithName": "{0} е премахнато от библиотеката",
"LabelIpAddressValue": "ИП адрес: {0}",
- "LabelRunningTimeValue": "",
+ "LabelRunningTimeValue": "Стартирано от: {0}",
"Latest": "Последни",
"MessageApplicationUpdated": "Сървърът е обновен",
"MessageApplicationUpdatedTo": "Сървърът е обновен до {0}",
- "MessageNamedServerConfigurationUpdatedWithValue": "",
- "MessageServerConfigurationUpdated": "",
+ "MessageNamedServerConfigurationUpdatedWithValue": "Секцията {0} от сървърната конфигурация се актуализира",
+ "MessageServerConfigurationUpdated": "Конфигурацията на сървъра се актуализира",
"MixedContent": "Смесено съдържание",
"Movies": "Филми",
"Music": "Музика",
"MusicVideos": "Музикални клипове",
- "NameInstallFailed": "",
+ "NameInstallFailed": "{0} не можа да се инсталира",
"NameSeasonNumber": "Сезон {0}",
"NameSeasonUnknown": "Неразпознат сезон",
- "NewVersionIsAvailable": "",
+ "NewVersionIsAvailable": "Нова версия на Jellyfin сървъра е достъпна за сваляне.",
"NotificationOptionApplicationUpdateAvailable": "Налично е обновление на програмата",
"NotificationOptionApplicationUpdateInstalled": "Обновлението на програмата е инсталирано",
"NotificationOptionAudioPlayback": "Възпроизвеждането на звук започна",
@@ -58,7 +58,7 @@
"NotificationOptionPluginUpdateInstalled": "Обновлението на приставката е инсталирано",
"NotificationOptionServerRestartRequired": "Нужно е повторно пускане на сървъра",
"NotificationOptionTaskFailed": "Грешка в планирана задача",
- "NotificationOptionUserLockedOut": "",
+ "NotificationOptionUserLockedOut": "Потребителя е заключен",
"NotificationOptionVideoPlayback": "Възпроизвеждането на видео започна",
"NotificationOptionVideoPlaybackStopped": "Възпроизвеждането на видео е спряно",
"Photos": "Снимки",
@@ -70,28 +70,49 @@
"ProviderValue": "Доставчик: {0}",
"ScheduledTaskFailedWithName": "{0} се провали",
"ScheduledTaskStartedWithName": "{0} започна",
- "ServerNameNeedsToBeRestarted": "",
+ "ServerNameNeedsToBeRestarted": "{0} е нужно да се рестартира",
"Shows": "Сериали",
"Songs": "Песни",
"StartupEmbyServerIsLoading": "Сървърът зарежда. Моля, опитайте отново след малко.",
"SubtitleDownloadFailureForItem": "Неуспешно изтегляне на субтитри за {0}",
- "SubtitleDownloadFailureFromForItem": "",
- "SubtitlesDownloadedForItem": "Изтеглени са субтитри за {0}",
+ "SubtitleDownloadFailureFromForItem": "Поднадписите за {1} от {0} не можаха да се изтеглят",
"Sync": "Синхронизиране",
"System": "Система",
"TvShows": "Телевизионни сериали",
"User": "Потребител",
"UserCreatedWithName": "Потребителят {0} е създаден",
"UserDeletedWithName": "Потребителят {0} е изтрит",
- "UserDownloadingItemWithValues": "",
- "UserLockedOutWithName": "",
+ "UserDownloadingItemWithValues": "{0} изтегля {1}",
+ "UserLockedOutWithName": "Потребител {0} се заключи",
"UserOfflineFromDevice": "{0} се разкачи от {1}",
"UserOnlineFromDevice": "{0} е на линия от {1}",
"UserPasswordChangedWithName": "Паролата на потребителя {0} е променена",
- "UserPolicyUpdatedWithName": "",
+ "UserPolicyUpdatedWithName": "Потребителската политика за {0} се актуализира",
"UserStartedPlayingItemWithValues": "{0} пусна {1}",
"UserStoppedPlayingItemWithValues": "{0} спря {1}",
- "ValueHasBeenAddedToLibrary": "",
+ "ValueHasBeenAddedToLibrary": "{0} беше добавен във Вашата библиотека",
"ValueSpecialEpisodeName": "Специални - {0}",
- "VersionNumber": "Версия {0}"
+ "VersionNumber": "Версия {0}",
+ "TaskDownloadMissingSubtitlesDescription": "Търси Интернет за липсващи поднадписи, на база конфигурацията за мета-данни.",
+ "TaskDownloadMissingSubtitles": "Изтегляне на липсващи поднадписи",
+ "TaskRefreshChannelsDescription": "Обновява информацията за интернет канала.",
+ "TaskRefreshChannels": "Обновяване на Канали",
+ "TaskCleanTranscodeDescription": "Изтрива прекодирани файлове по-стари от един ден.",
+ "TaskCleanTranscode": "Изчиства директорията за прекодиране",
+ "TaskUpdatePluginsDescription": "Изтегля и инсталира актуализации за добавките, които са настроени за автоматична актуализация.",
+ "TaskUpdatePlugins": "Актуализира добавките",
+ "TaskRefreshPeopleDescription": "Актуализира мета-данните за артистите и режисьорите за Вашата медийна библиотека.",
+ "TaskRefreshPeople": "Обновяване на участниците",
+ "TaskCleanLogsDescription": "Изтрива лог файлове по-стари от {0} дни.",
+ "TaskCleanLogs": "Изчисти директорията с логове",
+ "TaskRefreshLibraryDescription": "Сканира Вашата библиотека с медия за нови файлове и обновява мета-данните.",
+ "TaskRefreshLibrary": "Сканиране на библиотеката с медия",
+ "TaskRefreshChapterImagesDescription": "Създава иконки за видеа, които имат епизоди.",
+ "TaskRefreshChapterImages": "Извличане на изображения за епизода",
+ "TaskCleanCacheDescription": "Изтриване на ненужните от системата файлове.",
+ "TaskCleanCache": "Изчистване на Кеш-директорията",
+ "TasksChannelsCategory": "Интернет Канали",
+ "TasksApplicationCategory": "Приложение",
+ "TasksLibraryCategory": "Библиотека",
+ "TasksMaintenanceCategory": "Поддръжка"
}
diff --git a/Emby.Server.Implementations/Localization/Core/bn.json b/Emby.Server.Implementations/Localization/Core/bn.json
index a7219a725..ef7792356 100644
--- a/Emby.Server.Implementations/Localization/Core/bn.json
+++ b/Emby.Server.Implementations/Localization/Core/bn.json
@@ -38,7 +38,6 @@
"TvShows": "টিভি শোগুলো",
"System": "সিস্টেম",
"Sync": "সিংক",
- "SubtitlesDownloadedForItem": "{0} এর জন্য সাবটাইটেল ডাউনলোড করা হয়েছে",
"SubtitleDownloadFailureFromForItem": "{2} থেকে {1} এর জন্য সাবটাইটেল ডাউনলোড ব্যর্থ",
"StartupEmbyServerIsLoading": "জেলিফিন সার্ভার লোড হচ্ছে। দয়া করে একটু পরে আবার চেষ্টা করুন।",
"Songs": "গানগুলো",
diff --git a/Emby.Server.Implementations/Localization/Core/ca.json b/Emby.Server.Implementations/Localization/Core/ca.json
index 44e7cf0ce..7464ac1c0 100644
--- a/Emby.Server.Implementations/Localization/Core/ca.json
+++ b/Emby.Server.Implementations/Localization/Core/ca.json
@@ -3,19 +3,19 @@
"AppDeviceValues": "Aplicació: {0}, Dispositiu: {1}",
"Application": "Aplicació",
"Artists": "Artistes",
- "AuthenticationSucceededWithUserName": "{0} s'ha autentificat correctament",
+ "AuthenticationSucceededWithUserName": "{0} s'ha autenticat correctament",
"Books": "Llibres",
- "CameraImageUploadedFrom": "Una nova imatge de la càmera ha sigut pujada des de {0}",
+ "CameraImageUploadedFrom": "Una nova imatge de la càmera ha estat pujada des de {0}",
"Channels": "Canals",
- "ChapterNameValue": "Episodi {0}",
+ "ChapterNameValue": "Capítol {0}",
"Collections": "Col·leccions",
"DeviceOfflineWithName": "{0} s'ha desconnectat",
"DeviceOnlineWithName": "{0} està connectat",
"FailedLoginAttemptWithUserName": "Intent de connexió fallit des de {0}",
"Favorites": "Preferits",
- "Folders": "Directoris",
+ "Folders": "Carpetes",
"Genres": "Gèneres",
- "HeaderAlbumArtists": "Artistes dels Àlbums",
+ "HeaderAlbumArtists": "Artistes del Àlbum",
"HeaderCameraUploads": "Pujades de Càmera",
"HeaderContinueWatching": "Continua Veient",
"HeaderFavoriteAlbums": "Àlbums Preferits",
@@ -76,7 +76,6 @@
"StartupEmbyServerIsLoading": "El Servidor d'Jellyfin est&agrave; carregant. Si et plau, prova de nou en breus.",
"SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}",
"SubtitleDownloadFailureFromForItem": "Els subtítols no s'han pogut baixar de {0} per {1}",
- "SubtitlesDownloadedForItem": "Subtítols descarregats per a {0}",
"Sync": "Sincronitzar",
"System": "System",
"TvShows": "Espectacles de TV",
diff --git a/Emby.Server.Implementations/Localization/Core/cs.json b/Emby.Server.Implementations/Localization/Core/cs.json
index 86fbac380..992bb9df3 100644
--- a/Emby.Server.Implementations/Localization/Core/cs.json
+++ b/Emby.Server.Implementations/Localization/Core/cs.json
@@ -5,7 +5,7 @@
"Artists": "Umělci",
"AuthenticationSucceededWithUserName": "{0} úspěšně ověřen",
"Books": "Knihy",
- "CameraImageUploadedFrom": "Z {0} byla nahrána nová fotografie",
+ "CameraImageUploadedFrom": "Z {0} byla nahrána nová fotografie z fotoaparátu",
"Channels": "Kanály",
"ChapterNameValue": "Kapitola {0}",
"Collections": "Kolekce",
@@ -76,7 +76,6 @@
"StartupEmbyServerIsLoading": "Jellyfin Server je spouštěn. Zkuste to prosím v brzké době znovu.",
"SubtitleDownloadFailureForItem": "Stahování titulků selhalo pro {0}",
"SubtitleDownloadFailureFromForItem": "Stažení titulků pro {1} z {0} selhalo",
- "SubtitlesDownloadedForItem": "Staženy titulky pro {0}",
"Sync": "Synchronizace",
"System": "Systém",
"TvShows": "TV seriály",
@@ -93,5 +92,27 @@
"UserStoppedPlayingItemWithValues": "{0} zastavil přehrávání {1}",
"ValueHasBeenAddedToLibrary": "{0} byl přidán do vaší knihovny médií",
"ValueSpecialEpisodeName": "Speciál - {0}",
- "VersionNumber": "Verze {0}"
+ "VersionNumber": "Verze {0}",
+ "TaskDownloadMissingSubtitlesDescription": "Vyhledá na internetu chybějící titulky na základě nastavení metadat.",
+ "TaskDownloadMissingSubtitles": "Stáhnout chybějící titulky",
+ "TaskRefreshChannelsDescription": "Obnoví informace o internetových kanálech.",
+ "TaskRefreshChannels": "Obnovit kanály",
+ "TaskCleanTranscodeDescription": "Odstraní více než 1 den staré transkódované soubory.",
+ "TaskCleanTranscode": "Vyčistit adresář s transkódovaným obsahem",
+ "TaskUpdatePluginsDescription": "Stáhne a nainstaluje aktualizace zásuvných modulů, které mají nastavenou automatickou aktualizaci.",
+ "TaskUpdatePlugins": "Aktualizovat zásuvné moduly",
+ "TaskRefreshPeopleDescription": "Aktualizuje metadata umělců a režisérů ve Vaší knihovně médií.",
+ "TaskRefreshPeople": "Obnovit umělce",
+ "TaskCleanLogsDescription": "Odstraní soubory protokolu, které jsou starší více než {0} dní.",
+ "TaskCleanLogs": "Vyčistit adresář se souborem protokolu",
+ "TaskRefreshLibraryDescription": "Prohledá Vaši knihovnu médií zda neobsahuje nové soubory a obnoví metadatada.",
+ "TaskRefreshLibrary": "Prohledat knihovnu médií",
+ "TaskRefreshChapterImagesDescription": "Vytvoří náhledy videí, které obsahují kapitoly.",
+ "TaskRefreshChapterImages": "Extrahovat obrázky kapitol",
+ "TaskCleanCacheDescription": "Odstraní soubory mezipaměti, které systém již nebude potřebovat.",
+ "TaskCleanCache": "Vyčistit složku s mezipamětí",
+ "TasksChannelsCategory": "Internetové kanály",
+ "TasksApplicationCategory": "Aplikace",
+ "TasksLibraryCategory": "Knihovna",
+ "TasksMaintenanceCategory": "Údržba"
}
diff --git a/Emby.Server.Implementations/Localization/Core/da.json b/Emby.Server.Implementations/Localization/Core/da.json
index c421db87d..f5397b62c 100644
--- a/Emby.Server.Implementations/Localization/Core/da.json
+++ b/Emby.Server.Implementations/Localization/Core/da.json
@@ -1,5 +1,5 @@
{
- "Albums": "Album",
+ "Albums": "Albums",
"AppDeviceValues": "App: {0}, Enhed: {1}",
"Application": "Applikation",
"Artists": "Kunstnere",
@@ -35,8 +35,8 @@
"Latest": "Seneste",
"MessageApplicationUpdated": "Jellyfin Server er blevet opdateret",
"MessageApplicationUpdatedTo": "Jellyfin Server er blevet opdateret til {0}",
- "MessageNamedServerConfigurationUpdatedWithValue": "Serverkonfigurationsafsnit {0} er blevet opdateret",
- "MessageServerConfigurationUpdated": "Serverkonfigurationen er blevet opdateret",
+ "MessageNamedServerConfigurationUpdatedWithValue": "Server konfiguration sektion {0} er blevet opdateret",
+ "MessageServerConfigurationUpdated": "Server konfigurationen er blevet opdateret",
"MixedContent": "Blandet indhold",
"Movies": "Film",
"Music": "Musik",
@@ -76,7 +76,6 @@
"StartupEmbyServerIsLoading": "Jellyfin Server er i gang med at starte op. Prøv venligst igen om lidt.",
"SubtitleDownloadFailureForItem": "Fejlet i download af undertekster for {0}",
"SubtitleDownloadFailureFromForItem": "Undertekster kunne ikke downloades fra {0} til {1}",
- "SubtitlesDownloadedForItem": "Undertekster downloadet for {0}",
"Sync": "Synk",
"System": "System",
"TvShows": "TV serier",
@@ -93,5 +92,27 @@
"UserStoppedPlayingItemWithValues": "{0} har afsluttet afspilning af {1} på {2}",
"ValueHasBeenAddedToLibrary": "{0} er blevet tilføjet til dit mediebibliotek",
"ValueSpecialEpisodeName": "Special - {0}",
- "VersionNumber": "Version {0}"
+ "VersionNumber": "Version {0}",
+ "TaskDownloadMissingSubtitlesDescription": "Søger på internettet efter manglende undertekster baseret på metadata konfiguration.",
+ "TaskDownloadMissingSubtitles": "Download manglende undertekster",
+ "TaskUpdatePluginsDescription": "Downloader og installere opdateringer for plugins som er konfigureret til at opdatere automatisk.",
+ "TaskUpdatePlugins": "Opdater Plugins",
+ "TaskCleanLogsDescription": "Sletter log filer som er mere end {0} dage gammle.",
+ "TaskCleanLogs": "Ryd Log Mappe",
+ "TaskRefreshLibraryDescription": "Scanner dit medie bibliotek for nye filer og opdaterer metadata.",
+ "TaskRefreshLibrary": "Scan Medie Bibliotek",
+ "TaskCleanCacheDescription": "Sletter cache filer som systemet ikke har brug for længere.",
+ "TaskCleanCache": "Ryd Cache Mappe",
+ "TasksChannelsCategory": "Internet Kanaler",
+ "TasksApplicationCategory": "Applikation",
+ "TasksLibraryCategory": "Bibliotek",
+ "TasksMaintenanceCategory": "Vedligeholdelse",
+ "TaskRefreshChapterImages": "Udtræk Kapitel billeder",
+ "TaskRefreshChapterImagesDescription": "Lav miniaturebilleder for videoer der har kapitler.",
+ "TaskRefreshChannelsDescription": "Genopfrisker internet kanal information.",
+ "TaskRefreshChannels": "Genopfrisk Kanaler",
+ "TaskCleanTranscodeDescription": "Fjern transcode filer som er mere end en dag gammel.",
+ "TaskCleanTranscode": "Rengør Transcode Mappen",
+ "TaskRefreshPeople": "Genopfrisk Personer",
+ "TaskRefreshPeopleDescription": "Opdatere metadata for skuespillere og instruktører i dit bibliotek."
}
diff --git a/Emby.Server.Implementations/Localization/Core/de.json b/Emby.Server.Implementations/Localization/Core/de.json
index 69678c268..82df43be1 100644
--- a/Emby.Server.Implementations/Localization/Core/de.json
+++ b/Emby.Server.Implementations/Localization/Core/de.json
@@ -1,9 +1,9 @@
{
"Albums": "Alben",
- "AppDeviceValues": "Anw: {0}, Gerät: {1}",
- "Application": "Programm",
+ "AppDeviceValues": "App: {0}, Gerät: {1}",
+ "Application": "Anwendung",
"Artists": "Interpreten",
- "AuthenticationSucceededWithUserName": "{0} hat sich erfolgreich angemeldet",
+ "AuthenticationSucceededWithUserName": "{0} hat sich erfolgreich authentifiziert",
"Books": "Bücher",
"CameraImageUploadedFrom": "Ein neues Foto wurde von {0} hochgeladen",
"Channels": "Kanäle",
@@ -50,7 +50,7 @@
"NotificationOptionAudioPlayback": "Audiowiedergabe gestartet",
"NotificationOptionAudioPlaybackStopped": "Audiowiedergabe gestoppt",
"NotificationOptionCameraImageUploaded": "Foto hochgeladen",
- "NotificationOptionInstallationFailed": "Installationsfehler",
+ "NotificationOptionInstallationFailed": "Installation fehlgeschlagen",
"NotificationOptionNewLibraryContent": "Neuer Inhalt hinzugefügt",
"NotificationOptionPluginError": "Plugin-Fehler",
"NotificationOptionPluginInstalled": "Plugin installiert",
@@ -76,7 +76,6 @@
"StartupEmbyServerIsLoading": "Jellyfin-Server startet, bitte versuche es gleich noch einmal.",
"SubtitleDownloadFailureForItem": "Download der Untertitel fehlgeschlagen für {0}",
"SubtitleDownloadFailureFromForItem": "Untertitel von {0} für {1} konnten nicht heruntergeladen werden",
- "SubtitlesDownloadedForItem": "Untertitel heruntergeladen für {0}",
"Sync": "Synchronisation",
"System": "System",
"TvShows": "TV-Sendungen",
@@ -93,5 +92,27 @@
"UserStoppedPlayingItemWithValues": "{0} hat die Wiedergabe von {1} auf {2} beendet",
"ValueHasBeenAddedToLibrary": "{0} wurde deiner Bibliothek hinzugefügt",
"ValueSpecialEpisodeName": "Extra - {0}",
- "VersionNumber": "Version {0}"
+ "VersionNumber": "Version {0}",
+ "TaskDownloadMissingSubtitlesDescription": "Durchsucht das Internet nach fehlenden Untertiteln, basierend auf den Meta Einstellungen.",
+ "TaskDownloadMissingSubtitles": "Lade fehlende Untertitel herunter",
+ "TaskRefreshChannelsDescription": "Erneuere Internet Kanal Informationen.",
+ "TaskRefreshChannels": "Erneuere Kanäle",
+ "TaskCleanTranscodeDescription": "Löscht Transkodierdateien welche älter als ein Tag sind.",
+ "TaskCleanTranscode": "Lösche Transkodier Pfad",
+ "TaskUpdatePluginsDescription": "Lädt Updates für Plugins herunter, welche dazu eingestellt sind automatisch zu updaten und installiert sie.",
+ "TaskUpdatePlugins": "Update Plugins",
+ "TaskRefreshPeopleDescription": "Erneuert Metadaten für Schausteller und Regisseure in deinen Bibliotheken.",
+ "TaskRefreshPeople": "Erneuere Schausteller",
+ "TaskCleanLogsDescription": "Lösche Log Dateien die älter als {0} Tage sind.",
+ "TaskCleanLogs": "Lösche Log Pfad",
+ "TaskRefreshLibraryDescription": "Scanne alle Bibliotheken für hinzugefügte Datein und erneuere Metadaten.",
+ "TaskRefreshLibrary": "Scanne alle Bibliotheken",
+ "TaskRefreshChapterImagesDescription": "Kreiert Vorschaubilder für Videos welche Kapitel haben.",
+ "TaskRefreshChapterImages": "Extrahiert Kapitel-Bilder",
+ "TaskCleanCacheDescription": "Löscht Zwischenspeicherdatein die nicht länger von System gebraucht werden.",
+ "TaskCleanCache": "Leere Cache Pfad",
+ "TasksChannelsCategory": "Internet Kanäle",
+ "TasksApplicationCategory": "Anwendung",
+ "TasksLibraryCategory": "Bibliothek",
+ "TasksMaintenanceCategory": "Wartung"
}
diff --git a/Emby.Server.Implementations/Localization/Core/el.json b/Emby.Server.Implementations/Localization/Core/el.json
index 580b42330..0753ea39d 100644
--- a/Emby.Server.Implementations/Localization/Core/el.json
+++ b/Emby.Server.Implementations/Localization/Core/el.json
@@ -1,5 +1,5 @@
{
- "Albums": "Άλμπουμ",
+ "Albums": "Άλμπουμς",
"AppDeviceValues": "Εφαρμογή: {0}, Συσκευή: {1}",
"Application": "Εφαρμογή",
"Artists": "Καλλιτέχνες",
@@ -76,7 +76,6 @@
"StartupEmbyServerIsLoading": "Ο Jellyfin Server φορτώνει. Παρακαλώ δοκιμάστε σε λίγο.",
"SubtitleDownloadFailureForItem": "Οι υπότιτλοι απέτυχαν να κατέβουν για {0}",
"SubtitleDownloadFailureFromForItem": "Αποτυχίες μεταφόρτωσης υποτίτλων από {0} για {1}",
- "SubtitlesDownloadedForItem": "Οι υπότιτλοι κατέβηκαν για {0}",
"Sync": "Συγχρονισμός",
"System": "Σύστημα",
"TvShows": "Τηλεοπτικές Σειρές",
@@ -93,5 +92,27 @@
"UserStoppedPlayingItemWithValues": "{0} τελείωσε να παίζει {1} σε {2}",
"ValueHasBeenAddedToLibrary": "{0} προστέθηκαν στη βιβλιοθήκη πολυμέσων σας",
"ValueSpecialEpisodeName": "Σπέσιαλ - {0}",
- "VersionNumber": "Έκδοση {0}"
+ "VersionNumber": "Έκδοση {0}",
+ "TaskRefreshPeople": "Ανανέωση Ατόμων",
+ "TaskCleanLogsDescription": "Διαγράφει τα αρχεία καταγραφής που είναι άνω των {0} ημερών.",
+ "TaskCleanLogs": "Καθαρισμός Καταλόγου Καταγραφής",
+ "TaskRefreshLibraryDescription": "Σαρώνει την βιβλιοθήκη πολυμέσων σας για νέα αρχεία και αναζωογονεί τα μεταδεδομένα.",
+ "TaskRefreshLibrary": "Βιβλιοθήκη Σάρωσης Πολυμέσων",
+ "TaskRefreshChapterImagesDescription": "Δημιουργεί μικρογραφίες για βίντεο με κεφάλαια.",
+ "TaskRefreshChapterImages": "Εξαγωγή Εικόνων Κεφαλαίου",
+ "TaskCleanCacheDescription": "Τα διαγραμμένα αρχεία προσωρινής μνήμης που δεν χρειάζονται πλέον από το σύστημα.",
+ "TaskCleanCache": "Καθαρισμός Καταλόγου Προσωρινής Μνήμης",
+ "TasksChannelsCategory": "Κανάλια Διαδικτύου",
+ "TasksApplicationCategory": "Εφαρμογή",
+ "TasksLibraryCategory": "Βιβλιοθήκη",
+ "TasksMaintenanceCategory": "Συντήρηση",
+ "TaskDownloadMissingSubtitlesDescription": "Αναζητήσεις στο διαδίκτυο όπου λείπουν υπότιτλους με βάση τη διαμόρφωση μεταδεδομένων.",
+ "TaskDownloadMissingSubtitles": "Λήψη υπότιτλων που λείπουν",
+ "TaskRefreshChannelsDescription": "Ανανεώνει τις πληροφορίες καναλιού στο διαδικτύου.",
+ "TaskRefreshChannels": "Ανανέωση Καναλιών",
+ "TaskCleanTranscodeDescription": "Διαγράφει αρχείου διακωδικοποιητή περισσότερο από μία ημέρα.",
+ "TaskCleanTranscode": "Καθαρισμός Kαταλόγου Διακωδικοποιητή",
+ "TaskUpdatePluginsDescription": "Κατεβάζει και εγκαθιστά ενημερώσεις για τις προσθήκες που έχουν ρυθμιστεί για αυτόματη ενημέρωση.",
+ "TaskUpdatePlugins": "Ενημέρωση Προσθηκών",
+ "TaskRefreshPeopleDescription": "Ενημερώνει μεταδεδομένα για ηθοποιούς και σκηνοθέτες στην βιβλιοθήκη των πολυμέσων σας."
}
diff --git a/Emby.Server.Implementations/Localization/Core/en-GB.json b/Emby.Server.Implementations/Localization/Core/en-GB.json
index 67d4068cf..544c38cfa 100644
--- a/Emby.Server.Implementations/Localization/Core/en-GB.json
+++ b/Emby.Server.Implementations/Localization/Core/en-GB.json
@@ -76,7 +76,6 @@
"StartupEmbyServerIsLoading": "Jellyfin Server is loading. Please try again shortly.",
"SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}",
"SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
- "SubtitlesDownloadedForItem": "Subtitles downloaded for {0}",
"Sync": "Sync",
"System": "System",
"TvShows": "TV Shows",
@@ -93,5 +92,27 @@
"UserStoppedPlayingItemWithValues": "{0} has finished playing {1} on {2}",
"ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
"ValueSpecialEpisodeName": "Special - {0}",
- "VersionNumber": "Version {0}"
+ "VersionNumber": "Version {0}",
+ "TaskDownloadMissingSubtitlesDescription": "Searches the internet for missing subtitles based on metadata configuration.",
+ "TaskDownloadMissingSubtitles": "Download missing subtitles",
+ "TaskRefreshChannelsDescription": "Refreshes internet channel information.",
+ "TaskRefreshChannels": "Refresh Channels",
+ "TaskCleanTranscodeDescription": "Deletes transcode files more than one day old.",
+ "TaskCleanTranscode": "Clean Transcode Directory",
+ "TaskUpdatePluginsDescription": "Downloads and installs updates for plugins that are configured to update automatically.",
+ "TaskUpdatePlugins": "Update Plugins",
+ "TaskRefreshPeopleDescription": "Updates metadata for actors and directors in your media library.",
+ "TaskRefreshPeople": "Refresh People",
+ "TaskCleanLogsDescription": "Deletes log files that are more than {0} days old.",
+ "TaskCleanLogs": "Clean Log Directory",
+ "TaskRefreshLibraryDescription": "Scans your media library for new files and refreshes metadata.",
+ "TaskRefreshLibrary": "Scan Media Library",
+ "TaskRefreshChapterImagesDescription": "Creates thumbnails for videos that have chapters.",
+ "TaskRefreshChapterImages": "Extract Chapter Images",
+ "TaskCleanCacheDescription": "Deletes cache files no longer needed by the system.",
+ "TaskCleanCache": "Clean Cache Directory",
+ "TasksChannelsCategory": "Internet Channels",
+ "TasksApplicationCategory": "Application",
+ "TasksLibraryCategory": "Library",
+ "TasksMaintenanceCategory": "Maintenance"
}
diff --git a/Emby.Server.Implementations/Localization/Core/en-US.json b/Emby.Server.Implementations/Localization/Core/en-US.json
index aa855ed21..97a843160 100644
--- a/Emby.Server.Implementations/Localization/Core/en-US.json
+++ b/Emby.Server.Implementations/Localization/Core/en-US.json
@@ -75,7 +75,6 @@
"Songs": "Songs",
"StartupEmbyServerIsLoading": "Jellyfin Server is loading. Please try again shortly.",
"SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
- "SubtitlesDownloadedForItem": "Subtitles downloaded for {0}",
"Sync": "Sync",
"System": "System",
"TvShows": "TV Shows",
@@ -92,5 +91,27 @@
"UserStoppedPlayingItemWithValues": "{0} has finished playing {1} on {2}",
"ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
"ValueSpecialEpisodeName": "Special - {0}",
- "VersionNumber": "Version {0}"
+ "VersionNumber": "Version {0}",
+ "TasksMaintenanceCategory": "Maintenance",
+ "TasksLibraryCategory": "Library",
+ "TasksApplicationCategory": "Application",
+ "TasksChannelsCategory": "Internet Channels",
+ "TaskCleanCache": "Clean Cache Directory",
+ "TaskCleanCacheDescription": "Deletes cache files no longer needed by the system.",
+ "TaskRefreshChapterImages": "Extract Chapter Images",
+ "TaskRefreshChapterImagesDescription": "Creates thumbnails for videos that have chapters.",
+ "TaskRefreshLibrary": "Scan Media Library",
+ "TaskRefreshLibraryDescription": "Scans your media library for new files and refreshes metadata.",
+ "TaskCleanLogs": "Clean Log Directory",
+ "TaskCleanLogsDescription": "Deletes log files that are more than {0} days old.",
+ "TaskRefreshPeople": "Refresh People",
+ "TaskRefreshPeopleDescription": "Updates metadata for actors and directors in your media library.",
+ "TaskUpdatePlugins": "Update Plugins",
+ "TaskUpdatePluginsDescription": "Downloads and installs updates for plugins that are configured to update automatically.",
+ "TaskCleanTranscode": "Clean Transcode Directory",
+ "TaskCleanTranscodeDescription": "Deletes transcode files more than one day old.",
+ "TaskRefreshChannels": "Refresh Channels",
+ "TaskRefreshChannelsDescription": "Refreshes internet channel information.",
+ "TaskDownloadMissingSubtitles": "Download missing subtitles",
+ "TaskDownloadMissingSubtitlesDescription": "Searches the internet for missing subtitles based on metadata configuration."
}
diff --git a/Emby.Server.Implementations/Localization/Core/es-AR.json b/Emby.Server.Implementations/Localization/Core/es-AR.json
index dc73ba6b3..1b6c6b5ae 100644
--- a/Emby.Server.Implementations/Localization/Core/es-AR.json
+++ b/Emby.Server.Implementations/Localization/Core/es-AR.json
@@ -11,20 +11,20 @@
"Collections": "Colecciones",
"DeviceOfflineWithName": "{0} se ha desconectado",
"DeviceOnlineWithName": "{0} está conectado",
- "FailedLoginAttemptWithUserName": "Error al intentar iniciar sesión desde {0}",
+ "FailedLoginAttemptWithUserName": "Error al intentar iniciar sesión de {0}",
"Favorites": "Favoritos",
"Folders": "Carpetas",
"Genres": "Géneros",
- "HeaderAlbumArtists": "Artistas de álbumes",
+ "HeaderAlbumArtists": "Artistas de álbum",
"HeaderCameraUploads": "Subidas de cámara",
- "HeaderContinueWatching": "Continuar viendo",
+ "HeaderContinueWatching": "Seguir viendo",
"HeaderFavoriteAlbums": "Álbumes favoritos",
"HeaderFavoriteArtists": "Artistas favoritos",
"HeaderFavoriteEpisodes": "Episodios favoritos",
"HeaderFavoriteShows": "Programas favoritos",
"HeaderFavoriteSongs": "Canciones favoritas",
"HeaderLiveTV": "TV en vivo",
- "HeaderNextUp": "Continuar Viendo",
+ "HeaderNextUp": "A Continuación",
"HeaderRecordingGroups": "Grupos de grabación",
"HomeVideos": "Videos caseros",
"Inherit": "Heredar",
@@ -35,48 +35,47 @@
"Latest": "Últimos",
"MessageApplicationUpdated": "El servidor Jellyfin fue actualizado",
"MessageApplicationUpdatedTo": "Se ha actualizado el servidor Jellyfin a la versión {0}",
- "MessageNamedServerConfigurationUpdatedWithValue": "Fue actualizada la sección {0} de la configuración del servidor",
- "MessageServerConfigurationUpdated": "Fue actualizada la configuración del servidor",
- "MixedContent": "Contenido mixto",
+ "MessageNamedServerConfigurationUpdatedWithValue": "Se ha actualizado la sección {0} de la configuración del servidor",
+ "MessageServerConfigurationUpdated": "Se ha actualizado la configuración del servidor",
+ "MixedContent": "Contenido mezclado",
"Movies": "Películas",
"Music": "Música",
"MusicVideos": "Videos musicales",
- "NameInstallFailed": "{0} error de instalación",
+ "NameInstallFailed": "{0} instalación fallida",
"NameSeasonNumber": "Temporada {0}",
"NameSeasonUnknown": "Temporada desconocida",
- "NewVersionIsAvailable": "Disponible una nueva versión de Jellyfin para descargar.",
+ "NewVersionIsAvailable": "Una nueva versión del Servidor Jellyfin está disponible para descargar.",
"NotificationOptionApplicationUpdateAvailable": "Actualización de la aplicación disponible",
"NotificationOptionApplicationUpdateInstalled": "Actualización de la aplicación instalada",
"NotificationOptionAudioPlayback": "Se inició la reproducción de audio",
"NotificationOptionAudioPlaybackStopped": "Se detuvo la reproducción de audio",
- "NotificationOptionCameraImageUploaded": "Imagen de la cámara cargada",
+ "NotificationOptionCameraImageUploaded": "Imagen de la cámara subida",
"NotificationOptionInstallationFailed": "Error de instalación",
"NotificationOptionNewLibraryContent": "Nuevo contenido añadido",
- "NotificationOptionPluginError": "Error en plugin",
- "NotificationOptionPluginInstalled": "Plugin instalado",
- "NotificationOptionPluginUninstalled": "Plugin desinstalado",
- "NotificationOptionPluginUpdateInstalled": "Actualización del complemento instalada",
- "NotificationOptionServerRestartRequired": "Se requiere reinicio del servidor",
- "NotificationOptionTaskFailed": "Error de tarea programada",
+ "NotificationOptionPluginError": "Falla de complemento",
+ "NotificationOptionPluginInstalled": "Complemento instalado",
+ "NotificationOptionPluginUninstalled": "Complemento desinstalado",
+ "NotificationOptionPluginUpdateInstalled": "Actualización de complemento instalada",
+ "NotificationOptionServerRestartRequired": "Se necesita reiniciar el Servidor",
+ "NotificationOptionTaskFailed": "Falla de tarea programada",
"NotificationOptionUserLockedOut": "Usuario bloqueado",
"NotificationOptionVideoPlayback": "Se inició la reproducción de video",
"NotificationOptionVideoPlaybackStopped": "Reproducción de video detenida",
"Photos": "Fotos",
"Playlists": "Listas de reproducción",
- "Plugin": "Plugin",
+ "Plugin": "Complemento",
"PluginInstalledWithName": "{0} fue instalado",
"PluginUninstalledWithName": "{0} fue desinstalado",
"PluginUpdatedWithName": "{0} fue actualizado",
"ProviderValue": "Proveedor: {0}",
"ScheduledTaskFailedWithName": "{0} falló",
- "ScheduledTaskStartedWithName": "{0} iniciada",
+ "ScheduledTaskStartedWithName": "{0} iniciado",
"ServerNameNeedsToBeRestarted": "{0} necesita ser reiniciado",
"Shows": "Series",
"Songs": "Canciones",
- "StartupEmbyServerIsLoading": "Jellyfin Server se está cargando. Vuelve a intentarlo en breve.",
+ "StartupEmbyServerIsLoading": "El servidor Jellyfin se está cargando. Vuelve a intentarlo en breve.",
"SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}",
- "SubtitleDownloadFailureFromForItem": "Fallo de descarga de subtítulos desde {0} para {1}",
- "SubtitlesDownloadedForItem": "Descargar subtítulos para {0}",
+ "SubtitleDownloadFailureFromForItem": "Falló la descarga de subtitulos desde {0} para {1}",
"Sync": "Sincronizar",
"System": "Sistema",
"TvShows": "Series de TV",
@@ -88,10 +87,32 @@
"UserOfflineFromDevice": "{0} se ha desconectado de {1}",
"UserOnlineFromDevice": "{0} está en línea desde {1}",
"UserPasswordChangedWithName": "Se ha cambiado la contraseña para el usuario {0}",
- "UserPolicyUpdatedWithName": "Actualizada política de usuario para {0}",
+ "UserPolicyUpdatedWithName": "Las política de usuario ha sido actualizada para {0}",
"UserStartedPlayingItemWithValues": "{0} está reproduciendo {1} en {2}",
"UserStoppedPlayingItemWithValues": "{0} ha terminado de reproducir {1} en {2}",
"ValueHasBeenAddedToLibrary": "{0} ha sido añadido a tu biblioteca multimedia",
"ValueSpecialEpisodeName": "Especial - {0}",
- "VersionNumber": "Versión {0}"
+ "VersionNumber": "Versión {0}",
+ "TaskDownloadMissingSubtitlesDescription": "Busca en internet los subtítulos que falten basándose en la configuración de los metadatos.",
+ "TaskDownloadMissingSubtitles": "Descargar subtítulos extraviados",
+ "TaskRefreshChannelsDescription": "Actualizar información de canales de internet.",
+ "TaskRefreshChannels": "Actualizar canales",
+ "TaskCleanTranscodeDescription": "Eliminar archivos transcodificados con mas de un día de antigüedad.",
+ "TaskCleanTranscode": "Limpiar directorio de Transcodificado",
+ "TaskUpdatePluginsDescription": "Descargar e instalar actualizaciones para complementos que estén configurados en actualizar automáticamente.",
+ "TaskUpdatePlugins": "Actualizar complementos",
+ "TaskRefreshPeopleDescription": "Actualizar metadatos de actores y directores en su librería multimedia.",
+ "TaskRefreshPeople": "Actualizar personas",
+ "TaskCleanLogsDescription": "Eliminar archivos de registro que tengan mas de {0} días de antigüedad.",
+ "TaskCleanLogs": "Limpiar directorio de registros",
+ "TaskRefreshLibraryDescription": "Escanear su librería multimedia por nuevos archivos y refrescar metadatos.",
+ "TaskRefreshLibrary": "Escanear librería multimedia",
+ "TaskRefreshChapterImagesDescription": "Crear miniaturas de videos que tengan capítulos.",
+ "TaskRefreshChapterImages": "Extraer imágenes de capitulo",
+ "TaskCleanCacheDescription": "Eliminar archivos de cache que no se necesiten en el sistema.",
+ "TaskCleanCache": "Limpiar directorio Cache",
+ "TasksChannelsCategory": "Canales de Internet",
+ "TasksApplicationCategory": "Solicitud",
+ "TasksLibraryCategory": "Biblioteca",
+ "TasksMaintenanceCategory": "Mantenimiento"
}
diff --git a/Emby.Server.Implementations/Localization/Core/es-MX.json b/Emby.Server.Implementations/Localization/Core/es-MX.json
index 99fda7aa6..e0bbe90b3 100644
--- a/Emby.Server.Implementations/Localization/Core/es-MX.json
+++ b/Emby.Server.Implementations/Localization/Core/es-MX.json
@@ -76,7 +76,6 @@
"StartupEmbyServerIsLoading": "El servidor Jellyfin esta cargando. Por favor intente de nuevo dentro de poco.",
"SubtitleDownloadFailureForItem": "Falló la descarga de subtítulos para {0}",
"SubtitleDownloadFailureFromForItem": "Falló la descarga de subtitulos desde {0} para {1}",
- "SubtitlesDownloadedForItem": "Subtítulos descargados para {0}",
"Sync": "Sincronizar",
"System": "Sistema",
"TvShows": "Programas de TV",
@@ -93,5 +92,27 @@
"UserStoppedPlayingItemWithValues": "{0} ha terminado de reproducirse {1} en {2}",
"ValueHasBeenAddedToLibrary": "{0} se han añadido a su biblioteca de medios",
"ValueSpecialEpisodeName": "Especial - {0}",
- "VersionNumber": "Versión {0}"
+ "VersionNumber": "Versión {0}",
+ "TaskDownloadMissingSubtitlesDescription": "Buscar subtítulos de internet basado en configuración de metadatos.",
+ "TaskDownloadMissingSubtitles": "Descargar subtítulos perdidos",
+ "TaskRefreshChannelsDescription": "Refrescar información de canales de internet.",
+ "TaskRefreshChannels": "Actualizar canales",
+ "TaskCleanTranscodeDescription": "Eliminar archivos transcodificados que tengan mas de un día.",
+ "TaskCleanTranscode": "Limpiar directorio de transcodificado",
+ "TaskUpdatePluginsDescription": "Descargar y actualizar complementos que están configurados para actualizarse automáticamente.",
+ "TaskUpdatePlugins": "Actualizar complementos",
+ "TaskRefreshPeopleDescription": "Actualizar datos de actores y directores en su librería multimedia.",
+ "TaskRefreshPeople": "Refrescar persona",
+ "TaskCleanLogsDescription": "Eliminar archivos de registro con mas de {0} días.",
+ "TaskCleanLogs": "Directorio de logo limpio",
+ "TaskRefreshLibraryDescription": "Escanear su librería multimedia para nuevos archivos y refrescar metadatos.",
+ "TaskRefreshLibrary": "Escanear librería multimerdia",
+ "TaskRefreshChapterImagesDescription": "Crear miniaturas para videos con capítulos.",
+ "TaskRefreshChapterImages": "Extraer imágenes de capítulos",
+ "TaskCleanCacheDescription": "Eliminar archivos cache que ya no se necesiten por el sistema.",
+ "TaskCleanCache": "Limpiar directorio cache",
+ "TasksChannelsCategory": "Canales de Internet",
+ "TasksApplicationCategory": "Aplicación",
+ "TasksLibraryCategory": "Biblioteca",
+ "TasksMaintenanceCategory": "Mantenimiento"
}
diff --git a/Emby.Server.Implementations/Localization/Core/es.json b/Emby.Server.Implementations/Localization/Core/es.json
index 2dcc2c1c8..de1baada8 100644
--- a/Emby.Server.Implementations/Localization/Core/es.json
+++ b/Emby.Server.Implementations/Localization/Core/es.json
@@ -76,7 +76,6 @@
"StartupEmbyServerIsLoading": "Jellyfin Server se está cargando. Vuelve a intentarlo en breve.",
"SubtitleDownloadFailureForItem": "Error al descargar subtítulos para {0}",
"SubtitleDownloadFailureFromForItem": "Fallo de descarga de subtítulos desde {0} para {1}",
- "SubtitlesDownloadedForItem": "Descargar subtítulos para {0}",
"Sync": "Sincronizar",
"System": "Sistema",
"TvShows": "Programas de televisión",
@@ -93,5 +92,27 @@
"UserStoppedPlayingItemWithValues": "{0} ha terminado de reproducir {1} en {2}",
"ValueHasBeenAddedToLibrary": "{0} ha sido añadido a tu biblioteca multimedia",
"ValueSpecialEpisodeName": "Especial - {0}",
- "VersionNumber": "Versión {0}"
+ "VersionNumber": "Versión {0}",
+ "TasksMaintenanceCategory": "Mantenimiento",
+ "TasksLibraryCategory": "Librería",
+ "TasksApplicationCategory": "Aplicación",
+ "TasksChannelsCategory": "Canales de internet",
+ "TaskCleanCache": "Eliminar archivos temporales",
+ "TaskCleanCacheDescription": "Elimina los archivos temporales que ya no son necesarios para el servidor.",
+ "TaskRefreshChapterImages": "Extraer imágenes de los capítulos",
+ "TaskRefreshChapterImagesDescription": "Crea las miniaturas de los vídeos que tengan capítulos.",
+ "TaskRefreshLibrary": "Escanear la biblioteca",
+ "TaskRefreshLibraryDescription": "Añade los archivos que se hayan añadido a la biblioteca y actualiza las etiquetas de los ya presentes.",
+ "TaskCleanLogs": "Limpiar registros",
+ "TaskCleanLogsDescription": "Elimina los archivos de registro que tengan más de {0} días.",
+ "TaskRefreshPeople": "Actualizar personas",
+ "TaskRefreshPeopleDescription": "Actualiza las etiquetas de los intérpretes y directores presentes en tus bibliotecas.",
+ "TaskUpdatePlugins": "Actualizar extensiones",
+ "TaskUpdatePluginsDescription": "Actualiza las extensiones que están configuradas para actualizarse automáticamente.",
+ "TaskCleanTranscode": "Limpiar las transcodificaciones",
+ "TaskCleanTranscodeDescription": "Elimina los archivos temporales de transcodificación anteriores a un día de antigüedad.",
+ "TaskRefreshChannels": "Actualizar canales",
+ "TaskRefreshChannelsDescription": "Actualiza la información de los canales de internet.",
+ "TaskDownloadMissingSubtitles": "Descargar los subtítulos que faltan",
+ "TaskDownloadMissingSubtitlesDescription": "Busca en internet los subtítulos que falten en el contenido de tus bibliotecas, basándose en la configuración de los metadatos."
}
diff --git a/Emby.Server.Implementations/Localization/Core/es_DO.json b/Emby.Server.Implementations/Localization/Core/es_DO.json
index 1a7b57c53..0ef16542f 100644
--- a/Emby.Server.Implementations/Localization/Core/es_DO.json
+++ b/Emby.Server.Implementations/Localization/Core/es_DO.json
@@ -5,7 +5,7 @@
"Collections": "Colecciones",
"Artists": "Artistas",
"DeviceOnlineWithName": "{0} está conectado",
- "DeviceOfflineWithName": "{0} ha desconectado",
+ "DeviceOfflineWithName": "{0} se ha desconectado",
"ChapterNameValue": "Capítulo {0}",
"CameraImageUploadedFrom": "Se ha subido una nueva imagen de cámara desde {0}",
"AuthenticationSucceededWithUserName": "{0} autenticado con éxito",
diff --git a/Emby.Server.Implementations/Localization/Core/fa.json b/Emby.Server.Implementations/Localization/Core/fa.json
index faa658ed5..500c29217 100644
--- a/Emby.Server.Implementations/Localization/Core/fa.json
+++ b/Emby.Server.Implementations/Localization/Core/fa.json
@@ -1,56 +1,56 @@
{
- "Albums": "آلبوم ها",
+ "Albums": "آلبوم‌ها",
"AppDeviceValues": "برنامه: {0} ، دستگاه: {1}",
"Application": "برنامه",
"Artists": "هنرمندان",
"AuthenticationSucceededWithUserName": "{0} با موفقیت تایید اعتبار شد",
- "Books": "کتاب ها",
- "CameraImageUploadedFrom": "یک عکس جدید از دوربین ارسال شده {0}",
- "Channels": "کانال ها",
- "ChapterNameValue": "فصل {0}",
- "Collections": "کلکسیون ها",
+ "Books": "کتاب‌ها",
+ "CameraImageUploadedFrom": "یک عکس جدید از دوربین ارسال شده است {0}",
+ "Channels": "کانال‌ها",
+ "ChapterNameValue": "قسمت {0}",
+ "Collections": "مجموعه‌ها",
"DeviceOfflineWithName": "ارتباط {0} قطع شد",
- "DeviceOnlineWithName": "{0} متصل شده",
+ "DeviceOnlineWithName": "{0} متصل شد",
"FailedLoginAttemptWithUserName": "تلاش برای ورود از {0} ناموفق بود",
- "Favorites": "مورد علاقه ها",
- "Folders": "پوشه ها",
+ "Favorites": "مورد علاقه‌ها",
+ "Folders": "پوشه‌ها",
"Genres": "ژانرها",
"HeaderAlbumArtists": "هنرمندان آلبوم",
"HeaderCameraUploads": "آپلودهای دوربین",
"HeaderContinueWatching": "ادامه تماشا",
- "HeaderFavoriteAlbums": "آلبوم های مورد علاقه",
+ "HeaderFavoriteAlbums": "آلبوم‌های مورد علاقه",
"HeaderFavoriteArtists": "هنرمندان مورد علاقه",
- "HeaderFavoriteEpisodes": "قسمت های مورد علاقه",
- "HeaderFavoriteShows": "سریال های مورد علاقه",
- "HeaderFavoriteSongs": "آهنگ های مورد علاقه",
- "HeaderLiveTV": "پخش زنده تلویزیون",
- "HeaderNextUp": "بعدی چیه",
- "HeaderRecordingGroups": "گروه های ضبط",
+ "HeaderFavoriteEpisodes": "قسمت‌های مورد علاقه",
+ "HeaderFavoriteShows": "سریال‌های مورد علاقه",
+ "HeaderFavoriteSongs": "آهنگ‌های مورد علاقه",
+ "HeaderLiveTV": "پخش زنده",
+ "HeaderNextUp": "قسمت بعدی",
+ "HeaderRecordingGroups": "گروه‌های ضبط",
"HomeVideos": "ویدیوهای خانگی",
"Inherit": "به ارث برده",
"ItemAddedWithName": "{0} به کتابخانه افزوده شد",
"ItemRemovedWithName": "{0} از کتابخانه حذف شد",
"LabelIpAddressValue": "آدرس آی پی: {0}",
"LabelRunningTimeValue": "زمان اجرا: {0}",
- "Latest": "آخرین",
+ "Latest": "جدیدترین‌ها",
"MessageApplicationUpdated": "سرور Jellyfin بروزرسانی شد",
- "MessageApplicationUpdatedTo": "سرور جلیفین آپدیت شده به نسخه {0}",
+ "MessageApplicationUpdatedTo": "سرور Jellyfin به نسخه {0} بروزرسانی شد",
"MessageNamedServerConfigurationUpdatedWithValue": "پکربندی بخش {0} سرور بروزرسانی شد",
"MessageServerConfigurationUpdated": "پیکربندی سرور بروزرسانی شد",
- "MixedContent": "محتوای درهم",
- "Movies": "فیلم های سینمایی",
+ "MixedContent": "محتوای مخلوط",
+ "Movies": "فیلم‌ها",
"Music": "موسیقی",
"MusicVideos": "موزیک ویدیوها",
- "NameInstallFailed": "{0} نصب با مشکل مواجه شده",
+ "NameInstallFailed": "{0} نصب با مشکل مواجه شد",
"NameSeasonNumber": "فصل {0}",
- "NameSeasonUnknown": "فصل های ناشناخته",
- "NewVersionIsAvailable": "یک نسخه جدید جلیفین برای بروزرسانی آماده میباشد.",
+ "NameSeasonUnknown": "فصل ناشناخته",
+ "NewVersionIsAvailable": "یک نسخه جدید Jellyfin برای بروزرسانی آماده می‌باشد.",
"NotificationOptionApplicationUpdateAvailable": "بروزرسانی برنامه موجود است",
"NotificationOptionApplicationUpdateInstalled": "بروزرسانی برنامه نصب شد",
"NotificationOptionAudioPlayback": "پخش صدا آغاز شد",
"NotificationOptionAudioPlaybackStopped": "پخش صدا متوقف شد",
"NotificationOptionCameraImageUploaded": "تصاویر دوربین آپلود شد",
- "NotificationOptionInstallationFailed": "شکست نصب",
+ "NotificationOptionInstallationFailed": "نصب شکست خورد",
"NotificationOptionNewLibraryContent": "محتوای جدید افزوده شد",
"NotificationOptionPluginError": "خرابی افزونه",
"NotificationOptionPluginInstalled": "افزونه نصب شد",
@@ -58,40 +58,61 @@
"NotificationOptionPluginUpdateInstalled": "بروزرسانی افزونه نصب شد",
"NotificationOptionServerRestartRequired": "شروع مجدد سرور نیاز است",
"NotificationOptionTaskFailed": "شکست وظیفه برنامه ریزی شده",
- "NotificationOptionUserLockedOut": "کاربر از سیستم خارج شد",
+ "NotificationOptionUserLockedOut": "کاربر قفل شد",
"NotificationOptionVideoPlayback": "پخش ویدیو آغاز شد",
"NotificationOptionVideoPlaybackStopped": "پخش ویدیو متوقف شد",
- "Photos": "عکس ها",
- "Playlists": "لیست های پخش",
+ "Photos": "عکس‌ها",
+ "Playlists": "لیست‌های پخش",
"Plugin": "افزونه",
"PluginInstalledWithName": "{0} نصب شد",
"PluginUninstalledWithName": "{0} حذف شد",
"PluginUpdatedWithName": "{0} آپدیت شد",
"ProviderValue": "ارائه دهنده: {0}",
- "ScheduledTaskFailedWithName": "{0} ناموفق بود",
+ "ScheduledTaskFailedWithName": "{0} شکست خورد",
"ScheduledTaskStartedWithName": "{0} شروع شد",
- "ServerNameNeedsToBeRestarted": "{0} احتیاج به راه اندازی مجدد",
- "Shows": "سریال ها",
- "Songs": "آهنگ ها",
+ "ServerNameNeedsToBeRestarted": "{0} نیاز به راه اندازی مجدد دارد",
+ "Shows": "سریال‌ها",
+ "Songs": "موسیقی‌ها",
"StartupEmbyServerIsLoading": "سرور Jellyfin در حال بارگیری است. لطفا کمی بعد دوباره تلاش کنید.",
"SubtitleDownloadFailureForItem": "دانلود زیرنویس برای {0} ناموفق بود",
- "SubtitleDownloadFailureFromForItem": "زیرنویس برای دانلود با مشکل مواجه شده از {0} برای {1}",
- "SubtitlesDownloadedForItem": "زیرنویس {0} دانلود شد",
- "Sync": "همگامسازی",
+ "SubtitleDownloadFailureFromForItem": "بارگیری زیرنویس برای {1} از {0} شکست خورد",
+ "Sync": "همگام‌سازی",
"System": "سیستم",
- "TvShows": "سریال های تلویزیونی",
+ "TvShows": "سریال‌های تلویزیونی",
"User": "کاربر",
"UserCreatedWithName": "کاربر {0} ایجاد شد",
"UserDeletedWithName": "کاربر {0} حذف شد",
- "UserDownloadingItemWithValues": "{0} در حال دانلود است {1}",
- "UserLockedOutWithName": "کاربر {0} از سیستم خارج شد",
+ "UserDownloadingItemWithValues": "{0} در حال بارگیری {1} می‌باشد",
+ "UserLockedOutWithName": "کاربر {0} قفل شده است",
"UserOfflineFromDevice": "ارتباط {0} از {1} قطع شد",
- "UserOnlineFromDevice": "{0}از {1} آنلاین میباشد",
- "UserPasswordChangedWithName": "رمز برای کاربر {0} تغییر یافت",
+ "UserOnlineFromDevice": "{0} از {1} آنلاین می‌باشد",
+ "UserPasswordChangedWithName": "گذرواژه برای کاربر {0} تغییر کرد",
"UserPolicyUpdatedWithName": "سیاست کاربری برای {0} بروزرسانی شد",
- "UserStartedPlayingItemWithValues": "{0} شروع به پخش {1} کرد",
- "UserStoppedPlayingItemWithValues": "{0} پخش {1} را متوقف کرد",
- "ValueHasBeenAddedToLibrary": "{0} اضافه شده به کتابخانه رسانه شما",
- "ValueSpecialEpisodeName": "ویژه- {0}",
- "VersionNumber": "نسخه {0}"
+ "UserStartedPlayingItemWithValues": "{0} در حال پخش {1} بر روی {2} است",
+ "UserStoppedPlayingItemWithValues": "{0} پخش {1} را بر روی {2} به پایان رساند",
+ "ValueHasBeenAddedToLibrary": "{0} به کتابخانه‌ی رسانه‌ی شما افزوده شد",
+ "ValueSpecialEpisodeName": "ویژه - {0}",
+ "VersionNumber": "نسخه {0}",
+ "TaskCleanTranscodeDescription": "فایل‌های کدگذاری که قدیمی‌تر از یک روز هستند را حذف می‌کند.",
+ "TaskCleanTranscode": "پاکسازی مسیر کد گذاری",
+ "TaskUpdatePluginsDescription": "دانلود و نصب به روز رسانی افزونه‌هایی که برای به روز رسانی خودکار پیکربندی شده‌اند.",
+ "TaskDownloadMissingSubtitlesDescription": "جستجوی زیرنویس‌های ناموجود در اینترنت بر اساس پیکربندی ابرداده‌ها.",
+ "TaskDownloadMissingSubtitles": "دانلود زیرنویس‌های ناموجود",
+ "TaskRefreshChannelsDescription": "اطلاعات کانال اینترنتی را تازه سازی می‌کند.",
+ "TaskRefreshChannels": "تازه سازی کانال‌ها",
+ "TaskUpdatePlugins": "به روز رسانی افزونه‌ها",
+ "TaskRefreshPeopleDescription": "ابرداده‌ها برای بازیگران و کارگردانان در کتابخانه رسانه شما به روزرسانی می شوند.",
+ "TaskRefreshPeople": "تازه سازی افراد",
+ "TaskCleanLogsDescription": "واقعه نگارهایی را که قدیمی تر {0} روز هستند را حذف می کند.",
+ "TaskCleanLogs": "پاکسازی مسیر واقعه نگار",
+ "TaskRefreshLibraryDescription": "کتابخانه رسانه شما را اسکن می‌کند و ابرداده‌ها را تازه سازی می‌کند.",
+ "TaskRefreshLibrary": "اسکن کتابخانه رسانه",
+ "TaskRefreshChapterImagesDescription": "عکس‌های کوچک برای ویدیوهایی که سکانس دارند ایجاد می‌کند.",
+ "TaskRefreshChapterImages": "استخراج عکس‌های سکانس",
+ "TaskCleanCacheDescription": "فایل‌های حافظه موقت که توسط سیستم دیگر مورد نیاز نیستند حذف می‌شوند.",
+ "TaskCleanCache": "پاکسازی مسیر حافظه موقت",
+ "TasksChannelsCategory": "کانال‌های داخلی",
+ "TasksApplicationCategory": "برنامه",
+ "TasksLibraryCategory": "کتابخانه",
+ "TasksMaintenanceCategory": "تعمیر"
}
diff --git a/Emby.Server.Implementations/Localization/Core/fi.json b/Emby.Server.Implementations/Localization/Core/fi.json
index a38103d25..4ed7b301a 100644
--- a/Emby.Server.Implementations/Localization/Core/fi.json
+++ b/Emby.Server.Implementations/Localization/Core/fi.json
@@ -1,5 +1,5 @@
{
- "HeaderLiveTV": "TV-lähetykset",
+ "HeaderLiveTV": "Live-TV",
"NewVersionIsAvailable": "Uusi versio Jellyfin palvelimesta on ladattavissa.",
"NameSeasonUnknown": "Tuntematon Kausi",
"NameSeasonNumber": "Kausi {0}",
@@ -19,12 +19,12 @@
"ItemAddedWithName": "{0} lisättiin kirjastoon",
"Inherit": "Periytyä",
"HomeVideos": "Kotivideot",
- "HeaderRecordingGroups": "Nauhoitusryhmät",
+ "HeaderRecordingGroups": "Nauhoiteryhmät",
"HeaderNextUp": "Seuraavaksi",
"HeaderFavoriteSongs": "Lempikappaleet",
"HeaderFavoriteShows": "Lempisarjat",
"HeaderFavoriteEpisodes": "Lempijaksot",
- "HeaderCameraUploads": "Kameralataukset",
+ "HeaderCameraUploads": "Kamerasta Lähetetyt",
"HeaderFavoriteArtists": "Lempiartistit",
"HeaderFavoriteAlbums": "Lempialbumit",
"HeaderContinueWatching": "Jatka katsomista",
@@ -63,34 +63,55 @@
"UserPasswordChangedWithName": "Salasana vaihdettu käyttäjälle {0}",
"UserOnlineFromDevice": "{0} on paikalla osoitteesta {1}",
"UserOfflineFromDevice": "{0} yhteys katkaistu {1}",
- "UserLockedOutWithName": "Käyttäjä {0} kirjautui ulos",
- "UserDownloadingItemWithValues": "{0} latautumassa {1}",
- "UserDeletedWithName": "Poistettiin käyttäjä {0}",
- "UserCreatedWithName": "Luotiin käyttäjä {0}",
- "TvShows": "TV-Ohjelmat",
+ "UserLockedOutWithName": "Käyttäjä {0} lukittu",
+ "UserDownloadingItemWithValues": "{0} lataa {1}",
+ "UserDeletedWithName": "Käyttäjä {0} poistettu",
+ "UserCreatedWithName": "Käyttäjä {0} luotu",
+ "TvShows": "TV-sarjat",
"Sync": "Synkronoi",
- "SubtitlesDownloadedForItem": "Tekstitys ladattu {0}",
- "SubtitleDownloadFailureFromForItem": "Tekstityksen lataaminen epäonnistui {0} - {1}",
+ "SubtitleDownloadFailureFromForItem": "Tekstitysten lataus ({0} -> {1}) epäonnistui //this string would have to be generated for each provider and movie because of finnish cases, sorry",
"StartupEmbyServerIsLoading": "Jellyfin palvelin latautuu. Kokeile hetken kuluttua uudelleen.",
"Songs": "Kappaleet",
- "Shows": "Ohjelmat",
- "ServerNameNeedsToBeRestarted": "{0} vaatii uudelleenkäynnistyksen",
- "ProviderValue": "Palveluntarjoaja: {0}",
+ "Shows": "Sarjat",
+ "ServerNameNeedsToBeRestarted": "{0} täytyy käynnistää uudelleen",
+ "ProviderValue": "Tarjoaja: {0}",
"Plugin": "Liitännäinen",
- "NotificationOptionVideoPlaybackStopped": "Videon toistaminen pysäytetty",
- "NotificationOptionVideoPlayback": "Videon toistaminen aloitettu",
+ "NotificationOptionVideoPlaybackStopped": "Videon toisto pysäytetty",
+ "NotificationOptionVideoPlayback": "Videota toistetaan",
"NotificationOptionUserLockedOut": "Käyttäjä kirjautui ulos",
- "NotificationOptionTaskFailed": "Ajastetun tehtävän ongelma",
- "NotificationOptionServerRestartRequired": "Palvelimen uudelleenkäynnistys vaaditaan",
+ "NotificationOptionTaskFailed": "Ajastettu tehtävä epäonnistui",
+ "NotificationOptionServerRestartRequired": "Palvelin pitää käynnistää uudelleen",
"NotificationOptionPluginUpdateInstalled": "Liitännäinen päivitetty",
"NotificationOptionPluginUninstalled": "Liitännäinen poistettu",
"NotificationOptionPluginInstalled": "Liitännäinen asennettu",
"NotificationOptionPluginError": "Ongelma liitännäisessä",
"NotificationOptionNewLibraryContent": "Uutta sisältöä lisätty",
"NotificationOptionInstallationFailed": "Asennus epäonnistui",
- "NotificationOptionCameraImageUploaded": "Kuva ladattu kamerasta",
- "NotificationOptionAudioPlaybackStopped": "Audion toisto pysäytetty",
- "NotificationOptionAudioPlayback": "Audion toisto aloitettu",
- "NotificationOptionApplicationUpdateInstalled": "Ohjelmistopäivitys asennettu",
- "NotificationOptionApplicationUpdateAvailable": "Ohjelmistopäivitys saatavilla"
+ "NotificationOptionCameraImageUploaded": "Kameran kuva ladattu",
+ "NotificationOptionAudioPlaybackStopped": "Äänen toisto lopetettu",
+ "NotificationOptionAudioPlayback": "Toistetaan ääntä",
+ "NotificationOptionApplicationUpdateInstalled": "Sovelluspäivitys asennettu",
+ "NotificationOptionApplicationUpdateAvailable": "Ohjelmistopäivitys saatavilla",
+ "TasksMaintenanceCategory": "Ylläpito",
+ "TaskDownloadMissingSubtitlesDescription": "Etsii puuttuvia tekstityksiä videon metadatatietojen pohjalta.",
+ "TaskDownloadMissingSubtitles": "Lataa puuttuvat tekstitykset",
+ "TaskRefreshChannelsDescription": "Päivittää internet-kanavien tiedot.",
+ "TaskRefreshChannels": "Päivitä kanavat",
+ "TaskCleanTranscodeDescription": "Poistaa transkoodatut tiedostot jotka ovat yli päivän vanhoja.",
+ "TaskCleanTranscode": "Puhdista transkoodaushakemisto",
+ "TaskUpdatePluginsDescription": "Lataa ja asentaa päivitykset liitännäisille jotka on asetettu päivittymään automaattisesti.",
+ "TaskUpdatePlugins": "Päivitä liitännäiset",
+ "TaskRefreshPeopleDescription": "Päivittää näyttelijöiden ja ohjaajien mediatiedot kirjastossasi.",
+ "TaskRefreshPeople": "Päivitä henkilöt",
+ "TaskCleanLogsDescription": "Poistaa lokitiedostot jotka ovat yli {0} päivää vanhoja.",
+ "TaskCleanLogs": "Puhdista lokihakemisto",
+ "TaskRefreshLibraryDescription": "Skannaa mediakirjastosi uusien tiedostojen varalle, sekä virkistää metatiedot.",
+ "TaskRefreshLibrary": "Skannaa mediakirjasto",
+ "TaskRefreshChapterImagesDescription": "Luo pienoiskuvat videoille joissa on lukuja.",
+ "TaskRefreshChapterImages": "Eristä lukujen kuvat",
+ "TaskCleanCacheDescription": "Poistaa järjestelmälle tarpeettomat väliaikaistiedostot.",
+ "TaskCleanCache": "Tyhjennä välimuisti-hakemisto",
+ "TasksChannelsCategory": "Internet kanavat",
+ "TasksApplicationCategory": "Sovellus",
+ "TasksLibraryCategory": "Kirjasto"
}
diff --git a/Emby.Server.Implementations/Localization/Core/fil.json b/Emby.Server.Implementations/Localization/Core/fil.json
index 66db059d9..47daf2044 100644
--- a/Emby.Server.Implementations/Localization/Core/fil.json
+++ b/Emby.Server.Implementations/Localization/Core/fil.json
@@ -16,7 +16,6 @@
"TvShows": "Pelikula",
"System": "Sistema",
"Sync": "Pag-sync",
- "SubtitlesDownloadedForItem": "Naidownload na ang subtitles {0}",
"SubtitleDownloadFailureFromForItem": "Hindi naidownload ang subtitles {0} para sa {1}",
"StartupEmbyServerIsLoading": "Nagloload ang Jellyfin Server. Sandaling maghintay.",
"Songs": "Kanta",
@@ -91,5 +90,13 @@
"Artists": "Artista",
"Application": "Aplikasyon",
"AppDeviceValues": "Aplikasyon: {0}, Aparato: {1}",
- "Albums": "Albums"
+ "Albums": "Albums",
+ "TaskRefreshLibrary": "Suriin ang nasa librerya",
+ "TaskRefreshChapterImagesDescription": "Gumawa ng larawan para sa mga pelikula na may kabanata",
+ "TaskRefreshChapterImages": "Kunin ang mga larawan ng kabanata",
+ "TaskCleanCacheDescription": "Tanggalin ang mga cache file na hindi na kailangan ng systema.",
+ "TasksChannelsCategory": "Palabas sa internet",
+ "TasksLibraryCategory": "Librerya",
+ "TasksMaintenanceCategory": "Pagpapanatili",
+ "HomeVideos": "Sariling pelikula"
}
diff --git a/Emby.Server.Implementations/Localization/Core/fr-CA.json b/Emby.Server.Implementations/Localization/Core/fr-CA.json
index 4b4db39a8..2c9dae6a1 100644
--- a/Emby.Server.Implementations/Localization/Core/fr-CA.json
+++ b/Emby.Server.Implementations/Localization/Core/fr-CA.json
@@ -76,7 +76,6 @@
"StartupEmbyServerIsLoading": "Le serveur Jellyfin est en cours de chargement. Veuillez réessayer dans quelques instants.",
"SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}",
"SubtitleDownloadFailureFromForItem": "Échec du téléchargement des sous-titres depuis {0} pour {1}",
- "SubtitlesDownloadedForItem": "Les sous-titres de {0} ont été téléchargés",
"Sync": "Synchroniser",
"System": "Système",
"TvShows": "Séries Télé",
@@ -93,5 +92,7 @@
"UserStoppedPlayingItemWithValues": "{0} vient d'arrêter la lecture de {1} sur {2}",
"ValueHasBeenAddedToLibrary": "{0} a été ajouté à votre médiathèque",
"ValueSpecialEpisodeName": "Spécial - {0}",
- "VersionNumber": "Version {0}"
+ "VersionNumber": "Version {0}",
+ "TasksLibraryCategory": "Bibliothèque",
+ "TasksMaintenanceCategory": "Entretien"
}
diff --git a/Emby.Server.Implementations/Localization/Core/fr.json b/Emby.Server.Implementations/Localization/Core/fr.json
index 90754e415..150952d8b 100644
--- a/Emby.Server.Implementations/Localization/Core/fr.json
+++ b/Emby.Server.Implementations/Localization/Core/fr.json
@@ -3,19 +3,19 @@
"AppDeviceValues": "Application : {0}, Appareil : {1}",
"Application": "Application",
"Artists": "Artistes",
- "AuthenticationSucceededWithUserName": "{0} s'est authentifié avec succès",
+ "AuthenticationSucceededWithUserName": "{0} authentifié avec succès",
"Books": "Livres",
- "CameraImageUploadedFrom": "Une nouvelle photo a été chargée depuis {0}",
+ "CameraImageUploadedFrom": "Une nouvelle photographie a été chargée depuis {0}",
"Channels": "Chaînes",
"ChapterNameValue": "Chapitre {0}",
"Collections": "Collections",
"DeviceOfflineWithName": "{0} s'est déconnecté",
"DeviceOnlineWithName": "{0} est connecté",
- "FailedLoginAttemptWithUserName": "Échec de connexion de {0}",
+ "FailedLoginAttemptWithUserName": "Échec de connexion depuis {0}",
"Favorites": "Favoris",
"Folders": "Dossiers",
"Genres": "Genres",
- "HeaderAlbumArtists": "Artistes d'album",
+ "HeaderAlbumArtists": "Artistes de l'album",
"HeaderCameraUploads": "Photos transférées",
"HeaderContinueWatching": "Continuer à regarder",
"HeaderFavoriteAlbums": "Albums favoris",
@@ -69,14 +69,13 @@
"PluginUpdatedWithName": "{0} a été mis à jour",
"ProviderValue": "Fournisseur : {0}",
"ScheduledTaskFailedWithName": "{0} a échoué",
- "ScheduledTaskStartedWithName": "{0} a commencé",
+ "ScheduledTaskStartedWithName": "{0} a démarré",
"ServerNameNeedsToBeRestarted": "{0} doit être redémarré",
"Shows": "Émissions",
"Songs": "Chansons",
"StartupEmbyServerIsLoading": "Le serveur Jellyfin est en cours de chargement. Veuillez réessayer dans quelques instants.",
"SubtitleDownloadFailureForItem": "Le téléchargement des sous-titres pour {0} a échoué.",
"SubtitleDownloadFailureFromForItem": "Échec du téléchargement des sous-titres depuis {0} pour {1}",
- "SubtitlesDownloadedForItem": "Les sous-titres de {0} ont été téléchargés",
"Sync": "Synchroniser",
"System": "Système",
"TvShows": "Séries Télé",
@@ -93,5 +92,27 @@
"UserStoppedPlayingItemWithValues": "{0} vient d'arrêter la lecture de {1} sur {2}",
"ValueHasBeenAddedToLibrary": "{0} a été ajouté à votre médiathèque",
"ValueSpecialEpisodeName": "Spécial - {0}",
- "VersionNumber": "Version {0}"
+ "VersionNumber": "Version {0}",
+ "TasksChannelsCategory": "Chaines en ligne",
+ "TaskDownloadMissingSubtitlesDescription": "Cherche les sous-titres manquant sur internet en se basant sur la configuration des métadonnées.",
+ "TaskDownloadMissingSubtitles": "Télécharger les sous-titres manquant",
+ "TaskRefreshChannelsDescription": "Rafraîchit les informations des chaines en ligne.",
+ "TaskRefreshChannels": "Rafraîchir les chaines",
+ "TaskCleanTranscodeDescription": "Supprime les fichiers transcodés de plus d'un jour.",
+ "TaskCleanTranscode": "Nettoyer les dossier des transcodages",
+ "TaskUpdatePluginsDescription": "Télécharge et installe les mises à jours des extensions configurés pour être mises à jour automatiquement.",
+ "TaskUpdatePlugins": "Mettre à jour les extensions",
+ "TaskRefreshPeopleDescription": "Met à jour les métadonnées pour les acteurs et réalisateurs dans votre bibliothèque.",
+ "TaskRefreshPeople": "Rafraîchir les acteurs",
+ "TaskCleanLogsDescription": "Supprime les journaux de plus de {0} jours.",
+ "TaskCleanLogs": "Nettoyer le répertoire des journaux",
+ "TaskRefreshLibraryDescription": "Scanne toute les bibliothèques pour trouver les nouveaux fichiers et rafraîchit les métadonnées.",
+ "TaskRefreshLibrary": "Scanner toute les Bibliothèques",
+ "TaskRefreshChapterImagesDescription": "Crée des images de miniature pour les vidéos ayant des chapitres.",
+ "TaskRefreshChapterImages": "Extraire les images de chapitre",
+ "TaskCleanCacheDescription": "Supprime les fichiers de cache dont le système n'a plus besoin.",
+ "TaskCleanCache": "Vider le répertoire cache",
+ "TasksApplicationCategory": "Application",
+ "TasksLibraryCategory": "Bibliothèque",
+ "TasksMaintenanceCategory": "Maintenance"
}
diff --git a/Emby.Server.Implementations/Localization/Core/gsw.json b/Emby.Server.Implementations/Localization/Core/gsw.json
index 69c157401..9611e33f5 100644
--- a/Emby.Server.Implementations/Localization/Core/gsw.json
+++ b/Emby.Server.Implementations/Localization/Core/gsw.json
@@ -76,7 +76,6 @@
"StartupEmbyServerIsLoading": "Jellyfin Server ladt. Bitte grad noeinisch probiere.",
"SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}",
"SubtitleDownloadFailureFromForItem": "Ondertetle vo {0} för {1} hend ned chönne abeglade wärde",
- "SubtitlesDownloadedForItem": "Ondertetle abeglade för {0}",
"Sync": "Synchronisation",
"System": "System",
"TvShows": "Färnsehserie",
diff --git a/Emby.Server.Implementations/Localization/Core/he.json b/Emby.Server.Implementations/Localization/Core/he.json
index 5618719dd..266291362 100644
--- a/Emby.Server.Implementations/Localization/Core/he.json
+++ b/Emby.Server.Implementations/Localization/Core/he.json
@@ -1,7 +1,7 @@
{
"Albums": "אלבומים",
"AppDeviceValues": "יישום: {0}, מכשיר: {1}",
- "Application": "אפליקציה",
+ "Application": "יישום",
"Artists": "אומנים",
"AuthenticationSucceededWithUserName": "{0} אומת בהצלחה",
"Books": "ספרים",
@@ -76,7 +76,6 @@
"StartupEmbyServerIsLoading": "שרת Jellyfin בהליכי טעינה. אנא נסה שנית בעוד זמן קצר.",
"SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}",
"SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
- "SubtitlesDownloadedForItem": "Subtitles downloaded for {0}",
"Sync": "סנכרן",
"System": "System",
"TvShows": "סדרות טלוויזיה",
@@ -93,5 +92,12 @@
"UserStoppedPlayingItemWithValues": "{0} סיים לנגן את {1} על {2}",
"ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
"ValueSpecialEpisodeName": "מיוחד- {0}",
- "VersionNumber": "Version {0}"
+ "VersionNumber": "Version {0}",
+ "TaskRefreshLibrary": "סרוק ספריית מדיה",
+ "TaskRefreshChapterImages": "חלץ תמונות פרקים",
+ "TaskCleanCacheDescription": "מחק קבצי מטמון שלא בשימוש המערכת.",
+ "TaskCleanCache": "נקה תיקיית מטמון",
+ "TasksApplicationCategory": "יישום",
+ "TasksLibraryCategory": "ספרייה",
+ "TasksMaintenanceCategory": "תחזוקה"
}
diff --git a/Emby.Server.Implementations/Localization/Core/hr.json b/Emby.Server.Implementations/Localization/Core/hr.json
index f284b3cd9..6947178d7 100644
--- a/Emby.Server.Implementations/Localization/Core/hr.json
+++ b/Emby.Server.Implementations/Localization/Core/hr.json
@@ -76,7 +76,6 @@
"StartupEmbyServerIsLoading": "Jellyfin Server se učitava. Pokušajte ponovo kasnije.",
"SubtitleDownloadFailureForItem": "Titlovi prijevoda nisu preuzeti za {0}",
"SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
- "SubtitlesDownloadedForItem": "Titlovi prijevoda preuzeti za {0}",
"Sync": "Sink.",
"System": "Sistem",
"TvShows": "TV Shows",
diff --git a/Emby.Server.Implementations/Localization/Core/hu.json b/Emby.Server.Implementations/Localization/Core/hu.json
index 6017aa7f9..c5c3844e3 100644
--- a/Emby.Server.Implementations/Localization/Core/hu.json
+++ b/Emby.Server.Implementations/Localization/Core/hu.json
@@ -7,7 +7,7 @@
"Books": "Könyvek",
"CameraImageUploadedFrom": "Új kamerakép került feltöltésre innen: {0}",
"Channels": "Csatornák",
- "ChapterNameValue": "Jelenet {0}",
+ "ChapterNameValue": "{0}. jelenet",
"Collections": "Gyűjtemények",
"DeviceOfflineWithName": "{0} kijelentkezett",
"DeviceOnlineWithName": "{0} belépett",
@@ -71,12 +71,11 @@
"ScheduledTaskFailedWithName": "{0} sikertelen",
"ScheduledTaskStartedWithName": "{0} elkezdve",
"ServerNameNeedsToBeRestarted": "{0}-t újra kell indítani",
- "Shows": "Műsorok",
+ "Shows": "Sorozatok",
"Songs": "Dalok",
"StartupEmbyServerIsLoading": "A Jellyfin Szerver betöltődik. Kérlek, próbáld újra hamarosan.",
"SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}",
"SubtitleDownloadFailureFromForItem": "Nem sikerült a felirat letöltése innen: {0} ehhez: {1}",
- "SubtitlesDownloadedForItem": "Letöltött feliratok a következőhöz: {0}",
"Sync": "Szinkronizál",
"System": "Rendszer",
"TvShows": "TV műsorok",
@@ -93,5 +92,27 @@
"UserStoppedPlayingItemWithValues": "{0} befejezte {1} lejátászását itt: {2}",
"ValueHasBeenAddedToLibrary": "{0} hozzáadva a médiatárhoz",
"ValueSpecialEpisodeName": "Special - {0}",
- "VersionNumber": "Verzió: {0}"
+ "VersionNumber": "Verzió: {0}",
+ "TaskCleanTranscode": "Átkódolási könyvtár ürítése",
+ "TaskUpdatePluginsDescription": "Letölti és telepíti a frissítéseket azokhoz a bővítményekhez, amelyeknél az automatikus frissítés engedélyezve van.",
+ "TaskUpdatePlugins": "Bővítmények frissítése",
+ "TaskRefreshPeopleDescription": "Frissíti a szereplők és a stábok metaadatait a könyvtáradban.",
+ "TaskRefreshPeople": "Személyek frissítése",
+ "TaskCleanLogsDescription": "Törli azokat a naplófájlokat, amelyek {0} napnál régebbiek.",
+ "TaskCleanLogs": "Naplózási könyvtár ürítése",
+ "TaskRefreshLibraryDescription": "Átvizsgálja a könyvtáraidat új fájlokért és frissíti a metaadatokat.",
+ "TaskRefreshLibrary": "Média könyvtár beolvasása",
+ "TaskRefreshChapterImagesDescription": "Miniatűröket generál olyan videókhoz, amely tartalmaz fejezeteket.",
+ "TaskRefreshChapterImages": "Fejezetek képeinek generálása",
+ "TaskCleanCacheDescription": "Törli azokat a gyorsítótárazott fájlokat, amikre a rendszernek már nincs szüksége.",
+ "TaskCleanCache": "Gyorsítótár könyvtárának ürítése",
+ "TasksChannelsCategory": "Internetes csatornák",
+ "TasksApplicationCategory": "Alkalmazás",
+ "TasksLibraryCategory": "Könyvtár",
+ "TasksMaintenanceCategory": "Karbantartás",
+ "TaskDownloadMissingSubtitlesDescription": "A metaadat konfiguráció alapján ellenőrzi és letölti a hiányzó feliratokat az internetről.",
+ "TaskDownloadMissingSubtitles": "Hiányzó feliratok letöltése",
+ "TaskRefreshChannelsDescription": "Frissíti az internetes csatornák adatait.",
+ "TaskRefreshChannels": "Csatornák frissítése",
+ "TaskCleanTranscodeDescription": "Törli az egy napnál régebbi átkódolási fájlokat."
}
diff --git a/Emby.Server.Implementations/Localization/Core/id.json b/Emby.Server.Implementations/Localization/Core/id.json
index 68fffbf0a..eabdb9138 100644
--- a/Emby.Server.Implementations/Localization/Core/id.json
+++ b/Emby.Server.Implementations/Localization/Core/id.json
@@ -54,7 +54,6 @@
"User": "Pengguna",
"System": "Sistem",
"Sync": "Sinkron",
- "SubtitlesDownloadedForItem": "Talop telah diunduh untuk {0}",
"Shows": "Tayangan",
"ServerNameNeedsToBeRestarted": "{0} perlu dimuat ulang",
"ScheduledTaskStartedWithName": "{0} dimulai",
diff --git a/Emby.Server.Implementations/Localization/Core/is.json b/Emby.Server.Implementations/Localization/Core/is.json
index 3490a7302..ef2a57e8e 100644
--- a/Emby.Server.Implementations/Localization/Core/is.json
+++ b/Emby.Server.Implementations/Localization/Core/is.json
@@ -86,7 +86,6 @@
"UserOfflineFromDevice": "{0} hefur aftengst frá {1}",
"UserLockedOutWithName": "Notanda {0} hefur verið hindraður aðgangur",
"UserDownloadingItemWithValues": "{0} Hleður niður {1}",
- "SubtitlesDownloadedForItem": "Skjátextum halað niður fyrir {0}",
"SubtitleDownloadFailureFromForItem": "Tókst ekki að hala niður skjátextum frá {0} til {1}",
"ProviderValue": "Veitandi: {0}",
"MessageNamedServerConfigurationUpdatedWithValue": "Stilling {0} hefur verið uppfærð á netþjón",
diff --git a/Emby.Server.Implementations/Localization/Core/it.json b/Emby.Server.Implementations/Localization/Core/it.json
index 395924af4..0758bbe9c 100644
--- a/Emby.Server.Implementations/Localization/Core/it.json
+++ b/Emby.Server.Implementations/Localization/Core/it.json
@@ -5,7 +5,7 @@
"Artists": "Artisti",
"AuthenticationSucceededWithUserName": "{0} autenticato con successo",
"Books": "Libri",
- "CameraImageUploadedFrom": "È stata caricata una nuova immagine della fotocamera da {0}",
+ "CameraImageUploadedFrom": "È stata caricata una nuova immagine della fotocamera dal device {0}",
"Channels": "Canali",
"ChapterNameValue": "Capitolo {0}",
"Collections": "Collezioni",
@@ -15,7 +15,7 @@
"Favorites": "Preferiti",
"Folders": "Cartelle",
"Genres": "Generi",
- "HeaderAlbumArtists": "Artisti dell' Album",
+ "HeaderAlbumArtists": "Artisti degli Album",
"HeaderCameraUploads": "Caricamenti Fotocamera",
"HeaderContinueWatching": "Continua a guardare",
"HeaderFavoriteAlbums": "Album Preferiti",
@@ -76,7 +76,6 @@
"StartupEmbyServerIsLoading": "Jellyfin server si sta avviando. Per favore riprova più tardi.",
"SubtitleDownloadFailureForItem": "Impossibile scaricare i sottotitoli per {0}",
"SubtitleDownloadFailureFromForItem": "Impossibile scaricare i sottotitoli da {0} per {1}",
- "SubtitlesDownloadedForItem": "Sottotitoli scaricati per {0}",
"Sync": "Sincronizza",
"System": "Sistema",
"TvShows": "Serie TV",
@@ -93,5 +92,27 @@
"UserStoppedPlayingItemWithValues": "{0} ha interrotto la riproduzione di {1} su {2}",
"ValueHasBeenAddedToLibrary": "{0} è stato aggiunto alla tua libreria multimediale",
"ValueSpecialEpisodeName": "Speciale - {0}",
- "VersionNumber": "Versione {0}"
+ "VersionNumber": "Versione {0}",
+ "TaskRefreshChannelsDescription": "Aggiorna le informazioni dei canali Internet.",
+ "TaskDownloadMissingSubtitlesDescription": "Cerca su internet i sottotitoli mancanti basandosi sulle configurazioni dei metadati.",
+ "TaskDownloadMissingSubtitles": "Scarica i sottotitoli mancanti",
+ "TaskRefreshChannels": "Aggiorna i canali",
+ "TaskCleanTranscodeDescription": "Cancella i file di transcode più vecchi di un giorno.",
+ "TaskCleanTranscode": "Svuota la cartella del transcoding",
+ "TaskUpdatePluginsDescription": "Scarica e installa gli aggiornamenti per i plugin che sono stati configurati per essere aggiornati contemporaneamente.",
+ "TaskUpdatePlugins": "Aggiorna i Plugin",
+ "TaskRefreshPeopleDescription": "Aggiorna i metadati per gli attori e registi nella tua libreria multimediale.",
+ "TaskRefreshPeople": "Aggiorna persone",
+ "TaskCleanLogsDescription": "Rimuovi i file di log più vecchi di {0} giorni.",
+ "TaskCleanLogs": "Pulisci la cartella dei log",
+ "TaskRefreshLibraryDescription": "Analizza la tua libreria multimediale per nuovi file e rinnova i metadati.",
+ "TaskRefreshLibrary": "Analizza la libreria dei contenuti multimediali",
+ "TaskRefreshChapterImagesDescription": "Crea le thumbnail per i video che hanno capitoli.",
+ "TaskRefreshChapterImages": "Estrai immagini capitolo",
+ "TaskCleanCacheDescription": "Cancella i file di cache non più necessari al sistema.",
+ "TaskCleanCache": "Pulisci la directory della cache",
+ "TasksChannelsCategory": "Canali su Internet",
+ "TasksApplicationCategory": "Applicazione",
+ "TasksLibraryCategory": "Libreria",
+ "TasksMaintenanceCategory": "Manutenzione"
}
diff --git a/Emby.Server.Implementations/Localization/Core/ja.json b/Emby.Server.Implementations/Localization/Core/ja.json
index 4aa0637c5..a4d9f9ef6 100644
--- a/Emby.Server.Implementations/Localization/Core/ja.json
+++ b/Emby.Server.Implementations/Localization/Core/ja.json
@@ -75,7 +75,6 @@
"Songs": "曲",
"StartupEmbyServerIsLoading": "Jellyfin Server は現在読み込み中です。しばらくしてからもう一度お試しください。",
"SubtitleDownloadFailureFromForItem": "{0} から {1}の字幕のダウンロードに失敗しました",
- "SubtitlesDownloadedForItem": "{0} の字幕がダウンロードされました",
"Sync": "同期",
"System": "システム",
"TvShows": "テレビ番組",
@@ -92,5 +91,27 @@
"UserStoppedPlayingItemWithValues": "{0} は{2}で{1} の再生が終わりました",
"ValueHasBeenAddedToLibrary": "{0}はあなたのメディアライブラリに追加されました",
"ValueSpecialEpisodeName": "スペシャル - {0}",
- "VersionNumber": "バージョン {0}"
+ "VersionNumber": "バージョン {0}",
+ "TaskCleanLogsDescription": "{0} 日以上前のログを消去します。",
+ "TaskCleanLogs": "ログの掃除",
+ "TaskRefreshLibraryDescription": "メディアライブラリをスキャンして新しいファイルを探し、メタデータをリフレッシュします。",
+ "TaskRefreshLibrary": "メディアライブラリのスキャン",
+ "TaskCleanCacheDescription": "不要なキャッシュを消去します。",
+ "TaskCleanCache": "キャッシュの掃除",
+ "TasksChannelsCategory": "ネットチャンネル",
+ "TasksApplicationCategory": "アプリケーション",
+ "TasksLibraryCategory": "ライブラリ",
+ "TasksMaintenanceCategory": "メンテナンス",
+ "TaskRefreshChannelsDescription": "ネットチャンネルの情報をリフレッシュします。",
+ "TaskRefreshChannels": "チャンネルのリフレッシュ",
+ "TaskCleanTranscodeDescription": "1日以上経過したトランスコードファイルを削除します。",
+ "TaskCleanTranscode": "トランスコードディレクトリの削除",
+ "TaskUpdatePluginsDescription": "自動更新可能なプラグインのアップデートをダウンロードしてインストールします。",
+ "TaskUpdatePlugins": "プラグインの更新",
+ "TaskRefreshPeopleDescription": "メディアライブラリで俳優や監督のメタデータを更新します。",
+ "TaskRefreshPeople": "俳優や監督のデータの更新",
+ "TaskDownloadMissingSubtitlesDescription": "メタデータ構成に基づいて、欠落している字幕をインターネットで検索します。",
+ "TaskRefreshChapterImagesDescription": "チャプターのあるビデオのサムネイルを作成します。",
+ "TaskRefreshChapterImages": "チャプター画像を抽出する",
+ "TaskDownloadMissingSubtitles": "不足している字幕をダウンロードする"
}
diff --git a/Emby.Server.Implementations/Localization/Core/kk.json b/Emby.Server.Implementations/Localization/Core/kk.json
index cbee71155..5618ff4a8 100644
--- a/Emby.Server.Implementations/Localization/Core/kk.json
+++ b/Emby.Server.Implementations/Localization/Core/kk.json
@@ -76,7 +76,6 @@
"StartupEmbyServerIsLoading": "Jellyfin Server júktelýde. Áreketti kóp uzamaı qaıtalańyz.",
"SubtitleDownloadFailureForItem": "Субтитрлер {0} үшін жүктеліп алынуы сәтсіз",
"SubtitleDownloadFailureFromForItem": "{1} úshin sýbtıtrlerdi {0} kózinen júktep alý sátsiz",
- "SubtitlesDownloadedForItem": "{0} úshin sýbtıtrler júktelip alyndy",
"Sync": "Úndestirý",
"System": "Júıe",
"TvShows": "TD-kórsetimder",
diff --git a/Emby.Server.Implementations/Localization/Core/ko.json b/Emby.Server.Implementations/Localization/Core/ko.json
index 0320a0a1b..9e3ecd5a8 100644
--- a/Emby.Server.Implementations/Localization/Core/ko.json
+++ b/Emby.Server.Implementations/Localization/Core/ko.json
@@ -76,7 +76,6 @@
"StartupEmbyServerIsLoading": "Jellyfin 서버를 불러오고 있습니다. 잠시 후에 다시 시도하십시오.",
"SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}",
"SubtitleDownloadFailureFromForItem": "{0}에서 {1} 자막 다운로드에 실패했습니다",
- "SubtitlesDownloadedForItem": "{0} 자막 다운로드 완료",
"Sync": "동기화",
"System": "시스템",
"TvShows": "TV 쇼",
@@ -93,5 +92,27 @@
"UserStoppedPlayingItemWithValues": "{2}에서 {0}이 {1} 재생을 마침",
"ValueHasBeenAddedToLibrary": "{0}가 미디어 라이브러리에 추가되었습니다",
"ValueSpecialEpisodeName": "스페셜 - {0}",
- "VersionNumber": "버전 {0}"
+ "VersionNumber": "버전 {0}",
+ "TasksApplicationCategory": "어플리케이션",
+ "TasksMaintenanceCategory": "유지 보수",
+ "TaskDownloadMissingSubtitlesDescription": "메타 데이터 기반으로 누락 된 자막이 있는지 인터넷을 검색합니다.",
+ "TaskDownloadMissingSubtitles": "누락 된 자막 다운로드",
+ "TaskRefreshChannelsDescription": "인터넷 채널 정보를 새로 고칩니다.",
+ "TaskRefreshChannels": "채널 새로고침",
+ "TaskCleanTranscodeDescription": "하루 이상 지난 트랜스 코드 파일을 삭제합니다.",
+ "TaskCleanTranscode": "트랜스코드 폴더 청소",
+ "TaskUpdatePluginsDescription": "자동으로 업데이트되도록 구성된 플러그인 업데이트를 다운로드하여 설치합니다.",
+ "TaskUpdatePlugins": "플러그인 업데이트",
+ "TaskRefreshPeopleDescription": "미디어 라이브러리에서 배우 및 감독의 메타 데이터를 업데이트합니다.",
+ "TaskRefreshPeople": "인물 새로고침",
+ "TaskCleanLogsDescription": "{0} 일이 지난 로그 파일을 삭제합니다.",
+ "TaskCleanLogs": "로그 폴더 청소",
+ "TaskRefreshLibraryDescription": "미디어 라이브러리에서 새 파일을 검색하고 메타 데이터를 새로 고칩니다.",
+ "TaskRefreshLibrary": "미디어 라이브러리 스캔",
+ "TaskRefreshChapterImagesDescription": "챕터가있는 비디오의 썸네일을 만듭니다.",
+ "TaskRefreshChapterImages": "챕터 이미지 추출",
+ "TaskCleanCacheDescription": "시스템에서 더 이상 필요하지 않은 캐시 파일을 삭제합니다.",
+ "TaskCleanCache": "캐시 폴더 청소",
+ "TasksChannelsCategory": "인터넷 채널",
+ "TasksLibraryCategory": "라이브러리"
}
diff --git a/Emby.Server.Implementations/Localization/Core/lt-LT.json b/Emby.Server.Implementations/Localization/Core/lt-LT.json
index e8e1b7740..01a740187 100644
--- a/Emby.Server.Implementations/Localization/Core/lt-LT.json
+++ b/Emby.Server.Implementations/Localization/Core/lt-LT.json
@@ -76,7 +76,6 @@
"StartupEmbyServerIsLoading": "Jellyfin Server kraunasi. Netrukus pabandykite dar kartą.",
"SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}",
"SubtitleDownloadFailureFromForItem": "{1} subtitrai buvo nesėkmingai parsiųsti iš {0}",
- "SubtitlesDownloadedForItem": "{0} subtitrai parsiųsti",
"Sync": "Sinchronizuoti",
"System": "System",
"TvShows": "TV Serialai",
diff --git a/Emby.Server.Implementations/Localization/Core/lv.json b/Emby.Server.Implementations/Localization/Core/lv.json
index 8b8d46b2e..dbcf17287 100644
--- a/Emby.Server.Implementations/Localization/Core/lv.json
+++ b/Emby.Server.Implementations/Localization/Core/lv.json
@@ -31,7 +31,6 @@
"TvShows": "TV Raidījumi",
"Sync": "Sinhronizācija",
"System": "Sistēma",
- "SubtitlesDownloadedForItem": "Subtitri lejupielādēti priekš {0}",
"StartupEmbyServerIsLoading": "Jellyfin Serveris lādējas. Lūdzu mēģiniet vēlreiz pēc brīža.",
"Songs": "Dziesmas",
"Shows": "Raidījumi",
@@ -92,5 +91,27 @@
"HeaderFavoriteShows": "Raidījumu Favorīti",
"HeaderFavoriteEpisodes": "Episožu Favorīti",
"HeaderFavoriteArtists": "Izpildītāju Favorīti",
- "HeaderFavoriteAlbums": "Albumu Favorīti"
+ "HeaderFavoriteAlbums": "Albumu Favorīti",
+ "TaskCleanCacheDescription": "Nodzēš keša datnes, kas vairs nav sistēmai vajadzīgas.",
+ "TaskRefreshChapterImages": "Izvilkt Nodaļu Attēlus",
+ "TasksApplicationCategory": "Lietotne",
+ "TasksLibraryCategory": "Bibliotēka",
+ "TaskDownloadMissingSubtitlesDescription": "Internetā meklē trūkstošus subtitrus pēc metadatu uzstādījumiem.",
+ "TaskDownloadMissingSubtitles": "Lejupielādēt trūkstošus subtitrus",
+ "TaskRefreshChannelsDescription": "Atjauno interneta kanālu informāciju.",
+ "TaskRefreshChannels": "Atjaunot Kanālus",
+ "TaskCleanTranscodeDescription": "Izdzēš trans-kodēšanas datnes, kas ir vecākas par vienu dienu.",
+ "TaskCleanTranscode": "Iztīrīt Trans-kodēšanas Mapi",
+ "TaskUpdatePluginsDescription": "Lejupielādē un uzstāda atjauninājumus paplašinājumiem, kam ir uzstādīta automātiskā atjaunināšana.",
+ "TaskUpdatePlugins": "Atjaunot Paplašinājumus",
+ "TaskRefreshPeopleDescription": "Atjauno metadatus priekš aktieriem un direktoriem tavā mediju bibliotēkā.",
+ "TaskRefreshPeople": "Atjaunot Cilvēkus",
+ "TaskCleanLogsDescription": "Nodzēš log datnes, kas ir vairāk par {0} dienām vecas.",
+ "TaskCleanLogs": "Iztīrīt Logdatņu Mapi",
+ "TaskRefreshLibraryDescription": "Skenē tavas mediju bibliotēkas priekš jaunām datnēm un atjauno metadatus.",
+ "TaskRefreshLibrary": "Skanēt Mediju Bibliotēku",
+ "TaskRefreshChapterImagesDescription": "Izveido sīktēlus priekš video ar sadaļām.",
+ "TaskCleanCache": "Iztīrīt Kešošanas Mapi",
+ "TasksChannelsCategory": "Interneta Kanāli",
+ "TasksMaintenanceCategory": "Apkope"
}
diff --git a/Emby.Server.Implementations/Localization/Core/mk.json b/Emby.Server.Implementations/Localization/Core/mk.json
index 684a97aad..8df137302 100644
--- a/Emby.Server.Implementations/Localization/Core/mk.json
+++ b/Emby.Server.Implementations/Localization/Core/mk.json
@@ -86,7 +86,6 @@
"TvShows": "ТВ Серии",
"System": "Систем",
"Sync": "Синхронизација",
- "SubtitlesDownloadedForItem": "Спуштање превод за {0}",
"SubtitleDownloadFailureFromForItem": "Преводот неуспешно се спушти од {0} за {1}",
"StartupEmbyServerIsLoading": "Jellyfin Server се пушта. Ве молиме причекајте.",
"Songs": "Песни",
diff --git a/Emby.Server.Implementations/Localization/Core/mr.json b/Emby.Server.Implementations/Localization/Core/mr.json
new file mode 100644
index 000000000..50b6360d8
--- /dev/null
+++ b/Emby.Server.Implementations/Localization/Core/mr.json
@@ -0,0 +1,61 @@
+{
+ "Books": "पुस्तकं",
+ "Artists": "संगीतकार",
+ "Albums": "अल्बम",
+ "Playlists": "प्लेलिस्ट",
+ "HeaderAlbumArtists": "अल्बम संगीतकार",
+ "Folders": "फोल्डर",
+ "HeaderFavoriteEpisodes": "आवडते भाग",
+ "HeaderFavoriteSongs": "आवडती गाणी",
+ "Movies": "चित्रपट",
+ "HeaderFavoriteArtists": "आवडते संगीतकार",
+ "Shows": "कार्यक्रम",
+ "HeaderFavoriteAlbums": "आवडते अल्बम",
+ "Channels": "वाहिन्या",
+ "ValueSpecialEpisodeName": "विशेष - {0}",
+ "HeaderFavoriteShows": "आवडते कार्यक्रम",
+ "Favorites": "आवडीचे",
+ "HeaderNextUp": "यानंतर",
+ "Songs": "गाणी",
+ "HeaderLiveTV": "लाइव्ह टीव्ही",
+ "Genres": "जाँनरे",
+ "Photos": "चित्र",
+ "TaskDownloadMissingSubtitles": "नसलेले सबटायटल डाउनलोड करा",
+ "TaskCleanTranscodeDescription": "एक दिवसापेक्षा जुन्या ट्रान्सकोड फायली काढून टाका.",
+ "TaskCleanTranscode": "ट्रान्सकोड डिरेक्टरी साफ करून टाका",
+ "TaskUpdatePlugins": "प्लगइन अपडेट करा",
+ "TaskCleanLogs": "लॉग डिरेक्टरी साफ करून टाका",
+ "TaskCleanCache": "कॅश डिरेक्टरी साफ करून टाका",
+ "TasksChannelsCategory": "इंटरनेट वाहिन्या",
+ "TasksApplicationCategory": "अ‍ॅप्लिकेशन",
+ "TasksLibraryCategory": "संग्रहालय",
+ "VersionNumber": "आवृत्ती {0}",
+ "UserPasswordChangedWithName": "{0} या प्रयोक्त्याचे पासवर्ड बदलण्यात आले आहे",
+ "UserOnlineFromDevice": "{0} हे {1} येथून ऑनलाइन आहेत",
+ "UserDeletedWithName": "प्रयोक्ता {0} काढून टाकण्यात आले आहे",
+ "UserCreatedWithName": "प्रयोक्ता {0} बनवण्यात आले आहे",
+ "User": "प्रयोक्ता",
+ "TvShows": "टीव्ही कार्यक्रम",
+ "StartupEmbyServerIsLoading": "जेलिफिन सर्व्हर लोड होत आहे. कृपया थोड्या वेळात पुन्हा प्रयत्न करा.",
+ "Plugin": "प्लगइन",
+ "NotificationOptionCameraImageUploaded": "कॅमेरा चित्र अपलोड केले आहे",
+ "NotificationOptionApplicationUpdateInstalled": "अ‍ॅप्लिकेशन अपडेट इन्स्टॉल केले आहे",
+ "NotificationOptionApplicationUpdateAvailable": "अ‍ॅप्लिकेशन अपडेट उपलब्ध आहे",
+ "NewVersionIsAvailable": "जेलिफिन सर्व्हरची एक नवीन आवृत्ती डाउनलोड करण्यास उपलब्ध आहे.",
+ "NameSeasonUnknown": "अज्ञात सीझन",
+ "NameSeasonNumber": "सीझन {0}",
+ "MusicVideos": "संगीत व्हिडीयो",
+ "Music": "संगीत",
+ "MessageApplicationUpdatedTo": "जेलिफिन सर्व्हर अपडेट होऊन {0} आवृत्तीवर पोहोचला आहे",
+ "MessageApplicationUpdated": "जेलिफिन सर्व्हर अपडेट केला गेला आहे",
+ "Latest": "नवीनतम",
+ "LabelIpAddressValue": "आयपी पत्ता: {0}",
+ "ItemRemovedWithName": "{0} हे संग्रहालयातून काढून टाकण्यात आले",
+ "ItemAddedWithName": "{0} हे संग्रहालयात जोडले गेले",
+ "HomeVideos": "घरचे व्हिडीयो",
+ "HeaderRecordingGroups": "रेकॉर्डिंग गट",
+ "HeaderCameraUploads": "कॅमेरा अपलोड",
+ "CameraImageUploadedFrom": "एक नवीन कॅमेरा चित्र {0} येथून अपलोड केले आहे",
+ "Application": "अ‍ॅप्लिकेशन",
+ "AppDeviceValues": "अ‍ॅप: {0}, यंत्र: {1}"
+}
diff --git a/Emby.Server.Implementations/Localization/Core/ms.json b/Emby.Server.Implementations/Localization/Core/ms.json
index 1d86257f8..79d078d4a 100644
--- a/Emby.Server.Implementations/Localization/Core/ms.json
+++ b/Emby.Server.Implementations/Localization/Core/ms.json
@@ -76,7 +76,6 @@
"StartupEmbyServerIsLoading": "Jellyfin Server is loading. Please try again shortly.",
"SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}",
"SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
- "SubtitlesDownloadedForItem": "Subtitles downloaded for {0}",
"Sync": "Sync",
"System": "Sistem",
"TvShows": "TV Shows",
diff --git a/Emby.Server.Implementations/Localization/Core/nb.json b/Emby.Server.Implementations/Localization/Core/nb.json
index f9fa1b68c..50d0d083c 100644
--- a/Emby.Server.Implementations/Localization/Core/nb.json
+++ b/Emby.Server.Implementations/Localization/Core/nb.json
@@ -76,7 +76,6 @@
"StartupEmbyServerIsLoading": "Jellyfin Server laster. Prøv igjen snart.",
"SubtitleDownloadFailureForItem": "En feil oppstå under nedlasting av undertekster for {0}",
"SubtitleDownloadFailureFromForItem": "Kunne ikke laste ned undertekster fra {0} for {1}",
- "SubtitlesDownloadedForItem": "Undertekster lastet ned for {0}",
"Sync": "Synkroniser",
"System": "System",
"TvShows": "TV-serier",
@@ -93,5 +92,10 @@
"UserStoppedPlayingItemWithValues": "{0} har stoppet avspilling {1}",
"ValueHasBeenAddedToLibrary": "{0} har blitt lagt til i mediebiblioteket ditt",
"ValueSpecialEpisodeName": "Spesialepisode - {0}",
- "VersionNumber": "Versjon {0}"
+ "VersionNumber": "Versjon {0}",
+ "TasksChannelsCategory": "Internett kanaler",
+ "TasksApplicationCategory": "Applikasjon",
+ "TasksLibraryCategory": "Bibliotek",
+ "TasksMaintenanceCategory": "Vedlikehold",
+ "TaskCleanCache": "Tøm buffer katalog"
}
diff --git a/Emby.Server.Implementations/Localization/Core/nl.json b/Emby.Server.Implementations/Localization/Core/nl.json
index e22f95ab4..baa12e98e 100644
--- a/Emby.Server.Implementations/Localization/Core/nl.json
+++ b/Emby.Server.Implementations/Localization/Core/nl.json
@@ -1,11 +1,11 @@
{
"Albums": "Albums",
"AppDeviceValues": "App: {0}, Apparaat: {1}",
- "Application": "Applicatie",
+ "Application": "Programma",
"Artists": "Artiesten",
- "AuthenticationSucceededWithUserName": "{0} succesvol geauthenticeerd",
+ "AuthenticationSucceededWithUserName": "{0} is succesvol geverifieerd",
"Books": "Boeken",
- "CameraImageUploadedFrom": "Er is een nieuwe foto toegevoegd van {0}",
+ "CameraImageUploadedFrom": "Er is een nieuwe afbeelding toegevoegd via {0}",
"Channels": "Kanalen",
"ChapterNameValue": "Hoofdstuk {0}",
"Collections": "Verzamelingen",
@@ -76,7 +76,6 @@
"StartupEmbyServerIsLoading": "Jellyfin Server is aan het laden, probeer het later opnieuw.",
"SubtitleDownloadFailureForItem": "Downloaden van ondertiteling voor {0} is mislukt",
"SubtitleDownloadFailureFromForItem": "Ondertitels konden niet gedownload worden van {0} voor {1}",
- "SubtitlesDownloadedForItem": "Ondertiteling voor {0} is gedownload",
"Sync": "Synchronisatie",
"System": "Systeem",
"TvShows": "TV-series",
@@ -93,5 +92,27 @@
"UserStoppedPlayingItemWithValues": "{0} heeft afspelen van {1} gestopt op {2}",
"ValueHasBeenAddedToLibrary": "{0} is toegevoegd aan je mediabibliotheek",
"ValueSpecialEpisodeName": "Speciaal - {0}",
- "VersionNumber": "Versie {0}"
+ "VersionNumber": "Versie {0}",
+ "TaskDownloadMissingSubtitlesDescription": "Zoekt op het internet naar missende ondertitels gebaseerd op metadata configuratie.",
+ "TaskDownloadMissingSubtitles": "Download missende ondertitels",
+ "TaskRefreshChannelsDescription": "Vernieuwt informatie van internet kanalen.",
+ "TaskRefreshChannels": "Vernieuw Kanalen",
+ "TaskCleanTranscodeDescription": "Verwijder transcode bestanden ouder dan 1 dag.",
+ "TaskCleanLogs": "Log Folder Opschonen",
+ "TaskCleanTranscode": "Transcode Folder Opschonen",
+ "TaskUpdatePluginsDescription": "Download en installeert updates voor plugins waar automatisch updaten aan staat.",
+ "TaskUpdatePlugins": "Update Plugins",
+ "TaskRefreshPeopleDescription": "Update metadata for acteurs en regisseurs in de media bibliotheek.",
+ "TaskRefreshPeople": "Vernieuw Personen",
+ "TaskCleanLogsDescription": "Verwijdert log bestanden ouder dan {0} dagen.",
+ "TaskRefreshLibraryDescription": "Scant de media bibliotheek voor nieuwe bestanden en vernieuwt de metadata.",
+ "TaskRefreshLibrary": "Scan Media Bibliotheek",
+ "TaskRefreshChapterImagesDescription": "Maakt thumbnails aan voor videos met hoofdstukken.",
+ "TaskRefreshChapterImages": "Hoofdstukafbeeldingen Uitpakken",
+ "TaskCleanCacheDescription": "Verwijder gecachte bestanden die het systeem niet langer nodig heeft.",
+ "TaskCleanCache": "Cache Folder Opschonen",
+ "TasksChannelsCategory": "Internet Kanalen",
+ "TasksApplicationCategory": "Applicatie",
+ "TasksLibraryCategory": "Bibliotheek",
+ "TasksMaintenanceCategory": "Onderhoud"
}
diff --git a/Emby.Server.Implementations/Localization/Core/nn.json b/Emby.Server.Implementations/Localization/Core/nn.json
index ec6da213f..281cadac5 100644
--- a/Emby.Server.Implementations/Localization/Core/nn.json
+++ b/Emby.Server.Implementations/Localization/Core/nn.json
@@ -36,5 +36,25 @@
"Artists": "Artistar",
"Application": "Program",
"AppDeviceValues": "App: {0}, Einheit: {1}",
- "Albums": "Album"
+ "Albums": "Album",
+ "NotificationOptionServerRestartRequired": "Tenaren krev omstart",
+ "NotificationOptionPluginUpdateInstalled": "Tilleggsprogram-oppdatering vart installert",
+ "NotificationOptionPluginUninstalled": "Tilleggsprogram avinstallert",
+ "NotificationOptionPluginInstalled": "Tilleggsprogram installert",
+ "NotificationOptionPluginError": "Tilleggsprogram feila",
+ "NotificationOptionNewLibraryContent": "Nytt innhald er lagt til",
+ "NotificationOptionInstallationFailed": "Installasjonen feila",
+ "NotificationOptionCameraImageUploaded": "Kamerabilde vart lasta opp",
+ "NotificationOptionAudioPlaybackStopped": "Lydavspilling stoppa",
+ "NotificationOptionAudioPlayback": "Lydavspilling påbyrja",
+ "NotificationOptionApplicationUpdateInstalled": "Applikasjonsoppdatering er installert",
+ "NotificationOptionApplicationUpdateAvailable": "Applikasjonsoppdatering er tilgjengeleg",
+ "NewVersionIsAvailable": "Ein ny versjon av Jellyfin serveren er tilgjengeleg for nedlasting.",
+ "NameSeasonUnknown": "Ukjend sesong",
+ "NameSeasonNumber": "Sesong {0}",
+ "NameInstallFailed": "{0} Installasjonen feila",
+ "MusicVideos": "Musikkvideoar",
+ "Music": "Musikk",
+ "Movies": "Filmar",
+ "MixedContent": "Blanda innhald"
}
diff --git a/Emby.Server.Implementations/Localization/Core/pl.json b/Emby.Server.Implementations/Localization/Core/pl.json
index e72f1a262..bdc0d0169 100644
--- a/Emby.Server.Implementations/Localization/Core/pl.json
+++ b/Emby.Server.Implementations/Localization/Core/pl.json
@@ -76,7 +76,6 @@
"StartupEmbyServerIsLoading": "Trwa wczytywanie serwera Jellyfin. Spróbuj ponownie za chwilę.",
"SubtitleDownloadFailureForItem": "Pobieranie napisów dla {0} zakończone niepowodzeniem",
"SubtitleDownloadFailureFromForItem": "Nieudane pobieranie napisów z {0} dla {1}",
- "SubtitlesDownloadedForItem": "Pobrano napisy dla {0}",
"Sync": "Synchronizacja",
"System": "System",
"TvShows": "Seriale",
@@ -93,5 +92,27 @@
"UserStoppedPlayingItemWithValues": "{0} zakończył odtwarzanie {1} na {2}",
"ValueHasBeenAddedToLibrary": "{0} został dodany do biblioteki mediów",
"ValueSpecialEpisodeName": "Specjalne - {0}",
- "VersionNumber": "Wersja {0}"
+ "VersionNumber": "Wersja {0}",
+ "TaskDownloadMissingSubtitlesDescription": "Przeszukuje internet w poszukiwaniu brakujących napisów w oparciu o konfigurację metadanych.",
+ "TaskDownloadMissingSubtitles": "Pobierz brakujące napisy",
+ "TaskRefreshChannelsDescription": "Odświeża informacje o kanałach internetowych.",
+ "TaskRefreshChannels": "Odśwież kanały",
+ "TaskCleanTranscodeDescription": "Usuwa transkodowane pliki starsze niż 1 dzień.",
+ "TaskCleanTranscode": "Wyczyść folder transkodowania",
+ "TaskUpdatePluginsDescription": "Pobiera i instaluje aktualizacje dla pluginów które są skonfigurowane do automatycznej aktualizacji.",
+ "TaskUpdatePlugins": "Aktualizuj pluginy",
+ "TaskRefreshPeopleDescription": "Odświeża metadane o aktorów i reżyserów w Twojej bibliotece mediów.",
+ "TaskRefreshPeople": "Odśwież obsadę",
+ "TaskCleanLogsDescription": "Kasuje pliki logów starsze niż {0} dni.",
+ "TaskCleanLogs": "Wyczyść folder logów",
+ "TaskRefreshLibraryDescription": "Skanuje Twoją bibliotekę mediów dla nowych plików i odświeżenia metadanych.",
+ "TaskRefreshLibrary": "Skanuj bibliotekę mediów",
+ "TaskRefreshChapterImagesDescription": "Tworzy miniatury dla filmów posiadających rozdziały.",
+ "TaskRefreshChapterImages": "Wydobądź grafiki rozdziałów",
+ "TaskCleanCacheDescription": "Usuwa niepotrzebne i przestarzałe pliki cache.",
+ "TaskCleanCache": "Wyczyść folder Cache",
+ "TasksChannelsCategory": "Kanały internetowe",
+ "TasksApplicationCategory": "Aplikacja",
+ "TasksLibraryCategory": "Biblioteka",
+ "TasksMaintenanceCategory": "Konserwacja"
}
diff --git a/Emby.Server.Implementations/Localization/Core/pt-BR.json b/Emby.Server.Implementations/Localization/Core/pt-BR.json
index 41a389e3b..3a69b6d7a 100644
--- a/Emby.Server.Implementations/Localization/Core/pt-BR.json
+++ b/Emby.Server.Implementations/Localization/Core/pt-BR.json
@@ -76,7 +76,6 @@
"StartupEmbyServerIsLoading": "O Servidor Jellyfin está carregando. Por favor, tente novamente mais tarde.",
"SubtitleDownloadFailureForItem": "Download de legendas falhou para {0}",
"SubtitleDownloadFailureFromForItem": "Houve um problema ao baixar as legendas de {0} para {1}",
- "SubtitlesDownloadedForItem": "Legendas baixadas para {0}",
"Sync": "Sincronizar",
"System": "Sistema",
"TvShows": "Séries",
@@ -93,5 +92,27 @@
"UserStoppedPlayingItemWithValues": "{0} parou de reproduzir {1} em {2}",
"ValueHasBeenAddedToLibrary": "{0} foi adicionado à sua biblioteca de mídia",
"ValueSpecialEpisodeName": "Especial - {0}",
- "VersionNumber": "Versão {0}"
+ "VersionNumber": "Versão {0}",
+ "TaskDownloadMissingSubtitlesDescription": "Procurar na internet por legendas faltando baseado na configuração de metadados.",
+ "TaskDownloadMissingSubtitles": "Baixar legendas que estão faltando",
+ "TaskRefreshChannelsDescription": "Atualizar informação de canais da internet .",
+ "TaskRefreshChannels": "Atualizar Canais",
+ "TaskCleanTranscodeDescription": "Deletar arquivos de transcodificação com mais de um dia de criação.",
+ "TaskCleanTranscode": "Limpar pasta de transcodificação",
+ "TaskUpdatePluginsDescription": "Baixa e instala atualizações para plugins que estão configurados para atualizar automaticamente.",
+ "TaskUpdatePlugins": "Atualizar Plugins",
+ "TaskRefreshPeopleDescription": "Atualiza metadados para atores e diretores na sua biblioteca de mídia.",
+ "TaskRefreshPeople": "Atualizar pessoas",
+ "TaskCleanLogsDescription": "Deletar arquivos temporários com mais de {0} dias.",
+ "TaskCleanLogs": "Limpar pasta de logs",
+ "TaskRefreshLibraryDescription": "Escaneie a sua biblioteca de mídia para arquivos novos e atualize os metadados.",
+ "TaskRefreshLibrary": "Escanear a Biblioteca de Mídia",
+ "TaskRefreshChapterImagesDescription": "Criar miniaturas para vídeos que tem capítulos.",
+ "TaskRefreshChapterImages": "Extrair imagens dos capítulos",
+ "TaskCleanCacheDescription": "Deletar arquivos temporários que não são mais necessários para o sistema.",
+ "TaskCleanCache": "Limpar Arquivos Temporários",
+ "TasksChannelsCategory": "Canais da Internet",
+ "TasksApplicationCategory": "Aplicativo",
+ "TasksLibraryCategory": "Biblioteca",
+ "TasksMaintenanceCategory": "Manutenção"
}
diff --git a/Emby.Server.Implementations/Localization/Core/pt-PT.json b/Emby.Server.Implementations/Localization/Core/pt-PT.json
index b12d391c1..c1fb65743 100644
--- a/Emby.Server.Implementations/Localization/Core/pt-PT.json
+++ b/Emby.Server.Implementations/Localization/Core/pt-PT.json
@@ -26,7 +26,7 @@
"HeaderLiveTV": "TV em Direto",
"HeaderNextUp": "A Seguir",
"HeaderRecordingGroups": "Grupos de Gravação",
- "HomeVideos": "Home videos",
+ "HomeVideos": "Videos caseiros",
"Inherit": "Herdar",
"ItemAddedWithName": "{0} foi adicionado à biblioteca",
"ItemRemovedWithName": "{0} foi removido da biblioteca",
@@ -76,7 +76,6 @@
"StartupEmbyServerIsLoading": "O servidor Jellyfin está a iniciar. Tente novamente mais tarde.",
"SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}",
"SubtitleDownloadFailureFromForItem": "Falha na transferência de legendas a partir de {0} para {1}",
- "SubtitlesDownloadedForItem": "Transferidas legendas para {0}",
"Sync": "Sincronização",
"System": "Sistema",
"TvShows": "Programas TV",
@@ -93,5 +92,27 @@
"UserStoppedPlayingItemWithValues": "{0} terminou a reprodução de {1} em {2}",
"ValueHasBeenAddedToLibrary": "{0} foi adicionado à sua biblioteca multimédia",
"ValueSpecialEpisodeName": "Especial - {0}",
- "VersionNumber": "Versão {0}"
+ "VersionNumber": "Versão {0}",
+ "TaskDownloadMissingSubtitlesDescription": "Procurar na internet por legendas em falta baseado na configuração de metadados.",
+ "TaskDownloadMissingSubtitles": "Fazer download de legendas em falta",
+ "TaskRefreshChannelsDescription": "Atualizar informação sobre canais da Internet.",
+ "TaskRefreshChannels": "Atualizar Canais",
+ "TaskCleanTranscodeDescription": "Apagar ficheiros de transcode com mais de um dia.",
+ "TaskCleanTranscode": "Limpar a Diretoria de Transcode",
+ "TaskUpdatePluginsDescription": "Faz o download e instala updates para os plugins que estão configurados para atualizar automaticamente.",
+ "TaskUpdatePlugins": "Atualizar Plugins",
+ "TaskRefreshPeopleDescription": "Atualizar metadados para atores e diretores na biblioteca.",
+ "TaskRefreshPeople": "Atualizar Pessoas",
+ "TaskCleanLogsDescription": "Apagar ficheiros de log que têm mais de {0} dias.",
+ "TaskCleanLogs": "Limpar a Diretoria de Logs",
+ "TaskRefreshLibraryDescription": "Scannear a biblioteca de música para novos ficheiros e atualizar os metadados.",
+ "TaskRefreshLibrary": "Scannear Biblioteca de Música",
+ "TaskRefreshChapterImagesDescription": "Criar thumbnails para os vídeos que têm capítulos.",
+ "TaskRefreshChapterImages": "Extrair Imagens dos Capítulos",
+ "TaskCleanCacheDescription": "Apagar ficheiros em cache que já não são necessários.",
+ "TaskCleanCache": "Limpar Cache",
+ "TasksChannelsCategory": "Canais da Internet",
+ "TasksApplicationCategory": "Aplicação",
+ "TasksLibraryCategory": "Biblioteca",
+ "TasksMaintenanceCategory": "Manutenção"
}
diff --git a/Emby.Server.Implementations/Localization/Core/pt.json b/Emby.Server.Implementations/Localization/Core/pt.json
index 9ee3c37a8..25c5b9053 100644
--- a/Emby.Server.Implementations/Localization/Core/pt.json
+++ b/Emby.Server.Implementations/Localization/Core/pt.json
@@ -31,7 +31,6 @@
"User": "Utilizador",
"TvShows": "Séries",
"System": "Sistema",
- "SubtitlesDownloadedForItem": "Legendas transferidas para {0}",
"SubtitleDownloadFailureFromForItem": "Falha na transferência de legendas de {0} para {1}",
"StartupEmbyServerIsLoading": "O servidor Jellyfin está a iniciar. Tente novamente dentro de momentos.",
"ServerNameNeedsToBeRestarted": "{0} necessita ser reiniciado",
@@ -92,5 +91,17 @@
"CameraImageUploadedFrom": "Uma nova imagem da câmara foi enviada a partir de {0}",
"AuthenticationSucceededWithUserName": "{0} autenticado com sucesso",
"Application": "Aplicação",
- "AppDeviceValues": "Aplicação {0}, Dispositivo: {1}"
+ "AppDeviceValues": "Aplicação {0}, Dispositivo: {1}",
+ "TaskCleanCache": "Limpar Diretório de Cache",
+ "TasksApplicationCategory": "Aplicação",
+ "TasksLibraryCategory": "Biblioteca",
+ "TasksMaintenanceCategory": "Manutenção",
+ "TaskRefreshChannels": "Atualizar Canais",
+ "TaskUpdatePlugins": "Atualizar Plugins",
+ "TaskCleanLogsDescription": "Deletar arquivos de log que existe a mais de {0} dias.",
+ "TaskCleanLogs": "Limpar diretório de log",
+ "TaskRefreshLibrary": "Escanear biblioteca de mídias",
+ "TaskRefreshChapterImagesDescription": "Criar miniaturas para videos que tem capítulos.",
+ "TaskCleanCacheDescription": "Deletar arquivos de cache que não são mais usados pelo sistema.",
+ "TasksChannelsCategory": "Canais de Internet"
}
diff --git a/Emby.Server.Implementations/Localization/Core/ro.json b/Emby.Server.Implementations/Localization/Core/ro.json
index 71bffffc6..699dd26da 100644
--- a/Emby.Server.Implementations/Localization/Core/ro.json
+++ b/Emby.Server.Implementations/Localization/Core/ro.json
@@ -17,7 +17,6 @@
"TvShows": "Spectacole TV",
"System": "Sistem",
"Sync": "Sincronizare",
- "SubtitlesDownloadedForItem": "Subtitrari descarcate pentru {0}",
"SubtitleDownloadFailureFromForItem": "Subtitrările nu au putut fi descărcate de la {0} pentru {1}",
"StartupEmbyServerIsLoading": "Se încarcă serverul Jellyfin. Încercați din nou în scurt timp.",
"Songs": "Melodii",
@@ -92,5 +91,27 @@
"Artists": "Artiști",
"Application": "Aplicație",
"AppDeviceValues": "Aplicație: {0}, Dispozitiv: {1}",
- "Albums": "Albume"
+ "Albums": "Albume",
+ "TaskDownloadMissingSubtitlesDescription": "Caută pe internet subtitrările lipsă pe baza configurației metadatelor.",
+ "TaskDownloadMissingSubtitles": "Descarcă subtitrările lipsă",
+ "TaskRefreshChannelsDescription": "Actualizează informațiile despre canalul de internet.",
+ "TaskRefreshChannels": "Actualizează canale",
+ "TaskCleanTranscodeDescription": "Șterge fișierele de transcodare mai vechi de o zi.",
+ "TaskCleanTranscode": "Curățați directorul de transcodare",
+ "TaskUpdatePluginsDescription": "Descarcă și instalează actualizări pentru pluginuri care sunt configurate să se actualizeze automat.",
+ "TaskUpdatePlugins": "Actualizați plugin-uri",
+ "TaskRefreshPeopleDescription": "Actualizează metadatele pentru actori și regizori din biblioteca media.",
+ "TaskRefreshPeople": "Actualizează oamenii",
+ "TaskCleanLogsDescription": "Șterge fișierele jurnal care au mai mult de {0} zile.",
+ "TaskCleanLogs": "Curățare director jurnal",
+ "TaskRefreshLibraryDescription": "Scanează biblioteca media pentru fișiere noi și reîmprospătează metadatele.",
+ "TaskRefreshLibrary": "Scanează Biblioteca Media",
+ "TaskRefreshChapterImagesDescription": "Creează miniaturi pentru videourile care au capitole.",
+ "TaskRefreshChapterImages": "Extrage Imaginile de Capitol",
+ "TaskCleanCacheDescription": "Șterge fișierele cache care nu mai sunt necesare sistemului.",
+ "TaskCleanCache": "Curățați directorul cache",
+ "TasksChannelsCategory": "Canale de pe Internet",
+ "TasksApplicationCategory": "Aplicație",
+ "TasksLibraryCategory": "Librărie",
+ "TasksMaintenanceCategory": "Mentenanță"
}
diff --git a/Emby.Server.Implementations/Localization/Core/ru.json b/Emby.Server.Implementations/Localization/Core/ru.json
index 7cf957a94..71ee6446c 100644
--- a/Emby.Server.Implementations/Localization/Core/ru.json
+++ b/Emby.Server.Implementations/Localization/Core/ru.json
@@ -9,8 +9,8 @@
"Channels": "Каналы",
"ChapterNameValue": "Сцена {0}",
"Collections": "Коллекции",
- "DeviceOfflineWithName": "{0} - подкл. разъ-но",
- "DeviceOnlineWithName": "{0} - подкл. уст-но",
+ "DeviceOfflineWithName": "{0} - отключено",
+ "DeviceOnlineWithName": "{0} - подключено",
"FailedLoginAttemptWithUserName": "{0} - попытка входа неудачна",
"Favorites": "Избранное",
"Folders": "Папки",
@@ -26,30 +26,30 @@
"HeaderLiveTV": "Эфир",
"HeaderNextUp": "Очередное",
"HeaderRecordingGroups": "Группы записей",
- "HomeVideos": "Дом. видео",
+ "HomeVideos": "Домашнее видео",
"Inherit": "Наследуемое",
"ItemAddedWithName": "{0} - добавлено в медиатеку",
"ItemRemovedWithName": "{0} - изъято из медиатеки",
"LabelIpAddressValue": "IP-адрес: {0}",
"LabelRunningTimeValue": "Длительность: {0}",
- "Latest": "Новейшее",
+ "Latest": "Последнее",
"MessageApplicationUpdated": "Jellyfin Server был обновлён",
"MessageApplicationUpdatedTo": "Jellyfin Server был обновлён до {0}",
- "MessageNamedServerConfigurationUpdatedWithValue": "Конфиг-ия сервера (раздел {0}) была обновлена",
- "MessageServerConfigurationUpdated": "Конфиг-ия сервера была обновлена",
+ "MessageNamedServerConfigurationUpdatedWithValue": "Конфигурация сервера (раздел {0}) была обновлена",
+ "MessageServerConfigurationUpdated": "Конфигурация сервера была обновлена",
"MixedContent": "Смешанное содержимое",
"Movies": "Кино",
"Music": "Музыка",
- "MusicVideos": "Муз. видео",
+ "MusicVideos": "Музыкальные клипы",
"NameInstallFailed": "Установка {0} неудачна",
"NameSeasonNumber": "Сезон {0}",
"NameSeasonUnknown": "Сезон неопознан",
"NewVersionIsAvailable": "Новая версия Jellyfin Server доступна для загрузки.",
"NotificationOptionApplicationUpdateAvailable": "Имеется обновление приложения",
"NotificationOptionApplicationUpdateInstalled": "Обновление приложения установлено",
- "NotificationOptionAudioPlayback": "Воспр-ие аудио зап-но",
- "NotificationOptionAudioPlaybackStopped": "Восп-ие аудио ост-но",
- "NotificationOptionCameraImageUploaded": "Произведена выкладка отснятого с камеры",
+ "NotificationOptionAudioPlayback": "Воспроизведение аудио запущено",
+ "NotificationOptionAudioPlaybackStopped": "Воспроизведение аудио остановлено",
+ "NotificationOptionCameraImageUploaded": "Изображения с камеры загружены",
"NotificationOptionInstallationFailed": "Сбой установки",
"NotificationOptionNewLibraryContent": "Новое содержание добавлено",
"NotificationOptionPluginError": "Сбой плагина",
@@ -59,8 +59,8 @@
"NotificationOptionServerRestartRequired": "Требуется перезапуск сервера",
"NotificationOptionTaskFailed": "Сбой назначенной задачи",
"NotificationOptionUserLockedOut": "Пользователь заблокирован",
- "NotificationOptionVideoPlayback": "Воспр-ие видео зап-но",
- "NotificationOptionVideoPlaybackStopped": "Восп-ие видео ост-но",
+ "NotificationOptionVideoPlayback": "Воспроизведение видео запущено",
+ "NotificationOptionVideoPlaybackStopped": "Воспроизведение видео остановлено",
"Photos": "Фото",
"Playlists": "Плей-листы",
"Plugin": "Плагин",
@@ -76,22 +76,43 @@
"StartupEmbyServerIsLoading": "Jellyfin Server загружается. Повторите попытку в ближайшее время.",
"SubtitleDownloadFailureForItem": "Субтитры к {0} не удалось загрузить",
"SubtitleDownloadFailureFromForItem": "Субтитры к {1} не удалось загрузить с {0}",
- "SubtitlesDownloadedForItem": "Субтитры к {0} загружены",
- "Sync": "Синхро",
+ "Sync": "Синхронизация",
"System": "Система",
"TvShows": "ТВ",
- "User": "Польз-ль",
+ "User": "Пользователь",
"UserCreatedWithName": "Пользователь {0} был создан",
"UserDeletedWithName": "Пользователь {0} был удалён",
"UserDownloadingItemWithValues": "{0} загружает {1}",
"UserLockedOutWithName": "Пользователь {0} был заблокирован",
- "UserOfflineFromDevice": "{0} - подкл. с {1} разъ-но",
- "UserOnlineFromDevice": "{0} - подкл. с {1} уст-но",
- "UserPasswordChangedWithName": "Пароль польз-ля {0} был изменён",
- "UserPolicyUpdatedWithName": "Польз-ие политики {0} были обновлены",
- "UserStartedPlayingItemWithValues": "{0} - воспр. «{1}» на {2}",
- "UserStoppedPlayingItemWithValues": "{0} - воспр. «{1}» ост-но на {2}",
+ "UserOfflineFromDevice": "{0} отключился с {1}",
+ "UserOnlineFromDevice": "{0} подключился с {1}",
+ "UserPasswordChangedWithName": "Пароль пользователя {0} был изменён",
+ "UserPolicyUpdatedWithName": "Политики пользователя {0} были обновлены",
+ "UserStartedPlayingItemWithValues": "{0} - воспроизведение «{1}» на {2}",
+ "UserStoppedPlayingItemWithValues": "{0} - воспроизведение остановлено «{1}» на {2}",
"ValueHasBeenAddedToLibrary": "{0} (добавлено в медиатеку)",
- "ValueSpecialEpisodeName": "Спецэпизод - {0}",
- "VersionNumber": "Версия {0}"
+ "ValueSpecialEpisodeName": "Специальный эпизод - {0}",
+ "VersionNumber": "Версия {0}",
+ "TaskDownloadMissingSubtitles": "Загрузка отсутствующих субтитров",
+ "TaskRefreshChannels": "Обновление каналов",
+ "TaskCleanTranscode": "Очистка каталога перекодировки",
+ "TaskUpdatePlugins": "Обновление плагинов",
+ "TaskRefreshPeople": "Обновление метаданных людей",
+ "TaskCleanLogs": "Очистка каталога журналов",
+ "TaskRefreshLibrary": "Сканирование медиатеки",
+ "TaskRefreshChapterImages": "Извлечение изображений сцен",
+ "TaskCleanCache": "Очистка каталога кеша",
+ "TasksChannelsCategory": "Интернет-каналы",
+ "TasksApplicationCategory": "Приложение",
+ "TasksLibraryCategory": "Медиатека",
+ "TasksMaintenanceCategory": "Обслуживание",
+ "TaskDownloadMissingSubtitlesDescription": "Выполняется поиск в Интернете отсутствующих субтитров на основе конфигурации метаданных.",
+ "TaskRefreshChannelsDescription": "Обновляются данные интернет-каналов.",
+ "TaskCleanTranscodeDescription": "Удаляются файлы перекодировки старше одного дня.",
+ "TaskUpdatePluginsDescription": "Загружаются и устанавливаются обновления для плагинов, у которых включено автоматическое обновление.",
+ "TaskRefreshPeopleDescription": "Обновляются метаданные актеров и режиссёров в медиатеке.",
+ "TaskCleanLogsDescription": "Удаляются файлы журнала, возраст которых превышает {0} дн(я/ей).",
+ "TaskRefreshLibraryDescription": "Сканируется медиатека на новые файлы и обновляются метаданные.",
+ "TaskRefreshChapterImagesDescription": "Создаются эскизы для видео, которые содержат сцены.",
+ "TaskCleanCacheDescription": "Удаляются файлы кэша, которые больше не нужны системе."
}
diff --git a/Emby.Server.Implementations/Localization/Core/sk.json b/Emby.Server.Implementations/Localization/Core/sk.json
index 1988bda52..0ee652637 100644
--- a/Emby.Server.Implementations/Localization/Core/sk.json
+++ b/Emby.Server.Implementations/Localization/Core/sk.json
@@ -76,7 +76,6 @@
"StartupEmbyServerIsLoading": "Jellyfin Server sa spúšťa. Prosím, skúste to o chvíľu znova.",
"SubtitleDownloadFailureForItem": "Sťahovanie titulkov pre {0} zlyhalo",
"SubtitleDownloadFailureFromForItem": "Sťahovanie titulkov z {0} pre {1} zlyhalo",
- "SubtitlesDownloadedForItem": "Titulky pre {0} stiahnuté",
"Sync": "Synchronizácia",
"System": "Systém",
"TvShows": "TV seriály",
@@ -93,5 +92,27 @@
"UserStoppedPlayingItemWithValues": "{0} ukončil prehrávanie {1} na {2}",
"ValueHasBeenAddedToLibrary": "{0} bol pridané do vašej knižnice médií",
"ValueSpecialEpisodeName": "Špeciál - {0}",
- "VersionNumber": "Verzia {0}"
+ "VersionNumber": "Verzia {0}",
+ "TaskDownloadMissingSubtitlesDescription": "Vyhľadá na internete chýbajúce titulky podľa toho, ako sú nakonfigurované metadáta.",
+ "TaskDownloadMissingSubtitles": "Stiahnuť chýbajúce titulky",
+ "TaskRefreshChannelsDescription": "Obnoví informácie o internetových kanáloch.",
+ "TaskRefreshChannels": "Obnoviť kanály",
+ "TaskCleanTranscodeDescription": "Vymaže súbory transkódovania, ktoré sú staršie ako jeden deň.",
+ "TaskCleanTranscode": "Vyčistiť priečinok pre transkódovanie",
+ "TaskUpdatePluginsDescription": "Stiahne a nainštaluje aktualizácie pre zásuvné moduly, ktoré sú nastavené tak, aby sa aktualizovali automaticky.",
+ "TaskUpdatePlugins": "Aktualizovať zásuvné moduly",
+ "TaskRefreshPeopleDescription": "Aktualizuje metadáta pre hercov a režisérov vo vašej mediálnej knižnici.",
+ "TaskRefreshPeople": "Obnoviť osoby",
+ "TaskCleanLogsDescription": "Vymaže log súbory, ktoré su staršie ako {0} deň/dni/dní.",
+ "TaskCleanLogs": "Vyčistiť priečinok s logmi",
+ "TaskRefreshLibraryDescription": "Hľadá vo vašej mediálnej knižnici nové súbory a obnovuje metadáta.",
+ "TaskRefreshLibrary": "Prehľadávať knižnicu medií",
+ "TaskRefreshChapterImagesDescription": "Vytvorí náhľady pre videá, ktoré majú kapitoly.",
+ "TaskRefreshChapterImages": "Extrahovať obrázky kapitol",
+ "TaskCleanCacheDescription": "Vymaže cache súbory, ktoré nie sú už potrebné pre systém.",
+ "TaskCleanCache": "Vyčistiť Cache priečinok",
+ "TasksChannelsCategory": "Internetové kanály",
+ "TasksApplicationCategory": "Aplikácia",
+ "TasksLibraryCategory": "Knižnica",
+ "TasksMaintenanceCategory": "Údržba"
}
diff --git a/Emby.Server.Implementations/Localization/Core/sl-SI.json b/Emby.Server.Implementations/Localization/Core/sl-SI.json
index b43cfbb74..b60dd33bd 100644
--- a/Emby.Server.Implementations/Localization/Core/sl-SI.json
+++ b/Emby.Server.Implementations/Localization/Core/sl-SI.json
@@ -40,7 +40,7 @@
"MixedContent": "Razne vsebine",
"Movies": "Filmi",
"Music": "Glasba",
- "MusicVideos": "Glasbeni posnetki",
+ "MusicVideos": "Glasbeni videi",
"NameInstallFailed": "{0} namestitev neuspešna",
"NameSeasonNumber": "Sezona {0}",
"NameSeasonUnknown": "Season neznana",
@@ -76,7 +76,6 @@
"StartupEmbyServerIsLoading": "Jellyfin Server se nalaga. Poskusi ponovno kasneje.",
"SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}",
"SubtitleDownloadFailureFromForItem": "Neuspešen prenos podnapisov iz {0} za {1}",
- "SubtitlesDownloadedForItem": "Podnapisi preneseni za {0}",
"Sync": "Sinhroniziraj",
"System": "System",
"TvShows": "TV serije",
diff --git a/Emby.Server.Implementations/Localization/Core/sr.json b/Emby.Server.Implementations/Localization/Core/sr.json
index da0088991..5f3cbb1c8 100644
--- a/Emby.Server.Implementations/Localization/Core/sr.json
+++ b/Emby.Server.Implementations/Localization/Core/sr.json
@@ -17,7 +17,6 @@
"TvShows": "ТВ серије",
"System": "Систем",
"Sync": "Усклади",
- "SubtitlesDownloadedForItem": "Титлови преузети за {0}",
"SubtitleDownloadFailureFromForItem": "Неуспело преузимање титлова за {1} са {0}",
"StartupEmbyServerIsLoading": "Џелифин сервер се подиже. Покушајте поново убрзо.",
"Songs": "Песме",
@@ -82,7 +81,7 @@
"Favorites": "Омиљено",
"FailedLoginAttemptWithUserName": "Неуспела пријава са {0}",
"DeviceOnlineWithName": "{0} се повезао",
- "DeviceOfflineWithName": "{0} се одвезао",
+ "DeviceOfflineWithName": "{0} је прекинуо везу",
"Collections": "Колекције",
"ChapterNameValue": "Поглавље {0}",
"Channels": "Канали",
@@ -92,5 +91,27 @@
"Artists": "Извођач",
"Application": "Апликација",
"AppDeviceValues": "Апл: {0}, уређај: {1}",
- "Albums": "Албуми"
+ "Albums": "Албуми",
+ "TaskDownloadMissingSubtitlesDescription": "Претражује интернет за недостајуће титлове на основу конфигурације метаподатака.",
+ "TaskDownloadMissingSubtitles": "Преузмите недостајуће титлове",
+ "TaskRefreshChannelsDescription": "Освежава информације о интернет каналу.",
+ "TaskRefreshChannels": "Освежи канале",
+ "TaskCleanTranscodeDescription": "Брише датотеке за кодирање старије од једног дана.",
+ "TaskCleanTranscode": "Очистите директоријум преноса",
+ "TaskUpdatePluginsDescription": "Преузима и инсталира исправке за додатке који су конфигурисани за аутоматско ажурирање.",
+ "TaskUpdatePlugins": "Ажурирајте додатке",
+ "TaskRefreshPeopleDescription": "Ажурира метаподатке за глумце и редитеље у вашој медијској библиотеци.",
+ "TaskRefreshPeople": "Освежите људе",
+ "TaskCleanLogsDescription": "Брише логове старије од {0} дана.",
+ "TaskCleanLogs": "Очистите директоријум логова",
+ "TaskRefreshLibraryDescription": "Скенира вашу медијску библиотеку за нове датотеке и освежава метаподатке.",
+ "TaskRefreshLibrary": "Скенирај Библиотеку Медија",
+ "TaskRefreshChapterImagesDescription": "Ствара сличице за видео записе који имају поглавља.",
+ "TaskRefreshChapterImages": "Издвоји слике из поглавља",
+ "TaskCleanCacheDescription": "Брише Кеш фајлове који више нису потребни систему.",
+ "TaskCleanCache": "Очистите Кеш Директоријум",
+ "TasksChannelsCategory": "Интернет канали",
+ "TasksApplicationCategory": "Апликација",
+ "TasksLibraryCategory": "Библиотека",
+ "TasksMaintenanceCategory": "Одржавање"
}
diff --git a/Emby.Server.Implementations/Localization/Core/sv.json b/Emby.Server.Implementations/Localization/Core/sv.json
index b2934545d..c8662b2ca 100644
--- a/Emby.Server.Implementations/Localization/Core/sv.json
+++ b/Emby.Server.Implementations/Localization/Core/sv.json
@@ -9,7 +9,7 @@
"Channels": "Kanaler",
"ChapterNameValue": "Kapitel {0}",
"Collections": "Samlingar",
- "DeviceOfflineWithName": "{0} har tappat anslutningen",
+ "DeviceOfflineWithName": "{0} har kopplat från",
"DeviceOnlineWithName": "{0} är ansluten",
"FailedLoginAttemptWithUserName": "Misslyckat inloggningsförsök från {0}",
"Favorites": "Favoriter",
@@ -50,7 +50,7 @@
"NotificationOptionAudioPlayback": "Ljuduppspelning har påbörjats",
"NotificationOptionAudioPlaybackStopped": "Ljuduppspelning stoppades",
"NotificationOptionCameraImageUploaded": "Kamerabild har laddats upp",
- "NotificationOptionInstallationFailed": "Fel vid installation",
+ "NotificationOptionInstallationFailed": "Installationen misslyckades",
"NotificationOptionNewLibraryContent": "Nytt innehåll har lagts till",
"NotificationOptionPluginError": "Fel uppstod med tillägget",
"NotificationOptionPluginInstalled": "Tillägg har installerats",
@@ -76,7 +76,6 @@
"StartupEmbyServerIsLoading": "Jellyfin Server arbetar. Pröva igen snart.",
"SubtitleDownloadFailureForItem": "Nerladdning av undertexter för {0} misslyckades",
"SubtitleDownloadFailureFromForItem": "Undertexter kunde inte laddas ner från {0} för {1}",
- "SubtitlesDownloadedForItem": "Undertexter har laddats ner till {0}",
"Sync": "Synk",
"System": "System",
"TvShows": "TV-serier",
@@ -93,5 +92,27 @@
"UserStoppedPlayingItemWithValues": "{0} har avslutat uppspelningen av {1} på {2}",
"ValueHasBeenAddedToLibrary": "{0} har lagts till i ditt mediebibliotek",
"ValueSpecialEpisodeName": "Specialavsnitt - {0}",
- "VersionNumber": "Version {0}"
+ "VersionNumber": "Version {0}",
+ "TaskDownloadMissingSubtitlesDescription": "Söker på internet efter saknade undertexter baserad på metadatas konfiguration.",
+ "TaskDownloadMissingSubtitles": "Ladda ned saknade undertexter",
+ "TaskRefreshChannelsDescription": "Uppdaterar information för internetkanaler.",
+ "TaskRefreshChannels": "Uppdatera kanaler",
+ "TaskCleanTranscodeDescription": "Raderar transkodningsfiler som är mer än en dag gamla.",
+ "TaskCleanTranscode": "Töm transkodningskatalog",
+ "TaskUpdatePluginsDescription": "Laddar ned och installerar uppdateringar till insticksprogram som är konfigurerade att uppdateras automatiskt.",
+ "TaskUpdatePlugins": "Uppdatera insticksprogram",
+ "TaskRefreshPeopleDescription": "Uppdaterar metadata för skådespelare och regissörer i ditt mediabibliotek.",
+ "TaskCleanLogsDescription": "Raderar loggfiler som är mer än {0} dagar gamla.",
+ "TaskCleanLogs": "Töm loggkatalog",
+ "TaskRefreshLibraryDescription": "Söker igenom ditt mediabibliotek efter nya filer och förnyar metadata.",
+ "TaskRefreshLibrary": "Genomsök mediabibliotek",
+ "TaskRefreshChapterImagesDescription": "Skapa miniatyrbilder för videor med kapitel.",
+ "TaskRefreshChapterImages": "Extrahera kapitelbilder",
+ "TaskCleanCacheDescription": "Radera cachade filer som systemet inte längre behöver.",
+ "TaskCleanCache": "Rensa cachekatalog",
+ "TasksChannelsCategory": "Internetkanaler",
+ "TasksApplicationCategory": "Applikation",
+ "TasksLibraryCategory": "Bibliotek",
+ "TasksMaintenanceCategory": "Underhåll",
+ "TaskRefreshPeople": "Uppdatera Personer"
}
diff --git a/Emby.Server.Implementations/Localization/Core/tr.json b/Emby.Server.Implementations/Localization/Core/tr.json
index eb1c2623f..3cf3482eb 100644
--- a/Emby.Server.Implementations/Localization/Core/tr.json
+++ b/Emby.Server.Implementations/Localization/Core/tr.json
@@ -34,8 +34,8 @@
"LabelRunningTimeValue": "Çalışma süresi: {0}",
"Latest": "En son",
"MessageApplicationUpdated": "Jellyfin Sunucusu güncellendi",
- "MessageApplicationUpdatedTo": "Jellyfin Sunucusu {0} olarak güncellendi",
- "MessageNamedServerConfigurationUpdatedWithValue": "Sunucu ayarları kısım {0} güncellendi",
+ "MessageApplicationUpdatedTo": "Jellyfin Sunucusu {0} sürümüne güncellendi",
+ "MessageNamedServerConfigurationUpdatedWithValue": "Sunucu ayar kısmı {0} güncellendi",
"MessageServerConfigurationUpdated": "Sunucu ayarları güncellendi",
"MixedContent": "Karışık içerik",
"Movies": "Filmler",
@@ -50,7 +50,7 @@
"NotificationOptionAudioPlayback": "Ses çalma başladı",
"NotificationOptionAudioPlaybackStopped": "Ses çalma durduruldu",
"NotificationOptionCameraImageUploaded": "Kamera fotoğrafı yüklendi",
- "NotificationOptionInstallationFailed": "Yükleme başarısız oldu",
+ "NotificationOptionInstallationFailed": "Kurulum hatası",
"NotificationOptionNewLibraryContent": "Yeni içerik eklendi",
"NotificationOptionPluginError": "Eklenti hatası",
"NotificationOptionPluginInstalled": "Eklenti yüklendi",
@@ -76,7 +76,6 @@
"StartupEmbyServerIsLoading": "Jellyfin Sunucusu yükleniyor. Lütfen kısa süre sonra tekrar deneyin.",
"SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}",
"SubtitleDownloadFailureFromForItem": "{1} için alt yazılar {0} 'dan indirilemedi",
- "SubtitlesDownloadedForItem": "{0} için altyazılar indirildi",
"Sync": "Eşitle",
"System": "Sistem",
"TvShows": "Diziler",
@@ -93,5 +92,27 @@
"UserStoppedPlayingItemWithValues": "{0}, {2} cihazında {1} izlemeyi bitirdi",
"ValueHasBeenAddedToLibrary": "Medya kitaplığınıza {0} eklendi",
"ValueSpecialEpisodeName": "Özel - {0}",
- "VersionNumber": "Versiyon {0}"
+ "VersionNumber": "Versiyon {0}",
+ "TaskCleanCache": "Geçici dosya klasörünü temizle",
+ "TasksChannelsCategory": "İnternet kanalları",
+ "TasksApplicationCategory": "Uygulama",
+ "TasksLibraryCategory": "Kütüphane",
+ "TasksMaintenanceCategory": "Onarım",
+ "TaskRefreshPeopleDescription": "Medya kütüphanenizdeki videoların oyuncu ve yönetmen bilgilerini günceller.",
+ "TaskDownloadMissingSubtitlesDescription": "Metadata ayarlarını baz alarak eksik altyazıları internette arar.",
+ "TaskDownloadMissingSubtitles": "Eksik altyazıları indir",
+ "TaskRefreshChannelsDescription": "Internet kanal bilgilerini yenile.",
+ "TaskRefreshChannels": "Kanalları Yenile",
+ "TaskCleanTranscodeDescription": "Bir günü dolmuş dönüştürme bilgisi içeren dosyaları siler.",
+ "TaskCleanTranscode": "Dönüşüm Dizinini Temizle",
+ "TaskUpdatePluginsDescription": "Otomatik güncellenmeye ayarlanmış eklentilerin güncellemelerini indirir ve kurar.",
+ "TaskUpdatePlugins": "Eklentileri Güncelle",
+ "TaskRefreshPeople": "Kullanıcıları Yenile",
+ "TaskCleanLogsDescription": "{0} günden eski log dosyalarını siler.",
+ "TaskCleanLogs": "Log Dizinini Temizle",
+ "TaskRefreshLibraryDescription": "Medya kütüphanenize eklenen yeni dosyaları arar ve bilgileri yeniler.",
+ "TaskRefreshLibrary": "Medya Kütüphanesini Tara",
+ "TaskRefreshChapterImagesDescription": "Sahnelere ayrılmış videolar için küçük resimler oluştur.",
+ "TaskRefreshChapterImages": "Bölüm Resimlerini Çıkar",
+ "TaskCleanCacheDescription": "Sistem tarafından artık ihtiyaç duyulmayan önbellek dosyalarını siler."
}
diff --git a/Emby.Server.Implementations/Localization/Core/ur_PK.json b/Emby.Server.Implementations/Localization/Core/ur_PK.json
new file mode 100644
index 000000000..9a5874e29
--- /dev/null
+++ b/Emby.Server.Implementations/Localization/Core/ur_PK.json
@@ -0,0 +1,117 @@
+{
+ "HeaderFavoriteAlbums": "پسندیدہ البمز",
+ "HeaderNextUp": "اگلا",
+ "HeaderFavoriteArtists": "پسندیدہ فنکار",
+ "HeaderAlbumArtists": "البم کے فنکار",
+ "Movies": "فلمیں",
+ "HeaderFavoriteEpisodes": "پسندیدہ اقساط",
+ "Collections": "مجموعہ",
+ "Folders": "فولڈرز",
+ "HeaderLiveTV": "براہ راست ٹی وی",
+ "Channels": "چینل",
+ "HeaderContinueWatching": "دیکھنا جاری رکھیں",
+ "Playlists": "پلے لسٹس",
+ "ValueSpecialEpisodeName": "خاص - {0}",
+ "Shows": "شوز",
+ "Genres": "انواع",
+ "Artists": "فنکار",
+ "Sync": "مطابقت",
+ "Photos": "تصوریں",
+ "Albums": "البم",
+ "Favorites": "پسندیدہ",
+ "Songs": "گانے",
+ "Books": "کتابیں",
+ "HeaderFavoriteSongs": "پسندیدہ گانے",
+ "HeaderFavoriteShows": "پسندیدہ شوز",
+ "TaskDownloadMissingSubtitlesDescription": "میٹا ڈیٹا کی تشکیل پر مبنی ذیلی عنوانات کے غائب عنوانات انٹرنیٹ پے تلاش کرتا ہے۔",
+ "TaskDownloadMissingSubtitles": "غائب سب ٹائٹلز ڈاؤن لوڈ کریں",
+ "TaskRefreshChannelsDescription": "انٹرنیٹ چینل کی معلومات کو تازہ دم کرتا ہے۔",
+ "TaskRefreshChannels": "چینلز ریفریش کریں",
+ "TaskCleanTranscodeDescription": "ایک دن سے زیادہ پرانی ٹرانسکوڈ فائلوں کو حذف کرتا ہے۔",
+ "TaskCleanTranscode": "ٹرانس کوڈ ڈائرکٹری صاف کریں",
+ "TaskUpdatePluginsDescription": "پلگ انز کے لئے اپ ڈیٹس ڈاؤن لوڈ اور انسٹال کرتے ہیں جو خود بخود اپ ڈیٹ کرنے کیلئے تشکیل شدہ ہیں۔",
+ "TaskUpdatePlugins": "پلگ انز کو اپ ڈیٹ کریں",
+ "TaskRefreshPeopleDescription": "آپ کی میڈیا لائبریری میں اداکاروں اور ہدایت کاروں کے لئے میٹا ڈیٹا کی تازہ کاری۔",
+ "TaskRefreshPeople": "لوگوں کو تروتازہ کریں",
+ "TaskCleanLogsDescription": "لاگ فائلوں کو حذف کریں جو {0} دن سے زیادہ پرانی ہیں۔",
+ "TaskCleanLogs": "لاگ ڈائرکٹری کو صاف کریں",
+ "TaskRefreshLibraryDescription": "میڈیا لائبریری کو اسکین کرتا ھے ہر میٹا دیٹا کہ تازہ دم کرتا ھے.",
+ "TaskRefreshLibrary": "اسکین میڈیا لائبریری",
+ "TaskRefreshChapterImagesDescription": "بابوں والی ویڈیوز کے لئے تمبنیل بنایں۔",
+ "TaskRefreshChapterImages": "باب کی تصاویر نکالیں",
+ "TaskCleanCacheDescription": "فائلوں کو حذف کریں جنکی ضرورت نھیں ھے۔",
+ "TaskCleanCache": "کیش ڈائرکٹری کلیر کریں",
+ "TasksChannelsCategory": "انٹرنیٹ چینلز",
+ "TasksApplicationCategory": "پروگرام",
+ "TasksLibraryCategory": "لآیبریری",
+ "TasksMaintenanceCategory": "مرمت",
+ "VersionNumber": "ورژن {0}",
+ "ValueHasBeenAddedToLibrary": "{0} آپ کی میڈیا لائبریری میں شامل کر دیا گیا ہے",
+ "UserStoppedPlayingItemWithValues": "{0} نے {1} چلانا ختم کر دیا ھے {2} پے",
+ "UserStartedPlayingItemWithValues": "{0} چلا رہا ہے {1} {2} پے",
+ "UserPolicyUpdatedWithName": "صارف {0} کی پالیسی کیلئے تازہ کاری کی گئی ہے",
+ "UserPasswordChangedWithName": "صارف {0} کے لئے پاس ورڈ تبدیل کر دیا گیا ہے",
+ "UserOnlineFromDevice": "{0} آن لائن ہے {1} سے",
+ "UserOfflineFromDevice": "{0} سے منقطع ہوگیا ہے {1}",
+ "UserLockedOutWithName": "صارف {0} کو لاک آؤٹ کردیا گیا ہے",
+ "UserDownloadingItemWithValues": "{0} ڈاؤن لوڈ کر رھا ھے {1}",
+ "UserDeletedWithName": "صارف {0} کو ہٹا دیا گیا ہے",
+ "UserCreatedWithName": "صارف {0} تشکیل دیا گیا ہے",
+ "User": "صارف",
+ "TvShows": "ٹی وی کے پروگرام",
+ "System": "نظام",
+ "SubtitleDownloadFailureFromForItem": "ذیلی عنوانات {0} سے ڈاؤن لوڈ کرنے میں ناکام {1} کے لیے",
+ "StartupEmbyServerIsLoading": "جیلیفن سرور لوڈ ہورہا ہے۔ براہ کرم جلد ہی دوبارہ کوشش کریں۔",
+ "ServerNameNeedsToBeRestarted": "{0} دوبارہ چلانے کرنے کی ضرورت ہے",
+ "ScheduledTaskStartedWithName": "{0} شروع",
+ "ScheduledTaskFailedWithName": "{0} ناکام",
+ "ProviderValue": "فراہم کرنے والا: {0}",
+ "PluginUpdatedWithName": "{0} تازہ کاری کی گئی تھی",
+ "PluginUninstalledWithName": "[0} ہٹا دیا گیا تھا",
+ "PluginInstalledWithName": "{0} انسٹال کیا گیا تھا",
+ "Plugin": "پلگن",
+ "NotificationOptionVideoPlaybackStopped": "ویڈیو پلے بیک رک گیا",
+ "NotificationOptionVideoPlayback": "ویڈیو پلے بیک شروع ہوا",
+ "NotificationOptionUserLockedOut": "صارف کو لاک آؤٹ کیا گیا",
+ "NotificationOptionTaskFailed": "طے شدہ کام کی ناکامی",
+ "NotificationOptionServerRestartRequired": "سرور دوبارہ چلانے کرنے کی ضرورت ہے",
+ "NotificationOptionPluginUpdateInstalled": "پلگ ان اپ ڈیٹ انسٹال",
+ "NotificationOptionPluginUninstalled": "پلگ ان ہٹا دیا گیا",
+ "NotificationOptionPluginInstalled": "پلگ ان انسٹال ہوا",
+ "NotificationOptionPluginError": "پلگ ان کی ناکامی",
+ "NotificationOptionNewLibraryContent": "نیا مواد شامل کیا گیا",
+ "NotificationOptionInstallationFailed": "تنصیب کی ناکامی",
+ "NotificationOptionCameraImageUploaded": "کیمرے کی تصویر اپ لوڈ ہوگئی",
+ "NotificationOptionAudioPlaybackStopped": "آڈیو پلے بیک رک گیا",
+ "NotificationOptionAudioPlayback": "آڈیو پلے بیک شروع ہوا",
+ "NotificationOptionApplicationUpdateInstalled": "پروگرام اپ ڈیٹ انسٹال ہوچکا ھے",
+ "NotificationOptionApplicationUpdateAvailable": "پروگرام کی تازہ کاری دستیاب ہے",
+ "NewVersionIsAvailable": "جیلیفن سرور کا ایک نیا ورژن ڈاؤن لوڈ کے لئے دستیاب ہے۔",
+ "NameSeasonUnknown": "نامعلوم باب",
+ "NameSeasonNumber": "باب {0}",
+ "NameInstallFailed": "{0} تنصیب ناکام ہوگئی",
+ "MusicVideos": "موسیقی ویڈیو",
+ "Music": "موسیقی",
+ "MixedContent": "مخلوط مواد",
+ "MessageServerConfigurationUpdated": "سرور کو اپ ڈیٹ کر دیا گیا ہے",
+ "MessageNamedServerConfigurationUpdatedWithValue": "سرور ضمن {0} کو ترتیب دے دیا گیا ھے",
+ "MessageApplicationUpdatedTo": "جیلیفن سرور کو اپ ڈیٹ کیا ہے {0}",
+ "MessageApplicationUpdated": "جیلیفن سرور کو اپ ڈیٹ کر دیا گیا ہے",
+ "Latest": "تازہ ترین",
+ "LabelRunningTimeValue": "چلانے کی مدت",
+ "LabelIpAddressValue": "ای پی پتے {0}",
+ "ItemRemovedWithName": "لائبریری سے ہٹا دیا گیا ھے",
+ "ItemAddedWithName": "[0} لائبریری میں شامل کیا گیا ھے",
+ "Inherit": "وراثت میں",
+ "HomeVideos": "ہوم ویڈیو",
+ "HeaderRecordingGroups": "ریکارڈنگ گروپس",
+ "HeaderCameraUploads": "کیمرہ اپلوڈز",
+ "FailedLoginAttemptWithUserName": "لاگن کئ کوشش ناکام {0}",
+ "DeviceOnlineWithName": "{0} متصل ھو چکا ھے",
+ "DeviceOfflineWithName": "{0} منقطع ھو چکا ھے",
+ "ChapterNameValue": "باب",
+ "AuthenticationSucceededWithUserName": "{0} کامیابی کے ساتھ تصدیق ھوچکی ھے",
+ "CameraImageUploadedFrom": "ایک نئی کیمرہ تصویر اپ لوڈ کی گئی ہے {0}",
+ "Application": "پروگرام",
+ "AppDeviceValues": "پروگرام:{0}, آلہ:{1}"
+}
diff --git a/Emby.Server.Implementations/Localization/Core/zh-CN.json b/Emby.Server.Implementations/Localization/Core/zh-CN.json
index 68134a151..6b563a9b1 100644
--- a/Emby.Server.Implementations/Localization/Core/zh-CN.json
+++ b/Emby.Server.Implementations/Localization/Core/zh-CN.json
@@ -3,11 +3,11 @@
"AppDeviceValues": "应用: {0}, 设备: {1}",
"Application": "应用程序",
"Artists": "艺术家",
- "AuthenticationSucceededWithUserName": "{0} 验证成功",
+ "AuthenticationSucceededWithUserName": "{0} 认证成功",
"Books": "书籍",
"CameraImageUploadedFrom": "新的相机图像已从 {0} 上传",
"Channels": "频道",
- "ChapterNameValue": "章节 {0}",
+ "ChapterNameValue": "第 {0} 集",
"Collections": "合集",
"DeviceOfflineWithName": "{0} 已断开",
"DeviceOnlineWithName": "{0} 已连接",
@@ -76,7 +76,6 @@
"StartupEmbyServerIsLoading": "Jellyfin 服务器加载中。请稍后再试。",
"SubtitleDownloadFailureForItem": "为 {0} 下载字幕失败",
"SubtitleDownloadFailureFromForItem": "无法从 {0} 下载 {1} 的字幕",
- "SubtitlesDownloadedForItem": "已为 {0} 下载了字幕",
"Sync": "同步",
"System": "系统",
"TvShows": "电视剧",
@@ -93,5 +92,27 @@
"UserStoppedPlayingItemWithValues": "{0} 已在 {2} 上停止播放 {1}",
"ValueHasBeenAddedToLibrary": "{0} 已添加至您的媒体库中",
"ValueSpecialEpisodeName": "特典 - {0}",
- "VersionNumber": "版本 {0}"
+ "VersionNumber": "版本 {0}",
+ "TaskUpdatePluginsDescription": "为已设置为自动更新的插件下载和安装更新。",
+ "TaskRefreshPeople": "刷新人员",
+ "TasksChannelsCategory": "互联网频道",
+ "TasksLibraryCategory": "媒体库",
+ "TaskDownloadMissingSubtitlesDescription": "根据元数据设置在互联网上搜索缺少的字幕。",
+ "TaskDownloadMissingSubtitles": "下载缺少的字幕",
+ "TaskRefreshChannelsDescription": "刷新互联网频道信息。",
+ "TaskRefreshChannels": "刷新频道",
+ "TaskCleanTranscodeDescription": "删除存在超过 1 天的转码文件。",
+ "TaskCleanTranscode": "清理转码目录",
+ "TaskUpdatePlugins": "更新插件",
+ "TaskRefreshPeopleDescription": "更新媒体库中演员和导演的元数据。",
+ "TaskCleanLogsDescription": "删除存在超过 {0} 天的的日志文件。",
+ "TaskCleanLogs": "清理日志目录",
+ "TaskRefreshLibraryDescription": "扫描你的媒体库以获取新文件并刷新元数据。",
+ "TaskRefreshLibrary": "扫描媒体库",
+ "TaskRefreshChapterImagesDescription": "为包含剧集的视频提取缩略图。",
+ "TaskRefreshChapterImages": "提取剧集图片",
+ "TaskCleanCacheDescription": "删除系统不再需要的缓存文件。",
+ "TaskCleanCache": "清理缓存目录",
+ "TasksApplicationCategory": "应用程序",
+ "TasksMaintenanceCategory": "维护"
}
diff --git a/Emby.Server.Implementations/Localization/Core/zh-HK.json b/Emby.Server.Implementations/Localization/Core/zh-HK.json
index f3d9e5fce..224748e61 100644
--- a/Emby.Server.Implementations/Localization/Core/zh-HK.json
+++ b/Emby.Server.Implementations/Localization/Core/zh-HK.json
@@ -76,7 +76,6 @@
"StartupEmbyServerIsLoading": "Jellyfin 伺服器載入中,請稍後再試。",
"SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}",
"SubtitleDownloadFailureFromForItem": "無法從 {0} 下載 {1} 的字幕",
- "SubtitlesDownloadedForItem": "已為 {0} 下載了字幕",
"Sync": "同步",
"System": "System",
"TvShows": "電視節目",
diff --git a/Emby.Server.Implementations/Localization/Core/zh-TW.json b/Emby.Server.Implementations/Localization/Core/zh-TW.json
index acd211f22..a22f66df9 100644
--- a/Emby.Server.Implementations/Localization/Core/zh-TW.json
+++ b/Emby.Server.Implementations/Localization/Core/zh-TW.json
@@ -20,7 +20,7 @@
"HeaderContinueWatching": "繼續觀賞",
"HeaderFavoriteAlbums": "最愛專輯",
"HeaderFavoriteArtists": "最愛演出者",
- "HeaderFavoriteEpisodes": "最愛級數",
+ "HeaderFavoriteEpisodes": "最愛影集",
"HeaderFavoriteShows": "最愛節目",
"HeaderFavoriteSongs": "最愛歌曲",
"HeaderLiveTV": "電視直播",
@@ -50,10 +50,10 @@
"NotificationOptionCameraImageUploaded": "相機相片已上傳",
"NotificationOptionInstallationFailed": "安裝失敗",
"NotificationOptionNewLibraryContent": "已新增新內容",
- "NotificationOptionPluginError": "擴充元件錯誤",
- "NotificationOptionPluginInstalled": "擴充元件已安裝",
- "NotificationOptionPluginUninstalled": "擴充元件已移除",
- "NotificationOptionPluginUpdateInstalled": "已更新擴充元件",
+ "NotificationOptionPluginError": "插件安裝錯誤",
+ "NotificationOptionPluginInstalled": "插件已安裝",
+ "NotificationOptionPluginUninstalled": "插件已移除",
+ "NotificationOptionPluginUpdateInstalled": "插件已更新",
"NotificationOptionServerRestartRequired": "伺服器需要重新啟動",
"NotificationOptionTaskFailed": "排程任務失敗",
"NotificationOptionUserLockedOut": "使用者已鎖定",
@@ -61,7 +61,7 @@
"NotificationOptionVideoPlaybackStopped": "影片停止播放",
"Photos": "相片",
"Playlists": "播放清單",
- "Plugin": "外掛",
+ "Plugin": "插件",
"PluginInstalledWithName": "{0} 已安裝",
"PluginUninstalledWithName": "{0} 已移除",
"PluginUpdatedWithName": "{0} 已更新",
@@ -72,7 +72,6 @@
"Shows": "節目",
"Songs": "歌曲",
"StartupEmbyServerIsLoading": "Jellyfin Server正在啟動,請稍後再試一次。",
- "SubtitlesDownloadedForItem": "已為 {0} 下載字幕",
"Sync": "同步",
"System": "系統",
"TvShows": "電視節目",
@@ -92,5 +91,27 @@
"VersionNumber": "版本 {0}",
"HeaderRecordingGroups": "錄製組",
"Inherit": "繼承",
- "SubtitleDownloadFailureFromForItem": "無法為 {1} 從 {0} 下載字幕"
+ "SubtitleDownloadFailureFromForItem": "無法為 {1} 從 {0} 下載字幕",
+ "TaskDownloadMissingSubtitlesDescription": "在網路上透過描述資料搜尋遺失的字幕。",
+ "TaskDownloadMissingSubtitles": "下載遺失的字幕",
+ "TaskRefreshChannels": "重新整理頻道",
+ "TaskUpdatePlugins": "更新插件",
+ "TaskRefreshPeople": "重新整理人員",
+ "TaskCleanLogsDescription": "刪除超過{0}天的紀錄檔案。",
+ "TaskCleanLogs": "清空紀錄資料夾",
+ "TaskRefreshLibraryDescription": "掃描媒體庫內新的檔案並重新整理描述資料。",
+ "TaskRefreshLibrary": "掃描媒體庫",
+ "TaskRefreshChapterImages": "擷取章節圖片",
+ "TaskCleanCacheDescription": "刪除系統長時間不需要的快取。",
+ "TaskCleanCache": "清除快取資料夾",
+ "TasksLibraryCategory": "媒體庫",
+ "TaskRefreshChannelsDescription": "重新整理網絡頻道資料。",
+ "TaskCleanTranscodeDescription": "刪除超過一天的轉碼檔案。",
+ "TaskCleanTranscode": "清除轉碼資料夾",
+ "TaskUpdatePluginsDescription": "下載並安裝配置為自動更新的插件的更新。",
+ "TaskRefreshPeopleDescription": "更新媒體庫中演員和導演的中繼資料。",
+ "TaskRefreshChapterImagesDescription": "為有章節的視頻創建縮圖。",
+ "TasksChannelsCategory": "網絡頻道",
+ "TasksApplicationCategory": "應用程式",
+ "TasksMaintenanceCategory": "維修"
}
diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs
index bda43e832..e2a634e1a 100644
--- a/Emby.Server.Implementations/Localization/LocalizationManager.cs
+++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs
@@ -23,9 +23,6 @@ namespace Emby.Server.Implementations.Localization
private static readonly Assembly _assembly = typeof(LocalizationManager).Assembly;
private static readonly string[] _unratedValues = { "n/a", "unrated", "not rated" };
- /// <summary>
- /// The _configuration manager.
- /// </summary>
private readonly IServerConfigurationManager _configurationManager;
private readonly IJsonSerializer _jsonSerializer;
private readonly ILogger _logger;
diff --git a/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs b/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs
index 840aca7a6..677d68b4c 100644
--- a/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs
+++ b/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs
@@ -26,14 +26,20 @@ namespace Emby.Server.Implementations.MediaEncoder
private readonly IChapterManager _chapterManager;
private readonly ILibraryManager _libraryManager;
+ /// <summary>
+ /// The first chapter ticks.
+ /// </summary>
+ private static readonly long _firstChapterTicks = TimeSpan.FromSeconds(15).Ticks;
+
public EncodingManager(
+ ILogger<EncodingManager> logger,
IFileSystem fileSystem,
- ILoggerFactory loggerFactory,
IMediaEncoder encoder,
- IChapterManager chapterManager, ILibraryManager libraryManager)
+ IChapterManager chapterManager,
+ ILibraryManager libraryManager)
{
+ _logger = logger;
_fileSystem = fileSystem;
- _logger = loggerFactory.CreateLogger(nameof(EncodingManager));
_encoder = encoder;
_chapterManager = chapterManager;
_libraryManager = libraryManager;
@@ -97,12 +103,7 @@ namespace Emby.Server.Implementations.MediaEncoder
return video.DefaultVideoStreamIndex.HasValue;
}
- /// <summary>
- /// The first chapter ticks
- /// </summary>
- private static readonly long FirstChapterTicks = TimeSpan.FromSeconds(15).Ticks;
-
- public async Task<bool> RefreshChapterImages(Video video, IDirectoryService directoryService, List<ChapterInfo> chapters, bool extractImages, bool saveChapters, CancellationToken cancellationToken)
+ public async Task<bool> RefreshChapterImages(Video video, IDirectoryService directoryService, IReadOnlyList<ChapterInfo> chapters, bool extractImages, bool saveChapters, CancellationToken cancellationToken)
{
if (!IsEligibleForChapterImageExtraction(video))
{
@@ -135,7 +136,7 @@ namespace Emby.Server.Implementations.MediaEncoder
try
{
// Add some time for the first chapter to make sure we don't end up with a black image
- var time = chapter.StartPositionTicks == 0 ? TimeSpan.FromTicks(Math.Min(FirstChapterTicks, video.RunTimeTicks ?? 0)) : TimeSpan.FromTicks(chapter.StartPositionTicks);
+ var time = chapter.StartPositionTicks == 0 ? TimeSpan.FromTicks(Math.Min(_firstChapterTicks, video.RunTimeTicks ?? 0)) : TimeSpan.FromTicks(chapter.StartPositionTicks);
var protocol = MediaProtocol.File;
@@ -152,9 +153,9 @@ namespace Emby.Server.Implementations.MediaEncoder
{
_fileSystem.DeleteFile(tempFile);
}
- catch
+ catch (IOException ex)
{
-
+ _logger.LogError(ex, "Error deleting temporary chapter image encoding file {Path}", tempFile);
}
chapter.ImagePath = path;
@@ -184,7 +185,7 @@ namespace Emby.Server.Implementations.MediaEncoder
if (saveChapters && changesMade)
{
- _chapterManager.SaveChapters(video.Id.ToString(), chapters);
+ _chapterManager.SaveChapters(video.Id, chapters);
}
DeleteDeadImages(currentImages, chapters);
@@ -199,22 +200,21 @@ namespace Emby.Server.Implementations.MediaEncoder
return Path.Combine(GetChapterImagesPath(video), filename);
}
- private static List<string> GetSavedChapterImages(Video video, IDirectoryService directoryService)
+ private static IReadOnlyList<string> GetSavedChapterImages(Video video, IDirectoryService directoryService)
{
var path = GetChapterImagesPath(video);
if (!Directory.Exists(path))
{
- return new List<string>();
+ return Array.Empty<string>();
}
try
{
- return directoryService.GetFilePaths(path)
- .ToList();
+ return directoryService.GetFilePaths(path);
}
catch (IOException)
{
- return new List<string>();
+ return Array.Empty<string>();
}
}
@@ -227,7 +227,7 @@ namespace Emby.Server.Implementations.MediaEncoder
foreach (var image in deadImages)
{
- _logger.LogDebug("Deleting dead chapter image {path}", image);
+ _logger.LogDebug("Deleting dead chapter image {Path}", image);
try
{
@@ -235,7 +235,7 @@ namespace Emby.Server.Implementations.MediaEncoder
}
catch (IOException ex)
{
- _logger.LogError(ex, "Error deleting {path}.", image);
+ _logger.LogError(ex, "Error deleting {Path}.", image);
}
}
}
diff --git a/Emby.Server.Implementations/Networking/NetworkManager.cs b/Emby.Server.Implementations/Networking/NetworkManager.cs
index 1d8d3cf39..b3e88b667 100644
--- a/Emby.Server.Implementations/Networking/NetworkManager.cs
+++ b/Emby.Server.Implementations/Networking/NetworkManager.cs
@@ -500,7 +500,7 @@ namespace Emby.Server.Implementations.Networking
{
if (ip.Address.Equals(address) && ip.IPv4Mask != null)
{
- return ip.IPv4Mask;
+ return ip.IPv4Mask;
}
}
}
diff --git a/Emby.Server.Implementations/Playlists/PlaylistManager.cs b/Emby.Server.Implementations/Playlists/PlaylistManager.cs
index ad7a5005e..9b1510ac9 100644
--- a/Emby.Server.Implementations/Playlists/PlaylistManager.cs
+++ b/Emby.Server.Implementations/Playlists/PlaylistManager.cs
@@ -8,12 +8,14 @@ using System.Threading.Tasks;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
+using MediaBrowser.Controller.Extensions;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Playlists;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Playlists;
+using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using PlaylistsNET.Content;
using PlaylistsNET.Models;
@@ -28,6 +30,7 @@ namespace Emby.Server.Implementations.Playlists
private readonly ILogger _logger;
private readonly IUserManager _userManager;
private readonly IProviderManager _providerManager;
+ private readonly IConfiguration _appConfig;
public PlaylistManager(
ILibraryManager libraryManager,
@@ -35,7 +38,8 @@ namespace Emby.Server.Implementations.Playlists
ILibraryMonitor iLibraryMonitor,
ILogger<PlaylistManager> logger,
IUserManager userManager,
- IProviderManager providerManager)
+ IProviderManager providerManager,
+ IConfiguration appConfig)
{
_libraryManager = libraryManager;
_fileSystem = fileSystem;
@@ -43,6 +47,7 @@ namespace Emby.Server.Implementations.Playlists
_logger = logger;
_userManager = userManager;
_providerManager = providerManager;
+ _appConfig = appConfig;
}
public IEnumerable<Playlist> GetPlaylists(Guid userId)
@@ -177,7 +182,7 @@ namespace Emby.Server.Implementations.Playlists
return Playlist.GetPlaylistItems(playlistMediaType, items, user, options);
}
- public void AddToPlaylist(string playlistId, IEnumerable<Guid> itemIds, Guid userId)
+ public void AddToPlaylist(string playlistId, ICollection<Guid> itemIds, Guid userId)
{
var user = userId.Equals(Guid.Empty) ? null : _userManager.GetUserById(userId);
@@ -187,37 +192,59 @@ namespace Emby.Server.Implementations.Playlists
});
}
- private void AddToPlaylistInternal(string playlistId, IEnumerable<Guid> itemIds, User user, DtoOptions options)
+ private void AddToPlaylistInternal(string playlistId, ICollection<Guid> newItemIds, User user, DtoOptions options)
{
- var playlist = _libraryManager.GetItemById(playlistId) as Playlist;
+ // Retrieve the existing playlist
+ var playlist = _libraryManager.GetItemById(playlistId) as Playlist
+ ?? throw new ArgumentException("No Playlist exists with Id " + playlistId);
- if (playlist == null)
+ // Retrieve all the items to be added to the playlist
+ var newItems = GetPlaylistItems(newItemIds, playlist.MediaType, user, options)
+ .Where(i => i.SupportsAddingToPlaylist);
+
+ // Filter out duplicate items, if necessary
+ if (!_appConfig.DoPlaylistsAllowDuplicates())
{
- throw new ArgumentException("No Playlist exists with the supplied Id");
+ var existingIds = playlist.LinkedChildren.Select(c => c.ItemId).ToHashSet();
+ newItems = newItems
+ .Where(i => !existingIds.Contains(i.Id))
+ .Distinct();
}
- var list = new List<LinkedChild>();
-
- var items = GetPlaylistItems(itemIds, playlist.MediaType, user, options)
- .Where(i => i.SupportsAddingToPlaylist)
+ // Create a list of the new linked children to add to the playlist
+ var childrenToAdd = newItems
+ .Select(i => LinkedChild.Create(i))
.ToList();
- foreach (var item in items)
+ // Log duplicates that have been ignored, if any
+ int numDuplicates = newItemIds.Count - childrenToAdd.Count;
+ if (numDuplicates > 0)
{
- list.Add(LinkedChild.Create(item));
+ _logger.LogWarning("Ignored adding {DuplicateCount} duplicate items to playlist {PlaylistName}.", numDuplicates, playlist.Name);
}
- var newList = playlist.LinkedChildren.ToList();
- newList.AddRange(list);
- playlist.LinkedChildren = newList.ToArray();
+ // Do nothing else if there are no items to add to the playlist
+ if (childrenToAdd.Count == 0)
+ {
+ return;
+ }
+
+ // Create a new array with the updated playlist items
+ var newLinkedChildren = new LinkedChild[playlist.LinkedChildren.Length + childrenToAdd.Count];
+ playlist.LinkedChildren.CopyTo(newLinkedChildren, 0);
+ childrenToAdd.CopyTo(newLinkedChildren, playlist.LinkedChildren.Length);
+ // Update the playlist in the repository
+ playlist.LinkedChildren = newLinkedChildren;
playlist.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None);
+ // Update the playlist on disk
if (playlist.IsFile)
{
SavePlaylistFile(playlist);
}
+ // Refresh playlist metadata
_providerManager.QueueRefresh(
playlist.Id,
new MetadataRefreshOptions(new DirectoryService(_fileSystem))
diff --git a/Emby.Server.Implementations/ScheduledTasks/TaskManager.cs b/Emby.Server.Implementations/ScheduledTasks/TaskManager.cs
index ecf58dbc0..6ffa581a9 100644
--- a/Emby.Server.Implementations/ScheduledTasks/TaskManager.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/TaskManager.cs
@@ -32,22 +32,8 @@ namespace Emby.Server.Implementations.ScheduledTasks
private readonly ConcurrentQueue<Tuple<Type, TaskOptions>> _taskQueue =
new ConcurrentQueue<Tuple<Type, TaskOptions>>();
- /// <summary>
- /// Gets or sets the json serializer.
- /// </summary>
- /// <value>The json serializer.</value>
private readonly IJsonSerializer _jsonSerializer;
-
- /// <summary>
- /// Gets or sets the application paths.
- /// </summary>
- /// <value>The application paths.</value>
private readonly IApplicationPaths _applicationPaths;
-
- /// <summary>
- /// Gets the logger.
- /// </summary>
- /// <value>The logger.</value>
private readonly ILogger _logger;
private readonly IFileSystem _fileSystem;
@@ -56,17 +42,17 @@ namespace Emby.Server.Implementations.ScheduledTasks
/// </summary>
/// <param name="applicationPaths">The application paths.</param>
/// <param name="jsonSerializer">The json serializer.</param>
- /// <param name="loggerFactory">The logger factory.</param>
+ /// <param name="logger">The logger.</param>
/// <param name="fileSystem">The filesystem manager.</param>
public TaskManager(
IApplicationPaths applicationPaths,
IJsonSerializer jsonSerializer,
- ILoggerFactory loggerFactory,
+ ILogger<TaskManager> logger,
IFileSystem fileSystem)
{
_applicationPaths = applicationPaths;
_jsonSerializer = jsonSerializer;
- _logger = loggerFactory.CreateLogger(nameof(TaskManager));
+ _logger = logger;
_fileSystem = fileSystem;
ScheduledTasks = Array.Empty<IScheduledTaskWorker>();
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs
index 5822c467b..ea6a70615 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs
@@ -15,6 +15,7 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Tasks;
using Microsoft.Extensions.Logging;
+using MediaBrowser.Model.Globalization;
namespace Emby.Server.Implementations.ScheduledTasks
{
@@ -39,11 +40,19 @@ namespace Emby.Server.Implementations.ScheduledTasks
private readonly IEncodingManager _encodingManager;
private readonly IFileSystem _fileSystem;
+ private readonly ILocalizationManager _localization;
/// <summary>
/// Initializes a new instance of the <see cref="ChapterImagesTask" /> class.
/// </summary>
- public ChapterImagesTask(ILoggerFactory loggerFactory, ILibraryManager libraryManager, IItemRepository itemRepo, IApplicationPaths appPaths, IEncodingManager encodingManager, IFileSystem fileSystem)
+ public ChapterImagesTask(
+ ILoggerFactory loggerFactory,
+ ILibraryManager libraryManager,
+ IItemRepository itemRepo,
+ IApplicationPaths appPaths,
+ IEncodingManager encodingManager,
+ IFileSystem fileSystem,
+ ILocalizationManager localization)
{
_logger = loggerFactory.CreateLogger(GetType().Name);
_libraryManager = libraryManager;
@@ -51,6 +60,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
_appPaths = appPaths;
_encodingManager = encodingManager;
_fileSystem = fileSystem;
+ _localization = localization;
}
/// <summary>
@@ -159,11 +169,11 @@ namespace Emby.Server.Implementations.ScheduledTasks
}
}
- public string Name => "Extract Chapter Images";
+ public string Name => _localization.GetLocalizedString("TaskRefreshChapterImages");
- public string Description => "Creates thumbnails for videos that have chapters.";
+ public string Description => _localization.GetLocalizedString("TaskRefreshChapterImagesDescription");
- public string Category => "Library";
+ public string Category => _localization.GetLocalizedString("TasksLibraryCategory");
public string Key => "RefreshChapterImages";
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs
index b7668c872..9df7c538b 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs
@@ -8,6 +8,7 @@ using MediaBrowser.Common.Configuration;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Tasks;
using Microsoft.Extensions.Logging;
+using MediaBrowser.Model.Globalization;
namespace Emby.Server.Implementations.ScheduledTasks.Tasks
{
@@ -25,6 +26,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
private readonly ILogger _logger;
private readonly IFileSystem _fileSystem;
+ private readonly ILocalizationManager _localization;
/// <summary>
/// Initializes a new instance of the <see cref="DeleteCacheFileTask" /> class.
@@ -32,11 +34,13 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
public DeleteCacheFileTask(
IApplicationPaths appPaths,
ILogger<DeleteCacheFileTask> logger,
- IFileSystem fileSystem)
+ IFileSystem fileSystem,
+ ILocalizationManager localization)
{
ApplicationPaths = appPaths;
_logger = logger;
_fileSystem = fileSystem;
+ _localization = localization;
}
/// <summary>
@@ -161,11 +165,11 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
}
}
- public string Name => "Clean Cache Directory";
+ public string Name => _localization.GetLocalizedString("TaskCleanCache");
- public string Description => "Deletes cache files no longer needed by the system.";
+ public string Description => _localization.GetLocalizedString("TaskCleanCacheDescription");
- public string Category => "Maintenance";
+ public string Category => _localization.GetLocalizedString("TasksMaintenanceCategory");
public string Key => "DeleteCacheFiles";
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs
index 9f9c6353a..3140aa489 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs
@@ -6,6 +6,7 @@ using System.Threading.Tasks;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Tasks;
+using MediaBrowser.Model.Globalization;
namespace Emby.Server.Implementations.ScheduledTasks.Tasks
{
@@ -21,15 +22,17 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
private IConfigurationManager ConfigurationManager { get; set; }
private readonly IFileSystem _fileSystem;
+ private readonly ILocalizationManager _localization;
/// <summary>
/// Initializes a new instance of the <see cref="DeleteLogFileTask" /> class.
/// </summary>
/// <param name="configurationManager">The configuration manager.</param>
- public DeleteLogFileTask(IConfigurationManager configurationManager, IFileSystem fileSystem)
+ public DeleteLogFileTask(IConfigurationManager configurationManager, IFileSystem fileSystem, ILocalizationManager localization)
{
ConfigurationManager = configurationManager;
_fileSystem = fileSystem;
+ _localization = localization;
}
/// <summary>
@@ -79,11 +82,11 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
return Task.CompletedTask;
}
- public string Name => "Clean Log Directory";
+ public string Name => _localization.GetLocalizedString("TaskCleanLogs");
- public string Description => string.Format("Deletes log files that are more than {0} days old.", ConfigurationManager.CommonConfiguration.LogFileRetentionDays);
+ public string Description => string.Format(_localization.GetLocalizedString("TaskCleanLogsDescription"), ConfigurationManager.CommonConfiguration.LogFileRetentionDays);
- public string Category => "Maintenance";
+ public string Category => _localization.GetLocalizedString("TasksMaintenanceCategory");
public string Key => "CleanLogFiles";
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs
index 37136f438..1d133dcda 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs
@@ -8,6 +8,7 @@ using MediaBrowser.Common.Configuration;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Tasks;
using Microsoft.Extensions.Logging;
+using MediaBrowser.Model.Globalization;
namespace Emby.Server.Implementations.ScheduledTasks.Tasks
{
@@ -19,6 +20,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
private readonly ILogger _logger;
private readonly IConfigurationManager _configurationManager;
private readonly IFileSystem _fileSystem;
+ private readonly ILocalizationManager _localization;
/// <summary>
/// Initializes a new instance of the <see cref="DeleteTranscodeFileTask" /> class.
@@ -26,11 +28,13 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
public DeleteTranscodeFileTask(
ILogger<DeleteTranscodeFileTask> logger,
IFileSystem fileSystem,
- IConfigurationManager configurationManager)
+ IConfigurationManager configurationManager,
+ ILocalizationManager localization)
{
_logger = logger;
_fileSystem = fileSystem;
_configurationManager = configurationManager;
+ _localization = localization;
}
/// <summary>
@@ -128,11 +132,11 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
}
}
- public string Name => "Clean Transcode Directory";
+ public string Name => _localization.GetLocalizedString("TaskCleanTranscode");
- public string Description => "Deletes transcode files more than one day old.";
+ public string Description => _localization.GetLocalizedString("TaskCleanTranscodeDescription");
- public string Category => "Maintenance";
+ public string Category => _localization.GetLocalizedString("TasksMaintenanceCategory");
public string Key => "DeleteTranscodeFiles";
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/PeopleValidationTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/PeopleValidationTask.cs
index eaf17aace..63f867bf6 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/PeopleValidationTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/PeopleValidationTask.cs
@@ -5,6 +5,7 @@ using System.Threading.Tasks;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Tasks;
+using MediaBrowser.Model.Globalization;
namespace Emby.Server.Implementations.ScheduledTasks
{
@@ -19,16 +20,18 @@ namespace Emby.Server.Implementations.ScheduledTasks
private readonly ILibraryManager _libraryManager;
private readonly IServerApplicationHost _appHost;
+ private readonly ILocalizationManager _localization;
/// <summary>
/// Initializes a new instance of the <see cref="PeopleValidationTask" /> class.
/// </summary>
/// <param name="libraryManager">The library manager.</param>
/// <param name="appHost">The server application host</param>
- public PeopleValidationTask(ILibraryManager libraryManager, IServerApplicationHost appHost)
+ public PeopleValidationTask(ILibraryManager libraryManager, IServerApplicationHost appHost, ILocalizationManager localization)
{
_libraryManager = libraryManager;
_appHost = appHost;
+ _localization = localization;
}
/// <summary>
@@ -57,11 +60,11 @@ namespace Emby.Server.Implementations.ScheduledTasks
return _libraryManager.ValidatePeople(cancellationToken, progress);
}
- public string Name => "Refresh People";
+ public string Name => _localization.GetLocalizedString("TaskRefreshPeople");
- public string Description => "Updates metadata for actors and directors in your media library.";
+ public string Description => _localization.GetLocalizedString("TaskRefreshPeopleDescription");
- public string Category => "Library";
+ public string Category => _localization.GetLocalizedString("TasksLibraryCategory");
public string Key => "RefreshPeople";
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs
index 9d87316e4..6a1afced7 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs
@@ -8,6 +8,7 @@ using MediaBrowser.Common.Updates;
using MediaBrowser.Model.Net;
using MediaBrowser.Model.Tasks;
using Microsoft.Extensions.Logging;
+using MediaBrowser.Model.Globalization;
namespace Emby.Server.Implementations.ScheduledTasks
{
@@ -22,11 +23,13 @@ namespace Emby.Server.Implementations.ScheduledTasks
private readonly ILogger _logger;
private readonly IInstallationManager _installationManager;
+ private readonly ILocalizationManager _localization;
- public PluginUpdateTask(ILogger<PluginUpdateTask> logger, IInstallationManager installationManager)
+ public PluginUpdateTask(ILogger<PluginUpdateTask> logger, IInstallationManager installationManager, ILocalizationManager localization)
{
_logger = logger;
_installationManager = installationManager;
+ _localization = localization;
}
/// <summary>
@@ -52,9 +55,8 @@ namespace Emby.Server.Implementations.ScheduledTasks
{
progress.Report(0);
- var packagesToInstall = await _installationManager.GetAvailablePluginUpdates(cancellationToken)
- .ToListAsync(cancellationToken)
- .ConfigureAwait(false);
+ var packageFetchTask = _installationManager.GetAvailablePluginUpdates(cancellationToken);
+ var packagesToInstall = (await packageFetchTask.ConfigureAwait(false)).ToList();
progress.Report(10);
@@ -96,13 +98,13 @@ namespace Emby.Server.Implementations.ScheduledTasks
}
/// <inheritdoc />
- public string Name => "Update Plugins";
+ public string Name => _localization.GetLocalizedString("TaskUpdatePlugins");
/// <inheritdoc />
- public string Description => "Downloads and installs updates for plugins that are configured to update automatically.";
+ public string Description => _localization.GetLocalizedString("TaskUpdatePluginsDescription");
/// <inheritdoc />
- public string Category => "Application";
+ public string Category => _localization.GetLocalizedString("TasksApplicationCategory");
/// <inheritdoc />
public string Key => "PluginUpdates";
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/RefreshMediaLibraryTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/RefreshMediaLibraryTask.cs
index 073678019..74cb01444 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/RefreshMediaLibraryTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/RefreshMediaLibraryTask.cs
@@ -6,6 +6,7 @@ using Emby.Server.Implementations.Library;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Tasks;
+using MediaBrowser.Model.Globalization;
namespace Emby.Server.Implementations.ScheduledTasks
{
@@ -19,15 +20,17 @@ namespace Emby.Server.Implementations.ScheduledTasks
/// </summary>
private readonly ILibraryManager _libraryManager;
private readonly IServerConfigurationManager _config;
+ private readonly ILocalizationManager _localization;
/// <summary>
/// Initializes a new instance of the <see cref="RefreshMediaLibraryTask" /> class.
/// </summary>
/// <param name="libraryManager">The library manager.</param>
- public RefreshMediaLibraryTask(ILibraryManager libraryManager, IServerConfigurationManager config)
+ public RefreshMediaLibraryTask(ILibraryManager libraryManager, IServerConfigurationManager config, ILocalizationManager localization)
{
_libraryManager = libraryManager;
_config = config;
+ _localization = localization;
}
/// <summary>
@@ -38,7 +41,8 @@ namespace Emby.Server.Implementations.ScheduledTasks
{
yield return new TaskTriggerInfo
{
- Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(12).Ticks
+ Type = TaskTriggerInfo.TriggerInterval,
+ IntervalTicks = TimeSpan.FromHours(12).Ticks
};
}
@@ -57,11 +61,11 @@ namespace Emby.Server.Implementations.ScheduledTasks
return ((LibraryManager)_libraryManager).ValidateMediaLibraryInternal(progress, cancellationToken);
}
- public string Name => "Scan Media Library";
+ public string Name => _localization.GetLocalizedString("TaskRefreshLibrary");
- public string Description => "Scans your media library for new files and refreshes metadata.";
+ public string Description => _localization.GetLocalizedString("TaskRefreshLibraryDescription");
- public string Category => "Library";
+ public string Category => _localization.GetLocalizedString("TasksLibraryCategory");
public string Key => "RefreshLibrary";
diff --git a/Emby.Server.Implementations/Security/AuthenticationRepository.cs b/Emby.Server.Implementations/Security/AuthenticationRepository.cs
index 1ef5c4b99..4e4029f06 100644
--- a/Emby.Server.Implementations/Security/AuthenticationRepository.cs
+++ b/Emby.Server.Implementations/Security/AuthenticationRepository.cs
@@ -15,8 +15,8 @@ namespace Emby.Server.Implementations.Security
{
public class AuthenticationRepository : BaseSqliteRepository, IAuthenticationRepository
{
- public AuthenticationRepository(ILoggerFactory loggerFactory, IServerConfigurationManager config)
- : base(loggerFactory.CreateLogger(nameof(AuthenticationRepository)))
+ public AuthenticationRepository(ILogger<AuthenticationRepository> logger, IServerConfigurationManager config)
+ : base(logger)
{
DbFilePath = Path.Combine(config.ApplicationPaths.DataPath, "authentication.db");
}
diff --git a/Emby.Server.Implementations/ServerApplicationPaths.cs b/Emby.Server.Implementations/ServerApplicationPaths.cs
index 2f57c97a1..dfdd4200e 100644
--- a/Emby.Server.Implementations/ServerApplicationPaths.cs
+++ b/Emby.Server.Implementations/ServerApplicationPaths.cs
@@ -9,8 +9,6 @@ namespace Emby.Server.Implementations
/// </summary>
public class ServerApplicationPaths : BaseApplicationPaths, IServerApplicationPaths
{
- private string _internalMetadataPath;
-
/// <summary>
/// Initializes a new instance of the <see cref="ServerApplicationPaths" /> class.
/// </summary>
@@ -27,6 +25,7 @@ namespace Emby.Server.Implementations
cacheDirectoryPath,
webDirectoryPath)
{
+ InternalMetadataPath = DefaultInternalMetadataPath;
}
/// <summary>
@@ -98,12 +97,11 @@ namespace Emby.Server.Implementations
/// <value>The user configuration directory path.</value>
public string UserConfigurationDirectoryPath => Path.Combine(ConfigurationDirectoryPath, "users");
+ /// <inheritdoc/>
+ public string DefaultInternalMetadataPath => Path.Combine(ProgramDataPath, "metadata");
+
/// <inheritdoc />
- public string InternalMetadataPath
- {
- get => _internalMetadataPath ?? (_internalMetadataPath = Path.Combine(DataPath, "metadata"));
- set => _internalMetadataPath = value;
- }
+ public string InternalMetadataPath { get; set; }
/// <inheritdoc />
public string VirtualInternalMetadataPath { get; } = "%MetadataPath%";
diff --git a/Emby.Server.Implementations/Services/ServiceController.cs b/Emby.Server.Implementations/Services/ServiceController.cs
index d963f9043..e24a95dbb 100644
--- a/Emby.Server.Implementations/Services/ServiceController.cs
+++ b/Emby.Server.Implementations/Services/ServiceController.cs
@@ -3,14 +3,27 @@ using System.Collections.Generic;
using System.Threading.Tasks;
using Emby.Server.Implementations.HttpServer;
using MediaBrowser.Model.Services;
+using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.Services
{
public delegate object ActionInvokerFn(object intance, object request);
+
public delegate void VoidActionInvokerFn(object intance, object request);
public class ServiceController
{
+ private readonly ILogger _logger;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ServiceController"/> class.
+ /// </summary>
+ /// <param name="logger">The <see cref="ServiceController"/> logger.</param>
+ public ServiceController(ILogger<ServiceController> logger)
+ {
+ _logger = logger;
+ }
+
public void Init(HttpListenerHost appHost, IEnumerable<Type> serviceTypes)
{
foreach (var serviceType in serviceTypes)
@@ -21,6 +34,13 @@ namespace Emby.Server.Implementations.Services
public void RegisterService(HttpListenerHost appHost, Type serviceType)
{
+ // Make sure the provided type implements IService
+ if (!typeof(IService).IsAssignableFrom(serviceType))
+ {
+ _logger.LogWarning("Tried to register a service that does not implement IService: {ServiceType}", serviceType);
+ return;
+ }
+
var processedReqs = new HashSet<Type>();
var actions = ServiceExecGeneral.Reset(serviceType);
diff --git a/Emby.Server.Implementations/Services/StringMapTypeDeserializer.cs b/Emby.Server.Implementations/Services/StringMapTypeDeserializer.cs
index 23e22afd5..56e23d549 100644
--- a/Emby.Server.Implementations/Services/StringMapTypeDeserializer.cs
+++ b/Emby.Server.Implementations/Services/StringMapTypeDeserializer.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Reflection;
+using MediaBrowser.Common.Extensions;
namespace Emby.Server.Implementations.Services
{
@@ -81,7 +82,7 @@ namespace Emby.Server.Implementations.Services
if (propertySerializerEntry.PropertyType == typeof(bool))
{
//InputExtensions.cs#530 MVC Checkbox helper emits extra hidden input field, generating 2 values, first is the real value
- propertyTextValue = LeftPart(propertyTextValue, ',');
+ propertyTextValue = StringExtensions.LeftPart(propertyTextValue, ',').ToString();
}
var value = propertySerializerEntry.PropertyParseStringFn(propertyTextValue);
@@ -95,19 +96,6 @@ namespace Emby.Server.Implementations.Services
return instance;
}
-
- public static string LeftPart(string strVal, char needle)
- {
- if (strVal == null)
- {
- return null;
- }
-
- var pos = strVal.IndexOf(needle);
- return pos == -1
- ? strVal
- : strVal.Substring(0, pos);
- }
}
internal static class TypeAccessor
diff --git a/Emby.Server.Implementations/Services/SwaggerService.cs b/Emby.Server.Implementations/Services/SwaggerService.cs
index c30f32af9..5177251c3 100644
--- a/Emby.Server.Implementations/Services/SwaggerService.cs
+++ b/Emby.Server.Implementations/Services/SwaggerService.cs
@@ -1,9 +1,9 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using Emby.Server.Implementations.HttpServer;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Services;
-using Emby.Server.Implementations.HttpServer;
namespace Emby.Server.Implementations.Services
{
@@ -241,7 +241,7 @@ namespace Emby.Server.Implementations.Services
responses = responses,
- security = new [] { apiKeySecurity }
+ security = new[] { apiKeySecurity }
};
}
diff --git a/Emby.Server.Implementations/Services/UrlExtensions.cs b/Emby.Server.Implementations/Services/UrlExtensions.cs
index 5d4407f3b..483c63ade 100644
--- a/Emby.Server.Implementations/Services/UrlExtensions.cs
+++ b/Emby.Server.Implementations/Services/UrlExtensions.cs
@@ -1,4 +1,5 @@
using System;
+using MediaBrowser.Common.Extensions;
namespace Emby.Server.Implementations.Services
{
@@ -13,25 +14,12 @@ namespace Emby.Server.Implementations.Services
public static string GetMethodName(this Type type)
{
var typeName = type.FullName != null // can be null, e.g. generic types
- ? LeftPart(type.FullName, "[[") // Generic Fullname
- .Replace(type.Namespace + ".", string.Empty) // Trim Namespaces
- .Replace("+", ".") // Convert nested into normal type
+ ? StringExtensions.LeftPart(type.FullName, "[[", StringComparison.Ordinal).ToString() // Generic Fullname
+ .Replace(type.Namespace + ".", string.Empty, StringComparison.Ordinal) // Trim Namespaces
+ .Replace("+", ".", StringComparison.Ordinal) // Convert nested into normal type
: type.Name;
return type.IsGenericParameter ? "'" + typeName : typeName;
}
-
- private static string LeftPart(string strVal, string needle)
- {
- if (strVal == null)
- {
- return null;
- }
-
- var pos = strVal.IndexOf(needle, StringComparison.OrdinalIgnoreCase);
- return pos == -1
- ? strVal
- : strVal.Substring(0, pos);
- }
}
}
diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs
index dfcd3843c..c93c02c48 100644
--- a/Emby.Server.Implementations/Session/SessionManager.cs
+++ b/Emby.Server.Implementations/Session/SessionManager.cs
@@ -1401,10 +1401,20 @@ namespace Emby.Server.Implementations.Session
user = _userManager.GetUserByName(request.Username);
}
+ if (enforcePassword)
+ {
+ user = await _userManager.AuthenticateUser(
+ request.Username,
+ request.Password,
+ request.PasswordSha1,
+ request.RemoteEndPoint,
+ true).ConfigureAwait(false);
+ }
+
if (user == null)
{
AuthenticationFailed?.Invoke(this, new GenericEventArgs<AuthenticationRequest>(request));
- throw new SecurityException("Invalid username or password entered.");
+ throw new AuthenticationException("Invalid username or password entered.");
}
if (!string.IsNullOrEmpty(request.DeviceId)
@@ -1413,16 +1423,6 @@ namespace Emby.Server.Implementations.Session
throw new SecurityException("User is not allowed access from this device.");
}
- if (enforcePassword)
- {
- user = await _userManager.AuthenticateUser(
- request.Username,
- request.Password,
- request.PasswordSha1,
- request.RemoteEndPoint,
- true).ConfigureAwait(false);
- }
-
var token = GetAuthorizationToken(user, request.DeviceId, request.App, request.AppVersion, request.DeviceName);
var session = LogSessionActivity(
diff --git a/Emby.Server.Implementations/SocketSharp/WebSocketSharpRequest.cs b/Emby.Server.Implementations/SocketSharp/WebSocketSharpRequest.cs
index 1781df8b5..9c638f439 100644
--- a/Emby.Server.Implementations/SocketSharp/WebSocketSharpRequest.cs
+++ b/Emby.Server.Implementations/SocketSharp/WebSocketSharpRequest.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Mime;
+using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions;
@@ -216,14 +217,14 @@ namespace Emby.Server.Implementations.SocketSharp
pi = pi.Slice(1);
}
- format = LeftPart(pi, '/');
+ format = pi.LeftPart('/');
if (format.Length > FormatMaxLength)
{
return null;
}
}
- format = LeftPart(format, '.');
+ format = format.LeftPart('.');
if (format.Contains("json", StringComparison.OrdinalIgnoreCase))
{
return "application/json";
@@ -235,16 +236,5 @@ namespace Emby.Server.Implementations.SocketSharp
return null;
}
-
- public static ReadOnlySpan<char> LeftPart(ReadOnlySpan<char> strVal, char needle)
- {
- if (strVal == null)
- {
- return null;
- }
-
- var pos = strVal.IndexOf(needle);
- return pos == -1 ? strVal : strVal.Slice(0, pos);
- }
}
}
diff --git a/Emby.Server.Implementations/Updates/InstallationManager.cs b/Emby.Server.Implementations/Updates/InstallationManager.cs
index c897036eb..0b2309889 100644
--- a/Emby.Server.Implementations/Updates/InstallationManager.cs
+++ b/Emby.Server.Implementations/Updates/InstallationManager.cs
@@ -3,8 +3,10 @@ using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
+using System.Net;
using System.Net.Http;
using System.Runtime.CompilerServices;
+using System.Runtime.Serialization;
using System.Security.Cryptography;
using System.Threading;
using System.Threading.Tasks;
@@ -18,17 +20,23 @@ using MediaBrowser.Model.Events;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Updates;
+using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.Updates
{
/// <summary>
- /// Manages all install, uninstall and update operations (both plugins and system).
+ /// Manages all install, uninstall, and update operations for the system and individual plugins.
/// </summary>
public class InstallationManager : IInstallationManager
{
/// <summary>
- /// The _logger.
+ /// The key for a setting that specifies a URL for the plugin repository JSON manifest.
+ /// </summary>
+ public const string PluginManifestUrlKey = "InstallationManager:PluginManifestUrl";
+
+ /// <summary>
+ /// The logger.
/// </summary>
private readonly ILogger _logger;
private readonly IApplicationPaths _appPaths;
@@ -44,6 +52,7 @@ namespace Emby.Server.Implementations.Updates
private readonly IApplicationHost _applicationHost;
private readonly IZipClient _zipClient;
+ private readonly IConfiguration _appConfig;
private readonly object _currentInstallationsLock = new object();
@@ -65,7 +74,8 @@ namespace Emby.Server.Implementations.Updates
IJsonSerializer jsonSerializer,
IServerConfigurationManager config,
IFileSystem fileSystem,
- IZipClient zipClient)
+ IZipClient zipClient,
+ IConfiguration appConfig)
{
if (logger == null)
{
@@ -83,6 +93,7 @@ namespace Emby.Server.Implementations.Updates
_config = config;
_fileSystem = fileSystem;
_zipClient = zipClient;
+ _appConfig = appConfig;
}
/// <inheritdoc />
@@ -101,10 +112,10 @@ namespace Emby.Server.Implementations.Updates
public event EventHandler<GenericEventArgs<IPlugin>> PluginUninstalled;
/// <inheritdoc />
- public event EventHandler<GenericEventArgs<(IPlugin, PackageVersionInfo)>> PluginUpdated;
+ public event EventHandler<GenericEventArgs<(IPlugin, VersionInfo)>> PluginUpdated;
/// <inheritdoc />
- public event EventHandler<GenericEventArgs<PackageVersionInfo>> PluginInstalled;
+ public event EventHandler<GenericEventArgs<VersionInfo>> PluginInstalled;
/// <inheritdoc />
public IEnumerable<InstallationInfo> CompletedInstallations => _completedInstallationsInternal;
@@ -112,19 +123,43 @@ namespace Emby.Server.Implementations.Updates
/// <inheritdoc />
public async Task<IReadOnlyList<PackageInfo>> GetAvailablePackages(CancellationToken cancellationToken = default)
{
- using (var response = await _httpClient.SendAsync(
- new HttpRequestOptions
+ var manifestUrl = _appConfig.GetValue<string>(PluginManifestUrlKey);
+
+ try
+ {
+ using (var response = await _httpClient.SendAsync(
+ new HttpRequestOptions
+ {
+ Url = manifestUrl,
+ CancellationToken = cancellationToken,
+ CacheMode = CacheMode.Unconditional,
+ CacheLength = TimeSpan.FromMinutes(3)
+ },
+ HttpMethod.Get).ConfigureAwait(false))
+ using (Stream stream = response.Content)
{
- Url = "https://repo.jellyfin.org/releases/plugin/manifest.json",
- CancellationToken = cancellationToken,
- CacheMode = CacheMode.Unconditional,
- CacheLength = TimeSpan.FromMinutes(3)
- },
- HttpMethod.Get).ConfigureAwait(false))
- using (Stream stream = response.Content)
+ try
+ {
+ return await _jsonSerializer.DeserializeFromStreamAsync<IReadOnlyList<PackageInfo>>(stream).ConfigureAwait(false);
+ }
+ catch (SerializationException ex)
+ {
+ const string LogTemplate =
+ "Failed to deserialize the plugin manifest retrieved from {PluginManifestUrl}. If you " +
+ "have specified a custom plugin repository manifest URL with --plugin-manifest-url or " +
+ PluginManifestUrlKey + ", please ensure that it is correct.";
+ _logger.LogError(ex, LogTemplate, manifestUrl);
+ throw;
+ }
+ }
+ }
+ catch (UriFormatException ex)
{
- return await _jsonSerializer.DeserializeFromStreamAsync<IReadOnlyList<PackageInfo>>(
- stream).ConfigureAwait(false);
+ const string LogTemplate =
+ "The URL configured for the plugin repository manifest URL is not valid: {PluginManifestUrl}. " +
+ "Please check the URL configured by --plugin-manifest-url or " + PluginManifestUrlKey;
+ _logger.LogError(ex, LogTemplate, manifestUrl);
+ throw;
}
}
@@ -148,60 +183,56 @@ namespace Emby.Server.Implementations.Updates
}
/// <inheritdoc />
- public IEnumerable<PackageVersionInfo> GetCompatibleVersions(
- IEnumerable<PackageVersionInfo> availableVersions,
- Version minVersion = null,
- PackageVersionClass classification = PackageVersionClass.Release)
+ public IEnumerable<VersionInfo> GetCompatibleVersions(
+ IEnumerable<VersionInfo> availableVersions,
+ Version minVersion = null)
{
var appVer = _applicationHost.ApplicationVersion;
availableVersions = availableVersions
- .Where(x => x.classification == classification
- && Version.Parse(x.requiredVersionStr) <= appVer);
+ .Where(x => Version.Parse(x.targetAbi) <= appVer);
if (minVersion != null)
{
- availableVersions = availableVersions.Where(x => x.Version >= minVersion);
+ availableVersions = availableVersions.Where(x => x.version >= minVersion);
}
- return availableVersions.OrderByDescending(x => x.Version);
+ return availableVersions.OrderByDescending(x => x.version);
}
/// <inheritdoc />
- public IEnumerable<PackageVersionInfo> GetCompatibleVersions(
+ public IEnumerable<VersionInfo> GetCompatibleVersions(
IEnumerable<PackageInfo> availablePackages,
string name = null,
Guid guid = default,
- Version minVersion = null,
- PackageVersionClass classification = PackageVersionClass.Release)
+ Version minVersion = null)
{
var package = FilterPackages(availablePackages, name, guid).FirstOrDefault();
- // Package not found.
+ // Package not found in repository
if (package == null)
{
- return Enumerable.Empty<PackageVersionInfo>();
+ return Enumerable.Empty<VersionInfo>();
}
return GetCompatibleVersions(
package.versions,
- minVersion,
- classification);
+ minVersion);
}
/// <inheritdoc />
- public async IAsyncEnumerable<PackageVersionInfo> GetAvailablePluginUpdates([EnumeratorCancellation] CancellationToken cancellationToken = default)
+ public async Task<IEnumerable<VersionInfo>> GetAvailablePluginUpdates(CancellationToken cancellationToken = default)
{
var catalog = await GetAvailablePackages(cancellationToken).ConfigureAwait(false);
+ return GetAvailablePluginUpdates(catalog);
+ }
- var systemUpdateLevel = _applicationHost.SystemUpdateLevel;
-
- // Figure out what needs to be installed
+ private IEnumerable<VersionInfo> GetAvailablePluginUpdates(IReadOnlyList<PackageInfo> pluginCatalog)
+ {
foreach (var plugin in _applicationHost.Plugins)
{
- var compatibleversions = GetCompatibleVersions(catalog, plugin.Name, plugin.Id, plugin.Version, systemUpdateLevel);
- var version = compatibleversions.FirstOrDefault(y => y.Version > plugin.Version);
- if (version != null
- && !CompletedInstallations.Any(x => string.Equals(x.AssemblyGuid, version.guid, StringComparison.OrdinalIgnoreCase)))
+ var compatibleversions = GetCompatibleVersions(pluginCatalog, plugin.Name, plugin.Id, plugin.Version);
+ var version = compatibleversions.FirstOrDefault(y => y.version > plugin.Version);
+ if (version != null && !CompletedInstallations.Any(x => string.Equals(x.Guid, version.guid, StringComparison.OrdinalIgnoreCase)))
{
yield return version;
}
@@ -209,7 +240,7 @@ namespace Emby.Server.Implementations.Updates
}
/// <inheritdoc />
- public async Task InstallPackage(PackageVersionInfo package, CancellationToken cancellationToken)
+ public async Task InstallPackage(VersionInfo package, CancellationToken cancellationToken)
{
if (package == null)
{
@@ -218,11 +249,9 @@ namespace Emby.Server.Implementations.Updates
var installationInfo = new InstallationInfo
{
- Id = Guid.NewGuid(),
+ Guid = package.guid,
Name = package.name,
- AssemblyGuid = package.guid,
- UpdateClass = package.classification,
- Version = package.versionStr
+ Version = package.version.ToString()
};
var innerCancellationTokenSource = new CancellationTokenSource();
@@ -240,7 +269,7 @@ namespace Emby.Server.Implementations.Updates
var installationEventArgs = new InstallationEventArgs
{
InstallationInfo = installationInfo,
- PackageVersionInfo = package
+ VersionInfo = package
};
PackageInstalling?.Invoke(this, installationEventArgs);
@@ -265,7 +294,7 @@ namespace Emby.Server.Implementations.Updates
_currentInstallations.Remove(tuple);
}
- _logger.LogInformation("Package installation cancelled: {0} {1}", package.name, package.versionStr);
+ _logger.LogInformation("Package installation cancelled: {0} {1}", package.name, package.version);
PackageInstallationCancelled?.Invoke(this, installationEventArgs);
@@ -301,7 +330,7 @@ namespace Emby.Server.Implementations.Updates
/// <param name="package">The package.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns><see cref="Task" />.</returns>
- private async Task InstallPackageInternal(PackageVersionInfo package, CancellationToken cancellationToken)
+ private async Task InstallPackageInternal(VersionInfo package, CancellationToken cancellationToken)
{
// Set last update time if we were installed before
IPlugin plugin = _applicationHost.Plugins.FirstOrDefault(p => string.Equals(p.Id.ToString(), package.guid, StringComparison.OrdinalIgnoreCase))
@@ -313,26 +342,26 @@ namespace Emby.Server.Implementations.Updates
// Do plugin-specific processing
if (plugin == null)
{
- _logger.LogInformation("New plugin installed: {0} {1} {2}", package.name, package.versionStr ?? string.Empty, package.classification);
+ _logger.LogInformation("New plugin installed: {0} {1} {2}", package.name, package.version);
- PluginInstalled?.Invoke(this, new GenericEventArgs<PackageVersionInfo>(package));
+ PluginInstalled?.Invoke(this, new GenericEventArgs<VersionInfo>(package));
}
else
{
- _logger.LogInformation("Plugin updated: {0} {1} {2}", package.name, package.versionStr ?? string.Empty, package.classification);
+ _logger.LogInformation("Plugin updated: {0} {1} {2}", package.name, package.version);
- PluginUpdated?.Invoke(this, new GenericEventArgs<(IPlugin, PackageVersionInfo)>((plugin, package)));
+ PluginUpdated?.Invoke(this, new GenericEventArgs<(IPlugin, VersionInfo)>((plugin, package)));
}
_applicationHost.NotifyPendingRestart();
}
- private async Task PerformPackageInstallation(PackageVersionInfo package, CancellationToken cancellationToken)
+ private async Task PerformPackageInstallation(VersionInfo package, CancellationToken cancellationToken)
{
- var extension = Path.GetExtension(package.targetFilename);
+ var extension = Path.GetExtension(package.filename);
if (!string.Equals(extension, ".zip", StringComparison.OrdinalIgnoreCase))
{
- _logger.LogError("Only zip packages are supported. {Filename} is not a zip archive.", package.targetFilename);
+ _logger.LogError("Only zip packages are supported. {Filename} is not a zip archive.", package.filename);
return;
}
@@ -379,7 +408,7 @@ namespace Emby.Server.Implementations.Updates
}
/// <summary>
- /// Uninstalls a plugin
+ /// Uninstalls a plugin.
/// </summary>
/// <param name="plugin">The plugin.</param>
public void UninstallPlugin(IPlugin plugin)
@@ -437,7 +466,7 @@ namespace Emby.Server.Implementations.Updates
{
lock (_currentInstallationsLock)
{
- var install = _currentInstallations.Find(x => x.info.Id == id);
+ var install = _currentInstallations.Find(x => x.info.Guid == id.ToString());
if (install == default((InstallationInfo, CancellationTokenSource)))
{
return false;