diff --git a/Emby.Notifications/Api/NotificationsService.cs b/Emby.Notifications/Api/NotificationsService.cs
index f2f38183815..67401c1f5bb 100644
--- a/Emby.Notifications/Api/NotificationsService.cs
+++ b/Emby.Notifications/Api/NotificationsService.cs
@@ -1,6 +1,5 @@
#pragma warning disable CS1591
#pragma warning disable SA1402
-#pragma warning disable SA1600
#pragma warning disable SA1649
using System;
diff --git a/Emby.Notifications/CoreNotificationTypes.cs b/Emby.Notifications/CoreNotificationTypes.cs
index 73e0b0256ac..a602b72213b 100644
--- a/Emby.Notifications/CoreNotificationTypes.cs
+++ b/Emby.Notifications/CoreNotificationTypes.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Collections.Generic;
diff --git a/Emby.Notifications/NotificationConfigurationFactory.cs b/Emby.Notifications/NotificationConfigurationFactory.cs
index b168ed221b0..3fb3553d0ec 100644
--- a/Emby.Notifications/NotificationConfigurationFactory.cs
+++ b/Emby.Notifications/NotificationConfigurationFactory.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System.Collections.Generic;
using MediaBrowser.Common.Configuration;
diff --git a/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs b/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs
index c3cdcc22222..bc478174387 100644
--- a/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs
+++ b/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs
@@ -5,7 +5,7 @@ using MediaBrowser.Common.Configuration;
namespace Emby.Server.Implementations.AppBase
{
///
- /// Provides a base class to hold common application paths used by both the Ui and Server.
+ /// Provides a base class to hold common application paths used by both the UI and Server.
/// This can be subclassed to add application-specific paths.
///
public abstract class BaseApplicationPaths : IApplicationPaths
@@ -37,10 +37,7 @@ namespace Emby.Server.Implementations.AppBase
/// The program data path.
public string ProgramDataPath { get; }
- ///
- /// Gets the path to the web UI resources folder.
- ///
- /// The web UI resources path.
+ ///
public string WebPath { get; }
///
diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs
index d6a572818a0..4fbc933a811 100644
--- a/Emby.Server.Implementations/ApplicationHost.cs
+++ b/Emby.Server.Implementations/ApplicationHost.cs
@@ -235,11 +235,6 @@ namespace Emby.Server.Implementations
///
public int HttpsPort { get; private set; }
- ///
- /// Gets the content root for the webhost.
- ///
- public string ContentRoot { get; private set; }
-
///
/// Gets the server configuration manager.
///
@@ -612,13 +607,7 @@ namespace Emby.Server.Implementations
DiscoverTypes();
- await RegisterResources(serviceCollection, startupConfig).ConfigureAwait(false);
-
- ContentRoot = ServerConfigurationManager.Configuration.DashboardSourcePath;
- if (string.IsNullOrEmpty(ContentRoot))
- {
- ContentRoot = ServerConfigurationManager.ApplicationPaths.WebPath;
- }
+ await RegisterServices(serviceCollection, startupConfig).ConfigureAwait(false);
}
public async Task ExecuteWebsocketHandlerAsync(HttpContext context, Func next)
@@ -649,9 +638,9 @@ namespace Emby.Server.Implementations
}
///
- /// Registers resources that classes will depend on
+ /// Registers services/resources with the service collection that will be available via DI.
///
- protected async Task RegisterResources(IServiceCollection serviceCollection, IConfiguration startupConfig)
+ protected async Task RegisterServices(IServiceCollection serviceCollection, IConfiguration startupConfig)
{
serviceCollection.AddMemoryCache();
@@ -769,20 +758,8 @@ namespace Emby.Server.Implementations
CertificateInfo = GetCertificateInfo(true);
Certificate = GetCertificate(CertificateInfo);
- HttpServer = new HttpListenerHost(
- this,
- LoggerFactory.CreateLogger(),
- ServerConfigurationManager,
- startupConfig,
- NetworkManager,
- JsonSerializer,
- XmlSerializer,
- CreateHttpListener())
- {
- GlobalResponse = LocalizationManager.GetLocalizedString("StartupEmbyServerIsLoading")
- };
-
- serviceCollection.AddSingleton(HttpServer);
+ serviceCollection.AddSingleton();
+ serviceCollection.AddSingleton();
ImageProcessor = new ImageProcessor(LoggerFactory.CreateLogger(), ServerConfigurationManager.ApplicationPaths, FileSystemManager, ImageEncoder, () => LibraryManager, () => MediaEncoder);
serviceCollection.AddSingleton(ImageProcessor);
@@ -844,10 +821,15 @@ namespace Emby.Server.Implementations
serviceCollection.AddSingleton(new DeviceDiscovery(ServerConfigurationManager));
- ChapterManager = new ChapterManager(LibraryManager, LoggerFactory, ServerConfigurationManager, ItemRepository);
+ ChapterManager = new ChapterManager(ItemRepository);
serviceCollection.AddSingleton(ChapterManager);
- EncodingManager = new MediaEncoder.EncodingManager(FileSystemManager, LoggerFactory, MediaEncoder, ChapterManager, LibraryManager);
+ EncodingManager = new MediaEncoder.EncodingManager(
+ LoggerFactory.CreateLogger(),
+ FileSystemManager,
+ MediaEncoder,
+ ChapterManager,
+ LibraryManager);
serviceCollection.AddSingleton(EncodingManager);
var activityLogRepo = GetActivityLogRepository();
@@ -890,6 +872,14 @@ namespace Emby.Server.Implementations
((LibraryManager)LibraryManager).ItemRepository = ItemRepository;
}
+ ///
+ /// Create services registered with the service container that need to be initialized at application startup.
+ ///
+ public void InitializeServices()
+ {
+ HttpServer = Resolve();
+ }
+
public static void LogEnvironmentInfo(ILogger logger, IApplicationPaths appPaths)
{
// Distinct these to prevent users from reporting problems that aren't actually problems
@@ -1167,7 +1157,7 @@ namespace Emby.Server.Implementations
{
exportedTypes = ass.GetExportedTypes();
}
- catch (TypeLoadException ex)
+ catch (FileNotFoundException ex)
{
Logger.LogError(ex, "Error getting exported types from {Assembly}", ass.FullName);
continue;
@@ -1207,8 +1197,6 @@ namespace Emby.Server.Implementations
});
}
- protected IHttpListener CreateHttpListener() => new WebSocketSharpListener(LoggerFactory.CreateLogger());
-
private CertificateInfo GetCertificateInfo(bool generateCertificate)
{
// Custom cert
diff --git a/Emby.Server.Implementations/Browser/BrowserLauncher.cs b/Emby.Server.Implementations/Browser/BrowserLauncher.cs
index f5da0d01836..96096e142aa 100644
--- a/Emby.Server.Implementations/Browser/BrowserLauncher.cs
+++ b/Emby.Server.Implementations/Browser/BrowserLauncher.cs
@@ -1,51 +1,48 @@
using System;
using MediaBrowser.Controller;
+using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.Browser
{
///
- /// Class BrowserLauncher.
+ /// Assists in opening application URLs in an external browser.
///
public static class BrowserLauncher
{
///
- /// Opens the dashboard page.
- ///
- /// The page.
- /// The app host.
- private static void OpenDashboardPage(string page, IServerApplicationHost appHost)
- {
- var url = appHost.GetLocalApiUrl("localhost") + "/web/" + page;
-
- OpenUrl(appHost, url);
- }
-
- ///
- /// Opens the web client.
+ /// Opens the home page of the web client.
///
/// The app host.
public static void OpenWebApp(IServerApplicationHost appHost)
{
- OpenDashboardPage("index.html", appHost);
+ TryOpenUrl(appHost, "/web/index.html");
}
///
- /// Opens the URL.
+ /// Opens the swagger API page.
///
- /// The application host instance.
+ /// The app host.
+ public static void OpenSwaggerPage(IServerApplicationHost appHost)
+ {
+ TryOpenUrl(appHost, "/swagger/index.html");
+ }
+
+ ///
+ /// Opens the specified URL in an external browser window. Any exceptions will be logged, but ignored.
+ ///
+ /// The application host.
/// The URL.
- private static void OpenUrl(IServerApplicationHost appHost, string url)
+ private static void TryOpenUrl(IServerApplicationHost appHost, string url)
{
try
{
- appHost.LaunchUrl(url);
+ string baseUrl = appHost.GetLocalApiUrl("localhost");
+ appHost.LaunchUrl(baseUrl + url);
}
- catch (NotSupportedException)
- {
-
- }
- catch (Exception)
+ catch (Exception ex)
{
+ var logger = appHost.Resolve();
+ logger?.LogError(ex, "Failed to open browser window with URL {URL}", url);
}
}
}
diff --git a/Emby.Server.Implementations/ConfigurationOptions.cs b/Emby.Server.Implementations/ConfigurationOptions.cs
index 31fb5ca58c8..4574a64fdb4 100644
--- a/Emby.Server.Implementations/ConfigurationOptions.cs
+++ b/Emby.Server.Implementations/ConfigurationOptions.cs
@@ -1,13 +1,22 @@
using System.Collections.Generic;
+using Emby.Server.Implementations.HttpServer;
+using MediaBrowser.Providers.Music;
using static MediaBrowser.Controller.Extensions.ConfigurationExtensions;
namespace Emby.Server.Implementations
{
+ ///
+ /// Static class containing the default configuration options for the web server.
+ ///
public static class ConfigurationOptions
{
- public static Dictionary Configuration => new Dictionary
+ ///
+ /// Gets a new copy of the default configuration options.
+ ///
+ public static Dictionary DefaultConfiguration => new Dictionary
{
- { "HttpListenerHost:DefaultRedirectPath", "web/index.html" },
+ { HostWebClientKey, bool.TrueString },
+ { HttpListenerHost.DefaultRedirectKey, "web/index.html" },
{ FfmpegProbeSizeKey, "1G" },
{ FfmpegAnalyzeDurationKey, "200M" },
{ PlaylistsAllowDuplicatesKey, bool.TrueString }
diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
index cb5b398eefe..e3242f7b4f8 100644
--- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs
+++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
@@ -2006,7 +2006,7 @@ namespace Emby.Server.Implementations.Data
///
/// Saves the chapters.
///
- public void SaveChapters(Guid id, List chapters)
+ public void SaveChapters(Guid id, IReadOnlyList chapters)
{
CheckDisposed();
@@ -2035,22 +2035,24 @@ namespace Emby.Server.Implementations.Data
}
}
- private void InsertChapters(byte[] idBlob, List chapters, IDatabaseConnection db)
+ private void InsertChapters(byte[] idBlob, IReadOnlyList chapters, IDatabaseConnection db)
{
var startIndex = 0;
var limit = 100;
var chapterIndex = 0;
+ const string StartInsertText = "insert into " + ChaptersTableName + " (ItemId, ChapterIndex, StartPositionTicks, Name, ImagePath, ImageDateModified) values ";
+ var insertText = new StringBuilder(StartInsertText, 256);
+
while (startIndex < chapters.Count)
{
- var insertText = new StringBuilder("insert into " + ChaptersTableName + " (ItemId, ChapterIndex, StartPositionTicks, Name, ImagePath, ImageDateModified) values ");
-
var endIndex = Math.Min(chapters.Count, startIndex + limit);
for (var i = startIndex; i < endIndex; i++)
{
insertText.AppendFormat("(@ItemId, @ChapterIndex{0}, @StartPositionTicks{0}, @Name{0}, @ImagePath{0}, @ImageDateModified{0}),", i.ToString(CultureInfo.InvariantCulture));
}
+
insertText.Length -= 1; // Remove last ,
using (var statement = PrepareStatement(db, insertText.ToString()))
@@ -2077,6 +2079,7 @@ namespace Emby.Server.Implementations.Data
}
startIndex += limit;
+ insertText.Length = StartInsertText.Length;
}
}
diff --git a/Emby.Server.Implementations/EntryPoints/StartupWizard.cs b/Emby.Server.Implementations/EntryPoints/StartupWizard.cs
index 5f2d629fead..8e97719311f 100644
--- a/Emby.Server.Implementations/EntryPoints/StartupWizard.cs
+++ b/Emby.Server.Implementations/EntryPoints/StartupWizard.cs
@@ -2,7 +2,9 @@ using System.Threading.Tasks;
using Emby.Server.Implementations.Browser;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Extensions;
using MediaBrowser.Controller.Plugins;
+using Microsoft.Extensions.Configuration;
namespace Emby.Server.Implementations.EntryPoints
{
@@ -11,10 +13,8 @@ namespace Emby.Server.Implementations.EntryPoints
///
public sealed class StartupWizard : IServerEntryPoint
{
- ///
- /// The app host.
- ///
private readonly IServerApplicationHost _appHost;
+ private readonly IConfiguration _appConfig;
private readonly IServerConfigurationManager _config;
///
@@ -22,9 +22,10 @@ namespace Emby.Server.Implementations.EntryPoints
///
/// The application host.
/// The configuration manager.
- public StartupWizard(IServerApplicationHost appHost, IServerConfigurationManager config)
+ public StartupWizard(IServerApplicationHost appHost, IConfiguration appConfig, IServerConfigurationManager config)
{
_appHost = appHost;
+ _appConfig = appConfig;
_config = config;
}
@@ -36,7 +37,11 @@ namespace Emby.Server.Implementations.EntryPoints
return Task.CompletedTask;
}
- if (!_config.Configuration.IsStartupWizardCompleted)
+ if (!_appConfig.HostWebClient())
+ {
+ BrowserLauncher.OpenSwaggerPage(_appHost);
+ }
+ else if (!_config.Configuration.IsStartupWizardCompleted)
{
BrowserLauncher.OpenWebApp(_appHost);
}
diff --git a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
index 93572b8bfe8..655130fcfcc 100644
--- a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
+++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
@@ -17,6 +17,7 @@ using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Events;
+using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Services;
using Microsoft.AspNetCore.Http;
@@ -29,6 +30,12 @@ namespace Emby.Server.Implementations.HttpServer
{
public class HttpListenerHost : IHttpServer, IDisposable
{
+ ///
+ /// The key for a setting that specifies the default redirect path
+ /// to use for requests where the URL base prefix is invalid or missing.
+ ///
+ public const string DefaultRedirectKey = "HttpListenerHost:DefaultRedirectPath";
+
private readonly ILogger _logger;
private readonly IServerConfigurationManager _config;
private readonly INetworkManager _networkManager;
@@ -52,12 +59,13 @@ namespace Emby.Server.Implementations.HttpServer
INetworkManager networkManager,
IJsonSerializer jsonSerializer,
IXmlSerializer xmlSerializer,
- IHttpListener socketListener)
+ IHttpListener socketListener,
+ ILocalizationManager localizationManager)
{
_appHost = applicationHost;
_logger = logger;
_config = config;
- _defaultRedirectPath = configuration["HttpListenerHost:DefaultRedirectPath"];
+ _defaultRedirectPath = configuration[DefaultRedirectKey];
_baseUrlPrefix = _config.Configuration.BaseUrl;
_networkManager = networkManager;
_jsonSerializer = jsonSerializer;
@@ -69,6 +77,7 @@ namespace Emby.Server.Implementations.HttpServer
Instance = this;
ResponseFilters = Array.Empty>();
+ GlobalResponse = localizationManager.GetLocalizedString("StartupEmbyServerIsLoading");
}
public event EventHandler> WebSocketConnected;
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs
index 9c4f5fe3d84..e2bff97c813 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.IO;
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
index 8590c56dfd4..d24fc679225 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs
index a716b6240f4..69a9cb78aad 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System.Threading.Tasks;
using MediaBrowser.Controller.Plugins;
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/IRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/IRecorder.cs
index d6a1aee38b2..4712724d672 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/IRecorder.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/IRecorder.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Threading;
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs
index 6d42a58f42d..fc543dc5515 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs
index 4cb9f6fe88f..0b0ff6cb31f 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Globalization;
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs
index 9cc53fddcd9..194e4606deb 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using MediaBrowser.Controller.LiveTv;
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs
index 330e881ef5d..7ebb043d8ec 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Collections.Concurrent;
diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
index e9d3105bf10..00f469d8389 100644
--- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
+++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Collections.Concurrent;
diff --git a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs
index c159b60a91b..609b397da80 100644
--- a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs
+++ b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/LiveTv/LiveTvConfigurationFactory.cs b/Emby.Server.Implementations/LiveTv/LiveTvConfigurationFactory.cs
index 222fed9d92f..ba916af389e 100644
--- a/Emby.Server.Implementations/LiveTv/LiveTvConfigurationFactory.cs
+++ b/Emby.Server.Implementations/LiveTv/LiveTvConfigurationFactory.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System.Collections.Generic;
using MediaBrowser.Common.Configuration;
diff --git a/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs b/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs
index 14b627f82ea..6e903a18eff 100644
--- a/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs
+++ b/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Globalization;
diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
index f20f6140e50..b64fe8634cf 100644
--- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
+++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs b/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs
index 33887bbfd20..7f63991d0c4 100644
--- a/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs
+++ b/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs
index 419ec3635a9..80ee1ee33a6 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Collections.Concurrent;
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
index a2d972d19c0..25b2c674c58 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs
index 56864ab116d..57c5b75002c 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Buffers;
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs
index 1bad389822e..03ee5bfb656 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs
index 9fa042090c2..4e4f1d7f609 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
index 46c77e7b0ed..f5dda79db3b 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
index 511af150bb6..59451fccd24 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs
index 86151838710..9ced65cca9e 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System;
using System.Collections.Generic;
diff --git a/Emby.Server.Implementations/Localization/Core/bg-BG.json b/Emby.Server.Implementations/Localization/Core/bg-BG.json
index 345f384605d..3fc7c7dc0fe 100644
--- a/Emby.Server.Implementations/Localization/Core/bg-BG.json
+++ b/Emby.Server.Implementations/Localization/Core/bg-BG.json
@@ -1,8 +1,8 @@
{
"Albums": "Албуми",
- "AppDeviceValues": "Програма: {0}, устройство: {1}",
+ "AppDeviceValues": "Програма: {0}, Устройство: {1}",
"Application": "Програма",
- "Artists": "Изпълнители",
+ "Artists": "Артисти",
"AuthenticationSucceededWithUserName": "{0} се удостовери успешно",
"Books": "Книги",
"CameraImageUploadedFrom": "Нова снимка от камера беше качена от {0}",
@@ -92,5 +92,27 @@
"UserStoppedPlayingItemWithValues": "{0} спря {1}",
"ValueHasBeenAddedToLibrary": "{0} беше добавен във Вашата библиотека",
"ValueSpecialEpisodeName": "Специални - {0}",
- "VersionNumber": "Версия {0}"
+ "VersionNumber": "Версия {0}",
+ "TaskDownloadMissingSubtitlesDescription": "Търси Интернет за липсващи поднадписи, на база конфигурацията за мета-данни.",
+ "TaskDownloadMissingSubtitles": "Изтегляне на липсващи поднадписи",
+ "TaskRefreshChannelsDescription": "Обновява информацията за интернет канала.",
+ "TaskRefreshChannels": "Обновяване на Канали",
+ "TaskCleanTranscodeDescription": "Изтрива прекодирани файлове по-стари от един ден.",
+ "TaskCleanTranscode": "Изчиства директорията за прекодиране",
+ "TaskUpdatePluginsDescription": "Изтегля и инсталира актуализации за добавките, които са настроени за автоматична актуализация.",
+ "TaskUpdatePlugins": "Актуализира добавките",
+ "TaskRefreshPeopleDescription": "Актуализира мета-данните за артистите и режисьорите за Вашата медийна библиотека.",
+ "TaskRefreshPeople": "Обновяване на участниците",
+ "TaskCleanLogsDescription": "Изтрива лог файлове по-стари от {0} дни.",
+ "TaskCleanLogs": "Изчисти директорията с логове",
+ "TaskRefreshLibraryDescription": "Сканира Вашата библиотека с медия за нови файлове и обновява мета-данните.",
+ "TaskRefreshLibrary": "Сканиране на библиотеката с медия",
+ "TaskRefreshChapterImagesDescription": "Създава иконки за видеа, които имат епизоди.",
+ "TaskRefreshChapterImages": "Извличане на изображения за епизода",
+ "TaskCleanCacheDescription": "Изтриване на ненужните от системата файлове.",
+ "TaskCleanCache": "Изчистване на Кеш-директорията",
+ "TasksChannelsCategory": "Интернет Канали",
+ "TasksApplicationCategory": "Приложение",
+ "TasksLibraryCategory": "Библиотека",
+ "TasksMaintenanceCategory": "Поддръжка"
}
diff --git a/Emby.Server.Implementations/Localization/Core/fr.json b/Emby.Server.Implementations/Localization/Core/fr.json
index d93c803a352..88a7ac19064 100644
--- a/Emby.Server.Implementations/Localization/Core/fr.json
+++ b/Emby.Server.Implementations/Localization/Core/fr.json
@@ -92,5 +92,27 @@
"UserStoppedPlayingItemWithValues": "{0} vient d'arrêter la lecture de {1} sur {2}",
"ValueHasBeenAddedToLibrary": "{0} a été ajouté à votre médiathèque",
"ValueSpecialEpisodeName": "Spécial - {0}",
- "VersionNumber": "Version {0}"
+ "VersionNumber": "Version {0}",
+ "TasksChannelsCategory": "Chaines en ligne",
+ "TaskDownloadMissingSubtitlesDescription": "Cherche les sous-titres manquant sur internet en se basant sur la configuration des métadonnées.",
+ "TaskDownloadMissingSubtitles": "Télécharge les sous-titres manquant",
+ "TaskRefreshChannelsDescription": "Rafraîchit les informations des chaines en ligne.",
+ "TaskRefreshChannels": "Rafraîchit les chaines",
+ "TaskCleanTranscodeDescription": "Supprime les fichiers transcodés de plus d'un jour.",
+ "TaskCleanTranscode": "Nettoie les dossier des transcodages",
+ "TaskUpdatePluginsDescription": "Télécharge et installe les mises à jours des plugins configurés pour être mis à jour automatiquement.",
+ "TaskUpdatePlugins": "Mettre à jour les plugins",
+ "TaskRefreshPeopleDescription": "Met à jour les métadonnées pour les acteurs et directeurs dans votre bibliothèque.",
+ "TaskRefreshPeople": "Rafraîchit les acteurs",
+ "TaskCleanLogsDescription": "Supprime les journaux de plus de {0} jours.",
+ "TaskCleanLogs": "Nettoie le répertoire des journaux",
+ "TaskRefreshLibraryDescription": "Scanne toute les bibliothèques pour trouver les nouveaux fichiers et rafraîchit les métadonnées.",
+ "TaskRefreshLibrary": "Scanne toute les Bibliothèques",
+ "TaskRefreshChapterImagesDescription": "Crée des images de miniature pour les vidéos ayant des chapitres.",
+ "TaskRefreshChapterImages": "Extrait les images de chapitre",
+ "TaskCleanCacheDescription": "Supprime les fichiers de cache dont le système n'a plus besoin.",
+ "TaskCleanCache": "Vider le répertoire cache",
+ "TasksApplicationCategory": "Application",
+ "TasksLibraryCategory": "Bibliothèque",
+ "TasksMaintenanceCategory": "Maintenance"
}
diff --git a/Emby.Server.Implementations/Localization/Core/it.json b/Emby.Server.Implementations/Localization/Core/it.json
index b9348e058f5..0758bbe9ceb 100644
--- a/Emby.Server.Implementations/Localization/Core/it.json
+++ b/Emby.Server.Implementations/Localization/Core/it.json
@@ -5,7 +5,7 @@
"Artists": "Artisti",
"AuthenticationSucceededWithUserName": "{0} autenticato con successo",
"Books": "Libri",
- "CameraImageUploadedFrom": "È stata caricata una nuova immagine della fotocamera da {0}",
+ "CameraImageUploadedFrom": "È stata caricata una nuova immagine della fotocamera dal device {0}",
"Channels": "Canali",
"ChapterNameValue": "Capitolo {0}",
"Collections": "Collezioni",
@@ -15,7 +15,7 @@
"Favorites": "Preferiti",
"Folders": "Cartelle",
"Genres": "Generi",
- "HeaderAlbumArtists": "Artisti dell' Album",
+ "HeaderAlbumArtists": "Artisti degli Album",
"HeaderCameraUploads": "Caricamenti Fotocamera",
"HeaderContinueWatching": "Continua a guardare",
"HeaderFavoriteAlbums": "Album Preferiti",
@@ -92,5 +92,27 @@
"UserStoppedPlayingItemWithValues": "{0} ha interrotto la riproduzione di {1} su {2}",
"ValueHasBeenAddedToLibrary": "{0} è stato aggiunto alla tua libreria multimediale",
"ValueSpecialEpisodeName": "Speciale - {0}",
- "VersionNumber": "Versione {0}"
+ "VersionNumber": "Versione {0}",
+ "TaskRefreshChannelsDescription": "Aggiorna le informazioni dei canali Internet.",
+ "TaskDownloadMissingSubtitlesDescription": "Cerca su internet i sottotitoli mancanti basandosi sulle configurazioni dei metadati.",
+ "TaskDownloadMissingSubtitles": "Scarica i sottotitoli mancanti",
+ "TaskRefreshChannels": "Aggiorna i canali",
+ "TaskCleanTranscodeDescription": "Cancella i file di transcode più vecchi di un giorno.",
+ "TaskCleanTranscode": "Svuota la cartella del transcoding",
+ "TaskUpdatePluginsDescription": "Scarica e installa gli aggiornamenti per i plugin che sono stati configurati per essere aggiornati contemporaneamente.",
+ "TaskUpdatePlugins": "Aggiorna i Plugin",
+ "TaskRefreshPeopleDescription": "Aggiorna i metadati per gli attori e registi nella tua libreria multimediale.",
+ "TaskRefreshPeople": "Aggiorna persone",
+ "TaskCleanLogsDescription": "Rimuovi i file di log più vecchi di {0} giorni.",
+ "TaskCleanLogs": "Pulisci la cartella dei log",
+ "TaskRefreshLibraryDescription": "Analizza la tua libreria multimediale per nuovi file e rinnova i metadati.",
+ "TaskRefreshLibrary": "Analizza la libreria dei contenuti multimediali",
+ "TaskRefreshChapterImagesDescription": "Crea le thumbnail per i video che hanno capitoli.",
+ "TaskRefreshChapterImages": "Estrai immagini capitolo",
+ "TaskCleanCacheDescription": "Cancella i file di cache non più necessari al sistema.",
+ "TaskCleanCache": "Pulisci la directory della cache",
+ "TasksChannelsCategory": "Canali su Internet",
+ "TasksApplicationCategory": "Applicazione",
+ "TasksLibraryCategory": "Libreria",
+ "TasksMaintenanceCategory": "Manutenzione"
}
diff --git a/Emby.Server.Implementations/Localization/Core/pt-BR.json b/Emby.Server.Implementations/Localization/Core/pt-BR.json
index 10ca4f9326d..3a69b6d7a5d 100644
--- a/Emby.Server.Implementations/Localization/Core/pt-BR.json
+++ b/Emby.Server.Implementations/Localization/Core/pt-BR.json
@@ -92,5 +92,27 @@
"UserStoppedPlayingItemWithValues": "{0} parou de reproduzir {1} em {2}",
"ValueHasBeenAddedToLibrary": "{0} foi adicionado à sua biblioteca de mídia",
"ValueSpecialEpisodeName": "Especial - {0}",
- "VersionNumber": "Versão {0}"
+ "VersionNumber": "Versão {0}",
+ "TaskDownloadMissingSubtitlesDescription": "Procurar na internet por legendas faltando baseado na configuração de metadados.",
+ "TaskDownloadMissingSubtitles": "Baixar legendas que estão faltando",
+ "TaskRefreshChannelsDescription": "Atualizar informação de canais da internet .",
+ "TaskRefreshChannels": "Atualizar Canais",
+ "TaskCleanTranscodeDescription": "Deletar arquivos de transcodificação com mais de um dia de criação.",
+ "TaskCleanTranscode": "Limpar pasta de transcodificação",
+ "TaskUpdatePluginsDescription": "Baixa e instala atualizações para plugins que estão configurados para atualizar automaticamente.",
+ "TaskUpdatePlugins": "Atualizar Plugins",
+ "TaskRefreshPeopleDescription": "Atualiza metadados para atores e diretores na sua biblioteca de mídia.",
+ "TaskRefreshPeople": "Atualizar pessoas",
+ "TaskCleanLogsDescription": "Deletar arquivos temporários com mais de {0} dias.",
+ "TaskCleanLogs": "Limpar pasta de logs",
+ "TaskRefreshLibraryDescription": "Escaneie a sua biblioteca de mídia para arquivos novos e atualize os metadados.",
+ "TaskRefreshLibrary": "Escanear a Biblioteca de Mídia",
+ "TaskRefreshChapterImagesDescription": "Criar miniaturas para vídeos que tem capítulos.",
+ "TaskRefreshChapterImages": "Extrair imagens dos capítulos",
+ "TaskCleanCacheDescription": "Deletar arquivos temporários que não são mais necessários para o sistema.",
+ "TaskCleanCache": "Limpar Arquivos Temporários",
+ "TasksChannelsCategory": "Canais da Internet",
+ "TasksApplicationCategory": "Aplicativo",
+ "TasksLibraryCategory": "Biblioteca",
+ "TasksMaintenanceCategory": "Manutenção"
}
diff --git a/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs b/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs
index 840aca7a6ba..677d68b4c9c 100644
--- a/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs
+++ b/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs
@@ -26,14 +26,20 @@ namespace Emby.Server.Implementations.MediaEncoder
private readonly IChapterManager _chapterManager;
private readonly ILibraryManager _libraryManager;
+ ///
+ /// The first chapter ticks.
+ ///
+ private static readonly long _firstChapterTicks = TimeSpan.FromSeconds(15).Ticks;
+
public EncodingManager(
+ ILogger logger,
IFileSystem fileSystem,
- ILoggerFactory loggerFactory,
IMediaEncoder encoder,
- IChapterManager chapterManager, ILibraryManager libraryManager)
+ IChapterManager chapterManager,
+ ILibraryManager libraryManager)
{
+ _logger = logger;
_fileSystem = fileSystem;
- _logger = loggerFactory.CreateLogger(nameof(EncodingManager));
_encoder = encoder;
_chapterManager = chapterManager;
_libraryManager = libraryManager;
@@ -97,12 +103,7 @@ namespace Emby.Server.Implementations.MediaEncoder
return video.DefaultVideoStreamIndex.HasValue;
}
- ///
- /// The first chapter ticks
- ///
- private static readonly long FirstChapterTicks = TimeSpan.FromSeconds(15).Ticks;
-
- public async Task RefreshChapterImages(Video video, IDirectoryService directoryService, List chapters, bool extractImages, bool saveChapters, CancellationToken cancellationToken)
+ public async Task RefreshChapterImages(Video video, IDirectoryService directoryService, IReadOnlyList chapters, bool extractImages, bool saveChapters, CancellationToken cancellationToken)
{
if (!IsEligibleForChapterImageExtraction(video))
{
@@ -135,7 +136,7 @@ namespace Emby.Server.Implementations.MediaEncoder
try
{
// Add some time for the first chapter to make sure we don't end up with a black image
- var time = chapter.StartPositionTicks == 0 ? TimeSpan.FromTicks(Math.Min(FirstChapterTicks, video.RunTimeTicks ?? 0)) : TimeSpan.FromTicks(chapter.StartPositionTicks);
+ var time = chapter.StartPositionTicks == 0 ? TimeSpan.FromTicks(Math.Min(_firstChapterTicks, video.RunTimeTicks ?? 0)) : TimeSpan.FromTicks(chapter.StartPositionTicks);
var protocol = MediaProtocol.File;
@@ -152,9 +153,9 @@ namespace Emby.Server.Implementations.MediaEncoder
{
_fileSystem.DeleteFile(tempFile);
}
- catch
+ catch (IOException ex)
{
-
+ _logger.LogError(ex, "Error deleting temporary chapter image encoding file {Path}", tempFile);
}
chapter.ImagePath = path;
@@ -184,7 +185,7 @@ namespace Emby.Server.Implementations.MediaEncoder
if (saveChapters && changesMade)
{
- _chapterManager.SaveChapters(video.Id.ToString(), chapters);
+ _chapterManager.SaveChapters(video.Id, chapters);
}
DeleteDeadImages(currentImages, chapters);
@@ -199,22 +200,21 @@ namespace Emby.Server.Implementations.MediaEncoder
return Path.Combine(GetChapterImagesPath(video), filename);
}
- private static List GetSavedChapterImages(Video video, IDirectoryService directoryService)
+ private static IReadOnlyList GetSavedChapterImages(Video video, IDirectoryService directoryService)
{
var path = GetChapterImagesPath(video);
if (!Directory.Exists(path))
{
- return new List();
+ return Array.Empty();
}
try
{
- return directoryService.GetFilePaths(path)
- .ToList();
+ return directoryService.GetFilePaths(path);
}
catch (IOException)
{
- return new List();
+ return Array.Empty();
}
}
@@ -227,7 +227,7 @@ namespace Emby.Server.Implementations.MediaEncoder
foreach (var image in deadImages)
{
- _logger.LogDebug("Deleting dead chapter image {path}", image);
+ _logger.LogDebug("Deleting dead chapter image {Path}", image);
try
{
@@ -235,7 +235,7 @@ namespace Emby.Server.Implementations.MediaEncoder
}
catch (IOException ex)
{
- _logger.LogError(ex, "Error deleting {path}.", image);
+ _logger.LogError(ex, "Error deleting {Path}.", image);
}
}
}
diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs
index 8a7420780c8..5766a9dc361 100644
--- a/Jellyfin.Server/Program.cs
+++ b/Jellyfin.Server/Program.cs
@@ -12,11 +12,14 @@ using System.Threading.Tasks;
using CommandLine;
using Emby.Drawing;
using Emby.Server.Implementations;
+using Emby.Server.Implementations.HttpServer;
using Emby.Server.Implementations.IO;
using Emby.Server.Implementations.Networking;
using Jellyfin.Drawing.Skia;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Drawing;
+using MediaBrowser.Controller.Extensions;
+using MediaBrowser.WebDashboard.Api;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
@@ -109,9 +112,10 @@ namespace Jellyfin.Server
// $JELLYFIN_LOG_DIR needs to be set for the logger configuration manager
Environment.SetEnvironmentVariable("JELLYFIN_LOG_DIR", appPaths.LogDirectoryPath);
- // Create an instance of the application configuration to use for application startup
await InitLoggingConfigFile(appPaths).ConfigureAwait(false);
- IConfiguration startupConfig = CreateAppConfiguration(appPaths);
+
+ // Create an instance of the application configuration to use for application startup
+ IConfiguration startupConfig = CreateAppConfiguration(options, appPaths);
// Initialize logging framework
InitializeLoggingFramework(startupConfig, appPaths);
@@ -180,15 +184,31 @@ namespace Jellyfin.Server
new ManagedFileSystem(_loggerFactory.CreateLogger(), appPaths),
GetImageEncoder(appPaths),
new NetworkManager(_loggerFactory.CreateLogger()));
+
try
{
+ // If hosting the web client, validate the client content path
+ if (startupConfig.HostWebClient())
+ {
+ string webContentPath = DashboardService.GetDashboardUIPath(startupConfig, appHost.ServerConfigurationManager);
+ if (!Directory.Exists(webContentPath) || Directory.GetFiles(webContentPath).Length == 0)
+ {
+ throw new InvalidOperationException(
+ "The server is expected to host the web client, but the provided content directory is either " +
+ $"invalid or empty: {webContentPath}. If you do not want to host the web client with the " +
+ "server, you may set the '--nowebclient' command line flag, or set" +
+ $"'{MediaBrowser.Controller.Extensions.ConfigurationExtensions.HostWebClientKey}=false' in your config settings.");
+ }
+ }
+
ServiceCollection serviceCollection = new ServiceCollection();
await appHost.InitAsync(serviceCollection, startupConfig).ConfigureAwait(false);
- var webHost = CreateWebHostBuilder(appHost, serviceCollection, appPaths).Build();
+ var webHost = CreateWebHostBuilder(appHost, serviceCollection, options, startupConfig, appPaths).Build();
- // A bit hacky to re-use service provider since ASP.NET doesn't allow a custom service collection.
+ // Re-use the web host service provider in the app host since ASP.NET doesn't allow a custom service collection.
appHost.ServiceProvider = webHost.Services;
+ appHost.InitializeServices();
appHost.FindParts();
Migrations.MigrationRunner.Run(appHost, _loggerFactory);
@@ -230,7 +250,12 @@ namespace Jellyfin.Server
}
}
- private static IWebHostBuilder CreateWebHostBuilder(ApplicationHost appHost, IServiceCollection serviceCollection, IApplicationPaths appPaths)
+ private static IWebHostBuilder CreateWebHostBuilder(
+ ApplicationHost appHost,
+ IServiceCollection serviceCollection,
+ StartupOptions commandLineOpts,
+ IConfiguration startupConfig,
+ IApplicationPaths appPaths)
{
return new WebHostBuilder()
.UseKestrel(options =>
@@ -270,9 +295,8 @@ namespace Jellyfin.Server
}
}
})
- .ConfigureAppConfiguration(config => config.ConfigureAppConfiguration(appPaths))
+ .ConfigureAppConfiguration(config => config.ConfigureAppConfiguration(commandLineOpts, appPaths, startupConfig))
.UseSerilog()
- .UseContentRoot(appHost.ContentRoot)
.ConfigureServices(services =>
{
// Merge the external ServiceCollection into ASP.NET DI
@@ -395,9 +419,8 @@ namespace Jellyfin.Server
// webDir
// IF --webdir
// ELSE IF $JELLYFIN_WEB_DIR
- // ELSE use /jellyfin-web
+ // ELSE /jellyfin-web
var webDir = options.WebDir;
-
if (string.IsNullOrEmpty(webDir))
{
webDir = Environment.GetEnvironmentVariable("JELLYFIN_WEB_DIR");
@@ -468,21 +491,33 @@ namespace Jellyfin.Server
await resource.CopyToAsync(dst).ConfigureAwait(false);
}
- private static IConfiguration CreateAppConfiguration(IApplicationPaths appPaths)
+ private static IConfiguration CreateAppConfiguration(StartupOptions commandLineOpts, IApplicationPaths appPaths)
{
return new ConfigurationBuilder()
- .ConfigureAppConfiguration(appPaths)
+ .ConfigureAppConfiguration(commandLineOpts, appPaths)
.Build();
}
- private static IConfigurationBuilder ConfigureAppConfiguration(this IConfigurationBuilder config, IApplicationPaths appPaths)
+ private static IConfigurationBuilder ConfigureAppConfiguration(
+ this IConfigurationBuilder config,
+ StartupOptions commandLineOpts,
+ IApplicationPaths appPaths,
+ IConfiguration? startupConfig = null)
{
+ // Use the swagger API page as the default redirect path if not hosting the web client
+ var inMemoryDefaultConfig = ConfigurationOptions.DefaultConfiguration;
+ if (startupConfig != null && !startupConfig.HostWebClient())
+ {
+ inMemoryDefaultConfig[HttpListenerHost.DefaultRedirectKey] = "swagger/index.html";
+ }
+
return config
.SetBasePath(appPaths.ConfigurationDirectoryPath)
- .AddInMemoryCollection(ConfigurationOptions.Configuration)
+ .AddInMemoryCollection(inMemoryDefaultConfig)
.AddJsonFile(LoggingConfigFileDefault, optional: false, reloadOnChange: true)
.AddJsonFile(LoggingConfigFileSystem, optional: true, reloadOnChange: true)
- .AddEnvironmentVariables("JELLYFIN_");
+ .AddEnvironmentVariables("JELLYFIN_")
+ .AddInMemoryCollection(commandLineOpts.ConvertToConfig());
}
///
diff --git a/Jellyfin.Server/Properties/launchSettings.json b/Jellyfin.Server/Properties/launchSettings.json
new file mode 100644
index 00000000000..53d9fe1656a
--- /dev/null
+++ b/Jellyfin.Server/Properties/launchSettings.json
@@ -0,0 +1,11 @@
+{
+ "profiles": {
+ "Jellyfin.Server": {
+ "commandName": "Project"
+ },
+ "Jellyfin.Server (nowebclient)": {
+ "commandName": "Project",
+ "commandLineArgs": "--nowebclient"
+ }
+ }
+}
diff --git a/Jellyfin.Server/StartupOptions.cs b/Jellyfin.Server/StartupOptions.cs
index 1fb1c5af8e1..c93577d3ea7 100644
--- a/Jellyfin.Server/StartupOptions.cs
+++ b/Jellyfin.Server/StartupOptions.cs
@@ -1,5 +1,8 @@
+using System.Collections.Generic;
+using System.Globalization;
using CommandLine;
using Emby.Server.Implementations;
+using MediaBrowser.Controller.Extensions;
namespace Jellyfin.Server
{
@@ -15,6 +18,12 @@ namespace Jellyfin.Server
[Option('d', "datadir", Required = false, HelpText = "Path to use for the data folder (database files, etc.).")]
public string? DataDir { get; set; }
+ ///
+ /// Gets or sets a value indicating whether the server should not host the web client.
+ ///
+ [Option("nowebclient", Required = false, HelpText = "Indicates that the web server should not host the web client.")]
+ public bool NoWebClient { get; set; }
+
///
/// Gets or sets the path to the web directory.
///
@@ -66,5 +75,21 @@ namespace Jellyfin.Server
///
[Option("restartargs", Required = false, HelpText = "Arguments for restart script.")]
public string? RestartArgs { get; set; }
+
+ ///
+ /// Gets the command line options as a dictionary that can be used in the .NET configuration system.
+ ///
+ /// The configuration dictionary.
+ public Dictionary ConvertToConfig()
+ {
+ var config = new Dictionary();
+
+ if (NoWebClient)
+ {
+ config.Add(ConfigurationExtensions.HostWebClientKey, bool.FalseString);
+ }
+
+ return config;
+ }
}
}
diff --git a/MediaBrowser.Common/Configuration/IApplicationPaths.cs b/MediaBrowser.Common/Configuration/IApplicationPaths.cs
index 5bdea7d8b30..870b90796cb 100644
--- a/MediaBrowser.Common/Configuration/IApplicationPaths.cs
+++ b/MediaBrowser.Common/Configuration/IApplicationPaths.cs
@@ -1,3 +1,5 @@
+using MediaBrowser.Model.Configuration;
+
namespace MediaBrowser.Common.Configuration
{
///
@@ -12,9 +14,12 @@ namespace MediaBrowser.Common.Configuration
string ProgramDataPath { get; }
///
- /// Gets the path to the web UI resources folder
+ /// Gets the path to the web UI resources folder.
///
- /// The web UI resources path.
+ ///
+ /// This value is not relevant if the server is configured to not host any static web content. Additionally,
+ /// the value for takes precedence over this one.
+ ///
string WebPath { get; }
///
diff --git a/MediaBrowser.Controller/Chapters/IChapterManager.cs b/MediaBrowser.Controller/Chapters/IChapterManager.cs
index d061898a141..f82e5b41a20 100644
--- a/MediaBrowser.Controller/Chapters/IChapterManager.cs
+++ b/MediaBrowser.Controller/Chapters/IChapterManager.cs
@@ -1,16 +1,17 @@
+using System;
using System.Collections.Generic;
using MediaBrowser.Model.Entities;
namespace MediaBrowser.Controller.Chapters
{
///
- /// Interface IChapterManager
+ /// Interface IChapterManager.
///
public interface IChapterManager
{
///
/// Saves the chapters.
///
- void SaveChapters(string itemId, List chapters);
+ void SaveChapters(Guid itemId, IReadOnlyList chapters);
}
}
diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs
index c72bd487e76..bb48605e553 100644
--- a/MediaBrowser.Controller/Entities/Folder.cs
+++ b/MediaBrowser.Controller/Entities/Folder.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -28,7 +30,6 @@ namespace MediaBrowser.Controller.Entities
///
public class Folder : BaseItem
{
- public static IUserManager UserManager { get; set; }
public static IUserViewManager UserViewManager { get; set; }
///
@@ -620,7 +621,6 @@ namespace MediaBrowser.Controller.Entities
{
EnableImages = false
}
-
}).TotalRecordCount;
}
@@ -1713,7 +1713,6 @@ namespace MediaBrowser.Controller.Entities
{
EnableImages = false
}
-
});
double unplayedCount = unplayedQueryResult.TotalRecordCount;
diff --git a/MediaBrowser.Controller/Entities/IItemByName.cs b/MediaBrowser.Controller/Entities/IItemByName.cs
index 89b5dfee36f..8ef5c8d9613 100644
--- a/MediaBrowser.Controller/Entities/IItemByName.cs
+++ b/MediaBrowser.Controller/Entities/IItemByName.cs
@@ -3,7 +3,7 @@ using System.Collections.Generic;
namespace MediaBrowser.Controller.Entities
{
///
- /// Marker interface
+ /// Marker interface.
///
public interface IItemByName
{
diff --git a/MediaBrowser.Controller/Extensions/ConfigurationExtensions.cs b/MediaBrowser.Controller/Extensions/ConfigurationExtensions.cs
index 48316499a4d..c0043c0efab 100644
--- a/MediaBrowser.Controller/Extensions/ConfigurationExtensions.cs
+++ b/MediaBrowser.Controller/Extensions/ConfigurationExtensions.cs
@@ -1,3 +1,4 @@
+using System;
using Microsoft.Extensions.Configuration;
namespace MediaBrowser.Controller.Extensions
@@ -7,6 +8,11 @@ namespace MediaBrowser.Controller.Extensions
///
public static class ConfigurationExtensions
{
+ ///
+ /// The key for a setting that indicates whether the application should host web client content.
+ ///
+ public const string HostWebClientKey = "hostwebclient";
+
///
/// The key for the FFmpeg probe size option.
///
@@ -22,6 +28,15 @@ namespace MediaBrowser.Controller.Extensions
///
public const string PlaylistsAllowDuplicatesKey = "playlists:allowDuplicates";
+ ///
+ /// Gets a value indicating whether the application should host static web content from the .
+ ///
+ /// The configuration to retrieve the value from.
+ /// The parsed config value.
+ /// The config value is not a valid bool string. See .
+ public static bool HostWebClient(this IConfiguration configuration)
+ => configuration.GetValue(HostWebClientKey);
+
///
/// Gets the FFmpeg probe size from the .
///
diff --git a/MediaBrowser.Controller/IServerApplicationHost.cs b/MediaBrowser.Controller/IServerApplicationHost.cs
index 25f0905eb83..608ffc61c29 100644
--- a/MediaBrowser.Controller/IServerApplicationHost.cs
+++ b/MediaBrowser.Controller/IServerApplicationHost.cs
@@ -82,6 +82,11 @@ namespace MediaBrowser.Controller
/// The local API URL.
string GetLocalApiUrl(IPAddress address);
+ ///
+ /// Open a URL in an external browser window.
+ ///
+ /// The URL to open.
+ /// is false.
void LaunchUrl(string url);
void EnableLoopback(string appName);
diff --git a/MediaBrowser.Controller/Library/IMediaSourceProvider.cs b/MediaBrowser.Controller/Library/IMediaSourceProvider.cs
index ec7798551b8..5bf4acebb4b 100644
--- a/MediaBrowser.Controller/Library/IMediaSourceProvider.cs
+++ b/MediaBrowser.Controller/Library/IMediaSourceProvider.cs
@@ -1,5 +1,4 @@
#pragma warning disable CS1591
-#pragma warning disable SA1600
using System.Collections.Generic;
using System.Threading;
diff --git a/MediaBrowser.Controller/MediaEncoding/IEncodingManager.cs b/MediaBrowser.Controller/MediaEncoding/IEncodingManager.cs
index e560999e8f1..15a2580afd8 100644
--- a/MediaBrowser.Controller/MediaEncoding/IEncodingManager.cs
+++ b/MediaBrowser.Controller/MediaEncoding/IEncodingManager.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
@@ -12,6 +14,6 @@ namespace MediaBrowser.Controller.MediaEncoding
///
/// Refreshes the chapter images.
///
- Task RefreshChapterImages(Video video, IDirectoryService directoryService, List chapters, bool extractImages, bool saveChapters, CancellationToken cancellationToken);
+ Task RefreshChapterImages(Video video, IDirectoryService directoryService, IReadOnlyList chapters, bool extractImages, bool saveChapters, CancellationToken cancellationToken);
}
}
diff --git a/MediaBrowser.Controller/Persistence/IItemRepository.cs b/MediaBrowser.Controller/Persistence/IItemRepository.cs
index 5a5b7f58f0a..eb5a8ded84a 100644
--- a/MediaBrowser.Controller/Persistence/IItemRepository.cs
+++ b/MediaBrowser.Controller/Persistence/IItemRepository.cs
@@ -61,7 +61,7 @@ namespace MediaBrowser.Controller.Persistence
///
/// Saves the chapters.
///
- void SaveChapters(Guid id, List chapters);
+ void SaveChapters(Guid id, IReadOnlyList chapters);
///
/// Gets the media streams.
diff --git a/MediaBrowser.Controller/Providers/DirectoryService.cs b/MediaBrowser.Controller/Providers/DirectoryService.cs
index ca470872b19..b7640c20503 100644
--- a/MediaBrowser.Controller/Providers/DirectoryService.cs
+++ b/MediaBrowser.Controller/Providers/DirectoryService.cs
@@ -66,12 +66,10 @@ namespace MediaBrowser.Controller.Providers
return file;
}
- public List GetFilePaths(string path)
- {
- return GetFilePaths(path, false);
- }
+ public IReadOnlyList GetFilePaths(string path)
+ => GetFilePaths(path, false);
- public List GetFilePaths(string path, bool clearCache)
+ public IReadOnlyList GetFilePaths(string path, bool clearCache)
{
if (clearCache || !_filePathCache.TryGetValue(path, out List result))
{
diff --git a/MediaBrowser.Controller/Providers/IDirectoryService.cs b/MediaBrowser.Controller/Providers/IDirectoryService.cs
index b304fc33562..949a17740bb 100644
--- a/MediaBrowser.Controller/Providers/IDirectoryService.cs
+++ b/MediaBrowser.Controller/Providers/IDirectoryService.cs
@@ -11,8 +11,8 @@ namespace MediaBrowser.Controller.Providers
FileSystemMetadata GetFile(string path);
- List GetFilePaths(string path);
+ IReadOnlyList GetFilePaths(string path);
- List GetFilePaths(string path, bool clearCache);
+ IReadOnlyList GetFilePaths(string path, bool clearCache);
}
}
diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
index 8c46af7dbc6..f3f2b86eee2 100644
--- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
@@ -429,7 +429,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
}
}
- return new ProbeResultNormalizer(_logger, _fileSystem, _localization).GetMediaInfo(result, videoType, isAudio, primaryPath, protocol);
+ return new ProbeResultNormalizer(_logger, _localization).GetMediaInfo(result, videoType, isAudio, primaryPath, protocol);
}
}
diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
index ded8dffd644..b24d97f4ef2 100644
--- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
+++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
@@ -9,7 +9,6 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Globalization;
-using MediaBrowser.Model.IO;
using MediaBrowser.Model.MediaInfo;
using Microsoft.Extensions.Logging;
@@ -19,13 +18,11 @@ namespace MediaBrowser.MediaEncoding.Probing
{
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
private readonly ILogger _logger;
- private readonly IFileSystem _fileSystem;
private readonly ILocalizationManager _localization;
- public ProbeResultNormalizer(ILogger logger, IFileSystem fileSystem, ILocalizationManager localization)
+ public ProbeResultNormalizer(ILogger logger, ILocalizationManager localization)
{
_logger = logger;
- _fileSystem = fileSystem;
_localization = localization;
}
@@ -40,7 +37,7 @@ namespace MediaBrowser.MediaEncoding.Probing
FFProbeHelpers.NormalizeFFProbeResult(data);
SetSize(data, info);
- var internalStreams = data.Streams ?? new MediaStreamInfo[] { };
+ var internalStreams = data.Streams ?? Array.Empty();
info.MediaStreams = internalStreams.Select(s => GetMediaStream(isAudio, s, data.Format))
.Where(i => i != null)
diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs
index dfb5631807a..3107ec24262 100644
--- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs
+++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs
@@ -148,9 +148,9 @@ namespace MediaBrowser.Model.Configuration
public bool EnableDashboardResponseCaching { get; set; }
///
- /// Allows the dashboard to be served from a custom path.
+ /// Gets or sets a custom path to serve the dashboard from.
///
- /// The dashboard source path.
+ /// The dashboard source path, or null if the default path should be used.
public string DashboardSourcePath { get; set; }
///
diff --git a/MediaBrowser.Providers/Chapters/ChapterManager.cs b/MediaBrowser.Providers/Chapters/ChapterManager.cs
index 45e87f137e1..3cbfe7d4d7a 100644
--- a/MediaBrowser.Providers/Chapters/ChapterManager.cs
+++ b/MediaBrowser.Providers/Chapters/ChapterManager.cs
@@ -1,36 +1,26 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using MediaBrowser.Controller.Chapters;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Model.Entities;
-using Microsoft.Extensions.Logging;
namespace MediaBrowser.Providers.Chapters
{
public class ChapterManager : IChapterManager
{
- private readonly ILibraryManager _libraryManager;
- private readonly ILogger _logger;
- private readonly IServerConfigurationManager _config;
private readonly IItemRepository _itemRepo;
- public ChapterManager(
- ILibraryManager libraryManager,
- ILoggerFactory loggerFactory,
- IServerConfigurationManager config,
- IItemRepository itemRepo)
+ public ChapterManager(IItemRepository itemRepo)
{
- _libraryManager = libraryManager;
- _logger = loggerFactory.CreateLogger(nameof(ChapterManager));
- _config = config;
_itemRepo = itemRepo;
}
- public void SaveChapters(string itemId, List chapters)
+ ///
+ public void SaveChapters(Guid itemId, IReadOnlyList chapters)
{
- _itemRepo.SaveChapters(new Guid(itemId), chapters);
+ _itemRepo.SaveChapters(itemId, chapters);
}
}
}
diff --git a/MediaBrowser.Providers/Manager/ProviderManager.cs b/MediaBrowser.Providers/Manager/ProviderManager.cs
index e7b349f67bc..7125f34c554 100644
--- a/MediaBrowser.Providers/Manager/ProviderManager.cs
+++ b/MediaBrowser.Providers/Manager/ProviderManager.cs
@@ -1,5 +1,9 @@
+#pragma warning disable CS1591
+
using System;
+using System.Collections.Concurrent;
using System.Collections.Generic;
+using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading;
@@ -897,7 +901,10 @@ namespace MediaBrowser.Providers.Manager
return new ExternalUrl
{
Name = i.Name,
- Url = string.Format(i.UrlFormatString, value)
+ Url = string.Format(
+ CultureInfo.InvariantCulture,
+ i.UrlFormatString,
+ value)
};
}).Where(i => i != null).Concat(item.GetRelatedUrls());
@@ -911,11 +918,10 @@ namespace MediaBrowser.Providers.Manager
Name = i.Name,
Key = i.Key,
UrlFormatString = i.UrlFormatString
-
});
}
- private Dictionary _activeRefreshes = new Dictionary();
+ private ConcurrentDictionary _activeRefreshes = new ConcurrentDictionary();
public Dictionary GetRefreshQueue()
{
@@ -927,66 +933,54 @@ namespace MediaBrowser.Providers.Manager
{
dict[item.Item1] = item.Item1;
}
+
return dict;
}
}
public void OnRefreshStart(BaseItem item)
{
- //_logger.LogInformation("OnRefreshStart {0}", item.Id.ToString("N", CultureInfo.InvariantCulture));
- var id = item.Id;
-
- lock (_activeRefreshes)
- {
- _activeRefreshes[id] = 0;
- }
-
+ _logger.LogInformation("OnRefreshStart {0}", item.Id.ToString("N", CultureInfo.InvariantCulture));
+ _activeRefreshes[item.Id] = 0;
RefreshStarted?.Invoke(this, new GenericEventArgs(item));
}
public void OnRefreshComplete(BaseItem item)
{
- //_logger.LogInformation("OnRefreshComplete {0}", item.Id.ToString("N", CultureInfo.InvariantCulture));
- lock (_activeRefreshes)
- {
- _activeRefreshes.Remove(item.Id);
- }
+ _logger.LogInformation("OnRefreshComplete {0}", item.Id.ToString("N", CultureInfo.InvariantCulture));
+
+ _activeRefreshes.Remove(item.Id, out _);
RefreshCompleted?.Invoke(this, new GenericEventArgs(item));
}
public double? GetRefreshProgress(Guid id)
{
- lock (_activeRefreshes)
+ if (_activeRefreshes.TryGetValue(id, out double value))
{
- if (_activeRefreshes.TryGetValue(id, out double value))
- {
- return value;
- }
-
- return null;
+ return value;
}
+
+ return null;
}
public void OnRefreshProgress(BaseItem item, double progress)
{
- //_logger.LogInformation("OnRefreshProgress {0} {1}", item.Id.ToString("N", CultureInfo.InvariantCulture), progress);
var id = item.Id;
+ _logger.LogInformation("OnRefreshProgress {0} {1}", id.ToString("N", CultureInfo.InvariantCulture), progress);
- lock (_activeRefreshes)
- {
- if (_activeRefreshes.ContainsKey(id))
- {
- _activeRefreshes[id] = progress;
+ // TODO: Need to hunt down the conditions for this happening
+ _activeRefreshes.AddOrUpdate(
+ id,
+ (_) => throw new Exception(
+ string.Format(
+ CultureInfo.InvariantCulture,
+ "Cannot update refresh progress of item '{0}' ({1}) because a refresh for this item is not running",
+ item.GetType().Name,
+ item.Id.ToString("N", CultureInfo.InvariantCulture))),
+ (_, __) => progress);
- RefreshProgress?.Invoke(this, new GenericEventArgs>(new Tuple(item, progress)));
- }
- else
- {
- // TODO: Need to hunt down the conditions for this happening
- //throw new Exception(string.Format("Refresh for item {0} {1} is not in progress", item.GetType().Name, item.Id.ToString("N", CultureInfo.InvariantCulture)));
- }
- }
+ RefreshProgress?.Invoke(this, new GenericEventArgs>(new Tuple(item, progress)));
}
private readonly SimplePriorityQueue> _refreshQueue =
@@ -1040,10 +1034,9 @@ namespace MediaBrowser.Providers.Manager
// Try to throttle this a little bit.
await Task.Delay(100).ConfigureAwait(false);
- var artist = item as MusicArtist;
- var task = artist == null
- ? RefreshItem(item, refreshItem.Item2, cancellationToken)
- : RefreshArtist(artist, refreshItem.Item2, cancellationToken);
+ var task = item is MusicArtist artist
+ ? RefreshArtist(artist, refreshItem.Item2, cancellationToken)
+ : RefreshItem(item, refreshItem.Item2, cancellationToken);
await task.ConfigureAwait(false);
}
@@ -1125,8 +1118,7 @@ namespace MediaBrowser.Providers.Manager
}
}
- public Task RefreshFullItem(BaseItem item, MetadataRefreshOptions options,
- CancellationToken cancellationToken)
+ public Task RefreshFullItem(BaseItem item, MetadataRefreshOptions options, CancellationToken cancellationToken)
{
return RefreshItem(item, options, cancellationToken);
}
diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj
index 9a366364db3..330a4d1e534 100644
--- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj
+++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj
@@ -24,6 +24,18 @@
true
+
+
+
+
+
+
+
+
+
+ ../jellyfin.ruleset
+
+
diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs
index db6e49634cb..3999025d8e0 100644
--- a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs
+++ b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs
@@ -194,7 +194,19 @@ namespace MediaBrowser.Providers.MediaInfo
FetchShortcutInfo(item);
}
- var prober = new FFProbeVideoInfo(_logger, _mediaSourceManager, _isoManager, _mediaEncoder, _itemRepo, _blurayExaminer, _localization, _appPaths, _json, _encodingManager, _fileSystem, _config, _subtitleManager, _chapterManager, _libraryManager);
+ var prober = new FFProbeVideoInfo(
+ _logger,
+ _mediaSourceManager,
+ _mediaEncoder,
+ _itemRepo,
+ _blurayExaminer,
+ _localization,
+ _encodingManager,
+ _fileSystem,
+ _config,
+ _subtitleManager,
+ _chapterManager,
+ _libraryManager);
return prober.ProbeVideo(item, options, cancellationToken);
}
diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs
index 2b178d4d4e6..d2e98a5a94c 100644
--- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs
+++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -25,7 +27,6 @@ using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.MediaInfo;
using MediaBrowser.Model.Providers;
-using MediaBrowser.Model.Serialization;
using Microsoft.Extensions.Logging;
namespace MediaBrowser.Providers.MediaInfo
@@ -33,13 +34,10 @@ namespace MediaBrowser.Providers.MediaInfo
public class FFProbeVideoInfo
{
private readonly ILogger _logger;
- private readonly IIsoManager _isoManager;
private readonly IMediaEncoder _mediaEncoder;
private readonly IItemRepository _itemRepo;
private readonly IBlurayExaminer _blurayExaminer;
private readonly ILocalizationManager _localization;
- private readonly IApplicationPaths _appPaths;
- private readonly IJsonSerializer _json;
private readonly IEncodingManager _encodingManager;
private readonly IFileSystem _fileSystem;
private readonly IServerConfigurationManager _config;
@@ -48,16 +46,27 @@ namespace MediaBrowser.Providers.MediaInfo
private readonly ILibraryManager _libraryManager;
private readonly IMediaSourceManager _mediaSourceManager;
- public FFProbeVideoInfo(ILogger logger, IMediaSourceManager mediaSourceManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IItemRepository itemRepo, IBlurayExaminer blurayExaminer, ILocalizationManager localization, IApplicationPaths appPaths, IJsonSerializer json, IEncodingManager encodingManager, IFileSystem fileSystem, IServerConfigurationManager config, ISubtitleManager subtitleManager, IChapterManager chapterManager, ILibraryManager libraryManager)
+ private readonly long _dummyChapterDuration = TimeSpan.FromMinutes(5).Ticks;
+
+ public FFProbeVideoInfo(
+ ILogger logger,
+ IMediaSourceManager mediaSourceManager,
+ IMediaEncoder mediaEncoder,
+ IItemRepository itemRepo,
+ IBlurayExaminer blurayExaminer,
+ ILocalizationManager localization,
+ IEncodingManager encodingManager,
+ IFileSystem fileSystem,
+ IServerConfigurationManager config,
+ ISubtitleManager subtitleManager,
+ IChapterManager chapterManager,
+ ILibraryManager libraryManager)
{
_logger = logger;
- _isoManager = isoManager;
_mediaEncoder = mediaEncoder;
_itemRepo = itemRepo;
_blurayExaminer = blurayExaminer;
_localization = localization;
- _appPaths = appPaths;
- _json = json;
_encodingManager = encodingManager;
_fileSystem = fileSystem;
_config = config;
@@ -159,7 +168,7 @@ namespace MediaBrowser.Providers.MediaInfo
{
List mediaStreams;
IReadOnlyList mediaAttachments;
- List chapters;
+ ChapterInfo[] chapters;
if (mediaInfo != null)
{
@@ -177,6 +186,7 @@ namespace MediaBrowser.Providers.MediaInfo
{
video.RunTimeTicks = mediaInfo.RunTimeTicks;
}
+
video.Size = mediaInfo.Size;
if (video.VideoType == VideoType.VideoFile)
@@ -189,19 +199,20 @@ namespace MediaBrowser.Providers.MediaInfo
{
video.Container = null;
}
+
video.Container = mediaInfo.Container;
- chapters = mediaInfo.Chapters == null ? new List() : mediaInfo.Chapters.ToList();
+ chapters = mediaInfo.Chapters == null ? Array.Empty() : mediaInfo.Chapters;
if (blurayInfo != null)
{
- FetchBdInfo(video, chapters, mediaStreams, blurayInfo);
+ FetchBdInfo(video, ref chapters, mediaStreams, blurayInfo);
}
}
else
{
mediaStreams = new List();
mediaAttachments = Array.Empty();
- chapters = new List();
+ chapters = Array.Empty();
}
await AddExternalSubtitles(video, mediaStreams, options, cancellationToken).ConfigureAwait(false);
@@ -231,9 +242,9 @@ namespace MediaBrowser.Providers.MediaInfo
if (options.MetadataRefreshMode == MetadataRefreshMode.FullRefresh ||
options.MetadataRefreshMode == MetadataRefreshMode.Default)
{
- if (chapters.Count == 0 && mediaStreams.Any(i => i.Type == MediaStreamType.Video))
+ if (chapters.Length == 0 && mediaStreams.Any(i => i.Type == MediaStreamType.Video))
{
- AddDummyChapters(video, chapters);
+ chapters = CreateDummyChapters(video);
}
NormalizeChapterNames(chapters);
@@ -246,28 +257,29 @@ namespace MediaBrowser.Providers.MediaInfo
await _encodingManager.RefreshChapterImages(video, options.DirectoryService, chapters, extractDuringScan, false, cancellationToken).ConfigureAwait(false);
- _chapterManager.SaveChapters(video.Id.ToString(), chapters);
+ _chapterManager.SaveChapters(video.Id, chapters);
}
}
- private void NormalizeChapterNames(List chapters)
+ private void NormalizeChapterNames(ChapterInfo[] chapters)
{
- var index = 1;
-
- foreach (var chapter in chapters)
+ for (int i = 0; i < chapters.Length; i++)
{
+ string name = chapters[i].Name;
// Check if the name is empty and/or if the name is a time
// Some ripping programs do that.
- if (string.IsNullOrWhiteSpace(chapter.Name) ||
- TimeSpan.TryParse(chapter.Name, out var time))
+ if (string.IsNullOrWhiteSpace(name) ||
+ TimeSpan.TryParse(name, out _))
{
- chapter.Name = string.Format(_localization.GetLocalizedString("ChapterNameValue"), index.ToString(CultureInfo.InvariantCulture));
+ chapters[i].Name = string.Format(
+ CultureInfo.InvariantCulture,
+ _localization.GetLocalizedString("ChapterNameValue"),
+ (i + 1).ToString(CultureInfo.InvariantCulture));
}
- index++;
}
}
- private void FetchBdInfo(BaseItem item, List chapters, List mediaStreams, BlurayDiscInfo blurayInfo)
+ private void FetchBdInfo(BaseItem item, ref ChapterInfo[] chapters, List mediaStreams, BlurayDiscInfo blurayInfo)
{
var video = (Video)item;
@@ -301,13 +313,15 @@ namespace MediaBrowser.Providers.MediaInfo
if (blurayInfo.Chapters != null)
{
- chapters.Clear();
-
- chapters.AddRange(blurayInfo.Chapters.Select(c => new ChapterInfo
+ double[] brChapter = blurayInfo.Chapters;
+ chapters = new ChapterInfo[brChapter.Length];
+ for (int i = 0; i < brChapter.Length; i++)
{
- StartPositionTicks = TimeSpan.FromSeconds(c).Ticks
-
- }));
+ chapters[i] = new ChapterInfo
+ {
+ StartPositionTicks = TimeSpan.FromSeconds(brChapter[i]).Ticks
+ };
+ }
}
videoStream = mediaStreams.FirstOrDefault(s => s.Type == MediaStreamType.Video);
@@ -495,17 +509,17 @@ namespace MediaBrowser.Providers.MediaInfo
var libraryOptions = _libraryManager.GetLibraryOptions(video);
string[] subtitleDownloadLanguages;
- bool SkipIfEmbeddedSubtitlesPresent;
- bool SkipIfAudioTrackMatches;
- bool RequirePerfectMatch;
+ bool skipIfEmbeddedSubtitlesPresent;
+ bool skipIfAudioTrackMatches;
+ bool requirePerfectMatch;
bool enabled;
if (libraryOptions.SubtitleDownloadLanguages == null)
{
subtitleDownloadLanguages = subtitleOptions.DownloadLanguages;
- SkipIfEmbeddedSubtitlesPresent = subtitleOptions.SkipIfEmbeddedSubtitlesPresent;
- SkipIfAudioTrackMatches = subtitleOptions.SkipIfAudioTrackMatches;
- RequirePerfectMatch = subtitleOptions.RequirePerfectMatch;
+ skipIfEmbeddedSubtitlesPresent = subtitleOptions.SkipIfEmbeddedSubtitlesPresent;
+ skipIfAudioTrackMatches = subtitleOptions.SkipIfAudioTrackMatches;
+ requirePerfectMatch = subtitleOptions.RequirePerfectMatch;
enabled = (subtitleOptions.DownloadEpisodeSubtitles &&
video is Episode) ||
(subtitleOptions.DownloadMovieSubtitles &&
@@ -514,9 +528,9 @@ namespace MediaBrowser.Providers.MediaInfo
else
{
subtitleDownloadLanguages = libraryOptions.SubtitleDownloadLanguages;
- SkipIfEmbeddedSubtitlesPresent = libraryOptions.SkipSubtitlesIfEmbeddedSubtitlesPresent;
- SkipIfAudioTrackMatches = libraryOptions.SkipSubtitlesIfAudioTrackMatches;
- RequirePerfectMatch = libraryOptions.RequirePerfectSubtitleMatch;
+ skipIfEmbeddedSubtitlesPresent = libraryOptions.SkipSubtitlesIfEmbeddedSubtitlesPresent;
+ skipIfAudioTrackMatches = libraryOptions.SkipSubtitlesIfAudioTrackMatches;
+ requirePerfectMatch = libraryOptions.RequirePerfectSubtitleMatch;
enabled = true;
}
@@ -526,9 +540,9 @@ namespace MediaBrowser.Providers.MediaInfo
_subtitleManager)
.DownloadSubtitles(video,
currentStreams.Concat(externalSubtitleStreams).ToList(),
- SkipIfEmbeddedSubtitlesPresent,
- SkipIfAudioTrackMatches,
- RequirePerfectMatch,
+ skipIfEmbeddedSubtitlesPresent,
+ skipIfAudioTrackMatches,
+ requirePerfectMatch,
subtitleDownloadLanguages,
libraryOptions.DisabledSubtitleFetchers,
libraryOptions.SubtitleFetcherOrder,
@@ -547,43 +561,45 @@ namespace MediaBrowser.Providers.MediaInfo
}
///
- /// The dummy chapter duration
- ///
- private readonly long _dummyChapterDuration = TimeSpan.FromMinutes(5).Ticks;
-
- ///
- /// Adds the dummy chapters.
+ /// Creates dummy chapters.
///
/// The video.
- /// The chapters.
- private void AddDummyChapters(Video video, List chapters)
+ /// An array of dummy chapters.
+ private ChapterInfo[] CreateDummyChapters(Video video)
{
var runtime = video.RunTimeTicks ?? 0;
if (runtime < 0)
{
- throw new ArgumentException(string.Format("{0} has invalid runtime of {1}", video.Name, runtime));
+ throw new ArgumentException(
+ string.Format(
+ CultureInfo.InvariantCulture,
+ "{0} has invalid runtime of {1}",
+ video.Name,
+ runtime));
}
if (runtime < _dummyChapterDuration)
{
- return;
+ return Array.Empty();
}
- long currentChapterTicks = 0;
- var index = 1;
-
// Limit to 100 chapters just in case there's some incorrect metadata here
- while (currentChapterTicks < runtime && index < 100)
+ int chapterCount = (int)Math.Min(runtime / _dummyChapterDuration, 100);
+ var chapters = new ChapterInfo[chapterCount];
+
+ long currentChapterTicks = 0;
+ for (int i = 0; i < chapterCount; i++)
{
- chapters.Add(new ChapterInfo
+ chapters[i] = new ChapterInfo
{
StartPositionTicks = currentChapterTicks
- });
+ };
- index++;
currentChapterTicks += _dummyChapterDuration;
}
+
+ return chapters;
}
private string[] FetchFromDvdLib(Video item)
diff --git a/MediaBrowser.Providers/Tmdb/Movies/TmdbMovieProvider.cs b/MediaBrowser.Providers/Tmdb/Movies/TmdbMovieProvider.cs
index fbb87d25dd8..e2fd5b9e308 100644
--- a/MediaBrowser.Providers/Tmdb/Movies/TmdbMovieProvider.cs
+++ b/MediaBrowser.Providers/Tmdb/Movies/TmdbMovieProvider.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Net;
+using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common;
@@ -36,7 +37,6 @@ namespace MediaBrowser.Providers.Tmdb.Movies
private readonly IFileSystem _fileSystem;
private readonly IServerConfigurationManager _configurationManager;
private readonly ILogger _logger;
- private readonly ILocalizationManager _localization;
private readonly ILibraryManager _libraryManager;
private readonly IApplicationHost _appHost;
@@ -48,7 +48,6 @@ namespace MediaBrowser.Providers.Tmdb.Movies
IFileSystem fileSystem,
IServerConfigurationManager configurationManager,
ILogger logger,
- ILocalizationManager localization,
ILibraryManager libraryManager,
IApplicationHost appHost)
{
@@ -57,7 +56,6 @@ namespace MediaBrowser.Providers.Tmdb.Movies
_fileSystem = fileSystem;
_configurationManager = configurationManager;
_logger = logger;
- _localization = localization;
_libraryManager = libraryManager;
_appHost = appHost;
Current = this;
@@ -409,15 +407,15 @@ namespace MediaBrowser.Providers.Tmdb.Movies
private static long _lastRequestTicks;
// The limit is 40 requests per 10 seconds
- private static int requestIntervalMs = 300;
+ private const int RequestIntervalMs = 300;
///
/// Gets the movie db response.
///
internal async Task GetMovieDbResponse(HttpRequestOptions options)
{
- var delayTicks = (requestIntervalMs * 10000) - (DateTime.UtcNow.Ticks - _lastRequestTicks);
- var delayMs = Math.Min(delayTicks / 10000, requestIntervalMs);
+ var delayTicks = (RequestIntervalMs * 10000) - (DateTime.UtcNow.Ticks - _lastRequestTicks);
+ var delayMs = Math.Min(delayTicks / 10000, RequestIntervalMs);
if (delayMs > 0)
{
@@ -430,11 +428,13 @@ namespace MediaBrowser.Providers.Tmdb.Movies
options.BufferContent = true;
options.UserAgent = _appHost.ApplicationUserAgent;
- return await _httpClient.SendAsync(options, "GET").ConfigureAwait(false);
+ return await _httpClient.SendAsync(options, HttpMethod.Get).ConfigureAwait(false);
}
+ ///
public int Order => 1;
+ ///
public Task GetImageResponse(string url, CancellationToken cancellationToken)
{
return _httpClient.GetResponse(new HttpRequestOptions
diff --git a/MediaBrowser.WebDashboard/Api/DashboardService.cs b/MediaBrowser.WebDashboard/Api/DashboardService.cs
index 99d8d044f3c..133a35527d7 100644
--- a/MediaBrowser.WebDashboard/Api/DashboardService.cs
+++ b/MediaBrowser.WebDashboard/Api/DashboardService.cs
@@ -12,12 +12,14 @@ using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Plugins;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Extensions;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Plugins;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Net;
using MediaBrowser.Model.Plugins;
using MediaBrowser.Model.Services;
+using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
namespace MediaBrowser.WebDashboard.Api
@@ -102,6 +104,7 @@ namespace MediaBrowser.WebDashboard.Api
/// The HTTP result factory.
private readonly IHttpResultFactory _resultFactory;
private readonly IServerApplicationHost _appHost;
+ private readonly IConfiguration _appConfig;
private readonly IServerConfigurationManager _serverConfigurationManager;
private readonly IFileSystem _fileSystem;
private readonly IResourceFileManager _resourceFileManager;
@@ -111,6 +114,7 @@ namespace MediaBrowser.WebDashboard.Api
///
/// The logger.
/// The application host.
+ /// The application configuration.
/// The resource file manager.
/// The server configuration manager.
/// The file system.
@@ -118,6 +122,7 @@ namespace MediaBrowser.WebDashboard.Api
public DashboardService(
ILogger logger,
IServerApplicationHost appHost,
+ IConfiguration appConfig,
IResourceFileManager resourceFileManager,
IServerConfigurationManager serverConfigurationManager,
IFileSystem fileSystem,
@@ -125,6 +130,7 @@ namespace MediaBrowser.WebDashboard.Api
{
_logger = logger;
_appHost = appHost;
+ _appConfig = appConfig;
_resourceFileManager = resourceFileManager;
_serverConfigurationManager = serverConfigurationManager;
_fileSystem = fileSystem;
@@ -138,20 +144,30 @@ namespace MediaBrowser.WebDashboard.Api
public IRequest Request { get; set; }
///
- /// Gets the path for the web interface.
+ /// Gets the path of the directory containing the static web interface content, or null if the server is not
+ /// hosting the web client.
///
- /// The path for the web interface.
- public string DashboardUIPath
- {
- get
- {
- if (!string.IsNullOrEmpty(_serverConfigurationManager.Configuration.DashboardSourcePath))
- {
- return _serverConfigurationManager.Configuration.DashboardSourcePath;
- }
+ public string DashboardUIPath => GetDashboardUIPath(_appConfig, _serverConfigurationManager);
- return _serverConfigurationManager.ApplicationPaths.WebPath;
+ ///
+ /// Gets the path of the directory containing the static web interface content.
+ ///
+ /// The app configuration.
+ /// The server configuration manager.
+ /// The directory path, or null if the server is not hosting the web client.
+ public static string GetDashboardUIPath(IConfiguration appConfig, IServerConfigurationManager serverConfigManager)
+ {
+ if (!appConfig.HostWebClient())
+ {
+ return null;
}
+
+ if (!string.IsNullOrEmpty(serverConfigManager.Configuration.DashboardSourcePath))
+ {
+ return serverConfigManager.Configuration.DashboardSourcePath;
+ }
+
+ return serverConfigManager.ApplicationPaths.WebPath;
}
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
@@ -209,7 +225,7 @@ namespace MediaBrowser.WebDashboard.Api
return _resultFactory.GetStaticResult(Request, plugin.Version.ToString().GetMD5(), null, null, MimeTypes.GetMimeType("page.html"), () => Task.FromResult(stream));
}
- return _resultFactory.GetStaticResult(Request, plugin.Version.ToString().GetMD5(), null, null, MimeTypes.GetMimeType("page.html"), () => GetPackageCreator(DashboardUIPath).ModifyHtml("dummy.html", stream, null, _appHost.ApplicationVersionString, null));
+ return _resultFactory.GetStaticResult(Request, plugin.Version.ToString().GetMD5(), null, null, MimeTypes.GetMimeType("page.html"), () => PackageCreator.ModifyHtml(false, stream, null, _appHost.ApplicationVersionString, null));
}
throw new ResourceNotFoundException();
@@ -307,6 +323,11 @@ namespace MediaBrowser.WebDashboard.Api
/// System.Object.
public async Task