diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index e14636a577..4f36249655 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -1,41 +1,132 @@ # Jellyfin Contributors - - [JoshuaBoniface](https://github.com/joshuaboniface) - - [nvllsvm](https://github.com/nvllsvm) - - [JustAMan](https://github.com/JustAMan) + - [97carmine](https://github.com/97carmine) + - [Abbe98](https://github.com/Abbe98) + - [agrenott](https://github.com/agrenott) + - [AndreCarvalho](https://github.com/AndreCarvalho) + - [anthonylavado](https://github.com/anthonylavado) + - [Artiume](https://github.com/Artiume) + - [AThomsen](https://github.com/AThomsen) + - [bilde2910](https://github.com/bilde2910) + - [bfayers](https://github.com/bfayers) + - [BnMcG](https://github.com/BnMcG) + - [Bond-009](https://github.com/Bond-009) + - [brianjmurrell](https://github.com/brianjmurrell) + - [bugfixin](https://github.com/bugfixin) + - [chaosinnovator](https://github.com/chaosinnovator) + - [ckcr4lyf](https://github.com/ckcr4lyf) + - [crankdoofus](https://github.com/crankdoofus) + - [crobibero](https://github.com/crobibero) + - [cromefire](https://github.com/cromefire) + - [cryptobank](https://github.com/cryptobank) + - [cvium](https://github.com/cvium) + - [dannymichel](https://github.com/dannymichel) + - [DaveChild](https://github.com/DaveChild) - [dcrdev](https://github.com/dcrdev) + - [dhartung](https://github.com/dhartung) + - [dinki](https://github.com/dinki) + - [dkanada](https://github.com/dkanada) + - [dlahoti](https://github.com/dlahoti) + - [dmitrylyzo](https://github.com/dmitrylyzo) + - [DMouse10462](https://github.com/DMouse10462) + - [DrPandemic](https://github.com/DrPandemic) - [EraYaN](https://github.com/EraYaN) + - [escabe](https://github.com/escabe) + - [excelite](https://github.com/excelite) + - [fasheng](https://github.com/fasheng) + - [ferferga](https://github.com/ferferga) + - [fhriley](https://github.com/fhriley) - [flemse](https://github.com/flemse) - - [bfayers](https://github.com/bfayers) - - [Bond_009](https://github.com/Bond-009) - - [AnthonyLavado](https://github.com/anthonylavado) - - [sparky8251](https://github.com/sparky8251) - - [LeoVerto](https://github.com/LeoVerto) + - [Froghut](https://github.com/Froghut) + - [fruhnow](https://github.com/fruhnow) + - [geilername](https://github.com/geilername) + - [gnattu](https://github.com/gnattu) - [grafixeyehero](https://github.com/grafixeyehero) - - [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) - - [WillWill56](https://github.com/WillWill56) + - [h1nk](https://github.com/h1nk) + - [hawken93](https://github.com/hawken93) + - [HelloWorld017](https://github.com/HelloWorld017) + - [jftuga](https://github.com/jftuga) + - [joern-h](https://github.com/joern-h) + - [joshuaboniface](https://github.com/joshuaboniface) + - [JustAMan](https://github.com/JustAMan) + - [justinfenn](https://github.com/justinfenn) + - [KerryRJ](https://github.com/KerryRJ) + - [Larvitar](https://github.com/Larvitar) + - [LeoVerto](https://github.com/LeoVerto) - [Liggy](https://github.com/Liggy) - - [fruhnow](https://github.com/fruhnow) + - [LogicalPhallacy](https://github.com/LogicalPhallacy) + - [loli10K](https://github.com/loli10K) + - [lostmypillow](https://github.com/lostmypillow) - [Lynxy](https://github.com/Lynxy) - - [fasheng](https://github.com/fasheng) - - [ploughpuff](https://github.com/ploughpuff) - - [pjeanjean](https://github.com/pjeanjean) - - [DrPandemic](https://github.com/drpandemic) - - [joern-h](https://github.com/joern-h) - - [Khinenw](https://github.com/HelloWorld017) - - [fhriley](https://github.com/fhriley) - - [nevado](https://github.com/nevado) + - [ManfredRichthofen](https://github.com/ManfredRichthofen) + - [Marenz](https://github.com/Marenz) + - [marius-luca-87](https://github.com/marius-luca-87) - [mark-monteiro](https://github.com/mark-monteiro) - - [ullmie02](https://github.com/ullmie02) - - [geilername](https://github.com/geilername) + - [Matt07211](https://github.com/Matt07211) + - [mcarlton00](https://github.com/mcarlton00) + - [mitchfizz05](https://github.com/mitchfizz05) + - [MrTimscampi](https://github.com/MrTimscampi) + - [n8225](https://github.com/n8225) + - [Narfinger](https://github.com/Narfinger) + - [NathanPickard](https://github.com/NathanPickard) + - [neilsb](https://github.com/neilsb) + - [nevado](https://github.com/nevado) + - [Nickbert7](https://github.com/Nickbert7) + - [nvllsvm](https://github.com/nvllsvm) + - [nyanmisaka](https://github.com/nyanmisaka) + - [oddstr13](https://github.com/oddstr13) + - [petermcneil](https://github.com/petermcneil) + - [Phlogi](https://github.com/Phlogi) + - [pjeanjean](https://github.com/pjeanjean) + - [ploughpuff](https://github.com/ploughpuff) - [pR0Ps](https://github.com/pR0Ps) - - [artiume](https://github.com/Artiume) - + - [PrplHaz4](https://github.com/PrplHaz4) + - [RazeLighter777](https://github.com/RazeLighter777) + - [redSpoutnik](https://github.com/redSpoutnik) + - [ringmatter](https://github.com/ringmatter) + - [ryan-hartzell](https://github.com/ryan-hartzell) + - [s0urcelab](https://github.com/s0urcelab) + - [sachk](https://github.com/sachk) + - [sammyrc34](https://github.com/sammyrc34) + - [samuel9554](https://github.com/samuel9554) + - [scheidleon](https://github.com/scheidleon) + - [sebPomme](https://github.com/sebPomme) + - [SenorSmartyPants](https://github.com/SenorSmartyPants) + - [shemanaev](https://github.com/shemanaev) + - [skaro13](https://github.com/skaro13) + - [sl1288](https://github.com/sl1288) + - [sorinyo2004](https://github.com/sorinyo2004) + - [sparky8251](https://github.com/sparky8251) + - [stanionascu](https://github.com/stanionascu) + - [stevehayles](https://github.com/stevehayles) + - [SuperSandro2000](https://github.com/SuperSandro2000) + - [tbraeutigam](https://github.com/tbraeutigam) + - [teacupx](https://github.com/teacupx) + - [Terror-Gene](https://github.com/Terror-Gene) + - [ThatNerdyPikachu](https://github.com/ThatNerdyPikachu) + - [ThibaultNocchi](https://github.com/ThibaultNocchi) + - [thornbill](https://github.com/thornbill) + - [ThreeFive-O](https://github.com/ThreeFive-O) + - [TrisMcC](https://github.com/TrisMcC) + - [trumblejoe](https://github.com/trumblejoe) + - [TtheCreator](https://github.com/TtheCreator) + - [twinkybot](https://github.com/twinkybot) + - [Ullmie02](https://github.com/Ullmie02) + - [Unhelpful](https://github.com/Unhelpful) + - [viaregio](https://github.com/viaregio) + - [vitorsemeano](https://github.com/vitorsemeano) + - [voodoos](https://github.com/voodoos) + - [whooo](https://github.com/whooo) + - [WiiPlayer2](https://github.com/WiiPlayer2) + - [WillWill56](https://github.com/WillWill56) + - [wtayl0r](https://github.com/wtayl0r) + - [Wuerfelbecher](https://github.com/Wuerfelbecher) + - [Wunax](https://github.com/Wunax) + - [WWWesten](https://github.com/WWWesten) + - [WX9yMOXWId](https://github.com/WX9yMOXWId) + - [xosdy](https://github.com/xosdy) + - [XVicarious](https://github.com/XVicarious) + - [YouKnowBlom](https://github.com/YouKnowBlom) # Emby Contributors diff --git a/Dockerfile b/Dockerfile index 1029a4cee1..73ab3d7904 100644 --- a/Dockerfile +++ b/Dockerfile @@ -21,6 +21,13 @@ RUN dotnet publish Jellyfin.Server --disable-parallel --configuration Release -- FROM jellyfin/ffmpeg:${FFMPEG_VERSION} as ffmpeg FROM debian:buster-slim +# https://askubuntu.com/questions/972516/debian-frontend-environment-variable +ARG DEBIAN_FRONTEND="noninteractive" +# http://stackoverflow.com/questions/48162574/ddg#49462622 +ARG APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE=DontWarn +# https://github.com/NVIDIA/nvidia-docker/wiki/Installation-(Native-GPU-Support) +ENV NVIDIA_DRIVER_CAPABILITIES="compute,video,utility" + COPY --from=ffmpeg /opt/ffmpeg /opt/ffmpeg COPY --from=builder /jellyfin /jellyfin COPY --from=web-builder /dist /jellyfin/jellyfin-web @@ -31,9 +38,16 @@ COPY --from=web-builder /dist /jellyfin/jellyfin-web # mesa-va-drivers: needed for VAAPI RUN apt-get update \ && apt-get install --no-install-recommends --no-install-suggests -y \ - libfontconfig1 libgomp1 libva-drm2 mesa-va-drivers openssl ca-certificates \ - && apt-get clean autoclean \ - && apt-get autoremove \ + libfontconfig1 \ + libgomp1 \ + libva-drm2 \ + mesa-va-drivers \ + openssl \ + ca-certificates \ + vainfo \ + i965-va-driver \ + && apt-get clean autoclean -y\ + && apt-get autoremove -y\ && rm -rf /var/lib/apt/lists/* \ && mkdir -p /cache /config /media \ && chmod 777 /cache /config /media \ diff --git a/Dockerfile.arm b/Dockerfile.arm index 5847de9189..07780e27bf 100644 --- a/Dockerfile.arm +++ b/Dockerfile.arm @@ -27,10 +27,35 @@ RUN dotnet publish Jellyfin.Server --configuration Release --output="/jellyfin" FROM multiarch/qemu-user-static:x86_64-arm as qemu FROM arm32v7/debian:buster-slim + +# https://askubuntu.com/questions/972516/debian-frontend-environment-variable +ARG DEBIAN_FRONTEND="noninteractive" +# http://stackoverflow.com/questions/48162574/ddg#49462622 +ARG APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE=DontWarn +# https://github.com/NVIDIA/nvidia-docker/wiki/Installation-(Native-GPU-Support) +ENV NVIDIA_DRIVER_CAPABILITIES="compute,video,utility" + COPY --from=qemu /usr/bin/qemu-arm-static /usr/bin RUN apt-get update \ - && apt-get install --no-install-recommends --no-install-suggests -y ffmpeg \ - libssl-dev ca-certificates \ + && apt-get install --no-install-recommends --no-install-suggests -y ca-certificates gnupg curl && \ + curl -ks https://repo.jellyfin.org/debian/jellyfin_team.gpg.key | apt-key add - && \ + curl -s https://keyserver.ubuntu.com/pks/lookup?op=get\&search=0x6587ffd6536b8826e88a62547876ae518cbcf2f2 | apt-key add - && \ + echo 'deb [arch=armhf] https://repo.jellyfin.org/debian buster main' > /etc/apt/sources.list.d/jellyfin.list && \ + echo "deb http://ppa.launchpad.net/ubuntu-raspi2/ppa/ubuntu bionic main">> /etc/apt/sources.list.d/raspbins.list && \ + apt-get update && \ + apt-get install --no-install-recommends --no-install-suggests -y \ + jellyfin-ffmpeg \ + libssl-dev \ + libfontconfig1 \ + libfreetype6 \ + libomxil-bellagio0 \ + libomxil-bellagio-bin \ + libraspberrypi0 \ + vainfo \ + libva2 \ + && apt-get remove curl gnupg -y \ + && apt-get clean autoclean -y \ + && apt-get autoremove -y \ && rm -rf /var/lib/apt/lists/* \ && mkdir -p /cache /config /media \ && chmod 777 /cache /config /media @@ -44,4 +69,4 @@ VOLUME /cache /config /media ENTRYPOINT ["./jellyfin/jellyfin", \ "--datadir", "/config", \ "--cachedir", "/cache", \ - "--ffmpeg", "/usr/bin/ffmpeg"] + "--ffmpeg", "/usr/lib/jellyfin-ffmpeg"] diff --git a/Dockerfile.arm64 b/Dockerfile.arm64 index a9f6c50d9f..9dc6fa7edb 100644 --- a/Dockerfile.arm64 +++ b/Dockerfile.arm64 @@ -26,10 +26,25 @@ RUN dotnet publish Jellyfin.Server --configuration Release --output="/jellyfin" FROM multiarch/qemu-user-static:x86_64-aarch64 as qemu FROM arm64v8/debian:buster-slim + +# https://askubuntu.com/questions/972516/debian-frontend-environment-variable +ARG DEBIAN_FRONTEND="noninteractive" +# http://stackoverflow.com/questions/48162574/ddg#49462622 +ARG APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE=DontWarn +# https://github.com/NVIDIA/nvidia-docker/wiki/Installation-(Native-GPU-Support) +ENV NVIDIA_DRIVER_CAPABILITIES="compute,video,utility" + COPY --from=qemu /usr/bin/qemu-aarch64-static /usr/bin -RUN apt-get update \ - && apt-get install --no-install-recommends --no-install-suggests -y ffmpeg \ - libssl-dev ca-certificates \ +RUN apt-get update && apt-get install --no-install-recommends --no-install-suggests -y \ + ffmpeg \ + libssl-dev \ + ca-certificates \ + libfontconfig1 \ + libfreetype6 \ + libomxil-bellagio0 \ + libomxil-bellagio-bin \ + && apt-get clean autoclean -y \ + && apt-get autoremove -y \ && rm -rf /var/lib/apt/lists/* \ && mkdir -p /cache /config /media \ && chmod 777 /cache /config /media diff --git a/Emby.Dlna/ConnectionManager/ConnectionManager.cs b/Emby.Dlna/ConnectionManager/ConnectionManager.cs index 365249c54a..245c03adc8 100644 --- a/Emby.Dlna/ConnectionManager/ConnectionManager.cs +++ b/Emby.Dlna/ConnectionManager/ConnectionManager.cs @@ -16,7 +16,11 @@ namespace Emby.Dlna.ConnectionManager private readonly ILogger _logger; private readonly IServerConfigurationManager _config; - public ConnectionManager(IDlnaManager dlna, IServerConfigurationManager config, ILogger logger, IHttpClient httpClient) + public ConnectionManager( + IDlnaManager dlna, + IServerConfigurationManager config, + ILogger logger, + IHttpClient httpClient) : base(logger, httpClient) { _dlna = dlna; diff --git a/Emby.Dlna/ContentDirectory/ContentDirectory.cs b/Emby.Dlna/ContentDirectory/ContentDirectory.cs index 523430e430..c89cd1f937 100644 --- a/Emby.Dlna/ContentDirectory/ContentDirectory.cs +++ b/Emby.Dlna/ContentDirectory/ContentDirectory.cs @@ -38,7 +38,7 @@ namespace Emby.Dlna.ContentDirectory ILibraryManager libraryManager, IServerConfigurationManager config, IUserManager userManager, - ILogger logger, + ILogger logger, IHttpClient httpClient, ILocalizationManager localization, IMediaSourceManager mediaSourceManager, diff --git a/Emby.Dlna/Main/DlnaEntryPoint.cs b/Emby.Dlna/Main/DlnaEntryPoint.cs index 1ee4151e4b..75b07efc4c 100644 --- a/Emby.Dlna/Main/DlnaEntryPoint.cs +++ b/Emby.Dlna/Main/DlnaEntryPoint.cs @@ -2,8 +2,8 @@ #pragma warning disable SA1600 using System; -using System.Net.Sockets; using System.Globalization; +using System.Net.Sockets; using System.Threading; using System.Threading.Tasks; using Emby.Dlna.PlayTo; @@ -27,7 +27,7 @@ using MediaBrowser.Model.System; using Microsoft.Extensions.Logging; using Rssdp; using Rssdp.Infrastructure; -using OperatingSystem = MediaBrowser.Common.System.OperatingSystem; +using OperatingSystem = MediaBrowser.Common.System.OperatingSystem; namespace Emby.Dlna.Main { @@ -59,7 +59,9 @@ namespace Emby.Dlna.Main private ISsdpCommunicationsServer _communicationsServer; internal IContentDirectory ContentDirectory { get; private set; } + internal IConnectionManager ConnectionManager { get; private set; } + internal IMediaReceiverRegistrar MediaReceiverRegistrar { get; private set; } public static DlnaEntryPoint Current; @@ -107,7 +109,7 @@ namespace Emby.Dlna.Main libraryManager, config, userManager, - _logger, + loggerFactory.CreateLogger(), httpClient, localizationManager, mediaSourceManager, @@ -115,9 +117,16 @@ namespace Emby.Dlna.Main mediaEncoder, tvSeriesManager); - ConnectionManager = new ConnectionManager.ConnectionManager(dlnaManager, config, _logger, httpClient); + ConnectionManager = new ConnectionManager.ConnectionManager( + dlnaManager, + config, + loggerFactory.CreateLogger(), + httpClient); - MediaReceiverRegistrar = new MediaReceiverRegistrar.MediaReceiverRegistrar(_logger, httpClient, config); + MediaReceiverRegistrar = new MediaReceiverRegistrar.MediaReceiverRegistrar( + loggerFactory.CreateLogger(), + httpClient, + config); Current = this; } diff --git a/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrar.cs b/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrar.cs index e2d48bc01f..8c52d32f17 100644 --- a/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrar.cs +++ b/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrar.cs @@ -13,7 +13,10 @@ namespace Emby.Dlna.MediaReceiverRegistrar { private readonly IServerConfigurationManager _config; - public MediaReceiverRegistrar(ILogger logger, IHttpClient httpClient, IServerConfigurationManager config) + public MediaReceiverRegistrar( + ILogger logger, + IHttpClient httpClient, + IServerConfigurationManager config) : base(logger, httpClient) { _config = config; diff --git a/Emby.Dlna/Service/BaseService.cs b/Emby.Dlna/Service/BaseService.cs index d7e5c541d9..c8f68b6471 100644 --- a/Emby.Dlna/Service/BaseService.cs +++ b/Emby.Dlna/Service/BaseService.cs @@ -13,7 +13,7 @@ namespace Emby.Dlna.Service protected IHttpClient HttpClient; protected ILogger Logger; - protected BaseService(ILogger logger, IHttpClient httpClient) + protected BaseService(ILogger logger, IHttpClient httpClient) { Logger = logger; HttpClient = httpClient; diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 8ea188724a..3d79cae1e5 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -119,7 +119,6 @@ namespace Emby.Server.Implementations public abstract class ApplicationHost : IServerApplicationHost, IDisposable { private SqliteUserRepository _userRepository; - private SqliteDisplayPreferencesRepository _displayPreferencesRepository; /// @@ -167,10 +166,9 @@ namespace Emby.Server.Implementations public bool IsShuttingDown { get; private set; } /// - /// Gets or sets the logger. + /// Gets the logger. /// - /// The logger. - protected ILogger Logger { get; set; } + protected ILogger Logger { get; } private IPlugin[] _plugins; @@ -181,10 +179,9 @@ namespace Emby.Server.Implementations public IReadOnlyList Plugins => _plugins; /// - /// Gets or sets the logger factory. + /// Gets the logger factory. /// - /// The logger factory. - public ILoggerFactory LoggerFactory { get; protected set; } + protected ILoggerFactory LoggerFactory { get; } /// /// Gets or sets the application paths. @@ -328,8 +325,6 @@ namespace Emby.Server.Implementations private IMediaSourceManager MediaSourceManager { get; set; } - private readonly IConfiguration _configuration; - /// /// Gets the installation manager. /// @@ -367,11 +362,8 @@ namespace Emby.Server.Implementations IStartupOptions options, IFileSystem fileSystem, IImageEncoder imageEncoder, - INetworkManager networkManager, - IConfiguration configuration) + INetworkManager networkManager) { - _configuration = configuration; - XmlSerializer = new MyXmlSerializer(); NetworkManager = networkManager; @@ -587,7 +579,8 @@ namespace Emby.Server.Implementations } } - public async Task InitAsync(IServiceCollection serviceCollection) + /// + public async Task InitAsync(IServiceCollection serviceCollection, IConfiguration startupConfig) { HttpPort = ServerConfigurationManager.Configuration.HttpServerPortNumber; HttpsPort = ServerConfigurationManager.Configuration.HttpsPortNumber; @@ -620,7 +613,7 @@ namespace Emby.Server.Implementations DiscoverTypes(); - await RegisterResources(serviceCollection).ConfigureAwait(false); + await RegisterResources(serviceCollection, startupConfig).ConfigureAwait(false); ContentRoot = ServerConfigurationManager.Configuration.DashboardSourcePath; if (string.IsNullOrEmpty(ContentRoot)) @@ -652,14 +645,14 @@ namespace Emby.Server.Implementations var response = context.Response; var localPath = context.Request.Path.ToString(); - var req = new WebSocketSharpRequest(request, response, request.Path, Logger); + var req = new WebSocketSharpRequest(request, response, request.Path, LoggerFactory.CreateLogger()); await HttpServer.RequestHandler(req, request.GetDisplayUrl(), request.Host.ToString(), localPath, context.RequestAborted).ConfigureAwait(false); } /// /// Registers resources that classes will depend on /// - protected async Task RegisterResources(IServiceCollection serviceCollection) + protected async Task RegisterResources(IServiceCollection serviceCollection, IConfiguration startupConfig) { serviceCollection.AddMemoryCache(); @@ -668,13 +661,10 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(ApplicationPaths); - serviceCollection.AddSingleton(_configuration); - serviceCollection.AddSingleton(JsonSerializer); - serviceCollection.AddSingleton(LoggerFactory); - serviceCollection.AddLogging(); - serviceCollection.AddSingleton(Logger); + // TODO: Support for injecting ILogger should be deprecated in favour of ILogger and this removed + serviceCollection.AddSingleton(Logger); serviceCollection.AddSingleton(FileSystemManager); serviceCollection.AddSingleton(); @@ -762,7 +752,7 @@ namespace Emby.Server.Implementations ProcessFactory, LocalizationManager, () => SubtitleEncoder, - _configuration, + startupConfig, StartupOptions.FFmpegPath); serviceCollection.AddSingleton(MediaEncoder); @@ -784,7 +774,7 @@ namespace Emby.Server.Implementations this, LoggerFactory.CreateLogger(), ServerConfigurationManager, - _configuration, + startupConfig, NetworkManager, JsonSerializer, XmlSerializer, @@ -1206,7 +1196,7 @@ namespace Emby.Server.Implementations }); } - protected IHttpListener CreateHttpListener() => new WebSocketSharpListener(Logger); + protected IHttpListener CreateHttpListener() => new WebSocketSharpListener(LoggerFactory.CreateLogger()); private CertificateInfo GetCertificateInfo(bool generateCertificate) { diff --git a/Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs b/Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs index 03e6abcfba..91d72d930e 100644 --- a/Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs +++ b/Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs @@ -20,7 +20,11 @@ namespace Emby.Server.Implementations.Channels private readonly ILogger _logger; private readonly ILibraryManager _libraryManager; - public RefreshChannelsScheduledTask(IChannelManager channelManager, IUserManager userManager, ILogger logger, ILibraryManager libraryManager) + public RefreshChannelsScheduledTask( + IChannelManager channelManager, + IUserManager userManager, + ILogger logger, + ILibraryManager libraryManager) { _channelManager = channelManager; _userManager = userManager; diff --git a/Emby.Server.Implementations/Collections/CollectionManager.cs b/Emby.Server.Implementations/Collections/CollectionManager.cs index efdef8481b..1d477094cd 100644 --- a/Emby.Server.Implementations/Collections/CollectionManager.cs +++ b/Emby.Server.Implementations/Collections/CollectionManager.cs @@ -348,7 +348,10 @@ namespace Emby.Server.Implementations.Collections private readonly IServerConfigurationManager _config; private readonly ILogger _logger; - public CollectionManagerEntryPoint(ICollectionManager collectionManager, IServerConfigurationManager config, ILogger logger) + public CollectionManagerEntryPoint( + ICollectionManager collectionManager, + IServerConfigurationManager config, + ILogger logger) { _collectionManager = (CollectionManager)collectionManager; _config = config; diff --git a/Emby.Server.Implementations/ConfigurationOptions.cs b/Emby.Server.Implementations/ConfigurationOptions.cs index 08a493cb79..a7e9369cf9 100644 --- a/Emby.Server.Implementations/ConfigurationOptions.cs +++ b/Emby.Server.Implementations/ConfigurationOptions.cs @@ -17,7 +17,6 @@ namespace Emby.Server.Implementations { { NoWebContentKey, bool.FalseString }, { HttpListenerHost.DefaultRedirectKey, "web/index.html" }, - { MusicBrainzAlbumProvider.BaseUrlKey, "https://www.musicbrainz.org" }, { FfmpegProbeSizeKey, "1G" }, { FfmpegAnalyzeDurationKey, "200M" } }; diff --git a/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs b/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs index 8a5387e9b4..f51dfb725a 100644 --- a/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs +++ b/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs @@ -15,7 +15,7 @@ namespace Emby.Server.Implementations.Data private readonly ILibraryManager _libraryManager; private readonly ILogger _logger; - public CleanDatabaseScheduledTask(ILibraryManager libraryManager, ILogger logger) + public CleanDatabaseScheduledTask(ILibraryManager libraryManager, ILogger logger) { _libraryManager = libraryManager; _logger = logger; diff --git a/Emby.Server.Implementations/Devices/DeviceManager.cs b/Emby.Server.Implementations/Devices/DeviceManager.cs index 4f8f9f23b3..9ac6a1d5e8 100644 --- a/Emby.Server.Implementations/Devices/DeviceManager.cs +++ b/Emby.Server.Implementations/Devices/DeviceManager.cs @@ -407,7 +407,10 @@ namespace Emby.Server.Implementations.Devices private readonly IServerConfigurationManager _config; private ILogger _logger; - public DeviceManagerEntryPoint(IDeviceManager deviceManager, IServerConfigurationManager config, ILogger logger) + public DeviceManagerEntryPoint( + IDeviceManager deviceManager, + IServerConfigurationManager config, + ILogger logger) { _deviceManager = (DeviceManager)deviceManager; _config = config; diff --git a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs index 06458baedc..b31023cdbd 100644 --- a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs +++ b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs @@ -56,7 +56,12 @@ namespace Emby.Server.Implementations.EntryPoints private readonly IProviderManager _providerManager; - public LibraryChangedNotifier(ILibraryManager libraryManager, ISessionManager sessionManager, IUserManager userManager, ILogger logger, IProviderManager providerManager) + public LibraryChangedNotifier( + ILibraryManager libraryManager, + ISessionManager sessionManager, + IUserManager userManager, + ILogger logger, + IProviderManager providerManager) { _libraryManager = libraryManager; _sessionManager = sessionManager; diff --git a/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs b/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs index 9603d79761..eb86ea4645 100644 --- a/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs +++ b/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs @@ -20,7 +20,11 @@ namespace Emby.Server.Implementations.EntryPoints private readonly IUserManager _userManager; private readonly ILogger _logger; - public RecordingNotifier(ISessionManager sessionManager, IUserManager userManager, ILogger logger, ILiveTvManager liveTvManager) + public RecordingNotifier( + ISessionManager sessionManager, + IUserManager userManager, + ILogger logger, + ILiveTvManager liveTvManager) { _sessionManager = sessionManager; _userManager = userManager; diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 5d16a9050c..5f7f5b2f0e 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -944,7 +944,6 @@ namespace Emby.Server.Implementations.Library IncludeItemTypes = new[] { typeof(T).Name }, Name = name, DtoOptions = options - }).Cast() .OrderBy(i => i.IsAccessedByName ? 1 : 0) .Cast() @@ -1080,7 +1079,7 @@ namespace Emby.Server.Implementations.Library var innerProgress = new ActionableProgress(); - innerProgress.RegisterAction(pct => progress.Report(pct * pct * 0.96)); + innerProgress.RegisterAction(pct => progress.Report(pct * 0.96)); // Validate the entire media library await RootFolder.ValidateChildren(innerProgress, cancellationToken, new MetadataRefreshOptions(new DirectoryService(_fileSystem)), recursive: true).ConfigureAwait(false); diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs index 013fdbf138..681db4896e 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs @@ -27,7 +27,11 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio /// The file system. /// The library manager. /// The configuration manager. - public MusicArtistResolver(ILogger logger, IFileSystem fileSystem, ILibraryManager libraryManager, IServerConfigurationManager config) + public MusicArtistResolver( + ILogger logger, + IFileSystem fileSystem, + ILibraryManager libraryManager, + IServerConfigurationManager config) { _logger = logger; _fileSystem = fileSystem; diff --git a/Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs b/Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs index 5e672f221a..fa6d574660 100644 --- a/Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs @@ -1,65 +1,68 @@ -#pragma warning disable CS1591 -#pragma warning disable SA1600 - using System; using System.IO; using System.Linq; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Playlists; +using MediaBrowser.Controller.Resolvers; +using MediaBrowser.LocalMetadata.Savers; using MediaBrowser.Model.Entities; namespace Emby.Server.Implementations.Library.Resolvers { + /// + /// for library items. + /// public class PlaylistResolver : FolderResolver { - private string[] SupportedCollectionTypes = new string[] { - + private string[] _musicPlaylistCollectionTypes = new string[] { string.Empty, CollectionType.Music }; - /// - /// Resolves the specified args. - /// - /// The args. - /// BoxSet. + /// protected override Playlist Resolve(ItemResolveArgs args) { - // It's a boxset if all of the following conditions are met: - // Is a Directory - // Contains [playlist] in the path if (args.IsDirectory) { - var filename = Path.GetFileName(args.Path); - - if (string.IsNullOrEmpty(filename)) + // It's a boxset if the path is a directory with [playlist] in it's the name + // TODO: Should this use Path.GetDirectoryName() instead? + bool isBoxSet = Path.GetFileName(args.Path) + ?.Contains("[playlist]", StringComparison.OrdinalIgnoreCase) + ?? false; + if (isBoxSet) { - return null; + return new Playlist + { + Path = args.Path, + Name = Path.GetFileName(args.Path).Replace("[playlist]", string.Empty, StringComparison.OrdinalIgnoreCase).Trim() + }; } - if (filename.IndexOf("[playlist]", StringComparison.OrdinalIgnoreCase) != -1) + // It's a directory-based playlist if the directory contains a playlist file + var filePaths = Directory.EnumerateFiles(args.Path); + if (filePaths.Any(f => f.EndsWith(PlaylistXmlSaver.DefaultPlaylistFilename, StringComparison.OrdinalIgnoreCase))) { return new Playlist { Path = args.Path, - Name = Path.GetFileName(args.Path).Replace("[playlist]", string.Empty, StringComparison.OrdinalIgnoreCase).Trim() + Name = Path.GetFileName(args.Path) }; } } - else + + // Check if this is a music playlist file + // It should have the correct collection type and a supported file extension + else if (_musicPlaylistCollectionTypes.Contains(args.CollectionType ?? string.Empty, StringComparer.OrdinalIgnoreCase)) { - if (SupportedCollectionTypes.Contains(args.CollectionType ?? string.Empty, StringComparer.OrdinalIgnoreCase)) + var extension = Path.GetExtension(args.Path); + if (Playlist.SupportedExtensions.Contains(extension ?? string.Empty, StringComparer.OrdinalIgnoreCase)) { - var extension = Path.GetExtension(args.Path); - if (Playlist.SupportedExtensions.Contains(extension ?? string.Empty, StringComparer.OrdinalIgnoreCase)) + return new Playlist { - return new Playlist - { - Path = args.Path, - Name = Path.GetFileNameWithoutExtension(args.Path), - IsInMixedFolder = true - }; - } + Path = args.Path, + Name = Path.GetFileNameWithoutExtension(args.Path), + IsInMixedFolder = true + }; } } diff --git a/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs b/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs index 3e88c0287e..18145b7f17 100644 --- a/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs @@ -25,7 +25,11 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV /// The library manager. /// The localization /// The logger - public SeasonResolver(IServerConfigurationManager config, ILibraryManager libraryManager, ILocalizationManager localization, ILogger logger) + public SeasonResolver( + IServerConfigurationManager config, + ILibraryManager libraryManager, + ILocalizationManager localization, + ILogger logger) { _config = config; _libraryManager = libraryManager; diff --git a/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs b/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs index b547fc8c91..f8c26c2e16 100644 --- a/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs @@ -31,7 +31,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV /// The file system. /// The logger. /// The library manager. - public SeriesResolver(IFileSystem fileSystem, ILogger logger, ILibraryManager libraryManager) + public SeriesResolver(IFileSystem fileSystem, ILogger logger, ILibraryManager libraryManager) { _fileSystem = fileSystem; _logger = logger; diff --git a/Emby.Server.Implementations/Library/Validators/ArtistsPostScanTask.cs b/Emby.Server.Implementations/Library/Validators/ArtistsPostScanTask.cs index 61a07d0d67..2af8ff5cbf 100644 --- a/Emby.Server.Implementations/Library/Validators/ArtistsPostScanTask.cs +++ b/Emby.Server.Implementations/Library/Validators/ArtistsPostScanTask.cs @@ -25,7 +25,10 @@ namespace Emby.Server.Implementations.Library.Validators /// The library manager. /// The logger. /// The item repository. - public ArtistsPostScanTask(ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo) + public ArtistsPostScanTask( + ILibraryManager libraryManager, + ILogger logger, + IItemRepository itemRepo) { _libraryManager = libraryManager; _logger = logger; diff --git a/Emby.Server.Implementations/Library/Validators/GenresPostScanTask.cs b/Emby.Server.Implementations/Library/Validators/GenresPostScanTask.cs index 06d1dd89dc..251785dfd8 100644 --- a/Emby.Server.Implementations/Library/Validators/GenresPostScanTask.cs +++ b/Emby.Server.Implementations/Library/Validators/GenresPostScanTask.cs @@ -25,7 +25,10 @@ namespace Emby.Server.Implementations.Library.Validators /// The library manager. /// The logger. /// The item repository. - public GenresPostScanTask(ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo) + public GenresPostScanTask( + ILibraryManager libraryManager, + ILogger logger, + IItemRepository itemRepo) { _libraryManager = libraryManager; _logger = logger; diff --git a/Emby.Server.Implementations/Library/Validators/MusicGenresPostScanTask.cs b/Emby.Server.Implementations/Library/Validators/MusicGenresPostScanTask.cs index 58549e9d76..9d8690116f 100644 --- a/Emby.Server.Implementations/Library/Validators/MusicGenresPostScanTask.cs +++ b/Emby.Server.Implementations/Library/Validators/MusicGenresPostScanTask.cs @@ -25,7 +25,10 @@ namespace Emby.Server.Implementations.Library.Validators /// The library manager. /// The logger. /// The item repository. - public MusicGenresPostScanTask(ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo) + public MusicGenresPostScanTask( + ILibraryManager libraryManager, + ILogger logger, + IItemRepository itemRepo) { _libraryManager = libraryManager; _logger = logger; diff --git a/Emby.Server.Implementations/Library/Validators/StudiosPostScanTask.cs b/Emby.Server.Implementations/Library/Validators/StudiosPostScanTask.cs index 00899c3361..2f8f906b97 100644 --- a/Emby.Server.Implementations/Library/Validators/StudiosPostScanTask.cs +++ b/Emby.Server.Implementations/Library/Validators/StudiosPostScanTask.cs @@ -26,7 +26,10 @@ namespace Emby.Server.Implementations.Library.Validators /// The library manager. /// The logger. /// The item repository. - public StudiosPostScanTask(ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo) + public StudiosPostScanTask( + ILibraryManager libraryManager, + ILogger logger, + IItemRepository itemRepo) { _libraryManager = libraryManager; _logger = logger; diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index 49d4ddbaf5..bbef93b8c0 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -80,7 +80,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV IServerApplicationHost appHost, IStreamHelper streamHelper, IMediaSourceManager mediaSourceManager, - ILogger logger, + ILogger logger, IJsonSerializer jsonSerializer, IHttpClient httpClient, IServerConfigurationManager config, diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs index 906f42d2e9..e9d3105bf1 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs +++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs @@ -33,7 +33,11 @@ namespace Emby.Server.Implementations.LiveTv.Listings private const string ApiUrl = "https://json.schedulesdirect.org/20141201"; - public SchedulesDirect(ILogger logger, IJsonSerializer jsonSerializer, IHttpClient httpClient, IApplicationHost appHost) + public SchedulesDirect( + ILogger logger, + IJsonSerializer jsonSerializer, + IHttpClient httpClient, + IApplicationHost appHost) { _logger = logger; _jsonSerializer = jsonSerializer; diff --git a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs index 42daa98f5c..c159b60a91 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs +++ b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs @@ -34,7 +34,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings public XmlTvListingsProvider( IServerConfigurationManager config, IHttpClient httpClient, - ILogger logger, + ILogger logger, IFileSystem fileSystem, IZipClient zipClient) { diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs index e5cb6c7b9c..a2d972d19c 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs @@ -39,7 +39,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun public HdHomerunHost( IServerConfigurationManager config, - ILogger logger, + ILogger logger, IJsonSerializer jsonSerializer, IFileSystem fileSystem, IHttpClient httpClient, diff --git a/Emby.Server.Implementations/Localization/Core/ar.json b/Emby.Server.Implementations/Localization/Core/ar.json index f0f165b22a..24da1aa567 100644 --- a/Emby.Server.Implementations/Localization/Core/ar.json +++ b/Emby.Server.Implementations/Localization/Core/ar.json @@ -2,22 +2,22 @@ "Albums": "ألبومات", "AppDeviceValues": "تطبيق: {0}, جهاز: {1}", "Application": "التطبيق", - "Artists": "الفنان", + "Artists": "الفنانين", "AuthenticationSucceededWithUserName": "{0} سجل الدخول بنجاح", "Books": "كتب", "CameraImageUploadedFrom": "صورة كاميرا جديدة تم رفعها من {0}", "Channels": "القنوات", - "ChapterNameValue": "الباب {0}", + "ChapterNameValue": "فصل {0}", "Collections": "مجموعات", - "DeviceOfflineWithName": "تم قطع اتصال {0}", + "DeviceOfflineWithName": "قُطِع الاتصال بـ{0}", "DeviceOnlineWithName": "{0} متصل", "FailedLoginAttemptWithUserName": "عملية تسجيل الدخول فشلت من {0}", - "Favorites": "التفضيلات", + "Favorites": "المفضلة", "Folders": "المجلدات", - "Genres": "أنواع الأفلام", + "Genres": "الأنواع", "HeaderAlbumArtists": "فناني الألبومات", "HeaderCameraUploads": "تحميلات الكاميرا", - "HeaderContinueWatching": "استئناف المشاهدة", + "HeaderContinueWatching": "استئناف", "HeaderFavoriteAlbums": "الألبومات المفضلة", "HeaderFavoriteArtists": "الفنانون المفضلون", "HeaderFavoriteEpisodes": "الحلقات المفضلة", @@ -31,28 +31,28 @@ "ItemAddedWithName": "تم إضافة {0} للمكتبة", "ItemRemovedWithName": "تم إزالة {0} من المكتبة", "LabelIpAddressValue": "عنوان الآي بي: {0}", - "LabelRunningTimeValue": "وقت التشغيل: {0}", + "LabelRunningTimeValue": "المدة: {0}", "Latest": "الأحدث", - "MessageApplicationUpdated": "لقد تم تحديث خادم أمبي", + "MessageApplicationUpdated": "لقد تم تحديث خادم Jellyfin", "MessageApplicationUpdatedTo": "تم تحديث سيرفر Jellyfin الى {0}", "MessageNamedServerConfigurationUpdatedWithValue": "تم تحديث إعدادات الخادم في قسم {0}", "MessageServerConfigurationUpdated": "تم تحديث إعدادات الخادم", - "MixedContent": "محتوى مخلوط", + "MixedContent": "محتوى مختلط", "Movies": "الأفلام", "Music": "الموسيقى", "MusicVideos": "الفيديوهات الموسيقية", "NameInstallFailed": "فشل التثبيت {0}", "NameSeasonNumber": "الموسم {0}", "NameSeasonUnknown": "الموسم غير معروف", - "NewVersionIsAvailable": "نسخة حديثة من سيرفر Jellyfin متوفرة للتحميل .", + "NewVersionIsAvailable": "نسخة جديدة من سيرفر Jellyfin متوفرة للتحميل.", "NotificationOptionApplicationUpdateAvailable": "يوجد تحديث للتطبيق", "NotificationOptionApplicationUpdateInstalled": "تم تحديث التطبيق", "NotificationOptionAudioPlayback": "بدأ تشغيل المقطع الصوتي", "NotificationOptionAudioPlaybackStopped": "تم إيقاف تشغيل المقطع الصوتي", - "NotificationOptionCameraImageUploaded": "تم رقع صورة الكاميرا", + "NotificationOptionCameraImageUploaded": "تم رفع صورة الكاميرا", "NotificationOptionInstallationFailed": "فشل في التثبيت", - "NotificationOptionNewLibraryContent": "تم إضافة محتوى جديد", - "NotificationOptionPluginError": "فشل في الملحق", + "NotificationOptionNewLibraryContent": "أُضِيفَ محتوى جديد", + "NotificationOptionPluginError": "فشل في الـPlugin", "NotificationOptionPluginInstalled": "تم تثبيت الملحق", "NotificationOptionPluginUninstalled": "تمت إزالة الملحق", "NotificationOptionPluginUpdateInstalled": "تم تثبيت تحديثات الملحق", diff --git a/Emby.Server.Implementations/Localization/Core/da.json b/Emby.Server.Implementations/Localization/Core/da.json index cc8b7dbd53..c421db87d4 100644 --- a/Emby.Server.Implementations/Localization/Core/da.json +++ b/Emby.Server.Implementations/Localization/Core/da.json @@ -3,7 +3,7 @@ "AppDeviceValues": "App: {0}, Enhed: {1}", "Application": "Applikation", "Artists": "Kunstnere", - "AuthenticationSucceededWithUserName": "{0} bekræftet med succes", + "AuthenticationSucceededWithUserName": "{0} succesfuldt autentificeret", "Books": "Bøger", "CameraImageUploadedFrom": "Et nyt kamerabillede er blevet uploadet fra {0}", "Channels": "Kanaler", diff --git a/Emby.Server.Implementations/Localization/Core/de.json b/Emby.Server.Implementations/Localization/Core/de.json index 019736c471..69678c2684 100644 --- a/Emby.Server.Implementations/Localization/Core/de.json +++ b/Emby.Server.Implementations/Localization/Core/de.json @@ -1,15 +1,15 @@ { "Albums": "Alben", - "AppDeviceValues": "App: {0}, Gerät: {1}", - "Application": "Anwendung", + "AppDeviceValues": "Anw: {0}, Gerät: {1}", + "Application": "Programm", "Artists": "Interpreten", "AuthenticationSucceededWithUserName": "{0} hat sich erfolgreich angemeldet", "Books": "Bücher", - "CameraImageUploadedFrom": "Ein neues Foto wurde hochgeladen von {0}", + "CameraImageUploadedFrom": "Ein neues Foto wurde von {0} hochgeladen", "Channels": "Kanäle", "ChapterNameValue": "Kapitel {0}", "Collections": "Sammlungen", - "DeviceOfflineWithName": "{0} wurde getrennt", + "DeviceOfflineWithName": "{0} hat die Verbindung getrennt", "DeviceOnlineWithName": "{0} ist verbunden", "FailedLoginAttemptWithUserName": "Fehlgeschlagener Anmeldeversuch von {0}", "Favorites": "Favoriten", @@ -17,7 +17,7 @@ "Genres": "Genres", "HeaderAlbumArtists": "Album-Interpreten", "HeaderCameraUploads": "Kamera-Uploads", - "HeaderContinueWatching": "Weiterschauen", + "HeaderContinueWatching": "Fortsetzen", "HeaderFavoriteAlbums": "Lieblingsalben", "HeaderFavoriteArtists": "Lieblings-Interpreten", "HeaderFavoriteEpisodes": "Lieblingsepisoden", diff --git a/Emby.Server.Implementations/Localization/Core/fi.json b/Emby.Server.Implementations/Localization/Core/fi.json index d02f841fd1..a38103d25a 100644 --- a/Emby.Server.Implementations/Localization/Core/fi.json +++ b/Emby.Server.Implementations/Localization/Core/fi.json @@ -17,31 +17,31 @@ "LabelIpAddressValue": "IP-osoite: {0}", "ItemRemovedWithName": "{0} poistettiin kirjastosta", "ItemAddedWithName": "{0} lisättiin kirjastoon", - "Inherit": "Periä", + "Inherit": "Periytyä", "HomeVideos": "Kotivideot", - "HeaderRecordingGroups": "Äänitysryhmät", + "HeaderRecordingGroups": "Nauhoitusryhmät", "HeaderNextUp": "Seuraavaksi", "HeaderFavoriteSongs": "Lempikappaleet", "HeaderFavoriteShows": "Lempisarjat", "HeaderFavoriteEpisodes": "Lempijaksot", - "HeaderCameraUploads": "Kamerasta lähetetyt", + "HeaderCameraUploads": "Kameralataukset", "HeaderFavoriteArtists": "Lempiartistit", "HeaderFavoriteAlbums": "Lempialbumit", "HeaderContinueWatching": "Jatka katsomista", - "HeaderAlbumArtists": "Albumin artistit", - "Genres": "Tyylilaji", + "HeaderAlbumArtists": "Albumin esittäjä", + "Genres": "Tyylilajit", "Folders": "Kansiot", "Favorites": "Suosikit", - "FailedLoginAttemptWithUserName": "Epäonnistunut kirjautumisyritys kohteesta {0}", - "DeviceOnlineWithName": "{0} on yhdistynyt", + "FailedLoginAttemptWithUserName": "Kirjautuminen epäonnistui kohteesta {0}", + "DeviceOnlineWithName": "{0} on yhdistetty", "DeviceOfflineWithName": "{0} on katkaissut yhteytensä", "Collections": "Kokoelmat", "ChapterNameValue": "Luku: {0}", "Channels": "Kanavat", - "CameraImageUploadedFrom": "Uusi kamerakuva on lähetetty kohteesta {0}", + "CameraImageUploadedFrom": "Uusi kamerakuva on ladattu {0}", "Books": "Kirjat", - "AuthenticationSucceededWithUserName": "{0} todennettu onnistuneesti", - "Artists": "Artistit", + "AuthenticationSucceededWithUserName": "{0} todennus onnistui", + "Artists": "Esiintyjät", "Application": "Sovellus", "AppDeviceValues": "Sovellus: {0}, Laite: {1}", "Albums": "Albumit", @@ -76,21 +76,21 @@ "Shows": "Ohjelmat", "ServerNameNeedsToBeRestarted": "{0} vaatii uudelleenkäynnistyksen", "ProviderValue": "Palveluntarjoaja: {0}", - "Plugin": "Laajennus", + "Plugin": "Liitännäinen", "NotificationOptionVideoPlaybackStopped": "Videon toistaminen pysäytetty", "NotificationOptionVideoPlayback": "Videon toistaminen aloitettu", "NotificationOptionUserLockedOut": "Käyttäjä kirjautui ulos", "NotificationOptionTaskFailed": "Ajastetun tehtävän ongelma", "NotificationOptionServerRestartRequired": "Palvelimen uudelleenkäynnistys vaaditaan", - "NotificationOptionPluginUpdateInstalled": "Laajennuksen päivitys asennettu", - "NotificationOptionPluginUninstalled": "Laajennus poistettu", - "NotificationOptionPluginInstalled": "Laajennus asennettu", - "NotificationOptionPluginError": "Ongelma laajennuksessa", - "NotificationOptionNewLibraryContent": "Uusi sisältö lisätty", - "NotificationOptionInstallationFailed": "Asennusvirhe", - "NotificationOptionCameraImageUploaded": "Kameran kuva lisätty", - "NotificationOptionAudioPlaybackStopped": "Äänen toistaminen pysäytetty", - "NotificationOptionAudioPlayback": "Äänen toistaminen aloitettu", + "NotificationOptionPluginUpdateInstalled": "Liitännäinen päivitetty", + "NotificationOptionPluginUninstalled": "Liitännäinen poistettu", + "NotificationOptionPluginInstalled": "Liitännäinen asennettu", + "NotificationOptionPluginError": "Ongelma liitännäisessä", + "NotificationOptionNewLibraryContent": "Uutta sisältöä lisätty", + "NotificationOptionInstallationFailed": "Asennus epäonnistui", + "NotificationOptionCameraImageUploaded": "Kuva ladattu kamerasta", + "NotificationOptionAudioPlaybackStopped": "Audion toisto pysäytetty", + "NotificationOptionAudioPlayback": "Audion toisto aloitettu", "NotificationOptionApplicationUpdateInstalled": "Ohjelmistopäivitys asennettu", "NotificationOptionApplicationUpdateAvailable": "Ohjelmistopäivitys saatavilla" } diff --git a/Emby.Server.Implementations/Localization/Core/he.json b/Emby.Server.Implementations/Localization/Core/he.json index b08c8966e2..5618719dd3 100644 --- a/Emby.Server.Implementations/Localization/Core/he.json +++ b/Emby.Server.Implementations/Localization/Core/he.json @@ -2,33 +2,33 @@ "Albums": "אלבומים", "AppDeviceValues": "יישום: {0}, מכשיר: {1}", "Application": "אפליקציה", - "Artists": "אמנים", - "AuthenticationSucceededWithUserName": "{0} זוהה בהצלחה", + "Artists": "אומנים", + "AuthenticationSucceededWithUserName": "{0} אומת בהצלחה", "Books": "ספרים", - "CameraImageUploadedFrom": "תמונה חדשה הועלתה מ{0}", + "CameraImageUploadedFrom": "תמונת מצלמה חדשה הועלתה מ {0}", "Channels": "ערוצים", "ChapterNameValue": "פרק {0}", - "Collections": "קולקציות", + "Collections": "אוספים", "DeviceOfflineWithName": "{0} התנתק", "DeviceOnlineWithName": "{0} מחובר", "FailedLoginAttemptWithUserName": "ניסיון כניסה שגוי מ{0}", - "Favorites": "אהובים", + "Favorites": "מועדפים", "Folders": "תיקיות", "Genres": "ז'אנרים", "HeaderAlbumArtists": "אמני האלבום", "HeaderCameraUploads": "העלאות ממצלמה", "HeaderContinueWatching": "המשך לצפות", "HeaderFavoriteAlbums": "אלבומים שאהבתי", - "HeaderFavoriteArtists": "אמנים שאהבתי", - "HeaderFavoriteEpisodes": "פרקים אהובים", - "HeaderFavoriteShows": "תוכניות אהובות", - "HeaderFavoriteSongs": "שירים שאהבתי", - "HeaderLiveTV": "טלוויזיה בשידור חי", + "HeaderFavoriteArtists": "אמנים מועדפים", + "HeaderFavoriteEpisodes": "פרקים מועדפים", + "HeaderFavoriteShows": "סדרות מועדפות", + "HeaderFavoriteSongs": "שירים מועדפים", + "HeaderLiveTV": "שידורים חיים", "HeaderNextUp": "הבא", "HeaderRecordingGroups": "קבוצות הקלטה", "HomeVideos": "סרטונים בייתים", "Inherit": "הורש", - "ItemAddedWithName": "{0} was added to the library", + "ItemAddedWithName": "{0} הוסף לספרייה", "ItemRemovedWithName": "{0} נמחק מהספרייה", "LabelIpAddressValue": "Ip כתובת: {0}", "LabelRunningTimeValue": "משך צפייה: {0}", @@ -36,15 +36,15 @@ "MessageApplicationUpdated": "שרת הJellyfin עודכן", "MessageApplicationUpdatedTo": "שרת הJellyfin עודכן לגרסא {0}", "MessageNamedServerConfigurationUpdatedWithValue": "הגדרת השרת {0} שונתה", - "MessageServerConfigurationUpdated": "Server configuration has been updated", + "MessageServerConfigurationUpdated": "תצורת השרת עודכנה", "MixedContent": "תוכן מעורב", "Movies": "סרטים", "Music": "מוזיקה", - "MusicVideos": "Music videos", - "NameInstallFailed": "{0} installation failed", - "NameSeasonNumber": "Season {0}", - "NameSeasonUnknown": "Season Unknown", - "NewVersionIsAvailable": "A new version of Jellyfin Server is available for download.", + "MusicVideos": "קליפים", + "NameInstallFailed": "התקנת {0} נכשלה", + "NameSeasonNumber": "עונה {0}", + "NameSeasonUnknown": "עונה לא ידועה", + "NewVersionIsAvailable": "גרסה חדשה של שרת Jellyfin זמינה להורדה.", "NotificationOptionApplicationUpdateAvailable": "Application update available", "NotificationOptionApplicationUpdateInstalled": "Application update installed", "NotificationOptionAudioPlayback": "Audio playback started", @@ -53,10 +53,10 @@ "NotificationOptionInstallationFailed": "התקנה נכשלה", "NotificationOptionNewLibraryContent": "New content added", "NotificationOptionPluginError": "Plugin failure", - "NotificationOptionPluginInstalled": "Plugin installed", - "NotificationOptionPluginUninstalled": "Plugin uninstalled", - "NotificationOptionPluginUpdateInstalled": "Plugin update installed", - "NotificationOptionServerRestartRequired": "Server restart required", + "NotificationOptionPluginInstalled": "התוסף הותקן", + "NotificationOptionPluginUninstalled": "התוסף הוסר", + "NotificationOptionPluginUpdateInstalled": "העדכון לתוסף הותקן", + "NotificationOptionServerRestartRequired": "יש לאתחל את השרת", "NotificationOptionTaskFailed": "Scheduled task failure", "NotificationOptionUserLockedOut": "User locked out", "NotificationOptionVideoPlayback": "Video playback started", @@ -71,26 +71,26 @@ "ScheduledTaskFailedWithName": "{0} failed", "ScheduledTaskStartedWithName": "{0} started", "ServerNameNeedsToBeRestarted": "{0} needs to be restarted", - "Shows": "Shows", - "Songs": "Songs", - "StartupEmbyServerIsLoading": "Jellyfin Server is loading. Please try again shortly.", + "Shows": "סדרות", + "Songs": "שירים", + "StartupEmbyServerIsLoading": "שרת Jellyfin בהליכי טעינה. אנא נסה שנית בעוד זמן קצר.", "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}", "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}", "SubtitlesDownloadedForItem": "Subtitles downloaded for {0}", "Sync": "סנכרן", "System": "System", - "TvShows": "TV Shows", + "TvShows": "סדרות טלוויזיה", "User": "User", - "UserCreatedWithName": "User {0} has been created", - "UserDeletedWithName": "User {0} has been deleted", - "UserDownloadingItemWithValues": "{0} is downloading {1}", + "UserCreatedWithName": "המשתמש {0} נוצר", + "UserDeletedWithName": "המשתמש {0} הוסר", + "UserDownloadingItemWithValues": "{0} מוריד את {1}", "UserLockedOutWithName": "User {0} has been locked out", "UserOfflineFromDevice": "{0} has disconnected from {1}", "UserOnlineFromDevice": "{0} is online from {1}", "UserPasswordChangedWithName": "Password has been changed for user {0}", "UserPolicyUpdatedWithName": "User policy has been updated for {0}", - "UserStartedPlayingItemWithValues": "{0} is playing {1} on {2}", - "UserStoppedPlayingItemWithValues": "{0} has finished playing {1} on {2}", + "UserStartedPlayingItemWithValues": "{0} מנגן את {1} על {2}", + "UserStoppedPlayingItemWithValues": "{0} סיים לנגן את {1} על {2}", "ValueHasBeenAddedToLibrary": "{0} has been added to your media library", "ValueSpecialEpisodeName": "מיוחד- {0}", "VersionNumber": "Version {0}" diff --git a/Emby.Server.Implementations/Localization/Core/hu.json b/Emby.Server.Implementations/Localization/Core/hu.json index 950be68d03..6017aa7f90 100644 --- a/Emby.Server.Implementations/Localization/Core/hu.json +++ b/Emby.Server.Implementations/Localization/Core/hu.json @@ -11,7 +11,7 @@ "Collections": "Gyűjtemények", "DeviceOfflineWithName": "{0} kijelentkezett", "DeviceOnlineWithName": "{0} belépett", - "FailedLoginAttemptWithUserName": "Sikertelen bejelentkezési kísérlet {0}", + "FailedLoginAttemptWithUserName": "Sikertelen bejelentkezési kísérlet tőle: {0}", "Favorites": "Kedvencek", "Folders": "Könyvtárak", "Genres": "Műfajok", @@ -27,7 +27,7 @@ "HeaderNextUp": "Következik", "HeaderRecordingGroups": "Felvételi csoportok", "HomeVideos": "Házi videók", - "Inherit": "Öröklés", + "Inherit": "Örökölt", "ItemAddedWithName": "{0} hozzáadva a könyvtárhoz", "ItemRemovedWithName": "{0} eltávolítva a könyvtárból", "LabelIpAddressValue": "IP cím: {0}", @@ -73,7 +73,7 @@ "ServerNameNeedsToBeRestarted": "{0}-t újra kell indítani", "Shows": "Műsorok", "Songs": "Dalok", - "StartupEmbyServerIsLoading": "A Jellyfin Szerver betöltődik. Kérlek próbáld újra később.", + "StartupEmbyServerIsLoading": "A Jellyfin Szerver betöltődik. Kérlek, próbáld újra hamarosan.", "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}", "SubtitleDownloadFailureFromForItem": "Nem sikerült a felirat letöltése innen: {0} ehhez: {1}", "SubtitlesDownloadedForItem": "Letöltött feliratok a következőhöz: {0}", @@ -86,11 +86,11 @@ "UserDownloadingItemWithValues": "{0} letölti {1}", "UserLockedOutWithName": "{0} felhasználó zárolva van", "UserOfflineFromDevice": "{0} kijelentkezett innen: {1}", - "UserOnlineFromDevice": "{0} online itt: {1}", + "UserOnlineFromDevice": "{0} online innen: {1}", "UserPasswordChangedWithName": "Jelszó megváltozott a következő felhasználó számára: {0}", "UserPolicyUpdatedWithName": "A felhasználói házirend frissítve lett neki: {0}", "UserStartedPlayingItemWithValues": "{0} elkezdte játszani a következőt: {1} itt: {2}", - "UserStoppedPlayingItemWithValues": "{0} befejezte a következőt: {1} itt: {2}", + "UserStoppedPlayingItemWithValues": "{0} befejezte {1} lejátászását itt: {2}", "ValueHasBeenAddedToLibrary": "{0} hozzáadva a médiatárhoz", "ValueSpecialEpisodeName": "Special - {0}", "VersionNumber": "Verzió: {0}" diff --git a/Emby.Server.Implementations/Localization/Core/id.json b/Emby.Server.Implementations/Localization/Core/id.json index 38c6cf54f6..68fffbf0ad 100644 --- a/Emby.Server.Implementations/Localization/Core/id.json +++ b/Emby.Server.Implementations/Localization/Core/id.json @@ -91,5 +91,6 @@ "NotificationOptionVideoPlayback": "Pemutaran video dimulai", "NotificationOptionAudioPlaybackStopped": "Pemutaran audio berhenti", "NotificationOptionAudioPlayback": "Pemutaran audio dimulai", - "MixedContent": "Konten campur" + "MixedContent": "Konten campur", + "PluginUninstalledWithName": "{0} telah dihapus" } diff --git a/Emby.Server.Implementations/Localization/Core/it.json b/Emby.Server.Implementations/Localization/Core/it.json index c1c40c18e1..395924af4b 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 {0}", + "CameraImageUploadedFrom": "È stata caricata una nuova immagine della fotocamera da {0}", "Channels": "Canali", "ChapterNameValue": "Capitolo {0}", "Collections": "Collezioni", @@ -18,7 +18,7 @@ "HeaderAlbumArtists": "Artisti dell' Album", "HeaderCameraUploads": "Caricamenti Fotocamera", "HeaderContinueWatching": "Continua a guardare", - "HeaderFavoriteAlbums": "Album preferiti", + "HeaderFavoriteAlbums": "Album Preferiti", "HeaderFavoriteArtists": "Artisti Preferiti", "HeaderFavoriteEpisodes": "Episodi Preferiti", "HeaderFavoriteShows": "Serie TV Preferite", diff --git a/Emby.Server.Implementations/Localization/Core/lv.json b/Emby.Server.Implementations/Localization/Core/lv.json new file mode 100644 index 0000000000..8b8d46b2e0 --- /dev/null +++ b/Emby.Server.Implementations/Localization/Core/lv.json @@ -0,0 +1,96 @@ +{ + "ServerNameNeedsToBeRestarted": "{0} ir vajadzīgs restarts", + "NotificationOptionTaskFailed": "Plānota uzdevuma kļūme", + "HeaderRecordingGroups": "Ierakstu Grupas", + "UserPolicyUpdatedWithName": "Lietotāju politika atjaunota priekš {0}", + "SubtitleDownloadFailureFromForItem": "Subtitru lejupielāde no {0} priekš {1} neizdevās", + "NotificationOptionVideoPlaybackStopped": "Video atskaņošana apturēta", + "NotificationOptionVideoPlayback": "Video atskaņošana sākta", + "NotificationOptionInstallationFailed": "Instalācija neizdevās", + "AuthenticationSucceededWithUserName": "{0} veiksmīgi autentificējies", + "ValueSpecialEpisodeName": "Speciālais - {0}", + "ScheduledTaskStartedWithName": "{0} iesākts", + "ScheduledTaskFailedWithName": "{0} neizdevās", + "Photos": "Attēli", + "NotificationOptionUserLockedOut": "Lietotājs bloķēts", + "LabelRunningTimeValue": "Garums: {0}", + "Inherit": "Mantot", + "AppDeviceValues": "Lietotne:{0}, Ierīce:{1}", + "VersionNumber": "Versija {0}", + "ValueHasBeenAddedToLibrary": "{0} ir ticis pievienots tavai multvides bibliotēkai", + "UserStoppedPlayingItemWithValues": "{0} ir beidzis atskaņot {1} uz {2}", + "UserStartedPlayingItemWithValues": "{0} atskaņo {1} uz {2}", + "UserPasswordChangedWithName": "Parole nomainīta lietotājam {0}", + "UserOnlineFromDevice": "{0} ir tiešsaistē no {1}", + "UserOfflineFromDevice": "{0} ir atvienojies no {1}", + "UserLockedOutWithName": "Lietotājs {0} ir ticis bloķēts", + "UserDownloadingItemWithValues": "{0} lejupielādē {1}", + "UserDeletedWithName": "Lietotājs {0} ir izdzēsts", + "UserCreatedWithName": "Lietotājs {0} ir ticis izveidots", + "User": "Lietotājs", + "TvShows": "TV Raidījumi", + "Sync": "Sinhronizācija", + "System": "Sistēma", + "SubtitlesDownloadedForItem": "Subtitri lejupielādēti priekš {0}", + "StartupEmbyServerIsLoading": "Jellyfin Serveris lādējas. Lūdzu mēģiniet vēlreiz pēc brīža.", + "Songs": "Dziesmas", + "Shows": "Raidījumi", + "PluginUpdatedWithName": "{0} tika atjaunots", + "PluginUninstalledWithName": "{0} tika noņemts", + "PluginInstalledWithName": "{0} tika uzstādīts", + "Plugin": "Paplašinājums", + "Playlists": "Atskaņošanas Saraksti", + "MixedContent": "Jaukts saturs", + "HomeVideos": "Mājas Video", + "HeaderNextUp": "Nākamais", + "ChapterNameValue": "Nodaļa {0}", + "Application": "Lietotne", + "NotificationOptionServerRestartRequired": "Vajadzīgs servera restarts", + "NotificationOptionPluginUpdateInstalled": "Paplašinājuma atjauninājums uzstādīts", + "NotificationOptionPluginUninstalled": "Paplašinājums noņemts", + "NotificationOptionPluginInstalled": "Paplašinājums uzstādīts", + "NotificationOptionPluginError": "Paplašinājuma kļūda", + "NotificationOptionNewLibraryContent": "Jauns saturs pievienots", + "NotificationOptionCameraImageUploaded": "Kameras attēls augšupielādēts", + "NotificationOptionAudioPlaybackStopped": "Audio atskaņošana apturēta", + "NotificationOptionAudioPlayback": "Audio atskaņošana sākta", + "NotificationOptionApplicationUpdateInstalled": "Lietotnes atjauninājums uzstādīts", + "NotificationOptionApplicationUpdateAvailable": "Lietotnes atjauninājums pieejams", + "NewVersionIsAvailable": "Lejupielādei ir pieejama jauna Jellyfin Server versija.", + "NameSeasonUnknown": "Nezināma Sezona", + "NameSeasonNumber": "Sezona {0}", + "NameInstallFailed": "{0} instalācija neizdevās", + "MusicVideos": "Mūzikas video", + "Music": "Mūzika", + "Movies": "Filmas", + "MessageServerConfigurationUpdated": "Servera konfigurācija ir tikusi atjaunota", + "MessageNamedServerConfigurationUpdatedWithValue": "Servera konfigurācijas sadaļa {0} ir tikusi atjaunota", + "MessageApplicationUpdatedTo": "Jellyfin Server ir ticis atjaunots uz {0}", + "MessageApplicationUpdated": "Jellyfin Server ir ticis atjaunots", + "Latest": "Jaunākais", + "LabelIpAddressValue": "IP adrese: {0}", + "ItemRemovedWithName": "{0} tika noņemts no bibliotēkas", + "ItemAddedWithName": "{0} tika pievienots bibliotēkai", + "HeaderLiveTV": "Tiešraides TV", + "HeaderContinueWatching": "Turpināt Skatīšanos", + "HeaderCameraUploads": "Kameras augšupielādes", + "HeaderAlbumArtists": "Albumu Izpildītāji", + "Genres": "Žanri", + "Folders": "Mapes", + "Favorites": "Favorīti", + "FailedLoginAttemptWithUserName": "Neizdevies pieslēgšanās mēģinājums no {0}", + "DeviceOnlineWithName": "{0} ir pievienojies", + "DeviceOfflineWithName": "{0} ir atvienojies", + "Collections": "Kolekcijas", + "Channels": "Kanāli", + "CameraImageUploadedFrom": "Jauns kameras attēls ir ticis augšupielādēts no {0}", + "Books": "Grāmatas", + "Artists": "Izpildītāji", + "Albums": "Albumi", + "ProviderValue": "Provider: {0}", + "HeaderFavoriteSongs": "Dziesmu Favorīti", + "HeaderFavoriteShows": "Raidījumu Favorīti", + "HeaderFavoriteEpisodes": "Episožu Favorīti", + "HeaderFavoriteArtists": "Izpildītāju Favorīti", + "HeaderFavoriteAlbums": "Albumu Favorīti" +} diff --git a/Emby.Server.Implementations/Localization/Core/mk.json b/Emby.Server.Implementations/Localization/Core/mk.json new file mode 100644 index 0000000000..684a97aade --- /dev/null +++ b/Emby.Server.Implementations/Localization/Core/mk.json @@ -0,0 +1,96 @@ +{ + "ScheduledTaskFailedWithName": "{0} неуспешно", + "ProviderValue": "Провајдер: {0}", + "PluginUpdatedWithName": "{0} беше надоградено", + "PluginUninstalledWithName": "{0} беше успешно деинсталирано", + "PluginInstalledWithName": "{0} беше успешно инсталирано", + "Plugin": "Додатоци", + "Playlists": "Листи", + "Photos": "Слики", + "NotificationOptionVideoPlaybackStopped": "Видео стопирано", + "NotificationOptionVideoPlayback": "Видео пуштено", + "NotificationOptionUserLockedOut": "Корисникот е ослободен", + "NotificationOptionTaskFailed": "Закажани задачи неуспешно", + "NotificationOptionServerRestartRequired": "Задолжително рестартирање на серверот", + "NotificationOptionPluginUpdateInstalled": "Надоградба на Додаток успешна", + "NotificationOptionPluginUninstalled": "Додаток успешно деинсталиран", + "NotificationOptionPluginInstalled": "Додаток успешно инсталиран", + "NotificationOptionPluginError": "Грешка на додаток", + "NotificationOptionNewLibraryContent": "Додадена нова содржина", + "NotificationOptionInstallationFailed": "Неуспешна Инсталација", + "NotificationOptionCameraImageUploaded": "Слика од камера поставена", + "NotificationOptionAudioPlaybackStopped": "Аудио стопирано", + "NotificationOptionAudioPlayback": "Аудио стартувано", + "NotificationOptionApplicationUpdateInstalled": "Надоградбата на Апликацијата е иснталирана", + "NotificationOptionApplicationUpdateAvailable": "Возможна надоградба на Апликацијата", + "NewVersionIsAvailable": "Нова верзија од Jellyfin е возможна за спуштање.", + "NameSeasonUnknown": "Непозната Сезона", + "NameSeasonNumber": "Сезона {0}", + "NameInstallFailed": "{0} неуспешна инсталација", + "MusicVideos": "Музички видеа", + "Music": "Музика", + "Movies": "Филмови", + "MixedContent": "Мешана содржина", + "MessageServerConfigurationUpdated": "Серверската конфигурација беше надградена", + "MessageNamedServerConfigurationUpdatedWithValue": "Секцијата на конфигурација на сервер {0} беше надоградена", + "MessageApplicationUpdatedTo": "Jellyfin беше надограден до {0}", + "MessageApplicationUpdated": "Jellyfin Серверот беше надограден", + "Latest": "Последно", + "LabelRunningTimeValue": "Време на работа: {0}", + "LabelIpAddressValue": "ИП Адреса: {0}", + "ItemRemovedWithName": "{0} е избришано до Библиотеката", + "ItemAddedWithName": "{0} беше додадено во Библиотеката", + "Inherit": "Следно", + "HomeVideos": "Домашни Видеа", + "HeaderRecordingGroups": "Групи на снимање", + "HeaderNextUp": "Следно", + "HeaderLiveTV": "ТВ", + "HeaderFavoriteSongs": "Омилени Песни", + "HeaderFavoriteShows": "Омилени Серии", + "HeaderFavoriteEpisodes": "Омилени Епизоди", + "HeaderFavoriteArtists": "Омилени Изведувачи", + "HeaderFavoriteAlbums": "Омилени Албуми", + "HeaderContinueWatching": "Продолжи со гледање", + "HeaderCameraUploads": "Поставувања од камера", + "HeaderAlbumArtists": "Изведувачи од Албуми", + "Genres": "Жанрови", + "Folders": "Папки", + "Favorites": "Омилени", + "FailedLoginAttemptWithUserName": "Неуспешно поврзување од {0}", + "DeviceOnlineWithName": "{0} е приклучен", + "DeviceOfflineWithName": "{0} се исклучи", + "Collections": "Колекции", + "ChapterNameValue": "Дел {0}", + "Channels": "Канали", + "CameraImageUploadedFrom": "Нова слика од камера беше поставена од {0}", + "Books": "Книги", + "AuthenticationSucceededWithUserName": "{0} успешно поврзан", + "Artists": "Изведувач", + "Application": "Апликација", + "AppDeviceValues": "Аплиакција: {0}, Уред: {1}", + "Albums": "Албуми", + "VersionNumber": "Верзија {0}", + "ValueSpecialEpisodeName": "Специјално - {0}", + "ValueHasBeenAddedToLibrary": "{0} е додадено во твојата библиотека", + "UserStoppedPlayingItemWithValues": "{0} заврши со репродукција {1} во {2}", + "UserStartedPlayingItemWithValues": "{0} пушти {1} на {2}", + "UserPolicyUpdatedWithName": "Полисата на користење беше надоградена за {0}", + "UserPasswordChangedWithName": "Лозинката е сменета за корисникот {0}", + "UserOnlineFromDevice": "{0} е приклучен од {1}", + "UserOfflineFromDevice": "{0} е дисконектиран од {1}", + "UserLockedOutWithName": "Корисникот {0} е заклучен", + "UserDownloadingItemWithValues": "{0} се спушта {1}", + "UserDeletedWithName": "Корисникот {0} е избришан", + "UserCreatedWithName": "Корисникот {0} е креиран", + "User": "Корисник", + "TvShows": "ТВ Серии", + "System": "Систем", + "Sync": "Синхронизација", + "SubtitlesDownloadedForItem": "Спуштање превод за {0}", + "SubtitleDownloadFailureFromForItem": "Преводот неуспешно се спушти од {0} за {1}", + "StartupEmbyServerIsLoading": "Jellyfin Server се пушта. Ве молиме причекајте.", + "Songs": "Песни", + "Shows": "Серии", + "ServerNameNeedsToBeRestarted": "{0} треба да се рестартира", + "ScheduledTaskStartedWithName": "{0} започна" +} diff --git a/Emby.Server.Implementations/Localization/Core/nl.json b/Emby.Server.Implementations/Localization/Core/nl.json index 4423b7f988..e22f95ab46 100644 --- a/Emby.Server.Implementations/Localization/Core/nl.json +++ b/Emby.Server.Implementations/Localization/Core/nl.json @@ -3,13 +3,13 @@ "AppDeviceValues": "App: {0}, Apparaat: {1}", "Application": "Applicatie", "Artists": "Artiesten", - "AuthenticationSucceededWithUserName": "{0} is succesvol geverifieerd", + "AuthenticationSucceededWithUserName": "{0} succesvol geauthenticeerd", "Books": "Boeken", "CameraImageUploadedFrom": "Er is een nieuwe foto toegevoegd van {0}", "Channels": "Kanalen", "ChapterNameValue": "Hoofdstuk {0}", "Collections": "Verzamelingen", - "DeviceOfflineWithName": "{0} heeft de verbinding verbroken", + "DeviceOfflineWithName": "Verbinding met {0} is verbroken", "DeviceOnlineWithName": "{0} is verbonden", "FailedLoginAttemptWithUserName": "Mislukte aanmeld poging van {0}", "Favorites": "Favorieten", diff --git a/Emby.Server.Implementations/Localization/Core/pt.json b/Emby.Server.Implementations/Localization/Core/pt.json index ef8d988c87..9ee3c37a8a 100644 --- a/Emby.Server.Implementations/Localization/Core/pt.json +++ b/Emby.Server.Implementations/Localization/Core/pt.json @@ -1,5 +1,5 @@ { - "HeaderLiveTV": "TV ao Vivo", + "HeaderLiveTV": "TV em Directo", "Collections": "Colecções", "Books": "Livros", "Artists": "Artistas", @@ -10,13 +10,13 @@ "HeaderFavoriteAlbums": "Álbuns Favoritos", "HeaderFavoriteEpisodes": "Episódios Favoritos", "HeaderFavoriteShows": "Séries Favoritas", - "HeaderContinueWatching": "Continuar a Ver", + "HeaderContinueWatching": "Continuar a Assistir", "HeaderAlbumArtists": "Artistas do Álbum", "Genres": "Géneros", - "Folders": "Pastas", + "Folders": "Directórios", "Favorites": "Favoritos", "Channels": "Canais", - "UserDownloadingItemWithValues": "{0} está a transferir {1}", + "UserDownloadingItemWithValues": "{0} está a ser transferido {1}", "VersionNumber": "Versão {0}", "ValueHasBeenAddedToLibrary": "{0} foi adicionado à sua biblioteca multimédia", "UserStoppedPlayingItemWithValues": "{0} terminou a reprodução de {1} em {2}", @@ -24,12 +24,12 @@ "UserPolicyUpdatedWithName": "A política do utilizador {0} foi alterada", "UserPasswordChangedWithName": "A palavra-passe do utilizador {0} foi alterada", "UserOnlineFromDevice": "{0} ligou-se a partir de {1}", - "UserOfflineFromDevice": "{0} desligou-se a partir de {1}", - "UserLockedOutWithName": "Utilizador {0} bloqueado", - "UserDeletedWithName": "Utilizador {0} removido", - "UserCreatedWithName": "Utilizador {0} criado", + "UserOfflineFromDevice": "{0} desconectou-se a partir de {1}", + "UserLockedOutWithName": "O utilizador {0} foi bloqueado", + "UserDeletedWithName": "O utilizador {0} foi removido", + "UserCreatedWithName": "O utilizador {0} foi criado", "User": "Utilizador", - "TvShows": "Programas", + "TvShows": "Séries", "System": "Sistema", "SubtitlesDownloadedForItem": "Legendas transferidas para {0}", "SubtitleDownloadFailureFromForItem": "Falha na transferência de legendas de {0} para {1}", @@ -38,22 +38,22 @@ "ScheduledTaskStartedWithName": "{0} iniciou", "ScheduledTaskFailedWithName": "{0} falhou", "ProviderValue": "Fornecedor: {0}", - "PluginUpdatedWithName": "{0} foi actualizado", + "PluginUpdatedWithName": "{0} foi atualizado", "PluginUninstalledWithName": "{0} foi desinstalado", "PluginInstalledWithName": "{0} foi instalado", - "Plugin": "Extensão", + "Plugin": "Plugin", "NotificationOptionVideoPlaybackStopped": "Reprodução de vídeo parada", "NotificationOptionVideoPlayback": "Reprodução de vídeo iniciada", "NotificationOptionUserLockedOut": "Utilizador bloqueado", "NotificationOptionTaskFailed": "Falha em tarefa agendada", "NotificationOptionServerRestartRequired": "É necessário reiniciar o servidor", - "NotificationOptionPluginUpdateInstalled": "Extensão actualizada", - "NotificationOptionPluginUninstalled": "Extensão desinstalada", - "NotificationOptionPluginInstalled": "Extensão instalada", - "NotificationOptionPluginError": "Falha na extensão", + "NotificationOptionPluginUpdateInstalled": "Plugin actualizado", + "NotificationOptionPluginUninstalled": "Plugin desinstalado", + "NotificationOptionPluginInstalled": "Plugin instalado", + "NotificationOptionPluginError": "Falha no plugin", "NotificationOptionNewLibraryContent": "Novo conteúdo adicionado", "NotificationOptionInstallationFailed": "Falha de instalação", - "NotificationOptionCameraImageUploaded": "Imagem da câmara enviada", + "NotificationOptionCameraImageUploaded": "Imagem de câmara enviada", "NotificationOptionAudioPlaybackStopped": "Reprodução Parada", "NotificationOptionAudioPlayback": "Reprodução Iniciada", "NotificationOptionApplicationUpdateInstalled": "A actualização da aplicação foi instalada", @@ -66,30 +66,30 @@ "Music": "Música", "MixedContent": "Conteúdo Misto", "MessageServerConfigurationUpdated": "A configuração do servidor foi actualizada", - "MessageNamedServerConfigurationUpdatedWithValue": "Configurações do servidor na secção {0} foram atualizadas", - "MessageApplicationUpdatedTo": "O servidor Jellyfin foi actualizado para a versão {0}", + "MessageNamedServerConfigurationUpdatedWithValue": "As configurações do servidor na secção {0} foram atualizadas", + "MessageApplicationUpdatedTo": "O servidor Jellyfin foi atualizado para a versão {0}", "MessageApplicationUpdated": "O servidor Jellyfin foi actualizado", "Latest": "Mais Recente", "LabelRunningTimeValue": "Duração: {0}", - "LabelIpAddressValue": "Endereço IP: {0}", + "LabelIpAddressValue": "Endereço de IP: {0}", "ItemRemovedWithName": "{0} foi removido da biblioteca", "ItemAddedWithName": "{0} foi adicionado à biblioteca", "Inherit": "Herdar", "HomeVideos": "Vídeos Caseiros", "HeaderRecordingGroups": "Grupos de Gravação", - "ValueSpecialEpisodeName": "Especial - {0}", + "ValueSpecialEpisodeName": "Episódio Especial - {0}", "Sync": "Sincronização", "Songs": "Músicas", "Shows": "Séries", "Playlists": "Listas de Reprodução", "Photos": "Fotografias", "Movies": "Filmes", - "HeaderCameraUploads": "Envios a partir da câmara", - "FailedLoginAttemptWithUserName": "Tentativa de ligação a partir de {0} falhou", - "DeviceOnlineWithName": "{0} ligou-se", - "DeviceOfflineWithName": "{0} desligou-se", + "HeaderCameraUploads": "Carregamentos a partir da câmara", + "FailedLoginAttemptWithUserName": "Tentativa de ligação falhada a partir de {0}", + "DeviceOnlineWithName": "{0} está connectado", + "DeviceOfflineWithName": "{0} desconectou-se", "ChapterNameValue": "Capítulo {0}", - "CameraImageUploadedFrom": "Uma nova imagem de câmara foi enviada a partir de {0}", + "CameraImageUploadedFrom": "Uma nova imagem da câmara foi enviada a partir de {0}", "AuthenticationSucceededWithUserName": "{0} autenticado com sucesso", "Application": "Aplicação", "AppDeviceValues": "Aplicação {0}, Dispositivo: {1}" diff --git a/Emby.Server.Implementations/Localization/Core/sv.json b/Emby.Server.Implementations/Localization/Core/sv.json index db4cfde957..b2934545d3 100644 --- a/Emby.Server.Implementations/Localization/Core/sv.json +++ b/Emby.Server.Implementations/Localization/Core/sv.json @@ -1,7 +1,7 @@ { "Albums": "Album", - "AppDeviceValues": "App: {0}, Enhet: {1}", - "Application": "App", + "AppDeviceValues": "Applikation: {0}, Enhet: {1}", + "Application": "Applikation", "Artists": "Artister", "AuthenticationSucceededWithUserName": "{0} har autentiserats", "Books": "Böcker", @@ -16,7 +16,7 @@ "Folders": "Mappar", "Genres": "Genrer", "HeaderAlbumArtists": "Albumartister", - "HeaderCameraUploads": "Kamera Uppladdningar", + "HeaderCameraUploads": "Kamerauppladdningar", "HeaderContinueWatching": "Fortsätt kolla", "HeaderFavoriteAlbums": "Favoritalbum", "HeaderFavoriteArtists": "Favoritartister", @@ -34,9 +34,9 @@ "LabelRunningTimeValue": "Speltid: {0}", "Latest": "Senaste", "MessageApplicationUpdated": "Jellyfin Server har uppdaterats", - "MessageApplicationUpdatedTo": "Jellyfin Server har uppgraderats till {0}", + "MessageApplicationUpdatedTo": "Jellyfin Server har uppdaterats till {0}", "MessageNamedServerConfigurationUpdatedWithValue": "Serverinställningarna {0} har uppdaterats", - "MessageServerConfigurationUpdated": "Server konfigurationen har uppdaterats", + "MessageServerConfigurationUpdated": "Serverkonfigurationen har uppdaterats", "MixedContent": "Blandat innehåll", "Movies": "Filmer", "Music": "Musik", @@ -44,11 +44,11 @@ "NameInstallFailed": "{0} installationen misslyckades", "NameSeasonNumber": "Säsong {0}", "NameSeasonUnknown": "Okänd säsong", - "NewVersionIsAvailable": "En ny version av Jellyfin Server är klar för nedladdning.", + "NewVersionIsAvailable": "En ny version av Jellyfin Server är tillgänglig att hämta.", "NotificationOptionApplicationUpdateAvailable": "Ny programversion tillgänglig", "NotificationOptionApplicationUpdateInstalled": "Programuppdatering installerad", "NotificationOptionAudioPlayback": "Ljuduppspelning har påbörjats", - "NotificationOptionAudioPlaybackStopped": "Ljuduppspelning stoppad", + "NotificationOptionAudioPlaybackStopped": "Ljuduppspelning stoppades", "NotificationOptionCameraImageUploaded": "Kamerabild har laddats upp", "NotificationOptionInstallationFailed": "Fel vid installation", "NotificationOptionNewLibraryContent": "Nytt innehåll har lagts till", @@ -60,7 +60,7 @@ "NotificationOptionTaskFailed": "Schemalagd aktivitet har misslyckats", "NotificationOptionUserLockedOut": "Användare har låsts ut", "NotificationOptionVideoPlayback": "Videouppspelning har påbörjats", - "NotificationOptionVideoPlaybackStopped": "Videouppspelning stoppad", + "NotificationOptionVideoPlaybackStopped": "Videouppspelning stoppades", "Photos": "Bilder", "Playlists": "Spellistor", "Plugin": "Tillägg", @@ -69,13 +69,13 @@ "PluginUpdatedWithName": "{0} uppdaterades", "ProviderValue": "Källa: {0}", "ScheduledTaskFailedWithName": "{0} misslyckades", - "ScheduledTaskStartedWithName": "{0} startad", + "ScheduledTaskStartedWithName": "{0} startades", "ServerNameNeedsToBeRestarted": "{0} behöver startas om", "Shows": "Serier", "Songs": "Låtar", - "StartupEmbyServerIsLoading": "Jellyfin server arbetar. Pröva igen inom kort.", + "StartupEmbyServerIsLoading": "Jellyfin Server arbetar. Pröva igen snart.", "SubtitleDownloadFailureForItem": "Nerladdning av undertexter för {0} misslyckades", - "SubtitleDownloadFailureFromForItem": "Undertexter misslyckades att ladda ner {0} för {1}", + "SubtitleDownloadFailureFromForItem": "Undertexter kunde inte laddas ner från {0} för {1}", "SubtitlesDownloadedForItem": "Undertexter har laddats ner till {0}", "Sync": "Synk", "System": "System", @@ -89,9 +89,9 @@ "UserOnlineFromDevice": "{0} är uppkopplad från {1}", "UserPasswordChangedWithName": "Lösenordet för {0} har ändrats", "UserPolicyUpdatedWithName": "Användarpolicyn har uppdaterats för {0}", - "UserStartedPlayingItemWithValues": "{0} har börjat spela upp {1}", - "UserStoppedPlayingItemWithValues": "{0} har avslutat uppspelningen av {1}", - "ValueHasBeenAddedToLibrary": "{0} har blivit tillagd till ditt mediabibliotek", + "UserStartedPlayingItemWithValues": "{0} spelar upp {1} på {2}", + "UserStoppedPlayingItemWithValues": "{0} har avslutat uppspelningen av {1} på {2}", + "ValueHasBeenAddedToLibrary": "{0} har lagts till i ditt mediebibliotek", "ValueSpecialEpisodeName": "Specialavsnitt - {0}", "VersionNumber": "Version {0}" } diff --git a/Emby.Server.Implementations/Localization/Core/zh-CN.json b/Emby.Server.Implementations/Localization/Core/zh-CN.json index dd61686145..68134a1510 100644 --- a/Emby.Server.Implementations/Localization/Core/zh-CN.json +++ b/Emby.Server.Implementations/Localization/Core/zh-CN.json @@ -17,14 +17,14 @@ "Genres": "风格", "HeaderAlbumArtists": "专辑作家", "HeaderCameraUploads": "相机上传", - "HeaderContinueWatching": "继续观看", + "HeaderContinueWatching": "继续观影", "HeaderFavoriteAlbums": "收藏的专辑", "HeaderFavoriteArtists": "最爱的艺术家", "HeaderFavoriteEpisodes": "最爱的剧集", "HeaderFavoriteShows": "最爱的节目", "HeaderFavoriteSongs": "最爱的歌曲", "HeaderLiveTV": "电视直播", - "HeaderNextUp": "下一步", + "HeaderNextUp": "接下来", "HeaderRecordingGroups": "录制组", "HomeVideos": "家庭视频", "Inherit": "继承", diff --git a/Emby.Server.Implementations/Playlists/PlaylistManager.cs b/Emby.Server.Implementations/Playlists/PlaylistManager.cs index b26f4026c5..ad7a5005ed 100644 --- a/Emby.Server.Implementations/Playlists/PlaylistManager.cs +++ b/Emby.Server.Implementations/Playlists/PlaylistManager.cs @@ -33,14 +33,14 @@ namespace Emby.Server.Implementations.Playlists ILibraryManager libraryManager, IFileSystem fileSystem, ILibraryMonitor iLibraryMonitor, - ILoggerFactory loggerFactory, + ILogger logger, IUserManager userManager, IProviderManager providerManager) { _libraryManager = libraryManager; _fileSystem = fileSystem; _iLibraryMonitor = iLibraryMonitor; - _logger = loggerFactory.CreateLogger(nameof(PlaylistManager)); + _logger = logger; _userManager = userManager; _providerManager = providerManager; } diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs index b55a59f05a..b7668c872a 100644 --- a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs +++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs @@ -29,7 +29,10 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks /// /// Initializes a new instance of the class. /// - public DeleteCacheFileTask(IApplicationPaths appPaths, ILogger logger, IFileSystem fileSystem) + public DeleteCacheFileTask( + IApplicationPaths appPaths, + ILogger logger, + IFileSystem fileSystem) { ApplicationPaths = appPaths; _logger = logger; diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs index 9ff490f188..37136f4386 100644 --- a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs +++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs @@ -23,7 +23,10 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks /// /// Initializes a new instance of the class. /// - public DeleteTranscodeFileTask(ILogger logger, IFileSystem fileSystem, IConfigurationManager configurationManager) + public DeleteTranscodeFileTask( + ILogger logger, + IFileSystem fileSystem, + IConfigurationManager configurationManager) { _logger = logger; _fileSystem = fileSystem; diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs index 992b77c256..9d87316e43 100644 --- a/Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs +++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs @@ -23,7 +23,7 @@ namespace Emby.Server.Implementations.ScheduledTasks private readonly IInstallationManager _installationManager; - public PluginUpdateTask(ILogger logger, IInstallationManager installationManager) + public PluginUpdateTask(ILogger logger, IInstallationManager installationManager) { _logger = logger; _installationManager = installationManager; diff --git a/Emby.Server.Implementations/SocketSharp/WebSocketSharpListener.cs b/Emby.Server.Implementations/SocketSharp/WebSocketSharpListener.cs index 2e12a19fd9..b85750c9b9 100644 --- a/Emby.Server.Implementations/SocketSharp/WebSocketSharpListener.cs +++ b/Emby.Server.Implementations/SocketSharp/WebSocketSharpListener.cs @@ -21,15 +21,14 @@ namespace Emby.Server.Implementations.SocketSharp private CancellationTokenSource _disposeCancellationTokenSource = new CancellationTokenSource(); private CancellationToken _disposeCancellationToken; - public WebSocketSharpListener( - ILogger logger) + public WebSocketSharpListener(ILogger logger) { _logger = logger; - _disposeCancellationToken = _disposeCancellationTokenSource.Token; } public Func ErrorHandler { get; set; } + public Func RequestHandler { get; set; } public Action WebSocketConnected { get; set; } diff --git a/Emby.Server.Implementations/Sorting/AlphanumComparator.cs b/Emby.Server.Implementations/Sorting/AlphanumComparator.cs deleted file mode 100644 index 2e00c24d75..0000000000 --- a/Emby.Server.Implementations/Sorting/AlphanumComparator.cs +++ /dev/null @@ -1,99 +0,0 @@ -using System.Collections.Generic; -using System.Text; -using MediaBrowser.Controller.Sorting; - -namespace Emby.Server.Implementations.Sorting -{ - public class AlphanumComparator : IComparer - { - public static int CompareValues(string s1, string s2) - { - if (s1 == null || s2 == null) - { - return 0; - } - - int thisMarker = 0, thisNumericChunk = 0; - int thatMarker = 0, thatNumericChunk = 0; - - while ((thisMarker < s1.Length) || (thatMarker < s2.Length)) - { - if (thisMarker >= s1.Length) - { - return -1; - } - else if (thatMarker >= s2.Length) - { - return 1; - } - char thisCh = s1[thisMarker]; - char thatCh = s2[thatMarker]; - - var thisChunk = new StringBuilder(); - var thatChunk = new StringBuilder(); - - while ((thisMarker < s1.Length) && (thisChunk.Length == 0 || SortHelper.InChunk(thisCh, thisChunk[0]))) - { - thisChunk.Append(thisCh); - thisMarker++; - - if (thisMarker < s1.Length) - { - thisCh = s1[thisMarker]; - } - } - - while ((thatMarker < s2.Length) && (thatChunk.Length == 0 || SortHelper.InChunk(thatCh, thatChunk[0]))) - { - thatChunk.Append(thatCh); - thatMarker++; - - if (thatMarker < s2.Length) - { - thatCh = s2[thatMarker]; - } - } - - int result = 0; - // If both chunks contain numeric characters, sort them numerically - if (char.IsDigit(thisChunk[0]) && char.IsDigit(thatChunk[0])) - { - if (!int.TryParse(thisChunk.ToString(), out thisNumericChunk)) - { - return 0; - } - if (!int.TryParse(thatChunk.ToString(), out thatNumericChunk)) - { - return 0; - } - - if (thisNumericChunk < thatNumericChunk) - { - result = -1; - } - - if (thisNumericChunk > thatNumericChunk) - { - result = 1; - } - } - else - { - result = thisChunk.ToString().CompareTo(thatChunk.ToString()); - } - - if (result != 0) - { - return result; - } - } - - return 0; - } - - public int Compare(string x, string y) - { - return CompareValues(x, y); - } - } -} diff --git a/Jellyfin.Server/CoreAppHost.cs b/Jellyfin.Server/CoreAppHost.cs index 8b4b61e290..ed5968ad6f 100644 --- a/Jellyfin.Server/CoreAppHost.cs +++ b/Jellyfin.Server/CoreAppHost.cs @@ -23,23 +23,20 @@ namespace Jellyfin.Server /// The to be used by the . /// The to be used by the . /// The to be used by the . - /// The to be used by the . public CoreAppHost( ServerApplicationPaths applicationPaths, ILoggerFactory loggerFactory, StartupOptions options, IFileSystem fileSystem, IImageEncoder imageEncoder, - INetworkManager networkManager, - IConfiguration configuration) + INetworkManager networkManager) : base( applicationPaths, loggerFactory, options, fileSystem, imageEncoder, - networkManager, - configuration) + networkManager) { } diff --git a/Jellyfin.Server/Migrations/IMigrationRoutine.cs b/Jellyfin.Server/Migrations/IMigrationRoutine.cs new file mode 100644 index 0000000000..eab995d67e --- /dev/null +++ b/Jellyfin.Server/Migrations/IMigrationRoutine.cs @@ -0,0 +1,28 @@ +using System; +using Microsoft.Extensions.Logging; + +namespace Jellyfin.Server.Migrations +{ + /// + /// Interface that describes a migration routine. + /// + internal interface IMigrationRoutine + { + /// + /// Gets the unique id for this migration. This should never be modified after the migration has been created. + /// + public Guid Id { get; } + + /// + /// Gets the display name of the migration. + /// + public string Name { get; } + + /// + /// Execute the migration routine. + /// + /// Host that hosts current version. + /// Host logger. + public void Perform(CoreAppHost host, ILogger logger); + } +} diff --git a/Jellyfin.Server/Migrations/MigrationOptions.cs b/Jellyfin.Server/Migrations/MigrationOptions.cs new file mode 100644 index 0000000000..816dd9ee74 --- /dev/null +++ b/Jellyfin.Server/Migrations/MigrationOptions.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; + +namespace Jellyfin.Server.Migrations +{ + /// + /// Configuration part that holds all migrations that were applied. + /// + public class MigrationOptions + { + /// + /// Initializes a new instance of the class. + /// + public MigrationOptions() + { + Applied = new List<(Guid Id, string Name)>(); + } + + /// + /// Gets the list of applied migration routine names. + /// + public List<(Guid Id, string Name)> Applied { get; } + } +} diff --git a/Jellyfin.Server/Migrations/MigrationRunner.cs b/Jellyfin.Server/Migrations/MigrationRunner.cs new file mode 100644 index 0000000000..b5ea04dcac --- /dev/null +++ b/Jellyfin.Server/Migrations/MigrationRunner.cs @@ -0,0 +1,73 @@ +using System; +using System.Linq; +using MediaBrowser.Common.Configuration; +using Microsoft.Extensions.Logging; + +namespace Jellyfin.Server.Migrations +{ + /// + /// The class that knows which migrations to apply and how to apply them. + /// + public sealed class MigrationRunner + { + /// + /// The list of known migrations, in order of applicability. + /// + internal static readonly IMigrationRoutine[] Migrations = + { + new Routines.DisableTranscodingThrottling(), + new Routines.CreateUserLoggingConfigFile() + }; + + /// + /// Run all needed migrations. + /// + /// CoreAppHost that hosts current version. + /// Factory for making the logger. + public static void Run(CoreAppHost host, ILoggerFactory loggerFactory) + { + var logger = loggerFactory.CreateLogger(); + var migrationOptions = ((IConfigurationManager)host.ServerConfigurationManager).GetConfiguration(MigrationsListStore.StoreKey); + + if (!host.ServerConfigurationManager.Configuration.IsStartupWizardCompleted && migrationOptions.Applied.Count == 0) + { + // If startup wizard is not finished, this is a fresh install. + // Don't run any migrations, just mark all of them as applied. + logger.LogInformation("Marking all known migrations as applied because this is a fresh install"); + migrationOptions.Applied.AddRange(Migrations.Select(m => (m.Id, m.Name))); + host.ServerConfigurationManager.SaveConfiguration(MigrationsListStore.StoreKey, migrationOptions); + return; + } + + var appliedMigrationIds = migrationOptions.Applied.Select(m => m.Id).ToHashSet(); + + for (var i = 0; i < Migrations.Length; i++) + { + var migrationRoutine = Migrations[i]; + if (appliedMigrationIds.Contains(migrationRoutine.Id)) + { + logger.LogDebug("Skipping migration '{Name}' since it is already applied", migrationRoutine.Name); + continue; + } + + logger.LogInformation("Applying migration '{Name}'", migrationRoutine.Name); + + try + { + migrationRoutine.Perform(host, logger); + } + catch (Exception ex) + { + logger.LogError(ex, "Could not apply migration '{Name}'", migrationRoutine.Name); + throw; + } + + // Mark the migration as completed + logger.LogInformation("Migration '{Name}' applied successfully", migrationRoutine.Name); + migrationOptions.Applied.Add((migrationRoutine.Id, migrationRoutine.Name)); + host.ServerConfigurationManager.SaveConfiguration(MigrationsListStore.StoreKey, migrationOptions); + logger.LogDebug("Migration '{Name}' marked as applied in configuration.", migrationRoutine.Name); + } + } + } +} diff --git a/Jellyfin.Server/Migrations/MigrationsFactory.cs b/Jellyfin.Server/Migrations/MigrationsFactory.cs new file mode 100644 index 0000000000..23c1b1ee6f --- /dev/null +++ b/Jellyfin.Server/Migrations/MigrationsFactory.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; +using MediaBrowser.Common.Configuration; + +namespace Jellyfin.Server.Migrations +{ + /// + /// A factory that can find a persistent file of the migration configuration, which lists all applied migrations. + /// + public class MigrationsFactory : IConfigurationFactory + { + /// + public IEnumerable GetConfigurations() + { + return new[] + { + new MigrationsListStore() + }; + } + } +} diff --git a/Jellyfin.Server/Migrations/MigrationsListStore.cs b/Jellyfin.Server/Migrations/MigrationsListStore.cs new file mode 100644 index 0000000000..7a1ca66714 --- /dev/null +++ b/Jellyfin.Server/Migrations/MigrationsListStore.cs @@ -0,0 +1,24 @@ +using MediaBrowser.Common.Configuration; + +namespace Jellyfin.Server.Migrations +{ + /// + /// A configuration that lists all the migration routines that were applied. + /// + public class MigrationsListStore : ConfigurationStore + { + /// + /// The name of the configuration in the storage. + /// + public static readonly string StoreKey = "migrations"; + + /// + /// Initializes a new instance of the class. + /// + public MigrationsListStore() + { + ConfigurationType = typeof(MigrationOptions); + Key = StoreKey; + } + } +} diff --git a/Jellyfin.Server/Migrations/Routines/CreateUserLoggingConfigFile.cs b/Jellyfin.Server/Migrations/Routines/CreateUserLoggingConfigFile.cs new file mode 100644 index 0000000000..3bc32c0478 --- /dev/null +++ b/Jellyfin.Server/Migrations/Routines/CreateUserLoggingConfigFile.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using MediaBrowser.Common.Configuration; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json.Linq; + +namespace Jellyfin.Server.Migrations.Routines +{ + /// + /// Migration to initialize the user logging configuration file "logging.user.json". + /// If the deprecated logging.json file exists and has a custom config, it will be used as logging.user.json, + /// otherwise a blank file will be created. + /// + internal class CreateUserLoggingConfigFile : IMigrationRoutine + { + /// + /// File history for logging.json as existed during this migration creation. The contents for each has been minified. + /// + private readonly List _defaultConfigHistory = new List + { + // 9a6c27947353585391e211aa88b925f81e8cd7b9 + @"{""Serilog"":{""MinimumLevel"":{""Default"":""Information"",""Override"":{""Microsoft"":""Warning"",""System"":""Warning""}},""WriteTo"":[{""Name"":""Console"",""Args"":{""outputTemplate"":""[{Timestamp:HH:mm:ss}] [{Level:u3}] [{ThreadId}] {SourceContext}: {Message:lj}{NewLine}{Exception}""}},{""Name"":""Async"",""Args"":{""configure"":[{""Name"":""File"",""Args"":{""path"":""%JELLYFIN_LOG_DIR%//log_.log"",""rollingInterval"":""Day"",""retainedFileCountLimit"":3,""rollOnFileSizeLimit"":true,""fileSizeLimitBytes"":100000000,""outputTemplate"":""[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] [{ThreadId}] {SourceContext}: {Message}{NewLine}{Exception}""}}]}}],""Enrich"":[""FromLogContext"",""WithThreadId""]}}", + // 71bdcd730705a714ee208eaad7290b7c68df3885 + @"{""Serilog"":{""MinimumLevel"":""Information"",""WriteTo"":[{""Name"":""Console"",""Args"":{""outputTemplate"":""[{Timestamp:HH:mm:ss}] [{Level:u3}] [{ThreadId}] {SourceContext}: {Message:lj}{NewLine}{Exception}""}},{""Name"":""Async"",""Args"":{""configure"":[{""Name"":""File"",""Args"":{""path"":""%JELLYFIN_LOG_DIR%//log_.log"",""rollingInterval"":""Day"",""retainedFileCountLimit"":3,""rollOnFileSizeLimit"":true,""fileSizeLimitBytes"":100000000,""outputTemplate"":""[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] [{ThreadId}] {SourceContext}: {Message}{NewLine}{Exception}""}}]}}],""Enrich"":[""FromLogContext"",""WithThreadId""]}}", + // a44936f97f8afc2817d3491615a7cfe1e31c251c + @"{""Serilog"":{""MinimumLevel"":""Information"",""WriteTo"":[{""Name"":""Console"",""Args"":{""outputTemplate"":""[{Timestamp:HH:mm:ss}] [{Level:u3}] {Message:lj}{NewLine}{Exception}""}},{""Name"":""File"",""Args"":{""path"":""%JELLYFIN_LOG_DIR%//log_.log"",""rollingInterval"":""Day"",""outputTemplate"":""[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] {Message}{NewLine}{Exception}""}}]}}", + // 7af3754a11ad5a4284f107997fb5419a010ce6f3 + @"{""Serilog"":{""MinimumLevel"":""Information"",""WriteTo"":[{""Name"":""Console"",""Args"":{""outputTemplate"":""[{Timestamp:HH:mm:ss}] [{Level:u3}] {Message:lj}{NewLine}{Exception}""}},{""Name"":""Async"",""Args"":{""configure"":[{""Name"":""File"",""Args"":{""path"":""%JELLYFIN_LOG_DIR%//log_.log"",""rollingInterval"":""Day"",""outputTemplate"":""[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] {Message}{NewLine}{Exception}""}}]}}]}}", + // 60691349a11f541958e0b2247c9abc13cb40c9fb + @"{""Serilog"":{""MinimumLevel"":""Information"",""WriteTo"":[{""Name"":""Console"",""Args"":{""outputTemplate"":""[{Timestamp:HH:mm:ss}] [{Level:u3}] {Message:lj}{NewLine}{Exception}""}},{""Name"":""Async"",""Args"":{""configure"":[{""Name"":""File"",""Args"":{""path"":""%JELLYFIN_LOG_DIR%//log_.log"",""rollingInterval"":""Day"",""retainedFileCountLimit"":3,""rollOnFileSizeLimit"":true,""fileSizeLimitBytes"":100000000,""outputTemplate"":""[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] {Message}{NewLine}{Exception}""}}]}}]}}", + // 65fe243afbcc4b596cf8726708c1965cd34b5f68 + @"{""Serilog"":{""MinimumLevel"":""Information"",""WriteTo"":[{""Name"":""Console"",""Args"":{""outputTemplate"":""[{Timestamp:HH:mm:ss}] [{Level:u3}] {ThreadId} {SourceContext}: {Message:lj} {NewLine}{Exception}""}},{""Name"":""Async"",""Args"":{""configure"":[{""Name"":""File"",""Args"":{""path"":""%JELLYFIN_LOG_DIR%//log_.log"",""rollingInterval"":""Day"",""retainedFileCountLimit"":3,""rollOnFileSizeLimit"":true,""fileSizeLimitBytes"":100000000,""outputTemplate"":""[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] {ThreadId} {SourceContext}:{Message} {NewLine}{Exception}""}}]}}],""Enrich"":[""FromLogContext"",""WithThreadId""]}}", + // 96c9af590494aa8137d5a061aaf1e68feee60b67 + @"{""Serilog"":{""MinimumLevel"":""Information"",""WriteTo"":[{""Name"":""Console"",""Args"":{""outputTemplate"":""[{Timestamp:HH:mm:ss}] [{Level:u3}] [{ThreadId}] {SourceContext}: {Message:lj}{NewLine}{Exception}""}},{""Name"":""Async"",""Args"":{""configure"":[{""Name"":""File"",""Args"":{""path"":""%JELLYFIN_LOG_DIR%//log_.log"",""rollingInterval"":""Day"",""retainedFileCountLimit"":3,""rollOnFileSizeLimit"":true,""fileSizeLimitBytes"":100000000,""outputTemplate"":""[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] [{ThreadId}] {SourceContext}:{Message}{NewLine}{Exception}""}}]}}],""Enrich"":[""FromLogContext"",""WithThreadId""]}}", + }; + + /// + public Guid Id => Guid.Parse("{EF103419-8451-40D8-9F34-D1A8E93A1679}"); + + /// + public string Name => "CreateLoggingConfigHeirarchy"; + + /// + public void Perform(CoreAppHost host, ILogger logger) + { + var logDirectory = host.Resolve().ConfigurationDirectoryPath; + var existingConfigPath = Path.Combine(logDirectory, "logging.json"); + + // If the existing logging.json config file is unmodified, then 'reset' it by moving it to 'logging.old.json' + // NOTE: This config file has 'reloadOnChange: true', so this change will take effect immediately even though it has already been loaded + if (File.Exists(existingConfigPath) && ExistingConfigUnmodified(existingConfigPath)) + { + File.Move(existingConfigPath, Path.Combine(logDirectory, "logging.old.json")); + } + } + + /// + /// Check if the existing logging.json file has not been modified by the user by comparing it to all the + /// versions in our git history. Until now, the file has never been migrated after first creation so users + /// could have any version from the git history. + /// + /// does not exist or could not be read. + private bool ExistingConfigUnmodified(string oldConfigPath) + { + var existingConfigJson = JToken.Parse(File.ReadAllText(oldConfigPath)); + return _defaultConfigHistory + .Select(historicalConfigText => JToken.Parse(historicalConfigText)) + .Any(historicalConfigJson => JToken.DeepEquals(existingConfigJson, historicalConfigJson)); + } + } +} diff --git a/Jellyfin.Server/Migrations/Routines/DisableTranscodingThrottling.cs b/Jellyfin.Server/Migrations/Routines/DisableTranscodingThrottling.cs new file mode 100644 index 0000000000..673f0e4155 --- /dev/null +++ b/Jellyfin.Server/Migrations/Routines/DisableTranscodingThrottling.cs @@ -0,0 +1,35 @@ +using System; +using System.IO; +using MediaBrowser.Common.Configuration; +using MediaBrowser.Model.Configuration; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; + +namespace Jellyfin.Server.Migrations.Routines +{ + /// + /// Disable transcode throttling for all installations since it is currently broken for certain video formats. + /// + internal class DisableTranscodingThrottling : IMigrationRoutine + { + /// + public Guid Id => Guid.Parse("{4124C2CD-E939-4FFB-9BE9-9B311C413638}"); + + /// + public string Name => "DisableTranscodingThrottling"; + + /// + public void Perform(CoreAppHost host, ILogger logger) + { + // Set EnableThrottling to false since it wasn't used before and may introduce issues + var encoding = ((IConfigurationManager)host.ServerConfigurationManager).GetConfiguration("encoding"); + if (encoding.EnableThrottling) + { + logger.LogInformation("Disabling transcoding throttling during migration"); + encoding.EnableThrottling = false; + + host.ServerConfigurationManager.SaveConfiguration("encoding", encoding); + } + } + } +} diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index 843eb9ea94..9450fee705 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -27,6 +27,7 @@ using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using Serilog; +using Serilog.Events; using Serilog.Extensions.Logging; using SQLitePCL; using ILogger = Microsoft.Extensions.Logging.ILogger; @@ -38,6 +39,16 @@ namespace Jellyfin.Server /// public static class Program { + /// + /// The name of logging configuration file containing application defaults. + /// + public static readonly string LoggingConfigFileDefault = "logging.default.json"; + + /// + /// The name of the logging configuration file containing the system-specific override settings. + /// + public static readonly string LoggingConfigFileSystem = "logging.json"; + private static readonly CancellationTokenSource _tokenSource = new CancellationTokenSource(); private static readonly ILoggerFactory _loggerFactory = new SerilogLoggerFactory(); private static ILogger _logger = NullLogger.Instance; @@ -102,10 +113,12 @@ namespace Jellyfin.Server // $JELLYFIN_LOG_DIR needs to be set for the logger configuration manager Environment.SetEnvironmentVariable("JELLYFIN_LOG_DIR", appPaths.LogDirectoryPath); - IConfiguration appConfig = await CreateConfiguration(appPaths).ConfigureAwait(false); - - CreateLogger(appConfig, appPaths); + // Create an instance of the application configuration to use for application startup + await InitLoggingConfigFile(appPaths).ConfigureAwait(false); + IConfiguration startupConfig = CreateAppConfiguration(appPaths); + // Initialize logging framework + InitializeLoggingFramework(startupConfig, appPaths); _logger = _loggerFactory.CreateLogger("Main"); // Log uncaught exceptions to the logging instead of std error @@ -170,22 +183,22 @@ namespace Jellyfin.Server options, new ManagedFileSystem(_loggerFactory.CreateLogger(), appPaths), GetImageEncoder(appPaths), - new NetworkManager(_loggerFactory.CreateLogger()), - appConfig); + new NetworkManager(_loggerFactory.CreateLogger())); try { ServiceCollection serviceCollection = new ServiceCollection(); - await appHost.InitAsync(serviceCollection).ConfigureAwait(false); + await appHost.InitAsync(serviceCollection, startupConfig).ConfigureAwait(false); - var host = CreateWebHostBuilder(appHost, serviceCollection, appConfig).Build(); + var webHost = CreateWebHostBuilder(appHost, serviceCollection, startupConfig, appPaths).Build(); // A bit hacky to re-use service provider since ASP.NET doesn't allow a custom service collection. - appHost.ServiceProvider = host.Services; + appHost.ServiceProvider = webHost.Services; appHost.FindParts(); + Migrations.MigrationRunner.Run(appHost, _loggerFactory); try { - await host.StartAsync().ConfigureAwait(false); + await webHost.StartAsync().ConfigureAwait(false); } catch { @@ -221,7 +234,11 @@ namespace Jellyfin.Server } } - private static IWebHostBuilder CreateWebHostBuilder(ApplicationHost appHost, IServiceCollection serviceCollection, IConfiguration appConfig) + private static IWebHostBuilder CreateWebHostBuilder( + ApplicationHost appHost, + IServiceCollection serviceCollection, + IConfiguration startupConfig, + IApplicationPaths appPaths) { var webhostBuilder = new WebHostBuilder() .UseKestrel(options => @@ -261,6 +278,8 @@ namespace Jellyfin.Server } } }) + .ConfigureAppConfiguration(config => config.ConfigureAppConfiguration(appPaths)) + .UseSerilog() .ConfigureServices(services => { // Merge the external ServiceCollection into ASP.NET DI @@ -268,7 +287,7 @@ namespace Jellyfin.Server }) .UseStartup(); - if (!appConfig.IsNoWebContent()) + if (!startupConfig.IsNoWebContent()) { // Fail startup if the web content does not exist if (!Directory.Exists(appHost.ContentRoot) || !Directory.GetFiles(appHost.ContentRoot).Any()) @@ -448,28 +467,39 @@ namespace Jellyfin.Server return new ServerApplicationPaths(dataDir, logDir, configDir, cacheDir, webDir); } - private static async Task CreateConfiguration(IApplicationPaths appPaths) + /// + /// Initialize the logging configuration file using the bundled resource file as a default if it doesn't exist + /// already. + /// + private static async Task InitLoggingConfigFile(IApplicationPaths appPaths) { + // Do nothing if the config file already exists + string configPath = Path.Combine(appPaths.ConfigurationDirectoryPath, LoggingConfigFileDefault); + if (File.Exists(configPath)) + { + return; + } + + // Get a stream of the resource contents + // NOTE: The .csproj name is used instead of the assembly name in the resource path const string ResourcePath = "Jellyfin.Server.Resources.Configuration.logging.json"; - string configPath = Path.Combine(appPaths.ConfigurationDirectoryPath, "logging.json"); + await using Stream? resource = typeof(Program).Assembly.GetManifestResourceStream(ResourcePath) + ?? throw new InvalidOperationException($"Invalid resource path: '{ResourcePath}'"); - if (!File.Exists(configPath)) - { - // For some reason the csproj name is used instead of the assembly name - await using Stream? resource = typeof(Program).Assembly.GetManifestResourceStream(ResourcePath); - if (resource == null) - { - throw new InvalidOperationException( - string.Format( - CultureInfo.InvariantCulture, - "Invalid resource path: '{0}'", - ResourcePath)); - } + // Copy the resource contents to the expected file path for the config file + await using Stream dst = File.Open(configPath, FileMode.CreateNew); + await resource.CopyToAsync(dst).ConfigureAwait(false); + } - await using Stream dst = File.Open(configPath, FileMode.CreateNew); - await resource.CopyToAsync(dst).ConfigureAwait(false); - } + private static IConfiguration CreateAppConfiguration(IApplicationPaths appPaths) + { + return new ConfigurationBuilder() + .ConfigureAppConfiguration(appPaths) + .Build(); + } + private static IConfigurationBuilder ConfigureAppConfiguration(this IConfigurationBuilder config, IApplicationPaths appPaths) + { // Use the swagger API page as the default redirect path if not hosting the jellyfin-web content var inMemoryDefaultConfig = ConfigurationOptions.DefaultConfiguration; if (string.IsNullOrEmpty(appPaths.WebPath)) @@ -477,15 +507,18 @@ namespace Jellyfin.Server inMemoryDefaultConfig[HttpListenerHost.DefaultRedirectKey] = "swagger/index.html"; } - return new ConfigurationBuilder() + return config .SetBasePath(appPaths.ConfigurationDirectoryPath) .AddInMemoryCollection(inMemoryDefaultConfig) - .AddJsonFile("logging.json", false, true) - .AddEnvironmentVariables("JELLYFIN_") - .Build(); + .AddJsonFile(LoggingConfigFileDefault, optional: false, reloadOnChange: true) + .AddJsonFile(LoggingConfigFileSystem, optional: true, reloadOnChange: true) + .AddEnvironmentVariables("JELLYFIN_"); } - private static void CreateLogger(IConfiguration configuration, IApplicationPaths appPaths) + /// + /// Initialize Serilog using configuration and fall back to defaults on failure. + /// + private static void InitializeLoggingFramework(IConfiguration configuration, IApplicationPaths appPaths) { try { diff --git a/Jellyfin.Server/Resources/Configuration/logging.json b/Jellyfin.Server/Resources/Configuration/logging.json index acbca8b856..f64a85219d 100644 --- a/Jellyfin.Server/Resources/Configuration/logging.json +++ b/Jellyfin.Server/Resources/Configuration/logging.json @@ -1,6 +1,12 @@ { "Serilog": { - "MinimumLevel": "Information", + "MinimumLevel": { + "Default": "Information", + "Override": { + "Microsoft": "Warning", + "System": "Warning" + } + }, "WriteTo": [ { "Name": "Console", diff --git a/MediaBrowser.Api/ApiEntryPoint.cs b/MediaBrowser.Api/ApiEntryPoint.cs index 7d3546eb7d..4bd13df003 100644 --- a/MediaBrowser.Api/ApiEntryPoint.cs +++ b/MediaBrowser.Api/ApiEntryPoint.cs @@ -61,7 +61,7 @@ namespace MediaBrowser.Api /// The file system. /// The media source manager. public ApiEntryPoint( - ILogger logger, + ILogger logger, ISessionManager sessionManager, IServerConfigurationManager config, IFileSystem fileSystem, diff --git a/MediaBrowser.Api/Library/LibraryService.cs b/MediaBrowser.Api/Library/LibraryService.cs index 3d1e4a3637..15284958d7 100644 --- a/MediaBrowser.Api/Library/LibraryService.cs +++ b/MediaBrowser.Api/Library/LibraryService.cs @@ -815,7 +815,7 @@ namespace MediaBrowser.Api.Library if (!string.IsNullOrWhiteSpace(filename)) { // Kestrel doesn't support non-ASCII characters in headers - if (Regex.IsMatch(filename, "[^[:ascii:]]")) + if (Regex.IsMatch(filename, @"[^\p{IsBasicLatin}]")) { // Manually encoding non-ASCII characters, following https://tools.ietf.org/html/rfc5987#section-3.2.2 headers[HeaderNames.ContentDisposition] = "attachment; filename*=UTF-8''" + WebUtility.UrlEncode(filename); diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs index a44e1720fe..08a7e534f9 100644 --- a/MediaBrowser.Api/Playback/MediaInfoService.cs +++ b/MediaBrowser.Api/Playback/MediaInfoService.cs @@ -573,7 +573,8 @@ namespace MediaBrowser.Api.Playback { attachment.DeliveryUrl = string.Format( CultureInfo.InvariantCulture, - "/Videos/{0}/{1}/Attachments/{2}", + "{0}/Videos/{1}/{2}/Attachments/{3}", + ServerConfigurationManager.Configuration.BaseUrl, item.Id, mediaSource.Id, attachment.Index); diff --git a/MediaBrowser.Api/ScheduledTasks/ScheduledTasksWebSocketListener.cs b/MediaBrowser.Api/ScheduledTasks/ScheduledTasksWebSocketListener.cs index d9530ffb79..14b9b3618b 100644 --- a/MediaBrowser.Api/ScheduledTasks/ScheduledTasksWebSocketListener.cs +++ b/MediaBrowser.Api/ScheduledTasks/ScheduledTasksWebSocketListener.cs @@ -28,7 +28,7 @@ namespace MediaBrowser.Api.ScheduledTasks /// /// Initializes a new instance of the class. /// - public ScheduledTasksWebSocketListener(ILogger logger, ITaskManager taskManager) + public ScheduledTasksWebSocketListener(ILogger logger, ITaskManager taskManager) : base(logger) { TaskManager = taskManager; diff --git a/MediaBrowser.Api/Sessions/SessionInfoWebSocketListener.cs b/MediaBrowser.Api/Sessions/SessionInfoWebSocketListener.cs index 051d09850a..d882aac887 100644 --- a/MediaBrowser.Api/Sessions/SessionInfoWebSocketListener.cs +++ b/MediaBrowser.Api/Sessions/SessionInfoWebSocketListener.cs @@ -26,7 +26,7 @@ namespace MediaBrowser.Api.Sessions /// /// Initializes a new instance of the class. /// - public SessionInfoWebSocketListener(ILogger logger, ISessionManager sessionManager) + public SessionInfoWebSocketListener(ILogger logger, ISessionManager sessionManager) : base(logger) { _sessionManager = sessionManager; diff --git a/MediaBrowser.Api/System/ActivityLogWebSocketListener.cs b/MediaBrowser.Api/System/ActivityLogWebSocketListener.cs index 4b6a22b7d5..f8b6ee65d6 100644 --- a/MediaBrowser.Api/System/ActivityLogWebSocketListener.cs +++ b/MediaBrowser.Api/System/ActivityLogWebSocketListener.cs @@ -23,7 +23,7 @@ namespace MediaBrowser.Api.System /// private readonly IActivityManager _activityManager; - public ActivityLogWebSocketListener(ILogger logger, IActivityManager activityManager) : base(logger) + public ActivityLogWebSocketListener(ILogger logger, IActivityManager activityManager) : base(logger) { _activityManager = activityManager; _activityManager.EntryCreated += _activityManager_EntryCreated; diff --git a/MediaBrowser.Common/Extensions/ShuffleExtensions.cs b/MediaBrowser.Common/Extensions/ShuffleExtensions.cs index 5889d09c4b..0432f36b57 100644 --- a/MediaBrowser.Common/Extensions/ShuffleExtensions.cs +++ b/MediaBrowser.Common/Extensions/ShuffleExtensions.cs @@ -16,12 +16,23 @@ namespace MediaBrowser.Common.Extensions /// The list that should get shuffled. /// The type. public static void Shuffle(this IList list) + { + list.Shuffle(_rng); + } + + /// + /// Shuffles the items in a list. + /// + /// The list that should get shuffled. + /// The random number generator to use. + /// The type. + public static void Shuffle(this IList list, Random rng) { int n = list.Count; while (n > 1) { n--; - int k = _rng.Next(n + 1); + int k = rng.Next(n + 1); T value = list[k]; list[k] = list[n]; list[n] = value; diff --git a/MediaBrowser.Common/IApplicationHost.cs b/MediaBrowser.Common/IApplicationHost.cs index 68a24aabaa..0e282cf538 100644 --- a/MediaBrowser.Common/IApplicationHost.cs +++ b/MediaBrowser.Common/IApplicationHost.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Threading.Tasks; using MediaBrowser.Common.Plugins; using MediaBrowser.Model.Updates; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; namespace MediaBrowser.Common @@ -121,11 +122,12 @@ namespace MediaBrowser.Common void RemovePlugin(IPlugin plugin); /// - /// Inits this instance. + /// Initializes this instance. /// /// The service collection. + /// The configuration to use for initialization. /// A task. - Task InitAsync(IServiceCollection serviceCollection); + Task InitAsync(IServiceCollection serviceCollection, IConfiguration startupConfig); /// /// Creates the instance. diff --git a/MediaBrowser.Common/MediaBrowser.Common.csproj b/MediaBrowser.Common/MediaBrowser.Common.csproj index 3da8644040..04e0ee67a2 100644 --- a/MediaBrowser.Common/MediaBrowser.Common.csproj +++ b/MediaBrowser.Common/MediaBrowser.Common.csproj @@ -12,6 +12,7 @@ + diff --git a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs index efe0d3cf70..5e3056ccb0 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs @@ -198,6 +198,7 @@ namespace MediaBrowser.Controller.Entities.Audio return true; } } + return base.RequiresRefresh(); } diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 353c675cb3..5e4217cd54 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -33,7 +33,7 @@ namespace MediaBrowser.Controller.Entities /// /// Class BaseItem /// - public abstract class BaseItem : IHasProviderIds, IHasLookupInfo + public abstract class BaseItem : IHasProviderIds, IHasLookupInfo, IEquatable { /// /// The supported image extensions @@ -387,15 +387,12 @@ namespace MediaBrowser.Controller.Entities while (thisMarker < s1.Length) { - if (thisMarker >= s1.Length) - { - break; - } char thisCh = s1[thisMarker]; var thisChunk = new StringBuilder(); + bool isNumeric = char.IsDigit(thisCh); - while ((thisMarker < s1.Length) && (thisChunk.Length == 0 || SortHelper.InChunk(thisCh, thisChunk[0]))) + while (thisMarker < s1.Length && char.IsDigit(thisCh) == isNumeric) { thisChunk.Append(thisCh); thisMarker++; @@ -406,7 +403,6 @@ namespace MediaBrowser.Controller.Entities } } - var isNumeric = thisChunk.Length > 0 && char.IsDigit(thisChunk[0]); list.Add(new Tuple(thisChunk, isNumeric)); } @@ -2918,5 +2914,17 @@ namespace MediaBrowser.Controller.Entities public static readonly IReadOnlyCollection DisplayExtraTypes = new[] { Model.Entities.ExtraType.BehindTheScenes, Model.Entities.ExtraType.Clip, Model.Entities.ExtraType.DeletedScene, Model.Entities.ExtraType.Interview, Model.Entities.ExtraType.Sample, Model.Entities.ExtraType.Scene }; public virtual bool SupportsExternalTransfer => false; + + /// + public override bool Equals(object obj) + { + return obj is BaseItem baseItem && this.Equals(baseItem); + } + + /// + public bool Equals(BaseItem item) => Object.Equals(Id, item?.Id); + + /// + public override int GetHashCode() => HashCode.Combine(Id); } } diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index 07fbe60350..c72bd487e7 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -322,10 +322,10 @@ namespace MediaBrowser.Controller.Entities ProviderManager.OnRefreshProgress(this, 5); } - //build a dictionary of the current children we have now by Id so we can compare quickly and easily + // Build a dictionary of the current children we have now by Id so we can compare quickly and easily var currentChildren = GetActualChildrenDictionary(); - //create a list for our validated children + // Create a list for our validated children var newItems = new List(); cancellationToken.ThrowIfCancellationRequested(); @@ -391,7 +391,7 @@ namespace MediaBrowser.Controller.Entities var folder = this; innerProgress.RegisterAction(p => { - double newPct = .80 * p + 10; + double newPct = 0.80 * p + 10; progress.Report(newPct); ProviderManager.OnRefreshProgress(folder, newPct); }); @@ -421,7 +421,7 @@ namespace MediaBrowser.Controller.Entities var folder = this; innerProgress.RegisterAction(p => { - double newPct = .10 * p + 90; + double newPct = 0.10 * p + 90; progress.Report(newPct); if (recursive) { @@ -807,11 +807,45 @@ namespace MediaBrowser.Controller.Entities return false; } + private static BaseItem[] SortItemsByRequest(InternalItemsQuery query, IReadOnlyList items) + { + var ids = query.ItemIds; + int size = items.Count; + + // ids can potentially contain non-unique guids, but query result cannot, + // so we include only first occurrence of each guid + var positions = new Dictionary(size); + int index = 0; + for (int i = 0; i < ids.Length; i++) + { + if (positions.TryAdd(ids[i], index)) + { + index++; + } + } + + var newItems = new BaseItem[size]; + for (int i = 0; i < size; i++) + { + var item = items[i]; + newItems[positions[item.Id]] = item; + } + + return newItems; + } + public QueryResult GetItems(InternalItemsQuery query) { if (query.ItemIds.Length > 0) { - return LibraryManager.GetItemsResult(query); + var result = LibraryManager.GetItemsResult(query); + + if (query.OrderBy.Count == 0 && query.ItemIds.Length > 1) + { + result.Items = SortItemsByRequest(query, result.Items); + } + + return result; } return GetItemsInternal(query); @@ -823,7 +857,14 @@ namespace MediaBrowser.Controller.Entities if (query.ItemIds.Length > 0) { - return LibraryManager.GetItemList(query); + var result = LibraryManager.GetItemList(query); + + if (query.OrderBy.Count == 0 && query.ItemIds.Length > 1) + { + return SortItemsByRequest(query, result); + } + + return result.ToArray(); } return GetItemsInternal(query).Items; diff --git a/MediaBrowser.Controller/Library/Profiler.cs b/MediaBrowser.Controller/Library/Profiler.cs index 9fe175a7c4..46a97d181f 100644 --- a/MediaBrowser.Controller/Library/Profiler.cs +++ b/MediaBrowser.Controller/Library/Profiler.cs @@ -28,7 +28,7 @@ namespace MediaBrowser.Controller.Library /// /// The name. /// The logger. - public Profiler(string name, ILogger logger) + public Profiler(string name, ILogger logger) { this._name = name; diff --git a/MediaBrowser.Controller/Sorting/AlphanumComparator.cs b/MediaBrowser.Controller/Sorting/AlphanumComparator.cs new file mode 100644 index 0000000000..de7f72d1c2 --- /dev/null +++ b/MediaBrowser.Controller/Sorting/AlphanumComparator.cs @@ -0,0 +1,135 @@ +#nullable enable + +using System; +using System.Collections.Generic; + +namespace MediaBrowser.Controller.Sorting +{ + public class AlphanumComparator : IComparer + { + public static int CompareValues(string? s1, string? s2) + { + if (s1 == null && s2 == null) + { + return 0; + } + else if (s1 == null) + { + return -1; + } + else if (s2 == null) + { + return 1; + } + + int len1 = s1.Length; + int len2 = s2.Length; + + // Early return for empty strings + if (len1 == 0 && len2 == 0) + { + return 0; + } + else if (len1 == 0) + { + return -1; + } + else if (len2 == 0) + { + return 1; + } + + int pos1 = 0; + int pos2 = 0; + + do + { + int start1 = pos1; + int start2 = pos2; + + bool isNum1 = char.IsDigit(s1[pos1++]); + bool isNum2 = char.IsDigit(s2[pos2++]); + + while (pos1 < len1 && char.IsDigit(s1[pos1]) == isNum1) + { + pos1++; + } + + while (pos2 < len2 && char.IsDigit(s2[pos2]) == isNum2) + { + pos2++; + } + + var span1 = s1.AsSpan(start1, pos1 - start1); + var span2 = s2.AsSpan(start2, pos2 - start2); + + if (isNum1 && isNum2) + { + // Trim leading zeros so we can compare the length + // of the strings to find the largest number + span1 = span1.TrimStart('0'); + span2 = span2.TrimStart('0'); + var span1Len = span1.Length; + var span2Len = span2.Length; + if (span1Len < span2Len) + { + return -1; + } + else if (span1Len > span2Len) + { + return 1; + } + else if (span1Len >= 20) // Number is probably too big for a ulong + { + // Trim all the first digits that are the same + int i = 0; + while (i < span1Len && span1[i] == span2[i]) + { + i++; + } + + // If there are no more digits it's the same number + if (i == span1Len) + { + continue; + } + + // Only need to compare the most significant digit + span1 = span1.Slice(i, 1); + span2 = span2.Slice(i, 1); + } + + if (!ulong.TryParse(span1, out var num1) + || !ulong.TryParse(span2, out var num2)) + { + return 0; + } + else if (num1 < num2) + { + return -1; + } + else if (num1 > num2) + { + return 1; + } + } + else + { + int result = span1.CompareTo(span2, StringComparison.InvariantCulture); + if (result != 0) + { + return result; + } + } + } while (pos1 < len1 && pos2 < len2); + + return len1 - len2; + } + + /// + public int Compare(string x, string y) + { + return CompareValues(x, y); + } + } +} diff --git a/MediaBrowser.Controller/Sorting/SortExtensions.cs b/MediaBrowser.Controller/Sorting/SortExtensions.cs index 111f4f17fc..f5ee574a2d 100644 --- a/MediaBrowser.Controller/Sorting/SortExtensions.cs +++ b/MediaBrowser.Controller/Sorting/SortExtensions.cs @@ -7,137 +7,25 @@ namespace MediaBrowser.Controller.Sorting { public static class SortExtensions { + private static readonly AlphanumComparator _comparer = new AlphanumComparator(); public static IEnumerable OrderByString(this IEnumerable list, Func getName) { - return list.OrderBy(getName, new AlphanumComparator()); + return list.OrderBy(getName, _comparer); } public static IEnumerable OrderByStringDescending(this IEnumerable list, Func getName) { - return list.OrderByDescending(getName, new AlphanumComparator()); + return list.OrderByDescending(getName, _comparer); } public static IOrderedEnumerable ThenByString(this IOrderedEnumerable list, Func getName) { - return list.ThenBy(getName, new AlphanumComparator()); + return list.ThenBy(getName, _comparer); } public static IOrderedEnumerable ThenByStringDescending(this IOrderedEnumerable list, Func getName) { - return list.ThenByDescending(getName, new AlphanumComparator()); - } - - private class AlphanumComparator : IComparer - { - private enum ChunkType { Alphanumeric, Numeric }; - - private static bool InChunk(char ch, char otherCh) - { - var type = ChunkType.Alphanumeric; - - if (char.IsDigit(otherCh)) - { - type = ChunkType.Numeric; - } - - if ((type == ChunkType.Alphanumeric && char.IsDigit(ch)) - || (type == ChunkType.Numeric && !char.IsDigit(ch))) - { - return false; - } - - return true; - } - - public static int CompareValues(string s1, string s2) - { - if (s1 == null || s2 == null) - { - return 0; - } - - int thisMarker = 0, thisNumericChunk = 0; - int thatMarker = 0, thatNumericChunk = 0; - - while ((thisMarker < s1.Length) || (thatMarker < s2.Length)) - { - if (thisMarker >= s1.Length) - { - return -1; - } - else if (thatMarker >= s2.Length) - { - return 1; - } - char thisCh = s1[thisMarker]; - char thatCh = s2[thatMarker]; - - var thisChunk = new StringBuilder(); - var thatChunk = new StringBuilder(); - - while ((thisMarker < s1.Length) && (thisChunk.Length == 0 || InChunk(thisCh, thisChunk[0]))) - { - thisChunk.Append(thisCh); - thisMarker++; - - if (thisMarker < s1.Length) - { - thisCh = s1[thisMarker]; - } - } - - while ((thatMarker < s2.Length) && (thatChunk.Length == 0 || InChunk(thatCh, thatChunk[0]))) - { - thatChunk.Append(thatCh); - thatMarker++; - - if (thatMarker < s2.Length) - { - thatCh = s2[thatMarker]; - } - } - - int result = 0; - // If both chunks contain numeric characters, sort them numerically - if (char.IsDigit(thisChunk[0]) && char.IsDigit(thatChunk[0])) - { - if (!int.TryParse(thisChunk.ToString(), out thisNumericChunk)) - { - return 0; - } - if (!int.TryParse(thatChunk.ToString(), out thatNumericChunk)) - { - return 0; - } - - if (thisNumericChunk < thatNumericChunk) - { - result = -1; - } - - if (thisNumericChunk > thatNumericChunk) - { - result = 1; - } - } - else - { - result = thisChunk.ToString().CompareTo(thatChunk.ToString()); - } - - if (result != 0) - { - return result; - } - } - - return 0; - } - - public int Compare(string x, string y) - { - return CompareValues(x, y); - } + return list.ThenByDescending(getName, _comparer); } } } diff --git a/MediaBrowser.Controller/Sorting/SortHelper.cs b/MediaBrowser.Controller/Sorting/SortHelper.cs deleted file mode 100644 index 05981d9757..0000000000 --- a/MediaBrowser.Controller/Sorting/SortHelper.cs +++ /dev/null @@ -1,25 +0,0 @@ -namespace MediaBrowser.Controller.Sorting -{ - public static class SortHelper - { - private enum ChunkType { Alphanumeric, Numeric }; - - public static bool InChunk(char ch, char otherCh) - { - var type = ChunkType.Alphanumeric; - - if (char.IsDigit(otherCh)) - { - type = ChunkType.Numeric; - } - - if ((type == ChunkType.Alphanumeric && char.IsDigit(ch)) - || (type == ChunkType.Numeric && !char.IsDigit(ch))) - { - return false; - } - - return true; - } - } -} diff --git a/MediaBrowser.LocalMetadata/Providers/BoxSetXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/BoxSetXmlProvider.cs index 2e303efab6..b2e3bc9e2b 100644 --- a/MediaBrowser.LocalMetadata/Providers/BoxSetXmlProvider.cs +++ b/MediaBrowser.LocalMetadata/Providers/BoxSetXmlProvider.cs @@ -16,7 +16,7 @@ namespace MediaBrowser.LocalMetadata.Providers private readonly ILogger _logger; private readonly IProviderManager _providerManager; - public BoxSetXmlProvider(IFileSystem fileSystem, ILogger logger, IProviderManager providerManager) + public BoxSetXmlProvider(IFileSystem fileSystem, ILogger logger, IProviderManager providerManager) : base(fileSystem) { _logger = logger; diff --git a/MediaBrowser.LocalMetadata/Providers/PlaylistXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/PlaylistXmlProvider.cs index d111ae9ba8..df8107bade 100644 --- a/MediaBrowser.LocalMetadata/Providers/PlaylistXmlProvider.cs +++ b/MediaBrowser.LocalMetadata/Providers/PlaylistXmlProvider.cs @@ -13,7 +13,10 @@ namespace MediaBrowser.LocalMetadata.Providers private readonly ILogger _logger; private readonly IProviderManager _providerManager; - public PlaylistXmlProvider(IFileSystem fileSystem, ILogger logger, IProviderManager providerManager) + public PlaylistXmlProvider( + IFileSystem fileSystem, + ILogger logger, + IProviderManager providerManager) : base(fileSystem) { _logger = logger; diff --git a/MediaBrowser.LocalMetadata/Savers/BoxSetXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/BoxSetXmlSaver.cs index ea939e33b1..1dc09bf18d 100644 --- a/MediaBrowser.LocalMetadata/Savers/BoxSetXmlSaver.cs +++ b/MediaBrowser.LocalMetadata/Savers/BoxSetXmlSaver.cs @@ -30,7 +30,7 @@ namespace MediaBrowser.LocalMetadata.Savers return Path.Combine(item.Path, "collection.xml"); } - public BoxSetXmlSaver(IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataManager, ILogger logger) + public BoxSetXmlSaver(IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataManager, ILogger logger) : base(fileSystem, configurationManager, libraryManager, userManager, userDataManager, logger) { } diff --git a/MediaBrowser.LocalMetadata/Savers/PlaylistXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/PlaylistXmlSaver.cs index 35a431fa48..bbb0a3501d 100644 --- a/MediaBrowser.LocalMetadata/Savers/PlaylistXmlSaver.cs +++ b/MediaBrowser.LocalMetadata/Savers/PlaylistXmlSaver.cs @@ -11,6 +11,11 @@ namespace MediaBrowser.LocalMetadata.Savers { public class PlaylistXmlSaver : BaseXmlSaver { + /// + /// The default file name to use when creating a new playlist. + /// + public const string DefaultPlaylistFilename = "playlist.xml"; + public override bool IsEnabledFor(BaseItem item, ItemUpdateType updateType) { if (!item.SupportsLocalMetadata) @@ -45,10 +50,10 @@ namespace MediaBrowser.LocalMetadata.Savers return Path.ChangeExtension(itemPath, ".xml"); } - return Path.Combine(path, "playlist.xml"); + return Path.Combine(path, DefaultPlaylistFilename); } - public PlaylistXmlSaver(IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataManager, ILogger logger) + public PlaylistXmlSaver(IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataManager, ILogger logger) : base(fileSystem, configurationManager, libraryManager, userManager, userDataManager, logger) { } diff --git a/MediaBrowser.Model/Configuration/BaseApplicationConfiguration.cs b/MediaBrowser.Model/Configuration/BaseApplicationConfiguration.cs index 6a1a0f0901..cc2541f74f 100644 --- a/MediaBrowser.Model/Configuration/BaseApplicationConfiguration.cs +++ b/MediaBrowser.Model/Configuration/BaseApplicationConfiguration.cs @@ -1,3 +1,6 @@ +using System; +using System.Xml.Serialization; + namespace MediaBrowser.Model.Configuration { /// @@ -25,6 +28,24 @@ namespace MediaBrowser.Model.Configuration /// The cache path. public string CachePath { get; set; } + /// + /// Last known version that was ran using the configuration. + /// + /// The version from previous run. + [XmlIgnore] + public Version PreviousVersion { get; set; } + + /// + /// Stringified PreviousVersion to be stored/loaded, + /// because System.Version itself isn't xml-serializable + /// + /// String value of PreviousVersion + public string PreviousVersionStr + { + get => PreviousVersion?.ToString(); + set => PreviousVersion = Version.Parse(value); + } + /// /// Initializes a new instance of the class. /// diff --git a/MediaBrowser.Model/Configuration/EncodingOptions.cs b/MediaBrowser.Model/Configuration/EncodingOptions.cs index ff431e44cc..b3a784de75 100644 --- a/MediaBrowser.Model/Configuration/EncodingOptions.cs +++ b/MediaBrowser.Model/Configuration/EncodingOptions.cs @@ -34,7 +34,7 @@ namespace MediaBrowser.Model.Configuration public EncodingOptions() { DownMixAudioBoost = 2; - EnableThrottling = true; + EnableThrottling = false; ThrottleDelaySeconds = 180; EncodingThreadCount = -1; // This is a DRM device that is almost guaranteed to be there on every intel platform, plus it's the default one in ffmpeg if you don't specify anything diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs index 43a84d3815..7a8a4d3a5c 100644 --- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs @@ -238,7 +238,7 @@ namespace MediaBrowser.Model.Configuration CodecsUsed = Array.Empty(); PathSubstitutions = Array.Empty(); IgnoreVirtualInterfaces = false; - EnableSimpleArtistDetection = true; + EnableSimpleArtistDetection = false; DisplaySpecialsWithinSeasons = true; EnableExternalContentInSuggestions = true; @@ -248,7 +248,7 @@ namespace MediaBrowser.Model.Configuration PublicHttpsPort = DefaultHttpsPort; HttpServerPortNumber = DefaultHttpPort; HttpsPortNumber = DefaultHttpsPort; - EnableHttps = true; + EnableHttps = false; EnableDashboardResponseCaching = true; EnableCaseSensitiveItemIds = true; diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs index a1838acf35..1a1977e6e7 100644 --- a/MediaBrowser.Model/Dlna/StreamBuilder.cs +++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs @@ -24,7 +24,7 @@ namespace MediaBrowser.Model.Dlna _logger = logger; } - public StreamBuilder(ILogger logger) + public StreamBuilder(ILogger logger) : this(new FullTranscoderSupport(), logger) { } diff --git a/MediaBrowser.Providers/Books/AudioBookMetadataService.cs b/MediaBrowser.Providers/Books/AudioBookMetadataService.cs index 309241bfaa..8eaeeea080 100644 --- a/MediaBrowser.Providers/Books/AudioBookMetadataService.cs +++ b/MediaBrowser.Providers/Books/AudioBookMetadataService.cs @@ -13,7 +13,7 @@ namespace MediaBrowser.Providers.Books { public AudioBookMetadataService( IServerConfigurationManager serverConfigurationManager, - ILogger logger, + ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, ILibraryManager libraryManager) diff --git a/MediaBrowser.Providers/Books/BookMetadataService.cs b/MediaBrowser.Providers/Books/BookMetadataService.cs index 9d6a1ef59d..3406417113 100644 --- a/MediaBrowser.Providers/Books/BookMetadataService.cs +++ b/MediaBrowser.Providers/Books/BookMetadataService.cs @@ -13,7 +13,7 @@ namespace MediaBrowser.Providers.Books { public BookMetadataService( IServerConfigurationManager serverConfigurationManager, - ILogger logger, + ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, ILibraryManager libraryManager) diff --git a/MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs b/MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs index 5bf01232cd..3c9760ea7a 100644 --- a/MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs +++ b/MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs @@ -16,7 +16,7 @@ namespace MediaBrowser.Providers.BoxSets { public BoxSetMetadataService( IServerConfigurationManager serverConfigurationManager, - ILogger logger, + ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, ILibraryManager libraryManager) diff --git a/MediaBrowser.Providers/Channels/ChannelMetadataService.cs b/MediaBrowser.Providers/Channels/ChannelMetadataService.cs index da41f208c6..9afa823197 100644 --- a/MediaBrowser.Providers/Channels/ChannelMetadataService.cs +++ b/MediaBrowser.Providers/Channels/ChannelMetadataService.cs @@ -13,7 +13,7 @@ namespace MediaBrowser.Providers.Channels { public ChannelMetadataService( IServerConfigurationManager serverConfigurationManager, - ILogger logger, + ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, ILibraryManager libraryManager) diff --git a/MediaBrowser.Providers/Folders/CollectionFolderMetadataService.cs b/MediaBrowser.Providers/Folders/CollectionFolderMetadataService.cs index dd1b4709d3..921222543c 100644 --- a/MediaBrowser.Providers/Folders/CollectionFolderMetadataService.cs +++ b/MediaBrowser.Providers/Folders/CollectionFolderMetadataService.cs @@ -14,7 +14,7 @@ namespace MediaBrowser.Providers.Folders { public CollectionFolderMetadataService( IServerConfigurationManager serverConfigurationManager, - ILogger logger, + ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, ILibraryManager libraryManager) diff --git a/MediaBrowser.Providers/Folders/FolderMetadataService.cs b/MediaBrowser.Providers/Folders/FolderMetadataService.cs index 8409e03fdc..b6bd2515dd 100644 --- a/MediaBrowser.Providers/Folders/FolderMetadataService.cs +++ b/MediaBrowser.Providers/Folders/FolderMetadataService.cs @@ -13,7 +13,7 @@ namespace MediaBrowser.Providers.Folders { public FolderMetadataService( IServerConfigurationManager serverConfigurationManager, - ILogger logger, + ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, ILibraryManager libraryManager) diff --git a/MediaBrowser.Providers/Folders/UserViewMetadataService.cs b/MediaBrowser.Providers/Folders/UserViewMetadataService.cs index 2ceb71afc4..60ee811149 100644 --- a/MediaBrowser.Providers/Folders/UserViewMetadataService.cs +++ b/MediaBrowser.Providers/Folders/UserViewMetadataService.cs @@ -13,7 +13,7 @@ namespace MediaBrowser.Providers.Folders { public UserViewMetadataService( IServerConfigurationManager serverConfigurationManager, - ILogger logger, + ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, ILibraryManager libraryManager) diff --git a/MediaBrowser.Providers/Genres/GenreMetadataService.cs b/MediaBrowser.Providers/Genres/GenreMetadataService.cs index 932eb368cf..f3406c1abc 100644 --- a/MediaBrowser.Providers/Genres/GenreMetadataService.cs +++ b/MediaBrowser.Providers/Genres/GenreMetadataService.cs @@ -13,7 +13,7 @@ namespace MediaBrowser.Providers.Genres { public GenreMetadataService( IServerConfigurationManager serverConfigurationManager, - ILogger logger, + ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, ILibraryManager libraryManager) diff --git a/MediaBrowser.Providers/LiveTv/ProgramMetadataService.cs b/MediaBrowser.Providers/LiveTv/ProgramMetadataService.cs index 13dd972151..7dd49c71a9 100644 --- a/MediaBrowser.Providers/LiveTv/ProgramMetadataService.cs +++ b/MediaBrowser.Providers/LiveTv/ProgramMetadataService.cs @@ -13,7 +13,7 @@ namespace MediaBrowser.Providers.LiveTv { public LiveTvMetadataService( IServerConfigurationManager serverConfigurationManager, - ILogger logger, + ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, ILibraryManager libraryManager) diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index 5593c5036c..dfe3eb2ef6 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -24,4 +24,14 @@ true + + + + + + + + + + diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs index 4e11fcbb2a..db6e49634c 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs @@ -121,7 +121,24 @@ namespace MediaBrowser.Providers.MediaInfo } private SubtitleResolver _subtitleResolver; - public FFProbeProvider(ILogger logger, IMediaSourceManager mediaSourceManager, IChannelManager channelManager, 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) + + public FFProbeProvider( + ILogger logger, + IMediaSourceManager mediaSourceManager, + IChannelManager channelManager, + 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) { _logger = logger; _isoManager = isoManager; diff --git a/MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs b/MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs index 74f41f9df0..3a936632a0 100644 --- a/MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs +++ b/MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs @@ -26,7 +26,13 @@ namespace MediaBrowser.Providers.MediaInfo private readonly ILogger _logger; private readonly IJsonSerializer _json; - public SubtitleScheduledTask(ILibraryManager libraryManager, IJsonSerializer json, IServerConfigurationManager config, ISubtitleManager subtitleManager, ILogger logger, IMediaSourceManager mediaSourceManager) + public SubtitleScheduledTask( + ILibraryManager libraryManager, + IJsonSerializer json, + IServerConfigurationManager config, + ISubtitleManager subtitleManager, + ILogger logger, + IMediaSourceManager mediaSourceManager) { _libraryManager = libraryManager; _config = config; diff --git a/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs b/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs index 95b915b3d8..f405700407 100644 --- a/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs +++ b/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs @@ -20,7 +20,7 @@ namespace MediaBrowser.Providers.MediaInfo private readonly ILogger _logger; private readonly IFileSystem _fileSystem; - public VideoImageProvider(IMediaEncoder mediaEncoder, ILogger logger, IFileSystem fileSystem) + public VideoImageProvider(IMediaEncoder mediaEncoder, ILogger logger, IFileSystem fileSystem) { _mediaEncoder = mediaEncoder; _logger = logger; diff --git a/MediaBrowser.Providers/Movies/MovieMetadataService.cs b/MediaBrowser.Providers/Movies/MovieMetadataService.cs index c6cc5c7dc0..1e2c325d99 100644 --- a/MediaBrowser.Providers/Movies/MovieMetadataService.cs +++ b/MediaBrowser.Providers/Movies/MovieMetadataService.cs @@ -13,7 +13,7 @@ namespace MediaBrowser.Providers.Movies { public MovieMetadataService( IServerConfigurationManager serverConfigurationManager, - ILogger logger, + ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, ILibraryManager libraryManager) diff --git a/MediaBrowser.Providers/Movies/TrailerMetadataService.cs b/MediaBrowser.Providers/Movies/TrailerMetadataService.cs index 53b556940b..2e6f762b8e 100644 --- a/MediaBrowser.Providers/Movies/TrailerMetadataService.cs +++ b/MediaBrowser.Providers/Movies/TrailerMetadataService.cs @@ -13,7 +13,7 @@ namespace MediaBrowser.Providers.Movies { public TrailerMetadataService( IServerConfigurationManager serverConfigurationManager, - ILogger logger, + ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, ILibraryManager libraryManager) diff --git a/MediaBrowser.Providers/Music/AlbumMetadataService.cs b/MediaBrowser.Providers/Music/AlbumMetadataService.cs index 69133c1c16..ed6c01968d 100644 --- a/MediaBrowser.Providers/Music/AlbumMetadataService.cs +++ b/MediaBrowser.Providers/Music/AlbumMetadataService.cs @@ -17,7 +17,7 @@ namespace MediaBrowser.Providers.Music { public AlbumMetadataService( IServerConfigurationManager serverConfigurationManager, - ILogger logger, + ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, ILibraryManager libraryManager) diff --git a/MediaBrowser.Providers/Music/ArtistMetadataService.cs b/MediaBrowser.Providers/Music/ArtistMetadataService.cs index 1f099c60f2..f90a631c65 100644 --- a/MediaBrowser.Providers/Music/ArtistMetadataService.cs +++ b/MediaBrowser.Providers/Music/ArtistMetadataService.cs @@ -15,7 +15,7 @@ namespace MediaBrowser.Providers.Music { public ArtistMetadataService( IServerConfigurationManager serverConfigurationManager, - ILogger logger, + ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, ILibraryManager libraryManager) diff --git a/MediaBrowser.Providers/Music/AudioMetadataService.cs b/MediaBrowser.Providers/Music/AudioMetadataService.cs index 4d4739cef2..e726fa1e2c 100644 --- a/MediaBrowser.Providers/Music/AudioMetadataService.cs +++ b/MediaBrowser.Providers/Music/AudioMetadataService.cs @@ -13,7 +13,7 @@ namespace MediaBrowser.Providers.Music { public AudioMetadataService( IServerConfigurationManager serverConfigurationManager, - ILogger logger, + ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, ILibraryManager libraryManager) diff --git a/MediaBrowser.Providers/Music/MusicExternalIds.cs b/MediaBrowser.Providers/Music/MusicExternalIds.cs index 585c98af9e..628b9a9a10 100644 --- a/MediaBrowser.Providers/Music/MusicExternalIds.cs +++ b/MediaBrowser.Providers/Music/MusicExternalIds.cs @@ -1,105 +1,9 @@ using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; namespace MediaBrowser.Providers.Music { - public class MusicBrainzReleaseGroupExternalId : IExternalId - { - /// - public string Name => "MusicBrainz Release Group"; - - /// - public string Key => MetadataProviders.MusicBrainzReleaseGroup.ToString(); - - /// - public string UrlFormatString => "https://musicbrainz.org/release-group/{0}"; - - /// - public bool Supports(IHasProviderIds item) - => item is Audio || item is MusicAlbum; - } - - public class MusicBrainzAlbumArtistExternalId : IExternalId - { - /// - public string Name => "MusicBrainz Album Artist"; - - /// - public string Key => MetadataProviders.MusicBrainzAlbumArtist.ToString(); - - /// - public string UrlFormatString => "https://musicbrainz.org/artist/{0}"; - - /// - public bool Supports(IHasProviderIds item) - => item is Audio; - } - - public class MusicBrainzAlbumExternalId : IExternalId - { - /// - public string Name => "MusicBrainz Album"; - - /// - public string Key => MetadataProviders.MusicBrainzAlbum.ToString(); - - /// - public string UrlFormatString => "https://musicbrainz.org/release/{0}"; - - /// - public bool Supports(IHasProviderIds item) - => item is Audio || item is MusicAlbum; - } - - public class MusicBrainzArtistExternalId : IExternalId - { - /// - public string Name => "MusicBrainz"; - - /// - public string Key => MetadataProviders.MusicBrainzArtist.ToString(); - - /// - public string UrlFormatString => "https://musicbrainz.org/artist/{0}"; - - /// - public bool Supports(IHasProviderIds item) => item is MusicArtist; - } - - public class MusicBrainzOtherArtistExternalId : IExternalId - { - /// - public string Name => "MusicBrainz Artist"; - - /// - - public string Key => MetadataProviders.MusicBrainzArtist.ToString(); - - /// - public string UrlFormatString => "https://musicbrainz.org/artist/{0}"; - - /// - public bool Supports(IHasProviderIds item) - => item is Audio || item is MusicAlbum; - } - - public class MusicBrainzTrackId : IExternalId - { - /// - public string Name => "MusicBrainz Track"; - - /// - public string Key => MetadataProviders.MusicBrainzTrack.ToString(); - - /// - public string UrlFormatString => "https://musicbrainz.org/track/{0}"; - - /// - public bool Supports(IHasProviderIds item) => item is Audio; - } - public class ImvdbId : IExternalId { /// diff --git a/MediaBrowser.Providers/Music/MusicVideoMetadataService.cs b/MediaBrowser.Providers/Music/MusicVideoMetadataService.cs index bbf0cd8db5..d653e10638 100644 --- a/MediaBrowser.Providers/Music/MusicVideoMetadataService.cs +++ b/MediaBrowser.Providers/Music/MusicVideoMetadataService.cs @@ -13,7 +13,7 @@ namespace MediaBrowser.Providers.Music { public MusicVideoMetadataService( IServerConfigurationManager serverConfigurationManager, - ILogger logger, + ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, ILibraryManager libraryManager) diff --git a/MediaBrowser.Providers/MusicGenres/MusicGenreMetadataService.cs b/MediaBrowser.Providers/MusicGenres/MusicGenreMetadataService.cs index d74e91ad6c..bb47de40bc 100644 --- a/MediaBrowser.Providers/MusicGenres/MusicGenreMetadataService.cs +++ b/MediaBrowser.Providers/MusicGenres/MusicGenreMetadataService.cs @@ -13,7 +13,7 @@ namespace MediaBrowser.Providers.MusicGenres { public MusicGenreMetadataService( IServerConfigurationManager serverConfigurationManager, - ILogger logger, + ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, ILibraryManager libraryManager) diff --git a/MediaBrowser.Providers/People/PersonMetadataService.cs b/MediaBrowser.Providers/People/PersonMetadataService.cs index cdc3c77b71..804f3f3e32 100644 --- a/MediaBrowser.Providers/People/PersonMetadataService.cs +++ b/MediaBrowser.Providers/People/PersonMetadataService.cs @@ -13,7 +13,7 @@ namespace MediaBrowser.Providers.People { public PersonMetadataService( IServerConfigurationManager serverConfigurationManager, - ILogger logger, + ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, ILibraryManager libraryManager) diff --git a/MediaBrowser.Providers/Photos/PhotoAlbumMetadataService.cs b/MediaBrowser.Providers/Photos/PhotoAlbumMetadataService.cs index 845404dfbd..af8f7a2622 100644 --- a/MediaBrowser.Providers/Photos/PhotoAlbumMetadataService.cs +++ b/MediaBrowser.Providers/Photos/PhotoAlbumMetadataService.cs @@ -13,7 +13,7 @@ namespace MediaBrowser.Providers.Photos { public PhotoAlbumMetadataService( IServerConfigurationManager serverConfigurationManager, - ILogger logger, + ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, ILibraryManager libraryManager) diff --git a/MediaBrowser.Providers/Photos/PhotoMetadataService.cs b/MediaBrowser.Providers/Photos/PhotoMetadataService.cs index 5d6ff8814e..579b5a4d0f 100644 --- a/MediaBrowser.Providers/Photos/PhotoMetadataService.cs +++ b/MediaBrowser.Providers/Photos/PhotoMetadataService.cs @@ -13,7 +13,7 @@ namespace MediaBrowser.Providers.Photos { public PhotoMetadataService( IServerConfigurationManager serverConfigurationManager, - ILogger logger, + ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, ILibraryManager libraryManager) diff --git a/MediaBrowser.Providers/Playlists/PlaylistItemsProvider.cs b/MediaBrowser.Providers/Playlists/PlaylistItemsProvider.cs index dacb63f846..ae837c591a 100644 --- a/MediaBrowser.Providers/Playlists/PlaylistItemsProvider.cs +++ b/MediaBrowser.Providers/Playlists/PlaylistItemsProvider.cs @@ -23,7 +23,7 @@ namespace MediaBrowser.Providers.Playlists private ILogger _logger; private IFileSystem _fileSystem; - public PlaylistItemsProvider(IFileSystem fileSystem, ILogger logger) + public PlaylistItemsProvider(IFileSystem fileSystem, ILogger logger) { _fileSystem = fileSystem; _logger = logger; diff --git a/MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs b/MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs index 32bd6c2825..a41362ea3e 100644 --- a/MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs +++ b/MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs @@ -15,7 +15,7 @@ namespace MediaBrowser.Providers.Playlists { public PlaylistMetadataService( IServerConfigurationManager serverConfigurationManager, - ILogger logger, + ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, ILibraryManager libraryManager) diff --git a/MediaBrowser.Providers/Music/AudioDbAlbumImageProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/AlbumImageProvider.cs similarity index 95% rename from MediaBrowser.Providers/Music/AudioDbAlbumImageProvider.cs rename to MediaBrowser.Providers/Plugins/AudioDb/AlbumImageProvider.cs index 85a87630d7..dee2d59f0f 100644 --- a/MediaBrowser.Providers/Music/AudioDbAlbumImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/AudioDb/AlbumImageProvider.cs @@ -10,7 +10,7 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.Providers; using MediaBrowser.Model.Serialization; -namespace MediaBrowser.Providers.Music +namespace MediaBrowser.Providers.Plugins.AudioDb { public class AudioDbAlbumImageProvider : IRemoteImageProvider, IHasOrder { @@ -102,6 +102,7 @@ namespace MediaBrowser.Providers.Music } /// - public bool Supports(BaseItem item) => item is MusicAlbum; + public bool Supports(BaseItem item) + => Plugin.Instance.Configuration.Enable && item is MusicAlbum; } } diff --git a/MediaBrowser.Providers/Music/AudioDbAlbumProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs similarity index 95% rename from MediaBrowser.Providers/Music/AudioDbAlbumProvider.cs rename to MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs index 939c74c01c..1a0e878719 100644 --- a/MediaBrowser.Providers/Music/AudioDbAlbumProvider.cs +++ b/MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs @@ -16,8 +16,9 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; using MediaBrowser.Model.Providers; using MediaBrowser.Model.Serialization; +using MediaBrowser.Providers.Music; -namespace MediaBrowser.Providers.Music +namespace MediaBrowser.Providers.Plugins.AudioDb { public class AudioDbAlbumProvider : IRemoteMetadataProvider, IHasOrder { @@ -54,6 +55,12 @@ namespace MediaBrowser.Providers.Music { var result = new MetadataResult(); + // TODO maybe remove when artist metadata can be disabled + if (!Plugin.Instance.Configuration.Enable) + { + return result; + } + var id = info.GetReleaseGroupId(); if (!string.IsNullOrWhiteSpace(id)) @@ -77,6 +84,11 @@ namespace MediaBrowser.Providers.Music private void ProcessResult(MusicAlbum item, Album result, string preferredLanguage) { + if (Plugin.Instance.Configuration.ReplaceAlbumName && !string.IsNullOrWhiteSpace(result.strAlbum)) + { + item.Album = result.strAlbum; + } + if (!string.IsNullOrWhiteSpace(result.strArtist)) { item.AlbumArtists = new string[] { result.strArtist }; diff --git a/MediaBrowser.Providers/Music/AudioDbArtistImageProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/ArtistImageProvider.cs similarity index 96% rename from MediaBrowser.Providers/Music/AudioDbArtistImageProvider.cs rename to MediaBrowser.Providers/Plugins/AudioDb/ArtistImageProvider.cs index b9315744fd..18afd5dd5c 100644 --- a/MediaBrowser.Providers/Music/AudioDbArtistImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/AudioDb/ArtistImageProvider.cs @@ -10,7 +10,7 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.Providers; using MediaBrowser.Model.Serialization; -namespace MediaBrowser.Providers.Music +namespace MediaBrowser.Providers.Plugins.AudioDb { public class AudioDbArtistImageProvider : IRemoteImageProvider, IHasOrder { @@ -143,6 +143,7 @@ namespace MediaBrowser.Providers.Music } /// - public bool Supports(BaseItem item) => item is MusicArtist; + public bool Supports(BaseItem item) + => Plugin.Instance.Configuration.Enable && item is MusicArtist; } } diff --git a/MediaBrowser.Providers/Music/AudioDbArtistProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs similarity index 97% rename from MediaBrowser.Providers/Music/AudioDbArtistProvider.cs rename to MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs index e073a295b0..df0f3df8fa 100644 --- a/MediaBrowser.Providers/Music/AudioDbArtistProvider.cs +++ b/MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs @@ -15,8 +15,9 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; using MediaBrowser.Model.Providers; using MediaBrowser.Model.Serialization; +using MediaBrowser.Providers.Music; -namespace MediaBrowser.Providers.Music +namespace MediaBrowser.Providers.Plugins.AudioDb { public class AudioDbArtistProvider : IRemoteMetadataProvider, IHasOrder { @@ -55,6 +56,12 @@ namespace MediaBrowser.Providers.Music { var result = new MetadataResult(); + // TODO maybe remove when artist metadata can be disabled + if (!Plugin.Instance.Configuration.Enable) + { + return result; + } + var id = info.GetMusicBrainzArtistId(); if (!string.IsNullOrWhiteSpace(id)) diff --git a/MediaBrowser.Providers/Plugins/AudioDb/Configuration/PluginConfiguration.cs b/MediaBrowser.Providers/Plugins/AudioDb/Configuration/PluginConfiguration.cs new file mode 100644 index 0000000000..ad3c7eb4bd --- /dev/null +++ b/MediaBrowser.Providers/Plugins/AudioDb/Configuration/PluginConfiguration.cs @@ -0,0 +1,11 @@ +using MediaBrowser.Model.Plugins; + +namespace MediaBrowser.Providers.Plugins.AudioDb +{ + public class PluginConfiguration : BasePluginConfiguration + { + public bool Enable { get; set; } + + public bool ReplaceAlbumName { get; set; } + } +} diff --git a/MediaBrowser.Providers/Plugins/AudioDb/Configuration/config.html b/MediaBrowser.Providers/Plugins/AudioDb/Configuration/config.html new file mode 100644 index 0000000000..34494644d4 --- /dev/null +++ b/MediaBrowser.Providers/Plugins/AudioDb/Configuration/config.html @@ -0,0 +1,57 @@ + + + + AudioDB + + +
+
+
+
+ + +
+
+ +
+
+
+
+ +
+ + diff --git a/MediaBrowser.Providers/Music/AudioDbExternalIds.cs b/MediaBrowser.Providers/Plugins/AudioDb/ExternalIds.cs similarity index 88% rename from MediaBrowser.Providers/Music/AudioDbExternalIds.cs rename to MediaBrowser.Providers/Plugins/AudioDb/ExternalIds.cs index c866d12de5..2d8cb431ca 100644 --- a/MediaBrowser.Providers/Music/AudioDbExternalIds.cs +++ b/MediaBrowser.Providers/Plugins/AudioDb/ExternalIds.cs @@ -2,7 +2,7 @@ using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; -namespace MediaBrowser.Providers.Music +namespace MediaBrowser.Providers.Plugins.AudioDb { public class AudioDbAlbumExternalId : IExternalId { @@ -16,8 +16,7 @@ namespace MediaBrowser.Providers.Music public string UrlFormatString => "https://www.theaudiodb.com/album/{0}"; /// - public bool Supports(IHasProviderIds item) - => item is MusicAlbum; + public bool Supports(IHasProviderIds item) => item is MusicAlbum; } public class AudioDbOtherAlbumExternalId : IExternalId @@ -62,7 +61,6 @@ namespace MediaBrowser.Providers.Music public string UrlFormatString => "https://www.theaudiodb.com/artist/{0}"; /// - public bool Supports(IHasProviderIds item) - => item is Audio || item is MusicAlbum; + public bool Supports(IHasProviderIds item) => item is Audio || item is MusicAlbum; } } diff --git a/MediaBrowser.Providers/Plugins/AudioDb/Plugin.cs b/MediaBrowser.Providers/Plugins/AudioDb/Plugin.cs new file mode 100644 index 0000000000..8532c4df3a --- /dev/null +++ b/MediaBrowser.Providers/Plugins/AudioDb/Plugin.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.Plugins; +using MediaBrowser.Model.Plugins; +using MediaBrowser.Model.Serialization; + +namespace MediaBrowser.Providers.Plugins.AudioDb +{ + public class Plugin : BasePlugin, IHasWebPages + { + public static Plugin Instance { get; private set; } + + public override Guid Id => new Guid("a629c0da-fac5-4c7e-931a-7174223f14c8"); + + public override string Name => "AudioDB"; + + public override string Description => "Get artist and album metadata or images from AudioDB."; + + public Plugin(IApplicationPaths applicationPaths, IXmlSerializer xmlSerializer) + : base(applicationPaths, xmlSerializer) + { + Instance = this; + } + + public IEnumerable GetPages() + { + yield return new PluginPageInfo + { + Name = Name, + EmbeddedResourcePath = GetType().Namespace + ".Configuration.config.html" + }; + } + } +} diff --git a/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/AlbumProvider.cs similarity index 96% rename from MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs rename to MediaBrowser.Providers/Plugins/MusicBrainz/AlbumProvider.cs index d217c6ad20..bc973dee5a 100644 --- a/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs +++ b/MediaBrowser.Providers/Plugins/MusicBrainz/AlbumProvider.cs @@ -15,25 +15,20 @@ using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Providers; -using Microsoft.Extensions.Configuration; +using MediaBrowser.Providers.Plugins.MusicBrainz; using Microsoft.Extensions.Logging; namespace MediaBrowser.Providers.Music { public class MusicBrainzAlbumProvider : IRemoteMetadataProvider, IHasOrder { - /// - /// The key for a setting that specifies the base URL to use for sending requests to MusicBrainz. - /// - public const string BaseUrlKey = "MusicBrainz:BaseUrl"; - /// /// The Jellyfin user-agent is unrestricted but source IP must not exceed /// one request per second, therefore we rate limit to avoid throttling. /// Be prudent, use a value slightly above the minimun required. /// https://musicbrainz.org/doc/XML_Web_Service/Rate_Limiting /// - private const long MusicBrainzQueryIntervalMs = 1050u; + private readonly long _musicBrainzQueryIntervalMs; /// /// For each single MB lookup/search, this is the maximum number of @@ -55,14 +50,14 @@ namespace MediaBrowser.Providers.Music public MusicBrainzAlbumProvider( IHttpClient httpClient, IApplicationHost appHost, - ILogger logger, - IConfiguration configuration) + ILogger logger) { _httpClient = httpClient; _appHost = appHost; _logger = logger; - _musicBrainzBaseUrl = configuration[BaseUrlKey]; + _musicBrainzBaseUrl = Plugin.Instance.Configuration.Server; + _musicBrainzQueryIntervalMs = Plugin.Instance.Configuration.RateLimit; // Use a stopwatch to ensure we don't exceed the MusicBrainz rate limit _stopWatchMusicBrainz.Start(); @@ -79,6 +74,12 @@ namespace MediaBrowser.Providers.Music /// public async Task> GetSearchResults(AlbumInfo searchInfo, CancellationToken cancellationToken) { + // TODO maybe remove when artist metadata can be disabled + if (!Plugin.Instance.Configuration.Enable) + { + return Enumerable.Empty(); + } + var releaseId = searchInfo.GetReleaseId(); var releaseGroupId = searchInfo.GetReleaseGroupId(); @@ -112,8 +113,8 @@ namespace MediaBrowser.Providers.Music url = string.Format( CultureInfo.InvariantCulture, "/ws/2/release/?query=\"{0}\" AND artist:\"{1}\"", - WebUtility.UrlEncode(queryName), - WebUtility.UrlEncode(searchInfo.GetAlbumArtist())); + WebUtility.UrlEncode(queryName), + WebUtility.UrlEncode(searchInfo.GetAlbumArtist())); } } @@ -175,7 +176,6 @@ namespace MediaBrowser.Providers.Music } return result; - }); } } @@ -192,6 +192,12 @@ namespace MediaBrowser.Providers.Music Item = new MusicAlbum() }; + // TODO maybe remove when artist metadata can be disabled + if (!Plugin.Instance.Configuration.Enable) + { + return result; + } + // If we have a release group Id but not a release Id... if (string.IsNullOrWhiteSpace(releaseId) && !string.IsNullOrWhiteSpace(releaseGroupId)) { @@ -461,18 +467,6 @@ namespace MediaBrowser.Providers.Music } case "artist-credit": { - // TODO - - /* - * - - -SARCASTIC+ZOOKEEPER -SARCASTIC+ZOOKEEPER - - - - */ using (var subReader = reader.ReadSubtree()) { var artist = ParseArtistCredit(subReader); @@ -769,10 +763,10 @@ namespace MediaBrowser.Providers.Music { attempts++; - if (_stopWatchMusicBrainz.ElapsedMilliseconds < MusicBrainzQueryIntervalMs) + if (_stopWatchMusicBrainz.ElapsedMilliseconds < _musicBrainzQueryIntervalMs) { // MusicBrainz is extremely adamant about limiting to one request per second - var delayMs = MusicBrainzQueryIntervalMs - _stopWatchMusicBrainz.ElapsedMilliseconds; + var delayMs = _musicBrainzQueryIntervalMs - _stopWatchMusicBrainz.ElapsedMilliseconds; await Task.Delay((int)delayMs, cancellationToken).ConfigureAwait(false); } @@ -783,7 +777,7 @@ namespace MediaBrowser.Providers.Music response = await _httpClient.SendAsync(options, "GET").ConfigureAwait(false); - // We retry a finite number of times, and only whilst MB is indcating 503 (throttling) + // We retry a finite number of times, and only whilst MB is indicating 503 (throttling) } while (attempts < MusicBrainzQueryAttempts && response.StatusCode == HttpStatusCode.ServiceUnavailable); diff --git a/MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/ArtistProvider.cs similarity index 94% rename from MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs rename to MediaBrowser.Providers/Plugins/MusicBrainz/ArtistProvider.cs index 5d675392c9..260a3b6e7b 100644 --- a/MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs +++ b/MediaBrowser.Providers/Plugins/MusicBrainz/ArtistProvider.cs @@ -14,6 +14,7 @@ using MediaBrowser.Controller.Extensions; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Providers; +using MediaBrowser.Providers.Plugins.MusicBrainz; namespace MediaBrowser.Providers.Music { @@ -22,6 +23,12 @@ namespace MediaBrowser.Providers.Music /// public async Task> GetSearchResults(ArtistInfo searchInfo, CancellationToken cancellationToken) { + // TODO maybe remove when artist metadata can be disabled + if (!Plugin.Instance.Configuration.Enable) + { + return Enumerable.Empty(); + } + var musicBrainzId = searchInfo.GetMusicBrainzArtistId(); if (!string.IsNullOrWhiteSpace(musicBrainzId)) @@ -226,6 +233,12 @@ namespace MediaBrowser.Providers.Music Item = new MusicArtist() }; + // TODO maybe remove when artist metadata can be disabled + if (!Plugin.Instance.Configuration.Enable) + { + return result; + } + var musicBrainzId = id.GetMusicBrainzArtistId(); if (string.IsNullOrWhiteSpace(musicBrainzId)) @@ -237,8 +250,12 @@ namespace MediaBrowser.Providers.Music if (singleResult != null) { musicBrainzId = singleResult.GetProviderId(MetadataProviders.MusicBrainzArtist); - //result.Item.Name = singleResult.Name; result.Item.Overview = singleResult.Overview; + + if (Plugin.Instance.Configuration.ReplaceArtistName) + { + result.Item.Name = singleResult.Name; + } } } diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/PluginConfiguration.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/PluginConfiguration.cs new file mode 100644 index 0000000000..6910b4bb44 --- /dev/null +++ b/MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/PluginConfiguration.cs @@ -0,0 +1,44 @@ +using MediaBrowser.Model.Plugins; + +namespace MediaBrowser.Providers.Plugins.MusicBrainz +{ + public class PluginConfiguration : BasePluginConfiguration + { + private string _server = Plugin.DefaultServer; + + private long _rateLimit = Plugin.DefaultRateLimit; + + public string Server + { + get + { + return _server; + } + + set + { + _server = value.TrimEnd('/'); + } + } + + public long RateLimit + { + get + { + return _rateLimit; + } + + set + { + if (value < Plugin.DefaultRateLimit && _server == Plugin.DefaultServer) + { + RateLimit = Plugin.DefaultRateLimit; + } + } + } + + public bool Enable { get; set; } + + public bool ReplaceArtistName { get; set; } + } +} diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/config.html b/MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/config.html new file mode 100644 index 0000000000..1f02461da2 --- /dev/null +++ b/MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/config.html @@ -0,0 +1,69 @@ + + + + MusicBrainz + + +
+
+
+
+
+ +
This can be a mirror of the official server or even a custom server.
+
+
+ +
Span of time between requests in milliseconds. The official server is limited to one request every two seconds.
+
+ + +
+
+ +
+
+
+
+ +
+ + diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/ExternalIds.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/ExternalIds.cs new file mode 100644 index 0000000000..03565a34c4 --- /dev/null +++ b/MediaBrowser.Providers/Plugins/MusicBrainz/ExternalIds.cs @@ -0,0 +1,98 @@ +using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Entities; +using MediaBrowser.Providers.Plugins.MusicBrainz; + +namespace MediaBrowser.Providers.Music +{ + public class MusicBrainzReleaseGroupExternalId : IExternalId + { + /// + public string Name => "MusicBrainz Release Group"; + + /// + public string Key => MetadataProviders.MusicBrainzReleaseGroup.ToString(); + + /// + public string UrlFormatString => Plugin.Instance.Configuration.Server + "/release-group/{0}"; + + /// + public bool Supports(IHasProviderIds item) => item is Audio || item is MusicAlbum; + } + + public class MusicBrainzAlbumArtistExternalId : IExternalId + { + /// + public string Name => "MusicBrainz Album Artist"; + + /// + public string Key => MetadataProviders.MusicBrainzAlbumArtist.ToString(); + + /// + public string UrlFormatString => Plugin.Instance.Configuration.Server + "/artist/{0}"; + + /// + public bool Supports(IHasProviderIds item) => item is Audio; + } + + public class MusicBrainzAlbumExternalId : IExternalId + { + /// + public string Name => "MusicBrainz Album"; + + /// + public string Key => MetadataProviders.MusicBrainzAlbum.ToString(); + + /// + public string UrlFormatString => Plugin.Instance.Configuration.Server + "/release/{0}"; + + /// + public bool Supports(IHasProviderIds item) => item is Audio || item is MusicAlbum; + } + + public class MusicBrainzArtistExternalId : IExternalId + { + /// + public string Name => "MusicBrainz"; + + /// + public string Key => MetadataProviders.MusicBrainzArtist.ToString(); + + /// + public string UrlFormatString => Plugin.Instance.Configuration.Server + "/artist/{0}"; + + /// + public bool Supports(IHasProviderIds item) => item is MusicArtist; + } + + public class MusicBrainzOtherArtistExternalId : IExternalId + { + /// + public string Name => "MusicBrainz Artist"; + + /// + + public string Key => MetadataProviders.MusicBrainzArtist.ToString(); + + /// + public string UrlFormatString => Plugin.Instance.Configuration.Server + "/artist/{0}"; + + /// + public bool Supports(IHasProviderIds item) => item is Audio || item is MusicAlbum; + } + + public class MusicBrainzTrackId : IExternalId + { + /// + public string Name => "MusicBrainz Track"; + + /// + public string Key => MetadataProviders.MusicBrainzTrack.ToString(); + + /// + public string UrlFormatString => Plugin.Instance.Configuration.Server + "/track/{0}"; + + /// + public bool Supports(IHasProviderIds item) => item is Audio; + } +} diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/Plugin.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/Plugin.cs new file mode 100644 index 0000000000..8e1b3ea376 --- /dev/null +++ b/MediaBrowser.Providers/Plugins/MusicBrainz/Plugin.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.Plugins; +using MediaBrowser.Model.Plugins; +using MediaBrowser.Model.Serialization; + +namespace MediaBrowser.Providers.Plugins.MusicBrainz +{ + public class Plugin : BasePlugin, IHasWebPages + { + public static Plugin Instance { get; private set; } + + public override Guid Id => new Guid("8c95c4d2-e50c-4fb0-a4f3-6c06ff0f9a1a"); + + public override string Name => "MusicBrainz"; + + public override string Description => "Get artist and album metadata from any MusicBrainz server."; + + public const string DefaultServer = "https://musicbrainz.org"; + + public const long DefaultRateLimit = 2000u; + + public Plugin(IApplicationPaths applicationPaths, IXmlSerializer xmlSerializer) + : base(applicationPaths, xmlSerializer) + { + Instance = this; + } + + public IEnumerable GetPages() + { + yield return new PluginPageInfo + { + Name = Name, + EmbeddedResourcePath = GetType().Namespace + ".Configuration.config.html" + }; + } + } +} diff --git a/MediaBrowser.Providers/Studios/StudioMetadataService.cs b/MediaBrowser.Providers/Studios/StudioMetadataService.cs index 6fa30c753b..847e47f1b1 100644 --- a/MediaBrowser.Providers/Studios/StudioMetadataService.cs +++ b/MediaBrowser.Providers/Studios/StudioMetadataService.cs @@ -13,7 +13,7 @@ namespace MediaBrowser.Providers.Studios { public StudioMetadataService( IServerConfigurationManager serverConfigurationManager, - ILogger logger, + ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, ILibraryManager libraryManager) : base(serverConfigurationManager, logger, providerManager, fileSystem, libraryManager) diff --git a/MediaBrowser.Providers/TV/EpisodeMetadataService.cs b/MediaBrowser.Providers/TV/EpisodeMetadataService.cs index 89615f4068..758c47ba05 100644 --- a/MediaBrowser.Providers/TV/EpisodeMetadataService.cs +++ b/MediaBrowser.Providers/TV/EpisodeMetadataService.cs @@ -14,7 +14,7 @@ namespace MediaBrowser.Providers.TV { public EpisodeMetadataService( IServerConfigurationManager serverConfigurationManager, - ILogger logger, + ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, ILibraryManager libraryManager) diff --git a/MediaBrowser.Providers/TV/SeasonMetadataService.cs b/MediaBrowser.Providers/TV/SeasonMetadataService.cs index 0672f886a5..eb8032e0e1 100644 --- a/MediaBrowser.Providers/TV/SeasonMetadataService.cs +++ b/MediaBrowser.Providers/TV/SeasonMetadataService.cs @@ -17,7 +17,7 @@ namespace MediaBrowser.Providers.TV { public SeasonMetadataService( IServerConfigurationManager serverConfigurationManager, - ILogger logger, + ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, ILibraryManager libraryManager) diff --git a/MediaBrowser.Providers/TV/SeriesMetadataService.cs b/MediaBrowser.Providers/TV/SeriesMetadataService.cs index e9e633ce77..7bc5a87d8a 100644 --- a/MediaBrowser.Providers/TV/SeriesMetadataService.cs +++ b/MediaBrowser.Providers/TV/SeriesMetadataService.cs @@ -21,7 +21,7 @@ namespace MediaBrowser.Providers.TV public SeriesMetadataService( IServerConfigurationManager serverConfigurationManager, - ILogger logger, + ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, ILibraryManager libraryManager, diff --git a/MediaBrowser.Providers/Tmdb/BoxSets/TmdbBoxSetProvider.cs b/MediaBrowser.Providers/Tmdb/BoxSets/TmdbBoxSetProvider.cs index a215177a9a..dd3783ffbf 100644 --- a/MediaBrowser.Providers/Tmdb/BoxSets/TmdbBoxSetProvider.cs +++ b/MediaBrowser.Providers/Tmdb/BoxSets/TmdbBoxSetProvider.cs @@ -37,7 +37,14 @@ namespace MediaBrowser.Providers.Tmdb.BoxSets private readonly IHttpClient _httpClient; private readonly ILibraryManager _libraryManager; - public TmdbBoxSetProvider(ILogger logger, IJsonSerializer json, IServerConfigurationManager config, IFileSystem fileSystem, ILocalizationManager localization, IHttpClient httpClient, ILibraryManager libraryManager) + public TmdbBoxSetProvider( + ILogger logger, + IJsonSerializer json, + IServerConfigurationManager config, + IFileSystem fileSystem, + ILocalizationManager localization, + IHttpClient httpClient, + ILibraryManager libraryManager) { _logger = logger; _json = json; diff --git a/MediaBrowser.Providers/Tmdb/Movies/TmdbMovieProvider.cs b/MediaBrowser.Providers/Tmdb/Movies/TmdbMovieProvider.cs index 861847f71f..fbb87d25dd 100644 --- a/MediaBrowser.Providers/Tmdb/Movies/TmdbMovieProvider.cs +++ b/MediaBrowser.Providers/Tmdb/Movies/TmdbMovieProvider.cs @@ -42,7 +42,15 @@ namespace MediaBrowser.Providers.Tmdb.Movies private readonly CultureInfo _usCulture = new CultureInfo("en-US"); - public TmdbMovieProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILogger logger, ILocalizationManager localization, ILibraryManager libraryManager, IApplicationHost appHost) + public TmdbMovieProvider( + IJsonSerializer jsonSerializer, + IHttpClient httpClient, + IFileSystem fileSystem, + IServerConfigurationManager configurationManager, + ILogger logger, + ILocalizationManager localization, + ILibraryManager libraryManager, + IApplicationHost appHost) { _jsonSerializer = jsonSerializer; _httpClient = httpClient; diff --git a/MediaBrowser.Providers/Tmdb/People/TmdbPersonProvider.cs b/MediaBrowser.Providers/Tmdb/People/TmdbPersonProvider.cs index 50af9913aa..5880011691 100644 --- a/MediaBrowser.Providers/Tmdb/People/TmdbPersonProvider.cs +++ b/MediaBrowser.Providers/Tmdb/People/TmdbPersonProvider.cs @@ -37,7 +37,12 @@ namespace MediaBrowser.Providers.Tmdb.People private readonly IHttpClient _httpClient; private readonly ILogger _logger; - public TmdbPersonProvider(IFileSystem fileSystem, IServerConfigurationManager configurationManager, IJsonSerializer jsonSerializer, IHttpClient httpClient, ILogger logger) + public TmdbPersonProvider( + IFileSystem fileSystem, + IServerConfigurationManager configurationManager, + IJsonSerializer jsonSerializer, + IHttpClient httpClient, + ILogger logger) { _fileSystem = fileSystem; _configurationManager = configurationManager; diff --git a/MediaBrowser.Providers/Tmdb/TV/TmdbSeriesProvider.cs b/MediaBrowser.Providers/Tmdb/TV/TmdbSeriesProvider.cs index 7dcb272d68..7195dc42a7 100644 --- a/MediaBrowser.Providers/Tmdb/TV/TmdbSeriesProvider.cs +++ b/MediaBrowser.Providers/Tmdb/TV/TmdbSeriesProvider.cs @@ -39,7 +39,14 @@ namespace MediaBrowser.Providers.Tmdb.TV private readonly IHttpClient _httpClient; private readonly ILibraryManager _libraryManager; - public TmdbSeriesProvider(IJsonSerializer jsonSerializer, IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILogger logger, ILocalizationManager localization, IHttpClient httpClient, ILibraryManager libraryManager) + public TmdbSeriesProvider( + IJsonSerializer jsonSerializer, + IFileSystem fileSystem, + IServerConfigurationManager configurationManager, + ILogger logger, + ILocalizationManager localization, + IHttpClient httpClient, + ILibraryManager libraryManager) { _jsonSerializer = jsonSerializer; _fileSystem = fileSystem; diff --git a/MediaBrowser.Providers/Users/UserMetadataService.cs b/MediaBrowser.Providers/Users/UserMetadataService.cs index 9c2e27816e..fb6c91b6c0 100644 --- a/MediaBrowser.Providers/Users/UserMetadataService.cs +++ b/MediaBrowser.Providers/Users/UserMetadataService.cs @@ -13,7 +13,7 @@ namespace MediaBrowser.Providers.Users { public UserMetadataService( IServerConfigurationManager serverConfigurationManager, - ILogger logger, + ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, ILibraryManager libraryManager) diff --git a/MediaBrowser.Providers/Videos/VideoMetadataService.cs b/MediaBrowser.Providers/Videos/VideoMetadataService.cs index 996af0368c..21378ada0d 100644 --- a/MediaBrowser.Providers/Videos/VideoMetadataService.cs +++ b/MediaBrowser.Providers/Videos/VideoMetadataService.cs @@ -13,7 +13,7 @@ namespace MediaBrowser.Providers.Videos { public VideoMetadataService( IServerConfigurationManager serverConfigurationManager, - ILogger logger, + ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, ILibraryManager libraryManager) diff --git a/MediaBrowser.Providers/Years/YearMetadataService.cs b/MediaBrowser.Providers/Years/YearMetadataService.cs index 414795e359..2a0fa19ea3 100644 --- a/MediaBrowser.Providers/Years/YearMetadataService.cs +++ b/MediaBrowser.Providers/Years/YearMetadataService.cs @@ -13,7 +13,7 @@ namespace MediaBrowser.Providers.Years { public YearMetadataService( IServerConfigurationManager serverConfigurationManager, - ILogger logger, + ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, ILibraryManager libraryManager) diff --git a/MediaBrowser.WebDashboard/Api/DashboardService.cs b/MediaBrowser.WebDashboard/Api/DashboardService.cs index a8768459ac..9e0aae7820 100644 --- a/MediaBrowser.WebDashboard/Api/DashboardService.cs +++ b/MediaBrowser.WebDashboard/Api/DashboardService.cs @@ -122,7 +122,7 @@ namespace MediaBrowser.WebDashboard.Api IResourceFileManager resourceFileManager, IServerConfigurationManager serverConfigurationManager, IFileSystem fileSystem, - ILogger logger, + ILogger logger, IHttpResultFactory resultFactory) { _appHost = appHost; diff --git a/MediaBrowser.XbmcMetadata/EntryPoint.cs b/MediaBrowser.XbmcMetadata/EntryPoint.cs index fe4d50efaa..f0a9e0ad29 100644 --- a/MediaBrowser.XbmcMetadata/EntryPoint.cs +++ b/MediaBrowser.XbmcMetadata/EntryPoint.cs @@ -21,7 +21,7 @@ namespace MediaBrowser.XbmcMetadata public EntryPoint( IUserDataManager userDataManager, - ILogger logger, + ILogger logger, IProviderManager providerManager, IConfigurationManager config) { diff --git a/MediaBrowser.XbmcMetadata/Providers/AlbumNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/AlbumNfoProvider.cs index 3517bc32c1..c31df91e38 100644 --- a/MediaBrowser.XbmcMetadata/Providers/AlbumNfoProvider.cs +++ b/MediaBrowser.XbmcMetadata/Providers/AlbumNfoProvider.cs @@ -15,7 +15,11 @@ namespace MediaBrowser.XbmcMetadata.Providers private readonly IConfigurationManager _config; private readonly IProviderManager _providerManager; - public AlbumNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config, IProviderManager providerManager) + public AlbumNfoProvider( + IFileSystem fileSystem, + ILogger logger, + IConfigurationManager config, + IProviderManager providerManager) : base(fileSystem) { _logger = logger; diff --git a/MediaBrowser.XbmcMetadata/Providers/ArtistNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/ArtistNfoProvider.cs index 03d8dbc7e5..bc87a06d6f 100644 --- a/MediaBrowser.XbmcMetadata/Providers/ArtistNfoProvider.cs +++ b/MediaBrowser.XbmcMetadata/Providers/ArtistNfoProvider.cs @@ -15,7 +15,11 @@ namespace MediaBrowser.XbmcMetadata.Providers private readonly IConfigurationManager _config; private readonly IProviderManager _providerManager; - public ArtistNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config, IProviderManager providerManager) + public ArtistNfoProvider( + IFileSystem fileSystem, + ILogger logger, + IConfigurationManager config, + IProviderManager providerManager) : base(fileSystem) { _logger = logger; diff --git a/MediaBrowser.XbmcMetadata/Providers/EpisodeNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/EpisodeNfoProvider.cs index b2278ba4a9..691298e75f 100644 --- a/MediaBrowser.XbmcMetadata/Providers/EpisodeNfoProvider.cs +++ b/MediaBrowser.XbmcMetadata/Providers/EpisodeNfoProvider.cs @@ -15,7 +15,11 @@ namespace MediaBrowser.XbmcMetadata.Providers private readonly IConfigurationManager _config; private readonly IProviderManager _providerManager; - public EpisodeNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config, IProviderManager providerManager) + public EpisodeNfoProvider( + IFileSystem fileSystem, + ILogger logger, + IConfigurationManager config, + IProviderManager providerManager) : base(fileSystem) { _logger = logger; diff --git a/MediaBrowser.XbmcMetadata/Providers/MovieNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/MovieNfoProvider.cs index d21164c022..452bed1f16 100644 --- a/MediaBrowser.XbmcMetadata/Providers/MovieNfoProvider.cs +++ b/MediaBrowser.XbmcMetadata/Providers/MovieNfoProvider.cs @@ -9,7 +9,11 @@ namespace MediaBrowser.XbmcMetadata.Providers { public class MovieNfoProvider : BaseVideoNfoProvider { - public MovieNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config, IProviderManager providerManager) + public MovieNfoProvider( + IFileSystem fileSystem, + ILogger logger, + IConfigurationManager config, + IProviderManager providerManager) : base(fileSystem, logger, config, providerManager) { } @@ -17,7 +21,11 @@ namespace MediaBrowser.XbmcMetadata.Providers public class MusicVideoNfoProvider : BaseVideoNfoProvider { - public MusicVideoNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config, IProviderManager providerManager) + public MusicVideoNfoProvider( + IFileSystem fileSystem, + ILogger logger, + IConfigurationManager config, + IProviderManager providerManager) : base(fileSystem, logger, config, providerManager) { } @@ -25,7 +33,11 @@ namespace MediaBrowser.XbmcMetadata.Providers public class VideoNfoProvider : BaseVideoNfoProvider