aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CONTRIBUTORS.md1
-rw-r--r--Emby.Dlna/DlnaManager.cs9
-rw-r--r--Emby.Dlna/Main/DlnaEntryPoint.cs4
-rw-r--r--Emby.Notifications/Notifications.cs4
-rw-r--r--Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs5
-rw-r--r--Emby.Server.Implementations/ApplicationHost.cs50
-rw-r--r--Emby.Server.Implementations/Collections/CollectionManager.cs2
-rw-r--r--Emby.Server.Implementations/Devices/DeviceManager.cs2
-rw-r--r--Emby.Server.Implementations/Diagnostics/CommonProcess.cs2
-rw-r--r--Emby.Server.Implementations/EntryPoints/AutomaticRestartEntryPoint.cs4
-rw-r--r--Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs8
-rw-r--r--Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs5
-rw-r--r--Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs5
-rw-r--r--Emby.Server.Implementations/EntryPoints/ServerEventNotifier.cs5
-rw-r--r--Emby.Server.Implementations/EntryPoints/StartupWizard.cs7
-rw-r--r--Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs5
-rw-r--r--Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs4
-rw-r--r--Emby.Server.Implementations/IO/LibraryMonitor.cs3
-rw-r--r--Emby.Server.Implementations/Library/MediaSourceManager.cs16
-rw-r--r--Emby.Server.Implementations/Library/UserManager.cs4
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs2
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs5
-rw-r--r--Emby.Server.Implementations/Localization/LocalizationManager.cs282
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/br.csv (renamed from Emby.Server.Implementations/Localization/Ratings/br.txt)0
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/ca.csv (renamed from Emby.Server.Implementations/Localization/Ratings/ca.txt)0
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/co.csv (renamed from Emby.Server.Implementations/Localization/Ratings/co.txt)0
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/dk.csv (renamed from Emby.Server.Implementations/Localization/Ratings/dk.txt)0
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/es.csv (renamed from Emby.Server.Implementations/Localization/Ratings/es.txt)0
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/fr.csv (renamed from Emby.Server.Implementations/Localization/Ratings/fr.txt)0
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/gb.csv (renamed from Emby.Server.Implementations/Localization/Ratings/gb.txt)0
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/ie.csv (renamed from Emby.Server.Implementations/Localization/Ratings/ie.txt)0
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/jp.csv (renamed from Emby.Server.Implementations/Localization/Ratings/jp.txt)0
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/kz.csv (renamed from Emby.Server.Implementations/Localization/Ratings/kz.txt)0
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/mx.csv (renamed from Emby.Server.Implementations/Localization/Ratings/mx.txt)0
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/nl.csv (renamed from Emby.Server.Implementations/Localization/Ratings/nl.txt)0
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/nz.csv (renamed from Emby.Server.Implementations/Localization/Ratings/nz.txt)0
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/ro.csv (renamed from Emby.Server.Implementations/Localization/Ratings/ro.txt)0
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/uk.csv (renamed from Emby.Server.Implementations/Localization/Ratings/uk.txt)0
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/us.csv (renamed from Emby.Server.Implementations/Localization/Ratings/us.txt)0
-rw-r--r--Emby.Server.Implementations/Localization/TextLocalizer.cs63
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs (renamed from Emby.Server.Implementations/ScheduledTasks/ChapterImagesTask.cs)22
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs22
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs22
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/PeopleValidationTask.cs (renamed from Emby.Server.Implementations/ScheduledTasks/PeopleValidationTask.cs)22
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs119
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/RefreshMediaLibraryTask.cs (renamed from Emby.Server.Implementations/ScheduledTasks/RefreshMediaLibraryTask.cs)18
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Triggers/DailyTrigger.cs (renamed from Emby.Server.Implementations/ScheduledTasks/DailyTrigger.cs)0
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Triggers/IntervalTrigger.cs (renamed from Emby.Server.Implementations/ScheduledTasks/IntervalTrigger.cs)0
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Triggers/StartupTrigger.cs (renamed from Emby.Server.Implementations/ScheduledTasks/StartupTrigger.cs)0
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Triggers/WeeklyTrigger.cs (renamed from Emby.Server.Implementations/ScheduledTasks/WeeklyTrigger.cs)0
-rw-r--r--Emby.Server.Implementations/Updates/InstallationManager.cs14
-rw-r--r--Jellyfin.Server/Program.cs6
-rw-r--r--MediaBrowser.Api/ApiEntryPoint.cs39
-rw-r--r--MediaBrowser.Api/ItemUpdateService.cs7
-rw-r--r--MediaBrowser.Api/UserLibrary/PlaystateService.cs12
-rw-r--r--MediaBrowser.Common/IApplicationHost.cs4
-rw-r--r--MediaBrowser.Controller/Extensions/StringExtensions.cs46
-rw-r--r--MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs124
-rw-r--r--MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs22
-rw-r--r--MediaBrowser.Controller/Plugins/IServerEntryPoint.cs3
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs58
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs372
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs149
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs307
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs43
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs59
-rw-r--r--MediaBrowser.Model/Globalization/ILocalizationManager.cs8
-rw-r--r--MediaBrowser.Providers/Manager/MetadataService.cs3
-rw-r--r--MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs15
-rw-r--r--MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs18
-rw-r--r--MediaBrowser.WebDashboard/Api/DashboardService.cs6
-rw-r--r--MediaBrowser.WebDashboard/ServerEntryPoint.cs5
-rw-r--r--MediaBrowser.XbmcMetadata/EntryPoint.cs5
-rwxr-xr-xbump_version3
-rw-r--r--deployment/centos-package-x64/Dockerfile15
l---------deployment/centos-package-x64/clean.sh1
l---------deployment/centos-package-x64/package.sh1
l---------deployment/centos-package-x64/pkg-src1
-rw-r--r--deployment/fedora-package-x64/pkg-src/jellyfin.env2
-rw-r--r--deployment/fedora-package-x64/pkg-src/jellyfin.spec33
80 files changed, 553 insertions, 1550 deletions
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index eca16ad38..5e3455fba 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -15,6 +15,7 @@
- [cvium](https://github.com/cvium)
- [wtayl0r](https://github.com/wtayl0r)
- [TtheCreator](https://github.com/Tthecreator)
+ - [dkanada](https://github.com/dkanada)
- [LogicalPhallacy](https://github.com/LogicalPhallacy/)
- [RazeLighter777](https://github.com/RazeLighter777)
diff --git a/Emby.Dlna/DlnaManager.cs b/Emby.Dlna/DlnaManager.cs
index a43888270..c507b14e9 100644
--- a/Emby.Dlna/DlnaManager.cs
+++ b/Emby.Dlna/DlnaManager.cs
@@ -4,6 +4,7 @@ using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
+using System.Threading.Tasks;
using Emby.Dlna.Profiles;
using Emby.Dlna.Server;
using MediaBrowser.Common.Configuration;
@@ -48,11 +49,11 @@ namespace Emby.Dlna
_assemblyInfo = assemblyInfo;
}
- public void InitProfiles()
+ public async Task InitProfilesAsync()
{
try
{
- ExtractSystemProfiles();
+ await ExtractSystemProfilesAsync();
LoadProfiles();
}
catch (Exception ex)
@@ -359,7 +360,7 @@ namespace Emby.Dlna
};
}
- private void ExtractSystemProfiles()
+ private async Task ExtractSystemProfilesAsync()
{
var namespaceName = GetType().Namespace + ".Profiles.Xml.";
@@ -383,7 +384,7 @@ namespace Emby.Dlna
using (var fileStream = _fileSystem.GetFileStream(path, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read))
{
- stream.CopyTo(fileStream);
+ await stream.CopyToAsync(fileStream);
}
}
}
diff --git a/Emby.Dlna/Main/DlnaEntryPoint.cs b/Emby.Dlna/Main/DlnaEntryPoint.cs
index ad90da49b..7398b24cd 100644
--- a/Emby.Dlna/Main/DlnaEntryPoint.cs
+++ b/Emby.Dlna/Main/DlnaEntryPoint.cs
@@ -125,9 +125,9 @@ namespace Emby.Dlna.Main
Current = this;
}
- public void Run()
+ public async Task RunAsync()
{
- ((DlnaManager)_dlnaManager).InitProfiles();
+ await ((DlnaManager)_dlnaManager).InitProfilesAsync().ConfigureAwait(false);
ReloadComponents();
diff --git a/Emby.Notifications/Notifications.cs b/Emby.Notifications/Notifications.cs
index fbdc39f94..045aa6f16 100644
--- a/Emby.Notifications/Notifications.cs
+++ b/Emby.Notifications/Notifications.cs
@@ -71,12 +71,14 @@ namespace Emby.Notifications
_coreNotificationTypes = new CoreNotificationTypes(localization, appHost).GetNotificationTypes().Select(i => i.Type).ToArray();
}
- public void Run()
+ public Task RunAsync()
{
_libraryManager.ItemAdded += _libraryManager_ItemAdded;
_appHost.HasPendingRestartChanged += _appHost_HasPendingRestartChanged;
_appHost.HasUpdateAvailableChanged += _appHost_HasUpdateAvailableChanged;
_activityManager.EntryCreated += _activityManager_EntryCreated;
+
+ return Task.CompletedTask;
}
private async void _appHost_HasPendingRestartChanged(object sender, EventArgs e)
diff --git a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs
index 54b70d3b5..739f68767 100644
--- a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs
+++ b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs
@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
+using System.Threading.Tasks;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Plugins;
using MediaBrowser.Common.Updates;
@@ -58,7 +59,7 @@ namespace Emby.Server.Implementations.Activity
_deviceManager = deviceManager;
}
- public void Run()
+ public Task RunAsync()
{
_taskManager.TaskCompleted += _taskManager_TaskCompleted;
@@ -90,6 +91,8 @@ namespace Emby.Server.Implementations.Activity
_deviceManager.CameraImageUploaded += _deviceManager_CameraImageUploaded;
_appHost.ApplicationUpdated += _appHost_ApplicationUpdated;
+
+ return Task.CompletedTask;
}
void _deviceManager_CameraImageUploaded(object sender, GenericEventArgs<CameraImageUploadInfo> e)
diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs
index c59a648e5..365eb5856 100644
--- a/Emby.Server.Implementations/ApplicationHost.cs
+++ b/Emby.Server.Implementations/ApplicationHost.cs
@@ -110,7 +110,6 @@ using MediaBrowser.XbmcMetadata.Providers;
using Microsoft.Extensions.Logging;
using ServiceStack;
using ServiceStack.Text.Jsv;
-using StringExtensions = MediaBrowser.Controller.Extensions.StringExtensions;
using X509Certificate = System.Security.Cryptography.X509Certificates.X509Certificate;
namespace Emby.Server.Implementations
@@ -302,7 +301,7 @@ namespace Emby.Server.Implementations
private ILiveTvManager LiveTvManager { get; set; }
- public ILocalizationManager LocalizationManager { get; set; }
+ public LocalizationManager LocalizationManager { get; set; }
private IEncodingManager EncodingManager { get; set; }
private IChannelManager ChannelManager { get; set; }
@@ -646,8 +645,10 @@ namespace Emby.Server.Implementations
/// <summary>
/// Runs the startup tasks.
/// </summary>
- public Task RunStartupTasks()
+ public async Task RunStartupTasks()
{
+ Logger.LogInformation("Running startup tasks");
+
Resolve<ITaskManager>().AddTasks(GetExports<IScheduledTask>(false));
ConfigurationManager.ConfigurationUpdated += OnConfigurationUpdated;
@@ -666,20 +667,20 @@ namespace Emby.Server.Implementations
Logger.LogInformation("ServerId: {0}", SystemId);
var entryPoints = GetExports<IServerEntryPoint>();
- RunEntryPoints(entryPoints, true);
+
+ var now = DateTime.UtcNow;
+ await Task.WhenAll(StartEntryPoints(entryPoints, true));
+ Logger.LogInformation("Executed all pre-startup entry points in {Elapsed:fff} ms", DateTime.Now - now);
Logger.LogInformation("Core startup complete");
HttpServer.GlobalResponse = null;
- Logger.LogInformation("Post-init migrations complete");
-
- RunEntryPoints(entryPoints, false);
- Logger.LogInformation("All entry points have started");
-
- return Task.CompletedTask;
+ now = DateTime.UtcNow;
+ await Task.WhenAll(StartEntryPoints(entryPoints, false));
+ Logger.LogInformation("Executed all post-startup entry points in {Elapsed:fff} ms", DateTime.Now - now);
}
- private void RunEntryPoints(IEnumerable<IServerEntryPoint> entryPoints, bool isBeforeStartup)
+ private IEnumerable<Task> StartEntryPoints(IEnumerable<IServerEntryPoint> entryPoints, bool isBeforeStartup)
{
foreach (var entryPoint in entryPoints)
{
@@ -688,22 +689,13 @@ namespace Emby.Server.Implementations
continue;
}
- var name = entryPoint.GetType().FullName;
- Logger.LogInformation("Starting entry point {Name}", name);
- var now = DateTime.UtcNow;
- try
- {
- entryPoint.Run();
- }
- catch (Exception ex)
- {
- Logger.LogError(ex, "Error while running entrypoint {Name}", name);
- }
- Logger.LogInformation("Entry point completed: {Name}. Duration: {Duration} seconds", name, (DateTime.UtcNow - now).TotalSeconds.ToString(CultureInfo.InvariantCulture), "ImageInfos");
+ Logger.LogDebug("Starting entry point {Type}", entryPoint.GetType());
+
+ yield return entryPoint.RunAsync();
}
}
- public void Init()
+ public async Task Init()
{
HttpPort = ServerConfigurationManager.Configuration.HttpServerPortNumber;
HttpsPort = ServerConfigurationManager.Configuration.HttpsPortNumber;
@@ -733,7 +725,7 @@ namespace Emby.Server.Implementations
SetHttpLimit();
- RegisterResources();
+ await RegisterResources();
FindParts();
}
@@ -748,7 +740,7 @@ namespace Emby.Server.Implementations
/// <summary>
/// Registers resources that classes will depend on
/// </summary>
- protected void RegisterResources()
+ protected async Task RegisterResources()
{
RegisterSingleInstance(ConfigurationManager);
RegisterSingleInstance<IApplicationHost>(this);
@@ -809,9 +801,9 @@ namespace Emby.Server.Implementations
IAssemblyInfo assemblyInfo = new AssemblyInfo();
RegisterSingleInstance(assemblyInfo);
- LocalizationManager = new LocalizationManager(ServerConfigurationManager, FileSystemManager, JsonSerializer, LoggerFactory, assemblyInfo, new TextLocalizer());
- StringExtensions.LocalizationManager = LocalizationManager;
- RegisterSingleInstance(LocalizationManager);
+ LocalizationManager = new LocalizationManager(ServerConfigurationManager, FileSystemManager, JsonSerializer, LoggerFactory);
+ await LocalizationManager.LoadAll();
+ RegisterSingleInstance<ILocalizationManager>(LocalizationManager);
BlurayExaminer = new BdInfoExaminer(FileSystemManager);
RegisterSingleInstance(BlurayExaminer);
diff --git a/Emby.Server.Implementations/Collections/CollectionManager.cs b/Emby.Server.Implementations/Collections/CollectionManager.cs
index c8b822970..812e48a1f 100644
--- a/Emby.Server.Implementations/Collections/CollectionManager.cs
+++ b/Emby.Server.Implementations/Collections/CollectionManager.cs
@@ -353,7 +353,7 @@ namespace Emby.Server.Implementations.Collections
_logger = logger;
}
- public async void Run()
+ public async Task RunAsync()
{
if (!_config.Configuration.CollectionsUpgraded && _config.Configuration.IsStartupWizardCompleted)
{
diff --git a/Emby.Server.Implementations/Devices/DeviceManager.cs b/Emby.Server.Implementations/Devices/DeviceManager.cs
index e94a649bf..ec3649bca 100644
--- a/Emby.Server.Implementations/Devices/DeviceManager.cs
+++ b/Emby.Server.Implementations/Devices/DeviceManager.cs
@@ -425,7 +425,7 @@ namespace Emby.Server.Implementations.Devices
_logger = logger;
}
- public async void Run()
+ public async Task RunAsync()
{
if (!_config.Configuration.CameraUploadUpgraded && _config.Configuration.IsStartupWizardCompleted)
{
diff --git a/Emby.Server.Implementations/Diagnostics/CommonProcess.cs b/Emby.Server.Implementations/Diagnostics/CommonProcess.cs
index 55539eafc..78b22bda3 100644
--- a/Emby.Server.Implementations/Diagnostics/CommonProcess.cs
+++ b/Emby.Server.Implementations/Diagnostics/CommonProcess.cs
@@ -105,7 +105,7 @@ namespace Emby.Server.Implementations.Diagnostics
{
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.
diff --git a/Emby.Server.Implementations/EntryPoints/AutomaticRestartEntryPoint.cs b/Emby.Server.Implementations/EntryPoints/AutomaticRestartEntryPoint.cs
index 0fc4c3858..361656ff2 100644
--- a/Emby.Server.Implementations/EntryPoints/AutomaticRestartEntryPoint.cs
+++ b/Emby.Server.Implementations/EntryPoints/AutomaticRestartEntryPoint.cs
@@ -37,12 +37,14 @@ namespace Emby.Server.Implementations.EntryPoints
_timerFactory = timerFactory;
}
- public void Run()
+ public Task RunAsync()
{
if (_appHost.CanSelfRestart)
{
_appHost.HasPendingRestartChanged += _appHost_HasPendingRestartChanged;
}
+
+ return Task.CompletedTask;
}
void _appHost_HasPendingRestartChanged(object sender, EventArgs e)
diff --git a/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs b/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs
index 8755ee3a7..56c730c80 100644
--- a/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs
+++ b/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs
@@ -61,17 +61,17 @@ namespace Emby.Server.Implementations.EntryPoints
return string.Join("|", values.ToArray());
}
- void _config_ConfigurationUpdated(object sender, EventArgs e)
+ private async void _config_ConfigurationUpdated(object sender, EventArgs e)
{
if (!string.Equals(_lastConfigIdentifier, GetConfigIdentifier(), StringComparison.OrdinalIgnoreCase))
{
DisposeNat();
- Run();
+ await RunAsync();
}
}
- public void Run()
+ public Task RunAsync()
{
if (_config.Configuration.EnableUPnP && _config.Configuration.EnableRemoteAccess)
{
@@ -80,6 +80,8 @@ namespace Emby.Server.Implementations.EntryPoints
_config.ConfigurationUpdated -= _config_ConfigurationUpdated;
_config.ConfigurationUpdated += _config_ConfigurationUpdated;
+
+ return Task.CompletedTask;
}
private void Start()
diff --git a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
index a670a289c..9b61809c7 100644
--- a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
+++ b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading;
+using System.Threading.Tasks;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
@@ -65,7 +66,7 @@ namespace Emby.Server.Implementations.EntryPoints
_providerManager = providerManager;
}
- public void Run()
+ public Task RunAsync()
{
_libraryManager.ItemAdded += libraryManager_ItemAdded;
_libraryManager.ItemUpdated += libraryManager_ItemUpdated;
@@ -74,6 +75,8 @@ namespace Emby.Server.Implementations.EntryPoints
_providerManager.RefreshCompleted += _providerManager_RefreshCompleted;
_providerManager.RefreshStarted += _providerManager_RefreshStarted;
_providerManager.RefreshProgress += _providerManager_RefreshProgress;
+
+ return Task.CompletedTask;
}
private Dictionary<Guid, DateTime> _lastProgressMessageTimes = new Dictionary<Guid, DateTime>();
diff --git a/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs b/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs
index e37ea96a1..0186da9e1 100644
--- a/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs
+++ b/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs
@@ -1,6 +1,7 @@
using System;
using System.Linq;
using System.Threading;
+using System.Threading.Tasks;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.Plugins;
@@ -24,12 +25,14 @@ namespace Emby.Server.Implementations.EntryPoints
_liveTvManager = liveTvManager;
}
- public void Run()
+ public Task RunAsync()
{
_liveTvManager.TimerCancelled += _liveTvManager_TimerCancelled;
_liveTvManager.SeriesTimerCancelled += _liveTvManager_SeriesTimerCancelled;
_liveTvManager.TimerCreated += _liveTvManager_TimerCreated;
_liveTvManager.SeriesTimerCreated += _liveTvManager_SeriesTimerCreated;
+
+ return Task.CompletedTask;
}
private void _liveTvManager_SeriesTimerCreated(object sender, MediaBrowser.Model.Events.GenericEventArgs<TimerEventInfo> e)
diff --git a/Emby.Server.Implementations/EntryPoints/ServerEventNotifier.cs b/Emby.Server.Implementations/EntryPoints/ServerEventNotifier.cs
index 92ea3a8f4..091dd6a45 100644
--- a/Emby.Server.Implementations/EntryPoints/ServerEventNotifier.cs
+++ b/Emby.Server.Implementations/EntryPoints/ServerEventNotifier.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Threading;
+using System.Threading.Tasks;
using MediaBrowser.Common.Plugins;
using MediaBrowser.Common.Updates;
using MediaBrowser.Controller;
@@ -49,7 +50,7 @@ namespace Emby.Server.Implementations.EntryPoints
_sessionManager = sessionManager;
}
- public void Run()
+ public Task RunAsync()
{
_userManager.UserDeleted += userManager_UserDeleted;
_userManager.UserUpdated += userManager_UserUpdated;
@@ -65,6 +66,8 @@ namespace Emby.Server.Implementations.EntryPoints
_installationManager.PackageInstallationFailed += _installationManager_PackageInstallationFailed;
_taskManager.TaskCompleted += _taskManager_TaskCompleted;
+
+ return Task.CompletedTask;
}
void _installationManager_PackageInstalling(object sender, InstallationEventArgs e)
diff --git a/Emby.Server.Implementations/EntryPoints/StartupWizard.cs b/Emby.Server.Implementations/EntryPoints/StartupWizard.cs
index bb96120f4..8be6db87d 100644
--- a/Emby.Server.Implementations/EntryPoints/StartupWizard.cs
+++ b/Emby.Server.Implementations/EntryPoints/StartupWizard.cs
@@ -1,3 +1,4 @@
+using System.Threading.Tasks;
using Emby.Server.Implementations.Browser;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
@@ -32,11 +33,11 @@ namespace Emby.Server.Implementations.EntryPoints
/// <summary>
/// Runs this instance.
/// </summary>
- public void Run()
+ public Task RunAsync()
{
if (!_appHost.CanLaunchWebBrowser)
{
- return;
+ return Task.CompletedTask;
}
if (!_config.Configuration.IsStartupWizardCompleted)
@@ -52,6 +53,8 @@ namespace Emby.Server.Implementations.EntryPoints
BrowserLauncher.OpenWebApp(_appHost);
}
}
+
+ return Task.CompletedTask;
}
/// <summary>
diff --git a/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs b/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs
index 2c8246d13..5b90dc1fb 100644
--- a/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs
+++ b/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs
@@ -1,4 +1,5 @@
using System;
+using System.Threading.Tasks;
using Emby.Server.Implementations.Udp;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Plugins;
@@ -43,7 +44,7 @@ namespace Emby.Server.Implementations.EntryPoints
/// <summary>
/// Runs this instance.
/// </summary>
- public void Run()
+ public Task RunAsync()
{
var udpServer = new UdpServer(_logger, _appHost, _json, _socketFactory);
@@ -57,6 +58,8 @@ namespace Emby.Server.Implementations.EntryPoints
{
_logger.LogError(ex, "Failed to start UDP Server");
}
+
+ return Task.CompletedTask;
}
/// <summary>
diff --git a/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs b/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs
index 69784e410..d91a2d581 100644
--- a/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs
+++ b/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs
@@ -38,9 +38,11 @@ namespace Emby.Server.Implementations.EntryPoints
_timerFactory = timerFactory;
}
- public void Run()
+ public Task RunAsync()
{
_userDataManager.UserDataSaved += _userDataManager_UserDataSaved;
+
+ return Task.CompletedTask;
}
void _userDataManager_UserDataSaved(object sender, UserDataSaveEventArgs e)
diff --git a/Emby.Server.Implementations/IO/LibraryMonitor.cs b/Emby.Server.Implementations/IO/LibraryMonitor.cs
index ed5fddb52..3a746ef60 100644
--- a/Emby.Server.Implementations/IO/LibraryMonitor.cs
+++ b/Emby.Server.Implementations/IO/LibraryMonitor.cs
@@ -633,9 +633,10 @@ namespace Emby.Server.Implementations.IO
_monitor = monitor;
}
- public void Run()
+ public Task RunAsync()
{
_monitor.Start();
+ return Task.CompletedTask;
}
public void Dispose()
diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs
index b83fb9b39..4f72651d4 100644
--- a/Emby.Server.Implementations/Library/MediaSourceManager.cs
+++ b/Emby.Server.Implementations/Library/MediaSourceManager.cs
@@ -322,18 +322,18 @@ namespace Emby.Server.Implementations.Library
private string[] NormalizeLanguage(string language)
{
- if (language != null)
+ if (language == null)
{
- var culture = _localizationManager.FindLanguageInfo(language);
- if (culture != null)
- {
- return culture.ThreeLetterISOLanguageNames;
- }
+ return Array.Empty<string>();
+ }
- return new string[] { language };
+ var culture = _localizationManager.FindLanguageInfo(language);
+ if (culture != null)
+ {
+ return culture.ThreeLetterISOLanguageNames;
}
- return Array.Empty<string>();
+ return new string[] { language };
}
private void SetDefaultSubtitleStreamIndex(MediaSourceInfo source, UserItemData userData, User user, bool allowRememberingSelection)
diff --git a/Emby.Server.Implementations/Library/UserManager.cs b/Emby.Server.Implementations/Library/UserManager.cs
index 215978341..3ff84382f 100644
--- a/Emby.Server.Implementations/Library/UserManager.cs
+++ b/Emby.Server.Implementations/Library/UserManager.cs
@@ -1182,9 +1182,11 @@ namespace Emby.Server.Implementations.Library
_sessionManager = sessionManager;
}
- public void Run()
+ public Task RunAsync()
{
_userManager.UserPolicyUpdated += _userManager_UserPolicyUpdated;
+
+ return Task.CompletedTask;
}
private void _userManager_UserPolicyUpdated(object sender, GenericEventArgs<User> e)
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
index c5c0ac463..b9a42a6ff 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
@@ -122,7 +122,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
}
}
- public async void Start()
+ public async Task Start()
{
_timerProvider.RestartTimers();
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs
index 982a54b68..9c9ba09f5 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs
@@ -1,12 +1,13 @@
+using System.Threading.Tasks;
using MediaBrowser.Controller.Plugins;
namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
public class EntryPoint : IServerEntryPoint
{
- public void Run()
+ public Task RunAsync()
{
- EmbyTV.Current.Start();
+ return EmbyTV.Current.Start();
}
public void Dispose()
diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs
index 3c2f9b427..262ca24ec 100644
--- a/Emby.Server.Implementations/Localization/LocalizationManager.cs
+++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs
@@ -4,12 +4,14 @@ using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Reflection;
using MediaBrowser.Model.Serialization;
using Microsoft.Extensions.Logging;
@@ -36,8 +38,7 @@ namespace Emby.Server.Implementations.Localization
private readonly IFileSystem _fileSystem;
private readonly IJsonSerializer _jsonSerializer;
private readonly ILogger _logger;
- private readonly IAssemblyInfo _assemblyInfo;
- private readonly ITextLocalizer _textLocalizer;
+ private static readonly Assembly _assembly = typeof(LocalizationManager).Assembly;
/// <summary>
/// Initializes a new instance of the <see cref="LocalizationManager" /> class.
@@ -49,67 +50,57 @@ namespace Emby.Server.Implementations.Localization
IServerConfigurationManager configurationManager,
IFileSystem fileSystem,
IJsonSerializer jsonSerializer,
- ILoggerFactory loggerFactory,
- IAssemblyInfo assemblyInfo,
- ITextLocalizer textLocalizer)
+ ILoggerFactory loggerFactory)
{
_configurationManager = configurationManager;
_fileSystem = fileSystem;
_jsonSerializer = jsonSerializer;
_logger = loggerFactory.CreateLogger(nameof(LocalizationManager));
- _assemblyInfo = assemblyInfo;
- _textLocalizer = textLocalizer;
-
- ExtractAll();
}
- private void ExtractAll()
+ public async Task LoadAll()
{
- var type = GetType();
- var resourcePath = type.Namespace + ".Ratings.";
-
- var localizationPath = LocalizationPath;
+ const string ratingsResource = "Emby.Server.Implementations.Ratings.";
- Directory.CreateDirectory(localizationPath);
+ Directory.CreateDirectory(LocalizationPath);
- var existingFiles = GetRatingsFiles(localizationPath)
- .Select(Path.GetFileName)
- .ToList();
+ var existingFiles = GetRatingsFiles(LocalizationPath).Select(Path.GetFileName);
// Extract from the assembly
- foreach (var resource in _assemblyInfo
- .GetManifestResourceNames(type)
- .Where(i => i.StartsWith(resourcePath)))
+ foreach (var resource in _assembly.GetManifestResourceNames()
+ .Where(i => i.StartsWith(ratingsResource)))
{
- var filename = "ratings-" + resource.Substring(resourcePath.Length);
+ string filename = "ratings-" + resource.Substring(ratingsResource.Length);
if (!existingFiles.Contains(filename))
{
- using (var stream = _assemblyInfo.GetManifestResourceStream(type, resource))
+ using (var stream = _assembly.GetManifestResourceStream(resource))
{
- var target = Path.Combine(localizationPath, filename);
+ string target = Path.Combine(LocalizationPath, filename);
_logger.LogInformation("Extracting ratings to {0}", target);
using (var fs = _fileSystem.GetFileStream(target, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read))
{
- stream.CopyTo(fs);
+ await stream.CopyToAsync(fs);
}
}
}
}
- foreach (var file in GetRatingsFiles(localizationPath))
+ foreach (var file in GetRatingsFiles(LocalizationPath))
{
- LoadRatings(file);
+ await LoadRatings(file);
}
LoadAdditionalRatings();
+
+ await LoadCultures();
}
private void LoadAdditionalRatings()
{
- LoadRatings("au", new[] {
-
+ LoadRatings("au", new[]
+ {
new ParentalRating("AU-G", 1),
new ParentalRating("AU-PG", 5),
new ParentalRating("AU-M", 6),
@@ -120,8 +111,8 @@ namespace Emby.Server.Implementations.Localization
new ParentalRating("AU-RC", 11)
});
- LoadRatings("be", new[] {
-
+ LoadRatings("be", new[]
+ {
new ParentalRating("BE-AL", 1),
new ParentalRating("BE-MG6", 2),
new ParentalRating("BE-6", 3),
@@ -130,8 +121,8 @@ namespace Emby.Server.Implementations.Localization
new ParentalRating("BE-16", 8)
});
- LoadRatings("de", new[] {
-
+ LoadRatings("de", new[]
+ {
new ParentalRating("DE-0", 1),
new ParentalRating("FSK-0", 1),
new ParentalRating("DE-6", 5),
@@ -144,8 +135,8 @@ namespace Emby.Server.Implementations.Localization
new ParentalRating("FSK-18", 9)
});
- LoadRatings("ru", new[] {
-
+ LoadRatings("ru", new[]
+ {
new ParentalRating("RU-0+", 1),
new ParentalRating("RU-6+", 3),
new ParentalRating("RU-12+", 7),
@@ -159,29 +150,20 @@ namespace Emby.Server.Implementations.Localization
_allParentalRatings[country] = ratings.ToDictionary(i => i.Name);
}
- private List<string> GetRatingsFiles(string directory)
- {
- return _fileSystem.GetFilePaths(directory, false)
- .Where(i => string.Equals(Path.GetExtension(i), ".txt", StringComparison.OrdinalIgnoreCase))
- .Where(i => Path.GetFileName(i).StartsWith("ratings-", StringComparison.OrdinalIgnoreCase))
- .ToList();
- }
+ private IEnumerable<string> GetRatingsFiles(string directory)
+ => _fileSystem.GetFilePaths(directory, false)
+ .Where(i => string.Equals(Path.GetExtension(i), ".csv", StringComparison.OrdinalIgnoreCase))
+ .Where(i => Path.GetFileName(i).StartsWith("ratings-", StringComparison.OrdinalIgnoreCase));
/// <summary>
/// Gets the localization path.
/// </summary>
/// <value>The localization path.</value>
- public string LocalizationPath => Path.Combine(_configurationManager.ApplicationPaths.ProgramDataPath, "localization");
-
- public string RemoveDiacritics(string text)
- {
- return _textLocalizer.RemoveDiacritics(text);
- }
+ public string LocalizationPath
+ => Path.Combine(_configurationManager.ApplicationPaths.ProgramDataPath, "localization");
public string NormalizeFormKD(string text)
- {
- return _textLocalizer.NormalizeFormKD(text);
- }
+ => text.Normalize(NormalizationForm.FormKD);
private CultureDto[] _cultures;
@@ -190,90 +172,88 @@ namespace Emby.Server.Implementations.Localization
/// </summary>
/// <returns>IEnumerable{CultureDto}.</returns>
public CultureDto[] GetCultures()
- {
- var result = _cultures;
- if (result != null)
- {
- return result;
- }
+ => _cultures;
- var type = GetType();
- var path = type.Namespace + ".iso6392.txt";
+ private async Task LoadCultures()
+ {
+ List<CultureDto> list = new List<CultureDto>();
- var list = new List<CultureDto>();
+ const string path = "Emby.Server.Implementations.Localization.iso6392.txt";
- using (var stream = _assemblyInfo.GetManifestResourceStream(type, path))
+ using (var stream = _assembly.GetManifestResourceStream(path))
+ using (var reader = new StreamReader(stream))
{
- using (var reader = new StreamReader(stream))
+ while (!reader.EndOfStream)
{
- while (!reader.EndOfStream)
+ var line = await reader.ReadLineAsync();
+
+ if (string.IsNullOrWhiteSpace(line))
+ {
+ continue;
+ }
+
+ var parts = line.Split('|');
+
+ if (parts.Length == 5)
{
- var line = reader.ReadLine();
+ string name = parts[3];
+ if (string.IsNullOrWhiteSpace(name))
+ {
+ continue;
+ }
- if (!string.IsNullOrWhiteSpace(line))
+ string twoCharName = parts[2];
+ if (string.IsNullOrWhiteSpace(twoCharName))
+ {
+ continue;
+ }
+
+ string[] threeletterNames;
+ if (string.IsNullOrWhiteSpace(parts[1]))
+ {
+ threeletterNames = new [] { parts[0] };
+ }
+ else
{
- var parts = line.Split('|');
-
- if (parts.Length == 5)
- {
- var threeletterNames = new List<string> { parts[0] };
- if (!string.IsNullOrWhiteSpace(parts[1]))
- {
- threeletterNames.Add(parts[1]);
- }
-
- list.Add(new CultureDto
- {
- DisplayName = parts[3],
- Name = parts[3],
- ThreeLetterISOLanguageNames = threeletterNames.ToArray(),
- TwoLetterISOLanguageName = parts[2]
- });
- }
+ threeletterNames = new [] { parts[0], parts[1] };
}
+
+ list.Add(new CultureDto
+ {
+ DisplayName = name,
+ Name = name,
+ ThreeLetterISOLanguageNames = threeletterNames,
+ TwoLetterISOLanguageName = twoCharName
+ });
}
}
}
- result = list.Where(i => !string.IsNullOrWhiteSpace(i.Name) &&
- !string.IsNullOrWhiteSpace(i.DisplayName) &&
- i.ThreeLetterISOLanguageNames.Length > 0 &&
- !string.IsNullOrWhiteSpace(i.TwoLetterISOLanguageName)).ToArray();
-
- _cultures = result;
-
- return result;
+ _cultures = list.ToArray();
}
public CultureDto FindLanguageInfo(string language)
- {
- return GetCultures()
- .FirstOrDefault(i => string.Equals(i.DisplayName, language, StringComparison.OrdinalIgnoreCase) ||
- string.Equals(i.Name, language, StringComparison.OrdinalIgnoreCase) ||
- i.ThreeLetterISOLanguageNames.Contains(language, StringComparer.OrdinalIgnoreCase) ||
- string.Equals(i.TwoLetterISOLanguageName, language, StringComparison.OrdinalIgnoreCase));
- }
+ => GetCultures()
+ .FirstOrDefault(i =>
+ string.Equals(i.DisplayName, language, StringComparison.OrdinalIgnoreCase)
+ || string.Equals(i.Name, language, StringComparison.OrdinalIgnoreCase)
+ || i.ThreeLetterISOLanguageNames.Contains(language, StringComparer.OrdinalIgnoreCase)
+ || string.Equals(i.TwoLetterISOLanguageName, language, StringComparison.OrdinalIgnoreCase));
/// <summary>
/// Gets the countries.
/// </summary>
/// <returns>IEnumerable{CountryInfo}.</returns>
- public CountryInfo[] GetCountries()
- {
- // ToDo: DeserializeFromStream seems broken in this case
- string jsonCountries = "[{\"Name\":\"AF\",\"DisplayName\":\"Afghanistan\",\"TwoLetterISORegionName\":\"AF\",\"ThreeLetterISORegionName\":\"AFG\"},{\"Name\":\"AL\",\"DisplayName\":\"Albania\",\"TwoLetterISORegionName\":\"AL\",\"ThreeLetterISORegionName\":\"ALB\"},{\"Name\":\"DZ\",\"DisplayName\":\"Algeria\",\"TwoLetterISORegionName\":\"DZ\",\"ThreeLetterISORegionName\":\"DZA\"},{\"Name\":\"AR\",\"DisplayName\":\"Argentina\",\"TwoLetterISORegionName\":\"AR\",\"ThreeLetterISORegionName\":\"ARG\"},{\"Name\":\"AM\",\"DisplayName\":\"Armenia\",\"TwoLetterISORegionName\":\"AM\",\"ThreeLetterISORegionName\":\"ARM\"},{\"Name\":\"AU\",\"DisplayName\":\"Australia\",\"TwoLetterISORegionName\":\"AU\",\"ThreeLetterISORegionName\":\"AUS\"},{\"Name\":\"AT\",\"DisplayName\":\"Austria\",\"TwoLetterISORegionName\":\"AT\",\"ThreeLetterISORegionName\":\"AUT\"},{\"Name\":\"AZ\",\"DisplayName\":\"Azerbaijan\",\"TwoLetterISORegionName\":\"AZ\",\"ThreeLetterISORegionName\":\"AZE\"},{\"Name\":\"BH\",\"DisplayName\":\"Bahrain\",\"TwoLetterISORegionName\":\"BH\",\"ThreeLetterISORegionName\":\"BHR\"},{\"Name\":\"BD\",\"DisplayName\":\"Bangladesh\",\"TwoLetterISORegionName\":\"BD\",\"ThreeLetterISORegionName\":\"BGD\"},{\"Name\":\"BY\",\"DisplayName\":\"Belarus\",\"TwoLetterISORegionName\":\"BY\",\"ThreeLetterISORegionName\":\"BLR\"},{\"Name\":\"BE\",\"DisplayName\":\"Belgium\",\"TwoLetterISORegionName\":\"BE\",\"ThreeLetterISORegionName\":\"BEL\"},{\"Name\":\"BZ\",\"DisplayName\":\"Belize\",\"TwoLetterISORegionName\":\"BZ\",\"ThreeLetterISORegionName\":\"BLZ\"},{\"Name\":\"VE\",\"DisplayName\":\"Bolivarian Republic of Venezuela\",\"TwoLetterISORegionName\":\"VE\",\"ThreeLetterISORegionName\":\"VEN\"},{\"Name\":\"BO\",\"DisplayName\":\"Bolivia\",\"TwoLetterISORegionName\":\"BO\",\"ThreeLetterISORegionName\":\"BOL\"},{\"Name\":\"BA\",\"DisplayName\":\"Bosnia and Herzegovina\",\"TwoLetterISORegionName\":\"BA\",\"ThreeLetterISORegionName\":\"BIH\"},{\"Name\":\"BW\",\"DisplayName\":\"Botswana\",\"TwoLetterISORegionName\":\"BW\",\"ThreeLetterISORegionName\":\"BWA\"},{\"Name\":\"BR\",\"DisplayName\":\"Brazil\",\"TwoLetterISORegionName\":\"BR\",\"ThreeLetterISORegionName\":\"BRA\"},{\"Name\":\"BN\",\"DisplayName\":\"Brunei Darussalam\",\"TwoLetterISORegionName\":\"BN\",\"ThreeLetterISORegionName\":\"BRN\"},{\"Name\":\"BG\",\"DisplayName\":\"Bulgaria\",\"TwoLetterISORegionName\":\"BG\",\"ThreeLetterISORegionName\":\"BGR\"},{\"Name\":\"KH\",\"DisplayName\":\"Cambodia\",\"TwoLetterISORegionName\":\"KH\",\"ThreeLetterISORegionName\":\"KHM\"},{\"Name\":\"CM\",\"DisplayName\":\"Cameroon\",\"TwoLetterISORegionName\":\"CM\",\"ThreeLetterISORegionName\":\"CMR\"},{\"Name\":\"CA\",\"DisplayName\":\"Canada\",\"TwoLetterISORegionName\":\"CA\",\"ThreeLetterISORegionName\":\"CAN\"},{\"Name\":\"029\",\"DisplayName\":\"Caribbean\",\"TwoLetterISORegionName\":\"029\",\"ThreeLetterISORegionName\":\"029\"},{\"Name\":\"CL\",\"DisplayName\":\"Chile\",\"TwoLetterISORegionName\":\"CL\",\"ThreeLetterISORegionName\":\"CHL\"},{\"Name\":\"CO\",\"DisplayName\":\"Colombia\",\"TwoLetterISORegionName\":\"CO\",\"ThreeLetterISORegionName\":\"COL\"},{\"Name\":\"CD\",\"DisplayName\":\"Congo [DRC]\",\"TwoLetterISORegionName\":\"CD\",\"ThreeLetterISORegionName\":\"COD\"},{\"Name\":\"CR\",\"DisplayName\":\"Costa Rica\",\"TwoLetterISORegionName\":\"CR\",\"ThreeLetterISORegionName\":\"CRI\"},{\"Name\":\"HR\",\"DisplayName\":\"Croatia\",\"TwoLetterISORegionName\":\"HR\",\"ThreeLetterISORegionName\":\"HRV\"},{\"Name\":\"CZ\",\"DisplayName\":\"Czech Republic\",\"TwoLetterISORegionName\":\"CZ\",\"ThreeLetterISORegionName\":\"CZE\"},{\"Name\":\"DK\",\"DisplayName\":\"Denmark\",\"TwoLetterISORegionName\":\"DK\",\"ThreeLetterISORegionName\":\"DNK\"},{\"Name\":\"DO\",\"DisplayName\":\"Dominican Republic\",\"TwoLetterISORegionName\":\"DO\",\"ThreeLetterISORegionName\":\"DOM\"},{\"Name\":\"EC\",\"DisplayName\":\"Ecuador\",\"TwoLetterISORegionName\":\"EC\",\"ThreeLetterISORegionName\":\"ECU\"},{\"Name\":\"EG\",\"DisplayName\":\"Egypt\",\"TwoLetterISORegionName\":\"EG\",\"ThreeLetterISORegionName\":\"EGY\"},{\"Name\":\"SV\",\"DisplayName\":\"El Salvador\",\"TwoLetterISORegionName\":\"SV\",\"ThreeLetterISORegionName\":\"SLV\"},{\"Name\":\"ER\",\"DisplayName\":\"Eritrea\",\"TwoLetterISORegionName\":\"ER\",\"ThreeLetterISORegionName\":\"ERI\"},{\"Name\":\"EE\",\"DisplayName\":\"Estonia\",\"TwoLetterISORegionName\":\"EE\",\"ThreeLetterISORegionName\":\"EST\"},{\"Name\":\"ET\",\"DisplayName\":\"Ethiopia\",\"TwoLetterISORegionName\":\"ET\",\"ThreeLetterISORegionName\":\"ETH\"},{\"Name\":\"FO\",\"DisplayName\":\"Faroe Islands\",\"TwoLetterISORegionName\":\"FO\",\"ThreeLetterISORegionName\":\"FRO\"},{\"Name\":\"FI\",\"DisplayName\":\"Finland\",\"TwoLetterISORegionName\":\"FI\",\"ThreeLetterISORegionName\":\"FIN\"},{\"Name\":\"FR\",\"DisplayName\":\"France\",\"TwoLetterISORegionName\":\"FR\",\"ThreeLetterISORegionName\":\"FRA\"},{\"Name\":\"GE\",\"DisplayName\":\"Georgia\",\"TwoLetterISORegionName\":\"GE\",\"ThreeLetterISORegionName\":\"GEO\"},{\"Name\":\"DE\",\"DisplayName\":\"Germany\",\"TwoLetterISORegionName\":\"DE\",\"ThreeLetterISORegionName\":\"DEU\"},{\"Name\":\"GR\",\"DisplayName\":\"Greece\",\"TwoLetterISORegionName\":\"GR\",\"ThreeLetterISORegionName\":\"GRC\"},{\"Name\":\"GL\",\"DisplayName\":\"Greenland\",\"TwoLetterISORegionName\":\"GL\",\"ThreeLetterISORegionName\":\"GRL\"},{\"Name\":\"GT\",\"DisplayName\":\"Guatemala\",\"TwoLetterISORegionName\":\"GT\",\"ThreeLetterISORegionName\":\"GTM\"},{\"Name\":\"HT\",\"DisplayName\":\"Haiti\",\"TwoLetterISORegionName\":\"HT\",\"ThreeLetterISORegionName\":\"HTI\"},{\"Name\":\"HN\",\"DisplayName\":\"Honduras\",\"TwoLetterISORegionName\":\"HN\",\"ThreeLetterISORegionName\":\"HND\"},{\"Name\":\"HK\",\"DisplayName\":\"Hong Kong S.A.R.\",\"TwoLetterISORegionName\":\"HK\",\"ThreeLetterISORegionName\":\"HKG\"},{\"Name\":\"HU\",\"DisplayName\":\"Hungary\",\"TwoLetterISORegionName\":\"HU\",\"ThreeLetterISORegionName\":\"HUN\"},{\"Name\":\"IS\",\"DisplayName\":\"Iceland\",\"TwoLetterISORegionName\":\"IS\",\"ThreeLetterISORegionName\":\"ISL\"},{\"Name\":\"IN\",\"DisplayName\":\"India\",\"TwoLetterISORegionName\":\"IN\",\"ThreeLetterISORegionName\":\"IND\"},{\"Name\":\"ID\",\"DisplayName\":\"Indonesia\",\"TwoLetterISORegionName\":\"ID\",\"ThreeLetterISORegionName\":\"IDN\"},{\"Name\":\"IR\",\"DisplayName\":\"Iran\",\"TwoLetterISORegionName\":\"IR\",\"ThreeLetterISORegionName\":\"IRN\"},{\"Name\":\"IQ\",\"DisplayName\":\"Iraq\",\"TwoLetterISORegionName\":\"IQ\",\"ThreeLetterISORegionName\":\"IRQ\"},{\"Name\":\"IE\",\"DisplayName\":\"Ireland\",\"TwoLetterISORegionName\":\"IE\",\"ThreeLetterISORegionName\":\"IRL\"},{\"Name\":\"PK\",\"DisplayName\":\"Islamic Republic of Pakistan\",\"TwoLetterISORegionName\":\"PK\",\"ThreeLetterISORegionName\":\"PAK\"},{\"Name\":\"IL\",\"DisplayName\":\"Israel\",\"TwoLetterISORegionName\":\"IL\",\"ThreeLetterISORegionName\":\"ISR\"},{\"Name\":\"IT\",\"DisplayName\":\"Italy\",\"TwoLetterISORegionName\":\"IT\",\"ThreeLetterISORegionName\":\"ITA\"},{\"Name\":\"CI\",\"DisplayName\":\"Ivory Coast\",\"TwoLetterISORegionName\":\"CI\",\"ThreeLetterISORegionName\":\"CIV\"},{\"Name\":\"JM\",\"DisplayName\":\"Jamaica\",\"TwoLetterISORegionName\":\"JM\",\"ThreeLetterISORegionName\":\"JAM\"},{\"Name\":\"JP\",\"DisplayName\":\"Japan\",\"TwoLetterISORegionName\":\"JP\",\"ThreeLetterISORegionName\":\"JPN\"},{\"Name\":\"JO\",\"DisplayName\":\"Jordan\",\"TwoLetterISORegionName\":\"JO\",\"ThreeLetterISORegionName\":\"JOR\"},{\"Name\":\"KZ\",\"DisplayName\":\"Kazakhstan\",\"TwoLetterISORegionName\":\"KZ\",\"ThreeLetterISORegionName\":\"KAZ\"},{\"Name\":\"KE\",\"DisplayName\":\"Kenya\",\"TwoLetterISORegionName\":\"KE\",\"ThreeLetterISORegionName\":\"KEN\"},{\"Name\":\"KR\",\"DisplayName\":\"Korea\",\"TwoLetterISORegionName\":\"KR\",\"ThreeLetterISORegionName\":\"KOR\"},{\"Name\":\"KW\",\"DisplayName\":\"Kuwait\",\"TwoLetterISORegionName\":\"KW\",\"ThreeLetterISORegionName\":\"KWT\"},{\"Name\":\"KG\",\"DisplayName\":\"Kyrgyzstan\",\"TwoLetterISORegionName\":\"KG\",\"ThreeLetterISORegionName\":\"KGZ\"},{\"Name\":\"LA\",\"DisplayName\":\"Lao P.D.R.\",\"TwoLetterISORegionName\":\"LA\",\"ThreeLetterISORegionName\":\"LAO\"},{\"Name\":\"419\",\"DisplayName\":\"Latin America\",\"TwoLetterISORegionName\":\"419\",\"ThreeLetterISORegionName\":\"419\"},{\"Name\":\"LV\",\"DisplayName\":\"Latvia\",\"TwoLetterISORegionName\":\"LV\",\"ThreeLetterISORegionName\":\"LVA\"},{\"Name\":\"LB\",\"DisplayName\":\"Lebanon\",\"TwoLetterISORegionName\":\"LB\",\"ThreeLetterISORegionName\":\"LBN\"},{\"Name\":\"LY\",\"DisplayName\":\"Libya\",\"TwoLetterISORegionName\":\"LY\",\"ThreeLetterISORegionName\":\"LBY\"},{\"Name\":\"LI\",\"DisplayName\":\"Liechtenstein\",\"TwoLetterISORegionName\":\"LI\",\"ThreeLetterISORegionName\":\"LIE\"},{\"Name\":\"LT\",\"DisplayName\":\"Lithuania\",\"TwoLetterISORegionName\":\"LT\",\"ThreeLetterISORegionName\":\"LTU\"},{\"Name\":\"LU\",\"DisplayName\":\"Luxembourg\",\"TwoLetterISORegionName\":\"LU\",\"ThreeLetterISORegionName\":\"LUX\"},{\"Name\":\"MO\",\"DisplayName\":\"Macao S.A.R.\",\"TwoLetterISORegionName\":\"MO\",\"ThreeLetterISORegionName\":\"MAC\"},{\"Name\":\"MK\",\"DisplayName\":\"Macedonia (FYROM)\",\"TwoLetterISORegionName\":\"MK\",\"ThreeLetterISORegionName\":\"MKD\"},{\"Name\":\"MY\",\"DisplayName\":\"Malaysia\",\"TwoLetterISORegionName\":\"MY\",\"ThreeLetterISORegionName\":\"MYS\"},{\"Name\":\"MV\",\"DisplayName\":\"Maldives\",\"TwoLetterISORegionName\":\"MV\",\"ThreeLetterISORegionName\":\"MDV\"},{\"Name\":\"ML\",\"DisplayName\":\"Mali\",\"TwoLetterISORegionName\":\"ML\",\"ThreeLetterISORegionName\":\"MLI\"},{\"Name\":\"MT\",\"DisplayName\":\"Malta\",\"TwoLetterISORegionName\":\"MT\",\"ThreeLetterISORegionName\":\"MLT\"},{\"Name\":\"MX\",\"DisplayName\":\"Mexico\",\"TwoLetterISORegionName\":\"MX\",\"ThreeLetterISORegionName\":\"MEX\"},{\"Name\":\"MN\",\"DisplayName\":\"Mongolia\",\"TwoLetterISORegionName\":\"MN\",\"ThreeLetterISORegionName\":\"MNG\"},{\"Name\":\"ME\",\"DisplayName\":\"Montenegro\",\"TwoLetterISORegionName\":\"ME\",\"ThreeLetterISORegionName\":\"MNE\"},{\"Name\":\"MA\",\"DisplayName\":\"Morocco\",\"TwoLetterISORegionName\":\"MA\",\"ThreeLetterISORegionName\":\"MAR\"},{\"Name\":\"NP\",\"DisplayName\":\"Nepal\",\"TwoLetterISORegionName\":\"NP\",\"ThreeLetterISORegionName\":\"NPL\"},{\"Name\":\"NL\",\"DisplayName\":\"Netherlands\",\"TwoLetterISORegionName\":\"NL\",\"ThreeLetterISORegionName\":\"NLD\"},{\"Name\":\"NZ\",\"DisplayName\":\"New Zealand\",\"TwoLetterISORegionName\":\"NZ\",\"ThreeLetterISORegionName\":\"NZL\"},{\"Name\":\"NI\",\"DisplayName\":\"Nicaragua\",\"TwoLetterISORegionName\":\"NI\",\"ThreeLetterISORegionName\":\"NIC\"},{\"Name\":\"NG\",\"DisplayName\":\"Nigeria\",\"TwoLetterISORegionName\":\"NG\",\"ThreeLetterISORegionName\":\"NGA\"},{\"Name\":\"NO\",\"DisplayName\":\"Norway\",\"TwoLetterISORegionName\":\"NO\",\"ThreeLetterISORegionName\":\"NOR\"},{\"Name\":\"OM\",\"DisplayName\":\"Oman\",\"TwoLetterISORegionName\":\"OM\",\"ThreeLetterISORegionName\":\"OMN\"},{\"Name\":\"PA\",\"DisplayName\":\"Panama\",\"TwoLetterISORegionName\":\"PA\",\"ThreeLetterISORegionName\":\"PAN\"},{\"Name\":\"PY\",\"DisplayName\":\"Paraguay\",\"TwoLetterISORegionName\":\"PY\",\"ThreeLetterISORegionName\":\"PRY\"},{\"Name\":\"CN\",\"DisplayName\":\"People's Republic of China\",\"TwoLetterISORegionName\":\"CN\",\"ThreeLetterISORegionName\":\"CHN\"},{\"Name\":\"PE\",\"DisplayName\":\"Peru\",\"TwoLetterISORegionName\":\"PE\",\"ThreeLetterISORegionName\":\"PER\"},{\"Name\":\"PH\",\"DisplayName\":\"Philippines\",\"TwoLetterISORegionName\":\"PH\",\"ThreeLetterISORegionName\":\"PHL\"},{\"Name\":\"PL\",\"DisplayName\":\"Poland\",\"TwoLetterISORegionName\":\"PL\",\"ThreeLetterISORegionName\":\"POL\"},{\"Name\":\"PT\",\"DisplayName\":\"Portugal\",\"TwoLetterISORegionName\":\"PT\",\"ThreeLetterISORegionName\":\"PRT\"},{\"Name\":\"MC\",\"DisplayName\":\"Principality of Monaco\",\"TwoLetterISORegionName\":\"MC\",\"ThreeLetterISORegionName\":\"MCO\"},{\"Name\":\"PR\",\"DisplayName\":\"Puerto Rico\",\"TwoLetterISORegionName\":\"PR\",\"ThreeLetterISORegionName\":\"PRI\"},{\"Name\":\"QA\",\"DisplayName\":\"Qatar\",\"TwoLetterISORegionName\":\"QA\",\"ThreeLetterISORegionName\":\"QAT\"},{\"Name\":\"MD\",\"DisplayName\":\"Republica Moldova\",\"TwoLetterISORegionName\":\"MD\",\"ThreeLetterISORegionName\":\"MDA\"},{\"Name\":\"RE\",\"DisplayName\":\"Réunion\",\"TwoLetterISORegionName\":\"RE\",\"ThreeLetterISORegionName\":\"REU\"},{\"Name\":\"RO\",\"DisplayName\":\"Romania\",\"TwoLetterISORegionName\":\"RO\",\"ThreeLetterISORegionName\":\"ROU\"},{\"Name\":\"RU\",\"DisplayName\":\"Russia\",\"TwoLetterISORegionName\":\"RU\",\"ThreeLetterISORegionName\":\"RUS\"},{\"Name\":\"RW\",\"DisplayName\":\"Rwanda\",\"TwoLetterISORegionName\":\"RW\",\"ThreeLetterISORegionName\":\"RWA\"},{\"Name\":\"SA\",\"DisplayName\":\"Saudi Arabia\",\"TwoLetterISORegionName\":\"SA\",\"ThreeLetterISORegionName\":\"SAU\"},{\"Name\":\"SN\",\"DisplayName\":\"Senegal\",\"TwoLetterISORegionName\":\"SN\",\"ThreeLetterISORegionName\":\"SEN\"},{\"Name\":\"RS\",\"DisplayName\":\"Serbia\",\"TwoLetterISORegionName\":\"RS\",\"ThreeLetterISORegionName\":\"SRB\"},{\"Name\":\"CS\",\"DisplayName\":\"Serbia and Montenegro (Former)\",\"TwoLetterISORegionName\":\"CS\",\"ThreeLetterISORegionName\":\"SCG\"},{\"Name\":\"SG\",\"DisplayName\":\"Singapore\",\"TwoLetterISORegionName\":\"SG\",\"ThreeLetterISORegionName\":\"SGP\"},{\"Name\":\"SK\",\"DisplayName\":\"Slovakia\",\"TwoLetterISORegionName\":\"SK\",\"ThreeLetterISORegionName\":\"SVK\"},{\"Name\":\"SI\",\"DisplayName\":\"Slovenia\",\"TwoLetterISORegionName\":\"SI\",\"ThreeLetterISORegionName\":\"SVN\"},{\"Name\":\"SO\",\"DisplayName\":\"Soomaaliya\",\"TwoLetterISORegionName\":\"SO\",\"ThreeLetterISORegionName\":\"SOM\"},{\"Name\":\"ZA\",\"DisplayName\":\"South Africa\",\"TwoLetterISORegionName\":\"ZA\",\"ThreeLetterISORegionName\":\"ZAF\"},{\"Name\":\"ES\",\"DisplayName\":\"Spain\",\"TwoLetterISORegionName\":\"ES\",\"ThreeLetterISORegionName\":\"ESP\"},{\"Name\":\"LK\",\"DisplayName\":\"Sri Lanka\",\"TwoLetterISORegionName\":\"LK\",\"ThreeLetterISORegionName\":\"LKA\"},{\"Name\":\"SE\",\"DisplayName\":\"Sweden\",\"TwoLetterISORegionName\":\"SE\",\"ThreeLetterISORegionName\":\"SWE\"},{\"Name\":\"CH\",\"DisplayName\":\"Switzerland\",\"TwoLetterISORegionName\":\"CH\",\"ThreeLetterISORegionName\":\"CHE\"},{\"Name\":\"SY\",\"DisplayName\":\"Syria\",\"TwoLetterISORegionName\":\"SY\",\"ThreeLetterISORegionName\":\"SYR\"},{\"Name\":\"TW\",\"DisplayName\":\"Taiwan\",\"TwoLetterISORegionName\":\"TW\",\"ThreeLetterISORegionName\":\"TWN\"},{\"Name\":\"TJ\",\"DisplayName\":\"Tajikistan\",\"TwoLetterISORegionName\":\"TJ\",\"ThreeLetterISORegionName\":\"TAJ\"},{\"Name\":\"TH\",\"DisplayName\":\"Thailand\",\"TwoLetterISORegionName\":\"TH\",\"ThreeLetterISORegionName\":\"THA\"},{\"Name\":\"TT\",\"DisplayName\":\"Trinidad and Tobago\",\"TwoLetterISORegionName\":\"TT\",\"ThreeLetterISORegionName\":\"TTO\"},{\"Name\":\"TN\",\"DisplayName\":\"Tunisia\",\"TwoLetterISORegionName\":\"TN\",\"ThreeLetterISORegionName\":\"TUN\"},{\"Name\":\"TR\",\"DisplayName\":\"Turkey\",\"TwoLetterISORegionName\":\"TR\",\"ThreeLetterISORegionName\":\"TUR\"},{\"Name\":\"TM\",\"DisplayName\":\"Turkmenistan\",\"TwoLetterISORegionName\":\"TM\",\"ThreeLetterISORegionName\":\"TKM\"},{\"Name\":\"AE\",\"DisplayName\":\"U.A.E.\",\"TwoLetterISORegionName\":\"AE\",\"ThreeLetterISORegionName\":\"ARE\"},{\"Name\":\"UA\",\"DisplayName\":\"Ukraine\",\"TwoLetterISORegionName\":\"UA\",\"ThreeLetterISORegionName\":\"UKR\"},{\"Name\":\"GB\",\"DisplayName\":\"United Kingdom\",\"TwoLetterISORegionName\":\"GB\",\"ThreeLetterISORegionName\":\"GBR\"},{\"Name\":\"US\",\"DisplayName\":\"United States\",\"TwoLetterISORegionName\":\"US\",\"ThreeLetterISORegionName\":\"USA\"},{\"Name\":\"UY\",\"DisplayName\":\"Uruguay\",\"TwoLetterISORegionName\":\"UY\",\"ThreeLetterISORegionName\":\"URY\"},{\"Name\":\"UZ\",\"DisplayName\":\"Uzbekistan\",\"TwoLetterISORegionName\":\"UZ\",\"ThreeLetterISORegionName\":\"UZB\"},{\"Name\":\"VN\",\"DisplayName\":\"Vietnam\",\"TwoLetterISORegionName\":\"VN\",\"ThreeLetterISORegionName\":\"VNM\"},{\"Name\":\"YE\",\"DisplayName\":\"Yemen\",\"TwoLetterISORegionName\":\"YE\",\"ThreeLetterISORegionName\":\"YEM\"},{\"Name\":\"ZW\",\"DisplayName\":\"Zimbabwe\",\"TwoLetterISORegionName\":\"ZW\",\"ThreeLetterISORegionName\":\"ZWE\"}]";
-
- return _jsonSerializer.DeserializeFromString<CountryInfo[]>(jsonCountries);
- }
+ public Task<CountryInfo[]> GetCountries()
+ => _jsonSerializer.DeserializeFromStreamAsync<CountryInfo[]>(
+ _assembly.GetManifestResourceStream("Emby.Server.Implementations.Localization.countries.json"));
/// <summary>
/// Gets the parental ratings.
/// </summary>
/// <returns>IEnumerable{ParentalRating}.</returns>
- public ParentalRating[] GetParentalRatings()
- {
- return GetParentalRatingsDictionary().Values.ToArray();
- }
+ public IEnumerable<ParentalRating> GetParentalRatings()
+ => GetParentalRatingsDictionary().Values;
/// <summary>
/// Gets the parental ratings dictionary.
@@ -288,14 +268,7 @@ namespace Emby.Server.Implementations.Localization
countryCode = "us";
}
- var ratings = GetRatings(countryCode);
-
- if (ratings == null)
- {
- ratings = GetRatings("us");
- }
-
- return ratings;
+ return GetRatings(countryCode) ?? GetRatings("us");
}
/// <summary>
@@ -314,37 +287,39 @@ namespace Emby.Server.Implementations.Localization
/// </summary>
/// <param name="file">The file.</param>
/// <returns>Dictionary{System.StringParentalRating}.</returns>
- private void LoadRatings(string file)
+ private async Task LoadRatings(string file)
{
- var dict = File.ReadAllLines(file).Select(i =>
+ Dictionary<string, ParentalRating> dict = new Dictionary<string, ParentalRating>(StringComparer.OrdinalIgnoreCase);
+
+ using (var str = File.OpenRead(file))
+ using (var reader = new StreamReader(str))
{
- if (!string.IsNullOrWhiteSpace(i))
+ string line;
+ while ((line = await reader.ReadLineAsync()) != null)
{
- var parts = i.Split(',');
+ if (string.IsNullOrWhiteSpace(line))
+ {
+ continue;
+ }
- if (parts.Length == 2)
+ string[] parts = line.Split(',');
+ if (parts.Length == 2
+ && int.TryParse(parts[1], NumberStyles.Integer, UsCulture, out var value))
{
- if (int.TryParse(parts[1], NumberStyles.Integer, UsCulture, out var value))
- {
- return new ParentalRating { Name = parts[0], Value = value };
- }
+ dict.Add(parts[0], (new ParentalRating { Name = parts[0], Value = value }));
}
+#if DEBUG
+ _logger.LogWarning("Misformed line in {Path}", file);
+#endif
}
+ }
- return null;
-
- })
- .Where(i => i != null)
- .ToDictionary(i => i.Name, StringComparer.OrdinalIgnoreCase);
-
- var countryCode = Path.GetFileNameWithoutExtension(file)
- .Split('-')
- .Last();
+ var countryCode = Path.GetFileNameWithoutExtension(file).Split('-')[1];
_allParentalRatings[countryCode] = dict;
}
- private readonly string[] _unratedValues = { "n/a", "unrated", "not rated" };
+ private static readonly string[] _unratedValues = { "n/a", "unrated", "not rated" };
/// <summary>
/// Gets the rating level.
@@ -435,7 +410,7 @@ namespace Emby.Server.Implementations.Localization
return phrase;
}
- const string DefaultCulture = "en-US";
+ private const string DefaultCulture = "en-US";
private readonly ConcurrentDictionary<string, Dictionary<string, string>> _dictionaries =
new ConcurrentDictionary<string, Dictionary<string, string>>(StringComparer.OrdinalIgnoreCase);
@@ -450,10 +425,11 @@ namespace Emby.Server.Implementations.Localization
const string prefix = "Core";
var key = prefix + culture;
- return _dictionaries.GetOrAdd(key, k => GetDictionary(prefix, culture, DefaultCulture + ".json"));
+ return _dictionaries.GetOrAdd(key,
+ f => GetDictionary(prefix, culture, DefaultCulture + ".json").GetAwaiter().GetResult());
}
- private Dictionary<string, string> GetDictionary(string prefix, string culture, string baseFilename)
+ private async Task<Dictionary<string, string>> GetDictionary(string prefix, string culture, string baseFilename)
{
if (string.IsNullOrEmpty(culture))
{
@@ -464,24 +440,21 @@ namespace Emby.Server.Implementations.Localization
var namespaceName = GetType().Namespace + "." + prefix;
- CopyInto(dictionary, namespaceName + "." + baseFilename);
- CopyInto(dictionary, namespaceName + "." + GetResourceFilename(culture));
+ await CopyInto(dictionary, namespaceName + "." + baseFilename);
+ await CopyInto(dictionary, namespaceName + "." + GetResourceFilename(culture));
return dictionary;
}
- private void CopyInto(IDictionary<string, string> dictionary, string resourcePath)
+ private async Task CopyInto(IDictionary<string, string> dictionary, string resourcePath)
{
- using (var stream = _assemblyInfo.GetManifestResourceStream(GetType(), resourcePath))
+ using (var stream = _assembly.GetManifestResourceStream(resourcePath))
{
- if (stream != null)
- {
- var dict = _jsonSerializer.DeserializeFromStream<Dictionary<string, string>>(stream);
+ var dict = await _jsonSerializer.DeserializeFromStreamAsync<Dictionary<string, string>>(stream);
- foreach (var key in dict.Keys)
- {
- dictionary[key] = dict[key];
- }
+ foreach (var key in dict.Keys)
+ {
+ dictionary[key] = dict[key];
}
}
}
@@ -552,11 +525,4 @@ namespace Emby.Server.Implementations.Localization
new LocalizationOption("Vietnamese", "vi")
};
}
-
- public interface ITextLocalizer
- {
- string RemoveDiacritics(string text);
-
- string NormalizeFormKD(string text);
- }
}
diff --git a/Emby.Server.Implementations/Localization/Ratings/br.txt b/Emby.Server.Implementations/Localization/Ratings/br.csv
index e5edaf62c..e5edaf62c 100644
--- a/Emby.Server.Implementations/Localization/Ratings/br.txt
+++ b/Emby.Server.Implementations/Localization/Ratings/br.csv
diff --git a/Emby.Server.Implementations/Localization/Ratings/ca.txt b/Emby.Server.Implementations/Localization/Ratings/ca.csv
index 5aef0580f..5aef0580f 100644
--- a/Emby.Server.Implementations/Localization/Ratings/ca.txt
+++ b/Emby.Server.Implementations/Localization/Ratings/ca.csv
diff --git a/Emby.Server.Implementations/Localization/Ratings/co.txt b/Emby.Server.Implementations/Localization/Ratings/co.csv
index 9684fa052..9684fa052 100644
--- a/Emby.Server.Implementations/Localization/Ratings/co.txt
+++ b/Emby.Server.Implementations/Localization/Ratings/co.csv
diff --git a/Emby.Server.Implementations/Localization/Ratings/dk.txt b/Emby.Server.Implementations/Localization/Ratings/dk.csv
index 5364ae1f2..5364ae1f2 100644
--- a/Emby.Server.Implementations/Localization/Ratings/dk.txt
+++ b/Emby.Server.Implementations/Localization/Ratings/dk.csv
diff --git a/Emby.Server.Implementations/Localization/Ratings/es.txt b/Emby.Server.Implementations/Localization/Ratings/es.csv
index 887d91ba6..887d91ba6 100644
--- a/Emby.Server.Implementations/Localization/Ratings/es.txt
+++ b/Emby.Server.Implementations/Localization/Ratings/es.csv
diff --git a/Emby.Server.Implementations/Localization/Ratings/fr.txt b/Emby.Server.Implementations/Localization/Ratings/fr.csv
index f586a3fa9..f586a3fa9 100644
--- a/Emby.Server.Implementations/Localization/Ratings/fr.txt
+++ b/Emby.Server.Implementations/Localization/Ratings/fr.csv
diff --git a/Emby.Server.Implementations/Localization/Ratings/gb.txt b/Emby.Server.Implementations/Localization/Ratings/gb.csv
index c1f7d0452..c1f7d0452 100644
--- a/Emby.Server.Implementations/Localization/Ratings/gb.txt
+++ b/Emby.Server.Implementations/Localization/Ratings/gb.csv
diff --git a/Emby.Server.Implementations/Localization/Ratings/ie.txt b/Emby.Server.Implementations/Localization/Ratings/ie.csv
index e42be5cd4..e42be5cd4 100644
--- a/Emby.Server.Implementations/Localization/Ratings/ie.txt
+++ b/Emby.Server.Implementations/Localization/Ratings/ie.csv
diff --git a/Emby.Server.Implementations/Localization/Ratings/jp.txt b/Emby.Server.Implementations/Localization/Ratings/jp.csv
index a8fc2d143..a8fc2d143 100644
--- a/Emby.Server.Implementations/Localization/Ratings/jp.txt
+++ b/Emby.Server.Implementations/Localization/Ratings/jp.csv
diff --git a/Emby.Server.Implementations/Localization/Ratings/kz.txt b/Emby.Server.Implementations/Localization/Ratings/kz.csv
index 4441c5650..4441c5650 100644
--- a/Emby.Server.Implementations/Localization/Ratings/kz.txt
+++ b/Emby.Server.Implementations/Localization/Ratings/kz.csv
diff --git a/Emby.Server.Implementations/Localization/Ratings/mx.txt b/Emby.Server.Implementations/Localization/Ratings/mx.csv
index 785a8ba22..785a8ba22 100644
--- a/Emby.Server.Implementations/Localization/Ratings/mx.txt
+++ b/Emby.Server.Implementations/Localization/Ratings/mx.csv
diff --git a/Emby.Server.Implementations/Localization/Ratings/nl.txt b/Emby.Server.Implementations/Localization/Ratings/nl.csv
index 8c005092e..8c005092e 100644
--- a/Emby.Server.Implementations/Localization/Ratings/nl.txt
+++ b/Emby.Server.Implementations/Localization/Ratings/nl.csv
diff --git a/Emby.Server.Implementations/Localization/Ratings/nz.txt b/Emby.Server.Implementations/Localization/Ratings/nz.csv
index bba99b764..bba99b764 100644
--- a/Emby.Server.Implementations/Localization/Ratings/nz.txt
+++ b/Emby.Server.Implementations/Localization/Ratings/nz.csv
diff --git a/Emby.Server.Implementations/Localization/Ratings/ro.txt b/Emby.Server.Implementations/Localization/Ratings/ro.csv
index 4089b282f..4089b282f 100644
--- a/Emby.Server.Implementations/Localization/Ratings/ro.txt
+++ b/Emby.Server.Implementations/Localization/Ratings/ro.csv
diff --git a/Emby.Server.Implementations/Localization/Ratings/uk.txt b/Emby.Server.Implementations/Localization/Ratings/uk.csv
index 6c8005b3f..6c8005b3f 100644
--- a/Emby.Server.Implementations/Localization/Ratings/uk.txt
+++ b/Emby.Server.Implementations/Localization/Ratings/uk.csv
diff --git a/Emby.Server.Implementations/Localization/Ratings/us.txt b/Emby.Server.Implementations/Localization/Ratings/us.csv
index 34c897fe3..34c897fe3 100644
--- a/Emby.Server.Implementations/Localization/Ratings/us.txt
+++ b/Emby.Server.Implementations/Localization/Ratings/us.csv
diff --git a/Emby.Server.Implementations/Localization/TextLocalizer.cs b/Emby.Server.Implementations/Localization/TextLocalizer.cs
deleted file mode 100644
index 96591e5e6..000000000
--- a/Emby.Server.Implementations/Localization/TextLocalizer.cs
+++ /dev/null
@@ -1,63 +0,0 @@
-using System;
-using System.Globalization;
-using System.Linq;
-using System.Text;
-using System.Text.RegularExpressions;
-
-namespace Emby.Server.Implementations.Localization
-{
- public class TextLocalizer : ITextLocalizer
- {
- public string RemoveDiacritics(string text)
- {
- if (text == null)
- {
- throw new ArgumentNullException(nameof(text));
- }
-
- var chars = Normalize(text, NormalizationForm.FormD)
- .Where(ch => CharUnicodeInfo.GetUnicodeCategory(ch) != UnicodeCategory.NonSpacingMark);
-
- return Normalize(string.Concat(chars), NormalizationForm.FormC);
- }
-
- private static string Normalize(string text, NormalizationForm form, bool stripStringOnFailure = true)
- {
- if (stripStringOnFailure)
- {
- try
- {
- return text.Normalize(form);
- }
- catch (ArgumentException)
- {
- // will throw if input contains invalid unicode chars
- // https://mnaoumov.wordpress.com/2014/06/14/stripping-invalid-characters-from-utf-16-strings/
- text = StripInvalidUnicodeCharacters(text);
- return Normalize(text, form, false);
- }
- }
-
- try
- {
- return text.Normalize(form);
- }
- catch (ArgumentException)
- {
- // if it still fails, return the original text
- return text;
- }
- }
-
- private static string StripInvalidUnicodeCharacters(string str)
- {
- var invalidCharactersRegex = new Regex("([\ud800-\udbff](?![\udc00-\udfff]))|((?<![\ud800-\udbff])[\udc00-\udfff])");
- return invalidCharactersRegex.Replace(str, "");
- }
-
- public string NormalizeFormKD(string text)
- {
- return text.Normalize(NormalizationForm.FormKD);
- }
- }
-}
diff --git a/Emby.Server.Implementations/ScheduledTasks/ChapterImagesTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs
index 5373b4392..81fdb96d2 100644
--- a/Emby.Server.Implementations/ScheduledTasks/ChapterImagesTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs
@@ -68,8 +68,6 @@ namespace Emby.Server.Implementations.ScheduledTasks
};
}
- public string Key => "RefreshChapterImages";
-
/// <summary>
/// Returns the task to be executed
/// </summary>
@@ -161,22 +159,18 @@ namespace Emby.Server.Implementations.ScheduledTasks
}
}
- /// <summary>
- /// Gets the name of the task
- /// </summary>
- /// <value>The name.</value>
public string Name => "Chapter image extraction";
- /// <summary>
- /// Gets the description.
- /// </summary>
- /// <value>The description.</value>
public string Description => "Creates thumbnails for videos that have chapters.";
- /// <summary>
- /// Gets the category.
- /// </summary>
- /// <value>The category.</value>
public string Category => "Library";
+
+ public string Key => "RefreshChapterImages";
+
+ public bool IsHidden => false;
+
+ public bool IsEnabled => true;
+
+ public bool IsLogged => true;
}
}
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs
index 52077b242..6ec83b5c0 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs
@@ -158,31 +158,15 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
}
}
- /// <summary>
- /// Gets the name of the task
- /// </summary>
- /// <value>The name.</value>
public string Name => "Cache file cleanup";
- public string Key => "DeleteCacheFiles";
-
- /// <summary>
- /// Gets the description.
- /// </summary>
- /// <value>The description.</value>
public string Description => "Deletes cache files no longer needed by the system";
- /// <summary>
- /// Gets the category.
- /// </summary>
- /// <value>The category.</value>
public string Category => "Maintenance";
- /// <summary>
- /// Gets a value indicating whether this instance is hidden.
- /// </summary>
- /// <value><c>true</c> if this instance is hidden; otherwise, <c>false</c>.</value>
- public bool IsHidden => true;
+ public string Key => "DeleteCacheFiles";
+
+ public bool IsHidden => false;
public bool IsEnabled => true;
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs
index a57fe4945..e468c301a 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs
@@ -81,31 +81,15 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
return Task.CompletedTask;
}
- public string Key => "CleanLogFiles";
-
- /// <summary>
- /// Gets the name of the task
- /// </summary>
- /// <value>The name.</value>
public string Name => "Log file cleanup";
- /// <summary>
- /// Gets the description.
- /// </summary>
- /// <value>The description.</value>
public string Description => string.Format("Deletes log files that are more than {0} days old.", ConfigurationManager.CommonConfiguration.LogFileRetentionDays);
- /// <summary>
- /// Gets the category.
- /// </summary>
- /// <value>The category.</value>
public string Category => "Maintenance";
- /// <summary>
- /// Gets a value indicating whether this instance is hidden.
- /// </summary>
- /// <value><c>true</c> if this instance is hidden; otherwise, <c>false</c>.</value>
- public bool IsHidden => true;
+ public string Key => "CleanLogFiles";
+
+ public bool IsHidden => false;
public bool IsEnabled => true;
diff --git a/Emby.Server.Implementations/ScheduledTasks/PeopleValidationTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/PeopleValidationTask.cs
index 68031170f..d70799c47 100644
--- a/Emby.Server.Implementations/ScheduledTasks/PeopleValidationTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/PeopleValidationTask.cs
@@ -47,8 +47,6 @@ namespace Emby.Server.Implementations.ScheduledTasks
};
}
- public string Key => "RefreshPeople";
-
/// <summary>
/// Returns the task to be executed
/// </summary>
@@ -60,22 +58,18 @@ namespace Emby.Server.Implementations.ScheduledTasks
return _libraryManager.ValidatePeople(cancellationToken, progress);
}
- /// <summary>
- /// Gets the name of the task
- /// </summary>
- /// <value>The name.</value>
public string Name => "Refresh people";
- /// <summary>
- /// Gets the description.
- /// </summary>
- /// <value>The description.</value>
public string Description => "Updates metadata for actors and directors in your media library.";
- /// <summary>
- /// Gets the category.
- /// </summary>
- /// <value>The category.</value>
public string Category => "Library";
+
+ public string Key => "RefreshPeople";
+
+ public bool IsHidden => false;
+
+ public bool IsEnabled => true;
+
+ public bool IsLogged => true;
}
}
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs
new file mode 100644
index 000000000..c6431c311
--- /dev/null
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs
@@ -0,0 +1,119 @@
+using MediaBrowser.Common;
+using MediaBrowser.Common.Updates;
+using MediaBrowser.Model.Net;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using MediaBrowser.Common.Progress;
+using MediaBrowser.Model.Tasks;
+using Microsoft.Extensions.Logging;
+
+namespace Emby.Server.Implementations.ScheduledTasks
+{
+ /// <summary>
+ /// Plugin Update Task
+ /// </summary>
+ public class PluginUpdateTask : IScheduledTask, IConfigurableScheduledTask
+ {
+ /// <summary>
+ /// The _logger
+ /// </summary>
+ private readonly ILogger _logger;
+
+ private readonly IInstallationManager _installationManager;
+
+ private readonly IApplicationHost _appHost;
+
+ public PluginUpdateTask(ILogger logger, IInstallationManager installationManager, IApplicationHost appHost)
+ {
+ _logger = logger;
+ _installationManager = installationManager;
+ _appHost = appHost;
+ }
+
+ /// <summary>
+ /// Creates the triggers that define when the task will run
+ /// </summary>
+ /// <returns>IEnumerable{BaseTaskTrigger}.</returns>
+ public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
+ {
+ return new[] {
+
+ // At startup
+ new TaskTriggerInfo {Type = TaskTriggerInfo.TriggerStartup},
+
+ // Every so often
+ new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(24).Ticks}
+ };
+ }
+
+ /// <summary>
+ /// Update installed plugins
+ /// </summary>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <param name="progress">The progress.</param>
+ /// <returns>Task.</returns>
+ public async Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
+ {
+ progress.Report(0);
+
+ var packagesToInstall = (await _installationManager.GetAvailablePluginUpdates(typeof(PluginUpdateTask).Assembly.GetName().Version, true, cancellationToken).ConfigureAwait(false)).ToList();
+
+ progress.Report(10);
+
+ var numComplete = 0;
+
+ foreach (var package in packagesToInstall)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ try
+ {
+ await _installationManager.InstallPackage(package, true, new SimpleProgress<double>(), cancellationToken).ConfigureAwait(false);
+ }
+ catch (OperationCanceledException)
+ {
+ // InstallPackage has it's own inner cancellation token, so only throw this if it's ours
+ if (cancellationToken.IsCancellationRequested)
+ {
+ throw;
+ }
+ }
+ catch (HttpException ex)
+ {
+ _logger.LogError(ex, "Error downloading {0}", package.name);
+ }
+ catch (IOException ex)
+ {
+ _logger.LogError(ex, "Error updating {0}", package.name);
+ }
+
+ // Update progress
+ lock (progress)
+ {
+ numComplete++;
+ progress.Report(90.0 * numComplete / packagesToInstall.Count + 10);
+ }
+ }
+
+ progress.Report(100);
+ }
+
+ public string Name => "Check for plugin updates";
+
+ public string Description => "Downloads and installs updates for plugins that are configured to update automatically.";
+
+ public string Category => "Application";
+
+ public string Key => "PluginUpdates";
+
+ public bool IsHidden => false;
+
+ public bool IsEnabled => true;
+
+ public bool IsLogged => true;
+ }
+}
diff --git a/Emby.Server.Implementations/ScheduledTasks/RefreshMediaLibraryTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/RefreshMediaLibraryTask.cs
index f6fa64d13..1a3d85ad7 100644
--- a/Emby.Server.Implementations/ScheduledTasks/RefreshMediaLibraryTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/RefreshMediaLibraryTask.cs
@@ -58,24 +58,18 @@ namespace Emby.Server.Implementations.ScheduledTasks
return ((LibraryManager)_libraryManager).ValidateMediaLibraryInternal(progress, cancellationToken);
}
- /// <summary>
- /// Gets the name.
- /// </summary>
- /// <value>The name.</value>
public string Name => "Scan media library";
- /// <summary>
- /// Gets the description.
- /// </summary>
- /// <value>The description.</value>
public string Description => "Scans your media library and refreshes metatata based on configuration.";
- /// <summary>
- /// Gets the category.
- /// </summary>
- /// <value>The category.</value>
public string Category => "Library";
public string Key => "RefreshLibrary";
+
+ public bool IsHidden => false;
+
+ public bool IsEnabled => true;
+
+ public bool IsLogged => true;
}
}
diff --git a/Emby.Server.Implementations/ScheduledTasks/DailyTrigger.cs b/Emby.Server.Implementations/ScheduledTasks/Triggers/DailyTrigger.cs
index 98685cebe..98685cebe 100644
--- a/Emby.Server.Implementations/ScheduledTasks/DailyTrigger.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Triggers/DailyTrigger.cs
diff --git a/Emby.Server.Implementations/ScheduledTasks/IntervalTrigger.cs b/Emby.Server.Implementations/ScheduledTasks/Triggers/IntervalTrigger.cs
index 3a34da3af..3a34da3af 100644
--- a/Emby.Server.Implementations/ScheduledTasks/IntervalTrigger.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Triggers/IntervalTrigger.cs
diff --git a/Emby.Server.Implementations/ScheduledTasks/StartupTrigger.cs b/Emby.Server.Implementations/ScheduledTasks/Triggers/StartupTrigger.cs
index 08ff4f55f..08ff4f55f 100644
--- a/Emby.Server.Implementations/ScheduledTasks/StartupTrigger.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Triggers/StartupTrigger.cs
diff --git a/Emby.Server.Implementations/ScheduledTasks/WeeklyTrigger.cs b/Emby.Server.Implementations/ScheduledTasks/Triggers/WeeklyTrigger.cs
index 2a6a7b13c..2a6a7b13c 100644
--- a/Emby.Server.Implementations/ScheduledTasks/WeeklyTrigger.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Triggers/WeeklyTrigger.cs
diff --git a/Emby.Server.Implementations/Updates/InstallationManager.cs b/Emby.Server.Implementations/Updates/InstallationManager.cs
index 9a49b97c7..dc7f57f27 100644
--- a/Emby.Server.Implementations/Updates/InstallationManager.cs
+++ b/Emby.Server.Implementations/Updates/InstallationManager.cs
@@ -164,15 +164,13 @@ namespace Emby.Server.Implementations.Updates
/// Gets all available packages.
/// </summary>
/// <returns>Task{List{PackageInfo}}.</returns>
- public Task<List<PackageInfo>> GetAvailablePackages(CancellationToken cancellationToken,
+ public async Task<List<PackageInfo>> GetAvailablePackages(CancellationToken cancellationToken,
bool withRegistration = true,
string packageType = null,
Version applicationVersion = null)
{
- // TODO cvium: when plugins get back this would need to be fixed
- // var packages = await GetAvailablePackagesWithoutRegistrationInfo(cancellationToken).ConfigureAwait(false);
-
- return Task.FromResult(new List<PackageInfo>()); //FilterPackages(packages, packageType, applicationVersion);
+ var packages = await GetAvailablePackagesWithoutRegistrationInfo(cancellationToken).ConfigureAwait(false);
+ return FilterPackages(packages, packageType, applicationVersion);
}
/// <summary>
@@ -184,12 +182,10 @@ namespace Emby.Server.Implementations.Updates
{
using (var response = await _httpClient.SendAsync(new HttpRequestOptions
{
- Url = "https://www.mb3admin.local/admin/service/EmbyPackages.json",
+ Url = "https://repo.jellyfin.org/releases/plugin/manifest.json",
CancellationToken = cancellationToken,
Progress = new SimpleProgress<double>(),
- CacheLength = GetCacheLength(),
- CacheMode = CacheMode.Unconditional
-
+ CacheLength = GetCacheLength()
}, "GET").ConfigureAwait(false))
{
using (var stream = response.Content)
diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs
index bb0eff291..7826fde35 100644
--- a/Jellyfin.Server/Program.cs
+++ b/Jellyfin.Server/Program.cs
@@ -114,12 +114,10 @@ namespace Jellyfin.Server
new NullImageEncoder(),
new NetworkManager(_loggerFactory, environmentInfo)))
{
- appHost.Init();
+ await appHost.Init();
appHost.ImageProcessor.ImageEncoder = GetImageEncoder(fileSystem, appPaths, appHost.LocalizationManager);
- _logger.LogInformation("Running startup tasks");
-
await appHost.RunStartupTasks();
// TODO: read input for a stop command
@@ -133,8 +131,6 @@ namespace Jellyfin.Server
{
// Don't throw on cancellation
}
-
- _logger.LogInformation("Disposing app host");
}
if (_restartOnShutdown)
diff --git a/MediaBrowser.Api/ApiEntryPoint.cs b/MediaBrowser.Api/ApiEntryPoint.cs
index 763535129..88654a42c 100644
--- a/MediaBrowser.Api/ApiEntryPoint.cs
+++ b/MediaBrowser.Api/ApiEntryPoint.cs
@@ -130,7 +130,7 @@ namespace MediaBrowser.Api
/// <summary>
/// Runs this instance.
/// </summary>
- public void Run()
+ public Task RunAsync()
{
try
{
@@ -148,6 +148,8 @@ namespace MediaBrowser.Api
{
Logger.LogError(ex, "Error deleting encoded media cache");
}
+
+ return Task.CompletedTask;
}
public EncodingOptions GetEncodingOptions()
@@ -162,8 +164,7 @@ namespace MediaBrowser.Api
{
var path = _config.ApplicationPaths.TranscodingTempPath;
- foreach (var file in _fileSystem.GetFilePaths(path, true)
- .ToList())
+ foreach (var file in _fileSystem.GetFilePaths(path, true))
{
_fileSystem.DeleteFile(file);
}
@@ -462,7 +463,7 @@ namespace MediaBrowser.Api
Logger.LogInformation("Transcoding kill timer stopped for JobId {0} PlaySessionId {1}. Killing transcoding", job.Id, job.PlaySessionId);
- KillTranscodingJob(job, true, path => true);
+ KillTranscodingJob(job, true, path => true).GetAwaiter().GetResult();
}
/// <summary>
@@ -472,9 +473,9 @@ namespace MediaBrowser.Api
/// <param name="playSessionId">The play session identifier.</param>
/// <param name="deleteFiles">The delete files.</param>
/// <returns>Task.</returns>
- internal void KillTranscodingJobs(string deviceId, string playSessionId, Func<string, bool> deleteFiles)
+ internal Task KillTranscodingJobs(string deviceId, string playSessionId, Func<string, bool> deleteFiles)
{
- KillTranscodingJobs(j =>
+ return KillTranscodingJobs(j =>
{
if (!string.IsNullOrWhiteSpace(playSessionId))
{
@@ -492,7 +493,7 @@ namespace MediaBrowser.Api
/// <param name="killJob">The kill job.</param>
/// <param name="deleteFiles">The delete files.</param>
/// <returns>Task.</returns>
- private void KillTranscodingJobs(Func<TranscodingJob, bool> killJob, Func<string, bool> deleteFiles)
+ private Task KillTranscodingJobs(Func<TranscodingJob, bool> killJob, Func<string, bool> deleteFiles)
{
var jobs = new List<TranscodingJob>();
@@ -505,13 +506,18 @@ namespace MediaBrowser.Api
if (jobs.Count == 0)
{
- return;
+ return Task.CompletedTask;
}
- foreach (var job in jobs)
+ IEnumerable<Task> GetKillJobs()
{
- KillTranscodingJob(job, false, deleteFiles);
+ foreach (var job in jobs)
+ {
+ yield return KillTranscodingJob(job, false, deleteFiles);
+ }
}
+
+ return Task.WhenAll(GetKillJobs());
}
/// <summary>
@@ -520,7 +526,7 @@ namespace MediaBrowser.Api
/// <param name="job">The job.</param>
/// <param name="closeLiveStream">if set to <c>true</c> [close live stream].</param>
/// <param name="delete">The delete.</param>
- private async void KillTranscodingJob(TranscodingJob job, bool closeLiveStream, Func<string, bool> delete)
+ private async Task KillTranscodingJob(TranscodingJob job, bool closeLiveStream, Func<string, bool> delete)
{
job.DisposeKillTimer();
@@ -577,7 +583,7 @@ namespace MediaBrowser.Api
if (delete(job.Path))
{
- DeletePartialStreamFiles(job.Path, job.Type, 0, 1500);
+ await DeletePartialStreamFiles(job.Path, job.Type, 0, 1500).ConfigureAwait(false);
}
if (closeLiveStream && !string.IsNullOrWhiteSpace(job.LiveStreamId))
@@ -588,12 +594,12 @@ namespace MediaBrowser.Api
}
catch (Exception ex)
{
- Logger.LogError(ex, "Error closing live stream for {path}", job.Path);
+ Logger.LogError(ex, "Error closing live stream for {Path}", job.Path);
}
}
}
- private async void DeletePartialStreamFiles(string path, TranscodingJobType jobType, int retryCount, int delayMs)
+ private async Task DeletePartialStreamFiles(string path, TranscodingJobType jobType, int retryCount, int delayMs)
{
if (retryCount >= 10)
{
@@ -623,7 +629,7 @@ namespace MediaBrowser.Api
{
Logger.LogError(ex, "Error deleting partial stream file(s) {path}", path);
- DeletePartialStreamFiles(path, jobType, retryCount + 1, 500);
+ await DeletePartialStreamFiles(path, jobType, retryCount + 1, 500).ConfigureAwait(false);
}
catch (Exception ex)
{
@@ -650,8 +656,7 @@ namespace MediaBrowser.Api
var name = Path.GetFileNameWithoutExtension(outputFilePath);
var filesToDelete = _fileSystem.GetFilePaths(directory)
- .Where(f => f.IndexOf(name, StringComparison.OrdinalIgnoreCase) != -1)
- .ToList();
+ .Where(f => f.IndexOf(name, StringComparison.OrdinalIgnoreCase) != -1);
Exception e = null;
diff --git a/MediaBrowser.Api/ItemUpdateService.cs b/MediaBrowser.Api/ItemUpdateService.cs
index 3c8d4b4ff..825732888 100644
--- a/MediaBrowser.Api/ItemUpdateService.cs
+++ b/MediaBrowser.Api/ItemUpdateService.cs
@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
+using System.Threading.Tasks;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
@@ -60,15 +61,15 @@ namespace MediaBrowser.Api
_fileSystem = fileSystem;
}
- public object Get(GetMetadataEditorInfo request)
+ public async Task<object> Get(GetMetadataEditorInfo request)
{
var item = _libraryManager.GetItemById(request.ItemId);
var info = new MetadataEditorInfo
{
- ParentalRatingOptions = _localizationManager.GetParentalRatings(),
+ ParentalRatingOptions = _localizationManager.GetParentalRatings().ToArray(),
ExternalIdInfos = _providerManager.GetExternalIdInfos(item).ToArray(),
- Countries = _localizationManager.GetCountries(),
+ Countries = await _localizationManager.GetCountries(),
Cultures = _localizationManager.GetCultures()
};
diff --git a/MediaBrowser.Api/UserLibrary/PlaystateService.cs b/MediaBrowser.Api/UserLibrary/PlaystateService.cs
index a133bc8e9..b40a92a7c 100644
--- a/MediaBrowser.Api/UserLibrary/PlaystateService.cs
+++ b/MediaBrowser.Api/UserLibrary/PlaystateService.cs
@@ -366,9 +366,9 @@ namespace MediaBrowser.Api.UserLibrary
/// Posts the specified request.
/// </summary>
/// <param name="request">The request.</param>
- public void Delete(OnPlaybackStopped request)
+ public Task Delete(OnPlaybackStopped request)
{
- Post(new ReportPlaybackStopped
+ return Post(new ReportPlaybackStopped
{
ItemId = new Guid(request.Id),
PositionTicks = request.PositionTicks,
@@ -379,20 +379,18 @@ namespace MediaBrowser.Api.UserLibrary
});
}
- public void Post(ReportPlaybackStopped request)
+ public async Task Post(ReportPlaybackStopped request)
{
Logger.LogDebug("ReportPlaybackStopped PlaySessionId: {0}", request.PlaySessionId ?? string.Empty);
if (!string.IsNullOrWhiteSpace(request.PlaySessionId))
{
- ApiEntryPoint.Instance.KillTranscodingJobs(_authContext.GetAuthorizationInfo(Request).DeviceId, request.PlaySessionId, s => true);
+ await ApiEntryPoint.Instance.KillTranscodingJobs(_authContext.GetAuthorizationInfo(Request).DeviceId, request.PlaySessionId, s => true);
}
request.SessionId = GetSession(_sessionContext).Id;
- var task = _sessionManager.OnPlaybackStopped(request);
-
- Task.WaitAll(task);
+ await _sessionManager.OnPlaybackStopped(request);
}
/// <summary>
diff --git a/MediaBrowser.Common/IApplicationHost.cs b/MediaBrowser.Common/IApplicationHost.cs
index 385127c54..59e3c1767 100644
--- a/MediaBrowser.Common/IApplicationHost.cs
+++ b/MediaBrowser.Common/IApplicationHost.cs
@@ -89,7 +89,7 @@ namespace MediaBrowser.Common
/// <typeparam name="T"></typeparam>
/// <param name="manageLiftime">if set to <c>true</c> [manage liftime].</param>
/// <returns>IEnumerable{``0}.</returns>
- IEnumerable<T> GetExports<T>(bool manageLiftime = true);
+ IEnumerable<T> GetExports<T>(bool manageLifetime = true);
/// <summary>
/// Updates the application.
@@ -131,7 +131,7 @@ namespace MediaBrowser.Common
/// <summary>
/// Inits this instance.
/// </summary>
- void Init();
+ Task Init();
/// <summary>
/// Creates the instance.
diff --git a/MediaBrowser.Controller/Extensions/StringExtensions.cs b/MediaBrowser.Controller/Extensions/StringExtensions.cs
index 73f529fc0..b1aaf6534 100644
--- a/MediaBrowser.Controller/Extensions/StringExtensions.cs
+++ b/MediaBrowser.Controller/Extensions/StringExtensions.cs
@@ -1,4 +1,8 @@
-using MediaBrowser.Model.Globalization;
+using System;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
namespace MediaBrowser.Controller.Extensions
{
@@ -7,11 +11,45 @@ namespace MediaBrowser.Controller.Extensions
/// </summary>
public static class StringExtensions
{
- public static ILocalizationManager LocalizationManager { get; set; }
-
public static string RemoveDiacritics(this string text)
{
- return LocalizationManager.RemoveDiacritics(text);
+ if (text == null)
+ {
+ throw new ArgumentNullException(nameof(text));
+ }
+
+ var chars = Normalize(text, NormalizationForm.FormD)
+ .Where(ch => CharUnicodeInfo.GetUnicodeCategory(ch) != UnicodeCategory.NonSpacingMark);
+
+ return Normalize(string.Concat(chars), NormalizationForm.FormC);
+ }
+
+ private static string Normalize(string text, NormalizationForm form, bool stripStringOnFailure = true)
+ {
+ if (stripStringOnFailure)
+ {
+ try
+ {
+ return text.Normalize(form);
+ }
+ catch (ArgumentException)
+ {
+ // will throw if input contains invalid unicode chars
+ // https://mnaoumov.wordpress.com/2014/06/14/stripping-invalid-characters-from-utf-16-strings/
+ text = Regex.Replace(text, "([\ud800-\udbff](?![\udc00-\udfff]))|((?<![\ud800-\udbff])[\udc00-\udfff])", "");
+ return Normalize(text, form, false);
+ }
+ }
+
+ try
+ {
+ return text.Normalize(form);
+ }
+ catch (ArgumentException)
+ {
+ // if it still fails, return the original text
+ return text;
+ }
}
}
}
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs
index 1fe8856cc..916d691b8 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs
@@ -10,19 +10,13 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.MediaInfo;
using MediaBrowser.Model.Session;
-using Microsoft.Extensions.Logging;
-using System.IO;
using MediaBrowser.Model.Net;
-using MediaBrowser.Controller.Library;
-using System.Threading.Tasks;
namespace MediaBrowser.Controller.MediaEncoding
{
// For now, a common base class until the API and MediaEncoding classes are unified
public class EncodingJobInfo
{
- protected readonly IMediaSourceManager MediaSourceManager;
-
public MediaStream VideoStream { get; set; }
public VideoType VideoType { get; set; }
public Dictionary<string, string> RemoteHttpHeaders { get; set; }
@@ -49,7 +43,6 @@ namespace MediaBrowser.Controller.MediaEncoding
public string OutputFilePath { get; set; }
public string MimeType { get; set; }
- public long? EncodingDurationTicks { get; set; }
public string GetMimeType(string outputPath, bool enableStreamDefault = true)
{
@@ -68,7 +61,12 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (_transcodeReasons == null)
{
- _transcodeReasons = (BaseRequest.TranscodeReasons ?? string.Empty)
+ if (BaseRequest.TranscodeReasons == null)
+ {
+ return Array.Empty<TranscodeReason>();
+ }
+
+ _transcodeReasons = BaseRequest.TranscodeReasons
.Split(',')
.Where(i => !string.IsNullOrEmpty(i))
.Select(v => (TranscodeReason)Enum.Parse(typeof(TranscodeReason), v, true))
@@ -98,7 +96,8 @@ namespace MediaBrowser.Controller.MediaEncoding
get
{
// For live tv + in progress recordings
- if (string.Equals(InputContainer, "mpegts", StringComparison.OrdinalIgnoreCase) || string.Equals(InputContainer, "ts", StringComparison.OrdinalIgnoreCase))
+ if (string.Equals(InputContainer, "mpegts", StringComparison.OrdinalIgnoreCase)
+ || string.Equals(InputContainer, "ts", StringComparison.OrdinalIgnoreCase))
{
if (!MediaSource.RunTimeTicks.HasValue)
{
@@ -155,15 +154,7 @@ namespace MediaBrowser.Controller.MediaEncoding
}
}
- if (forceDeinterlaceIfSourceIsInterlaced)
- {
- if (isInputInterlaced)
- {
- return true;
- }
- }
-
- return false;
+ return forceDeinterlaceIfSourceIsInterlaced && isInputInterlaced;
}
public string[] GetRequestedProfiles(string codec)
@@ -211,7 +202,8 @@ namespace MediaBrowser.Controller.MediaEncoding
if (!string.IsNullOrEmpty(codec))
{
var value = BaseRequest.GetOption(codec, "maxrefframes");
- if (!string.IsNullOrEmpty(value) && int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var result))
+ if (!string.IsNullOrEmpty(value)
+ && int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var result))
{
return result;
}
@@ -230,7 +222,8 @@ namespace MediaBrowser.Controller.MediaEncoding
if (!string.IsNullOrEmpty(codec))
{
var value = BaseRequest.GetOption(codec, "videobitdepth");
- if (!string.IsNullOrEmpty(value) && int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var result))
+ if (!string.IsNullOrEmpty(value)
+ && int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var result))
{
return result;
}
@@ -249,7 +242,8 @@ namespace MediaBrowser.Controller.MediaEncoding
if (!string.IsNullOrEmpty(codec))
{
var value = BaseRequest.GetOption(codec, "audiobitdepth");
- if (!string.IsNullOrEmpty(value) && int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var result))
+ if (!string.IsNullOrEmpty(value)
+ && int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var result))
{
return result;
}
@@ -264,6 +258,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
return BaseRequest.MaxAudioChannels;
}
+
if (BaseRequest.AudioChannels.HasValue)
{
return BaseRequest.AudioChannels;
@@ -272,7 +267,8 @@ namespace MediaBrowser.Controller.MediaEncoding
if (!string.IsNullOrEmpty(codec))
{
var value = BaseRequest.GetOption(codec, "audiochannels");
- if (!string.IsNullOrEmpty(value) && int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var result))
+ if (!string.IsNullOrEmpty(value)
+ && int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var result))
{
return result;
}
@@ -294,7 +290,8 @@ namespace MediaBrowser.Controller.MediaEncoding
SupportedSubtitleCodecs = Array.Empty<string>();
}
- public bool IsSegmentedLiveStream => TranscodingType != TranscodingJobType.Progressive && !RunTimeTicks.HasValue;
+ public bool IsSegmentedLiveStream
+ => TranscodingType != TranscodingJobType.Progressive && !RunTimeTicks.HasValue;
public bool EnableBreakOnNonKeyFrames(string videoCodec)
{
@@ -428,11 +425,12 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (BaseRequest.Static || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
{
- return VideoStream == null ? null : VideoStream.Level;
+ return VideoStream?.Level;
}
var level = GetRequestedLevel(ActualOutputVideoCodec);
- if (!string.IsNullOrEmpty(level) && double.TryParse(level, NumberStyles.Any, CultureInfo.InvariantCulture, out var result))
+ if (!string.IsNullOrEmpty(level)
+ && double.TryParse(level, NumberStyles.Any, CultureInfo.InvariantCulture, out var result))
{
return result;
}
@@ -450,7 +448,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (BaseRequest.Static || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
{
- return VideoStream == null ? null : VideoStream.BitDepth;
+ return VideoStream?.BitDepth;
}
return null;
@@ -467,7 +465,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (BaseRequest.Static || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
{
- return VideoStream == null ? null : VideoStream.RefFrames;
+ return VideoStream?.RefFrames;
}
return null;
@@ -494,13 +492,14 @@ namespace MediaBrowser.Controller.MediaEncoding
{
get
{
- var defaultValue = string.Equals(OutputContainer, "m2ts", StringComparison.OrdinalIgnoreCase) ?
+ if (BaseRequest.Static)
+ {
+ return InputTimestamp;
+ }
+
+ return string.Equals(OutputContainer, "m2ts", StringComparison.OrdinalIgnoreCase) ?
TransportStreamTimestamp.Valid :
TransportStreamTimestamp.None;
-
- return !BaseRequest.Static
- ? defaultValue
- : InputTimestamp;
}
}
@@ -513,7 +512,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (BaseRequest.Static || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
{
- return VideoStream == null ? null : VideoStream.PacketLength;
+ return VideoStream?.PacketLength;
}
return null;
@@ -529,7 +528,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (BaseRequest.Static || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
{
- return VideoStream == null ? null : VideoStream.Profile;
+ return VideoStream?.Profile;
}
var requestedProfile = GetRequestedProfiles(ActualOutputVideoCodec).FirstOrDefault();
@@ -542,42 +541,13 @@ namespace MediaBrowser.Controller.MediaEncoding
}
}
- /// <summary>
- /// Predicts the audio sample rate that will be in the output stream
- /// </summary>
- public string TargetVideoRange
- {
- get
- {
- if (BaseRequest.Static || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
- {
- return VideoStream == null ? null : VideoStream.VideoRange;
- }
-
- return "SDR";
- }
- }
-
- public string TargetAudioProfile
- {
- get
- {
- if (BaseRequest.Static || string.Equals(OutputAudioCodec, "copy", StringComparison.OrdinalIgnoreCase))
- {
- return AudioStream == null ? null : AudioStream.Profile;
- }
-
- return null;
- }
- }
-
public string TargetVideoCodecTag
{
get
{
if (BaseRequest.Static || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
{
- return VideoStream == null ? null : VideoStream.CodecTag;
+ return VideoStream?.CodecTag;
}
return null;
@@ -590,7 +560,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (BaseRequest.Static || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
{
- return VideoStream == null ? null : VideoStream.IsAnamorphic;
+ return VideoStream?.IsAnamorphic;
}
return false;
@@ -605,14 +575,7 @@ namespace MediaBrowser.Controller.MediaEncoding
if (string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase))
{
- var stream = VideoStream;
-
- if (stream != null)
- {
- return stream.Codec;
- }
-
- return null;
+ return VideoStream?.Codec;
}
return codec;
@@ -627,14 +590,7 @@ namespace MediaBrowser.Controller.MediaEncoding
if (string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase))
{
- var stream = AudioStream;
-
- if (stream != null)
- {
- return stream.Codec;
- }
-
- return null;
+ return AudioStream?.Codec;
}
return codec;
@@ -647,7 +603,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (BaseRequest.Static || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
{
- return VideoStream == null ? (bool?)null : VideoStream.IsInterlaced;
+ return VideoStream?.IsInterlaced;
}
if (DeInterlace(ActualOutputVideoCodec, true))
@@ -655,7 +611,7 @@ namespace MediaBrowser.Controller.MediaEncoding
return false;
}
- return VideoStream == null ? (bool?)null : VideoStream.IsInterlaced;
+ return VideoStream?.IsInterlaced;
}
}
@@ -665,7 +621,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (BaseRequest.Static || string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
{
- return VideoStream == null ? null : VideoStream.IsAVC;
+ return VideoStream?.IsAVC;
}
return false;
diff --git a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
index 48055a37e..057e43910 100644
--- a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
+++ b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
@@ -82,28 +82,6 @@ namespace MediaBrowser.Controller.MediaEncoding
/// <returns>System.String.</returns>
string GetTimeParameter(long ticks);
- /// <summary>
- /// Encodes the audio.
- /// </summary>
- /// <param name="options">The options.</param>
- /// <param name="progress">The progress.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task<string> EncodeAudio(EncodingJobOptions options,
- IProgress<double> progress,
- CancellationToken cancellationToken);
-
- /// <summary>
- /// Encodes the video.
- /// </summary>
- /// <param name="options">The options.</param>
- /// <param name="progress">The progress.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task&lt;System.String&gt;.</returns>
- Task<string> EncodeVideo(EncodingJobOptions options,
- IProgress<double> progress,
- CancellationToken cancellationToken);
-
Task ConvertImage(string inputPath, string outputPath);
/// <summary>
diff --git a/MediaBrowser.Controller/Plugins/IServerEntryPoint.cs b/MediaBrowser.Controller/Plugins/IServerEntryPoint.cs
index 7b7a591aa..e57929989 100644
--- a/MediaBrowser.Controller/Plugins/IServerEntryPoint.cs
+++ b/MediaBrowser.Controller/Plugins/IServerEntryPoint.cs
@@ -1,4 +1,5 @@
using System;
+using System.Threading.Tasks;
namespace MediaBrowser.Controller.Plugins
{
@@ -10,7 +11,7 @@ namespace MediaBrowser.Controller.Plugins
/// <summary>
/// Runs this instance.
/// </summary>
- void Run();
+ Task RunAsync();
}
public interface IRunBeforeStartup
diff --git a/MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs
deleted file mode 100644
index d5773fe31..000000000
--- a/MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs
+++ /dev/null
@@ -1,58 +0,0 @@
-using System;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.MediaEncoding;
-using MediaBrowser.Controller.Session;
-using MediaBrowser.Model.Diagnostics;
-using MediaBrowser.Model.IO;
-using Microsoft.Extensions.Logging;
-
-namespace MediaBrowser.MediaEncoding.Encoder
-{
- public class AudioEncoder : BaseEncoder
- {
- public AudioEncoder(MediaEncoder mediaEncoder, ILogger logger, IServerConfigurationManager configurationManager, IFileSystem fileSystem, IIsoManager isoManager, ILibraryManager libraryManager, ISessionManager sessionManager, ISubtitleEncoder subtitleEncoder, IMediaSourceManager mediaSourceManager, IProcessFactory processFactory) : base(mediaEncoder, logger, configurationManager, fileSystem, isoManager, libraryManager, sessionManager, subtitleEncoder, mediaSourceManager, processFactory)
- {
- }
-
- protected override string GetCommandLineArguments(EncodingJob state)
- {
- var encodingOptions = GetEncodingOptions();
-
- return EncodingHelper.GetProgressiveAudioFullCommandLine(state, encodingOptions, state.OutputFilePath);
- }
-
- protected override string GetOutputFileExtension(EncodingJob state)
- {
- var ext = base.GetOutputFileExtension(state);
-
- if (!string.IsNullOrEmpty(ext))
- {
- return ext;
- }
-
- var audioCodec = state.Options.AudioCodec;
-
- if (string.Equals("aac", audioCodec, StringComparison.OrdinalIgnoreCase))
- {
- return ".aac";
- }
- if (string.Equals("mp3", audioCodec, StringComparison.OrdinalIgnoreCase))
- {
- return ".mp3";
- }
- if (string.Equals("vorbis", audioCodec, StringComparison.OrdinalIgnoreCase))
- {
- return ".ogg";
- }
- if (string.Equals("wma", audioCodec, StringComparison.OrdinalIgnoreCase))
- {
- return ".wma";
- }
-
- return null;
- }
-
- protected override bool IsVideoEncoder => false;
- }
-}
diff --git a/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs
deleted file mode 100644
index 81f7c16d3..000000000
--- a/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs
+++ /dev/null
@@ -1,372 +0,0 @@
-using System;
-using System.Globalization;
-using System.IO;
-using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.MediaEncoding;
-using MediaBrowser.Controller.Session;
-using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.Diagnostics;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.MediaInfo;
-using Microsoft.Extensions.Logging;
-
-namespace MediaBrowser.MediaEncoding.Encoder
-{
- public abstract class BaseEncoder
- {
- protected readonly MediaEncoder MediaEncoder;
- protected readonly ILogger Logger;
- protected readonly IServerConfigurationManager ConfigurationManager;
- protected readonly IFileSystem FileSystem;
- protected readonly IIsoManager IsoManager;
- protected readonly ILibraryManager LibraryManager;
- protected readonly ISessionManager SessionManager;
- protected readonly ISubtitleEncoder SubtitleEncoder;
- protected readonly IMediaSourceManager MediaSourceManager;
- protected IProcessFactory ProcessFactory;
-
- protected readonly CultureInfo UsCulture = new CultureInfo("en-US");
-
- protected EncodingHelper EncodingHelper;
-
- protected BaseEncoder(MediaEncoder mediaEncoder,
- ILogger logger,
- IServerConfigurationManager configurationManager,
- IFileSystem fileSystem,
- IIsoManager isoManager,
- ILibraryManager libraryManager,
- ISessionManager sessionManager,
- ISubtitleEncoder subtitleEncoder,
- IMediaSourceManager mediaSourceManager, IProcessFactory processFactory)
- {
- MediaEncoder = mediaEncoder;
- Logger = logger;
- ConfigurationManager = configurationManager;
- FileSystem = fileSystem;
- IsoManager = isoManager;
- LibraryManager = libraryManager;
- SessionManager = sessionManager;
- SubtitleEncoder = subtitleEncoder;
- MediaSourceManager = mediaSourceManager;
- ProcessFactory = processFactory;
-
- EncodingHelper = new EncodingHelper(MediaEncoder, FileSystem, SubtitleEncoder);
- }
-
- public async Task<EncodingJob> Start(EncodingJobOptions options,
- IProgress<double> progress,
- CancellationToken cancellationToken)
- {
- var encodingJob = await new EncodingJobFactory(Logger, LibraryManager, MediaSourceManager, ConfigurationManager, MediaEncoder)
- .CreateJob(options, EncodingHelper, IsVideoEncoder, progress, cancellationToken).ConfigureAwait(false);
-
- encodingJob.OutputFilePath = GetOutputFilePath(encodingJob);
- Directory.CreateDirectory(Path.GetDirectoryName(encodingJob.OutputFilePath));
-
- encodingJob.ReadInputAtNativeFramerate = options.ReadInputAtNativeFramerate;
-
- await AcquireResources(encodingJob, cancellationToken).ConfigureAwait(false);
-
- var commandLineArgs = GetCommandLineArguments(encodingJob);
-
- var process = ProcessFactory.Create(new ProcessOptions
- {
- CreateNoWindow = true,
- UseShellExecute = false,
-
- // Must consume both stdout and stderr or deadlocks may occur
- //RedirectStandardOutput = true,
- RedirectStandardError = true,
- RedirectStandardInput = true,
-
- FileName = MediaEncoder.EncoderPath,
- Arguments = commandLineArgs,
-
- IsHidden = true,
- ErrorDialog = false,
- EnableRaisingEvents = true
- });
-
- var workingDirectory = GetWorkingDirectory(options);
- if (!string.IsNullOrWhiteSpace(workingDirectory))
- {
- process.StartInfo.WorkingDirectory = workingDirectory;
- }
-
- OnTranscodeBeginning(encodingJob);
-
- var commandLineLogMessage = process.StartInfo.FileName + " " + process.StartInfo.Arguments;
- Logger.LogInformation(commandLineLogMessage);
-
- var logFilePath = Path.Combine(ConfigurationManager.CommonApplicationPaths.LogDirectoryPath, "transcode-" + Guid.NewGuid() + ".txt");
- Directory.CreateDirectory(Path.GetDirectoryName(logFilePath));
-
- // FFMpeg writes debug/error info to stderr. This is useful when debugging so let's put it in the log directory.
- encodingJob.LogFileStream = FileSystem.GetFileStream(logFilePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true);
-
- var commandLineLogMessageBytes = Encoding.UTF8.GetBytes(commandLineLogMessage + Environment.NewLine + Environment.NewLine);
- await encodingJob.LogFileStream.WriteAsync(commandLineLogMessageBytes, 0, commandLineLogMessageBytes.Length, cancellationToken).ConfigureAwait(false);
-
- process.Exited += (sender, args) => OnFfMpegProcessExited(process, encodingJob);
-
- try
- {
- process.Start();
- }
- catch (Exception ex)
- {
- Logger.LogError(ex, "Error starting ffmpeg");
-
- OnTranscodeFailedToStart(encodingJob.OutputFilePath, encodingJob);
-
- throw;
- }
-
- cancellationToken.Register(() => Cancel(process, encodingJob));
-
- // MUST read both stdout and stderr asynchronously or a deadlock may occurr
- //process.BeginOutputReadLine();
-
- // Important - don't await the log task or we won't be able to kill ffmpeg when the user stops playback
- new JobLogger(Logger).StartStreamingLog(encodingJob, process.StandardError.BaseStream, encodingJob.LogFileStream);
-
- // Wait for the file to exist before proceeeding
- while (!File.Exists(encodingJob.OutputFilePath) && !encodingJob.HasExited)
- {
- await Task.Delay(100, cancellationToken).ConfigureAwait(false);
- }
-
- return encodingJob;
- }
-
- private void Cancel(IProcess process, EncodingJob job)
- {
- Logger.LogInformation("Killing ffmpeg process for {0}", job.OutputFilePath);
-
- //process.Kill();
- process.StandardInput.WriteLine("q");
-
- job.IsCancelled = true;
- }
-
- /// <summary>
- /// Processes the exited.
- /// </summary>
- /// <param name="process">The process.</param>
- /// <param name="job">The job.</param>
- private void OnFfMpegProcessExited(IProcess process, EncodingJob job)
- {
- job.HasExited = true;
-
- Logger.LogDebug("Disposing stream resources");
- job.Dispose();
-
- var isSuccesful = false;
-
- try
- {
- var exitCode = process.ExitCode;
- Logger.LogInformation("FFMpeg exited with code {0}", exitCode);
-
- isSuccesful = exitCode == 0;
- }
- catch (Exception ex)
- {
- Logger.LogError(ex, "FFMpeg exited with an error.");
- }
-
- if (isSuccesful && !job.IsCancelled)
- {
- job.TaskCompletionSource.TrySetResult(true);
- }
- else if (job.IsCancelled)
- {
- try
- {
- DeleteFiles(job);
- }
- catch
- {
- }
- try
- {
- job.TaskCompletionSource.TrySetException(new OperationCanceledException());
- }
- catch
- {
- }
- }
- else
- {
- try
- {
- DeleteFiles(job);
- }
- catch
- {
- }
- try
- {
- job.TaskCompletionSource.TrySetException(new Exception("Encoding failed"));
- }
- catch
- {
- }
- }
-
- // This causes on exited to be called twice:
- //try
- //{
- // // Dispose the process
- // process.Dispose();
- //}
- //catch (Exception ex)
- //{
- // Logger.LogError("Error disposing ffmpeg.", ex);
- //}
- }
-
- protected virtual void DeleteFiles(EncodingJob job)
- {
- FileSystem.DeleteFile(job.OutputFilePath);
- }
-
- private void OnTranscodeBeginning(EncodingJob job)
- {
- job.ReportTranscodingProgress(null, null, null, null, null);
- }
-
- private void OnTranscodeFailedToStart(string path, EncodingJob job)
- {
- if (!string.IsNullOrWhiteSpace(job.Options.DeviceId))
- {
- SessionManager.ClearTranscodingInfo(job.Options.DeviceId);
- }
- }
-
- protected abstract bool IsVideoEncoder { get; }
-
- protected virtual string GetWorkingDirectory(EncodingJobOptions options)
- {
- return null;
- }
-
- protected EncodingOptions GetEncodingOptions()
- {
- return ConfigurationManager.GetConfiguration<EncodingOptions>("encoding");
- }
-
- protected abstract string GetCommandLineArguments(EncodingJob job);
-
- private string GetOutputFilePath(EncodingJob state)
- {
- var folder = string.IsNullOrWhiteSpace(state.Options.TempDirectory) ?
- ConfigurationManager.ApplicationPaths.TranscodingTempPath :
- state.Options.TempDirectory;
-
- var outputFileExtension = GetOutputFileExtension(state);
-
- var filename = state.Id + (outputFileExtension ?? string.Empty).ToLowerInvariant();
- return Path.Combine(folder, filename);
- }
-
- protected virtual string GetOutputFileExtension(EncodingJob state)
- {
- if (!string.IsNullOrWhiteSpace(state.Options.Container))
- {
- return "." + state.Options.Container;
- }
-
- return null;
- }
-
- /// <summary>
- /// Gets the name of the output video codec
- /// </summary>
- /// <param name="state">The state.</param>
- /// <returns>System.String.</returns>
- protected string GetVideoDecoder(EncodingJob state)
- {
- if (string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
- {
- return null;
- }
-
- // Only use alternative encoders for video files.
- // When using concat with folder rips, if the mfx session fails to initialize, ffmpeg will be stuck retrying and will not exit gracefully
- // Since transcoding of folder rips is expiremental anyway, it's not worth adding additional variables such as this.
- if (state.VideoType != VideoType.VideoFile)
- {
- return null;
- }
-
- if (state.VideoStream != null && !string.IsNullOrWhiteSpace(state.VideoStream.Codec))
- {
- if (string.Equals(GetEncodingOptions().HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase))
- {
- switch (state.MediaSource.VideoStream.Codec.ToLowerInvariant())
- {
- case "avc":
- case "h264":
- if (MediaEncoder.SupportsDecoder("h264_qsv"))
- {
- // Seeing stalls and failures with decoding. Not worth it compared to encoding.
- return "-c:v h264_qsv ";
- }
- break;
- case "mpeg2video":
- if (MediaEncoder.SupportsDecoder("mpeg2_qsv"))
- {
- return "-c:v mpeg2_qsv ";
- }
- break;
- case "vc1":
- if (MediaEncoder.SupportsDecoder("vc1_qsv"))
- {
- return "-c:v vc1_qsv ";
- }
- break;
- }
- }
- }
-
- // leave blank so ffmpeg will decide
- return null;
- }
-
- private async Task AcquireResources(EncodingJob state, CancellationToken cancellationToken)
- {
- if (state.VideoType == VideoType.Iso && state.IsoType.HasValue && IsoManager.CanMount(state.MediaPath))
- {
- state.IsoMount = await IsoManager.Mount(state.MediaPath, cancellationToken).ConfigureAwait(false);
- }
-
- if (state.MediaSource.RequiresOpening && string.IsNullOrWhiteSpace(state.Options.LiveStreamId))
- {
- var liveStreamResponse = await MediaSourceManager.OpenLiveStream(new LiveStreamRequest
- {
- OpenToken = state.MediaSource.OpenToken
-
- }, cancellationToken).ConfigureAwait(false);
-
- EncodingHelper.AttachMediaSourceInfo(state, liveStreamResponse.MediaSource, null);
-
- if (state.IsVideoRequest)
- {
- EncodingHelper.TryStreamCopy(state);
- }
- }
-
- if (state.MediaSource.BufferMs.HasValue)
- {
- await Task.Delay(state.MediaSource.BufferMs.Value, cancellationToken).ConfigureAwait(false);
- }
- }
- }
-}
diff --git a/MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs b/MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs
deleted file mode 100644
index d4040cd31..000000000
--- a/MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs
+++ /dev/null
@@ -1,149 +0,0 @@
-using System;
-using System.IO;
-using System.Threading.Tasks;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.MediaEncoding;
-using MediaBrowser.Model.Dlna;
-using MediaBrowser.Model.Net;
-using Microsoft.Extensions.Logging;
-
-namespace MediaBrowser.MediaEncoding.Encoder
-{
- public class EncodingJob : EncodingJobInfo, IDisposable
- {
- public bool HasExited { get; internal set; }
- public bool IsCancelled { get; internal set; }
-
- public Stream LogFileStream { get; set; }
- public TaskCompletionSource<bool> TaskCompletionSource;
-
- public EncodingJobOptions Options
- {
- get => (EncodingJobOptions)BaseRequest;
- set => BaseRequest = value;
- }
-
- public Guid Id { get; set; }
-
- public bool EstimateContentLength { get; set; }
- public TranscodeSeekInfo TranscodeSeekInfo { get; set; }
-
- public string ItemType { get; set; }
-
- private readonly ILogger _logger;
- private readonly IMediaSourceManager _mediaSourceManager;
-
- public EncodingJob(ILogger logger, IMediaSourceManager mediaSourceManager) :
- base(TranscodingJobType.Progressive)
- {
- _logger = logger;
- _mediaSourceManager = mediaSourceManager;
- Id = Guid.NewGuid();
-
- _logger = logger;
- TaskCompletionSource = new TaskCompletionSource<bool>();
- }
-
- public override void Dispose()
- {
- DisposeLiveStream();
- DisposeLogStream();
- DisposeIsoMount();
- }
-
- private void DisposeLogStream()
- {
- if (LogFileStream != null)
- {
- try
- {
- LogFileStream.Dispose();
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, "Error disposing log stream");
- }
-
- LogFileStream = null;
- }
- }
-
- private async void DisposeLiveStream()
- {
- if (MediaSource.RequiresClosing && string.IsNullOrWhiteSpace(Options.LiveStreamId) && !string.IsNullOrWhiteSpace(MediaSource.LiveStreamId))
- {
- try
- {
- await _mediaSourceManager.CloseLiveStream(MediaSource.LiveStreamId).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, "Error closing media source");
- }
- }
- }
-
-
- private void DisposeIsoMount()
- {
- if (IsoMount != null)
- {
- try
- {
- IsoMount.Dispose();
- }
- catch (Exception ex)
- {
- _logger.LogError("Error disposing iso mount", ex);
- }
-
- IsoMount = null;
- }
- }
-
- public void ReportTranscodingProgress(TimeSpan? transcodingPosition, float? framerate, double? percentComplete, long? bytesTranscoded, int? bitRate)
- {
- var ticks = transcodingPosition.HasValue ? transcodingPosition.Value.Ticks : (long?)null;
-
- //job.Framerate = framerate;
-
- if (!percentComplete.HasValue && ticks.HasValue && RunTimeTicks.HasValue)
- {
- var pct = ticks.Value / RunTimeTicks.Value;
- percentComplete = pct * 100;
- }
-
- if (percentComplete.HasValue)
- {
- Progress.Report(percentComplete.Value);
- }
-
- /*
- job.TranscodingPositionTicks = ticks;
- job.BytesTranscoded = bytesTranscoded;
-
- var deviceId = Options.DeviceId;
-
- if (!string.IsNullOrWhiteSpace(deviceId))
- {
- var audioCodec = ActualOutputVideoCodec;
- var videoCodec = ActualOutputVideoCodec;
-
- SessionManager.ReportTranscodingInfo(deviceId, new TranscodingInfo
- {
- Bitrate = job.TotalOutputBitrate,
- AudioCodec = audioCodec,
- VideoCodec = videoCodec,
- Container = job.Options.OutputContainer,
- Framerate = framerate,
- CompletionPercentage = percentComplete,
- Width = job.OutputWidth,
- Height = job.OutputHeight,
- AudioChannels = job.OutputAudioChannels,
- IsAudioDirect = string.Equals(job.OutputAudioCodec, "copy", StringComparison.OrdinalIgnoreCase),
- IsVideoDirect = string.Equals(job.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase)
- });
- }*/
- }
- }
-}
diff --git a/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs b/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs
deleted file mode 100644
index 95454c447..000000000
--- a/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs
+++ /dev/null
@@ -1,307 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.MediaEncoding;
-using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.Dlna;
-using MediaBrowser.Model.Entities;
-using Microsoft.Extensions.Logging;
-
-namespace MediaBrowser.MediaEncoding.Encoder
-{
- public class EncodingJobFactory
- {
- private readonly ILogger _logger;
- private readonly ILibraryManager _libraryManager;
- private readonly IMediaSourceManager _mediaSourceManager;
- private readonly IConfigurationManager _config;
- private readonly IMediaEncoder _mediaEncoder;
-
- protected static readonly CultureInfo UsCulture = new CultureInfo("en-US");
-
- public EncodingJobFactory(ILogger logger, ILibraryManager libraryManager, IMediaSourceManager mediaSourceManager, IConfigurationManager config, IMediaEncoder mediaEncoder)
- {
- _logger = logger;
- _libraryManager = libraryManager;
- _mediaSourceManager = mediaSourceManager;
- _config = config;
- _mediaEncoder = mediaEncoder;
- }
-
- public async Task<EncodingJob> CreateJob(EncodingJobOptions options, EncodingHelper encodingHelper, bool isVideoRequest, IProgress<double> progress, CancellationToken cancellationToken)
- {
- var request = options;
-
- if (string.IsNullOrEmpty(request.AudioCodec))
- {
- request.AudioCodec = InferAudioCodec(request.Container);
- }
-
- var state = new EncodingJob(_logger, _mediaSourceManager)
- {
- Options = options,
- IsVideoRequest = isVideoRequest,
- Progress = progress
- };
-
- if (!string.IsNullOrWhiteSpace(request.VideoCodec))
- {
- state.SupportedVideoCodecs = request.VideoCodec.Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToArray();
- request.VideoCodec = state.SupportedVideoCodecs.FirstOrDefault();
- }
-
- if (!string.IsNullOrWhiteSpace(request.AudioCodec))
- {
- state.SupportedAudioCodecs = request.AudioCodec.Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToArray();
- request.AudioCodec = state.SupportedAudioCodecs.FirstOrDefault();
- }
-
- if (!string.IsNullOrWhiteSpace(request.SubtitleCodec))
- {
- state.SupportedSubtitleCodecs = request.SubtitleCodec.Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToArray();
- request.SubtitleCodec = state.SupportedSubtitleCodecs.FirstOrDefault(i => _mediaEncoder.CanEncodeToSubtitleCodec(i))
- ?? state.SupportedSubtitleCodecs.FirstOrDefault();
- }
-
- var item = _libraryManager.GetItemById(request.Id);
- state.ItemType = item.GetType().Name;
-
- state.IsInputVideo = string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase);
-
- // TODO
- // var primaryImage = item.GetImageInfo(ImageType.Primary, 0) ??
- // item.Parents.Select(i => i.GetImageInfo(ImageType.Primary, 0)).FirstOrDefault(i => i != null);
-
- // if (primaryImage != null)
- // {
- // state.AlbumCoverPath = primaryImage.Path;
- // }
-
- // TODO network path substition useful ?
- var mediaSources = await _mediaSourceManager.GetPlayackMediaSources(item, null, true, true, cancellationToken).ConfigureAwait(false);
-
- var mediaSource = string.IsNullOrEmpty(request.MediaSourceId)
- ? mediaSources.First()
- : mediaSources.First(i => string.Equals(i.Id, request.MediaSourceId));
-
- var videoRequest = state.Options;
-
- encodingHelper.AttachMediaSourceInfo(state, mediaSource, null);
-
- //var container = Path.GetExtension(state.RequestedUrl);
-
- //if (string.IsNullOrEmpty(container))
- //{
- // container = request.Static ?
- // state.InputContainer :
- // (Path.GetExtension(GetOutputFilePath(state)) ?? string.Empty).TrimStart('.');
- //}
-
- //state.OutputContainer = (container ?? string.Empty).TrimStart('.');
-
- state.OutputAudioBitrate = encodingHelper.GetAudioBitrateParam(state.Options, state.AudioStream);
-
- state.OutputAudioCodec = state.Options.AudioCodec;
-
- state.OutputAudioChannels = encodingHelper.GetNumAudioChannelsParam(state, state.AudioStream, state.OutputAudioCodec);
-
- if (videoRequest != null)
- {
- state.OutputVideoCodec = state.Options.VideoCodec;
- state.OutputVideoBitrate = encodingHelper.GetVideoBitrateParamValue(state.Options, state.VideoStream, state.OutputVideoCodec);
-
- if (state.OutputVideoBitrate.HasValue)
- {
- var resolution = ResolutionNormalizer.Normalize(
- state.VideoStream == null ? (int?)null : state.VideoStream.BitRate,
- state.VideoStream == null ? (int?)null : state.VideoStream.Width,
- state.VideoStream == null ? (int?)null : state.VideoStream.Height,
- state.OutputVideoBitrate.Value,
- state.VideoStream == null ? null : state.VideoStream.Codec,
- state.OutputVideoCodec,
- videoRequest.MaxWidth,
- videoRequest.MaxHeight);
-
- videoRequest.MaxWidth = resolution.MaxWidth;
- videoRequest.MaxHeight = resolution.MaxHeight;
- }
- }
-
- ApplyDeviceProfileSettings(state);
-
- if (videoRequest != null)
- {
- encodingHelper.TryStreamCopy(state);
- }
-
- //state.OutputFilePath = GetOutputFilePath(state);
-
- return state;
- }
-
- protected EncodingOptions GetEncodingOptions()
- {
- return _config.GetConfiguration<EncodingOptions>("encoding");
- }
-
- /// <summary>
- /// Infers the video codec.
- /// </summary>
- /// <param name="container">The container.</param>
- /// <returns>System.Nullable{VideoCodecs}.</returns>
- private static string InferVideoCodec(string container)
- {
- var ext = "." + (container ?? string.Empty);
-
- if (string.Equals(ext, ".asf", StringComparison.OrdinalIgnoreCase))
- {
- return "wmv";
- }
- if (string.Equals(ext, ".webm", StringComparison.OrdinalIgnoreCase))
- {
- return "vpx";
- }
- if (string.Equals(ext, ".ogg", StringComparison.OrdinalIgnoreCase) || string.Equals(ext, ".ogv", StringComparison.OrdinalIgnoreCase))
- {
- return "theora";
- }
- if (string.Equals(ext, ".m3u8", StringComparison.OrdinalIgnoreCase) || string.Equals(ext, ".ts", StringComparison.OrdinalIgnoreCase))
- {
- return "h264";
- }
-
- return "copy";
- }
-
- private string InferAudioCodec(string container)
- {
- var ext = "." + (container ?? string.Empty);
-
- if (string.Equals(ext, ".mp3", StringComparison.OrdinalIgnoreCase))
- {
- return "mp3";
- }
- if (string.Equals(ext, ".aac", StringComparison.OrdinalIgnoreCase))
- {
- return "aac";
- }
- if (string.Equals(ext, ".wma", StringComparison.OrdinalIgnoreCase))
- {
- return "wma";
- }
- if (string.Equals(ext, ".ogg", StringComparison.OrdinalIgnoreCase))
- {
- return "vorbis";
- }
- if (string.Equals(ext, ".oga", StringComparison.OrdinalIgnoreCase))
- {
- return "vorbis";
- }
- if (string.Equals(ext, ".ogv", StringComparison.OrdinalIgnoreCase))
- {
- return "vorbis";
- }
- if (string.Equals(ext, ".webm", StringComparison.OrdinalIgnoreCase))
- {
- return "vorbis";
- }
- if (string.Equals(ext, ".webma", StringComparison.OrdinalIgnoreCase))
- {
- return "vorbis";
- }
-
- return "copy";
- }
-
- /// <summary>
- /// Determines whether the specified stream is H264.
- /// </summary>
- /// <param name="stream">The stream.</param>
- /// <returns><c>true</c> if the specified stream is H264; otherwise, <c>false</c>.</returns>
- protected bool IsH264(MediaStream stream)
- {
- var codec = stream.Codec ?? string.Empty;
-
- return codec.IndexOf("264", StringComparison.OrdinalIgnoreCase) != -1 ||
- codec.IndexOf("avc", StringComparison.OrdinalIgnoreCase) != -1;
- }
-
- private static int GetVideoProfileScore(string profile)
- {
- var list = new List<string>
- {
- "Constrained Baseline",
- "Baseline",
- "Extended",
- "Main",
- "High",
- "Progressive High",
- "Constrained High"
- };
-
- return Array.FindIndex(list.ToArray(), t => string.Equals(t, profile, StringComparison.OrdinalIgnoreCase));
- }
-
- private void ApplyDeviceProfileSettings(EncodingJob state)
- {
- var profile = state.Options.DeviceProfile;
-
- if (profile == null)
- {
- // Don't use settings from the default profile.
- // Only use a specific profile if it was requested.
- return;
- }
-
- var audioCodec = state.ActualOutputAudioCodec;
-
- var videoCodec = state.ActualOutputVideoCodec;
- var outputContainer = state.Options.Container;
-
- var mediaProfile = state.IsVideoRequest ?
- profile.GetAudioMediaProfile(outputContainer, audioCodec, state.OutputAudioChannels, state.OutputAudioBitrate, state.OutputAudioSampleRate, state.OutputAudioBitDepth) :
- profile.GetVideoMediaProfile(outputContainer,
- audioCodec,
- videoCodec,
- state.OutputWidth,
- state.OutputHeight,
- state.TargetVideoBitDepth,
- state.OutputVideoBitrate,
- state.TargetVideoProfile,
- state.TargetVideoLevel,
- state.TargetFramerate,
- state.TargetPacketLength,
- state.TargetTimestamp,
- state.IsTargetAnamorphic,
- state.IsTargetInterlaced,
- state.TargetRefFrames,
- state.TargetVideoStreamCount,
- state.TargetAudioStreamCount,
- state.TargetVideoCodecTag,
- state.IsTargetAVC);
-
- if (mediaProfile != null)
- {
- state.MimeType = mediaProfile.MimeType;
- }
-
- var transcodingProfile = state.IsVideoRequest ?
- profile.GetAudioTranscodingProfile(outputContainer, audioCodec) :
- profile.GetVideoTranscodingProfile(outputContainer, audioCodec, videoCodec);
-
- if (transcodingProfile != null)
- {
- state.EstimateContentLength = transcodingProfile.EstimateContentLength;
- //state.EnableMpegtsM2TsMode = transcodingProfile.EnableMpegtsM2TsMode;
- state.TranscodeSeekInfo = transcodingProfile.TranscodeSeekInfo;
-
- state.Options.CopyTimestamps = transcodingProfile.CopyTimestamps;
- }
- }
- }
-}
diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
index 54344424d..d922f1068 100644
--- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
@@ -878,49 +878,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
}
}
- public async Task<string> EncodeAudio(EncodingJobOptions options,
- IProgress<double> progress,
- CancellationToken cancellationToken)
- {
- var job = await new AudioEncoder(this,
- _logger,
- ConfigurationManager,
- FileSystem,
- IsoManager,
- LibraryManager,
- SessionManager,
- SubtitleEncoder(),
- MediaSourceManager(),
- _processFactory)
- .Start(options, progress, cancellationToken).ConfigureAwait(false);
-
- await job.TaskCompletionSource.Task.ConfigureAwait(false);
-
- return job.OutputFilePath;
- }
-
- public async Task<string> EncodeVideo(EncodingJobOptions options,
- IProgress<double> progress,
- CancellationToken cancellationToken)
- {
- _logger.LogError("EncodeVideo");
- var job = await new VideoEncoder(this,
- _logger,
- ConfigurationManager,
- FileSystem,
- IsoManager,
- LibraryManager,
- SessionManager,
- SubtitleEncoder(),
- MediaSourceManager(),
- _processFactory)
- .Start(options, progress, cancellationToken).ConfigureAwait(false);
-
- await job.TaskCompletionSource.Task.ConfigureAwait(false);
-
- return job.OutputFilePath;
- }
-
private void StartProcess(ProcessWrapper process)
{
process.Process.Start();
diff --git a/MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs
deleted file mode 100644
index bf23a73bd..000000000
--- a/MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs
+++ /dev/null
@@ -1,59 +0,0 @@
-using System;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.MediaEncoding;
-using MediaBrowser.Controller.Session;
-using MediaBrowser.Model.Diagnostics;
-using MediaBrowser.Model.IO;
-using Microsoft.Extensions.Logging;
-
-namespace MediaBrowser.MediaEncoding.Encoder
-{
- public class VideoEncoder : BaseEncoder
- {
- public VideoEncoder(MediaEncoder mediaEncoder, ILogger logger, IServerConfigurationManager configurationManager, IFileSystem fileSystem, IIsoManager isoManager, ILibraryManager libraryManager, ISessionManager sessionManager, ISubtitleEncoder subtitleEncoder, IMediaSourceManager mediaSourceManager, IProcessFactory processFactory) : base(mediaEncoder, logger, configurationManager, fileSystem, isoManager, libraryManager, sessionManager, subtitleEncoder, mediaSourceManager, processFactory)
- {
- }
-
- protected override string GetCommandLineArguments(EncodingJob state)
- {
- // Get the output codec name
- var encodingOptions = GetEncodingOptions();
-
- return EncodingHelper.GetProgressiveVideoFullCommandLine(state, encodingOptions, state.OutputFilePath, "superfast");
- }
-
- protected override string GetOutputFileExtension(EncodingJob state)
- {
- var ext = base.GetOutputFileExtension(state);
-
- if (!string.IsNullOrEmpty(ext))
- {
- return ext;
- }
-
- var videoCodec = state.Options.VideoCodec;
-
- if (string.Equals(videoCodec, "h264", StringComparison.OrdinalIgnoreCase))
- {
- return ".ts";
- }
- if (string.Equals(videoCodec, "theora", StringComparison.OrdinalIgnoreCase))
- {
- return ".ogv";
- }
- if (string.Equals(videoCodec, "vpx", StringComparison.OrdinalIgnoreCase))
- {
- return ".webm";
- }
- if (string.Equals(videoCodec, "wmv", StringComparison.OrdinalIgnoreCase))
- {
- return ".asf";
- }
-
- return null;
- }
-
- protected override bool IsVideoEncoder => true;
- }
-}
diff --git a/MediaBrowser.Model/Globalization/ILocalizationManager.cs b/MediaBrowser.Model/Globalization/ILocalizationManager.cs
index 05efb6681..a9ce60a2a 100644
--- a/MediaBrowser.Model/Globalization/ILocalizationManager.cs
+++ b/MediaBrowser.Model/Globalization/ILocalizationManager.cs
@@ -1,4 +1,6 @@
+using System.Collections.Generic;
using System.Globalization;
+using System.Threading.Tasks;
using MediaBrowser.Model.Entities;
namespace MediaBrowser.Model.Globalization
@@ -17,12 +19,12 @@ namespace MediaBrowser.Model.Globalization
/// Gets the countries.
/// </summary>
/// <returns>IEnumerable{CountryInfo}.</returns>
- CountryInfo[] GetCountries();
+ Task<CountryInfo[]> GetCountries();
/// <summary>
/// Gets the parental ratings.
/// </summary>
/// <returns>IEnumerable{ParentalRating}.</returns>
- ParentalRating[] GetParentalRatings();
+ IEnumerable<ParentalRating> GetParentalRatings();
/// <summary>
/// Gets the rating level.
/// </summary>
@@ -51,8 +53,6 @@ namespace MediaBrowser.Model.Globalization
/// <returns>IEnumerable{LocalizatonOption}.</returns>
LocalizationOption[] GetLocalizationOptions();
- string RemoveDiacritics(string text);
-
string NormalizeFormKD(string text);
bool HasUnicodeCategory(string value, UnicodeCategory category);
diff --git a/MediaBrowser.Providers/Manager/MetadataService.cs b/MediaBrowser.Providers/Manager/MetadataService.cs
index 1972ad290..77028e526 100644
--- a/MediaBrowser.Providers/Manager/MetadataService.cs
+++ b/MediaBrowser.Providers/Manager/MetadataService.cs
@@ -566,8 +566,7 @@ namespace MediaBrowser.Providers.Manager
var providersWithChanges = providers
.Where(i =>
{
- var hasFileChangeMonitor = i as IHasItemChangeMonitor;
- if (hasFileChangeMonitor != null)
+ if (i is IHasItemChangeMonitor hasFileChangeMonitor)
{
return HasChanged(item, hasFileChangeMonitor, options.DirectoryService);
}
diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs
index afc760dd4..4e11fcbb2 100644
--- a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs
+++ b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs
@@ -74,17 +74,12 @@ namespace MediaBrowser.Providers.MediaInfo
}
}
- if (item.SupportsLocalMetadata)
+ if (item.SupportsLocalMetadata && video != null && !video.IsPlaceHolder
+ && !video.SubtitleFiles.SequenceEqual(
+ _subtitleResolver.GetExternalSubtitleFiles(video, directoryService, false), StringComparer.Ordinal))
{
- if (video != null && !video.IsPlaceHolder)
- {
- if (!video.SubtitleFiles
- .SequenceEqual(_subtitleResolver.GetExternalSubtitleFiles(video, directoryService, false), StringComparer.Ordinal))
- {
- _logger.LogDebug("Refreshing {0} due to external subtitles change.", item.Path);
- return true;
- }
- }
+ _logger.LogDebug("Refreshing {0} due to external subtitles change.", item.Path);
+ return true;
}
return false;
diff --git a/MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs b/MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs
index b7598ef22..74f41f9df 100644
--- a/MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs
+++ b/MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs
@@ -36,12 +36,6 @@ namespace MediaBrowser.Providers.MediaInfo
_json = json;
}
- public string Name => "Download missing subtitles";
-
- public string Description => "Searches the internet for missing subtitles based on metadata configuration.";
-
- public string Category => "Library";
-
private SubtitleOptions GetOptions()
{
return _config.GetConfiguration<SubtitleOptions>("subtitles");
@@ -204,6 +198,18 @@ namespace MediaBrowser.Providers.MediaInfo
};
}
+ public string Name => "Download missing subtitles";
+
+ public string Description => "Searches the internet for missing subtitles based on metadata configuration.";
+
+ public string Category => "Library";
+
public string Key => "DownloadSubtitles";
+
+ public bool IsHidden => false;
+
+ public bool IsEnabled => true;
+
+ public bool IsLogged => true;
}
}
diff --git a/MediaBrowser.WebDashboard/Api/DashboardService.cs b/MediaBrowser.WebDashboard/Api/DashboardService.cs
index 2f4e21443..531978e1d 100644
--- a/MediaBrowser.WebDashboard/Api/DashboardService.cs
+++ b/MediaBrowser.WebDashboard/Api/DashboardService.cs
@@ -425,11 +425,9 @@ namespace MediaBrowser.WebDashboard.Api
private async Task DumpFile(PackageCreator packageCreator, string resourceVirtualPath, string destinationFilePath, string mode, string appVersion)
{
using (var stream = await packageCreator.GetResource(resourceVirtualPath, mode, null, appVersion).ConfigureAwait(false))
+ using (var fs = _fileSystem.GetFileStream(destinationFilePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read))
{
- using (var fs = _fileSystem.GetFileStream(destinationFilePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read))
- {
- stream.CopyTo(fs);
- }
+ await stream.CopyToAsync(fs);
}
}
diff --git a/MediaBrowser.WebDashboard/ServerEntryPoint.cs b/MediaBrowser.WebDashboard/ServerEntryPoint.cs
index 221fa62c7..18ed54a78 100644
--- a/MediaBrowser.WebDashboard/ServerEntryPoint.cs
+++ b/MediaBrowser.WebDashboard/ServerEntryPoint.cs
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Linq;
+using System.Threading.Tasks;
using MediaBrowser.Common;
using MediaBrowser.Controller.Plugins;
@@ -23,9 +24,11 @@ namespace MediaBrowser.WebDashboard
Instance = this;
}
- public void Run()
+ public Task RunAsync()
{
PluginConfigurationPages = _appHost.GetExports<IPluginConfigurationPage>().ToList();
+
+ return Task.CompletedTask;
}
public void Dispose()
diff --git a/MediaBrowser.XbmcMetadata/EntryPoint.cs b/MediaBrowser.XbmcMetadata/EntryPoint.cs
index 37a1d4c35..992991a7e 100644
--- a/MediaBrowser.XbmcMetadata/EntryPoint.cs
+++ b/MediaBrowser.XbmcMetadata/EntryPoint.cs
@@ -1,4 +1,5 @@
using System;
+using System.Threading.Tasks;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
@@ -28,9 +29,11 @@ namespace MediaBrowser.XbmcMetadata
_config = config;
}
- public void Run()
+ public Task RunAsync()
{
_userDataManager.UserDataSaved += _userDataManager_UserDataSaved;
+
+ return Task.CompletedTask;
}
void _userDataManager_UserDataSaved(object sender, UserDataSaveEventArgs e)
diff --git a/bump_version b/bump_version
index c3f1a78d5..a63fbf735 100755
--- a/bump_version
+++ b/bump_version
@@ -68,8 +68,7 @@ new_version="$1"
# Parse the version from the AssemblyVersion
old_version="$(
grep "AssemblyVersion" ${shared_version_file} \
- | sed -E 's/\[assembly: ?AssemblyVersion\("([0-9\.]+)"\)\]/\1/' \
- | sed -E 's/.0$//'
+ | sed -E 's/\[assembly: ?AssemblyVersion\("([0-9\.]+)"\)\]/\1/'
)"
# Set the shared version to the specified new_version
diff --git a/deployment/centos-package-x64/Dockerfile b/deployment/centos-package-x64/Dockerfile
new file mode 100644
index 000000000..bbe5ffd27
--- /dev/null
+++ b/deployment/centos-package-x64/Dockerfile
@@ -0,0 +1,15 @@
+FROM centos:7
+ARG HOME=/build
+RUN mkdir /build && \
+ yum install -y @buildsys-build rpmdevtools yum-plugins-core && \
+ rpm -Uvh https://packages.microsoft.com/config/rhel/7/packages-microsoft-prod.rpm && \
+ rpmdev-setuptree
+
+WORKDIR /build/rpmbuild
+COPY ./deployment/centos-package-x64/pkg-src/jellyfin.spec SPECS
+COPY ./deployment/centos-package-x64/pkg-src/ SOURCES
+
+RUN spectool -g -R SPECS/jellyfin.spec && \
+ rpmbuild -bs SPECS/jellyfin.spec && \
+ yum-builddep -y SRPMS/jellyfin-*.src.rpm && \
+ rpmbuild -bb SPECS/jellyfin.spec; \ No newline at end of file
diff --git a/deployment/centos-package-x64/clean.sh b/deployment/centos-package-x64/clean.sh
new file mode 120000
index 000000000..d6d2d1c09
--- /dev/null
+++ b/deployment/centos-package-x64/clean.sh
@@ -0,0 +1 @@
+../fedora-package-x64/clean.sh \ No newline at end of file
diff --git a/deployment/centos-package-x64/package.sh b/deployment/centos-package-x64/package.sh
new file mode 120000
index 000000000..a79de21eb
--- /dev/null
+++ b/deployment/centos-package-x64/package.sh
@@ -0,0 +1 @@
+../fedora-package-x64/package.sh \ No newline at end of file
diff --git a/deployment/centos-package-x64/pkg-src b/deployment/centos-package-x64/pkg-src
new file mode 120000
index 000000000..dfd6497cf
--- /dev/null
+++ b/deployment/centos-package-x64/pkg-src
@@ -0,0 +1 @@
+../fedora-package-x64/pkg-src \ No newline at end of file
diff --git a/deployment/fedora-package-x64/pkg-src/jellyfin.env b/deployment/fedora-package-x64/pkg-src/jellyfin.env
index f7f041f75..c23bfd470 100644
--- a/deployment/fedora-package-x64/pkg-src/jellyfin.env
+++ b/deployment/fedora-package-x64/pkg-src/jellyfin.env
@@ -21,7 +21,7 @@
JELLYFIN_DATA_DIRECTORY="/var/lib/jellyfin"
JELLYFIN_CONFIG_DIRECTORY="/etc/jellyfin"
JELLYFIN_LOG_DIRECTORY="/var/log/jellyfin"
-JELLYFIN_CACHE_DIRECTORY="/var/log/jellyfin"
+JELLYFIN_CACHE_DIRECTORY="/var/cache/jellyfin"
# In-App service control
JELLYFIN_RESTART_OPT="--restartpath /usr/libexec/jellyfin/restart.sh"
# Additional options for the binary
diff --git a/deployment/fedora-package-x64/pkg-src/jellyfin.spec b/deployment/fedora-package-x64/pkg-src/jellyfin.spec
index 6a4a5870b..851c40044 100644
--- a/deployment/fedora-package-x64/pkg-src/jellyfin.spec
+++ b/deployment/fedora-package-x64/pkg-src/jellyfin.spec
@@ -1,11 +1,11 @@
%global debug_package %{nil}
-# jellyfin tag to package
-%global gittag v10.1.0
-# Taglib-sharp commit of the submodule since github archive doesn't include submodules
-%global taglib_commit ee5ab21742b71fd1b87ee24895582327e9e04776
-%global taglib_shortcommit %(c=%{taglib_commit}; echo ${c:0:7})
+# Set the dotnet runtime
+%if 0%{?fedora}
+%global dotnet_runtime fedora-x64
+%else
+%global dotnet_runtime centos-x64
+%endif
-AutoReq: no
Name: jellyfin
Version: 10.1.0
Release: 1%{?dist}
@@ -31,13 +31,11 @@ BuildRequires: dotnet-sdk-2.2
# RPMfusion free
Requires: ffmpeg
-# For the update-db-paths.sh script to fix emby paths to jellyfin
-%{?fedora:Recommends: sqlite}
-
# Fedora has openssl1.1 which is incompatible with dotnet
%{?fedora:Requires: compat-openssl10}
-# Disable Automatic Dependency Processing for Centos
-%{?el7:AutoReqProv: no}
+
+# Disable Automatic Dependency Processing
+AutoReqProv: no
%description
Jellyfin is a free software media system that puts you in control of managing and streaming your media.
@@ -51,7 +49,7 @@ Jellyfin is a free software media system that puts you in control of managing an
%install
export DOTNET_CLI_TELEMETRY_OPTOUT=1
export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1
-dotnet publish --configuration Release --output='%{buildroot}%{_libdir}/jellyfin' --self-contained --runtime fedora-x64 Jellyfin.Server
+dotnet publish --configuration Release --output='%{buildroot}%{_libdir}/jellyfin' --self-contained --runtime %{dotnet_runtime} Jellyfin.Server
%{__install} -D -m 0644 LICENSE %{buildroot}%{_datadir}/licenses/%{name}/LICENSE
%{__install} -D -m 0644 %{SOURCE5} %{buildroot}%{_sysconfdir}/systemd/system/%{name}.service.d/override.conf
%{__install} -D -m 0644 Jellyfin.Server/Resources/Configuration/logging.json %{buildroot}%{_sysconfdir}/%{name}/logging.json
@@ -63,6 +61,7 @@ EOF
%{__mkdir} -p %{buildroot}%{_sharedstatedir}/jellyfin
%{__mkdir} -p %{buildroot}%{_sysconfdir}/%{name}
%{__mkdir} -p %{buildroot}%{_var}/log/jellyfin
+%{__mkdir} -p %{buildroot}%{_var}/cache/jellyfin
%{__install} -D -m 0644 %{SOURCE1} %{buildroot}%{_unitdir}/%{name}.service
%{__install} -D -m 0644 %{SOURCE2} %{buildroot}%{_sysconfdir}/sysconfig/%{name}
@@ -90,8 +89,9 @@ EOF
%config(noreplace) %attr(600,root,root) %{_sysconfdir}/sudoers.d/%{name}-sudoers
%config(noreplace) %{_sysconfdir}/systemd/system/%{name}.service.d/override.conf
%config(noreplace) %attr(644,jellyfin,jellyfin) %{_sysconfdir}/%{name}/logging.json
-%attr(-,jellyfin,jellyfin) %dir %{_sharedstatedir}/jellyfin
+%attr(750,jellyfin,jellyfin) %dir %{_sharedstatedir}/jellyfin
%attr(-,jellyfin,jellyfin) %dir %{_var}/log/jellyfin
+%attr(750,jellyfin,jellyfin) %dir %{_var}/cache/jellyfin
%if 0%{?fedora}
%license LICENSE
%else
@@ -106,7 +106,7 @@ getent passwd jellyfin >/dev/null || \
exit 0
%post
-# Move existing configuration to /etc/jellyfin and symlink config to /etc/jellyfin
+# Move existing configuration cache and logs to their new locations and symlink them.
if [ $1 -gt 1 ] ; then
service_state=$(systemctl is-active jellyfin.service)
if [ "${service_state}" = "active" ]; then
@@ -122,6 +122,11 @@ if [ $1 -gt 1 ] ; then
rmdir %{_sharedstatedir}/%{name}/logs
ln -sf %{_var}/log/jellyfin %{_sharedstatedir}/%{name}/logs
fi
+ if [ ! -L %{_sharedstatedir}/%{name}/cache ]; then
+ mv %{_sharedstatedir}/%{name}/cache/* %{_var}/cache/jellyfin
+ rmdir %{_sharedstatedir}/%{name}/cache
+ ln -sf %{_var}/cache/jellyfin %{_sharedstatedir}/%{name}/cache
+ fi
if [ "${service_state}" = "active" ]; then
systemctl start jellyfin.service
fi