aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.copr/Makefile8
-rw-r--r--.dockerignore1
-rw-r--r--.drone.yml103
-rw-r--r--.editorconfig4
-rw-r--r--.gitignore2
-rw-r--r--BDInfo/BDROM.cs4
-rw-r--r--BDInfo/TSPlaylistFile.cs7
-rw-r--r--BDInfo/TSStreamClipFile.cs4
-rw-r--r--CONTRIBUTORS.md2
-rw-r--r--Dockerfile13
-rw-r--r--Dockerfile.arm32
-rw-r--r--Dockerfile.arm6432
-rw-r--r--Emby.Dlna/PlayTo/Device.cs63
-rw-r--r--Emby.Dlna/PlayTo/PlayToManager.cs10
-rw-r--r--Emby.IsoMounting/IsoMounter/LinuxIsoManager.cs4
-rw-r--r--Emby.Notifications/CoreNotificationTypes.cs32
-rw-r--r--Emby.Notifications/Notifications.cs32
-rw-r--r--Emby.Photos/PhotoProvider.cs5
-rw-r--r--Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs6
-rw-r--r--Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs2
-rw-r--r--Emby.Server.Implementations/AppBase/ConfigurationHelper.cs4
-rw-r--r--Emby.Server.Implementations/ApplicationHost.cs58
-rw-r--r--Emby.Server.Implementations/Archiving/ZipClient.cs6
-rw-r--r--Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs2
-rw-r--r--Emby.Server.Implementations/Collections/CollectionImageProvider.cs8
-rw-r--r--Emby.Server.Implementations/Collections/CollectionManager.cs4
-rw-r--r--Emby.Server.Implementations/ConfigurationOptions.cs12
-rw-r--r--Emby.Server.Implementations/Data/BaseSqliteRepository.cs27
-rw-r--r--Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs11
-rw-r--r--Emby.Server.Implementations/Data/SqliteItemRepository.cs285
-rw-r--r--Emby.Server.Implementations/Devices/DeviceId.cs12
-rw-r--r--Emby.Server.Implementations/Devices/DeviceManager.cs12
-rw-r--r--Emby.Server.Implementations/Dto/DtoService.cs12
-rw-r--r--Emby.Server.Implementations/Emby.Server.Implementations.csproj1
-rw-r--r--Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs2
-rw-r--r--Emby.Server.Implementations/FFMpeg/FFMpegLoader.cs13
-rw-r--r--Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs38
-rw-r--r--Emby.Server.Implementations/HttpServer/HttpListenerHost.cs12
-rw-r--r--Emby.Server.Implementations/HttpServer/HttpResultFactory.cs134
-rw-r--r--Emby.Server.Implementations/HttpServer/StreamWriter.cs8
-rw-r--r--Emby.Server.Implementations/IO/FileRefresher.cs10
-rw-r--r--Emby.Server.Implementations/IO/LibraryMonitor.cs21
-rw-r--r--Emby.Server.Implementations/IO/ManagedFileSystem.cs59
-rw-r--r--Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs14
-rw-r--r--Emby.Server.Implementations/Library/LibraryManager.cs48
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs5
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs3
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs8
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/SpecialFolderResolver.cs2
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs4
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/VideoResolver.cs4
-rw-r--r--Emby.Server.Implementations/Library/UserManager.cs9
-rw-r--r--Emby.Server.Implementations/Library/Validators/PeopleValidator.cs4
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs6
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs15
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs4
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs5
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs5
-rw-r--r--Emby.Server.Implementations/LiveTv/LiveTvManager.cs2
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs5
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs13
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs9
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs4
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs2
-rw-r--r--Emby.Server.Implementations/Localization/Core/da.json50
-rw-r--r--Emby.Server.Implementations/Localization/Core/de.json54
-rw-r--r--Emby.Server.Implementations/Localization/Core/en-GB.json2
-rw-r--r--Emby.Server.Implementations/Localization/Core/en-US.json2
-rw-r--r--Emby.Server.Implementations/Localization/Core/es.json46
-rw-r--r--Emby.Server.Implementations/Localization/Core/fr.json2
-rw-r--r--Emby.Server.Implementations/Localization/Core/hu.json70
-rw-r--r--Emby.Server.Implementations/Localization/Core/it.json8
-rw-r--r--Emby.Server.Implementations/Localization/Core/kk.json188
-rw-r--r--Emby.Server.Implementations/Localization/Core/ms.json6
-rw-r--r--Emby.Server.Implementations/Localization/Core/nl.json36
-rw-r--r--Emby.Server.Implementations/Localization/Core/ru.json10
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs2
-rw-r--r--Emby.Server.Implementations/Serialization/JsonSerializer.cs21
-rw-r--r--Emby.Server.Implementations/Sorting/AiredEpisodeOrderComparer.cs2
-rw-r--r--Emby.Server.Implementations/Sorting/SeriesSortNameComparer.cs2
-rw-r--r--Emby.Server.Implementations/Updates/InstallationManager.cs40
-rw-r--r--Jellyfin.Drawing.Skia/SkiaEncoder.cs2
-rw-r--r--Jellyfin.Server/CoreAppHost.cs28
-rw-r--r--Jellyfin.Server/Jellyfin.Server.csproj3
-rw-r--r--Jellyfin.Server/Program.cs119
-rw-r--r--Jellyfin.Server/SocketSharp/HttpFile.cs4
-rw-r--r--Jellyfin.Server/SocketSharp/HttpPostedFile.cs204
-rw-r--r--Jellyfin.Server/SocketSharp/RequestMono.cs354
-rw-r--r--Jellyfin.Server/SocketSharp/SharpWebSocket.cs44
-rw-r--r--Jellyfin.Server/SocketSharp/WebSocketSharpListener.cs61
-rw-r--r--Jellyfin.Server/SocketSharp/WebSocketSharpRequest.cs92
-rw-r--r--Jellyfin.Server/SocketSharp/WebSocketSharpResponse.cs88
-rw-r--r--MediaBrowser.Api/BaseApiService.cs32
-rw-r--r--MediaBrowser.Api/EnvironmentService.cs10
-rw-r--r--MediaBrowser.Api/Playback/BaseStreamingService.cs2
-rw-r--r--MediaBrowser.Api/Playback/Progressive/AudioService.cs5
-rw-r--r--MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs3
-rw-r--r--MediaBrowser.Api/Playback/Progressive/VideoService.cs5
-rw-r--r--MediaBrowser.Api/Playback/UniversalAudioService.cs6
-rw-r--r--MediaBrowser.Api/ScheduledTasks/ScheduledTaskService.cs16
-rw-r--r--MediaBrowser.Api/Session/SessionInfoWebSocketListener.cs2
-rw-r--r--MediaBrowser.Api/System/ActivityLogWebSocketListener.cs2
-rw-r--r--MediaBrowser.Api/UserLibrary/ArtistsService.cs2
-rw-r--r--MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs4
-rw-r--r--MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs8
-rw-r--r--MediaBrowser.Api/UserLibrary/GenresService.cs2
-rw-r--r--MediaBrowser.Api/UserLibrary/ItemsService.cs28
-rw-r--r--MediaBrowser.Api/UserLibrary/MusicGenresService.cs2
-rw-r--r--MediaBrowser.Api/UserLibrary/PersonsService.cs6
-rw-r--r--MediaBrowser.Api/UserLibrary/StudiosService.cs2
-rw-r--r--MediaBrowser.Common/Configuration/IApplicationPaths.cs6
-rw-r--r--MediaBrowser.Common/IApplicationHost.cs12
-rw-r--r--MediaBrowser.Controller/Library/ILibraryManager.cs14
-rw-r--r--MediaBrowser.Controller/Net/StaticResultOptions.cs2
-rw-r--r--MediaBrowser.Controller/Persistence/IItemRepository.cs14
-rw-r--r--MediaBrowser.LocalMetadata/Providers/PlaylistXmlProvider.cs2
-rw-r--r--MediaBrowser.Model/Configuration/BaseApplicationConfiguration.cs15
-rw-r--r--MediaBrowser.Model/Dlna/StreamBuilder.cs2
-rw-r--r--MediaBrowser.Model/IO/IFileSystem.cs2
-rw-r--r--MediaBrowser.Model/Serialization/IJsonSerializer.cs8
-rw-r--r--MediaBrowser.Model/System/SystemInfo.cs6
-rw-r--r--MediaBrowser.Providers/BoxSets/MovieDbBoxSetImageProvider.cs2
-rw-r--r--MediaBrowser.Providers/Manager/GenericPriorityQueue.cs402
-rw-r--r--MediaBrowser.Providers/Manager/GenericPriorityQueueNode.cs22
-rw-r--r--MediaBrowser.Providers/Manager/IFixedSizePriorityQueue.cs24
-rw-r--r--MediaBrowser.Providers/Manager/IPriorityQueue.cs56
-rw-r--r--MediaBrowser.Providers/Manager/SimplePriorityQueue.cs247
-rw-r--r--MediaBrowser.Providers/MediaBrowser.Providers.csproj1
-rw-r--r--MediaBrowser.Providers/Movies/MovieDbImageProvider.cs2
-rw-r--r--MediaBrowser.Providers/Music/MusicVideoMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/Photos/PhotoAlbumMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/Photos/PhotoMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/TV/Omdb/OmdbEpisodeProvider.cs2
-rw-r--r--MediaBrowser.Providers/TV/TheMovieDb/MovieDbEpisodeProvider.cs2
m---------MediaBrowser.WebDashboard/jellyfin-web0
-rw-r--r--MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs2
-rw-r--r--SharedVersion.cs4
-rw-r--r--deployment/debian-package-x64/pkg-src/changelog108
-rw-r--r--deployment/debian-package-x64/pkg-src/control7
-rwxr-xr-xdeployment/fedora-package-x64/create_tarball.sh55
-rwxr-xr-xdeployment/fedora-package-x64/package.sh47
-rw-r--r--deployment/fedora-package-x64/pkg-src/jellyfin.spec108
-rwxr-xr-xdeployment/win-x64/package.sh3
-rw-r--r--hooks/pre_build6
-rw-r--r--jellyfin.ruleset8
146 files changed, 1786 insertions, 2378 deletions
diff --git a/.copr/Makefile b/.copr/Makefile
new file mode 100644
index 000000000..84b98a011
--- /dev/null
+++ b/.copr/Makefile
@@ -0,0 +1,8 @@
+srpm:
+ dnf -y install git
+ git submodule update --init --recursive
+ cd deployment/fedora-package-x64; \
+ ./create_tarball.sh; \
+ rpmbuild -bs pkg-src/jellyfin.spec \
+ --define "_sourcedir $$PWD/pkg-src/" \
+ --define "_srcrpmdir $(outdir)"
diff --git a/.dockerignore b/.dockerignore
index 45e543525..ffd6de2d6 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -8,3 +8,4 @@ README.md
deployment/*/dist
deployment/*/pkg-dist
deployment/collect-dist/
+ci/
diff --git a/.drone.yml b/.drone.yml
index 98db4884b..7705f4f93 100644
--- a/.drone.yml
+++ b/.drone.yml
@@ -1,12 +1,111 @@
+---
kind: pipeline
-name: build
+name: build-debug
steps:
- name: submodules
image: docker:git
commands:
- git submodule update --init --recursive
+
+- name: build
+ image: microsoft/dotnet:2-sdk
+ commands:
+ - dotnet publish "Jellyfin.Server" --configuration Debug --output "../ci/ci-debug"
+
+---
+kind: pipeline
+name: build-release
+
+steps:
+- name: submodules
+ image: docker:git
+ commands:
+ - git submodule update --init --recursive
+
+- name: build
+ image: microsoft/dotnet:2-sdk
+ commands:
+ - dotnet publish "Jellyfin.Server" --configuration Release --output "../ci/ci-release"
+
+---
+
+kind: pipeline
+name: check-abi
+
+steps:
+- name: submodules
+ image: docker:git
+ commands:
+ - git submodule update --init --recursive
+
- name: build
image: microsoft/dotnet:2-sdk
commands:
- - dotnet publish --configuration release --output /release Jellyfin.Server
+ - dotnet publish "Jellyfin.Server" --configuration Release --output "../ci/ci-release"
+
+- name: clone-dotnet-compat
+ image: docker:git
+ commands:
+ - git clone --depth 1 https://github.com/EraYaN/dotnet-compatibility ci/dotnet-compatibility
+
+- name: build-dotnet-compat
+ image: microsoft/dotnet:2-sdk
+ commands:
+ - dotnet publish "ci/dotnet-compatibility/CompatibilityCheckerCoreCLI" --configuration Release --output "../../ci-tools"
+
+- name: download-last-nuget-release-common
+ image: plugins/download
+ settings:
+ source: https://www.nuget.org/api/v2/package/Jellyfin.Common
+ destination: ci/Jellyfin.Common.nupkg
+
+- name: download-last-nuget-release-model
+ image: plugins/download
+ settings:
+ source: https://www.nuget.org/api/v2/package/Jellyfin.Model
+ destination: ci/Jellyfin.Model.nupkg
+
+- name: download-last-nuget-release-controller
+ image: plugins/download
+ settings:
+ source: https://www.nuget.org/api/v2/package/Jellyfin.Controller
+ destination: ci/Jellyfin.Controller.nupkg
+
+- name: download-last-nuget-release-naming
+ image: plugins/download
+ settings:
+ source: https://www.nuget.org/api/v2/package/Jellyfin.Naming
+ destination: ci/Jellyfin.Naming.nupkg
+
+- name: extract-downloaded-nuget-packages
+ image: garthk/unzip
+ commands:
+ - unzip -j ci/Jellyfin.Common.nupkg "*.dll" -d ci/nuget-packages
+ - unzip -j ci/Jellyfin.Model.nupkg "*.dll" -d ci/nuget-packages
+ - unzip -j ci/Jellyfin.Controller.nupkg "*.dll" -d ci/nuget-packages
+ - unzip -j ci/Jellyfin.Naming.nupkg "*.dll" -d ci/nuget-packages
+
+- name: run-dotnet-compat-common
+ image: microsoft/dotnet:2-runtime
+ err_ignore: true
+ commands:
+ - dotnet ci/ci-tools/CompatibilityCheckerCoreCLI.dll ci/nuget-packages/MediaBrowser.Common.dll ci/ci-release/MediaBrowser.Common.dll
+
+- name: run-dotnet-compat-model
+ image: microsoft/dotnet:2-runtime
+ err_ignore: true
+ commands:
+ - dotnet ci/ci-tools/CompatibilityCheckerCoreCLI.dll ci/nuget-packages/MediaBrowser.Model.dll ci/ci-release/MediaBrowser.Model.dll
+
+- name: run-dotnet-compat-controller
+ image: microsoft/dotnet:2-runtime
+ err_ignore: true
+ commands:
+ - dotnet ci/ci-tools/CompatibilityCheckerCoreCLI.dll ci/nuget-packages/MediaBrowser.Controller.dll ci/ci-release/MediaBrowser.Controller.dll
+
+- name: run-dotnet-compat-naming
+ image: microsoft/dotnet:2-runtime
+ err_ignore: true
+ commands:
+ - dotnet ci/ci-tools/CompatibilityCheckerCoreCLI.dll ci/nuget-packages/Emby.Naming.dll ci/ci-release/Emby.Naming.dll
diff --git a/.editorconfig b/.editorconfig
index 21f27df13..dc9aaa3ed 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -15,6 +15,10 @@ insert_final_newline = true
end_of_line = lf
max_line_length = null
+# YAML indentation
+[*.{yml,yaml}]
+indent_size = 2
+
# XML indentation
[*.{csproj,xml}]
indent_size = 2
diff --git a/.gitignore b/.gitignore
index aef666272..65e47747e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -264,3 +264,5 @@ deployment/**/pkg-dist-tmp/
deployment/collect-dist/
jellyfin_version.ini
+
+ci/
diff --git a/BDInfo/BDROM.cs b/BDInfo/BDROM.cs
index 4360ff1d4..6759ed55a 100644
--- a/BDInfo/BDROM.cs
+++ b/BDInfo/BDROM.cs
@@ -165,7 +165,7 @@ namespace BDInfo
foreach (var file in files)
{
PlaylistFiles.Add(
- file.Name.ToUpper(), new TSPlaylistFile(this, file, _fileSystem));
+ file.Name.ToUpper(), new TSPlaylistFile(this, file));
}
}
@@ -185,7 +185,7 @@ namespace BDInfo
foreach (var file in files)
{
StreamClipFiles.Add(
- file.Name.ToUpper(), new TSStreamClipFile(file, _fileSystem));
+ file.Name.ToUpper(), new TSStreamClipFile(file));
}
}
diff --git a/BDInfo/TSPlaylistFile.cs b/BDInfo/TSPlaylistFile.cs
index 6e91f6e40..1cc629b1d 100644
--- a/BDInfo/TSPlaylistFile.cs
+++ b/BDInfo/TSPlaylistFile.cs
@@ -28,7 +28,6 @@ namespace BDInfo
{
public class TSPlaylistFile
{
- private readonly IFileSystem _fileSystem;
private FileSystemMetadata FileInfo = null;
public string FileType = null;
public bool IsInitialized = false;
@@ -64,21 +63,19 @@ namespace BDInfo
new List<TSGraphicsStream>();
public TSPlaylistFile(BDROM bdrom,
- FileSystemMetadata fileInfo, IFileSystem fileSystem)
+ FileSystemMetadata fileInfo)
{
BDROM = bdrom;
FileInfo = fileInfo;
- _fileSystem = fileSystem;
Name = fileInfo.Name.ToUpper();
}
public TSPlaylistFile(BDROM bdrom,
string name,
- List<TSStreamClip> clips, IFileSystem fileSystem)
+ List<TSStreamClip> clips)
{
BDROM = bdrom;
Name = name;
- _fileSystem = fileSystem;
IsCustom = true;
foreach (var clip in clips)
{
diff --git a/BDInfo/TSStreamClipFile.cs b/BDInfo/TSStreamClipFile.cs
index d840542ba..e1097b23d 100644
--- a/BDInfo/TSStreamClipFile.cs
+++ b/BDInfo/TSStreamClipFile.cs
@@ -28,7 +28,6 @@ namespace BDInfo
{
public class TSStreamClipFile
{
- private readonly IFileSystem _fileSystem;
public FileSystemMetadata FileInfo = null;
public string FileType = null;
public bool IsValid = false;
@@ -37,10 +36,9 @@ namespace BDInfo
public Dictionary<ushort, TSStream> Streams =
new Dictionary<ushort, TSStream>();
- public TSStreamClipFile(FileSystemMetadata fileInfo, IFileSystem fileSystem)
+ public TSStreamClipFile(FileSystemMetadata fileInfo)
{
FileInfo = fileInfo;
- _fileSystem = fileSystem;
Name = fileInfo.Name.ToUpper();
}
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index 5e3455fba..39149910c 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -18,6 +18,8 @@
- [dkanada](https://github.com/dkanada)
- [LogicalPhallacy](https://github.com/LogicalPhallacy/)
- [RazeLighter777](https://github.com/RazeLighter777)
+ - [WillWill56](https://github.com/WillWill56)
+ - [fruhnow](https://github.com/fruhnow)
# Emby Contributors
diff --git a/Dockerfile b/Dockerfile
index 266b59f1c..6c0d2515f 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -3,9 +3,8 @@ ARG DOTNET_VERSION=2
FROM microsoft/dotnet:${DOTNET_VERSION}-sdk as builder
WORKDIR /repo
COPY . .
-RUN export DOTNET_CLI_TELEMETRY_OPTOUT=1 \
- && dotnet clean \
- && dotnet publish \
+ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
+RUN dotnet publish \
--configuration release \
--output /jellyfin \
Jellyfin.Server
@@ -18,9 +17,11 @@ RUN apt-get update \
libfontconfig1 \
&& apt-get clean autoclean \
&& apt-get autoremove \
- && rm -rf /var/lib/{apt,dpkg,cache,log}
+ && rm -rf /var/lib/{apt,dpkg,cache,log} \
+ && mkdir -p /cache /config /media \
+ && chmod 777 /cache /config /media
COPY --from=ffmpeg / /
COPY --from=builder /jellyfin /jellyfin
EXPOSE 8096
-VOLUME /config /media
-ENTRYPOINT dotnet /jellyfin/jellyfin.dll --datadir /config
+VOLUME /cache /config /media
+ENTRYPOINT dotnet /jellyfin/jellyfin.dll --datadir /config --cachedir /cache
diff --git a/Dockerfile.arm b/Dockerfile.arm
index 039274197..9d1c30619 100644
--- a/Dockerfile.arm
+++ b/Dockerfile.arm
@@ -1,24 +1,36 @@
+# Requires binfm_misc registration
+# https://github.com/multiarch/qemu-user-static#binfmt_misc-register
ARG DOTNET_VERSION=3.0
-FROM microsoft/dotnet:${DOTNET_VERSION}-sdk-stretch-arm32v7 as builder
+FROM multiarch/qemu-user-static:x86_64-arm as qemu
+FROM alpine as qemu_extract
+COPY --from=qemu /usr/bin qemu-arm-static.tar.gz
+RUN tar -xzvf qemu-arm-static.tar.gz
+
+FROM microsoft/dotnet:${DOTNET_VERSION}-sdk-stretch as builder
WORKDIR /repo
COPY . .
-#TODO Remove or update the sed line when we update dotnet version.
-RUN export DOTNET_CLI_TELEMETRY_OPTOUT=1 \
- && find . -type f -exec sed -i 's/netcoreapp2.1/netcoreapp3.0/g' {} \; \
- && dotnet clean -maxcpucount:1 \
- && dotnet publish \
- -maxcpucount:1 \
+ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
+# TODO Remove or update the sed line when we update dotnet version.
+RUN find . -type f -exec sed -i 's/netcoreapp2.1/netcoreapp3.0/g' {} \;
+# Discard objs - may cause failures if exists
+RUN find . -type d -name obj | xargs -r rm -r
+# Build
+RUN dotnet publish \
+ -r linux-arm \
--configuration release \
--output /jellyfin \
Jellyfin.Server
FROM microsoft/dotnet:${DOTNET_VERSION}-runtime-stretch-slim-arm32v7
+COPY --from=qemu_extract qemu-arm-static /usr/bin
RUN apt-get update \
- && apt-get install -y ffmpeg
+ && apt-get install --no-install-recommends --no-install-suggests -y ffmpeg \
+ && mkdir -p /cache /config /media \
+ && chmod 777 /cache /config /media
COPY --from=builder /jellyfin /jellyfin
EXPOSE 8096
-VOLUME /config /media
-ENTRYPOINT dotnet /jellyfin/jellyfin.dll --datadir /config
+VOLUME /cache /config /media
+ENTRYPOINT dotnet /jellyfin/jellyfin.dll --datadir /config --cachedir /cache
diff --git a/Dockerfile.arm64 b/Dockerfile.arm64
index 06ba21b91..e61aaa167 100644
--- a/Dockerfile.arm64
+++ b/Dockerfile.arm64
@@ -1,33 +1,37 @@
-# Requires binfm_misc registration for aarch64
+# Requires binfm_misc registration
# https://github.com/multiarch/qemu-user-static#binfmt_misc-register
ARG DOTNET_VERSION=3.0
FROM multiarch/qemu-user-static:x86_64-aarch64 as qemu
FROM alpine as qemu_extract
-COPY --from=qemu /usr/bin qemu_user_static.tgz
-RUN tar -xzvf qemu_user_static.tgz
+COPY --from=qemu /usr/bin qemu-aarch64-static.tar.gz
+RUN tar -xzvf qemu-aarch64-static.tar.gz
-FROM microsoft/dotnet:${DOTNET_VERSION}-sdk-stretch-arm64v8 as builder
-COPY --from=qemu_extract qemu-* /usr/bin
+FROM microsoft/dotnet:${DOTNET_VERSION}-sdk-stretch as builder
WORKDIR /repo
COPY . .
-#TODO Remove or update the sed line when we update dotnet version.
-RUN export DOTNET_CLI_TELEMETRY_OPTOUT=1 \
- && find . -type f -exec sed -i 's/netcoreapp2.1/netcoreapp3.0/g' {} \; \
- && dotnet clean \
- && dotnet publish \
+ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
+# TODO Remove or update the sed line when we update dotnet version.
+RUN find . -type f -exec sed -i 's/netcoreapp2.1/netcoreapp3.0/g' {} \;
+# Discard objs - may cause failures if exists
+RUN find . -type d -name obj | xargs -r rm -r
+# Build
+RUN dotnet publish \
+ -r linux-arm64 \
--configuration release \
--output /jellyfin \
Jellyfin.Server
FROM microsoft/dotnet:${DOTNET_VERSION}-runtime-stretch-slim-arm64v8
+COPY --from=qemu_extract qemu-aarch64-static /usr/bin
RUN apt-get update \
- && apt-get install -y ffmpeg
-COPY --from=qemu_extract qemu-* /usr/bin
+ && apt-get install --no-install-recommends --no-install-suggests -y ffmpeg \
+ && mkdir -p /cache /config /media \
+ && chmod 777 /cache /config /media
COPY --from=builder /jellyfin /jellyfin
EXPOSE 8096
-VOLUME /config /media
-ENTRYPOINT dotnet /jellyfin/jellyfin.dll --datadir /config
+VOLUME /cache /config /media
+ENTRYPOINT dotnet /jellyfin/jellyfin.dll --datadir /config --cachedir /cache
diff --git a/Emby.Dlna/PlayTo/Device.cs b/Emby.Dlna/PlayTo/Device.cs
index 037cdd8aa..b62c5e1d4 100644
--- a/Emby.Dlna/PlayTo/Device.cs
+++ b/Emby.Dlna/PlayTo/Device.cs
@@ -4,6 +4,7 @@ using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using System.Xml;
using System.Xml.Linq;
using Emby.Dlna.Common;
using Emby.Dlna.Server;
@@ -733,26 +734,21 @@ namespace Emby.Dlna.PlayTo
return (true, null);
}
- XElement uPnpResponse;
+ XElement uPnpResponse = null;
- // Handle different variations sent back by devices
try
{
- uPnpResponse = XElement.Parse(trackString);
+ uPnpResponse = ParseResponse(trackString);
}
- catch (Exception)
+ catch (Exception ex)
{
- // first try to add a root node with a dlna namesapce
- try
- {
- uPnpResponse = XElement.Parse("<data xmlns:dlna=\"urn:schemas-dlna-org:device-1-0\">" + trackString + "</data>");
- uPnpResponse = uPnpResponse.Descendants().First();
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, "Unable to parse xml {0}", trackString);
- return (true, null);
- }
+ _logger.LogError(ex, "Uncaught exception while parsing xml");
+ }
+
+ if (uPnpResponse == null)
+ {
+ _logger.LogError("Failed to parse xml: \n {Xml}", trackString);
+ return (true, null);
}
var e = uPnpResponse.Element(uPnpNamespaces.items);
@@ -762,6 +758,43 @@ namespace Emby.Dlna.PlayTo
return (true, uTrack);
}
+ private XElement ParseResponse(string xml)
+ {
+ // Handle different variations sent back by devices
+ try
+ {
+ return XElement.Parse(xml);
+ }
+ catch (XmlException)
+ {
+
+ }
+
+ // first try to add a root node with a dlna namesapce
+ try
+ {
+ return XElement.Parse("<data xmlns:dlna=\"urn:schemas-dlna-org:device-1-0\">" + xml + "</data>")
+ .Descendants()
+ .First();
+ }
+ catch (XmlException)
+ {
+
+ }
+
+ // some devices send back invalid xml
+ try
+ {
+ return XElement.Parse(xml.Replace("&", "&amp;"));
+ }
+ catch (XmlException)
+ {
+
+ }
+
+ return null;
+ }
+
private static uBaseObject CreateUBaseObject(XElement container, string trackUri)
{
if (container == null)
diff --git a/Emby.Dlna/PlayTo/PlayToManager.cs b/Emby.Dlna/PlayTo/PlayToManager.cs
index 6cce312ee..28e70d046 100644
--- a/Emby.Dlna/PlayTo/PlayToManager.cs
+++ b/Emby.Dlna/PlayTo/PlayToManager.cs
@@ -89,11 +89,6 @@ namespace Emby.Dlna.PlayTo
return;
}
- if (_sessionManager.Sessions.Any(i => usn.IndexOf(i.DeviceId, StringComparison.OrdinalIgnoreCase) != -1))
- {
- return;
- }
-
var cancellationToken = _disposeCancellationTokenSource.Token;
await _sessionLock.WaitAsync(cancellationToken).ConfigureAwait(false);
@@ -105,6 +100,11 @@ namespace Emby.Dlna.PlayTo
return;
}
+ if (_sessionManager.Sessions.Any(i => usn.IndexOf(i.DeviceId, StringComparison.OrdinalIgnoreCase) != -1))
+ {
+ return;
+ }
+
await AddDevice(info, location, cancellationToken).ConfigureAwait(false);
}
catch (OperationCanceledException)
diff --git a/Emby.IsoMounting/IsoMounter/LinuxIsoManager.cs b/Emby.IsoMounting/IsoMounter/LinuxIsoManager.cs
index a6fc53953..943caa3e6 100644
--- a/Emby.IsoMounting/IsoMounter/LinuxIsoManager.cs
+++ b/Emby.IsoMounting/IsoMounter/LinuxIsoManager.cs
@@ -19,7 +19,6 @@ namespace IsoMounter
private readonly IEnvironmentInfo EnvironmentInfo;
private readonly bool ExecutablesAvailable;
- private readonly IFileSystem FileSystem;
private readonly ILogger _logger;
private readonly string MountCommand;
private readonly string MountPointRoot;
@@ -31,11 +30,10 @@ namespace IsoMounter
#region Constructor(s)
- public LinuxIsoManager(ILogger logger, IFileSystem fileSystem, IEnvironmentInfo environment, IProcessFactory processFactory)
+ public LinuxIsoManager(ILogger logger, IEnvironmentInfo environment, IProcessFactory processFactory)
{
EnvironmentInfo = environment;
- FileSystem = fileSystem;
_logger = logger;
ProcessFactory = processFactory;
diff --git a/Emby.Notifications/CoreNotificationTypes.cs b/Emby.Notifications/CoreNotificationTypes.cs
index 8cc14fa01..0f9fc08d9 100644
--- a/Emby.Notifications/CoreNotificationTypes.cs
+++ b/Emby.Notifications/CoreNotificationTypes.cs
@@ -11,101 +11,81 @@ namespace Emby.Notifications
public class CoreNotificationTypes : INotificationTypeFactory
{
private readonly ILocalizationManager _localization;
- private readonly IServerApplicationHost _appHost;
- public CoreNotificationTypes(ILocalizationManager localization, IServerApplicationHost appHost)
+ public CoreNotificationTypes(ILocalizationManager localization)
{
_localization = localization;
- _appHost = appHost;
}
public IEnumerable<NotificationTypeInfo> GetNotificationTypes()
{
- var knownTypes = new List<NotificationTypeInfo>
+ var knownTypes = new NotificationTypeInfo[]
{
new NotificationTypeInfo
{
Type = NotificationType.ApplicationUpdateInstalled.ToString()
},
-
new NotificationTypeInfo
{
Type = NotificationType.InstallationFailed.ToString()
},
-
new NotificationTypeInfo
{
Type = NotificationType.PluginInstalled.ToString()
},
-
new NotificationTypeInfo
{
Type = NotificationType.PluginError.ToString()
},
-
new NotificationTypeInfo
{
Type = NotificationType.PluginUninstalled.ToString()
},
-
new NotificationTypeInfo
{
Type = NotificationType.PluginUpdateInstalled.ToString()
},
-
new NotificationTypeInfo
{
Type = NotificationType.ServerRestartRequired.ToString()
},
-
new NotificationTypeInfo
{
Type = NotificationType.TaskFailed.ToString()
},
-
new NotificationTypeInfo
{
Type = NotificationType.NewLibraryContent.ToString()
},
-
new NotificationTypeInfo
{
Type = NotificationType.AudioPlayback.ToString()
},
-
new NotificationTypeInfo
{
Type = NotificationType.VideoPlayback.ToString()
},
-
new NotificationTypeInfo
{
Type = NotificationType.AudioPlaybackStopped.ToString()
},
-
new NotificationTypeInfo
{
Type = NotificationType.VideoPlaybackStopped.ToString()
},
-
new NotificationTypeInfo
{
Type = NotificationType.CameraImageUploaded.ToString()
},
-
new NotificationTypeInfo
{
Type = NotificationType.UserLockedOut.ToString()
- }
- };
-
- if (!_appHost.CanSelfUpdate)
- {
- knownTypes.Add(new NotificationTypeInfo
+ },
+ new NotificationTypeInfo
{
Type = NotificationType.ApplicationUpdateAvailable.ToString()
- });
- }
+ }
+ };
foreach (var type in knownTypes)
{
diff --git a/Emby.Notifications/Notifications.cs b/Emby.Notifications/Notifications.cs
index d3290479f..ec08fd193 100644
--- a/Emby.Notifications/Notifications.cs
+++ b/Emby.Notifications/Notifications.cs
@@ -5,21 +5,17 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.Updates;
using MediaBrowser.Controller;
-using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Notifications;
using MediaBrowser.Controller.Plugins;
-using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Activity;
using MediaBrowser.Model.Events;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Notifications;
-using MediaBrowser.Model.Tasks;
using Microsoft.Extensions.Logging;
namespace Emby.Notifications
@@ -29,43 +25,40 @@ namespace Emby.Notifications
/// </summary>
public class Notifications : IServerEntryPoint
{
- private readonly IInstallationManager _installationManager;
- private readonly IUserManager _userManager;
private readonly ILogger _logger;
- private readonly ITaskManager _taskManager;
private readonly INotificationManager _notificationManager;
private readonly ILibraryManager _libraryManager;
- private readonly ISessionManager _sessionManager;
private readonly IServerApplicationHost _appHost;
private Timer LibraryUpdateTimer { get; set; }
private readonly object _libraryChangedSyncLock = new object();
private readonly IConfigurationManager _config;
- private readonly IDeviceManager _deviceManager;
private readonly ILocalizationManager _localization;
private readonly IActivityManager _activityManager;
private string[] _coreNotificationTypes;
- public Notifications(IInstallationManager installationManager, IActivityManager activityManager, ILocalizationManager localization, IUserManager userManager, ILogger logger, ITaskManager taskManager, INotificationManager notificationManager, ILibraryManager libraryManager, ISessionManager sessionManager, IServerApplicationHost appHost, IConfigurationManager config, IDeviceManager deviceManager)
+ public Notifications(
+ IActivityManager activityManager,
+ ILocalizationManager localization,
+ ILogger logger,
+ INotificationManager notificationManager,
+ ILibraryManager libraryManager,
+ IServerApplicationHost appHost,
+ IConfigurationManager config)
{
- _installationManager = installationManager;
- _userManager = userManager;
_logger = logger;
- _taskManager = taskManager;
_notificationManager = notificationManager;
_libraryManager = libraryManager;
- _sessionManager = sessionManager;
_appHost = appHost;
_config = config;
- _deviceManager = deviceManager;
_localization = localization;
_activityManager = activityManager;
- _coreNotificationTypes = new CoreNotificationTypes(localization, appHost).GetNotificationTypes().Select(i => i.Type).ToArray();
+ _coreNotificationTypes = new CoreNotificationTypes(localization).GetNotificationTypes().Select(i => i.Type).ToArray();
}
public Task RunAsync()
@@ -124,10 +117,9 @@ namespace Emby.Notifications
return _config.GetConfiguration<NotificationOptions>("notifications");
}
- async void _appHost_HasUpdateAvailableChanged(object sender, EventArgs e)
+ private async void _appHost_HasUpdateAvailableChanged(object sender, EventArgs e)
{
- // This notification is for users who can't auto-update (aka running as service)
- if (!_appHost.HasUpdateAvailable || _appHost.CanSelfUpdate)
+ if (!_appHost.HasUpdateAvailable)
{
return;
}
@@ -145,7 +137,7 @@ namespace Emby.Notifications
}
private readonly List<BaseItem> _itemsAdded = new List<BaseItem>();
- void _libraryManager_ItemAdded(object sender, ItemChangeEventArgs e)
+ private void _libraryManager_ItemAdded(object sender, ItemChangeEventArgs e)
{
if (!FilterItem(e.Item))
{
diff --git a/Emby.Photos/PhotoProvider.cs b/Emby.Photos/PhotoProvider.cs
index f3457d105..a4179e660 100644
--- a/Emby.Photos/PhotoProvider.cs
+++ b/Emby.Photos/PhotoProvider.cs
@@ -9,7 +9,6 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Drawing;
using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.IO;
using Microsoft.Extensions.Logging;
using TagLib;
using TagLib.IFD;
@@ -21,13 +20,11 @@ namespace Emby.Photos
public class PhotoProvider : ICustomMetadataProvider<Photo>, IForcedProvider, IHasItemChangeMonitor
{
private readonly ILogger _logger;
- private readonly IFileSystem _fileSystem;
private IImageProcessor _imageProcessor;
- public PhotoProvider(ILogger logger, IFileSystem fileSystem, IImageProcessor imageProcessor)
+ public PhotoProvider(ILogger logger, IImageProcessor imageProcessor)
{
_logger = logger;
- _fileSystem = fileSystem;
_imageProcessor = imageProcessor;
}
diff --git a/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs b/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs
index f26cc4f62..65cdccfa5 100644
--- a/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs
+++ b/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs
@@ -73,12 +73,6 @@ namespace Emby.Server.Implementations.AppBase
public string PluginConfigurationsPath => Path.Combine(PluginsPath, "configurations");
/// <summary>
- /// Gets the path to where temporary update files will be stored
- /// </summary>
- /// <value>The plugin configurations path.</value>
- public string TempUpdatePath => Path.Combine(ProgramDataPath, "updates");
-
- /// <summary>
/// Gets the path to the log directory
/// </summary>
/// <value>The log directory path.</value>
diff --git a/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs b/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs
index 5feac1adf..af60a8dce 100644
--- a/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs
+++ b/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs
@@ -79,7 +79,7 @@ namespace Emby.Server.Implementations.AppBase
get
{
// Lazy load
- LazyInitializer.EnsureInitialized(ref _configuration, ref _configurationLoaded, ref _configurationSyncLock, () => (BaseApplicationConfiguration)ConfigurationHelper.GetXmlConfiguration(ConfigurationType, CommonApplicationPaths.SystemConfigurationFilePath, XmlSerializer, FileSystem));
+ LazyInitializer.EnsureInitialized(ref _configuration, ref _configurationLoaded, ref _configurationSyncLock, () => (BaseApplicationConfiguration)ConfigurationHelper.GetXmlConfiguration(ConfigurationType, CommonApplicationPaths.SystemConfigurationFilePath, XmlSerializer));
return _configuration;
}
protected set
diff --git a/Emby.Server.Implementations/AppBase/ConfigurationHelper.cs b/Emby.Server.Implementations/AppBase/ConfigurationHelper.cs
index 3faad76e7..90b97061f 100644
--- a/Emby.Server.Implementations/AppBase/ConfigurationHelper.cs
+++ b/Emby.Server.Implementations/AppBase/ConfigurationHelper.cs
@@ -1,7 +1,6 @@
using System;
using System.IO;
using System.Linq;
-using MediaBrowser.Model.IO;
using MediaBrowser.Model.Serialization;
namespace Emby.Server.Implementations.AppBase
@@ -18,9 +17,8 @@ namespace Emby.Server.Implementations.AppBase
/// <param name="type">The type.</param>
/// <param name="path">The path.</param>
/// <param name="xmlSerializer">The XML serializer.</param>
- /// <param name="fileSystem">The file system</param>
/// <returns>System.Object.</returns>
- public static object GetXmlConfiguration(Type type, string path, IXmlSerializer xmlSerializer, IFileSystem fileSystem)
+ public static object GetXmlConfiguration(Type type, string path, IXmlSerializer xmlSerializer)
{
object configuration;
diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs
index 432cd0bea..042b04b3b 100644
--- a/Emby.Server.Implementations/ApplicationHost.cs
+++ b/Emby.Server.Implementations/ApplicationHost.cs
@@ -105,10 +105,10 @@ using MediaBrowser.Providers.Subtitles;
using MediaBrowser.Providers.TV.TheTVDB;
using MediaBrowser.WebDashboard.Api;
using MediaBrowser.XbmcMetadata.Providers;
+using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.DependencyInjection;
using ServiceStack;
-using ServiceStack.Text.Jsv;
using X509Certificate = System.Security.Cryptography.X509Certificates.X509Certificate;
namespace Emby.Server.Implementations
@@ -124,12 +124,6 @@ namespace Emby.Server.Implementations
/// <value><c>true</c> if this instance can self restart; otherwise, <c>false</c>.</value>
public abstract bool CanSelfRestart { get; }
- /// <summary>
- /// Gets or sets a value indicating whether this instance can self update.
- /// </summary>
- /// <value><c>true</c> if this instance can self update; otherwise, <c>false</c>.</value>
- public virtual bool CanSelfUpdate => false;
-
public virtual bool CanLaunchWebBrowser
{
get
@@ -319,6 +313,8 @@ namespace Emby.Server.Implementations
private IMediaSourceManager MediaSourceManager { get; set; }
private IPlaylistManager PlaylistManager { get; set; }
+ private readonly IConfiguration _configuration;
+
/// <summary>
/// Gets or sets the installation manager.
/// </summary>
@@ -357,8 +353,10 @@ namespace Emby.Server.Implementations
IFileSystem fileSystem,
IEnvironmentInfo environmentInfo,
IImageEncoder imageEncoder,
- INetworkManager networkManager)
+ INetworkManager networkManager,
+ IConfiguration configuration)
{
+ _configuration = configuration;
// hack alert, until common can target .net core
BaseExtensions.CryptographyProvider = CryptographyProvider;
@@ -434,7 +432,7 @@ namespace Emby.Server.Implementations
{
if (_deviceId == null)
{
- _deviceId = new DeviceId(ApplicationPaths, LoggerFactory, FileSystemManager);
+ _deviceId = new DeviceId(ApplicationPaths, LoggerFactory);
}
return _deviceId.Value;
@@ -472,7 +470,7 @@ namespace Emby.Server.Implementations
{
try
{
- Logger.LogWarning("Creating instance of {Type}", type);
+ Logger.LogDebug("Creating instance of {Type}", type);
return ActivatorUtilities.CreateInstance(_serviceProvider, type);
}
catch (Exception ex)
@@ -668,10 +666,10 @@ namespace Emby.Server.Implementations
SocketFactory = new SocketFactory();
serviceCollection.AddSingleton(SocketFactory);
- InstallationManager = new InstallationManager(LoggerFactory, this, ApplicationPaths, HttpClient, JsonSerializer, ServerConfigurationManager, FileSystemManager, CryptographyProvider, PackageRuntime);
+ InstallationManager = new InstallationManager(LoggerFactory, this, ApplicationPaths, HttpClient, JsonSerializer, ServerConfigurationManager, FileSystemManager, CryptographyProvider, ZipClient, PackageRuntime);
serviceCollection.AddSingleton(InstallationManager);
- ZipClient = new ZipClient(FileSystemManager);
+ ZipClient = new ZipClient();
serviceCollection.AddSingleton(ZipClient);
HttpResultFactory = new HttpResultFactory(LoggerFactory, FileSystemManager, JsonSerializer, CreateBrotliCompressor());
@@ -709,7 +707,7 @@ namespace Emby.Server.Implementations
AuthenticationRepository = GetAuthenticationRepository();
serviceCollection.AddSingleton(AuthenticationRepository);
- UserManager = new UserManager(LoggerFactory, ServerConfigurationManager, UserRepository, XmlSerializer, NetworkManager, () => ImageProcessor, () => DtoService, this, JsonSerializer, FileSystemManager, CryptographyProvider);
+ UserManager = new UserManager(LoggerFactory, ServerConfigurationManager, UserRepository, XmlSerializer, NetworkManager, () => ImageProcessor, () => DtoService, this, JsonSerializer, FileSystemManager);
serviceCollection.AddSingleton(UserManager);
LibraryManager = new LibraryManager(this, LoggerFactory, TaskManager, UserManager, ServerConfigurationManager, UserDataManager, () => LibraryMonitor, FileSystemManager, () => ProviderManager, () => UserViewManager);
@@ -719,7 +717,7 @@ namespace Emby.Server.Implementations
var musicManager = new MusicManager(LibraryManager);
serviceCollection.AddSingleton<IMusicManager>(new MusicManager(LibraryManager));
- LibraryMonitor = new LibraryMonitor(LoggerFactory, TaskManager, LibraryManager, ServerConfigurationManager, FileSystemManager, EnvironmentInfo);
+ LibraryMonitor = new LibraryMonitor(LoggerFactory, LibraryManager, ServerConfigurationManager, FileSystemManager, EnvironmentInfo);
serviceCollection.AddSingleton(LibraryMonitor);
serviceCollection.AddSingleton<ISearchEngine>(new SearchEngine(LoggerFactory, LibraryManager, UserManager));
@@ -730,11 +728,10 @@ namespace Emby.Server.Implementations
HttpServer = new HttpListenerHost(this,
LoggerFactory,
ServerConfigurationManager,
- "web/index.html",
+ _configuration,
NetworkManager,
JsonSerializer,
- XmlSerializer,
- GetParseFn);
+ XmlSerializer);
HttpServer.GlobalResponse = LocalizationManager.GetLocalizedString("StartupEmbyServerIsLoading");
serviceCollection.AddSingleton(HttpServer);
@@ -748,7 +745,7 @@ namespace Emby.Server.Implementations
var encryptionManager = new EncryptionManager();
serviceCollection.AddSingleton<IEncryptionManager>(encryptionManager);
- DeviceManager = new DeviceManager(AuthenticationRepository, JsonSerializer, LibraryManager, LocalizationManager, UserManager, FileSystemManager, LibraryMonitor, ServerConfigurationManager, LoggerFactory, NetworkManager);
+ DeviceManager = new DeviceManager(AuthenticationRepository, JsonSerializer, LibraryManager, LocalizationManager, UserManager, FileSystemManager, LibraryMonitor, ServerConfigurationManager);
serviceCollection.AddSingleton(DeviceManager);
MediaSourceManager = new MediaSourceManager(ItemRepository, ApplicationPaths, LocalizationManager, UserManager, LibraryManager, LoggerFactory, JsonSerializer, FileSystemManager, UserDataManager, () => MediaEncoder);
@@ -760,7 +757,7 @@ namespace Emby.Server.Implementations
ProviderManager = new ProviderManager(HttpClient, SubtitleManager, ServerConfigurationManager, LibraryMonitor, LoggerFactory, FileSystemManager, ApplicationPaths, () => LibraryManager, JsonSerializer);
serviceCollection.AddSingleton(ProviderManager);
- DtoService = new DtoService(LoggerFactory, LibraryManager, UserDataManager, ItemRepository, ImageProcessor, ServerConfigurationManager, FileSystemManager, ProviderManager, () => ChannelManager, this, () => DeviceManager, () => MediaSourceManager, () => LiveTvManager);
+ DtoService = new DtoService(LoggerFactory, LibraryManager, UserDataManager, ItemRepository, ImageProcessor, ProviderManager, this, () => MediaSourceManager, () => LiveTvManager);
serviceCollection.AddSingleton(DtoService);
ChannelManager = new ChannelManager(UserManager, DtoService, LibraryManager, LoggerFactory, ServerConfigurationManager, FileSystemManager, UserDataManager, JsonSerializer, LocalizationManager, HttpClient, ProviderManager);
@@ -834,11 +831,6 @@ namespace Emby.Server.Implementations
return null;
}
- private static Func<string, object> GetParseFn(Type propertyType)
- {
- return s => JsvReader.GetParseFn(propertyType)(s);
- }
-
public virtual string PackageRuntime => "netcore";
public static void LogEnvironmentInfo(ILogger logger, IApplicationPaths appPaths, EnvironmentInfo.EnvironmentInfo environmentInfo)
@@ -950,7 +942,7 @@ namespace Emby.Server.Implementations
protected virtual FFMpegInfo GetFFMpegInfo()
{
- return new FFMpegLoader(Logger, ApplicationPaths, HttpClient, ZipClient, FileSystemManager, GetFfmpegInstallInfo())
+ return new FFMpegLoader(ApplicationPaths, FileSystemManager, GetFfmpegInstallInfo())
.GetFFMpegInfo(StartupOptions);
}
@@ -1461,7 +1453,6 @@ namespace Emby.Server.Implementations
OperatingSystem = EnvironmentInfo.OperatingSystem.ToString(),
OperatingSystemDisplayName = EnvironmentInfo.OperatingSystemName,
CanSelfRestart = CanSelfRestart,
- CanSelfUpdate = CanSelfUpdate,
CanLaunchWebBrowser = CanLaunchWebBrowser,
WanAddress = wanAddress,
HasUpdateAvailable = HasUpdateAvailable,
@@ -1761,21 +1752,6 @@ namespace Emby.Server.Implementations
}
/// <summary>
- /// Updates the application.
- /// </summary>
- /// <param name="package">The package that contains the update</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <param name="progress">The progress.</param>
- public async Task UpdateApplication(PackageVersionInfo package, CancellationToken cancellationToken, IProgress<double> progress)
- {
- await InstallationManager.InstallPackage(package, false, progress, cancellationToken).ConfigureAwait(false);
-
- HasUpdateAvailable = false;
-
- OnApplicationUpdated(package);
- }
-
- /// <summary>
/// This returns localhost in the case of no external dns, and the hostname if the
/// dns is prefixed with a valid Uri prefix.
/// </summary>
diff --git a/Emby.Server.Implementations/Archiving/ZipClient.cs b/Emby.Server.Implementations/Archiving/ZipClient.cs
index 1135cf694..6b0fd2dc6 100644
--- a/Emby.Server.Implementations/Archiving/ZipClient.cs
+++ b/Emby.Server.Implementations/Archiving/ZipClient.cs
@@ -14,11 +14,9 @@ namespace Emby.Server.Implementations.Archiving
/// </summary>
public class ZipClient : IZipClient
{
- private readonly IFileSystem _fileSystem;
-
- public ZipClient(IFileSystem fileSystem)
+ public ZipClient()
{
- _fileSystem = fileSystem;
+
}
/// <summary>
diff --git a/Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs b/Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs
index 844f77a1a..303a8ac7b 100644
--- a/Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs
+++ b/Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs
@@ -10,7 +10,7 @@ using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.Channels
{
- class RefreshChannelsScheduledTask : IScheduledTask, IConfigurableScheduledTask
+ public class RefreshChannelsScheduledTask : IScheduledTask, IConfigurableScheduledTask
{
private readonly IChannelManager _channelManager;
private readonly IUserManager _userManager;
diff --git a/Emby.Server.Implementations/Collections/CollectionImageProvider.cs b/Emby.Server.Implementations/Collections/CollectionImageProvider.cs
index 6aeadda2f..cdfb5cadf 100644
--- a/Emby.Server.Implementations/Collections/CollectionImageProvider.cs
+++ b/Emby.Server.Implementations/Collections/CollectionImageProvider.cs
@@ -10,14 +10,18 @@ using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.IO;
namespace Emby.Server.Implementations.Collections
{
public class CollectionImageProvider : BaseDynamicImageProvider<BoxSet>
{
- public CollectionImageProvider(IFileSystem fileSystem, IProviderManager providerManager, IApplicationPaths applicationPaths, IImageProcessor imageProcessor) : base(fileSystem, providerManager, applicationPaths, imageProcessor)
+ public CollectionImageProvider(
+ IFileSystem fileSystem,
+ IProviderManager providerManager,
+ IApplicationPaths applicationPaths,
+ IImageProcessor imageProcessor)
+ : base(fileSystem, providerManager, applicationPaths, imageProcessor)
{
}
diff --git a/Emby.Server.Implementations/Collections/CollectionManager.cs b/Emby.Server.Implementations/Collections/CollectionManager.cs
index 812e48a1f..2b99e0ddf 100644
--- a/Emby.Server.Implementations/Collections/CollectionManager.cs
+++ b/Emby.Server.Implementations/Collections/CollectionManager.cs
@@ -342,14 +342,12 @@ namespace Emby.Server.Implementations.Collections
{
private readonly CollectionManager _collectionManager;
private readonly IServerConfigurationManager _config;
- private readonly IFileSystem _fileSystem;
private ILogger _logger;
- public CollectionManagerEntryPoint(ICollectionManager collectionManager, IServerConfigurationManager config, IFileSystem fileSystem, ILogger logger)
+ public CollectionManagerEntryPoint(ICollectionManager collectionManager, IServerConfigurationManager config, ILogger logger)
{
_collectionManager = (CollectionManager)collectionManager;
_config = config;
- _fileSystem = fileSystem;
_logger = logger;
}
diff --git a/Emby.Server.Implementations/ConfigurationOptions.cs b/Emby.Server.Implementations/ConfigurationOptions.cs
new file mode 100644
index 000000000..30bfd8749
--- /dev/null
+++ b/Emby.Server.Implementations/ConfigurationOptions.cs
@@ -0,0 +1,12 @@
+using System.Collections.Generic;
+
+namespace Emby.Server.Implementations
+{
+ public static class ConfigurationOptions
+ {
+ public static readonly Dictionary<string, string> Configuration = new Dictionary<string, string>
+ {
+ {"HttpListenerHost:DefaultRedirectPath", "web/index.html"}
+ };
+ }
+}
diff --git a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs
index 0f432c36c..fba81306b 100644
--- a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs
+++ b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs
@@ -224,7 +224,7 @@ namespace Emby.Server.Implementations.Data
});
}
- db.ExecuteAll(string.Join(";", queries.ToArray()));
+ db.ExecuteAll(string.Join(";", queries));
Logger.LogInformation("PRAGMA synchronous=" + db.Query("PRAGMA synchronous").SelectScalarString().First());
}
@@ -232,23 +232,6 @@ namespace Emby.Server.Implementations.Data
protected virtual int? CacheSize => null;
- internal static void CheckOk(int rc)
- {
- string msg = "";
-
- if (raw.SQLITE_OK != rc)
- {
- throw CreateException((ErrorCode)rc, msg);
- }
- }
-
- internal static Exception CreateException(ErrorCode rc, string msg)
- {
- var exp = new Exception(msg);
-
- return exp;
- }
-
private bool _disposed;
protected void CheckDisposed()
{
@@ -375,13 +358,6 @@ namespace Emby.Server.Implementations.Data
}
}
- public class DummyToken : IDisposable
- {
- public void Dispose()
- {
- }
- }
-
public static IDisposable Read(this ReaderWriterLockSlim obj)
{
//if (BaseSqliteRepository.ThreadSafeMode > 0)
@@ -390,6 +366,7 @@ namespace Emby.Server.Implementations.Data
//}
return new WriteLockToken(obj);
}
+
public static IDisposable Write(this ReaderWriterLockSlim obj)
{
//if (BaseSqliteRepository.ThreadSafeMode > 0)
diff --git a/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs b/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs
index dcfe14943..f7743a3c2 100644
--- a/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs
+++ b/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs
@@ -1,11 +1,8 @@
using System;
using System.Threading;
using System.Threading.Tasks;
-using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Persistence;
-using MediaBrowser.Model.IO;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.Data
@@ -13,18 +10,12 @@ namespace Emby.Server.Implementations.Data
public class CleanDatabaseScheduledTask : ILibraryPostScanTask
{
private readonly ILibraryManager _libraryManager;
- private readonly IItemRepository _itemRepo;
private readonly ILogger _logger;
- private readonly IFileSystem _fileSystem;
- private readonly IApplicationPaths _appPaths;
- public CleanDatabaseScheduledTask(ILibraryManager libraryManager, IItemRepository itemRepo, ILogger logger, IFileSystem fileSystem, IApplicationPaths appPaths)
+ public CleanDatabaseScheduledTask(ILibraryManager libraryManager, ILogger logger)
{
_libraryManager = libraryManager;
- _itemRepo = itemRepo;
_logger = logger;
- _fileSystem = fileSystem;
- _appPaths = appPaths;
}
public Task Run(IProgress<double> progress, CancellationToken cancellationToken)
diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
index 3014e482d..6502e4aed 100644
--- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs
+++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
@@ -536,7 +536,7 @@ namespace Emby.Server.Implementations.Data
throw new ArgumentNullException(nameof(item));
}
- SaveItems(new List<BaseItem> { item }, cancellationToken);
+ SaveItems(new [] { item }, cancellationToken);
}
public void SaveImages(BaseItem item)
@@ -576,7 +576,7 @@ namespace Emby.Server.Implementations.Data
/// or
/// cancellationToken
/// </exception>
- public void SaveItems(List<BaseItem> items, CancellationToken cancellationToken)
+ public void SaveItems(IEnumerable<BaseItem> items, CancellationToken cancellationToken)
{
if (items == null)
{
@@ -587,7 +587,7 @@ namespace Emby.Server.Implementations.Data
CheckDisposed();
- var tuples = new List<Tuple<BaseItem, List<Guid>, BaseItem, string, List<string>>>();
+ var tuples = new List<(BaseItem, List<Guid>, BaseItem, string, List<string>)>();
foreach (var item in items)
{
var ancestorIds = item.SupportsAncestors ?
@@ -599,7 +599,7 @@ namespace Emby.Server.Implementations.Data
var userdataKey = item.GetUserDataKeys().FirstOrDefault();
var inheritedTags = item.GetInheritedTags();
- tuples.Add(new Tuple<BaseItem, List<Guid>, BaseItem, string, List<string>>(item, ancestorIds, topParent, userdataKey, inheritedTags));
+ tuples.Add((item, ancestorIds, topParent, userdataKey, inheritedTags));
}
using (WriteLock.Write())
@@ -615,7 +615,7 @@ namespace Emby.Server.Implementations.Data
}
}
- private void SaveItemsInTranscation(IDatabaseConnection db, List<Tuple<BaseItem, List<Guid>, BaseItem, string, List<string>>> tuples)
+ private void SaveItemsInTranscation(IDatabaseConnection db, IEnumerable<(BaseItem, List<Guid>, BaseItem, string, List<string>)> tuples)
{
var statements = PrepareAllSafe(db, new string[]
{
@@ -966,7 +966,7 @@ namespace Emby.Server.Implementations.Data
if (item.ExtraIds.Length > 0)
{
- saveItemStatement.TryBind("@ExtraIds", string.Join("|", item.ExtraIds.ToArray()));
+ saveItemStatement.TryBind("@ExtraIds", string.Join("|", item.ExtraIds));
}
else
{
@@ -1183,9 +1183,9 @@ namespace Emby.Server.Implementations.Data
/// <exception cref="ArgumentException"></exception>
public BaseItem RetrieveItem(Guid id)
{
- if (id.Equals(Guid.Empty))
+ if (id == Guid.Empty)
{
- throw new ArgumentNullException(nameof(id));
+ throw new ArgumentException(nameof(id), "Guid can't be empty");
}
CheckDisposed();
@@ -2079,14 +2079,14 @@ namespace Emby.Server.Implementations.Data
return false;
}
- var sortingFields = query.OrderBy.Select(i => i.Item1);
+ var sortingFields = new HashSet<string>(query.OrderBy.Select(i => i.Item1), StringComparer.OrdinalIgnoreCase);
- return sortingFields.Contains(ItemSortBy.IsFavoriteOrLiked, StringComparer.OrdinalIgnoreCase)
- || sortingFields.Contains(ItemSortBy.IsPlayed, StringComparer.OrdinalIgnoreCase)
- || sortingFields.Contains(ItemSortBy.IsUnplayed, StringComparer.OrdinalIgnoreCase)
- || sortingFields.Contains(ItemSortBy.PlayCount, StringComparer.OrdinalIgnoreCase)
- || sortingFields.Contains(ItemSortBy.DatePlayed, StringComparer.OrdinalIgnoreCase)
- || sortingFields.Contains(ItemSortBy.SeriesDatePlayed, StringComparer.OrdinalIgnoreCase)
+ return sortingFields.Contains(ItemSortBy.IsFavoriteOrLiked)
+ || sortingFields.Contains(ItemSortBy.IsPlayed)
+ || sortingFields.Contains(ItemSortBy.IsUnplayed)
+ || sortingFields.Contains(ItemSortBy.PlayCount)
+ || sortingFields.Contains(ItemSortBy.DatePlayed)
+ || sortingFields.Contains(ItemSortBy.SeriesDatePlayed)
|| query.IsFavoriteOrLiked.HasValue
|| query.IsFavorite.HasValue
|| query.IsResumable.HasValue
@@ -2094,9 +2094,9 @@ namespace Emby.Server.Implementations.Data
|| query.IsLiked.HasValue;
}
- private readonly List<ItemFields> allFields = Enum.GetNames(typeof(ItemFields))
+ private readonly ItemFields[] _allFields = Enum.GetNames(typeof(ItemFields))
.Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true))
- .ToList();
+ .ToArray();
private string[] GetColumnNamesFromField(ItemFields field)
{
@@ -2151,18 +2151,26 @@ namespace Emby.Server.Implementations.Data
}
}
- private bool HasProgramAttributes(InternalItemsQuery query)
+ private static readonly HashSet<string> _programExcludeParentTypes = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
- var excludeParentTypes = new string[]
- {
- "Series",
- "Season",
- "MusicAlbum",
- "MusicArtist",
- "PhotoAlbum"
- };
+ "Series",
+ "Season",
+ "MusicAlbum",
+ "MusicArtist",
+ "PhotoAlbum"
+ };
+
+ private static readonly HashSet<string> _programTypes = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
+ {
+ "Program",
+ "TvChannel",
+ "LiveTvProgram",
+ "LiveTvTvChannel"
+ };
- if (excludeParentTypes.Contains(query.ParentType ?? string.Empty, StringComparer.OrdinalIgnoreCase))
+ private bool HasProgramAttributes(InternalItemsQuery query)
+ {
+ if (_programExcludeParentTypes.Contains(query.ParentType))
{
return false;
}
@@ -2172,29 +2180,18 @@ namespace Emby.Server.Implementations.Data
return true;
}
- var types = new string[]
- {
- "Program",
- "TvChannel",
- "LiveTvProgram",
- "LiveTvTvChannel"
- };
-
- return types.Any(i => query.IncludeItemTypes.Contains(i, StringComparer.OrdinalIgnoreCase));
+ return query.IncludeItemTypes.Any(x => _programTypes.Contains(x));
}
- private bool HasServiceName(InternalItemsQuery query)
+ private static readonly HashSet<string> _serviceTypes = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
- var excludeParentTypes = new string[]
- {
- "Series",
- "Season",
- "MusicAlbum",
- "MusicArtist",
- "PhotoAlbum"
- };
+ "TvChannel",
+ "LiveTvTvChannel"
+ };
- if (excludeParentTypes.Contains(query.ParentType ?? string.Empty, StringComparer.OrdinalIgnoreCase))
+ private bool HasServiceName(InternalItemsQuery query)
+ {
+ if (_programExcludeParentTypes.Contains(query.ParentType))
{
return false;
}
@@ -2204,27 +2201,18 @@ namespace Emby.Server.Implementations.Data
return true;
}
- var types = new string[]
- {
- "TvChannel",
- "LiveTvTvChannel"
- };
-
- return types.Any(i => query.IncludeItemTypes.Contains(i, StringComparer.OrdinalIgnoreCase));
+ return query.IncludeItemTypes.Any(x => _serviceTypes.Contains(x));
}
- private bool HasStartDate(InternalItemsQuery query)
+ private static readonly HashSet<string> _startDateTypes = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
- var excludeParentTypes = new string[]
- {
- "Series",
- "Season",
- "MusicAlbum",
- "MusicArtist",
- "PhotoAlbum"
- };
+ "Program",
+ "LiveTvProgram"
+ };
- if (excludeParentTypes.Contains(query.ParentType ?? string.Empty, StringComparer.OrdinalIgnoreCase))
+ private bool HasStartDate(InternalItemsQuery query)
+ {
+ if (_programExcludeParentTypes.Contains(query.ParentType))
{
return false;
}
@@ -2234,13 +2222,7 @@ namespace Emby.Server.Implementations.Data
return true;
}
- var types = new string[]
- {
- "Program",
- "LiveTvProgram"
- };
-
- return types.Any(i => query.IncludeItemTypes.Contains(i, StringComparer.OrdinalIgnoreCase));
+ return query.IncludeItemTypes.Any(x => _startDateTypes.Contains(x));
}
private bool HasEpisodeAttributes(InternalItemsQuery query)
@@ -2263,16 +2245,26 @@ namespace Emby.Server.Implementations.Data
return query.IncludeItemTypes.Contains("Trailer", StringComparer.OrdinalIgnoreCase);
}
- private bool HasArtistFields(InternalItemsQuery query)
+
+ private static readonly HashSet<string> _artistExcludeParentTypes = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
- var excludeParentTypes = new string[]
- {
- "Series",
- "Season",
- "PhotoAlbum"
- };
+ "Series",
+ "Season",
+ "PhotoAlbum"
+ };
+
+ private static readonly HashSet<string> _artistsTypes = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
+ {
+ "Audio",
+ "MusicAlbum",
+ "MusicVideo",
+ "AudioBook",
+ "AudioPodcast"
+ };
- if (excludeParentTypes.Contains(query.ParentType ?? string.Empty, StringComparer.OrdinalIgnoreCase))
+ private bool HasArtistFields(InternalItemsQuery query)
+ {
+ if (_artistExcludeParentTypes.Contains(query.ParentType))
{
return false;
}
@@ -2282,18 +2274,18 @@ namespace Emby.Server.Implementations.Data
return true;
}
- var types = new string[]
- {
- "Audio",
- "MusicAlbum",
- "MusicVideo",
- "AudioBook",
- "AudioPodcast"
- };
-
- return types.Any(i => query.IncludeItemTypes.Contains(i, StringComparer.OrdinalIgnoreCase));
+ return query.IncludeItemTypes.Any(x => _artistsTypes.Contains(x));
}
+ private static readonly HashSet<string> _seriesTypes = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
+ {
+ "Audio",
+ "MusicAlbum",
+ "MusicVideo",
+ "AudioBook",
+ "AudioPodcast"
+ };
+
private bool HasSeriesFields(InternalItemsQuery query)
{
if (string.Equals(query.ParentType, "PhotoAlbum", StringComparison.OrdinalIgnoreCase))
@@ -2306,26 +2298,18 @@ namespace Emby.Server.Implementations.Data
return true;
}
- var types = new string[]
- {
- "Book",
- "AudioBook",
- "Episode",
- "Season"
- };
-
- return types.Any(i => query.IncludeItemTypes.Contains(i, StringComparer.OrdinalIgnoreCase));
+ return query.IncludeItemTypes.Any(x => _seriesTypes.Contains(x));
}
- private string[] GetFinalColumnsToSelect(InternalItemsQuery query, string[] startColumns)
+ private List<string> GetFinalColumnsToSelect(InternalItemsQuery query, IEnumerable<string> startColumns)
{
var list = startColumns.ToList();
- foreach (var field in allFields)
+ foreach (var field in _allFields)
{
if (!HasField(query, field))
{
- foreach (var fieldToRemove in GetColumnNamesFromField(field).ToList())
+ foreach (var fieldToRemove in GetColumnNamesFromField(field))
{
list.Remove(fieldToRemove);
}
@@ -2419,11 +2403,14 @@ namespace Emby.Server.Implementations.Data
list.Add(builder.ToString());
- var excludeIds = query.ExcludeItemIds.ToList();
- excludeIds.Add(item.Id);
- excludeIds.AddRange(item.ExtraIds);
+ var oldLen = query.ExcludeItemIds.Length;
+ var newLen = oldLen + item.ExtraIds.Length + 1;
+ var excludeIds = new Guid[newLen];
+ query.ExcludeItemIds.CopyTo(excludeIds, 0);
+ excludeIds[oldLen] = item.Id;
+ item.ExtraIds.CopyTo(excludeIds, oldLen + 1);
- query.ExcludeItemIds = excludeIds.ToArray();
+ query.ExcludeItemIds = excludeIds;
query.ExcludeProviderIds = item.ProviderIds;
}
@@ -2444,7 +2431,7 @@ namespace Emby.Server.Implementations.Data
list.Add(builder.ToString());
}
- return list.ToArray();
+ return list;
}
private void BindSearchParams(InternalItemsQuery query, IStatement statement)
@@ -2723,18 +2710,17 @@ namespace Emby.Server.Implementations.Data
private void AddItem(List<BaseItem> items, BaseItem newItem)
{
- var providerIds = newItem.ProviderIds.ToList();
-
for (var i = 0; i < items.Count; i++)
{
var item = items[i];
- foreach (var providerId in providerIds)
+ foreach (var providerId in newItem.ProviderIds)
{
if (providerId.Key == MetadataProviders.TmdbCollection.ToString())
{
continue;
}
+
if (item.GetProviderId(providerId.Key) == providerId.Value)
{
if (newItem.SourceType == SourceType.Library)
@@ -2753,10 +2739,10 @@ namespace Emby.Server.Implementations.Data
{
var elapsed = (DateTime.UtcNow - startDate).TotalMilliseconds;
- int slowThreshold = 1000;
+ int slowThreshold = 100;
#if DEBUG
- slowThreshold = 250;
+ slowThreshold = 10;
#endif
if (elapsed >= slowThreshold)
@@ -2806,7 +2792,7 @@ namespace Emby.Server.Implementations.Data
var whereText = whereClauses.Count == 0 ?
string.Empty :
- " where " + string.Join(" AND ", whereClauses.ToArray());
+ " where " + string.Join(" AND ", whereClauses);
commandText += whereText
+ GetGroupBy(query)
@@ -2930,25 +2916,31 @@ namespace Emby.Server.Implementations.Data
private string GetOrderByText(InternalItemsQuery query)
{
- var orderBy = query.OrderBy.ToList();
- var enableOrderInversion = false;
-
- if (query.SimilarTo != null && orderBy.Count == 0)
+ if (string.IsNullOrEmpty(query.SearchTerm))
{
- orderBy.Add(new ValueTuple<string, SortOrder>("SimilarityScore", SortOrder.Descending));
- orderBy.Add(new ValueTuple<string, SortOrder>(ItemSortBy.Random, SortOrder.Ascending));
- }
+ int oldLen = query.OrderBy.Length;
- if (!string.IsNullOrEmpty(query.SearchTerm))
+ if (query.SimilarTo != null && oldLen == 0)
+ {
+ var arr = new (string, SortOrder)[oldLen + 2];
+ query.OrderBy.CopyTo(arr, 0);
+ arr[oldLen] = ("SimilarityScore", SortOrder.Descending);
+ arr[oldLen + 1] = (ItemSortBy.Random, SortOrder.Ascending);
+ query.OrderBy = arr;
+ }
+ }
+ else
{
- orderBy = new List<(string, SortOrder)>();
- orderBy.Add(new ValueTuple<string, SortOrder>("SearchScore", SortOrder.Descending));
- orderBy.Add(new ValueTuple<string, SortOrder>(ItemSortBy.SortName, SortOrder.Ascending));
+ query.OrderBy = new []
+ {
+ ("SearchScore", SortOrder.Descending),
+ (ItemSortBy.SortName, SortOrder.Ascending)
+ };
}
- query.OrderBy = orderBy.ToArray();
+ var orderBy = query.OrderBy;
- if (orderBy.Count == 0)
+ if (orderBy.Length == 0)
{
return string.Empty;
}
@@ -2957,6 +2949,7 @@ namespace Emby.Server.Implementations.Data
{
var columnMap = MapOrderByField(i.Item1, query);
var columnAscending = i.Item2 == SortOrder.Ascending;
+ const bool enableOrderInversion = false;
if (columnMap.Item2 && enableOrderInversion)
{
columnAscending = !columnAscending;
@@ -2968,7 +2961,7 @@ namespace Emby.Server.Implementations.Data
}));
}
- private ValueTuple<string, bool> MapOrderByField(string name, InternalItemsQuery query)
+ private (string, bool) MapOrderByField(string name, InternalItemsQuery query)
{
if (string.Equals(name, ItemSortBy.AirTime, StringComparison.OrdinalIgnoreCase))
{
@@ -3218,7 +3211,7 @@ namespace Emby.Server.Implementations.Data
var whereText = whereClauses.Count == 0 ?
string.Empty :
- " where " + string.Join(" AND ", whereClauses.ToArray());
+ " where " + string.Join(" AND ", whereClauses);
commandText += whereText
+ GetGroupBy(query)
@@ -4378,7 +4371,7 @@ namespace Emby.Server.Implementations.Data
}
else if (query.Years.Length > 1)
{
- var val = string.Join(",", query.Years.ToArray());
+ var val = string.Join(",", query.Years);
whereClauses.Add("ProductionYear in (" + val + ")");
}
@@ -4952,7 +4945,12 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
return result;
}
- return new[] { value }.Where(IsValidType);
+ if (IsValidType(value))
+ {
+ return new[] { value };
+ }
+
+ return Array.Empty<string>();
}
public void DeleteItem(Guid id, CancellationToken cancellationToken)
@@ -5215,32 +5213,32 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
}
}
- public QueryResult<Tuple<BaseItem, ItemCounts>> GetAllArtists(InternalItemsQuery query)
+ public QueryResult<(BaseItem, ItemCounts)> GetAllArtists(InternalItemsQuery query)
{
return GetItemValues(query, new[] { 0, 1 }, typeof(MusicArtist).FullName);
}
- public QueryResult<Tuple<BaseItem, ItemCounts>> GetArtists(InternalItemsQuery query)
+ public QueryResult<(BaseItem, ItemCounts)> GetArtists(InternalItemsQuery query)
{
return GetItemValues(query, new[] { 0 }, typeof(MusicArtist).FullName);
}
- public QueryResult<Tuple<BaseItem, ItemCounts>> GetAlbumArtists(InternalItemsQuery query)
+ public QueryResult<(BaseItem, ItemCounts)> GetAlbumArtists(InternalItemsQuery query)
{
return GetItemValues(query, new[] { 1 }, typeof(MusicArtist).FullName);
}
- public QueryResult<Tuple<BaseItem, ItemCounts>> GetStudios(InternalItemsQuery query)
+ public QueryResult<(BaseItem, ItemCounts)> GetStudios(InternalItemsQuery query)
{
return GetItemValues(query, new[] { 3 }, typeof(Studio).FullName);
}
- public QueryResult<Tuple<BaseItem, ItemCounts>> GetGenres(InternalItemsQuery query)
+ public QueryResult<(BaseItem, ItemCounts)> GetGenres(InternalItemsQuery query)
{
return GetItemValues(query, new[] { 2 }, typeof(Genre).FullName);
}
- public QueryResult<Tuple<BaseItem, ItemCounts>> GetMusicGenres(InternalItemsQuery query)
+ public QueryResult<(BaseItem, ItemCounts)> GetMusicGenres(InternalItemsQuery query)
{
return GetItemValues(query, new[] { 2 }, typeof(MusicGenre).FullName);
}
@@ -5317,7 +5315,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
}
}
- private QueryResult<Tuple<BaseItem, ItemCounts>> GetItemValues(InternalItemsQuery query, int[] itemValueTypes, string returnType)
+ private QueryResult<(BaseItem, ItemCounts)> GetItemValues(InternalItemsQuery query, int[] itemValueTypes, string returnType)
{
if (query == null)
{
@@ -5335,7 +5333,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
var typeClause = itemValueTypes.Length == 1 ?
("Type=" + itemValueTypes[0].ToString(CultureInfo.InvariantCulture)) :
- ("Type in (" + string.Join(",", itemValueTypes.Select(i => i.ToString(CultureInfo.InvariantCulture)).ToArray()) + ")");
+ ("Type in (" + string.Join(",", itemValueTypes.Select(i => i.ToString(CultureInfo.InvariantCulture))) + ")");
InternalItemsQuery typeSubQuery = null;
@@ -5363,11 +5361,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
whereClauses.Add("guid in (select ItemId from ItemValues where ItemValues.CleanValue=A.CleanName AND " + typeClause + ")");
- var typeWhereText = whereClauses.Count == 0 ?
- string.Empty :
- " where " + string.Join(" AND ", whereClauses);
-
- itemCountColumnQuery += typeWhereText;
+ itemCountColumnQuery += " where " + string.Join(" AND ", whereClauses);
itemCountColumns = new Dictionary<string, string>()
{
@@ -5400,7 +5394,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
IsSeries = query.IsSeries
};
- columns = GetFinalColumnsToSelect(query, columns.ToArray()).ToList();
+ columns = GetFinalColumnsToSelect(query, columns);
var commandText = "select "
+ string.Join(",", columns)
@@ -5492,8 +5486,8 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
{
return connection.RunInTransaction(db =>
{
- var list = new List<Tuple<BaseItem, ItemCounts>>();
- var result = new QueryResult<Tuple<BaseItem, ItemCounts>>();
+ var list = new List<(BaseItem, ItemCounts)>();
+ var result = new QueryResult<(BaseItem, ItemCounts)>();
var statements = PrepareAllSafe(db, statementTexts);
@@ -5531,7 +5525,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
{
var countStartColumn = columns.Count - 1;
- list.Add(new Tuple<BaseItem, ItemCounts>(item, GetItemCounts(row, countStartColumn, typesToCount)));
+ list.Add((item, GetItemCounts(row, countStartColumn, typesToCount)));
}
}
@@ -6198,6 +6192,5 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
return item;
}
-
}
}
diff --git a/Emby.Server.Implementations/Devices/DeviceId.cs b/Emby.Server.Implementations/Devices/DeviceId.cs
index 866bd137f..495c3436a 100644
--- a/Emby.Server.Implementations/Devices/DeviceId.cs
+++ b/Emby.Server.Implementations/Devices/DeviceId.cs
@@ -11,7 +11,6 @@ namespace Emby.Server.Implementations.Devices
{
private readonly IApplicationPaths _appPaths;
private readonly ILogger _logger;
- private readonly IFileSystem _fileSystem;
private readonly object _syncLock = new object();
@@ -86,19 +85,10 @@ namespace Emby.Server.Implementations.Devices
private string _id;
- public DeviceId(
- IApplicationPaths appPaths,
- ILoggerFactory loggerFactory,
- IFileSystem fileSystem)
+ public DeviceId(IApplicationPaths appPaths, ILoggerFactory loggerFactory)
{
- if (fileSystem == null)
- {
- throw new ArgumentNullException(nameof(fileSystem));
- }
-
_appPaths = appPaths;
_logger = loggerFactory.CreateLogger("SystemId");
- _fileSystem = fileSystem;
}
public string Value => _id ?? (_id = GetDeviceId());
diff --git a/Emby.Server.Implementations/Devices/DeviceManager.cs b/Emby.Server.Implementations/Devices/DeviceManager.cs
index ec3649bca..7d6529a67 100644
--- a/Emby.Server.Implementations/Devices/DeviceManager.cs
+++ b/Emby.Server.Implementations/Devices/DeviceManager.cs
@@ -34,8 +34,6 @@ namespace Emby.Server.Implementations.Devices
private readonly IFileSystem _fileSystem;
private readonly ILibraryMonitor _libraryMonitor;
private readonly IServerConfigurationManager _config;
- private readonly ILogger _logger;
- private readonly INetworkManager _network;
private readonly ILibraryManager _libraryManager;
private readonly ILocalizationManager _localizationManager;
@@ -55,17 +53,13 @@ namespace Emby.Server.Implementations.Devices
IUserManager userManager,
IFileSystem fileSystem,
ILibraryMonitor libraryMonitor,
- IServerConfigurationManager config,
- ILoggerFactory loggerFactory,
- INetworkManager network)
+ IServerConfigurationManager config)
{
_json = json;
_userManager = userManager;
_fileSystem = fileSystem;
_libraryMonitor = libraryMonitor;
_config = config;
- _logger = loggerFactory.CreateLogger(nameof(DeviceManager));
- _network = network;
_libraryManager = libraryManager;
_localizationManager = localizationManager;
_authRepo = authRepo;
@@ -414,14 +408,12 @@ namespace Emby.Server.Implementations.Devices
{
private readonly DeviceManager _deviceManager;
private readonly IServerConfigurationManager _config;
- private readonly IFileSystem _fileSystem;
private ILogger _logger;
- public DeviceManagerEntryPoint(IDeviceManager deviceManager, IServerConfigurationManager config, IFileSystem fileSystem, ILogger logger)
+ public DeviceManagerEntryPoint(IDeviceManager deviceManager, IServerConfigurationManager config, ILogger logger)
{
_deviceManager = (DeviceManager)deviceManager;
_config = config;
- _fileSystem = fileSystem;
_logger = logger;
}
diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs
index 983eb51e6..2233d3d40 100644
--- a/Emby.Server.Implementations/Dto/DtoService.cs
+++ b/Emby.Server.Implementations/Dto/DtoService.cs
@@ -36,13 +36,9 @@ namespace Emby.Server.Implementations.Dto
private readonly IItemRepository _itemRepo;
private readonly IImageProcessor _imageProcessor;
- private readonly IServerConfigurationManager _config;
- private readonly IFileSystem _fileSystem;
private readonly IProviderManager _providerManager;
- private readonly Func<IChannelManager> _channelManagerFactory;
private readonly IApplicationHost _appHost;
- private readonly Func<IDeviceManager> _deviceManager;
private readonly Func<IMediaSourceManager> _mediaSourceManager;
private readonly Func<ILiveTvManager> _livetvManager;
@@ -52,12 +48,8 @@ namespace Emby.Server.Implementations.Dto
IUserDataManager userDataRepository,
IItemRepository itemRepo,
IImageProcessor imageProcessor,
- IServerConfigurationManager config,
- IFileSystem fileSystem,
IProviderManager providerManager,
- Func<IChannelManager> channelManagerFactory,
IApplicationHost appHost,
- Func<IDeviceManager> deviceManager,
Func<IMediaSourceManager> mediaSourceManager,
Func<ILiveTvManager> livetvManager)
{
@@ -66,12 +58,8 @@ namespace Emby.Server.Implementations.Dto
_userDataRepository = userDataRepository;
_itemRepo = itemRepo;
_imageProcessor = imageProcessor;
- _config = config;
- _fileSystem = fileSystem;
_providerManager = providerManager;
- _channelManagerFactory = channelManagerFactory;
_appHost = appHost;
- _deviceManager = deviceManager;
_mediaSourceManager = mediaSourceManager;
_livetvManager = livetvManager;
}
diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
index 6bf776f53..bbf165d62 100644
--- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj
+++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
@@ -24,6 +24,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.2.0" />
+ <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="2.2.0" />
<PackageReference Include="ServiceStack.Text.Core" Version="5.4.0" />
<PackageReference Include="sharpcompress" Version="0.22.0" />
<PackageReference Include="SQLitePCL.pretty.netstandard" Version="1.0.0" />
diff --git a/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs b/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs
index 774ed09da..a5badacee 100644
--- a/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs
+++ b/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs
@@ -14,7 +14,7 @@ using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.EntryPoints
{
- class UserDataChangeNotifier : IServerEntryPoint
+ public class UserDataChangeNotifier : IServerEntryPoint
{
private readonly ISessionManager _sessionManager;
private readonly ILogger _logger;
diff --git a/Emby.Server.Implementations/FFMpeg/FFMpegLoader.cs b/Emby.Server.Implementations/FFMpeg/FFMpegLoader.cs
index 6167d1eaa..bbf51dd24 100644
--- a/Emby.Server.Implementations/FFMpeg/FFMpegLoader.cs
+++ b/Emby.Server.Implementations/FFMpeg/FFMpegLoader.cs
@@ -3,27 +3,19 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.Net;
using MediaBrowser.Model.IO;
-using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.FFMpeg
{
public class FFMpegLoader
{
- private readonly IHttpClient _httpClient;
private readonly IApplicationPaths _appPaths;
- private readonly ILogger _logger;
- private readonly IZipClient _zipClient;
private readonly IFileSystem _fileSystem;
private readonly FFMpegInstallInfo _ffmpegInstallInfo;
- public FFMpegLoader(ILogger logger, IApplicationPaths appPaths, IHttpClient httpClient, IZipClient zipClient, IFileSystem fileSystem, FFMpegInstallInfo ffmpegInstallInfo)
+ public FFMpegLoader(IApplicationPaths appPaths, IFileSystem fileSystem, FFMpegInstallInfo ffmpegInstallInfo)
{
- _logger = logger;
_appPaths = appPaths;
- _httpClient = httpClient;
- _zipClient = zipClient;
_fileSystem = fileSystem;
_ffmpegInstallInfo = ffmpegInstallInfo;
}
@@ -115,8 +107,7 @@ namespace Emby.Server.Implementations.FFMpeg
var encoderFilename = Path.GetFileName(info.EncoderPath);
var probeFilename = Path.GetFileName(info.ProbePath);
- foreach (var directory in _fileSystem.GetDirectoryPaths(rootEncoderPath)
- .ToList())
+ foreach (var directory in _fileSystem.GetDirectoryPaths(rootEncoderPath))
{
var allFiles = _fileSystem.GetFilePaths(directory, true).ToList();
diff --git a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs
index 6ea1bd08e..2232b3eeb 100644
--- a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs
+++ b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs
@@ -66,11 +66,6 @@ namespace Emby.Server.Implementations.HttpClientManager
// http://stackoverflow.com/questions/566437/http-post-returns-the-error-417-expectation-failed-c
ServicePointManager.Expect100Continue = false;
-
-#if NET46
-// Trakt requests sometimes fail without this
- ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls;
-#endif
}
/// <summary>
@@ -106,23 +101,6 @@ namespace Emby.Server.Implementations.HttpClientManager
return client;
}
- private static WebRequest CreateWebRequest(string url)
- {
- try
- {
- return WebRequest.Create(url);
- }
- catch (NotSupportedException)
- {
- //Webrequest creation does fail on MONO randomly when using WebRequest.Create
- //the issue occurs in the GetCreator method here: http://www.oschina.net/code/explore/mono-2.8.1/mcs/class/System/System.Net/WebRequest.cs
-
- var type = Type.GetType("System.Net.HttpRequestCreator, System, Version=4.0.0.0,Culture=neutral, PublicKeyToken=b77a5c561934e089");
- var creator = Activator.CreateInstance(type, nonPublic: true) as IWebRequestCreate;
- return creator.Create(new Uri(url)) as HttpWebRequest;
- }
- }
-
private WebRequest GetRequest(HttpRequestOptions options, string method)
{
string url = options.Url;
@@ -135,7 +113,7 @@ namespace Emby.Server.Implementations.HttpClientManager
url = url.Replace(userInfo + "@", string.Empty);
}
- var request = CreateWebRequest(url);
+ var request = WebRequest.Create(url);
if (request is HttpWebRequest httpWebRequest)
{
@@ -627,14 +605,16 @@ namespace Emby.Server.Implementations.HttpClientManager
var exception = new HttpException(webException.Message, webException);
- var response = webException.Response as HttpWebResponse;
- if (response != null)
+ using (var response = webException.Response as HttpWebResponse)
{
- exception.StatusCode = response.StatusCode;
-
- if ((int)response.StatusCode == 429)
+ if (response != null)
{
- client.LastTimeout = DateTime.UtcNow;
+ exception.StatusCode = response.StatusCode;
+
+ if ((int)response.StatusCode == 429)
+ {
+ client.LastTimeout = DateTime.UtcNow;
+ }
}
}
diff --git a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
index 834ffb130..d78891ac7 100644
--- a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
+++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
@@ -19,7 +19,9 @@ using MediaBrowser.Model.Events;
using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Services;
+using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
+using ServiceStack.Text.Jsv;
namespace Emby.Server.Implementations.HttpServer
{
@@ -53,20 +55,20 @@ namespace Emby.Server.Implementations.HttpServer
IServerApplicationHost applicationHost,
ILoggerFactory loggerFactory,
IServerConfigurationManager config,
- string defaultRedirectPath,
+ IConfiguration configuration,
INetworkManager networkManager,
IJsonSerializer jsonSerializer,
- IXmlSerializer xmlSerializer,
- Func<Type, Func<string, object>> funcParseFn)
+ IXmlSerializer xmlSerializer)
{
_appHost = applicationHost;
_logger = loggerFactory.CreateLogger("HttpServer");
_config = config;
- DefaultRedirectPath = defaultRedirectPath;
+ DefaultRedirectPath = configuration["HttpListenerHost:DefaultRedirectPath"];
_networkManager = networkManager;
_jsonSerializer = jsonSerializer;
_xmlSerializer = xmlSerializer;
- _funcParseFn = funcParseFn;
+
+ _funcParseFn = t => s => JsvReader.GetParseFn(t)(s);
Instance = this;
ResponseFilters = Array.Empty<Action<IRequest, IResponse, object>>();
diff --git a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs
index 7445fd3c2..070717d48 100644
--- a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs
+++ b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs
@@ -90,7 +90,7 @@ namespace Emby.Server.Implementations.HttpServer
/// </summary>
private IHasHeaders GetHttpResult(IRequest requestContext, Stream content, string contentType, bool addCachePrevention, IDictionary<string, string> responseHeaders = null)
{
- var result = new StreamWriter(content, contentType, _logger);
+ var result = new StreamWriter(content, contentType);
if (responseHeaders == null)
{
@@ -131,7 +131,7 @@ namespace Emby.Server.Implementations.HttpServer
content = Array.Empty<byte>();
}
- result = new StreamWriter(content, contentType, contentLength, _logger);
+ result = new StreamWriter(content, contentType, contentLength);
}
else
{
@@ -143,7 +143,7 @@ namespace Emby.Server.Implementations.HttpServer
responseHeaders = new Dictionary<string, string>();
}
- if (addCachePrevention && !responseHeaders.TryGetValue("Expires", out string expires))
+ if (addCachePrevention && !responseHeaders.TryGetValue("Expires", out string _))
{
responseHeaders["Expires"] = "-1";
}
@@ -175,7 +175,7 @@ namespace Emby.Server.Implementations.HttpServer
bytes = Array.Empty<byte>();
}
- result = new StreamWriter(bytes, contentType, contentLength, _logger);
+ result = new StreamWriter(bytes, contentType, contentLength);
}
else
{
@@ -187,7 +187,7 @@ namespace Emby.Server.Implementations.HttpServer
responseHeaders = new Dictionary<string, string>();
}
- if (addCachePrevention && !responseHeaders.TryGetValue("Expires", out string expires))
+ if (addCachePrevention && !responseHeaders.TryGetValue("Expires", out string _))
{
responseHeaders["Expires"] = "-1";
}
@@ -277,9 +277,10 @@ namespace Emby.Server.Implementations.HttpServer
private object ToOptimizedResultInternal<T>(IRequest request, T dto, IDictionary<string, string> responseHeaders = null)
{
- var contentType = request.ResponseContentType;
+ // TODO: @bond use Span and .Equals
+ var contentType = request.ResponseContentType?.Split(';')[0].Trim().ToLowerInvariant();
- switch (GetRealContentType(contentType))
+ switch (contentType)
{
case "application/xml":
case "text/xml":
@@ -333,13 +334,13 @@ namespace Emby.Server.Implementations.HttpServer
if (isHeadRequest)
{
- var result = new StreamWriter(Array.Empty<byte>(), contentType, contentLength, _logger);
+ var result = new StreamWriter(Array.Empty<byte>(), contentType, contentLength);
AddResponseHeaders(result, responseHeaders);
return result;
}
else
{
- var result = new StreamWriter(content, contentType, contentLength, _logger);
+ var result = new StreamWriter(content, contentType, contentLength);
AddResponseHeaders(result, responseHeaders);
return result;
}
@@ -348,13 +349,19 @@ namespace Emby.Server.Implementations.HttpServer
private byte[] Compress(byte[] bytes, string compressionType)
{
if (string.Equals(compressionType, "br", StringComparison.OrdinalIgnoreCase))
+ {
return CompressBrotli(bytes);
+ }
if (string.Equals(compressionType, "deflate", StringComparison.OrdinalIgnoreCase))
+ {
return Deflate(bytes);
+ }
if (string.Equals(compressionType, "gzip", StringComparison.OrdinalIgnoreCase))
+ {
return GZip(bytes);
+ }
throw new NotSupportedException(compressionType);
}
@@ -390,13 +397,6 @@ namespace Emby.Server.Implementations.HttpServer
}
}
- public static string GetRealContentType(string contentType)
- {
- return contentType == null
- ? null
- : contentType.Split(';')[0].ToLowerInvariant().Trim();
- }
-
private static string SerializeToXmlString(object from)
{
using (var ms = new MemoryStream())
@@ -422,18 +422,20 @@ namespace Emby.Server.Implementations.HttpServer
/// <summary>
/// Pres the process optimized result.
/// </summary>
- private object GetCachedResult(IRequest requestContext, IDictionary<string, string> responseHeaders, Guid cacheKey, string cacheKeyString, DateTime? lastDateModified, TimeSpan? cacheDuration, string contentType)
+ private object GetCachedResult(IRequest requestContext, IDictionary<string, string> responseHeaders, StaticResultOptions options)
{
bool noCache = (requestContext.Headers.Get("Cache-Control") ?? string.Empty).IndexOf("no-cache", StringComparison.OrdinalIgnoreCase) != -1;
+ AddCachingHeaders(responseHeaders, options.CacheDuration, noCache, options.DateLastModified);
if (!noCache)
{
- if (IsNotModified(requestContext, cacheKey))
+ DateTime.TryParse(requestContext.Headers.Get("If-Modified-Since"), out var ifModifiedSinceHeader);
+
+ if (IsNotModified(ifModifiedSinceHeader, options.CacheDuration, options.DateLastModified))
{
- AddAgeHeader(responseHeaders, lastDateModified);
- AddExpiresHeader(responseHeaders, cacheKeyString, cacheDuration);
+ AddAgeHeader(responseHeaders, options.DateLastModified);
- var result = new HttpResult(Array.Empty<byte>(), contentType ?? "text/html", HttpStatusCode.NotModified);
+ var result = new HttpResult(Array.Empty<byte>(), options.ContentType ?? "text/html", HttpStatusCode.NotModified);
AddResponseHeaders(result, responseHeaders);
@@ -441,8 +443,6 @@ namespace Emby.Server.Implementations.HttpServer
}
}
- AddCachingHeaders(responseHeaders, cacheKeyString, cacheDuration);
-
return null;
}
@@ -487,9 +487,6 @@ namespace Emby.Server.Implementations.HttpServer
options.DateLastModified = _fileSystem.GetLastWriteTimeUtc(path);
}
- var cacheKey = path + options.DateLastModified.Value.Ticks;
-
- options.CacheKey = cacheKey.GetMD5();
options.ContentFactory = () => Task.FromResult(GetFileStream(path, fileShare));
options.ResponseHeaders = options.ResponseHeaders ?? new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
@@ -520,7 +517,6 @@ namespace Emby.Server.Implementations.HttpServer
return GetStaticResult(requestContext, new StaticResultOptions
{
CacheDuration = cacheDuration,
- CacheKey = cacheKey,
ContentFactory = factoryFn,
ContentType = contentType,
DateLastModified = lastDateModified,
@@ -534,14 +530,10 @@ namespace Emby.Server.Implementations.HttpServer
options.ResponseHeaders = options.ResponseHeaders ?? new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
var contentType = options.ContentType;
- var etag = requestContext.Headers.Get("If-None-Match");
- var cacheKey = etag != null ? new Guid(etag.Trim('\"')) : Guid.Empty;
- if (!cacheKey.Equals(Guid.Empty))
+ if (!string.IsNullOrEmpty(requestContext.Headers.Get("If-Modified-Since")))
{
- var key = cacheKey.ToString("N");
-
// See if the result is already cached in the browser
- var result = GetCachedResult(requestContext, options.ResponseHeaders, cacheKey, key, options.DateLastModified, options.CacheDuration, contentType);
+ var result = GetCachedResult(requestContext, options.ResponseHeaders, options);
if (result != null)
{
@@ -553,6 +545,8 @@ namespace Emby.Server.Implementations.HttpServer
var isHeadRequest = options.IsHeadRequest || string.Equals(requestContext.Verb, "HEAD", StringComparison.OrdinalIgnoreCase);
var factoryFn = options.ContentFactory;
var responseHeaders = options.ResponseHeaders;
+ AddCachingHeaders(responseHeaders, options.CacheDuration, false, options.DateLastModified);
+ AddAgeHeader(responseHeaders, options.DateLastModified);
var rangeHeader = requestContext.Headers.Get("Range");
@@ -566,21 +560,10 @@ namespace Emby.Server.Implementations.HttpServer
};
AddResponseHeaders(hasHeaders, options.ResponseHeaders);
- // Generate an ETag based on identifying information - TODO read contents from filesystem instead?
- var responseId = $"{hasHeaders.ContentType}{options.Path}{hasHeaders.TotalContentLength}";
- var hashedId = MD5.Create().ComputeHash(Encoding.Default.GetBytes(responseId));
- hasHeaders.Headers["ETag"] = new Guid(hashedId).ToString("N");
-
return hasHeaders;
}
var stream = await factoryFn().ConfigureAwait(false);
- // Generate an etag based on stream content
- var streamHash = MD5.Create().ComputeHash(stream);
- var newEtag = new Guid(streamHash).ToString("N");
-
- // reset position so the response can re-use it -- TODO is this ok?
- stream.Position = 0;
var totalContentLength = options.ContentLength;
if (!totalContentLength.HasValue)
@@ -603,7 +586,6 @@ namespace Emby.Server.Implementations.HttpServer
};
AddResponseHeaders(hasHeaders, options.ResponseHeaders);
- hasHeaders.Headers["ETag"] = newEtag;
return hasHeaders;
}
else
@@ -621,14 +603,13 @@ namespace Emby.Server.Implementations.HttpServer
}
}
- var hasHeaders = new StreamWriter(stream, contentType, _logger)
+ var hasHeaders = new StreamWriter(stream, contentType)
{
OnComplete = options.OnComplete,
OnError = options.OnError
};
AddResponseHeaders(hasHeaders, options.ResponseHeaders);
- hasHeaders.Headers["ETag"] = newEtag;
return hasHeaders;
}
}
@@ -641,37 +622,28 @@ namespace Emby.Server.Implementations.HttpServer
/// <summary>
/// Adds the caching responseHeaders.
/// </summary>
- private void AddCachingHeaders(IDictionary<string, string> responseHeaders, string cacheKey, TimeSpan? cacheDuration)
+ private void AddCachingHeaders(IDictionary<string, string> responseHeaders, TimeSpan? cacheDuration,
+ bool noCache, DateTime? lastModifiedDate)
{
- if (cacheDuration.HasValue)
- {
- responseHeaders["Cache-Control"] = "public, max-age=" + Convert.ToInt32(cacheDuration.Value.TotalSeconds);
- }
- else if (!string.IsNullOrEmpty(cacheKey))
- {
- responseHeaders["Cache-Control"] = "public";
- }
- else
+ if (noCache)
{
responseHeaders["Cache-Control"] = "no-cache, no-store, must-revalidate";
responseHeaders["pragma"] = "no-cache, no-store, must-revalidate";
+ return;
}
- AddExpiresHeader(responseHeaders, cacheKey, cacheDuration);
- }
-
- /// <summary>
- /// Adds the expires header.
- /// </summary>
- private static void AddExpiresHeader(IDictionary<string, string> responseHeaders, string cacheKey, TimeSpan? cacheDuration)
- {
if (cacheDuration.HasValue)
{
- responseHeaders["Expires"] = DateTime.UtcNow.Add(cacheDuration.Value).ToString("r");
+ responseHeaders["Cache-Control"] = "public, max-age=" + cacheDuration.Value.TotalSeconds;
}
- else if (string.IsNullOrEmpty(cacheKey))
+ else
{
- responseHeaders["Expires"] = "-1";
+ responseHeaders["Cache-Control"] = "public";
+ }
+
+ if (lastModifiedDate.HasValue)
+ {
+ responseHeaders["Last-Modified"] = lastModifiedDate.ToString();
}
}
@@ -687,32 +659,6 @@ namespace Emby.Server.Implementations.HttpServer
responseHeaders["Age"] = Convert.ToInt64((DateTime.UtcNow - lastDateModified.Value).TotalSeconds).ToString(CultureInfo.InvariantCulture);
}
}
- /// <summary>
- /// Determines whether [is not modified] [the specified cache key].
- /// </summary>
- /// <param name="requestContext">The request context.</param>
- /// <param name="cacheKey">The cache key.</param>
- /// <param name="lastDateModified">The last date modified.</param>
- /// <param name="cacheDuration">Duration of the cache.</param>
- /// <returns><c>true</c> if [is not modified] [the specified cache key]; otherwise, <c>false</c>.</returns>
- private bool IsNotModified(IRequest requestContext, Guid cacheKey)
- {
- var ifNoneMatchHeader = requestContext.Headers.Get("If-None-Match");
-
- bool hasCacheKey = !cacheKey.Equals(Guid.Empty);
-
- // Validate If-None-Match
- if (hasCacheKey && !string.IsNullOrEmpty(ifNoneMatchHeader))
- {
- if (Guid.TryParse(ifNoneMatchHeader, out var ifNoneMatch)
- && cacheKey.Equals(ifNoneMatch))
- {
- return true;
- }
- }
-
- return false;
- }
/// <summary>
/// Determines whether [is not modified] [the specified if modified since].
diff --git a/Emby.Server.Implementations/HttpServer/StreamWriter.cs b/Emby.Server.Implementations/HttpServer/StreamWriter.cs
index 3269d44cf..cb2e3580b 100644
--- a/Emby.Server.Implementations/HttpServer/StreamWriter.cs
+++ b/Emby.Server.Implementations/HttpServer/StreamWriter.cs
@@ -14,8 +14,6 @@ namespace Emby.Server.Implementations.HttpServer
/// </summary>
public class StreamWriter : IAsyncStreamWriter, IHasHeaders
{
- private ILogger Logger { get; set; }
-
private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
/// <summary>
@@ -45,7 +43,7 @@ namespace Emby.Server.Implementations.HttpServer
/// <param name="source">The source.</param>
/// <param name="contentType">Type of the content.</param>
/// <param name="logger">The logger.</param>
- public StreamWriter(Stream source, string contentType, ILogger logger)
+ public StreamWriter(Stream source, string contentType)
{
if (string.IsNullOrEmpty(contentType))
{
@@ -53,7 +51,6 @@ namespace Emby.Server.Implementations.HttpServer
}
SourceStream = source;
- Logger = logger;
Headers["Content-Type"] = contentType;
@@ -69,7 +66,7 @@ namespace Emby.Server.Implementations.HttpServer
/// <param name="source">The source.</param>
/// <param name="contentType">Type of the content.</param>
/// <param name="logger">The logger.</param>
- public StreamWriter(byte[] source, string contentType, int contentLength, ILogger logger)
+ public StreamWriter(byte[] source, string contentType, int contentLength)
{
if (string.IsNullOrEmpty(contentType))
{
@@ -77,7 +74,6 @@ namespace Emby.Server.Implementations.HttpServer
}
SourceBytes = source;
- Logger = logger;
Headers["Content-Type"] = contentType;
diff --git a/Emby.Server.Implementations/IO/FileRefresher.cs b/Emby.Server.Implementations/IO/FileRefresher.cs
index 3668f6a7a..73242d0ad 100644
--- a/Emby.Server.Implementations/IO/FileRefresher.cs
+++ b/Emby.Server.Implementations/IO/FileRefresher.cs
@@ -17,31 +17,23 @@ namespace Emby.Server.Implementations.IO
public class FileRefresher : IDisposable
{
private ILogger Logger { get; set; }
- private ITaskManager TaskManager { get; set; }
private ILibraryManager LibraryManager { get; set; }
private IServerConfigurationManager ConfigurationManager { get; set; }
- private readonly IFileSystem _fileSystem;
private readonly List<string> _affectedPaths = new List<string>();
private Timer _timer;
private readonly object _timerLock = new object();
public string Path { get; private set; }
public event EventHandler<EventArgs> Completed;
- private readonly IEnvironmentInfo _environmentInfo;
- private readonly ILibraryManager _libraryManager;
- public FileRefresher(string path, IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, ITaskManager taskManager, ILogger logger, IEnvironmentInfo environmentInfo, ILibraryManager libraryManager1)
+ public FileRefresher(string path, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, ILogger logger)
{
logger.LogDebug("New file refresher created for {0}", path);
Path = path;
- _fileSystem = fileSystem;
ConfigurationManager = configurationManager;
LibraryManager = libraryManager;
- TaskManager = taskManager;
Logger = logger;
- _environmentInfo = environmentInfo;
- _libraryManager = libraryManager1;
AddPath(path);
}
diff --git a/Emby.Server.Implementations/IO/LibraryMonitor.cs b/Emby.Server.Implementations/IO/LibraryMonitor.cs
index 607a4d333..d47342511 100644
--- a/Emby.Server.Implementations/IO/LibraryMonitor.cs
+++ b/Emby.Server.Implementations/IO/LibraryMonitor.cs
@@ -34,7 +34,7 @@ namespace Emby.Server.Implementations.IO
/// <summary>
/// Any file name ending in any of these will be ignored by the watchers
/// </summary>
- private readonly HashSet<string> _alwaysIgnoreFiles = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
+ private static readonly HashSet<string> _alwaysIgnoreFiles = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
"small.jpg",
"albumart.jpg",
@@ -44,7 +44,7 @@ namespace Emby.Server.Implementations.IO
"TempSBE"
};
- private readonly string[] _alwaysIgnoreSubstrings = new string[]
+ private static readonly string[] _alwaysIgnoreSubstrings = new string[]
{
// Synology
"eaDir",
@@ -53,7 +53,7 @@ namespace Emby.Server.Implementations.IO
".actors"
};
- private readonly HashSet<string> _alwaysIgnoreExtensions = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
+ private static readonly HashSet<string> _alwaysIgnoreExtensions = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
// thumbs.db
".db",
@@ -123,12 +123,6 @@ namespace Emby.Server.Implementations.IO
/// <value>The logger.</value>
private ILogger Logger { get; set; }
- /// <summary>
- /// Gets or sets the task manager.
- /// </summary>
- /// <value>The task manager.</value>
- private ITaskManager TaskManager { get; set; }
-
private ILibraryManager LibraryManager { get; set; }
private IServerConfigurationManager ConfigurationManager { get; set; }
@@ -140,19 +134,12 @@ namespace Emby.Server.Implementations.IO
/// </summary>
public LibraryMonitor(
ILoggerFactory loggerFactory,
- ITaskManager taskManager,
ILibraryManager libraryManager,
IServerConfigurationManager configurationManager,
IFileSystem fileSystem,
IEnvironmentInfo environmentInfo)
{
- if (taskManager == null)
- {
- throw new ArgumentNullException(nameof(taskManager));
- }
-
LibraryManager = libraryManager;
- TaskManager = taskManager;
Logger = loggerFactory.CreateLogger(GetType().Name);
ConfigurationManager = configurationManager;
_fileSystem = fileSystem;
@@ -541,7 +528,7 @@ namespace Emby.Server.Implementations.IO
}
}
- var newRefresher = new FileRefresher(path, _fileSystem, ConfigurationManager, LibraryManager, TaskManager, Logger, _environmentInfo, LibraryManager);
+ var newRefresher = new FileRefresher(path, ConfigurationManager, LibraryManager, Logger);
newRefresher.Completed += NewRefresher_Completed;
_activeRefreshers.Add(newRefresher);
}
diff --git a/Emby.Server.Implementations/IO/ManagedFileSystem.cs b/Emby.Server.Implementations/IO/ManagedFileSystem.cs
index 7c44878ec..a64dfb607 100644
--- a/Emby.Server.Implementations/IO/ManagedFileSystem.cs
+++ b/Emby.Server.Implementations/IO/ManagedFileSystem.cs
@@ -4,8 +4,10 @@ using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
+using MediaBrowser.Common.Configuration;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.System;
+using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.IO
@@ -20,61 +22,27 @@ namespace Emby.Server.Implementations.IO
private readonly bool _supportsAsyncFileStreams;
private char[] _invalidFileNameChars;
private readonly List<IShortcutHandler> _shortcutHandlers = new List<IShortcutHandler>();
- private bool EnableSeparateFileAndDirectoryQueries;
- private string _tempPath;
+ private readonly string _tempPath;
- private IEnvironmentInfo _environmentInfo;
- private bool _isEnvironmentCaseInsensitive;
-
- private string _defaultDirectory;
+ private readonly IEnvironmentInfo _environmentInfo;
+ private readonly bool _isEnvironmentCaseInsensitive;
public ManagedFileSystem(
ILoggerFactory loggerFactory,
IEnvironmentInfo environmentInfo,
- string defaultDirectory,
- string tempPath,
- bool enableSeparateFileAndDirectoryQueries)
+ IApplicationPaths applicationPaths)
{
Logger = loggerFactory.CreateLogger("FileSystem");
_supportsAsyncFileStreams = true;
- _tempPath = tempPath;
+ _tempPath = applicationPaths.TempDirectory;
_environmentInfo = environmentInfo;
- _defaultDirectory = defaultDirectory;
-
- // On Linux with mono, this needs to be true or symbolic links are ignored
- EnableSeparateFileAndDirectoryQueries = enableSeparateFileAndDirectoryQueries;
SetInvalidFileNameChars(environmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Windows);
_isEnvironmentCaseInsensitive = environmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Windows;
}
- public virtual string DefaultDirectory
- {
- get
- {
- var value = _defaultDirectory;
-
- if (!string.IsNullOrEmpty(value))
- {
- try
- {
- if (Directory.Exists(value))
- {
- return value;
- }
- }
- catch
- {
-
- }
- }
-
- return null;
- }
- }
-
public virtual void AddShortcutHandler(IShortcutHandler handler)
{
_shortcutHandlers.Add(handler);
@@ -718,7 +686,7 @@ namespace Emby.Server.Implementations.IO
SetAttributes(path, false, false);
File.Delete(path);
}
-
+
public virtual List<FileSystemMetadata> GetDrives()
{
// Only include drives in the ready state or this method could end up being very slow, waiting for drives to timeout
@@ -777,20 +745,15 @@ namespace Emby.Server.Implementations.IO
var directoryInfo = new DirectoryInfo(path);
var searchOption = recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
- if (EnableSeparateFileAndDirectoryQueries)
- {
- return ToMetadata(directoryInfo.EnumerateDirectories("*", searchOption))
- .Concat(ToMetadata(directoryInfo.EnumerateFiles("*", searchOption)));
- }
-
- return ToMetadata(directoryInfo.EnumerateFileSystemInfos("*", searchOption));
+ return ToMetadata(directoryInfo.EnumerateDirectories("*", searchOption))
+ .Concat(ToMetadata(directoryInfo.EnumerateFiles("*", searchOption)));
}
private IEnumerable<FileSystemMetadata> ToMetadata(IEnumerable<FileSystemInfo> infos)
{
return infos.Select(GetFileSystemMetadata);
}
-
+
public virtual IEnumerable<string> GetDirectoryPaths(string path, bool recursive = false)
{
var searchOption = recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
diff --git a/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs b/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs
index 80f746c7a..c644d13ea 100644
--- a/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs
+++ b/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs
@@ -1,5 +1,4 @@
using System;
-using System.Collections.Generic;
using System.IO;
using System.Linq;
using MediaBrowser.Controller.Entities;
@@ -7,7 +6,6 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Resolvers;
using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.IO;
-using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.Library
{
@@ -16,16 +14,14 @@ namespace Emby.Server.Implementations.Library
/// </summary>
public class CoreResolutionIgnoreRule : IResolverIgnoreRule
{
- private readonly IFileSystem _fileSystem;
private readonly ILibraryManager _libraryManager;
- private readonly ILogger _logger;
private bool _ignoreDotPrefix;
/// <summary>
/// Any folder named in this list will be ignored - can be added to at runtime for extensibility
/// </summary>
- public static readonly Dictionary<string, string> IgnoreFolders = new List<string>
+ public static readonly string[] IgnoreFolders =
{
"metadata",
"ps3_update",
@@ -50,13 +46,11 @@ namespace Emby.Server.Implementations.Library
// macos
".AppleDouble"
- }.ToDictionary(i => i, StringComparer.OrdinalIgnoreCase);
+ };
- public CoreResolutionIgnoreRule(IFileSystem fileSystem, ILibraryManager libraryManager, ILogger logger)
+ public CoreResolutionIgnoreRule(ILibraryManager libraryManager)
{
- _fileSystem = fileSystem;
_libraryManager = libraryManager;
- _logger = logger;
_ignoreDotPrefix = Environment.OSVersion.Platform != PlatformID.Win32NT;
}
@@ -117,7 +111,7 @@ namespace Emby.Server.Implementations.Library
if (fileInfo.IsDirectory)
{
// Ignore any folders in our list
- if (IgnoreFolders.ContainsKey(filename))
+ if (IgnoreFolders.Contains(filename, StringComparer.OrdinalIgnoreCase))
{
return true;
}
diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs
index 064006ebd..3c2272b56 100644
--- a/Emby.Server.Implementations/Library/LibraryManager.cs
+++ b/Emby.Server.Implementations/Library/LibraryManager.cs
@@ -986,7 +986,7 @@ namespace Emby.Server.Implementations.Library
// Ensure the location is available.
Directory.CreateDirectory(ConfigurationManager.ApplicationPaths.PeoplePath);
- return new PeopleValidator(this, _logger, ConfigurationManager, _fileSystem).ValidatePeople(cancellationToken, progress);
+ return new PeopleValidator(this, _logger, _fileSystem).ValidatePeople(cancellationToken, progress);
}
/// <summary>
@@ -1225,9 +1225,9 @@ namespace Emby.Server.Implementations.Library
/// <exception cref="ArgumentNullException">id</exception>
public BaseItem GetItemById(Guid id)
{
- if (id.Equals(Guid.Empty))
+ if (id == Guid.Empty)
{
- throw new ArgumentNullException(nameof(id));
+ throw new ArgumentException(nameof(id), "Guid can't be empty");
}
if (LibraryItemsCache.TryGetValue(id, out BaseItem item))
@@ -1237,8 +1237,6 @@ namespace Emby.Server.Implementations.Library
item = RetrieveItem(id);
- //_logger.LogDebug("GetitemById {0}", id);
-
if (item != null)
{
RegisterItem(item);
@@ -1333,7 +1331,7 @@ namespace Emby.Server.Implementations.Library
return ItemRepository.GetItemIdsList(query);
}
- public QueryResult<Tuple<BaseItem, ItemCounts>> GetStudios(InternalItemsQuery query)
+ public QueryResult<(BaseItem, ItemCounts)> GetStudios(InternalItemsQuery query)
{
if (query.User != null)
{
@@ -1344,7 +1342,7 @@ namespace Emby.Server.Implementations.Library
return ItemRepository.GetStudios(query);
}
- public QueryResult<Tuple<BaseItem, ItemCounts>> GetGenres(InternalItemsQuery query)
+ public QueryResult<(BaseItem, ItemCounts)> GetGenres(InternalItemsQuery query)
{
if (query.User != null)
{
@@ -1355,7 +1353,7 @@ namespace Emby.Server.Implementations.Library
return ItemRepository.GetGenres(query);
}
- public QueryResult<Tuple<BaseItem, ItemCounts>> GetMusicGenres(InternalItemsQuery query)
+ public QueryResult<(BaseItem, ItemCounts)> GetMusicGenres(InternalItemsQuery query)
{
if (query.User != null)
{
@@ -1366,7 +1364,7 @@ namespace Emby.Server.Implementations.Library
return ItemRepository.GetMusicGenres(query);
}
- public QueryResult<Tuple<BaseItem, ItemCounts>> GetAllArtists(InternalItemsQuery query)
+ public QueryResult<(BaseItem, ItemCounts)> GetAllArtists(InternalItemsQuery query)
{
if (query.User != null)
{
@@ -1377,7 +1375,7 @@ namespace Emby.Server.Implementations.Library
return ItemRepository.GetAllArtists(query);
}
- public QueryResult<Tuple<BaseItem, ItemCounts>> GetArtists(InternalItemsQuery query)
+ public QueryResult<(BaseItem, ItemCounts)> GetArtists(InternalItemsQuery query)
{
if (query.User != null)
{
@@ -1421,7 +1419,7 @@ namespace Emby.Server.Implementations.Library
}
}
- public QueryResult<Tuple<BaseItem, ItemCounts>> GetAlbumArtists(InternalItemsQuery query)
+ public QueryResult<(BaseItem, ItemCounts)> GetAlbumArtists(InternalItemsQuery query)
{
if (query.User != null)
{
@@ -1808,18 +1806,16 @@ namespace Emby.Server.Implementations.Library
/// <returns>Task.</returns>
public void CreateItems(IEnumerable<BaseItem> items, BaseItem parent, CancellationToken cancellationToken)
{
- var list = items.ToList();
-
- ItemRepository.SaveItems(list, cancellationToken);
+ ItemRepository.SaveItems(items, cancellationToken);
- foreach (var item in list)
+ foreach (var item in items)
{
RegisterItem(item);
}
if (ItemAdded != null)
{
- foreach (var item in list)
+ foreach (var item in items)
{
// With the live tv guide this just creates too much noise
if (item.SourceType != SourceType.Library)
@@ -1853,7 +1849,7 @@ namespace Emby.Server.Implementations.Library
/// <summary>
/// Updates the item.
/// </summary>
- public void UpdateItems(List<BaseItem> items, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken)
+ public void UpdateItems(IEnumerable<BaseItem> items, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken)
{
foreach (var item in items)
{
@@ -1908,7 +1904,7 @@ namespace Emby.Server.Implementations.Library
/// <returns>Task.</returns>
public void UpdateItem(BaseItem item, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken)
{
- UpdateItems(new List<BaseItem> { item }, parent, updateReason, cancellationToken);
+ UpdateItems(new [] { item }, parent, updateReason, cancellationToken);
}
/// <summary>
@@ -2005,9 +2001,7 @@ namespace Emby.Server.Implementations.Library
.FirstOrDefault();
}
- var options = collectionFolder == null ? new LibraryOptions() : collectionFolder.GetLibraryOptions();
-
- return options;
+ return collectionFolder == null ? new LibraryOptions() : collectionFolder.GetLibraryOptions();
}
public string GetContentType(BaseItem item)
@@ -2017,11 +2011,13 @@ namespace Emby.Server.Implementations.Library
{
return configuredContentType;
}
+
configuredContentType = GetConfiguredContentType(item, true);
if (!string.IsNullOrEmpty(configuredContentType))
{
return configuredContentType;
}
+
return GetInheritedContentType(item);
}
@@ -2056,6 +2052,7 @@ namespace Emby.Server.Implementations.Library
{
return collectionFolder.CollectionType;
}
+
return GetContentTypeOverride(item.ContainingFolderPath, inheritConfiguredPath);
}
@@ -2066,6 +2063,7 @@ namespace Emby.Server.Implementations.Library
{
return nameValuePair.Value;
}
+
return null;
}
@@ -2108,9 +2106,9 @@ namespace Emby.Server.Implementations.Library
string viewType,
string sortName)
{
- var path = Path.Combine(ConfigurationManager.ApplicationPaths.InternalMetadataPath, "views");
-
- path = Path.Combine(path, _fileSystem.GetValidFilename(viewType));
+ var path = Path.Combine(ConfigurationManager.ApplicationPaths.InternalMetadataPath,
+ "views",
+ _fileSystem.GetValidFilename(viewType));
var id = GetNewItemId(path + "_namedview_" + name, typeof(UserView));
@@ -2543,7 +2541,7 @@ namespace Emby.Server.Implementations.Library
var resolvers = new IItemResolver[]
{
- new GenericVideoResolver<Trailer>(this, _fileSystem)
+ new GenericVideoResolver<Trailer>(this)
};
return ResolvePaths(files, directoryService, null, new LibraryOptions(), null, resolvers)
diff --git a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs
index d992f8d03..541b13cbe 100644
--- a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs
@@ -6,7 +6,6 @@ using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.IO;
namespace Emby.Server.Implementations.Library.Resolvers
{
@@ -18,11 +17,9 @@ namespace Emby.Server.Implementations.Library.Resolvers
where T : Video, new()
{
protected readonly ILibraryManager LibraryManager;
- protected readonly IFileSystem FileSystem;
- protected BaseVideoResolver(ILibraryManager libraryManager, IFileSystem fileSystem)
+ protected BaseVideoResolver(ILibraryManager libraryManager)
{
- FileSystem = fileSystem;
LibraryManager = libraryManager;
}
diff --git a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
index 472a3f105..848563679 100644
--- a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
@@ -548,7 +548,8 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
private IImageProcessor _imageProcessor;
- public MovieResolver(ILibraryManager libraryManager, IFileSystem fileSystem, IImageProcessor imageProcessor) : base(libraryManager, fileSystem)
+ public MovieResolver(ILibraryManager libraryManager, IImageProcessor imageProcessor)
+ : base(libraryManager)
{
_imageProcessor = imageProcessor;
}
diff --git a/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs
index a3298c580..db270c398 100644
--- a/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs
@@ -7,7 +7,6 @@ using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.IO;
namespace Emby.Server.Implementations.Library.Resolvers
{
@@ -15,13 +14,11 @@ namespace Emby.Server.Implementations.Library.Resolvers
{
private readonly IImageProcessor _imageProcessor;
private readonly ILibraryManager _libraryManager;
- private readonly IFileSystem _fileSystem;
- public PhotoResolver(IImageProcessor imageProcessor, ILibraryManager libraryManager, IFileSystem fileSystem)
+ public PhotoResolver(IImageProcessor imageProcessor, ILibraryManager libraryManager)
{
_imageProcessor = imageProcessor;
_libraryManager = libraryManager;
- _fileSystem = fileSystem;
}
/// <summary>
@@ -113,8 +110,7 @@ namespace Emby.Server.Implementations.Library.Resolvers
return false;
}
- return imageProcessor.SupportedInputFormats.Contains((Path.GetExtension(path) ?? string.Empty).TrimStart('.'));
+ return imageProcessor.SupportedInputFormats.Contains(Path.GetExtension(path).TrimStart('.'), StringComparer.Ordinal);
}
-
}
}
diff --git a/Emby.Server.Implementations/Library/Resolvers/SpecialFolderResolver.cs b/Emby.Server.Implementations/Library/Resolvers/SpecialFolderResolver.cs
index fa8c89e88..7e4b38b4c 100644
--- a/Emby.Server.Implementations/Library/Resolvers/SpecialFolderResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/SpecialFolderResolver.cs
@@ -9,7 +9,7 @@ using MediaBrowser.Model.IO;
namespace Emby.Server.Implementations.Library.Resolvers
{
- class SpecialFolderResolver : FolderResolver<Folder>
+ public class SpecialFolderResolver : FolderResolver<Folder>
{
private readonly IFileSystem _fileSystem;
private readonly IServerApplicationPaths _appPaths;
diff --git a/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs b/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs
index fed0904d1..a6d18c9b5 100644
--- a/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs
@@ -3,7 +3,6 @@ using System.Linq;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.IO;
namespace Emby.Server.Implementations.Library.Resolvers.TV
{
@@ -74,7 +73,8 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
return null;
}
- public EpisodeResolver(ILibraryManager libraryManager, IFileSystem fileSystem) : base(libraryManager, fileSystem)
+ public EpisodeResolver(ILibraryManager libraryManager)
+ : base(libraryManager)
{
}
}
diff --git a/Emby.Server.Implementations/Library/Resolvers/VideoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/VideoResolver.cs
index 60752a85d..68d5d8b2d 100644
--- a/Emby.Server.Implementations/Library/Resolvers/VideoResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/VideoResolver.cs
@@ -1,13 +1,13 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
-using MediaBrowser.Model.IO;
namespace Emby.Server.Implementations.Library.Resolvers
{
public class GenericVideoResolver<T> : BaseVideoResolver<T>
where T : Video, new()
{
- public GenericVideoResolver(ILibraryManager libraryManager, IFileSystem fileSystem) : base(libraryManager, fileSystem)
+ public GenericVideoResolver(ILibraryManager libraryManager)
+ : base(libraryManager)
{
}
}
diff --git a/Emby.Server.Implementations/Library/UserManager.cs b/Emby.Server.Implementations/Library/UserManager.cs
index 3ff84382f..dfef8e997 100644
--- a/Emby.Server.Implementations/Library/UserManager.cs
+++ b/Emby.Server.Implementations/Library/UserManager.cs
@@ -74,7 +74,6 @@ namespace Emby.Server.Implementations.Library
private readonly Func<IDtoService> _dtoServiceFactory;
private readonly IServerApplicationHost _appHost;
private readonly IFileSystem _fileSystem;
- private readonly ICryptoProvider _cryptographyProvider;
private IAuthenticationProvider[] _authenticationProviders;
private DefaultAuthenticationProvider _defaultAuthenticationProvider;
@@ -89,8 +88,7 @@ namespace Emby.Server.Implementations.Library
Func<IDtoService> dtoServiceFactory,
IServerApplicationHost appHost,
IJsonSerializer jsonSerializer,
- IFileSystem fileSystem,
- ICryptoProvider cryptographyProvider)
+ IFileSystem fileSystem)
{
_logger = loggerFactory.CreateLogger(nameof(UserManager));
UserRepository = userRepository;
@@ -101,7 +99,6 @@ namespace Emby.Server.Implementations.Library
_appHost = appHost;
_jsonSerializer = jsonSerializer;
_fileSystem = fileSystem;
- _cryptographyProvider = cryptographyProvider;
ConfigurationManager = configurationManager;
_users = Array.Empty<User>();
@@ -171,9 +168,9 @@ namespace Emby.Server.Implementations.Library
/// <exception cref="ArgumentNullException"></exception>
public User GetUserById(Guid id)
{
- if (id.Equals(Guid.Empty))
+ if (id == Guid.Empty)
{
- throw new ArgumentNullException(nameof(id));
+ throw new ArgumentException(nameof(id), "Guid can't be empty");
}
return Users.FirstOrDefault(u => u.Id == id);
diff --git a/Emby.Server.Implementations/Library/Validators/PeopleValidator.cs b/Emby.Server.Implementations/Library/Validators/PeopleValidator.cs
index 0ea543ba0..7899cf01b 100644
--- a/Emby.Server.Implementations/Library/Validators/PeopleValidator.cs
+++ b/Emby.Server.Implementations/Library/Validators/PeopleValidator.cs
@@ -24,7 +24,6 @@ namespace Emby.Server.Implementations.Library.Validators
/// </summary>
private readonly ILogger _logger;
- private readonly IServerConfigurationManager _config;
private readonly IFileSystem _fileSystem;
/// <summary>
@@ -32,11 +31,10 @@ namespace Emby.Server.Implementations.Library.Validators
/// </summary>
/// <param name="libraryManager">The library manager.</param>
/// <param name="logger">The logger.</param>
- public PeopleValidator(ILibraryManager libraryManager, ILogger logger, IServerConfigurationManager config, IFileSystem fileSystem)
+ public PeopleValidator(ILibraryManager libraryManager, ILogger logger, IFileSystem fileSystem)
{
_libraryManager = libraryManager;
_logger = logger;
- _config = config;
_fileSystem = fileSystem;
}
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
index 84ca130b7..fceb82ba1 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
@@ -105,8 +105,8 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
_mediaSourceManager = mediaSourceManager;
_streamHelper = streamHelper;
- _seriesTimerProvider = new SeriesTimerManager(fileSystem, jsonSerializer, _logger, Path.Combine(DataPath, "seriestimers"));
- _timerProvider = new TimerManager(fileSystem, jsonSerializer, _logger, Path.Combine(DataPath, "timers"), _logger);
+ _seriesTimerProvider = new SeriesTimerManager(jsonSerializer, _logger, Path.Combine(DataPath, "seriestimers"));
+ _timerProvider = new TimerManager(jsonSerializer, _logger, Path.Combine(DataPath, "timers"), _logger);
_timerProvider.TimerFired += _timerProvider_TimerFired;
_config.NamedConfigurationUpdated += _config_NamedConfigurationUpdated;
@@ -1708,7 +1708,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
if (mediaSource.RequiresLooping || !(mediaSource.Container ?? string.Empty).EndsWith("ts", StringComparison.OrdinalIgnoreCase) || (mediaSource.Protocol != MediaProtocol.File && mediaSource.Protocol != MediaProtocol.Http))
{
- return new EncodedRecorder(_logger, _fileSystem, _mediaEncoder, _config.ApplicationPaths, _jsonSerializer, _httpClient, _processFactory, _config, _assemblyInfo);
+ return new EncodedRecorder(_logger, _fileSystem, _mediaEncoder, _config.ApplicationPaths, _jsonSerializer, _processFactory, _config);
}
return new DirectRecorder(_logger, _httpClient, _fileSystem, _streamHelper);
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
index eed239514..9a9bae215 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
@@ -7,7 +7,6 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.Net;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Library;
@@ -17,7 +16,6 @@ using MediaBrowser.Model.Diagnostics;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Reflection;
using MediaBrowser.Model.Serialization;
using Microsoft.Extensions.Logging;
@@ -27,7 +25,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
private readonly ILogger _logger;
private readonly IFileSystem _fileSystem;
- private readonly IHttpClient _httpClient;
private readonly IMediaEncoder _mediaEncoder;
private readonly IServerApplicationPaths _appPaths;
private bool _hasExited;
@@ -38,19 +35,23 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
private readonly IJsonSerializer _json;
private readonly TaskCompletionSource<bool> _taskCompletionSource = new TaskCompletionSource<bool>();
private readonly IServerConfigurationManager _config;
- private readonly IAssemblyInfo _assemblyInfo;
- public EncodedRecorder(ILogger logger, IFileSystem fileSystem, IMediaEncoder mediaEncoder, IServerApplicationPaths appPaths, IJsonSerializer json, IHttpClient httpClient, IProcessFactory processFactory, IServerConfigurationManager config, IAssemblyInfo assemblyInfo)
+ public EncodedRecorder(
+ ILogger logger,
+ IFileSystem fileSystem,
+ IMediaEncoder mediaEncoder,
+ IServerApplicationPaths appPaths,
+ IJsonSerializer json,
+ IProcessFactory processFactory,
+ IServerConfigurationManager config)
{
_logger = logger;
_fileSystem = fileSystem;
_mediaEncoder = mediaEncoder;
_appPaths = appPaths;
_json = json;
- _httpClient = httpClient;
_processFactory = processFactory;
_config = config;
- _assemblyInfo = assemblyInfo;
}
private static bool CopySubtitles => false;
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs
index 6b02eaea8..b825ea3b0 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs
@@ -17,15 +17,13 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
protected readonly ILogger Logger;
private readonly string _dataPath;
protected readonly Func<T, T, bool> EqualityComparer;
- private readonly IFileSystem _fileSystem;
- public ItemDataProvider(IFileSystem fileSystem, IJsonSerializer jsonSerializer, ILogger logger, string dataPath, Func<T, T, bool> equalityComparer)
+ public ItemDataProvider(IJsonSerializer jsonSerializer, ILogger logger, string dataPath, Func<T, T, bool> equalityComparer)
{
Logger = logger;
_dataPath = dataPath;
EqualityComparer = equalityComparer;
_jsonSerializer = jsonSerializer;
- _fileSystem = fileSystem;
}
public IReadOnlyList<T> GetAll()
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs
index d2ad65a1e..520b44404 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs
@@ -1,6 +1,5 @@
using System;
using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Model.IO;
using MediaBrowser.Model.Serialization;
using Microsoft.Extensions.Logging;
@@ -8,8 +7,8 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
public class SeriesTimerManager : ItemDataProvider<SeriesTimerInfo>
{
- public SeriesTimerManager(IFileSystem fileSystem, IJsonSerializer jsonSerializer, ILogger logger, string dataPath)
- : base(fileSystem, jsonSerializer, logger, dataPath, (r1, r2) => string.Equals(r1.Id, r2.Id, StringComparison.OrdinalIgnoreCase))
+ public SeriesTimerManager(IJsonSerializer jsonSerializer, ILogger logger, string dataPath)
+ : base(jsonSerializer, logger, dataPath, (r1, r2) => string.Equals(r1.Id, r2.Id, StringComparison.OrdinalIgnoreCase))
{
}
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs
index 1dcb02f43..3c807a8ea 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs
@@ -5,7 +5,6 @@ using System.Linq;
using System.Threading;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Model.Events;
-using MediaBrowser.Model.IO;
using MediaBrowser.Model.LiveTv;
using MediaBrowser.Model.Serialization;
using Microsoft.Extensions.Logging;
@@ -19,8 +18,8 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
public event EventHandler<GenericEventArgs<TimerInfo>> TimerFired;
- public TimerManager(IFileSystem fileSystem, IJsonSerializer jsonSerializer, ILogger logger, string dataPath, ILogger logger1)
- : base(fileSystem, jsonSerializer, logger, dataPath, (r1, r2) => string.Equals(r1.Id, r2.Id, StringComparison.OrdinalIgnoreCase))
+ public TimerManager(IJsonSerializer jsonSerializer, ILogger logger, string dataPath, ILogger logger1)
+ : base(jsonSerializer, logger, dataPath, (r1, r2) => string.Equals(r1.Id, r2.Id, StringComparison.OrdinalIgnoreCase))
{
_logger = logger1;
}
diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
index a36302876..f7ef16fb0 100644
--- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
+++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
@@ -184,7 +184,7 @@ namespace Emby.Server.Implementations.LiveTv
public QueryResult<BaseItem> GetInternalChannels(LiveTvChannelQuery query, DtoOptions dtoOptions, CancellationToken cancellationToken)
{
- var user = query.UserId.Equals(Guid.Empty) ? null : _userManager.GetUserById(query.UserId);
+ var user = query.UserId == Guid.Empty ? null : _userManager.GetUserById(query.UserId);
var topFolder = GetInternalLiveTvFolder(cancellationToken);
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs
index 6d1eff187..715f600a1 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs
@@ -9,7 +9,6 @@ using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.LiveTv;
@@ -23,18 +22,16 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
protected readonly IServerConfigurationManager Config;
protected readonly ILogger Logger;
protected IJsonSerializer JsonSerializer;
- protected readonly IMediaEncoder MediaEncoder;
protected readonly IFileSystem FileSystem;
private readonly ConcurrentDictionary<string, ChannelCache> _channelCache =
new ConcurrentDictionary<string, ChannelCache>(StringComparer.OrdinalIgnoreCase);
- protected BaseTunerHost(IServerConfigurationManager config, ILogger logger, IJsonSerializer jsonSerializer, IMediaEncoder mediaEncoder, IFileSystem fileSystem)
+ protected BaseTunerHost(IServerConfigurationManager config, ILogger logger, IJsonSerializer jsonSerializer, IFileSystem fileSystem)
{
Config = config;
Logger = logger;
JsonSerializer = jsonSerializer;
- MediaEncoder = mediaEncoder;
FileSystem = fileSystem;
}
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
index 77b09a83d..24b100edd 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
@@ -31,15 +31,22 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
private readonly IServerApplicationHost _appHost;
private readonly ISocketFactory _socketFactory;
private readonly INetworkManager _networkManager;
- private readonly IEnvironmentInfo _environment;
- public HdHomerunHost(IServerConfigurationManager config, ILogger logger, IJsonSerializer jsonSerializer, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IHttpClient httpClient, IServerApplicationHost appHost, ISocketFactory socketFactory, INetworkManager networkManager, IEnvironmentInfo environment) : base(config, logger, jsonSerializer, mediaEncoder, fileSystem)
+ public HdHomerunHost(
+ IServerConfigurationManager config,
+ ILogger logger,
+ IJsonSerializer jsonSerializer,
+ IFileSystem fileSystem,
+ IHttpClient httpClient,
+ IServerApplicationHost appHost,
+ ISocketFactory socketFactory,
+ INetworkManager networkManager)
+ : base(config, logger, jsonSerializer, fileSystem)
{
_httpClient = httpClient;
_appHost = appHost;
_socketFactory = socketFactory;
_networkManager = networkManager;
- _environment = environment;
}
public string Name => "HD Homerun";
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
index 638796e2e..fdaaf0bae 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
@@ -26,15 +26,14 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
{
private readonly IHttpClient _httpClient;
private readonly IServerApplicationHost _appHost;
- private readonly IEnvironmentInfo _environment;
private readonly INetworkManager _networkManager;
private readonly IMediaSourceManager _mediaSourceManager;
- public M3UTunerHost(IServerConfigurationManager config, IMediaSourceManager mediaSourceManager, ILogger logger, IJsonSerializer jsonSerializer, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IHttpClient httpClient, IServerApplicationHost appHost, IEnvironmentInfo environment, INetworkManager networkManager) : base(config, logger, jsonSerializer, mediaEncoder, fileSystem)
+ public M3UTunerHost(IServerConfigurationManager config, IMediaSourceManager mediaSourceManager, ILogger logger, IJsonSerializer jsonSerializer, IFileSystem fileSystem, IHttpClient httpClient, IServerApplicationHost appHost, INetworkManager networkManager)
+ : base(config, logger, jsonSerializer, fileSystem)
{
_httpClient = httpClient;
_appHost = appHost;
- _environment = environment;
_networkManager = networkManager;
_mediaSourceManager = mediaSourceManager;
}
@@ -52,7 +51,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
{
var channelIdPrefix = GetFullChannelIdPrefix(info);
- var result = await new M3uParser(Logger, FileSystem, _httpClient, _appHost).Parse(info.Url, channelIdPrefix, info.Id, cancellationToken).ConfigureAwait(false);
+ var result = await new M3uParser(Logger, _httpClient, _appHost).Parse(info.Url, channelIdPrefix, info.Id, cancellationToken).ConfigureAwait(false);
return result.Cast<ChannelInfo>().ToList();
}
@@ -115,7 +114,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
public async Task Validate(TunerHostInfo info)
{
- using (var stream = await new M3uParser(Logger, FileSystem, _httpClient, _appHost).GetListingsStream(info.Url, CancellationToken.None).ConfigureAwait(false))
+ using (var stream = await new M3uParser(Logger, _httpClient, _appHost).GetListingsStream(info.Url, CancellationToken.None).ConfigureAwait(false))
{
}
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
index d2a835b85..ad124bb0f 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
@@ -19,14 +19,12 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
public class M3uParser
{
private readonly ILogger _logger;
- private readonly IFileSystem _fileSystem;
private readonly IHttpClient _httpClient;
private readonly IServerApplicationHost _appHost;
- public M3uParser(ILogger logger, IFileSystem fileSystem, IHttpClient httpClient, IServerApplicationHost appHost)
+ public M3uParser(ILogger logger, IHttpClient httpClient, IServerApplicationHost appHost)
{
_logger = logger;
- _fileSystem = fileSystem;
_httpClient = httpClient;
_appHost = appHost;
}
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs
index 4eff9252e..d74cf3be2 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs
@@ -94,7 +94,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
var now = DateTime.UtcNow;
- var _ = StartStreaming(response, taskCompletionSource, LiveStreamCancellationTokenSource.Token);
+ _ = StartStreaming(response, taskCompletionSource, LiveStreamCancellationTokenSource.Token);
//OpenedMediaSource.Protocol = MediaProtocol.File;
//OpenedMediaSource.Path = tempFile;
diff --git a/Emby.Server.Implementations/Localization/Core/da.json b/Emby.Server.Implementations/Localization/Core/da.json
index 7004d44db..9d4d74099 100644
--- a/Emby.Server.Implementations/Localization/Core/da.json
+++ b/Emby.Server.Implementations/Localization/Core/da.json
@@ -2,10 +2,10 @@
"Albums": "Album",
"AppDeviceValues": "App: {0}, Enhed: {1}",
"Application": "Applikation",
- "Artists": "Kunstner",
+ "Artists": "Kunstnere",
"AuthenticationSucceededWithUserName": "{0} bekræftet med succes",
"Books": "Bøger",
- "CameraImageUploadedFrom": "A new camera image has been uploaded from {0}",
+ "CameraImageUploadedFrom": "Et nyt kamerabillede er blevet uploadet fra {0}",
"Channels": "Kanaler",
"ChapterNameValue": "Kapitel {0}",
"Collections": "Samlinger",
@@ -14,41 +14,41 @@
"FailedLoginAttemptWithUserName": "Fejlet loginforsøg fra {0}",
"Favorites": "Favoritter",
"Folders": "Mapper",
- "Genres": "Genre",
+ "Genres": "Genrer",
"HeaderAlbumArtists": "Albumkunstnere",
- "HeaderCameraUploads": "Camera Uploads",
+ "HeaderCameraUploads": "Kamera Uploads",
"HeaderContinueWatching": "Fortsæt Afspilning",
"HeaderFavoriteAlbums": "Favoritalbum",
"HeaderFavoriteArtists": "Favoritkunstnere",
- "HeaderFavoriteEpisodes": "Favoritepisoder",
- "HeaderFavoriteShows": "Favorit serier",
- "HeaderFavoriteSongs": "Favoritsange",
+ "HeaderFavoriteEpisodes": "Favorit-afsnit",
+ "HeaderFavoriteShows": "Favorit-serier",
+ "HeaderFavoriteSongs": "Favorit-sange",
"HeaderLiveTV": "Live TV",
"HeaderNextUp": "Næste",
- "HeaderRecordingGroups": "Optagegrupper",
+ "HeaderRecordingGroups": "Optagelsesgrupper",
"HomeVideos": "Hjemmevideoer",
- "Inherit": "Arv",
+ "Inherit": "Nedarv",
"ItemAddedWithName": "{0} blev tilføjet til biblioteket",
"ItemRemovedWithName": "{0} blev fjernet fra biblioteket",
"LabelIpAddressValue": "IP-adresse: {0}",
"LabelRunningTimeValue": "Spilletid: {0}",
"Latest": "Seneste",
"MessageApplicationUpdated": "Jellyfin Server er blevet opdateret",
- "MessageApplicationUpdatedTo": "Jellyfin Server has been updated to {0}",
- "MessageNamedServerConfigurationUpdatedWithValue": "Server konfigurationssektion {0} er blevet opdateret",
- "MessageServerConfigurationUpdated": "Serverkonfiguration er blevet opdateret",
+ "MessageApplicationUpdatedTo": "Jellyfin Server er blevet opdateret til {0}",
+ "MessageNamedServerConfigurationUpdatedWithValue": "Serverkonfigurationsafsnit {0} er blevet opdateret",
+ "MessageServerConfigurationUpdated": "Serverkonfigurationen er blevet opdateret",
"MixedContent": "Blandet indhold",
"Movies": "Film",
"Music": "Musik",
"MusicVideos": "Musikvideoer",
- "NameInstallFailed": "{0} installation failed",
+ "NameInstallFailed": "{0} installationen mislykkedes",
"NameSeasonNumber": "Sæson {0}",
- "NameSeasonUnknown": "Season Unknown",
- "NewVersionIsAvailable": "A new version of Jellyfin Server is available for download.",
+ "NameSeasonUnknown": "Ukendt Sæson",
+ "NewVersionIsAvailable": "En ny version af Jellyfin Server er tilgængelig til download.",
"NotificationOptionApplicationUpdateAvailable": "Opdatering til applikation tilgængelig",
"NotificationOptionApplicationUpdateInstalled": "Opdatering til applikation installeret",
- "NotificationOptionAudioPlayback": "Audioafspilning påbegyndt",
- "NotificationOptionAudioPlaybackStopped": "Audioafspilning stoppet",
+ "NotificationOptionAudioPlayback": "Lydafspilning påbegyndt",
+ "NotificationOptionAudioPlaybackStopped": "Lydafspilning stoppet",
"NotificationOptionCameraImageUploaded": "Kamerabillede uploadet",
"NotificationOptionInstallationFailed": "Installationsfejl",
"NotificationOptionNewLibraryContent": "Nyt indhold tilføjet",
@@ -70,16 +70,16 @@
"ProviderValue": "Udbyder: {0}",
"ScheduledTaskFailedWithName": "{0} fejlet",
"ScheduledTaskStartedWithName": "{0} påbegyndt",
- "ServerNameNeedsToBeRestarted": "{0} needs to be restarted",
- "Shows": "Shows",
+ "ServerNameNeedsToBeRestarted": "{0} skal genstartes",
+ "Shows": "Serier",
"Songs": "Sange",
- "StartupEmbyServerIsLoading": "Jellyfin Server indlæser. Prøv venligst igen om kort tid.",
+ "StartupEmbyServerIsLoading": "Jellyfin Server er i gang med at starte op. Prøv venligst igen om lidt.",
"SubtitleDownloadFailureForItem": "Fejlet i download af undertekster for {0}",
- "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
+ "SubtitleDownloadFailureFromForItem": "Undertekster kunne ikke downloades fra {0} til {1}",
"SubtitlesDownloadedForItem": "Undertekster downloadet for {0}",
"Sync": "Synk",
"System": "System",
- "TvShows": "TV Shows",
+ "TvShows": "TV serier",
"User": "Bruger",
"UserCreatedWithName": "Bruger {0} er blevet oprettet",
"UserDeletedWithName": "Brugeren {0} er blevet slettet",
@@ -88,10 +88,10 @@
"UserOfflineFromDevice": "{0} har afbrudt fra {1}",
"UserOnlineFromDevice": "{0} er online fra {1}",
"UserPasswordChangedWithName": "Adgangskode er ændret for bruger {0}",
- "UserPolicyUpdatedWithName": "User policy has been updated for {0}",
+ "UserPolicyUpdatedWithName": "Brugerpolitik er blevet opdateret for {0}",
"UserStartedPlayingItemWithValues": "{0} har påbegyndt afspilning af {1}",
- "UserStoppedPlayingItemWithValues": "{0} har afsluttet afspilning af {1}",
- "ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
+ "UserStoppedPlayingItemWithValues": "{0} har afsluttet afspilning af {1} på {2}",
+ "ValueHasBeenAddedToLibrary": "{0} er blevet tilføjet til dit mediebibliotek",
"ValueSpecialEpisodeName": "Special - {0}",
"VersionNumber": "Version {0}"
}
diff --git a/Emby.Server.Implementations/Localization/Core/de.json b/Emby.Server.Implementations/Localization/Core/de.json
index 7bd2e90fe..6fd63a514 100644
--- a/Emby.Server.Implementations/Localization/Core/de.json
+++ b/Emby.Server.Implementations/Localization/Core/de.json
@@ -3,61 +3,61 @@
"AppDeviceValues": "App: {0}, Gerät: {1}",
"Application": "Anwendung",
"Artists": "Interpreten",
- "AuthenticationSucceededWithUserName": "{0} erfolgreich authentifiziert",
+ "AuthenticationSucceededWithUserName": "{0} hat sich angemeldet",
"Books": "Bücher",
- "CameraImageUploadedFrom": "Ein neues Bild wurde hochgeladen von {0}",
+ "CameraImageUploadedFrom": "Ein neues Foto wurde hochgeladen von {0}",
"Channels": "Kanäle",
"ChapterNameValue": "Kapitel {0}",
"Collections": "Sammlungen",
"DeviceOfflineWithName": "{0} wurde getrennt",
- "DeviceOnlineWithName": "{0} ist verbunden",
+ "DeviceOnlineWithName": "{0} hat sich verbunden",
"FailedLoginAttemptWithUserName": "Fehlgeschlagener Anmeldeversuch von {0}",
"Favorites": "Favoriten",
"Folders": "Verzeichnisse",
"Genres": "Genres",
- "HeaderAlbumArtists": "Album-Künstler",
- "HeaderCameraUploads": "Kamera Uploads",
+ "HeaderAlbumArtists": "Album-Interpreten",
+ "HeaderCameraUploads": "Kamera-Uploads",
"HeaderContinueWatching": "Weiterschauen",
"HeaderFavoriteAlbums": "Lieblingsalben",
- "HeaderFavoriteArtists": "Interpreten Favoriten",
+ "HeaderFavoriteArtists": "Lieblings-Interpreten",
"HeaderFavoriteEpisodes": "Lieblingsepisoden",
"HeaderFavoriteShows": "Lieblingsserien",
- "HeaderFavoriteSongs": "Lieder Favoriten",
- "HeaderLiveTV": "Live TV",
+ "HeaderFavoriteSongs": "Lieblingslieder",
+ "HeaderLiveTV": "Live-TV",
"HeaderNextUp": "Als Nächstes",
"HeaderRecordingGroups": "Aufnahme-Gruppen",
"HomeVideos": "Heimvideos",
"Inherit": "Übernehmen",
"ItemAddedWithName": "{0} wurde der Bibliothek hinzugefügt",
"ItemRemovedWithName": "{0} wurde aus der Bibliothek entfernt",
- "LabelIpAddressValue": "IP Adresse: {0}",
+ "LabelIpAddressValue": "IP-Adresse: {0}",
"LabelRunningTimeValue": "Laufzeit: {0}",
"Latest": "Neueste",
- "MessageApplicationUpdated": "Jellyfin Server wurde auf den neusten Stand gebracht.",
- "MessageApplicationUpdatedTo": "Jellyfin Server wurde auf Version {0} aktualisiert",
+ "MessageApplicationUpdated": "Jellyfin-Server wurde aktualisiert",
+ "MessageApplicationUpdatedTo": "Jellyfin-Server wurde auf Version {0} aktualisiert",
"MessageNamedServerConfigurationUpdatedWithValue": "Der Server Einstellungsbereich {0} wurde aktualisiert",
- "MessageServerConfigurationUpdated": "Server Einstellungen wurden aktualisiert",
+ "MessageServerConfigurationUpdated": "Servereinstellungen wurden aktualisiert",
"MixedContent": "Gemischte Inhalte",
"Movies": "Filme",
"Music": "Musik",
"MusicVideos": "Musikvideos",
- "NameInstallFailed": "{0} Installation fehlgeschlagen",
+ "NameInstallFailed": "Installation von {0} fehlgeschlagen",
"NameSeasonNumber": "Staffel {0}",
"NameSeasonUnknown": "Staffel unbekannt",
- "NewVersionIsAvailable": "Eine neue Version von Jellyfin Server steht zum Download bereit.",
+ "NewVersionIsAvailable": "Eine neue Version von Jellyfin-Server steht zum Download bereit.",
"NotificationOptionApplicationUpdateAvailable": "Anwendungsaktualisierung verfügbar",
"NotificationOptionApplicationUpdateInstalled": "Anwendungsaktualisierung installiert",
"NotificationOptionAudioPlayback": "Audiowiedergabe gestartet",
"NotificationOptionAudioPlaybackStopped": "Audiowiedergabe gestoppt",
- "NotificationOptionCameraImageUploaded": "Kamera Bild hochgeladen",
+ "NotificationOptionCameraImageUploaded": "Foto hochgeladen",
"NotificationOptionInstallationFailed": "Installationsfehler",
"NotificationOptionNewLibraryContent": "Neuer Inhalt hinzugefügt",
- "NotificationOptionPluginError": "Plugin Fehler",
+ "NotificationOptionPluginError": "Plugin-Fehler",
"NotificationOptionPluginInstalled": "Plugin installiert",
"NotificationOptionPluginUninstalled": "Plugin deinstalliert",
"NotificationOptionPluginUpdateInstalled": "Pluginaktualisierung installiert",
"NotificationOptionServerRestartRequired": "Serverneustart notwendig",
- "NotificationOptionTaskFailed": "Geplante Aufgaben fehlgeschlagen",
+ "NotificationOptionTaskFailed": "Geplante Aufgabe fehlgeschlagen",
"NotificationOptionUserLockedOut": "Benutzer ausgeschlossen",
"NotificationOptionVideoPlayback": "Videowiedergabe gestartet",
"NotificationOptionVideoPlaybackStopped": "Videowiedergabe gestoppt",
@@ -68,18 +68,18 @@
"PluginUninstalledWithName": "{0} wurde deinstalliert",
"PluginUpdatedWithName": "{0} wurde aktualisiert",
"ProviderValue": "Anbieter: {0}",
- "ScheduledTaskFailedWithName": "{0} fehlgeschlagen",
- "ScheduledTaskStartedWithName": "{0} gestartet",
+ "ScheduledTaskFailedWithName": "{0} ist fehlgeschlagen",
+ "ScheduledTaskStartedWithName": "{0} wurde gestartet",
"ServerNameNeedsToBeRestarted": "{0} muss neu gestartet werden",
"Shows": "Serien",
"Songs": "Songs",
- "StartupEmbyServerIsLoading": "Jellyfin Server startet, bitte versuche es gleich noch einmal.",
+ "StartupEmbyServerIsLoading": "Jellyfin-Server startet, bitte versuche es gleich noch einmal.",
"SubtitleDownloadFailureForItem": "Download der Untertitel fehlgeschlagen für {0}",
- "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
+ "SubtitleDownloadFailureFromForItem": "Untertitel von {0} für {1} konnten nicht heruntergeladen werden",
"SubtitlesDownloadedForItem": "Untertitel heruntergeladen für {0}",
"Sync": "Synchronisation",
"System": "System",
- "TvShows": "TV Sendungen",
+ "TvShows": "TV-Sendungen",
"User": "Benutzer",
"UserCreatedWithName": "Benutzer {0} wurde erstellt",
"UserDeletedWithName": "Benutzer {0} wurde gelöscht",
@@ -88,10 +88,10 @@
"UserOfflineFromDevice": "{0} wurde getrennt von {1}",
"UserOnlineFromDevice": "{0} ist online von {1}",
"UserPasswordChangedWithName": "Das Passwort für Benutzer {0} wurde geändert",
- "UserPolicyUpdatedWithName": "Benutzerrichtlinie wurde für {0} aktualisiert",
- "UserStartedPlayingItemWithValues": "{0} hat die Wiedergabe von {1} gestartet",
- "UserStoppedPlayingItemWithValues": "{0} hat die Wiedergabe von {1} beendet",
- "ValueHasBeenAddedToLibrary": "{0} wurde ihrer Bibliothek hinzugefügt",
- "ValueSpecialEpisodeName": "Special - {0}",
+ "UserPolicyUpdatedWithName": "Benutzerrichtlinie von {0} wurde aktualisiert",
+ "UserStartedPlayingItemWithValues": "{0} hat die Wiedergabe von {1} auf {2} gestartet",
+ "UserStoppedPlayingItemWithValues": "{0} hat die Wiedergabe von {1} auf {2} beendet",
+ "ValueHasBeenAddedToLibrary": "{0} wurde deiner Bibliothek hinzugefügt",
+ "ValueSpecialEpisodeName": "Extra - {0}",
"VersionNumber": "Version {0}"
}
diff --git a/Emby.Server.Implementations/Localization/Core/en-GB.json b/Emby.Server.Implementations/Localization/Core/en-GB.json
index 332902292..67d4068cf 100644
--- a/Emby.Server.Implementations/Localization/Core/en-GB.json
+++ b/Emby.Server.Implementations/Localization/Core/en-GB.json
@@ -90,7 +90,7 @@
"UserPasswordChangedWithName": "Password has been changed for user {0}",
"UserPolicyUpdatedWithName": "User policy has been updated for {0}",
"UserStartedPlayingItemWithValues": "{0} has started playing {1}",
- "UserStoppedPlayingItemWithValues": "{0} has stopped playing {1}",
+ "UserStoppedPlayingItemWithValues": "{0} has finished playing {1} on {2}",
"ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
"ValueSpecialEpisodeName": "Special - {0}",
"VersionNumber": "Version {0}"
diff --git a/Emby.Server.Implementations/Localization/Core/en-US.json b/Emby.Server.Implementations/Localization/Core/en-US.json
index f19cd532b..aa855ed21 100644
--- a/Emby.Server.Implementations/Localization/Core/en-US.json
+++ b/Emby.Server.Implementations/Localization/Core/en-US.json
@@ -30,7 +30,7 @@
"Inherit": "Inherit",
"ItemAddedWithName": "{0} was added to the library",
"ItemRemovedWithName": "{0} was removed from the library",
- "LabelIpAddressValue": "Ip address: {0}",
+ "LabelIpAddressValue": "IP address: {0}",
"LabelRunningTimeValue": "Running time: {0}",
"Latest": "Latest",
"MessageApplicationUpdated": "Jellyfin Server has been updated",
diff --git a/Emby.Server.Implementations/Localization/Core/es.json b/Emby.Server.Implementations/Localization/Core/es.json
index 5d118d21f..1850b8f25 100644
--- a/Emby.Server.Implementations/Localization/Core/es.json
+++ b/Emby.Server.Implementations/Localization/Core/es.json
@@ -5,46 +5,46 @@
"Artists": "Artistas",
"AuthenticationSucceededWithUserName": "{0} autenticado correctamente",
"Books": "Libros",
- "CameraImageUploadedFrom": "A new camera image has been uploaded from {0}",
+ "CameraImageUploadedFrom": "Se ha subido una nueva imagen de cámara desde {0}",
"Channels": "Canales",
"ChapterNameValue": "Capítulo {0}",
"Collections": "Colecciones",
"DeviceOfflineWithName": "{0} se ha desconectado",
"DeviceOnlineWithName": "{0} está conectado",
- "FailedLoginAttemptWithUserName": "Error al intentar iniciar sesión a partir de {0}",
+ "FailedLoginAttemptWithUserName": "Error al intentar iniciar sesión desde {0}",
"Favorites": "Favoritos",
"Folders": "Carpetas",
"Genres": "Géneros",
- "HeaderAlbumArtists": "Artistas del Álbum",
- "HeaderCameraUploads": "Camera Uploads",
+ "HeaderAlbumArtists": "Artistas del álbum",
+ "HeaderCameraUploads": "Subidas desde cámara",
"HeaderContinueWatching": "Continuar viendo",
"HeaderFavoriteAlbums": "Álbumes favoritos",
"HeaderFavoriteArtists": "Artistas favoritos",
"HeaderFavoriteEpisodes": "Episodios favoritos",
"HeaderFavoriteShows": "Programas favoritos",
"HeaderFavoriteSongs": "Canciones favoritas",
- "HeaderLiveTV": "TV en vivo",
+ "HeaderLiveTV": "TV en directo",
"HeaderNextUp": "Siguiendo",
"HeaderRecordingGroups": "Grupos de grabación",
- "HomeVideos": "Vídeos de inicio",
+ "HomeVideos": "Vídeos caseros",
"Inherit": "Heredar",
"ItemAddedWithName": "{0} se ha añadido a la biblioteca",
- "ItemRemovedWithName": "{0} se elimina de la biblioteca",
+ "ItemRemovedWithName": "{0} ha sido eliminado de la biblioteca",
"LabelIpAddressValue": "Dirección IP: {0}",
"LabelRunningTimeValue": "Tiempo de funcionamiento: {0}",
"Latest": "Últimos",
"MessageApplicationUpdated": "Se ha actualizado el servidor Jellyfin",
- "MessageApplicationUpdatedTo": "Jellyfin Server has been updated to {0}",
- "MessageNamedServerConfigurationUpdatedWithValue": "La sección de configuración del servidor {0} ha sido actualizado",
+ "MessageApplicationUpdatedTo": "Se ha actualizado el servidor Jellyfin a la versión {0}",
+ "MessageNamedServerConfigurationUpdatedWithValue": "La sección {0} de configuración del servidor ha sido actualizada",
"MessageServerConfigurationUpdated": "Se ha actualizado la configuración del servidor",
"MixedContent": "Contenido mixto",
- "Movies": "Peliculas",
+ "Movies": "Películas",
"Music": "Música",
- "MusicVideos": "Videos musicales",
- "NameInstallFailed": "{0} installation failed",
+ "MusicVideos": "Vídeos musicales",
+ "NameInstallFailed": "{0} error de instalación",
"NameSeasonNumber": "Temporada {0}",
- "NameSeasonUnknown": "Season Unknown",
- "NewVersionIsAvailable": "A new version of Jellyfin Server is available for download.",
+ "NameSeasonUnknown": "Temporada desconocida",
+ "NewVersionIsAvailable": "Disponible una nueva versión de Jellyfin para descargar.",
"NotificationOptionApplicationUpdateAvailable": "Actualización de la aplicación disponible",
"NotificationOptionApplicationUpdateInstalled": "Actualización de la aplicación instalada",
"NotificationOptionAudioPlayback": "Se inició la reproducción de audio",
@@ -56,13 +56,13 @@
"NotificationOptionPluginInstalled": "Plugin instalado",
"NotificationOptionPluginUninstalled": "Plugin desinstalado",
"NotificationOptionPluginUpdateInstalled": "Actualización del complemento instalada",
- "NotificationOptionServerRestartRequired": "Requiere reinicio del servidor",
+ "NotificationOptionServerRestartRequired": "Se requiere reinicio del servidor",
"NotificationOptionTaskFailed": "Error de tarea programada",
"NotificationOptionUserLockedOut": "Usuario bloqueado",
"NotificationOptionVideoPlayback": "Se inició la reproducción de vídeo",
"NotificationOptionVideoPlaybackStopped": "Reproducción de vídeo detenida",
"Photos": "Fotos",
- "Playlists": "Listas reproducción",
+ "Playlists": "Listas de reproducción",
"Plugin": "Plugin",
"PluginInstalledWithName": "{0} se ha instalado",
"PluginUninstalledWithName": "{0} se ha desinstalado",
@@ -70,16 +70,16 @@
"ProviderValue": "Proveedor: {0}",
"ScheduledTaskFailedWithName": "{0} falló",
"ScheduledTaskStartedWithName": "{0} iniciada",
- "ServerNameNeedsToBeRestarted": "{0} needs to be restarted",
+ "ServerNameNeedsToBeRestarted": "{0} necesita ser reiniciado",
"Shows": "Series",
"Songs": "Canciones",
"StartupEmbyServerIsLoading": "Jellyfin Server se está cargando. Vuelve a intentarlo en breve.",
"SubtitleDownloadFailureForItem": "Error al descargar subtítulos para {0}",
- "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
+ "SubtitleDownloadFailureFromForItem": "Fallo de descarga de subtítulos desde {0} para {1}",
"SubtitlesDownloadedForItem": "Descargar subtítulos para {0}",
"Sync": "Sincronizar",
"System": "Sistema",
- "TvShows": "Series TV",
+ "TvShows": "Series de TV",
"User": "Usuario",
"UserCreatedWithName": "El usuario {0} ha sido creado",
"UserDeletedWithName": "El usuario {0} ha sido borrado",
@@ -88,10 +88,10 @@
"UserOfflineFromDevice": "{0} se ha desconectado de {1}",
"UserOnlineFromDevice": "{0} está en línea desde {1}",
"UserPasswordChangedWithName": "Se ha cambiado la contraseña para el usuario {0}",
- "UserPolicyUpdatedWithName": "User policy has been updated for {0}",
- "UserStartedPlayingItemWithValues": "{0} ha comenzado reproducir {1}",
- "UserStoppedPlayingItemWithValues": "{0} ha parado de reproducir {1}",
- "ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
+ "UserPolicyUpdatedWithName": "Actualizada política de usuario para {0}",
+ "UserStartedPlayingItemWithValues": "{0} está reproduciendo {1} en {2}",
+ "UserStoppedPlayingItemWithValues": "{0} ha terminado de reproducir {1} en {2}",
+ "ValueHasBeenAddedToLibrary": "{0} ha sido añadido a tu biblioteca multimedia",
"ValueSpecialEpisodeName": "Especial - {0}",
"VersionNumber": "Versión {0}"
}
diff --git a/Emby.Server.Implementations/Localization/Core/fr.json b/Emby.Server.Implementations/Localization/Core/fr.json
index aa9a1add3..52afb4e49 100644
--- a/Emby.Server.Implementations/Localization/Core/fr.json
+++ b/Emby.Server.Implementations/Localization/Core/fr.json
@@ -36,7 +36,7 @@
"MessageApplicationUpdated": "Le serveur Jellyfin a été mis à jour",
"MessageApplicationUpdatedTo": "Jellyfin Serveur a été mis à jour en version {0}",
"MessageNamedServerConfigurationUpdatedWithValue": "La configuration de la section {0} du serveur a été mise à jour",
- "MessageServerConfigurationUpdated": "La configuration du serveur a été mise à jour.",
+ "MessageServerConfigurationUpdated": "La configuration du serveur a été mise à jour",
"MixedContent": "Contenu mixte",
"Movies": "Films",
"Music": "Musique",
diff --git a/Emby.Server.Implementations/Localization/Core/hu.json b/Emby.Server.Implementations/Localization/Core/hu.json
index d2d16b18f..c0f988abe 100644
--- a/Emby.Server.Implementations/Localization/Core/hu.json
+++ b/Emby.Server.Implementations/Localization/Core/hu.json
@@ -5,48 +5,48 @@
"Artists": "Előadók",
"AuthenticationSucceededWithUserName": "{0} sikeresen azonosítva",
"Books": "Könyvek",
- "CameraImageUploadedFrom": "A new camera image has been uploaded from {0}",
+ "CameraImageUploadedFrom": "Új kamerakép került feltöltésre {0}",
"Channels": "Csatornák",
"ChapterNameValue": "Jelenet {0}",
"Collections": "Gyűjtemények",
"DeviceOfflineWithName": "{0} kijelentkezett",
"DeviceOnlineWithName": "{0} belépett",
- "FailedLoginAttemptWithUserName": "Failed login attempt from {0}",
+ "FailedLoginAttemptWithUserName": "Sikertelen bejelentkezési kísérlet {0}",
"Favorites": "Kedvencek",
"Folders": "Könyvtárak",
"Genres": "Műfajok",
"HeaderAlbumArtists": "Album Előadók",
- "HeaderCameraUploads": "Camera Uploads",
- "HeaderContinueWatching": "Vetítés(ek) folytatása",
+ "HeaderCameraUploads": "Kamera feltöltések",
+ "HeaderContinueWatching": "Folyamatban lévő filmek",
"HeaderFavoriteAlbums": "Kedvenc Albumok",
"HeaderFavoriteArtists": "Kedvenc Művészek",
"HeaderFavoriteEpisodes": "Kedvenc Epizódok",
"HeaderFavoriteShows": "Kedvenc Műsorok",
"HeaderFavoriteSongs": "Kedvenc Dalok",
- "HeaderLiveTV": "Live TV",
+ "HeaderLiveTV": "Élő TV",
"HeaderNextUp": "Következik",
- "HeaderRecordingGroups": "Recording Groups",
+ "HeaderRecordingGroups": "Felvételi csoportok",
"HomeVideos": "Házi videók",
"Inherit": "Inherit",
- "ItemAddedWithName": "{0} was added to the library",
- "ItemRemovedWithName": "{0} was removed from the library",
- "LabelIpAddressValue": "Ip cím: {0}",
- "LabelRunningTimeValue": "Running time: {0}",
+ "ItemAddedWithName": "{0} hozzáadva a könyvtárhoz",
+ "ItemRemovedWithName": "{0} eltávolítva a könyvtárból",
+ "LabelIpAddressValue": "IP cím: {0}",
+ "LabelRunningTimeValue": "Futási idő: {0}",
"Latest": "Legújabb",
"MessageApplicationUpdated": "Jellyfin Szerver frissítve",
- "MessageApplicationUpdatedTo": "Jellyfin Server has been updated to {0}",
+ "MessageApplicationUpdatedTo": "Jellyfin Szerver frissítve lett a következőre {0}",
"MessageNamedServerConfigurationUpdatedWithValue": "Szerver konfigurációs rész {0} frissítve",
"MessageServerConfigurationUpdated": "Szerver konfiguráció frissítve",
"MixedContent": "Vegyes tartalom",
"Movies": "Filmek",
"Music": "Zene",
"MusicVideos": "Zenei Videók",
- "NameInstallFailed": "{0} installation failed",
- "NameSeasonNumber": "Season {0}",
- "NameSeasonUnknown": "Season Unknown",
- "NewVersionIsAvailable": "A new version of Jellyfin Server is available for download.",
- "NotificationOptionApplicationUpdateAvailable": "Program frissítés elérhető",
- "NotificationOptionApplicationUpdateInstalled": "Program frissítés telepítve",
+ "NameInstallFailed": "{0} sikertelen telepítés",
+ "NameSeasonNumber": "Évad {0}",
+ "NameSeasonUnknown": "Ismeretlen évad",
+ "NewVersionIsAvailable": "Letölthető a Jellyfin Szerver új verziója.",
+ "NotificationOptionApplicationUpdateAvailable": "Új programfrissítés érhető el",
+ "NotificationOptionApplicationUpdateInstalled": "Programfrissítés telepítve",
"NotificationOptionAudioPlayback": "Audió lejátszás elkezdve",
"NotificationOptionAudioPlaybackStopped": "Audió lejátszás befejezve",
"NotificationOptionCameraImageUploaded": "Kamera kép feltöltve",
@@ -57,7 +57,7 @@
"NotificationOptionPluginUninstalled": "Bővítmény eltávolítva",
"NotificationOptionPluginUpdateInstalled": "Bővítmény frissítés telepítve",
"NotificationOptionServerRestartRequired": "Szerver újraindítás szükséges",
- "NotificationOptionTaskFailed": "Scheduled task failure",
+ "NotificationOptionTaskFailed": "Ütemezett feladat hiba",
"NotificationOptionUserLockedOut": "Felhasználó tiltva",
"NotificationOptionVideoPlayback": "Videó lejátszás elkezdve",
"NotificationOptionVideoPlaybackStopped": "Videó lejátszás befejezve",
@@ -68,30 +68,30 @@
"PluginUninstalledWithName": "{0} eltávolítva",
"PluginUpdatedWithName": "{0} frissítve",
"ProviderValue": "Provider: {0}",
- "ScheduledTaskFailedWithName": "{0} failed",
- "ScheduledTaskStartedWithName": "{0} started",
- "ServerNameNeedsToBeRestarted": "{0} needs to be restarted",
+ "ScheduledTaskFailedWithName": "{0} hiba",
+ "ScheduledTaskStartedWithName": "{0} elkezdve",
+ "ServerNameNeedsToBeRestarted": "{0}-t újra kell indítani",
"Shows": "Műsorok",
"Songs": "Dalok",
- "StartupEmbyServerIsLoading": "Jellyfin Szerver betöltődik. Kérjük, próbáld meg újra később.",
+ "StartupEmbyServerIsLoading": "A Jellyfin Szerver betöltődik. Kérlek próbáld újra később.",
"SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}",
- "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
- "SubtitlesDownloadedForItem": "Subtitles downloaded for {0}",
+ "SubtitleDownloadFailureFromForItem": "Nem sikerült a felirat letöltése innen: {0} ehhez: {1}",
+ "SubtitlesDownloadedForItem": "Letöltött feliratok a következőhöz {0}",
"Sync": "Szinkronizál",
"System": "Rendszer",
"TvShows": "TV Műsorok",
"User": "Felhasználó",
- "UserCreatedWithName": "User {0} has been created",
- "UserDeletedWithName": "User {0} has been deleted",
+ "UserCreatedWithName": "{0} felhasználó létrehozva",
+ "UserDeletedWithName": "{0} felhasználó törölve",
"UserDownloadingItemWithValues": "{0} letölti {1}",
- "UserLockedOutWithName": "User {0} has been locked out",
- "UserOfflineFromDevice": "{0} kijelentkezett innen {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} elkezdte játszani a következőt {1}",
- "UserStoppedPlayingItemWithValues": "{0} befejezte a következőt {1}",
- "ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
+ "UserLockedOutWithName": "{0} felhasználó zárolva van",
+ "UserOfflineFromDevice": "{0} kijelentkezett innen: {1}",
+ "UserOnlineFromDevice": "{0} online itt: {1}",
+ "UserPasswordChangedWithName": "Jelszó megváltozott a következő felhasználó számára: {0}",
+ "UserPolicyUpdatedWithName": "A felhasználói házirend frissítve lett {0}",
+ "UserStartedPlayingItemWithValues": "{0} elkezdte játszani a következőt: {1} itt: {2}",
+ "UserStoppedPlayingItemWithValues": "{0} befejezte a következőt: {1} itt: {2}",
+ "ValueHasBeenAddedToLibrary": "{0} hozzáadva a médiatárhoz",
"ValueSpecialEpisodeName": "Special - {0}",
- "VersionNumber": "Verzió {0}"
+ "VersionNumber": "Verzió: {0}"
}
diff --git a/Emby.Server.Implementations/Localization/Core/it.json b/Emby.Server.Implementations/Localization/Core/it.json
index b3d9c16cf..a5f1e8f94 100644
--- a/Emby.Server.Implementations/Localization/Core/it.json
+++ b/Emby.Server.Implementations/Localization/Core/it.json
@@ -5,13 +5,13 @@
"Artists": "Artisti",
"AuthenticationSucceededWithUserName": "{0} autenticato con successo",
"Books": "Libri",
- "CameraImageUploadedFrom": "A new camera image has been uploaded from {0}",
+ "CameraImageUploadedFrom": "È stata caricata una nuova immagine della fotocamera {0}",
"Channels": "Canali",
"ChapterNameValue": "Capitolo {0}",
"Collections": "Collezioni",
"DeviceOfflineWithName": "{0} è stato disconnesso",
"DeviceOnlineWithName": "{0} è connesso",
- "FailedLoginAttemptWithUserName": "Tentativo di accesso fallito da {0}",
+ "FailedLoginAttemptWithUserName": "Tentativo di accesso fallito da {0}",
"Favorites": "Preferiti",
"Folders": "Cartelle",
"Genres": "Generi",
@@ -19,9 +19,9 @@
"HeaderCameraUploads": "Caricamenti Fotocamera",
"HeaderContinueWatching": "Continua a guardare",
"HeaderFavoriteAlbums": "Album preferiti",
- "HeaderFavoriteArtists": "Artisti preferiti",
+ "HeaderFavoriteArtists": "Artisti Preferiti",
"HeaderFavoriteEpisodes": "Episodi Preferiti",
- "HeaderFavoriteShows": "Show preferiti",
+ "HeaderFavoriteShows": "Serie TV Preferite",
"HeaderFavoriteSongs": "Brani Preferiti",
"HeaderLiveTV": "Diretta TV",
"HeaderNextUp": "Prossimo",
diff --git a/Emby.Server.Implementations/Localization/Core/kk.json b/Emby.Server.Implementations/Localization/Core/kk.json
index ae256f79d..658d168e9 100644
--- a/Emby.Server.Implementations/Localization/Core/kk.json
+++ b/Emby.Server.Implementations/Localization/Core/kk.json
@@ -1,97 +1,97 @@
{
- "Albums": "Альбомдар",
- "AppDeviceValues": "Қолданба: {0}, Құрылғы: {1}",
- "Application": "Қолданба",
- "Artists": "Орындаушылар",
- "AuthenticationSucceededWithUserName": "{0} түпнұсқалығын расталуы сәтті",
- "Books": "Кітаптар",
- "CameraImageUploadedFrom": "Жаңа сурет {0} камерасынан жүктеп алынды",
- "Channels": "Арналар",
- "ChapterNameValue": "{0}-сахна",
- "Collections": "Жиынтықтар",
- "DeviceOfflineWithName": "{0} ажыратылған",
- "DeviceOnlineWithName": "{0} қосылған",
- "FailedLoginAttemptWithUserName": "{0} тарапынан кіру әрекеті сәтсіз",
- "Favorites": "Таңдаулылар",
- "Folders": "Қалталар",
- "Genres": "Жанрлар",
- "HeaderAlbumArtists": "Альбом орындаушылары",
- "HeaderCameraUploads": "Камерадан жүктелгендер",
- "HeaderContinueWatching": "Қарауды жалғастыру",
- "HeaderFavoriteAlbums": "Таңдаулы альбомдар",
- "HeaderFavoriteArtists": "Таңдаулы орындаушылар",
- "HeaderFavoriteEpisodes": "Таңдаулы бөлімдер",
- "HeaderFavoriteShows": "Таңдаулы көрсетімдер",
- "HeaderFavoriteSongs": "Таңдаулы әуендер",
- "HeaderLiveTV": "Эфир",
- "HeaderNextUp": "Кезекті",
- "HeaderRecordingGroups": "Жазба топтары",
- "HomeVideos": "Үйлік бейнелер",
- "Inherit": "Мұраға иелену",
- "ItemAddedWithName": "{0} тасығышханаға үстелінді",
- "ItemRemovedWithName": "{0} тасығышханадан аласталды",
- "LabelIpAddressValue": "IP-мекенжайы: {0}",
- "LabelRunningTimeValue": "Іске қосылу уақыты: {0}",
- "Latest": "Ең кейінгі",
- "MessageApplicationUpdated": "Jellyfin Server жаңартылды.",
- "MessageApplicationUpdatedTo": "Jellyfin Server {0} үшін жаңартылды",
- "MessageNamedServerConfigurationUpdatedWithValue": "Сервер теңшелімі ({0} бөлімі) жаңартылды",
- "MessageServerConfigurationUpdated": "Сервер теңшелімі жаңартылды",
- "MixedContent": "Аралас мазмұн",
- "Movies": "Фильмдер",
- "Music": "Музыка",
- "MusicVideos": "Музыкалық бейнелер",
- "NameInstallFailed": "{0} орнатылуы сәтсіз",
- "NameSeasonNumber": "{0}-маусым",
- "NameSeasonUnknown": "Белгісіз маусым",
- "NewVersionIsAvailable": "Жаңа Jellyfin Server нұсқасы жүктеп алуға қолжетімді.",
- "NotificationOptionApplicationUpdateAvailable": "Қолданба жаңартуы қолжетімді",
- "NotificationOptionApplicationUpdateInstalled": "Қолданба жаңартуы орнатылды",
- "NotificationOptionAudioPlayback": "Дыбыс ойнатуы басталды",
- "NotificationOptionAudioPlaybackStopped": "Дыбыс ойнатуы тоқтатылды",
- "NotificationOptionCameraImageUploaded": "Камерадан фотосурет кері қотарылған",
- "NotificationOptionInstallationFailed": "Орнату сәтсіздігі",
- "NotificationOptionNewLibraryContent": "Жаңа мазмұн үстелген",
- "NotificationOptionPluginError": "Плагин сәтсіздігі",
- "NotificationOptionPluginInstalled": "Плагин орнатылды",
- "NotificationOptionPluginUninstalled": "Плагин орнатуы болдырылмады",
- "NotificationOptionPluginUpdateInstalled": "Плагин жаңартуы орнатылды",
- "NotificationOptionServerRestartRequired": "Серверді қайта іске қосу қажет",
- "NotificationOptionTaskFailed": "Жоспарлаған тапсырма сәтсіздігі",
- "NotificationOptionUserLockedOut": "Пайдаланушы құрсаулы",
- "NotificationOptionVideoPlayback": "Бейне ойнатуы басталды",
- "NotificationOptionVideoPlaybackStopped": "Бейне ойнатуы тоқтатылды",
- "Photos": "Фотосуреттер",
- "Playlists": "Ойнату тізімдері",
- "Plugin": "Плагин",
- "PluginInstalledWithName": "{0} орнатылды",
- "PluginUninstalledWithName": "{0} жойылды",
- "PluginUpdatedWithName": "{0} жаңартылды",
- "ProviderValue": "Жеткізуші: {0}",
- "ScheduledTaskFailedWithName": "{0} сәтсіз",
- "ScheduledTaskStartedWithName": "{0} іске қосылды",
- "ServerNameNeedsToBeRestarted": "{0} қайта іске қосу қажет",
- "Shows": "Көрсетімдер",
- "Songs": "Әуендер",
- "StartupEmbyServerIsLoading": "Jellyfin Server жүктелуде. Әрекетті көп ұзамай қайталаңыз.",
+ "Albums": "Álbomdar",
+ "AppDeviceValues": "Qoldanba: {0}, Qurylǵy: {1}",
+ "Application": "Qoldanba",
+ "Artists": "Oryndaýshylar",
+ "AuthenticationSucceededWithUserName": "{0} túpnusqalyǵyn rastalýy sátti",
+ "Books": "Kitaptar",
+ "CameraImageUploadedFrom": "Jańa sýret {0} kamerasynan júktep alyndy",
+ "Channels": "Arnalar",
+ "ChapterNameValue": "{0}-sahna",
+ "Collections": "Jıyntyqtar",
+ "DeviceOfflineWithName": "{0} ajyratylǵan",
+ "DeviceOnlineWithName": "{0} qosylǵan",
+ "FailedLoginAttemptWithUserName": "{0} tarapynan kirý áreketi sátsiz",
+ "Favorites": "Tańdaýlylar",
+ "Folders": "Qaltalar",
+ "Genres": "Janrlar",
+ "HeaderAlbumArtists": "Álbom oryndaýshylary",
+ "HeaderCameraUploads": "Kameradan júktelgender",
+ "HeaderContinueWatching": "Qaraýdy jalǵastyrý",
+ "HeaderFavoriteAlbums": "Tańdaýly álbomdar",
+ "HeaderFavoriteArtists": "Tańdaýly oryndaýshylar",
+ "HeaderFavoriteEpisodes": "Tańdaýly bólimder",
+ "HeaderFavoriteShows": "Tańdaýly kórsetimder",
+ "HeaderFavoriteSongs": "Tańdaýly áýender",
+ "HeaderLiveTV": "Efır",
+ "HeaderNextUp": "Kezekti",
+ "HeaderRecordingGroups": "Jazba toptary",
+ "HomeVideos": "Úılik beıneler",
+ "Inherit": "Muraǵa ıelený",
+ "ItemAddedWithName": "{0} tasyǵyshhanaǵa ústelindi",
+ "ItemRemovedWithName": "{0} tasyǵyshhanadan alastaldy",
+ "LabelIpAddressValue": "IP-mekenjaıy: {0}",
+ "LabelRunningTimeValue": "Oınatý ýaqyty: {0}",
+ "Latest": "Eń keıingi",
+ "MessageApplicationUpdated": "Jellyfin Serveri jańartyldy",
+ "MessageApplicationUpdatedTo": "Jellyfin Serveri {0} deńgeıge jańartyldy",
+ "MessageNamedServerConfigurationUpdatedWithValue": "Server teńsheliminiń {0} bólimi jańartyldy",
+ "MessageServerConfigurationUpdated": "Server teńshelimi jańartyldy",
+ "MixedContent": "Aralas mazmun",
+ "Movies": "Fılmder",
+ "Music": "Mýzyka",
+ "MusicVideos": "Mýzykalyq beıneler",
+ "NameInstallFailed": "{0} ornatylýy sátsiz",
+ "NameSeasonNumber": "{0}-maýsym",
+ "NameSeasonUnknown": "Belgisiz maýsym",
+ "NewVersionIsAvailable": "Jańa Jellyfin Server nusqasy júktep alýǵa qoljetimdi.",
+ "NotificationOptionApplicationUpdateAvailable": "Qoldanba jańartýy qoljetimdi",
+ "NotificationOptionApplicationUpdateInstalled": "Qoldanba jańartýy ornatyldy",
+ "NotificationOptionAudioPlayback": "Dybys oınatýy bastaldy",
+ "NotificationOptionAudioPlaybackStopped": "Dybys oınatýy toqtatyldy",
+ "NotificationOptionCameraImageUploaded": "Kameradan fotosýret keri qotarylǵan",
+ "NotificationOptionInstallationFailed": "Ornatý sátsizdigi",
+ "NotificationOptionNewLibraryContent": "Jańa mazmun ústelgen",
+ "NotificationOptionPluginError": "Plagın sátsizdigi",
+ "NotificationOptionPluginInstalled": "Plagın ornatyldy",
+ "NotificationOptionPluginUninstalled": "Plagın ornatýy boldyrylmady",
+ "NotificationOptionPluginUpdateInstalled": "Plagın jańartýy ornatyldy",
+ "NotificationOptionServerRestartRequired": "Serverdi qaıta iske qosý qajet",
+ "NotificationOptionTaskFailed": "Josparlaǵan tapsyrma sátsizdigi",
+ "NotificationOptionUserLockedOut": "Paıdalanýshy qursaýly",
+ "NotificationOptionVideoPlayback": "Beıne oınatýy bastaldy",
+ "NotificationOptionVideoPlaybackStopped": "Beıne oınatýy toqtatyldy",
+ "Photos": "Fotosýretter",
+ "Playlists": "Oınatý tizimderi",
+ "Plugin": "Plagın",
+ "PluginInstalledWithName": "{0} ornatyldy",
+ "PluginUninstalledWithName": "{0} joıyldy",
+ "PluginUpdatedWithName": "{0} jańartyldy",
+ "ProviderValue": "Jetkizýshi: {0}",
+ "ScheduledTaskFailedWithName": "{0} sátsiz",
+ "ScheduledTaskStartedWithName": "{0} iske qosyldy",
+ "ServerNameNeedsToBeRestarted": "{0} qaıta iske qosý qajet",
+ "Shows": "Kórsetimder",
+ "Songs": "Áýender",
+ "StartupEmbyServerIsLoading": "Jellyfin Server júktelýde. Áreketti kóp uzamaı qaıtalańyz.",
"SubtitleDownloadFailureForItem": "Субтитрлер {0} үшін жүктеліп алынуы сәтсіз",
- "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
- "SubtitlesDownloadedForItem": "{0} үшін субтитрлер жүктеліп алынды",
- "Sync": "Үндестіру",
- "System": "Жүйе",
- "TvShows": "ТД-көрсетімдер",
- "User": "Пайдаланушы",
- "UserCreatedWithName": "Пайдаланушы {0} жасалған",
- "UserDeletedWithName": "Пайдаланушы {0} жойылған",
- "UserDownloadingItemWithValues": "{0} мынаны жүктеп алуда: {1}",
- "UserLockedOutWithName": "Пайдаланушы {0} құрсаулы",
- "UserOfflineFromDevice": "{0} - {1} тарапынан ажыратылған",
- "UserOnlineFromDevice": "{0} - {1} арқылы қосылған",
- "UserPasswordChangedWithName": "Пайдаланушы {0} үшін құпия сөз өзгертілді",
- "UserPolicyUpdatedWithName": "Пайдаланушы {0} үшін саясаттары жаңартылды",
- "UserStartedPlayingItemWithValues": "{0} - {1} ойнатуын {2} бастады",
- "UserStoppedPlayingItemWithValues": "{0} - {1} ойнатуын {2} тоқтатты",
- "ValueHasBeenAddedToLibrary": "{0} (тасығышханаға үстелінді)",
- "ValueSpecialEpisodeName": "Арнайы - {0}",
- "VersionNumber": "Нұсқасы: {0}"
+ "SubtitleDownloadFailureFromForItem": "{1} úshin sýbtıtrlerdi {0} kózinen júktep alý sátsiz",
+ "SubtitlesDownloadedForItem": "{0} úshin sýbtıtrler júktelip alyndy",
+ "Sync": "Úndestirý",
+ "System": "Júıe",
+ "TvShows": "TD-kórsetimder",
+ "User": "Paıdalanýshy",
+ "UserCreatedWithName": "Paıdalanýshy {0} jasalǵan",
+ "UserDeletedWithName": "Paıdalanýshy {0} joıylǵan",
+ "UserDownloadingItemWithValues": "{0} mynany júktep alýda: {1}",
+ "UserLockedOutWithName": "Paıdalanýshy {0} qursaýly",
+ "UserOfflineFromDevice": "{0} - {1} tarapynan ajyratylǵan",
+ "UserOnlineFromDevice": "{0} - {1} arqyly qosylǵan",
+ "UserPasswordChangedWithName": "Paıdalanýshy {0} úshin paról ózgertildi",
+ "UserPolicyUpdatedWithName": "Paıdalanýshy {0} úshin saıasattary jańartyldy",
+ "UserStartedPlayingItemWithValues": "{0} - {1} oınatýyn {2} bastady",
+ "UserStoppedPlayingItemWithValues": "{0} - {1} oınatýyn {2} toqtatty",
+ "ValueHasBeenAddedToLibrary": "{0} (tasyǵyshhanaǵa ústelindi)",
+ "ValueSpecialEpisodeName": "Arnaıy - {0}",
+ "VersionNumber": "Nusqasy {0}"
}
diff --git a/Emby.Server.Implementations/Localization/Core/ms.json b/Emby.Server.Implementations/Localization/Core/ms.json
index c01bb0c50..c10fbe58f 100644
--- a/Emby.Server.Implementations/Localization/Core/ms.json
+++ b/Emby.Server.Implementations/Localization/Core/ms.json
@@ -1,10 +1,10 @@
{
- "Albums": "Albums",
+ "Albums": "Album-album",
"AppDeviceValues": "App: {0}, Device: {1}",
"Application": "Application",
- "Artists": "Artists",
+ "Artists": "Artis-artis",
"AuthenticationSucceededWithUserName": "{0} successfully authenticated",
- "Books": "Books",
+ "Books": "Buku-buku",
"CameraImageUploadedFrom": "A new camera image has been uploaded from {0}",
"Channels": "Channels",
"ChapterNameValue": "Chapter {0}",
diff --git a/Emby.Server.Implementations/Localization/Core/nl.json b/Emby.Server.Implementations/Localization/Core/nl.json
index 589753c44..24af1839f 100644
--- a/Emby.Server.Implementations/Localization/Core/nl.json
+++ b/Emby.Server.Implementations/Localization/Core/nl.json
@@ -5,28 +5,28 @@
"Artists": "Artiesten",
"AuthenticationSucceededWithUserName": "{0} is succesvol geverifieerd",
"Books": "Boeken",
- "CameraImageUploadedFrom": "A new camera image has been uploaded from {0}",
+ "CameraImageUploadedFrom": "Er is een nieuwe foto toegevoegd via {0}",
"Channels": "Kanalen",
"ChapterNameValue": "Hoofdstuk {0}",
"Collections": "Collecties",
- "DeviceOfflineWithName": "{0} is losgekoppeld",
+ "DeviceOfflineWithName": "{0} heeft de verbinding verbroken",
"DeviceOnlineWithName": "{0} is verbonden",
"FailedLoginAttemptWithUserName": "Mislukte aanmeld poging van {0}",
"Favorites": "Favorieten",
"Folders": "Mappen",
"Genres": "Genres",
- "HeaderAlbumArtists": "Album artiesten",
- "HeaderCameraUploads": "Camera uploads",
+ "HeaderAlbumArtists": "Albumartiesten",
+ "HeaderCameraUploads": "Camera-uploads",
"HeaderContinueWatching": "Kijken hervatten",
"HeaderFavoriteAlbums": "Favoriete albums",
"HeaderFavoriteArtists": "Favoriete artiesten",
"HeaderFavoriteEpisodes": "Favoriete afleveringen",
"HeaderFavoriteShows": "Favoriete shows",
- "HeaderFavoriteSongs": "Favoriete titels",
+ "HeaderFavoriteSongs": "Favoriete nummers",
"HeaderLiveTV": "Live TV",
"HeaderNextUp": "Volgende",
"HeaderRecordingGroups": "Opnamegroepen",
- "HomeVideos": "Thuis video's",
+ "HomeVideos": "Start video's",
"Inherit": "Overerven",
"ItemAddedWithName": "{0} is toegevoegd aan de bibliotheek",
"ItemRemovedWithName": "{0} is verwijderd uit de bibliotheek",
@@ -34,22 +34,22 @@
"LabelRunningTimeValue": "Looptijd: {0}",
"Latest": "Nieuwste",
"MessageApplicationUpdated": "Jellyfin Server is bijgewerkt",
- "MessageApplicationUpdatedTo": "Jellyfin Server has been updated to {0}",
+ "MessageApplicationUpdatedTo": "Jellyfin Server is bijgewerkt naar {0}",
"MessageNamedServerConfigurationUpdatedWithValue": "Sectie {0} van de server configuratie is bijgewerkt",
"MessageServerConfigurationUpdated": "Server configuratie is bijgewerkt",
"MixedContent": "Gemengde inhoud",
"Movies": "Films",
"Music": "Muziek",
"MusicVideos": "Muziekvideo's",
- "NameInstallFailed": "{0} installation failed",
+ "NameInstallFailed": "{0} installatie mislukt",
"NameSeasonNumber": "Seizoen {0}",
"NameSeasonUnknown": "Seizoen onbekend",
- "NewVersionIsAvailable": "A new version of Jellyfin Server is available for download.",
+ "NewVersionIsAvailable": "Een nieuwe versie van Jellyfin Server is beschikbaar om te downloaden.",
"NotificationOptionApplicationUpdateAvailable": "Programma-update beschikbaar",
"NotificationOptionApplicationUpdateInstalled": "Programma-update geïnstalleerd",
- "NotificationOptionAudioPlayback": "Geluid gestart",
- "NotificationOptionAudioPlaybackStopped": "Geluid gestopt",
- "NotificationOptionCameraImageUploaded": "Camera afbeelding geüpload",
+ "NotificationOptionAudioPlayback": "Muziek gestart",
+ "NotificationOptionAudioPlaybackStopped": "Muziek gestopt",
+ "NotificationOptionCameraImageUploaded": "Camera-afbeelding geüpload",
"NotificationOptionInstallationFailed": "Installatie mislukt",
"NotificationOptionNewLibraryContent": "Nieuwe content toegevoegd",
"NotificationOptionPluginError": "Plug-in fout",
@@ -70,12 +70,12 @@
"ProviderValue": "Aanbieder: {0}",
"ScheduledTaskFailedWithName": "{0} is mislukt",
"ScheduledTaskStartedWithName": "{0} is gestart",
- "ServerNameNeedsToBeRestarted": "{0} needs to be restarted",
+ "ServerNameNeedsToBeRestarted": "{0} moet herstart worden",
"Shows": "Series",
- "Songs": "Titels",
+ "Songs": "Nummers",
"StartupEmbyServerIsLoading": "Jellyfin Server is aan het laden, probeer het later opnieuw.",
"SubtitleDownloadFailureForItem": "Downloaden van ondertiteling voor {0} is mislukt",
- "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
+ "SubtitleDownloadFailureFromForItem": "Ondertitels konden niet gedownload worden van {0} voor {1}",
"SubtitlesDownloadedForItem": "Ondertiteling voor {0} is gedownload",
"Sync": "Synchronisatie",
"System": "Systeem",
@@ -89,9 +89,9 @@
"UserOnlineFromDevice": "{0} heeft verbinding met {1}",
"UserPasswordChangedWithName": "Wachtwoord voor {0} is gewijzigd",
"UserPolicyUpdatedWithName": "Gebruikersbeleid gewijzigd voor {0}",
- "UserStartedPlayingItemWithValues": "{0} heeft afspelen van {1} gestart",
- "UserStoppedPlayingItemWithValues": "{0} heeft afspelen van {1} gestopt",
- "ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
+ "UserStartedPlayingItemWithValues": "{0} heeft afspelen van {1} gestart op {2}",
+ "UserStoppedPlayingItemWithValues": "{0} heeft afspelen van {1} gestopt op {2}",
+ "ValueHasBeenAddedToLibrary": "{0} is toegevoegd aan je mediabibliotheek",
"ValueSpecialEpisodeName": "Speciaal - {0}",
"VersionNumber": "Versie {0}"
}
diff --git a/Emby.Server.Implementations/Localization/Core/ru.json b/Emby.Server.Implementations/Localization/Core/ru.json
index d799fa50b..c0465def8 100644
--- a/Emby.Server.Implementations/Localization/Core/ru.json
+++ b/Emby.Server.Implementations/Localization/Core/ru.json
@@ -5,7 +5,7 @@
"Artists": "Исполнители",
"AuthenticationSucceededWithUserName": "{0} - авторизация успешна",
"Books": "Литература",
- "CameraImageUploadedFrom": "Новое фото было выложено с {0}",
+ "CameraImageUploadedFrom": "Новое фото было выложено с камеры {0}",
"Channels": "Каналы",
"ChapterNameValue": "Сцена {0}",
"Collections": "Коллекции",
@@ -31,20 +31,20 @@
"ItemAddedWithName": "{0} - добавлено в медиатеку",
"ItemRemovedWithName": "{0} - изъято из медиатеки",
"LabelIpAddressValue": "IP-адрес: {0}",
- "LabelRunningTimeValue": "Время выполнения: {0}",
+ "LabelRunningTimeValue": "Длительность: {0}",
"Latest": "Новейшее",
"MessageApplicationUpdated": "Jellyfin Server был обновлён",
"MessageApplicationUpdatedTo": "Jellyfin Server был обновлён до {0}",
"MessageNamedServerConfigurationUpdatedWithValue": "Конфиг-ия сервера (раздел {0}) была обновлена",
"MessageServerConfigurationUpdated": "Конфиг-ия сервера была обновлена",
- "MixedContent": "Смешанное содержание",
+ "MixedContent": "Смешанное содержимое",
"Movies": "Кино",
"Music": "Музыка",
"MusicVideos": "Муз. видео",
"NameInstallFailed": "Установка {0} неудачна",
"NameSeasonNumber": "Сезон {0}",
"NameSeasonUnknown": "Сезон неопознан",
- "NewVersionIsAvailable": "Имеется новая версия Jellyfin Server",
+ "NewVersionIsAvailable": "Новая версия Jellyfin Server доступна для загрузки.",
"NotificationOptionApplicationUpdateAvailable": "Имеется обновление приложения",
"NotificationOptionApplicationUpdateInstalled": "Обновление приложения установлено",
"NotificationOptionAudioPlayback": "Воспр-ие аудио зап-но",
@@ -75,7 +75,7 @@
"Songs": "Композиции",
"StartupEmbyServerIsLoading": "Jellyfin Server загружается. Повторите попытку в ближайшее время.",
"SubtitleDownloadFailureForItem": "Субтитры к {0} не удалось загрузить",
- "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
+ "SubtitleDownloadFailureFromForItem": "Субтитры к {1} не удалось загрузить с {0}",
"SubtitlesDownloadedForItem": "Субтитры к {0} загружены",
"Sync": "Синхро",
"System": "Система",
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs
index 81fdb96d2..2f07ff15a 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs
@@ -21,7 +21,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
/// <summary>
/// Class ChapterImagesTask
/// </summary>
- class ChapterImagesTask : IScheduledTask
+ public class ChapterImagesTask : IScheduledTask
{
/// <summary>
/// The _logger
diff --git a/Emby.Server.Implementations/Serialization/JsonSerializer.cs b/Emby.Server.Implementations/Serialization/JsonSerializer.cs
index 44898d498..8ae7fd90c 100644
--- a/Emby.Server.Implementations/Serialization/JsonSerializer.cs
+++ b/Emby.Server.Implementations/Serialization/JsonSerializer.cs
@@ -42,6 +42,27 @@ namespace Emby.Server.Implementations.Serialization
}
/// <summary>
+ /// Serializes to stream.
+ /// </summary>
+ /// <param name="obj">The obj.</param>
+ /// <param name="stream">The stream.</param>
+ /// <exception cref="ArgumentNullException">obj</exception>
+ public void SerializeToStream<T>(T obj, Stream stream)
+ {
+ if (obj == null)
+ {
+ throw new ArgumentNullException(nameof(obj));
+ }
+
+ if (stream == null)
+ {
+ throw new ArgumentNullException(nameof(stream));
+ }
+
+ ServiceStack.Text.JsonSerializer.SerializeToStream<T>(obj, stream);
+ }
+
+ /// <summary>
/// Serializes to file.
/// </summary>
/// <param name="obj">The obj.</param>
diff --git a/Emby.Server.Implementations/Sorting/AiredEpisodeOrderComparer.cs b/Emby.Server.Implementations/Sorting/AiredEpisodeOrderComparer.cs
index 271188314..16507466f 100644
--- a/Emby.Server.Implementations/Sorting/AiredEpisodeOrderComparer.cs
+++ b/Emby.Server.Implementations/Sorting/AiredEpisodeOrderComparer.cs
@@ -6,7 +6,7 @@ using MediaBrowser.Model.Querying;
namespace Emby.Server.Implementations.Sorting
{
- class AiredEpisodeOrderComparer : IBaseItemComparer
+ public class AiredEpisodeOrderComparer : IBaseItemComparer
{
/// <summary>
/// Compares the specified x.
diff --git a/Emby.Server.Implementations/Sorting/SeriesSortNameComparer.cs b/Emby.Server.Implementations/Sorting/SeriesSortNameComparer.cs
index 942e84704..46e0dd918 100644
--- a/Emby.Server.Implementations/Sorting/SeriesSortNameComparer.cs
+++ b/Emby.Server.Implementations/Sorting/SeriesSortNameComparer.cs
@@ -5,7 +5,7 @@ using MediaBrowser.Model.Querying;
namespace Emby.Server.Implementations.Sorting
{
- class SeriesSortNameComparer : IBaseItemComparer
+ public class SeriesSortNameComparer : IBaseItemComparer
{
/// <summary>
/// Compares the specified x.
diff --git a/Emby.Server.Implementations/Updates/InstallationManager.cs b/Emby.Server.Implementations/Updates/InstallationManager.cs
index dc7f57f27..301802b8a 100644
--- a/Emby.Server.Implementations/Updates/InstallationManager.cs
+++ b/Emby.Server.Implementations/Updates/InstallationManager.cs
@@ -116,6 +116,7 @@ namespace Emby.Server.Implementations.Updates
private readonly IApplicationHost _applicationHost;
private readonly ICryptoProvider _cryptographyProvider;
+ private readonly IZipClient _zipClient;
// netframework or netcore
private readonly string _packageRuntime;
@@ -129,6 +130,7 @@ namespace Emby.Server.Implementations.Updates
IServerConfigurationManager config,
IFileSystem fileSystem,
ICryptoProvider cryptographyProvider,
+ IZipClient zipClient,
string packageRuntime)
{
if (loggerFactory == null)
@@ -146,6 +148,7 @@ namespace Emby.Server.Implementations.Updates
_config = config;
_fileSystem = fileSystem;
_cryptographyProvider = cryptographyProvider;
+ _zipClient = zipClient;
_packageRuntime = packageRuntime;
_logger = loggerFactory.CreateLogger(nameof(InstallationManager));
}
@@ -526,14 +529,18 @@ namespace Emby.Server.Implementations.Updates
private async Task PerformPackageInstallation(IProgress<double> progress, string target, PackageVersionInfo package, CancellationToken cancellationToken)
{
- // Target based on if it is an archive or single assembly
- // zip archives are assumed to contain directory structures relative to our ProgramDataPath
var extension = Path.GetExtension(package.targetFilename);
- var isArchive = string.Equals(extension, ".zip", StringComparison.OrdinalIgnoreCase) || string.Equals(extension, ".rar", StringComparison.OrdinalIgnoreCase) || string.Equals(extension, ".7z", StringComparison.OrdinalIgnoreCase);
+ var isArchive = string.Equals(extension, ".zip", StringComparison.OrdinalIgnoreCase);
+
+ if (!isArchive)
+ {
+ _logger.LogError("Only zip packages are supported. {Filename} is not a zip archive.", package.targetFilename);
+ return;
+ }
if (target == null)
{
- target = Path.Combine(isArchive ? _appPaths.TempUpdatePath : _appPaths.PluginsPath, package.targetFilename);
+ target = Path.Combine(_appPaths.PluginsPath, Path.GetFileNameWithoutExtension(package.targetFilename));
}
// Download to temporary file so that, if interrupted, it won't destroy the existing installation
@@ -547,36 +554,19 @@ namespace Emby.Server.Implementations.Updates
cancellationToken.ThrowIfCancellationRequested();
- // Validate with a checksum
- var packageChecksum = string.IsNullOrWhiteSpace(package.checksum) ? Guid.Empty : new Guid(package.checksum);
- if (!packageChecksum.Equals(Guid.Empty)) // support for legacy uploads for now
- {
- using (var stream = File.OpenRead(tempFile))
- {
- var check = Guid.Parse(BitConverter.ToString(_cryptographyProvider.ComputeMD5(stream)).Replace("-", string.Empty));
- if (check != packageChecksum)
- {
- throw new Exception(string.Format("Download validation failed for {0}. Probably corrupted during transfer.", package.name));
- }
- }
- }
-
- cancellationToken.ThrowIfCancellationRequested();
+ // TODO: Validate with a checksum, *properly*
// Success - move it to the real target
try
{
- Directory.CreateDirectory(Path.GetDirectoryName(target));
- File.Copy(tempFile, target, true);
- //If it is an archive - write out a version file so we know what it is
- if (isArchive)
+ using (var stream = File.OpenRead(tempFile))
{
- File.WriteAllText(target + ".ver", package.versionStr);
+ _zipClient.ExtractAllFromZip(stream, target, true);
}
}
catch (IOException ex)
{
- _logger.LogError(ex, "Error attempting to move file from {TempFile} to {TargetFile}", tempFile, target);
+ _logger.LogError(ex, "Error attempting to extract {TempFile} to {TargetFile}", tempFile, target);
throw;
}
diff --git a/Jellyfin.Drawing.Skia/SkiaEncoder.cs b/Jellyfin.Drawing.Skia/SkiaEncoder.cs
index dc714ed18..5060476ba 100644
--- a/Jellyfin.Drawing.Skia/SkiaEncoder.cs
+++ b/Jellyfin.Drawing.Skia/SkiaEncoder.cs
@@ -282,7 +282,7 @@ namespace Jellyfin.Drawing.Skia
var bitmap = new SKBitmap(codec.Info.Width, codec.Info.Height, !requiresTransparencyHack);
// decode
- var _ = codec.GetPixels(bitmap.Info, bitmap.GetPixels());
+ _ = codec.GetPixels(bitmap.Info, bitmap.GetPixels());
origin = codec.EncodedOrigin;
diff --git a/Jellyfin.Server/CoreAppHost.cs b/Jellyfin.Server/CoreAppHost.cs
index a486c2a47..84d78d3fb 100644
--- a/Jellyfin.Server/CoreAppHost.cs
+++ b/Jellyfin.Server/CoreAppHost.cs
@@ -5,19 +5,38 @@ using Emby.Server.Implementations.HttpServer;
using Jellyfin.Server.SocketSharp;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.System;
+using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
namespace Jellyfin.Server
{
public class CoreAppHost : ApplicationHost
{
- public CoreAppHost(ServerApplicationPaths applicationPaths, ILoggerFactory loggerFactory, StartupOptions options, IFileSystem fileSystem, IEnvironmentInfo environmentInfo, MediaBrowser.Controller.Drawing.IImageEncoder imageEncoder, MediaBrowser.Common.Net.INetworkManager networkManager)
- : base(applicationPaths, loggerFactory, options, fileSystem, environmentInfo, imageEncoder, networkManager)
+ public CoreAppHost(
+ ServerApplicationPaths applicationPaths,
+ ILoggerFactory loggerFactory,
+ StartupOptions options,
+ IFileSystem fileSystem,
+ IEnvironmentInfo environmentInfo,
+ MediaBrowser.Controller.Drawing.IImageEncoder imageEncoder,
+ MediaBrowser.Common.Net.INetworkManager networkManager,
+ IConfiguration configuration)
+ : base(
+ applicationPaths,
+ loggerFactory,
+ options,
+ fileSystem,
+ environmentInfo,
+ imageEncoder,
+ networkManager,
+ configuration)
{
}
public override bool CanSelfRestart => StartupOptions.RestartPath != null;
+ protected override bool SupportsDualModeSockets => true;
+
protected override void RestartInternal() => Program.Restart();
protected override IEnumerable<Assembly> GetAssembliesWithPartsInternal()
@@ -27,8 +46,6 @@ namespace Jellyfin.Server
protected override void ShutdownInternal() => Program.Shutdown();
- protected override bool SupportsDualModeSockets => true;
-
protected override IHttpListener CreateHttpListener()
=> new WebSocketSharpListener(
Logger,
@@ -39,7 +56,6 @@ namespace Jellyfin.Server
CryptographyProvider,
SupportsDualModeSockets,
FileSystemManager,
- EnvironmentInfo
- );
+ EnvironmentInfo);
}
}
diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj
index fe1397bcb..bd670df52 100644
--- a/Jellyfin.Server/Jellyfin.Server.csproj
+++ b/Jellyfin.Server/Jellyfin.Server.csproj
@@ -5,11 +5,14 @@
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.1</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
+ <GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
<PropertyGroup>
<!-- We need C# 7.1 for async main-->
<LangVersion>latest</LangVersion>
+ <!-- Disable documentation warnings (for now) -->
+ <NoWarn>SA1600;CS1591</NoWarn>
</PropertyGroup>
<ItemGroup>
diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs
index 2c177290e..41ee73a56 100644
--- a/Jellyfin.Server/Program.cs
+++ b/Jellyfin.Server/Program.cs
@@ -35,6 +35,7 @@ namespace Jellyfin.Server
private static readonly ILoggerFactory _loggerFactory = new SerilogLoggerFactory();
private static ILogger _logger;
private static bool _restartOnShutdown;
+ private static IConfiguration appConfig;
public static async Task Main(string[] args)
{
@@ -57,13 +58,32 @@ namespace Jellyfin.Server
errs => Task.FromResult(0)).ConfigureAwait(false);
}
+ public static void Shutdown()
+ {
+ if (!_tokenSource.IsCancellationRequested)
+ {
+ _tokenSource.Cancel();
+ }
+ }
+
+ public static void Restart()
+ {
+ _restartOnShutdown = true;
+
+ Shutdown();
+ }
+
private static async Task StartApp(StartupOptions options)
{
ServerApplicationPaths appPaths = CreateApplicationPaths(options);
// $JELLYFIN_LOG_DIR needs to be set for the logger configuration manager
Environment.SetEnvironmentVariable("JELLYFIN_LOG_DIR", appPaths.LogDirectoryPath);
- await CreateLogger(appPaths);
+
+ appConfig = await CreateConfiguration(appPaths).ConfigureAwait(false);
+
+ CreateLogger(appConfig, appPaths);
+
_logger = _loggerFactory.CreateLogger("Main");
AppDomain.CurrentDomain.UnhandledException += (sender, e)
@@ -76,6 +96,7 @@ namespace Jellyfin.Server
{
return; // Already shutting down
}
+
e.Cancel = true;
_logger.LogInformation("Ctrl+C, shutting down");
Environment.ExitCode = 128 + 2;
@@ -89,6 +110,7 @@ namespace Jellyfin.Server
{
return; // Already shutting down
}
+
_logger.LogInformation("Received a SIGTERM signal, shutting down");
Environment.ExitCode = 128 + 15;
Shutdown();
@@ -102,9 +124,9 @@ namespace Jellyfin.Server
SQLitePCL.Batteries_V2.Init();
// Allow all https requests
- ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(delegate { return true; });
+ ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(delegate { return true; } );
- var fileSystem = new ManagedFileSystem(_loggerFactory, environmentInfo, null, appPaths.TempDirectory, true);
+ var fileSystem = new ManagedFileSystem(_loggerFactory, environmentInfo, appPaths);
using (var appHost = new CoreAppHost(
appPaths,
@@ -113,20 +135,21 @@ namespace Jellyfin.Server
fileSystem,
environmentInfo,
new NullImageEncoder(),
- new NetworkManager(_loggerFactory, environmentInfo)))
+ new NetworkManager(_loggerFactory, environmentInfo),
+ appConfig))
{
- await appHost.Init(new ServiceCollection());
+ await appHost.Init(new ServiceCollection()).ConfigureAwait(false);
appHost.ImageProcessor.ImageEncoder = GetImageEncoder(fileSystem, appPaths, appHost.LocalizationManager);
- await appHost.RunStartupTasks();
+ await appHost.RunStartupTasks().ConfigureAwait(false);
// TODO: read input for a stop command
try
{
// Block main thread until shutdown
- await Task.Delay(-1, _tokenSource.Token);
+ await Task.Delay(-1, _tokenSource.Token).ConfigureAwait(false);
}
catch (TaskCanceledException)
{
@@ -146,7 +169,7 @@ namespace Jellyfin.Server
/// for everything else the XDG approach is followed:
/// https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
/// </summary>
- /// <param name="options"></param>
+ /// <param name="options">StartupOptions</param>
/// <returns>ServerApplicationPaths</returns>
private static ServerApplicationPaths CreateApplicationPaths(StartupOptions options)
{
@@ -164,26 +187,13 @@ namespace Jellyfin.Server
if (string.IsNullOrEmpty(dataDir))
{
- if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
- {
- dataDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
- }
- else
- {
- // $XDG_DATA_HOME defines the base directory relative to which user specific data files should be stored.
- dataDir = Environment.GetEnvironmentVariable("XDG_DATA_HOME");
-
- // If $XDG_DATA_HOME is either not set or empty, a default equal to $HOME/.local/share should be used.
- if (string.IsNullOrEmpty(dataDir))
- {
- dataDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".local", "share");
- }
- }
-
- dataDir = Path.Combine(dataDir, "jellyfin");
+ // LocalApplicationData follows the XDG spec on unix machines
+ dataDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "jellyfin");
}
}
+ Directory.CreateDirectory(dataDir);
+
// configDir
// IF --configdir
// ELSE IF $JELLYFIN_CONFIG_DIR
@@ -193,7 +203,6 @@ namespace Jellyfin.Server
// ELSE IF $XDG_CONFIG_HOME use $XDG_CONFIG_HOME/jellyfin
// ELSE $HOME/.config/jellyfin
var configDir = options.ConfigDir;
-
if (string.IsNullOrEmpty(configDir))
{
configDir = Environment.GetEnvironmentVariable("JELLYFIN_CONFIG_DIR");
@@ -277,7 +286,6 @@ namespace Jellyfin.Server
// Ensure the main folders exist before we continue
try
{
- Directory.CreateDirectory(dataDir);
Directory.CreateDirectory(logDir);
Directory.CreateDirectory(configDir);
Directory.CreateDirectory(cacheDir);
@@ -292,28 +300,33 @@ namespace Jellyfin.Server
return new ServerApplicationPaths(dataDir, logDir, configDir, cacheDir);
}
- private static async Task CreateLogger(IApplicationPaths appPaths)
+ private static async Task<IConfiguration> CreateConfiguration(IApplicationPaths appPaths)
{
- try
- {
- string configPath = Path.Combine(appPaths.ConfigurationDirectoryPath, "logging.json");
+ string configPath = Path.Combine(appPaths.ConfigurationDirectoryPath, "logging.json");
- if (!File.Exists(configPath))
+ if (!File.Exists(configPath))
+ {
+ // For some reason the csproj name is used instead of the assembly name
+ using (Stream rscstr = typeof(Program).Assembly
+ .GetManifestResourceStream("Jellyfin.Server.Resources.Configuration.logging.json"))
+ using (Stream fstr = File.Open(configPath, FileMode.CreateNew))
{
- // For some reason the csproj name is used instead of the assembly name
- using (Stream rscstr = typeof(Program).Assembly
- .GetManifestResourceStream("Jellyfin.Server.Resources.Configuration.logging.json"))
- using (Stream fstr = File.Open(configPath, FileMode.CreateNew))
- {
- await rscstr.CopyToAsync(fstr).ConfigureAwait(false);
- }
+ await rscstr.CopyToAsync(fstr).ConfigureAwait(false);
}
- var configuration = new ConfigurationBuilder()
- .SetBasePath(appPaths.ConfigurationDirectoryPath)
- .AddJsonFile("logging.json")
- .AddEnvironmentVariables("JELLYFIN_")
- .Build();
+ }
+ return new ConfigurationBuilder()
+ .SetBasePath(appPaths.ConfigurationDirectoryPath)
+ .AddJsonFile("logging.json")
+ .AddEnvironmentVariables("JELLYFIN_")
+ .AddInMemoryCollection(ConfigurationOptions.Configuration)
+ .Build();
+ }
+
+ private static void CreateLogger(IConfiguration configuration, IApplicationPaths appPaths)
+ {
+ try
+ {
// Serilog.Log is used by SerilogLoggerFactory when no logger is specified
Serilog.Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(configuration)
@@ -335,7 +348,7 @@ namespace Jellyfin.Server
}
}
- public static IImageEncoder GetImageEncoder(
+ private static IImageEncoder GetImageEncoder(
IFileSystem fileSystem,
IApplicationPaths appPaths,
ILocalizationManager localizationManager)
@@ -376,26 +389,12 @@ namespace Jellyfin.Server
{
return MediaBrowser.Model.System.OperatingSystem.BSD;
}
+
throw new Exception($"Can't resolve OS with description: '{osDescription}'");
}
}
}
- public static void Shutdown()
- {
- if (!_tokenSource.IsCancellationRequested)
- {
- _tokenSource.Cancel();
- }
- }
-
- public static void Restart()
- {
- _restartOnShutdown = true;
-
- Shutdown();
- }
-
private static void StartNewInstance(StartupOptions options)
{
_logger.LogInformation("Starting new instance");
diff --git a/Jellyfin.Server/SocketSharp/HttpFile.cs b/Jellyfin.Server/SocketSharp/HttpFile.cs
index 89c75e536..448b666b6 100644
--- a/Jellyfin.Server/SocketSharp/HttpFile.cs
+++ b/Jellyfin.Server/SocketSharp/HttpFile.cs
@@ -6,9 +6,13 @@ namespace Jellyfin.Server.SocketSharp
public class HttpFile : IHttpFile
{
public string Name { get; set; }
+
public string FileName { get; set; }
+
public long ContentLength { get; set; }
+
public string ContentType { get; set; }
+
public Stream InputStream { get; set; }
}
}
diff --git a/Jellyfin.Server/SocketSharp/HttpPostedFile.cs b/Jellyfin.Server/SocketSharp/HttpPostedFile.cs
new file mode 100644
index 000000000..f38ed848e
--- /dev/null
+++ b/Jellyfin.Server/SocketSharp/HttpPostedFile.cs
@@ -0,0 +1,204 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Net;
+using System.Text;
+using System.Threading.Tasks;
+using MediaBrowser.Model.Services;
+
+public sealed class HttpPostedFile : IDisposable
+{
+ private string _name;
+ private string _contentType;
+ private Stream _stream;
+ private bool _disposed = false;
+
+ internal HttpPostedFile(string name, string content_type, Stream base_stream, long offset, long length)
+ {
+ _name = name;
+ _contentType = content_type;
+ _stream = new ReadSubStream(base_stream, offset, length);
+ }
+
+ public string ContentType => _contentType;
+
+ public int ContentLength => (int)_stream.Length;
+
+ public string FileName => _name;
+
+ public Stream InputStream => _stream;
+
+ /// <summary>
+ /// Releases the unmanaged resources and disposes of the managed resources used.
+ /// </summary>
+ public void Dispose()
+ {
+ if (_disposed)
+ {
+ return;
+ }
+
+ _stream.Dispose();
+ _stream = null;
+
+ _name = null;
+ _contentType = null;
+
+ _disposed = true;
+ }
+
+ private class ReadSubStream : Stream
+ {
+ private Stream _stream;
+ private long _offset;
+ private long _end;
+ private long _position;
+
+ public ReadSubStream(Stream s, long offset, long length)
+ {
+ _stream = s;
+ _offset = offset;
+ _end = offset + length;
+ _position = offset;
+ }
+
+ public override void Flush()
+ {
+ }
+
+ public override int Read(byte[] buffer, int dest_offset, int count)
+ {
+ if (buffer == null)
+ {
+ throw new ArgumentNullException(nameof(buffer));
+ }
+
+ if (dest_offset < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(dest_offset), "< 0");
+ }
+
+ if (count < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(count), "< 0");
+ }
+
+ int len = buffer.Length;
+ if (dest_offset > len)
+ {
+ throw new ArgumentException("destination offset is beyond array size", nameof(dest_offset));
+ }
+
+ // reordered to avoid possible integer overflow
+ if (dest_offset > len - count)
+ {
+ throw new ArgumentException("Reading would overrun buffer", nameof(count));
+ }
+
+ if (count > _end - _position)
+ {
+ count = (int)(_end - _position);
+ }
+
+ if (count <= 0)
+ {
+ return 0;
+ }
+
+ _stream.Position = _position;
+ int result = _stream.Read(buffer, dest_offset, count);
+ if (result > 0)
+ {
+ _position += result;
+ }
+ else
+ {
+ _position = _end;
+ }
+
+ return result;
+ }
+
+ public override int ReadByte()
+ {
+ if (_position >= _end)
+ {
+ return -1;
+ }
+
+ _stream.Position = _position;
+ int result = _stream.ReadByte();
+ if (result < 0)
+ {
+ _position = _end;
+ }
+ else
+ {
+ _position++;
+ }
+
+ return result;
+ }
+
+ public override long Seek(long d, SeekOrigin origin)
+ {
+ long real;
+ switch (origin)
+ {
+ case SeekOrigin.Begin:
+ real = _offset + d;
+ break;
+ case SeekOrigin.End:
+ real = _end + d;
+ break;
+ case SeekOrigin.Current:
+ real = _position + d;
+ break;
+ default:
+ throw new ArgumentException("Unknown SeekOrigin value", nameof(origin));
+ }
+
+ long virt = real - _offset;
+ if (virt < 0 || virt > Length)
+ {
+ throw new ArgumentException("Invalid position", nameof(d));
+ }
+
+ _position = _stream.Seek(real, SeekOrigin.Begin);
+ return _position;
+ }
+
+ public override void SetLength(long value)
+ {
+ throw new NotSupportedException();
+ }
+
+ public override void Write(byte[] buffer, int offset, int count)
+ {
+ throw new NotSupportedException();
+ }
+
+ public override bool CanRead => true;
+
+ public override bool CanSeek => true;
+
+ public override bool CanWrite => false;
+
+ public override long Length => _end - _offset;
+
+ public override long Position
+ {
+ get => _position - _offset;
+ set
+ {
+ if (value > Length)
+ {
+ throw new ArgumentOutOfRangeException(nameof(value));
+ }
+
+ _position = Seek(value, SeekOrigin.Begin);
+ }
+ }
+ }
+}
diff --git a/Jellyfin.Server/SocketSharp/RequestMono.cs b/Jellyfin.Server/SocketSharp/RequestMono.cs
index a8ba4cdb5..f2a08c9ae 100644
--- a/Jellyfin.Server/SocketSharp/RequestMono.cs
+++ b/Jellyfin.Server/SocketSharp/RequestMono.cs
@@ -13,7 +13,7 @@ namespace Jellyfin.Server.SocketSharp
{
internal static string GetParameter(string header, string attr)
{
- int ap = header.IndexOf(attr);
+ int ap = header.IndexOf(attr, StringComparison.Ordinal);
if (ap == -1)
{
return null;
@@ -82,9 +82,7 @@ namespace Jellyfin.Server.SocketSharp
}
else
{
- //
// We use a substream, as in 2.x we will support large uploads streamed to disk,
- //
var sub = new HttpPostedFile(e.Filename, e.ContentType, input, e.Start, e.Length);
files[e.Name] = sub;
}
@@ -127,8 +125,12 @@ namespace Jellyfin.Server.SocketSharp
public string Authorization => string.IsNullOrEmpty(request.Headers["Authorization"]) ? null : request.Headers["Authorization"];
- protected bool validate_cookies, validate_query_string, validate_form;
- protected bool checked_cookies, checked_query_string, checked_form;
+ protected bool validate_cookies { get; set; }
+ protected bool validate_query_string { get; set; }
+ protected bool validate_form { get; set; }
+ protected bool checked_cookies { get; set; }
+ protected bool checked_query_string { get; set; }
+ protected bool checked_form { get; set; }
private static void ThrowValidationException(string name, string key, string value)
{
@@ -138,8 +140,12 @@ namespace Jellyfin.Server.SocketSharp
v = v.Substring(0, 16) + "...\"";
}
- string msg = string.Format("A potentially dangerous Request.{0} value was " +
- "detected from the client ({1}={2}).", name, key, v);
+ string msg = string.Format(
+ CultureInfo.InvariantCulture,
+ "A potentially dangerous Request.{0} value was detected from the client ({1}={2}).",
+ name,
+ key,
+ v);
throw new Exception(msg);
}
@@ -179,6 +185,7 @@ namespace Jellyfin.Server.SocketSharp
for (int idx = 1; idx < len; idx++)
{
char next = val[idx];
+
// See http://secunia.com/advisories/14325
if (current == '<' || current == '\xff1c')
{
@@ -218,7 +225,7 @@ namespace Jellyfin.Server.SocketSharp
if (starts_with)
{
- return StrUtils.StartsWith(ContentType, ct, true);
+ return ContentType.StartsWith(ct, StringComparison.OrdinalIgnoreCase);
}
return string.Equals(ContentType, ct, StringComparison.OrdinalIgnoreCase);
@@ -256,6 +263,7 @@ namespace Jellyfin.Server.SocketSharp
value.Append((char)c);
}
}
+
if (c == -1)
{
AddRawKeyValue(form, key, value);
@@ -271,6 +279,7 @@ namespace Jellyfin.Server.SocketSharp
key.Append((char)c);
}
}
+
if (c == -1)
{
AddRawKeyValue(form, key, value);
@@ -308,254 +317,54 @@ namespace Jellyfin.Server.SocketSharp
result.Append(key);
result.Append('=');
}
+
result.Append(pair.Value);
}
return result.ToString();
}
}
-
- public sealed class HttpPostedFile
+ private class HttpMultipart
{
- private string name;
- private string content_type;
- private Stream stream;
- private class ReadSubStream : Stream
+ public class Element
{
- private Stream s;
- private long offset;
- private long end;
- private long position;
-
- public ReadSubStream(Stream s, long offset, long length)
- {
- this.s = s;
- this.offset = offset;
- this.end = offset + length;
- position = offset;
- }
-
- public override void Flush()
- {
- }
-
- public override int Read(byte[] buffer, int dest_offset, int count)
- {
- if (buffer == null)
- {
- throw new ArgumentNullException(nameof(buffer));
- }
-
- if (dest_offset < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(dest_offset), "< 0");
- }
-
- if (count < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(count), "< 0");
- }
-
- int len = buffer.Length;
- if (dest_offset > len)
- {
- throw new ArgumentException("destination offset is beyond array size", nameof(dest_offset));
- }
-
- // reordered to avoid possible integer overflow
- if (dest_offset > len - count)
- {
- throw new ArgumentException("Reading would overrun buffer", nameof(count));
- }
-
- if (count > end - position)
- {
- count = (int)(end - position);
- }
-
- if (count <= 0)
- {
- return 0;
- }
-
- s.Position = position;
- int result = s.Read(buffer, dest_offset, count);
- if (result > 0)
- {
- position += result;
- }
- else
- {
- position = end;
- }
-
- return result;
- }
-
- public override int ReadByte()
- {
- if (position >= end)
- {
- return -1;
- }
-
- s.Position = position;
- int result = s.ReadByte();
- if (result < 0)
- {
- position = end;
- }
- else
- {
- position++;
- }
-
- return result;
- }
-
- public override long Seek(long d, SeekOrigin origin)
- {
- long real;
- switch (origin)
- {
- case SeekOrigin.Begin:
- real = offset + d;
- break;
- case SeekOrigin.End:
- real = end + d;
- break;
- case SeekOrigin.Current:
- real = position + d;
- break;
- default:
- throw new ArgumentException(nameof(origin));
- }
-
- long virt = real - offset;
- if (virt < 0 || virt > Length)
- {
- throw new ArgumentException();
- }
-
- position = s.Seek(real, SeekOrigin.Begin);
- return position;
- }
+ public string ContentType { get; set; }
- public override void SetLength(long value)
- {
- throw new NotSupportedException();
- }
-
- public override void Write(byte[] buffer, int offset, int count)
- {
- throw new NotSupportedException();
- }
+ public string Name { get; set; }
- public override bool CanRead => true;
+ public string Filename { get; set; }
- public override bool CanSeek => true;
+ public Encoding Encoding { get; set; }
- public override bool CanWrite => false;
+ public long Start { get; set; }
- public override long Length => end - offset;
+ public long Length { get; set; }
- public override long Position
+ public override string ToString()
{
- get => position - offset;
- set
- {
- if (value > Length)
- {
- throw new ArgumentOutOfRangeException(nameof(value));
- }
-
- position = Seek(value, SeekOrigin.Begin);
- }
+ return "ContentType " + ContentType + ", Name " + Name + ", Filename " + Filename + ", Start " +
+ Start.ToString(CultureInfo.CurrentCulture) + ", Length " + Length.ToString(CultureInfo.CurrentCulture);
}
}
- internal HttpPostedFile(string name, string content_type, Stream base_stream, long offset, long length)
- {
- this.name = name;
- this.content_type = content_type;
- this.stream = new ReadSubStream(base_stream, offset, length);
- }
-
- public string ContentType => content_type;
-
- public int ContentLength => (int)stream.Length;
-
- public string FileName => name;
+ private const byte LF = (byte)'\n';
- public Stream InputStream => stream;
- }
-
- private class Helpers
- {
- public static readonly CultureInfo InvariantCulture = CultureInfo.InvariantCulture;
- }
-
- internal static class StrUtils
- {
- public static bool StartsWith(string str1, string str2, bool ignore_case)
- {
- if (string.IsNullOrEmpty(str1))
- {
- return false;
- }
-
- var comparison = ignore_case ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal;
- return str1.IndexOf(str2, comparison) == 0;
- }
+ private const byte CR = (byte)'\r';
- public static bool EndsWith(string str1, string str2, bool ignore_case)
- {
- int l2 = str2.Length;
- if (l2 == 0)
- {
- return true;
- }
-
- int l1 = str1.Length;
- if (l2 > l1)
- {
- return false;
- }
+ private Stream data;
- var comparison = ignore_case ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal;
- return str1.IndexOf(str2, comparison) == str1.Length - str2.Length - 1;
- }
- }
+ private string boundary;
- private class HttpMultipart
- {
+ private byte[] boundaryBytes;
- public class Element
- {
- public string ContentType;
- public string Name;
- public string Filename;
- public Encoding Encoding;
- public long Start;
- public long Length;
+ private byte[] buffer;
- public override string ToString()
- {
- return "ContentType " + ContentType + ", Name " + Name + ", Filename " + Filename + ", Start " +
- Start.ToString(CultureInfo.CurrentCulture) + ", Length " + Length.ToString(CultureInfo.CurrentCulture);
- }
- }
+ private bool atEof;
- private Stream data;
- private string boundary;
- private byte[] boundary_bytes;
- private byte[] buffer;
- private bool at_eof;
private Encoding encoding;
- private StringBuilder sb;
- private const byte LF = (byte)'\n', CR = (byte)'\r';
+ private StringBuilder sb;
// See RFC 2046
// In the case of multipart entities, in which one or more different
@@ -570,18 +379,48 @@ namespace Jellyfin.Server.SocketSharp
public HttpMultipart(Stream data, string b, Encoding encoding)
{
this.data = data;
- //DB: 30/01/11: cannot set or read the Position in HttpListener in Win.NET
- //var ms = new MemoryStream(32 * 1024);
- //data.CopyTo(ms);
- //this.data = ms;
-
boundary = b;
- boundary_bytes = encoding.GetBytes(b);
- buffer = new byte[boundary_bytes.Length + 2]; // CRLF or '--'
+ boundaryBytes = encoding.GetBytes(b);
+ buffer = new byte[boundaryBytes.Length + 2]; // CRLF or '--'
this.encoding = encoding;
sb = new StringBuilder();
}
+ public Element ReadNextElement()
+ {
+ if (atEof || ReadBoundary())
+ {
+ return null;
+ }
+
+ var elem = new Element();
+ string header;
+ while ((header = ReadHeaders()) != null)
+ {
+ if (header.StartsWith("Content-Disposition:", StringComparison.OrdinalIgnoreCase))
+ {
+ elem.Name = GetContentDispositionAttribute(header, "name");
+ elem.Filename = StripPath(GetContentDispositionAttributeWithEncoding(header, "filename"));
+ }
+ else if (header.StartsWith("Content-Type:", StringComparison.OrdinalIgnoreCase))
+ {
+ elem.ContentType = header.Substring("Content-Type:".Length).Trim();
+ elem.Encoding = GetEncoding(elem.ContentType);
+ }
+ }
+
+ long start = data.Position;
+ elem.Start = start;
+ long pos = MoveToNextBoundary();
+ if (pos == -1)
+ {
+ return null;
+ }
+
+ elem.Length = pos - start;
+ return elem;
+ }
+
private string ReadLine()
{
// CRLF or LF are ok as line endings.
@@ -600,6 +439,7 @@ namespace Jellyfin.Server.SocketSharp
{
break;
}
+
got_cr = b == CR;
sb.Append((char)b);
}
@@ -681,13 +521,14 @@ namespace Jellyfin.Server.SocketSharp
return false;
}
- if (!StrUtils.EndsWith(line, boundary, false))
+ if (!line.EndsWith(boundary, StringComparison.Ordinal))
{
return true;
}
}
catch
{
+
}
return false;
@@ -769,7 +610,7 @@ namespace Jellyfin.Server.SocketSharp
return -1;
}
- if (!CompareBytes(boundary_bytes, buffer))
+ if (!CompareBytes(boundaryBytes, buffer))
{
state = 0;
data.Position = retval + 2;
@@ -785,7 +626,7 @@ namespace Jellyfin.Server.SocketSharp
if (buffer[bl - 2] == '-' && buffer[bl - 1] == '-')
{
- at_eof = true;
+ atEof = true;
}
else if (buffer[bl - 2] != CR || buffer[bl - 1] != LF)
{
@@ -800,6 +641,7 @@ namespace Jellyfin.Server.SocketSharp
c = data.ReadByte();
continue;
}
+
data.Position = retval + 2;
if (got_cr)
{
@@ -818,42 +660,6 @@ namespace Jellyfin.Server.SocketSharp
return retval;
}
- public Element ReadNextElement()
- {
- if (at_eof || ReadBoundary())
- {
- return null;
- }
-
- var elem = new Element();
- string header;
- while ((header = ReadHeaders()) != null)
- {
- if (StrUtils.StartsWith(header, "Content-Disposition:", true))
- {
- elem.Name = GetContentDispositionAttribute(header, "name");
- elem.Filename = StripPath(GetContentDispositionAttributeWithEncoding(header, "filename"));
- }
- else if (StrUtils.StartsWith(header, "Content-Type:", true))
- {
- elem.ContentType = header.Substring("Content-Type:".Length).Trim();
- elem.Encoding = GetEncoding(elem.ContentType);
- }
- }
-
- long start = 0;
- start = data.Position;
- elem.Start = start;
- long pos = MoveToNextBoundary();
- if (pos == -1)
- {
- return null;
- }
-
- elem.Length = pos - start;
- return elem;
- }
-
private static string StripPath(string path)
{
if (path == null || path.Length == 0)
diff --git a/Jellyfin.Server/SocketSharp/SharpWebSocket.cs b/Jellyfin.Server/SocketSharp/SharpWebSocket.cs
index f371cb25a..6eee4cd12 100644
--- a/Jellyfin.Server/SocketSharp/SharpWebSocket.cs
+++ b/Jellyfin.Server/SocketSharp/SharpWebSocket.cs
@@ -24,6 +24,7 @@ namespace Jellyfin.Server.SocketSharp
private TaskCompletionSource<bool> _taskCompletionSource = new TaskCompletionSource<bool>();
private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
+ private bool _disposed = false;
public SharpWebSocket(SocketHttpListener.WebSocket socket, ILogger logger)
{
@@ -40,9 +41,9 @@ namespace Jellyfin.Server.SocketSharp
_logger = logger;
WebSocket = socket;
- socket.OnMessage += socket_OnMessage;
- socket.OnClose += socket_OnClose;
- socket.OnError += socket_OnError;
+ socket.OnMessage += OnSocketMessage;
+ socket.OnClose += OnSocketClose;
+ socket.OnError += OnSocketError;
WebSocket.ConnectAsServer();
}
@@ -52,29 +53,22 @@ namespace Jellyfin.Server.SocketSharp
return _taskCompletionSource.Task;
}
- void socket_OnError(object sender, SocketHttpListener.ErrorEventArgs e)
+ private void OnSocketError(object sender, SocketHttpListener.ErrorEventArgs e)
{
_logger.LogError("Error in SharpWebSocket: {Message}", e.Message ?? string.Empty);
- //Closed?.Invoke(this, EventArgs.Empty);
+
+ // Closed?.Invoke(this, EventArgs.Empty);
}
- void socket_OnClose(object sender, SocketHttpListener.CloseEventArgs e)
+ private void OnSocketClose(object sender, SocketHttpListener.CloseEventArgs e)
{
_taskCompletionSource.TrySetResult(true);
Closed?.Invoke(this, EventArgs.Empty);
}
- void socket_OnMessage(object sender, SocketHttpListener.MessageEventArgs e)
+ private void OnSocketMessage(object sender, SocketHttpListener.MessageEventArgs e)
{
- //if (!string.IsNullOrEmpty(e.Data))
- //{
- // if (OnReceive != null)
- // {
- // OnReceive(e.Data);
- // }
- // return;
- //}
if (OnReceiveBytes != null)
{
OnReceiveBytes(e.RawData);
@@ -117,6 +111,7 @@ namespace Jellyfin.Server.SocketSharp
public void Dispose()
{
Dispose(true);
+ GC.SuppressFinalize(this);
}
/// <summary>
@@ -125,16 +120,23 @@ namespace Jellyfin.Server.SocketSharp
/// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
protected virtual void Dispose(bool dispose)
{
+ if (_disposed)
+ {
+ return;
+ }
+
if (dispose)
{
- WebSocket.OnMessage -= socket_OnMessage;
- WebSocket.OnClose -= socket_OnClose;
- WebSocket.OnError -= socket_OnError;
+ WebSocket.OnMessage -= OnSocketMessage;
+ WebSocket.OnClose -= OnSocketClose;
+ WebSocket.OnError -= OnSocketError;
_cancellationTokenSource.Cancel();
WebSocket.Close();
}
+
+ _disposed = true;
}
/// <summary>
@@ -142,11 +144,5 @@ namespace Jellyfin.Server.SocketSharp
/// </summary>
/// <value>The receive action.</value>
public Action<byte[]> OnReceiveBytes { get; set; }
-
- /// <summary>
- /// Gets or sets the on receive.
- /// </summary>
- /// <value>The on receive.</value>
- public Action<string> OnReceive { get; set; }
}
}
diff --git a/Jellyfin.Server/SocketSharp/WebSocketSharpListener.cs b/Jellyfin.Server/SocketSharp/WebSocketSharpListener.cs
index a44343ab2..58c4d38a2 100644
--- a/Jellyfin.Server/SocketSharp/WebSocketSharpListener.cs
+++ b/Jellyfin.Server/SocketSharp/WebSocketSharpListener.cs
@@ -34,9 +34,16 @@ namespace Jellyfin.Server.SocketSharp
private CancellationTokenSource _disposeCancellationTokenSource = new CancellationTokenSource();
private CancellationToken _disposeCancellationToken;
- public WebSocketSharpListener(ILogger logger, X509Certificate certificate, IStreamHelper streamHelper,
- INetworkManager networkManager, ISocketFactory socketFactory, ICryptoProvider cryptoProvider,
- bool enableDualMode, IFileSystem fileSystem, IEnvironmentInfo environment)
+ public WebSocketSharpListener(
+ ILogger logger,
+ X509Certificate certificate,
+ IStreamHelper streamHelper,
+ INetworkManager networkManager,
+ ISocketFactory socketFactory,
+ ICryptoProvider cryptoProvider,
+ bool enableDualMode,
+ IFileSystem fileSystem,
+ IEnvironmentInfo environment)
{
_logger = logger;
_certificate = certificate;
@@ -61,7 +68,9 @@ namespace Jellyfin.Server.SocketSharp
public void Start(IEnumerable<string> urlPrefixes)
{
if (_listener == null)
+ {
_listener = new HttpListener(_logger, _cryptoProvider, _socketFactory, _networkManager, _streamHelper, _fileSystem, _environment);
+ }
_listener.EnableDualMode = _enableDualMode;
@@ -83,15 +92,18 @@ namespace Jellyfin.Server.SocketSharp
private void ProcessContext(HttpListenerContext context)
{
- var _ = Task.Run(async () => await InitTask(context, _disposeCancellationToken));
+ _ = Task.Run(async () => await InitTask(context, _disposeCancellationToken).ConfigureAwait(false));
}
private static void LogRequest(ILogger logger, HttpListenerRequest request)
{
var url = request.Url.ToString();
- logger.LogInformation("{0} {1}. UserAgent: {2}",
- request.IsWebSocketRequest ? "WS" : "HTTP " + request.HttpMethod, url, request.UserAgent ?? string.Empty);
+ logger.LogInformation(
+ "{0} {1}. UserAgent: {2}",
+ request.IsWebSocketRequest ? "WS" : "HTTP " + request.HttpMethod,
+ url,
+ request.UserAgent ?? string.Empty);
}
private Task InitTask(HttpListenerContext context, CancellationToken cancellationToken)
@@ -201,7 +213,7 @@ namespace Jellyfin.Server.SocketSharp
}
catch (ObjectDisposedException)
{
- //TODO Investigate and properly fix.
+ // TODO: Investigate and properly fix.
}
catch (Exception ex)
{
@@ -223,38 +235,39 @@ namespace Jellyfin.Server.SocketSharp
public Task Stop()
{
_disposeCancellationTokenSource.Cancel();
-
- if (_listener != null)
- {
- _listener.Close();
- }
+ _listener?.Close();
return Task.CompletedTask;
}
+ /// <summary>
+ /// Releases the unmanaged resources and disposes of the managed resources used.
+ /// </summary>
public void Dispose()
{
Dispose(true);
+ GC.SuppressFinalize(this);
}
private bool _disposed;
- private readonly object _disposeLock = new object();
+
+ /// <summary>
+ /// Releases the unmanaged resources and disposes of the managed resources used.
+ /// </summary>
+ /// <param name="disposing">Whether or not the managed resources should be disposed</param>
protected virtual void Dispose(bool disposing)
{
- if (_disposed) return;
-
- lock (_disposeLock)
+ if (_disposed)
{
- if (_disposed) return;
-
- if (disposing)
- {
- Stop();
- }
+ return;
+ }
- //release unmanaged resources here...
- _disposed = true;
+ if (disposing)
+ {
+ Stop().GetAwaiter().GetResult();
}
+
+ _disposed = true;
}
}
}
diff --git a/Jellyfin.Server/SocketSharp/WebSocketSharpRequest.cs b/Jellyfin.Server/SocketSharp/WebSocketSharpRequest.cs
index ebeb18ea0..6458707d9 100644
--- a/Jellyfin.Server/SocketSharp/WebSocketSharpRequest.cs
+++ b/Jellyfin.Server/SocketSharp/WebSocketSharpRequest.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Globalization;
using System.IO;
using System.Text;
using Emby.Server.Implementations.HttpServer;
@@ -24,31 +25,7 @@ namespace Jellyfin.Server.SocketSharp
this.request = httpContext.Request;
this.response = new WebSocketSharpResponse(logger, httpContext.Response, this);
- //HandlerFactoryPath = GetHandlerPathIfAny(UrlPrefixes[0]);
- }
-
- private static string GetHandlerPathIfAny(string listenerUrl)
- {
- if (listenerUrl == null)
- {
- return null;
- }
-
- var pos = listenerUrl.IndexOf("://", StringComparison.OrdinalIgnoreCase);
- if (pos == -1)
- {
- return null;
- }
-
- var startHostUrl = listenerUrl.Substring(pos + "://".Length);
- var endPos = startHostUrl.IndexOf('/');
- if (endPos == -1)
- {
- return null;
- }
-
- var endHostUrl = startHostUrl.Substring(endPos + 1);
- return string.IsNullOrEmpty(endHostUrl) ? null : endHostUrl.TrimEnd('/');
+ // HandlerFactoryPath = GetHandlerPathIfAny(UrlPrefixes[0]);
}
public HttpListenerRequest HttpRequest => request;
@@ -69,9 +46,11 @@ namespace Jellyfin.Server.SocketSharp
public string UserHostAddress => request.UserHostAddress;
- public string XForwardedFor => string.IsNullOrEmpty(request.Headers["X-Forwarded-For"]) ? null : request.Headers["X-Forwarded-For"];
+ public string XForwardedFor
+ => string.IsNullOrEmpty(request.Headers["X-Forwarded-For"]) ? null : request.Headers["X-Forwarded-For"];
- public int? XForwardedPort => string.IsNullOrEmpty(request.Headers["X-Forwarded-Port"]) ? (int?)null : int.Parse(request.Headers["X-Forwarded-Port"]);
+ public int? XForwardedPort
+ => string.IsNullOrEmpty(request.Headers["X-Forwarded-Port"]) ? (int?)null : int.Parse(request.Headers["X-Forwarded-Port"], CultureInfo.InvariantCulture);
public string XForwardedProtocol => string.IsNullOrEmpty(request.Headers["X-Forwarded-Proto"]) ? null : request.Headers["X-Forwarded-Proto"];
@@ -99,7 +78,7 @@ namespace Jellyfin.Server.SocketSharp
name = name.Trim(HttpTrimCharacters);
// First, check for correctly formed multi-line value
- // Second, check for absenece of CTL characters
+ // Second, check for absence of CTL characters
int crlf = 0;
for (int i = 0; i < name.Length; ++i)
{
@@ -107,6 +86,7 @@ namespace Jellyfin.Server.SocketSharp
switch (crlf)
{
case 0:
+ {
if (c == '\r')
{
crlf = 1;
@@ -121,29 +101,39 @@ namespace Jellyfin.Server.SocketSharp
{
throw new ArgumentException("net_WebHeaderInvalidControlChars");
}
+
break;
+ }
case 1:
+ {
if (c == '\n')
{
crlf = 2;
break;
}
+
throw new ArgumentException("net_WebHeaderInvalidCRLFChars");
+ }
case 2:
+ {
if (c == ' ' || c == '\t')
{
crlf = 0;
break;
}
+
throw new ArgumentException("net_WebHeaderInvalidCRLFChars");
+ }
}
}
+
if (crlf != 0)
{
throw new ArgumentException("net_WebHeaderInvalidCRLFChars");
}
+
return name;
}
@@ -156,6 +146,7 @@ namespace Jellyfin.Server.SocketSharp
return true;
}
}
+
return false;
}
@@ -216,8 +207,15 @@ namespace Jellyfin.Server.SocketSharp
{
foreach (var acceptsType in acceptContentTypes)
{
- var contentType = HttpResultFactory.GetRealContentType(acceptsType);
- acceptsAnything = acceptsAnything || contentType == "*/*";
+ // TODO: @bond move to Span when Span.Split lands
+ // https://github.com/dotnet/corefx/issues/26528
+ var contentType = acceptsType?.Split(';')[0].Trim();
+ acceptsAnything = contentType.Equals("*/*", StringComparison.OrdinalIgnoreCase);
+
+ if (acceptsAnything)
+ {
+ break;
+ }
}
if (acceptsAnything)
@@ -226,7 +224,7 @@ namespace Jellyfin.Server.SocketSharp
{
return defaultContentType;
}
- else if (serverDefaultContentType != null)
+ else
{
return serverDefaultContentType;
}
@@ -269,11 +267,11 @@ namespace Jellyfin.Server.SocketSharp
private static string GetQueryStringContentType(IRequest httpReq)
{
- var format = httpReq.QueryString["format"];
+ ReadOnlySpan<char> format = httpReq.QueryString["format"];
if (format == null)
{
const int formatMaxLength = 4;
- var pi = httpReq.PathInfo;
+ ReadOnlySpan<char> pi = httpReq.PathInfo;
if (pi == null || pi.Length <= formatMaxLength)
{
return null;
@@ -281,7 +279,7 @@ namespace Jellyfin.Server.SocketSharp
if (pi[0] == '/')
{
- pi = pi.Substring(1);
+ pi = pi.Slice(1);
}
format = LeftPart(pi, '/');
@@ -315,6 +313,17 @@ namespace Jellyfin.Server.SocketSharp
return pos == -1 ? strVal : strVal.Substring(0, pos);
}
+ public static ReadOnlySpan<char> LeftPart(ReadOnlySpan<char> strVal, char needle)
+ {
+ if (strVal == null)
+ {
+ return null;
+ }
+
+ var pos = strVal.IndexOf(needle);
+ return pos == -1 ? strVal : strVal.Slice(0, pos);
+ }
+
public static string HandlerFactoryPath;
private string pathInfo;
@@ -326,7 +335,7 @@ namespace Jellyfin.Server.SocketSharp
{
var mode = HandlerFactoryPath;
- var pos = request.RawUrl.IndexOf("?", StringComparison.Ordinal);
+ var pos = request.RawUrl.IndexOf('?', StringComparison.Ordinal);
if (pos != -1)
{
var path = request.RawUrl.Substring(0, pos);
@@ -343,6 +352,7 @@ namespace Jellyfin.Server.SocketSharp
this.pathInfo = System.Net.WebUtility.UrlDecode(pathInfo);
this.pathInfo = NormalizePathInfo(pathInfo, mode);
}
+
return this.pathInfo;
}
}
@@ -444,7 +454,7 @@ namespace Jellyfin.Server.SocketSharp
public string ContentType => request.ContentType;
- public Encoding contentEncoding;
+ private Encoding contentEncoding;
public Encoding ContentEncoding
{
get => contentEncoding ?? request.ContentEncoding;
@@ -502,16 +512,20 @@ namespace Jellyfin.Server.SocketSharp
i++;
}
}
+
return httpFiles;
}
}
public static string NormalizePathInfo(string pathInfo, string handlerPath)
{
- var trimmed = pathInfo.TrimStart('/');
- if (handlerPath != null && trimmed.StartsWith(handlerPath, StringComparison.OrdinalIgnoreCase))
+ if (handlerPath != null)
{
- return trimmed.Substring(handlerPath.Length);
+ var trimmed = pathInfo.TrimStart('/');
+ if (trimmed.StartsWith(handlerPath, StringComparison.OrdinalIgnoreCase))
+ {
+ return trimmed.Substring(handlerPath.Length);
+ }
}
return pathInfo;
diff --git a/Jellyfin.Server/SocketSharp/WebSocketSharpResponse.cs b/Jellyfin.Server/SocketSharp/WebSocketSharpResponse.cs
index cabc96b23..cf5aee5d4 100644
--- a/Jellyfin.Server/SocketSharp/WebSocketSharpResponse.cs
+++ b/Jellyfin.Server/SocketSharp/WebSocketSharpResponse.cs
@@ -13,12 +13,12 @@ using HttpListenerResponse = SocketHttpListener.Net.HttpListenerResponse;
using IHttpResponse = MediaBrowser.Model.Services.IHttpResponse;
using IRequest = MediaBrowser.Model.Services.IRequest;
-
namespace Jellyfin.Server.SocketSharp
{
public class WebSocketSharpResponse : IHttpResponse
{
private readonly ILogger _logger;
+
private readonly HttpListenerResponse _response;
public WebSocketSharpResponse(ILogger logger, HttpListenerResponse response, IRequest request)
@@ -30,7 +30,9 @@ namespace Jellyfin.Server.SocketSharp
}
public IRequest Request { get; private set; }
+
public Dictionary<string, object> Items { get; private set; }
+
public object OriginalResponse => _response;
public int StatusCode
@@ -51,7 +53,42 @@ namespace Jellyfin.Server.SocketSharp
set => _response.ContentType = value;
}
- //public ICookies Cookies { get; set; }
+ public QueryParamCollection Headers => _response.Headers;
+
+ private static string AsHeaderValue(Cookie cookie)
+ {
+ DateTime defaultExpires = DateTime.MinValue;
+
+ var path = cookie.Expires == defaultExpires
+ ? "/"
+ : cookie.Path ?? "/";
+
+ var sb = new StringBuilder();
+
+ sb.Append($"{cookie.Name}={cookie.Value};path={path}");
+
+ if (cookie.Expires != defaultExpires)
+ {
+ sb.Append($";expires={cookie.Expires:R}");
+ }
+
+ if (!string.IsNullOrEmpty(cookie.Domain))
+ {
+ sb.Append($";domain={cookie.Domain}");
+ }
+
+ if (cookie.Secure)
+ {
+ sb.Append(";Secure");
+ }
+
+ if (cookie.HttpOnly)
+ {
+ sb.Append(";HttpOnly");
+ }
+
+ return sb.ToString();
+ }
public void AddHeader(string name, string value)
{
@@ -64,8 +101,6 @@ namespace Jellyfin.Server.SocketSharp
_response.AddHeader(name, value);
}
- public QueryParamCollection Headers => _response.Headers;
-
public string GetHeader(string name)
{
return _response.Headers[name];
@@ -114,9 +149,9 @@ namespace Jellyfin.Server.SocketSharp
public void SetContentLength(long contentLength)
{
- //you can happily set the Content-Length header in Asp.Net
- //but HttpListener will complain if you do - you have to set ContentLength64 on the response.
- //workaround: HttpListener throws "The parameter is incorrect" exceptions when we try to set the Content-Length header
+ // you can happily set the Content-Length header in Asp.Net
+ // but HttpListener will complain if you do - you have to set ContentLength64 on the response.
+ // workaround: HttpListener throws "The parameter is incorrect" exceptions when we try to set the Content-Length header
_response.ContentLength64 = contentLength;
}
@@ -126,45 +161,6 @@ namespace Jellyfin.Server.SocketSharp
_response.Headers.Add("Set-Cookie", cookieStr);
}
- public static string AsHeaderValue(Cookie cookie)
- {
- var defaultExpires = DateTime.MinValue;
-
- var path = cookie.Expires == defaultExpires
- ? "/"
- : cookie.Path ?? "/";
-
- var sb = new StringBuilder();
-
- sb.Append($"{cookie.Name}={cookie.Value};path={path}");
-
- if (cookie.Expires != defaultExpires)
- {
- sb.Append($";expires={cookie.Expires:R}");
- }
-
- if (!string.IsNullOrEmpty(cookie.Domain))
- {
- sb.Append($";domain={cookie.Domain}");
- }
- //else if (restrictAllCookiesToDomain != null)
- //{
- // sb.Append($";domain={restrictAllCookiesToDomain}");
- //}
-
- if (cookie.Secure)
- {
- sb.Append(";Secure");
- }
- if (cookie.HttpOnly)
- {
- sb.Append(";HttpOnly");
- }
-
- return sb.ToString();
- }
-
-
public bool SendChunked
{
get => _response.SendChunked;
diff --git a/MediaBrowser.Api/BaseApiService.cs b/MediaBrowser.Api/BaseApiService.cs
index 451ee72dd..a037357ed 100644
--- a/MediaBrowser.Api/BaseApiService.cs
+++ b/MediaBrowser.Api/BaseApiService.cs
@@ -9,6 +9,7 @@ using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Services;
+using MediaBrowser.Model.Querying;
using Microsoft.Extensions.Logging;
namespace MediaBrowser.Api
@@ -118,8 +119,7 @@ namespace MediaBrowser.Api
{
var options = new DtoOptions();
- var hasFields = request as IHasItemFields;
- if (hasFields != null)
+ if (request is IHasItemFields hasFields)
{
options.Fields = hasFields.GetItemFields();
}
@@ -133,9 +133,11 @@ namespace MediaBrowser.Api
client.IndexOf("media center", StringComparison.OrdinalIgnoreCase) != -1 ||
client.IndexOf("classic", StringComparison.OrdinalIgnoreCase) != -1)
{
- var list = options.Fields.ToList();
- list.Add(Model.Querying.ItemFields.RecursiveItemCount);
- options.Fields = list.ToArray();
+ int oldLen = options.Fields.Length;
+ var arr = new ItemFields[oldLen + 1];
+ options.Fields.CopyTo(arr, 0);
+ arr[oldLen] = Model.Querying.ItemFields.RecursiveItemCount;
+ options.Fields = arr;
}
if (client.IndexOf("kodi", StringComparison.OrdinalIgnoreCase) != -1 ||
@@ -146,9 +148,12 @@ namespace MediaBrowser.Api
client.IndexOf("samsung", StringComparison.OrdinalIgnoreCase) != -1 ||
client.IndexOf("androidtv", StringComparison.OrdinalIgnoreCase) != -1)
{
- var list = options.Fields.ToList();
- list.Add(Model.Querying.ItemFields.ChildCount);
- options.Fields = list.ToArray();
+
+ int oldLen = options.Fields.Length;
+ var arr = new ItemFields[oldLen + 1];
+ options.Fields.CopyTo(arr, 0);
+ arr[oldLen] = Model.Querying.ItemFields.ChildCount;
+ options.Fields = arr;
}
}
@@ -167,7 +172,16 @@ namespace MediaBrowser.Api
if (!string.IsNullOrWhiteSpace(hasDtoOptions.EnableImageTypes))
{
- options.ImageTypes = (hasDtoOptions.EnableImageTypes ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).Select(v => (ImageType)Enum.Parse(typeof(ImageType), v, true)).ToArray();
+ if (string.IsNullOrEmpty(hasDtoOptions.EnableImageTypes))
+ {
+ options.ImageTypes = Array.Empty<ImageType>();
+ }
+ else
+ {
+ options.ImageTypes = hasDtoOptions.EnableImageTypes.Split(new [] { ',' }, StringSplitOptions.RemoveEmptyEntries)
+ .Select(v => (ImageType)Enum.Parse(typeof(ImageType), v, true))
+ .ToArray();
+ }
}
}
diff --git a/MediaBrowser.Api/EnvironmentService.cs b/MediaBrowser.Api/EnvironmentService.cs
index bdd7a8f8f..f4813e713 100644
--- a/MediaBrowser.Api/EnvironmentService.cs
+++ b/MediaBrowser.Api/EnvironmentService.cs
@@ -173,14 +173,8 @@ namespace MediaBrowser.Api
_fileSystem.DeleteFile(file);
}
- public object Get(GetDefaultDirectoryBrowser request)
- {
- var result = new DefaultDirectoryBrowserInfo();
-
- result.Path = _fileSystem.DefaultDirectory;
-
- return ToOptimizedResult(result);
- }
+ public object Get(GetDefaultDirectoryBrowser request) =>
+ ToOptimizedResult(new DefaultDirectoryBrowserInfo {Path = null});
/// <summary>
/// Gets the specified request.
diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs
index 073686298..a6be071b8 100644
--- a/MediaBrowser.Api/Playback/BaseStreamingService.cs
+++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs
@@ -8,7 +8,6 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.Net;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Devices;
@@ -71,7 +70,6 @@ namespace MediaBrowser.Api.Playback
protected IMediaSourceManager MediaSourceManager { get; private set; }
protected IJsonSerializer JsonSerializer { get; private set; }
- public static IHttpClient HttpClient;
protected IAuthorizationContext AuthorizationContext { get; private set; }
protected EncodingHelper EncodingHelper { get; set; }
diff --git a/MediaBrowser.Api/Playback/Progressive/AudioService.cs b/MediaBrowser.Api/Playback/Progressive/AudioService.cs
index 208a5560d..48b4e2f24 100644
--- a/MediaBrowser.Api/Playback/Progressive/AudioService.cs
+++ b/MediaBrowser.Api/Playback/Progressive/AudioService.cs
@@ -1,4 +1,5 @@
using System.Threading.Tasks;
+using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Dlna;
@@ -33,6 +34,7 @@ namespace MediaBrowser.Api.Playback.Progressive
public class AudioService : BaseProgressiveStreamingService
{
public AudioService(
+ IHttpClient httpClient,
IServerConfigurationManager serverConfig,
IUserManager userManager,
ILibraryManager libraryManager,
@@ -46,7 +48,8 @@ namespace MediaBrowser.Api.Playback.Progressive
IJsonSerializer jsonSerializer,
IAuthorizationContext authorizationContext,
IEnvironmentInfo environmentInfo)
- : base(serverConfig,
+ : base(httpClient,
+ serverConfig,
userManager,
libraryManager,
isoManager,
diff --git a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs
index c197de173..6a98c5e8a 100644
--- a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs
+++ b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs
@@ -26,8 +26,10 @@ namespace MediaBrowser.Api.Playback.Progressive
public abstract class BaseProgressiveStreamingService : BaseStreamingService
{
protected readonly IEnvironmentInfo EnvironmentInfo;
+ protected IHttpClient HttpClient { get; private set; }
public BaseProgressiveStreamingService(
+ IHttpClient httpClient,
IServerConfigurationManager serverConfig,
IUserManager userManager,
ILibraryManager libraryManager,
@@ -55,6 +57,7 @@ namespace MediaBrowser.Api.Playback.Progressive
authorizationContext)
{
EnvironmentInfo = environmentInfo;
+ HttpClient = httpClient;
}
/// <summary>
diff --git a/MediaBrowser.Api/Playback/Progressive/VideoService.cs b/MediaBrowser.Api/Playback/Progressive/VideoService.cs
index a0ea5c62d..7aeb0e9e8 100644
--- a/MediaBrowser.Api/Playback/Progressive/VideoService.cs
+++ b/MediaBrowser.Api/Playback/Progressive/VideoService.cs
@@ -1,4 +1,5 @@
using System.Threading.Tasks;
+using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Dlna;
@@ -69,6 +70,7 @@ namespace MediaBrowser.Api.Playback.Progressive
public class VideoService : BaseProgressiveStreamingService
{
public VideoService(
+ IHttpClient httpClient,
IServerConfigurationManager serverConfig,
IUserManager userManager,
ILibraryManager libraryManager,
@@ -82,7 +84,8 @@ namespace MediaBrowser.Api.Playback.Progressive
IJsonSerializer jsonSerializer,
IAuthorizationContext authorizationContext,
IEnvironmentInfo environmentInfo)
- : base(serverConfig,
+ : base(httpClient,
+ serverConfig,
userManager,
libraryManager,
isoManager,
diff --git a/MediaBrowser.Api/Playback/UniversalAudioService.cs b/MediaBrowser.Api/Playback/UniversalAudioService.cs
index e07770a4c..f97e88e98 100644
--- a/MediaBrowser.Api/Playback/UniversalAudioService.cs
+++ b/MediaBrowser.Api/Playback/UniversalAudioService.cs
@@ -77,6 +77,7 @@ namespace MediaBrowser.Api.Playback
public class UniversalAudioService : BaseApiService
{
public UniversalAudioService(
+ IHttpClient httpClient,
IServerConfigurationManager serverConfigurationManager,
IUserManager userManager,
ILibraryManager libraryManager,
@@ -95,6 +96,7 @@ namespace MediaBrowser.Api.Playback
IEnvironmentInfo environmentInfo,
ILoggerFactory loggerFactory)
{
+ HttpClient = httpClient;
ServerConfigurationManager = serverConfigurationManager;
UserManager = userManager;
LibraryManager = libraryManager;
@@ -115,6 +117,7 @@ namespace MediaBrowser.Api.Playback
_logger = loggerFactory.CreateLogger(nameof(UniversalAudioService));
}
+ protected IHttpClient HttpClient { get; private set; }
protected IServerConfigurationManager ServerConfigurationManager { get; private set; }
protected IUserManager UserManager { get; private set; }
protected ILibraryManager LibraryManager { get; private set; }
@@ -323,7 +326,8 @@ namespace MediaBrowser.Api.Playback
}
else
{
- var service = new AudioService(ServerConfigurationManager,
+ var service = new AudioService(HttpClient,
+ ServerConfigurationManager,
UserManager,
LibraryManager,
IsoManager,
diff --git a/MediaBrowser.Api/ScheduledTasks/ScheduledTaskService.cs b/MediaBrowser.Api/ScheduledTasks/ScheduledTaskService.cs
index 16b036912..b7e94b73f 100644
--- a/MediaBrowser.Api/ScheduledTasks/ScheduledTaskService.cs
+++ b/MediaBrowser.Api/ScheduledTasks/ScheduledTaskService.cs
@@ -197,16 +197,6 @@ namespace MediaBrowser.Api.ScheduledTasks
throw new ResourceNotFoundException("Task not found");
}
- if (string.Equals(task.ScheduledTask.Key, "SystemUpdateTask", StringComparison.OrdinalIgnoreCase))
- {
- // This is a hack for now just to get the update application function to work when auto-update is disabled
- if (!_config.Configuration.EnableAutoUpdate)
- {
- _config.Configuration.EnableAutoUpdate = true;
- _config.SaveConfiguration();
- }
- }
-
TaskManager.Execute(task, new TaskOptions());
}
@@ -238,16 +228,14 @@ namespace MediaBrowser.Api.ScheduledTasks
// https://code.google.com/p/servicestack/source/browse/trunk/Common/ServiceStack.Text/ServiceStack.Text/Controller/PathInfo.cs
var id = GetPathValue(1);
- var task = TaskManager.ScheduledTasks.FirstOrDefault(i => string.Equals(i.Id, id));
+ var task = TaskManager.ScheduledTasks.FirstOrDefault(i => string.Equals(i.Id, id, StringComparison.Ordinal));
if (task == null)
{
throw new ResourceNotFoundException("Task not found");
}
- var triggerInfos = request;
-
- task.Triggers = triggerInfos.ToArray();
+ task.Triggers = request.ToArray();
}
}
}
diff --git a/MediaBrowser.Api/Session/SessionInfoWebSocketListener.cs b/MediaBrowser.Api/Session/SessionInfoWebSocketListener.cs
index 387ccad25..beb2fb11d 100644
--- a/MediaBrowser.Api/Session/SessionInfoWebSocketListener.cs
+++ b/MediaBrowser.Api/Session/SessionInfoWebSocketListener.cs
@@ -11,7 +11,7 @@ namespace MediaBrowser.Api.Session
/// <summary>
/// Class SessionInfoWebSocketListener
/// </summary>
- class SessionInfoWebSocketListener : BasePeriodicWebSocketListener<IEnumerable<SessionInfo>, WebSocketListenerState>
+ public class SessionInfoWebSocketListener : BasePeriodicWebSocketListener<IEnumerable<SessionInfo>, WebSocketListenerState>
{
/// <summary>
/// Gets the name.
diff --git a/MediaBrowser.Api/System/ActivityLogWebSocketListener.cs b/MediaBrowser.Api/System/ActivityLogWebSocketListener.cs
index 0df46c399..43f3c5a22 100644
--- a/MediaBrowser.Api/System/ActivityLogWebSocketListener.cs
+++ b/MediaBrowser.Api/System/ActivityLogWebSocketListener.cs
@@ -11,7 +11,7 @@ namespace MediaBrowser.Api.System
/// <summary>
/// Class SessionInfoWebSocketListener
/// </summary>
- class ActivityLogWebSocketListener : BasePeriodicWebSocketListener<List<ActivityLogEntry>, WebSocketListenerState>
+ public class ActivityLogWebSocketListener : BasePeriodicWebSocketListener<List<ActivityLogEntry>, WebSocketListenerState>
{
/// <summary>
/// Gets the name.
diff --git a/MediaBrowser.Api/UserLibrary/ArtistsService.cs b/MediaBrowser.Api/UserLibrary/ArtistsService.cs
index 651da1939..7a8455ff2 100644
--- a/MediaBrowser.Api/UserLibrary/ArtistsService.cs
+++ b/MediaBrowser.Api/UserLibrary/ArtistsService.cs
@@ -112,7 +112,7 @@ namespace MediaBrowser.Api.UserLibrary
return ToOptimizedResult(result);
}
- protected override QueryResult<Tuple<BaseItem, ItemCounts>> GetItems(GetItemsByName request, InternalItemsQuery query)
+ protected override QueryResult<(BaseItem, ItemCounts)> GetItems(GetItemsByName request, InternalItemsQuery query)
{
if (request is GetAlbumArtists)
{
diff --git a/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs b/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs
index 471b41127..e3c9ae58e 100644
--- a/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs
+++ b/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs
@@ -209,9 +209,9 @@ namespace MediaBrowser.Api.UserLibrary
};
}
- protected virtual QueryResult<Tuple<BaseItem, ItemCounts>> GetItems(GetItemsByName request, InternalItemsQuery query)
+ protected virtual QueryResult<(BaseItem, ItemCounts)> GetItems(GetItemsByName request, InternalItemsQuery query)
{
- return new QueryResult<Tuple<BaseItem, ItemCounts>>();
+ return new QueryResult<(BaseItem, ItemCounts)>();
}
private void SetItemCounts(BaseItemDto dto, ItemCounts counts)
diff --git a/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs b/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs
index 7af50c329..a26f59573 100644
--- a/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs
+++ b/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs
@@ -396,14 +396,12 @@ namespace MediaBrowser.Api.UserLibrary
public VideoType[] GetVideoTypes()
{
- var val = VideoTypes;
-
- if (string.IsNullOrEmpty(val))
+ if (string.IsNullOrEmpty(VideoTypes))
{
- return new VideoType[] { };
+ return Array.Empty<VideoType>();
}
- return val.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(v => (VideoType)Enum.Parse(typeof(VideoType), v, true)).ToArray();
+ return VideoTypes.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(v => (VideoType)Enum.Parse(typeof(VideoType), v, true)).ToArray();
}
/// <summary>
diff --git a/MediaBrowser.Api/UserLibrary/GenresService.cs b/MediaBrowser.Api/UserLibrary/GenresService.cs
index baf570d50..0c04d02dd 100644
--- a/MediaBrowser.Api/UserLibrary/GenresService.cs
+++ b/MediaBrowser.Api/UserLibrary/GenresService.cs
@@ -92,7 +92,7 @@ namespace MediaBrowser.Api.UserLibrary
return ToOptimizedResult(result);
}
- protected override QueryResult<Tuple<BaseItem, ItemCounts>> GetItems(GetItemsByName request, InternalItemsQuery query)
+ protected override QueryResult<(BaseItem, ItemCounts)> GetItems(GetItemsByName request, InternalItemsQuery query)
{
var viewType = GetParentItemViewType(request);
diff --git a/MediaBrowser.Api/UserLibrary/ItemsService.cs b/MediaBrowser.Api/UserLibrary/ItemsService.cs
index 3ae7da007..84475467f 100644
--- a/MediaBrowser.Api/UserLibrary/ItemsService.cs
+++ b/MediaBrowser.Api/UserLibrary/ItemsService.cs
@@ -12,6 +12,7 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Services;
+using Microsoft.Extensions.Logging;
namespace MediaBrowser.Api.UserLibrary
{
@@ -90,7 +91,7 @@ namespace MediaBrowser.Api.UserLibrary
var options = GetDtoOptions(_authContext, request);
- var ancestorIds = new List<Guid>();
+ var ancestorIds = Array.Empty<Guid>();
var excludeFolderIds = user.Configuration.LatestItemsExcludes;
if (parentIdGuid.Equals(Guid.Empty) && excludeFolderIds.Length > 0)
@@ -99,12 +100,12 @@ namespace MediaBrowser.Api.UserLibrary
.Where(i => i is Folder)
.Where(i => !excludeFolderIds.Contains(i.Id.ToString("N")))
.Select(i => i.Id)
- .ToList();
+ .ToArray();
}
var itemsResult = _libraryManager.GetItemsResult(new InternalItemsQuery(user)
{
- OrderBy = new[] { ItemSortBy.DatePlayed }.Select(i => new ValueTuple<string, SortOrder>(i, SortOrder.Descending)).ToArray(),
+ OrderBy = new[] { (ItemSortBy.DatePlayed, SortOrder.Descending) },
IsResumable = true,
StartIndex = request.StartIndex,
Limit = request.Limit,
@@ -115,7 +116,7 @@ namespace MediaBrowser.Api.UserLibrary
IsVirtualItem = false,
CollapseBoxSetItems = false,
EnableTotalRecordCount = request.EnableTotalRecordCount,
- AncestorIds = ancestorIds.ToArray(),
+ AncestorIds = ancestorIds,
IncludeItemTypes = request.GetIncludeItemTypes(),
ExcludeItemTypes = request.GetExcludeItemTypes(),
SearchTerm = request.SearchTerm
@@ -155,7 +156,7 @@ namespace MediaBrowser.Api.UserLibrary
/// <param name="request">The request.</param>
private QueryResult<BaseItemDto> GetItems(GetItems request)
{
- var user = !request.UserId.Equals(Guid.Empty) ? _userManager.GetUserById(request.UserId) : null;
+ var user = request.UserId == Guid.Empty ? null : _userManager.GetUserById(request.UserId);
var dtoOptions = GetDtoOptions(_authContext, request);
@@ -190,11 +191,8 @@ namespace MediaBrowser.Api.UserLibrary
/// </summary>
private QueryResult<BaseItem> GetQueryResult(GetItems request, DtoOptions dtoOptions, User user)
{
- if (string.Equals(request.IncludeItemTypes, "Playlist", StringComparison.OrdinalIgnoreCase))
- {
- request.ParentId = null;
- }
- else if (string.Equals(request.IncludeItemTypes, "BoxSet", StringComparison.OrdinalIgnoreCase))
+ if (string.Equals(request.IncludeItemTypes, "Playlist", StringComparison.OrdinalIgnoreCase)
+ || string.Equals(request.IncludeItemTypes, "BoxSet", StringComparison.OrdinalIgnoreCase))
{
request.ParentId = null;
}
@@ -227,6 +225,16 @@ namespace MediaBrowser.Api.UserLibrary
request.IncludeItemTypes = "Playlist";
}
+ if (!user.Policy.EnableAllFolders && !user.Policy.EnabledFolders.Any(i => new Guid(i) == item.Id))
+ {
+ Logger.LogWarning("{UserName} is not permitted to access Library {ItemName}.", user.Name, item.Name);
+ return new QueryResult<BaseItem>
+ {
+ Items = Array.Empty<BaseItem>(),
+ TotalRecordCount = 0
+ };
+ }
+
if (request.Recursive || !string.IsNullOrEmpty(request.Ids) || user == null)
{
return folder.GetItems(GetItemsQuery(request, dtoOptions, user));
diff --git a/MediaBrowser.Api/UserLibrary/MusicGenresService.cs b/MediaBrowser.Api/UserLibrary/MusicGenresService.cs
index 4fcc3aa53..94f5262b0 100644
--- a/MediaBrowser.Api/UserLibrary/MusicGenresService.cs
+++ b/MediaBrowser.Api/UserLibrary/MusicGenresService.cs
@@ -83,7 +83,7 @@ namespace MediaBrowser.Api.UserLibrary
return ToOptimizedResult(result);
}
- protected override QueryResult<Tuple<BaseItem, ItemCounts>> GetItems(GetItemsByName request, InternalItemsQuery query)
+ protected override QueryResult<(BaseItem, ItemCounts)> GetItems(GetItemsByName request, InternalItemsQuery query)
{
return LibraryManager.GetMusicGenres(query);
}
diff --git a/MediaBrowser.Api/UserLibrary/PersonsService.cs b/MediaBrowser.Api/UserLibrary/PersonsService.cs
index d317f9f38..c26457778 100644
--- a/MediaBrowser.Api/UserLibrary/PersonsService.cs
+++ b/MediaBrowser.Api/UserLibrary/PersonsService.cs
@@ -101,7 +101,7 @@ namespace MediaBrowser.Api.UserLibrary
throw new NotImplementedException();
}
- protected override QueryResult<Tuple<BaseItem, ItemCounts>> GetItems(GetItemsByName request, InternalItemsQuery query)
+ protected override QueryResult<(BaseItem, ItemCounts)> GetItems(GetItemsByName request, InternalItemsQuery query)
{
var items = LibraryManager.GetPeopleItems(new InternalPeopleQuery
{
@@ -109,10 +109,10 @@ namespace MediaBrowser.Api.UserLibrary
NameContains = query.NameContains ?? query.SearchTerm
});
- return new QueryResult<Tuple<BaseItem, ItemCounts>>
+ return new QueryResult<(BaseItem, ItemCounts)>
{
TotalRecordCount = items.Count,
- Items = items.Take(query.Limit ?? int.MaxValue).Select(i => new Tuple<BaseItem, ItemCounts>(i, new ItemCounts())).ToArray()
+ Items = items.Take(query.Limit ?? int.MaxValue).Select(i => (i as BaseItem, new ItemCounts())).ToArray()
};
}
diff --git a/MediaBrowser.Api/UserLibrary/StudiosService.cs b/MediaBrowser.Api/UserLibrary/StudiosService.cs
index 4e2483a56..890acc931 100644
--- a/MediaBrowser.Api/UserLibrary/StudiosService.cs
+++ b/MediaBrowser.Api/UserLibrary/StudiosService.cs
@@ -91,7 +91,7 @@ namespace MediaBrowser.Api.UserLibrary
return ToOptimizedResult(result);
}
- protected override QueryResult<Tuple<BaseItem, ItemCounts>> GetItems(GetItemsByName request, InternalItemsQuery query)
+ protected override QueryResult<(BaseItem, ItemCounts)> GetItems(GetItemsByName request, InternalItemsQuery query)
{
return LibraryManager.GetStudios(query);
}
diff --git a/MediaBrowser.Common/Configuration/IApplicationPaths.cs b/MediaBrowser.Common/Configuration/IApplicationPaths.cs
index 27092c0e1..cb4e8bf5f 100644
--- a/MediaBrowser.Common/Configuration/IApplicationPaths.cs
+++ b/MediaBrowser.Common/Configuration/IApplicationPaths.cs
@@ -42,12 +42,6 @@ namespace MediaBrowser.Common.Configuration
string PluginConfigurationsPath { get; }
/// <summary>
- /// Gets the path to where temporary update files will be stored
- /// </summary>
- /// <value>The plugin configurations path.</value>
- string TempUpdatePath { get; }
-
- /// <summary>
/// Gets the path to the log directory
/// </summary>
/// <value>The log directory path.</value>
diff --git a/MediaBrowser.Common/IApplicationHost.cs b/MediaBrowser.Common/IApplicationHost.cs
index 6891152ee..3a4098612 100644
--- a/MediaBrowser.Common/IApplicationHost.cs
+++ b/MediaBrowser.Common/IApplicationHost.cs
@@ -73,12 +73,6 @@ namespace MediaBrowser.Common
string ApplicationUserAgent { get; }
/// <summary>
- /// Gets or sets a value indicating whether this instance can self update.
- /// </summary>
- /// <value><c>true</c> if this instance can self update; otherwise, <c>false</c>.</value>
- bool CanSelfUpdate { get; }
-
- /// <summary>
/// Gets the exports.
/// </summary>
/// <typeparam name="T"></typeparam>
@@ -87,12 +81,6 @@ namespace MediaBrowser.Common
IEnumerable<T> GetExports<T>(bool manageLifetime = true);
/// <summary>
- /// Updates the application.
- /// </summary>
- /// <returns>Task.</returns>
- Task UpdateApplication(PackageVersionInfo package, CancellationToken cancellationToken, IProgress<double> progress);
-
- /// <summary>
/// Resolves this instance.
/// </summary>
/// <typeparam name="T"></typeparam>
diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs
index 60c183d04..511356aa4 100644
--- a/MediaBrowser.Controller/Library/ILibraryManager.cs
+++ b/MediaBrowser.Controller/Library/ILibraryManager.cs
@@ -193,7 +193,7 @@ namespace MediaBrowser.Controller.Library
/// <summary>
/// Updates the item.
/// </summary>
- void UpdateItems(List<BaseItem> items, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken);
+ void UpdateItems(IEnumerable<BaseItem> items, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken);
void UpdateItem(BaseItem item, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken);
/// <summary>
@@ -520,12 +520,12 @@ namespace MediaBrowser.Controller.Library
void UpdateMediaPath(string virtualFolderName, MediaPathInfo path);
void RemoveMediaPath(string virtualFolderName, string path);
- QueryResult<Tuple<BaseItem, ItemCounts>> GetGenres(InternalItemsQuery query);
- QueryResult<Tuple<BaseItem, ItemCounts>> GetMusicGenres(InternalItemsQuery query);
- QueryResult<Tuple<BaseItem, ItemCounts>> GetStudios(InternalItemsQuery query);
- QueryResult<Tuple<BaseItem, ItemCounts>> GetArtists(InternalItemsQuery query);
- QueryResult<Tuple<BaseItem, ItemCounts>> GetAlbumArtists(InternalItemsQuery query);
- QueryResult<Tuple<BaseItem, ItemCounts>> GetAllArtists(InternalItemsQuery query);
+ QueryResult<(BaseItem, ItemCounts)> GetGenres(InternalItemsQuery query);
+ QueryResult<(BaseItem, ItemCounts)> GetMusicGenres(InternalItemsQuery query);
+ QueryResult<(BaseItem, ItemCounts)> GetStudios(InternalItemsQuery query);
+ QueryResult<(BaseItem, ItemCounts)> GetArtists(InternalItemsQuery query);
+ QueryResult<(BaseItem, ItemCounts)> GetAlbumArtists(InternalItemsQuery query);
+ QueryResult<(BaseItem, ItemCounts)> GetAllArtists(InternalItemsQuery query);
int GetCount(InternalItemsQuery query);
diff --git a/MediaBrowser.Controller/Net/StaticResultOptions.cs b/MediaBrowser.Controller/Net/StaticResultOptions.cs
index a54de12be..7a179913a 100644
--- a/MediaBrowser.Controller/Net/StaticResultOptions.cs
+++ b/MediaBrowser.Controller/Net/StaticResultOptions.cs
@@ -12,8 +12,6 @@ namespace MediaBrowser.Controller.Net
public string ContentType { get; set; }
public TimeSpan? CacheDuration { get; set; }
public DateTime? DateLastModified { get; set; }
- public Guid CacheKey { get; set; }
-
public Func<Task<Stream>> ContentFactory { get; set; }
public bool IsHeadRequest { get; set; }
diff --git a/MediaBrowser.Controller/Persistence/IItemRepository.cs b/MediaBrowser.Controller/Persistence/IItemRepository.cs
index 5156fce11..47e0f3453 100644
--- a/MediaBrowser.Controller/Persistence/IItemRepository.cs
+++ b/MediaBrowser.Controller/Persistence/IItemRepository.cs
@@ -32,7 +32,7 @@ namespace MediaBrowser.Controller.Persistence
/// </summary>
/// <param name="items">The items.</param>
/// <param name="cancellationToken">The cancellation token.</param>
- void SaveItems(List<BaseItem> items, CancellationToken cancellationToken);
+ void SaveItems(IEnumerable<BaseItem> items, CancellationToken cancellationToken);
void SaveImages(BaseItem item);
@@ -141,12 +141,12 @@ namespace MediaBrowser.Controller.Persistence
int GetCount(InternalItemsQuery query);
- QueryResult<Tuple<BaseItem, ItemCounts>> GetGenres(InternalItemsQuery query);
- QueryResult<Tuple<BaseItem, ItemCounts>> GetMusicGenres(InternalItemsQuery query);
- QueryResult<Tuple<BaseItem, ItemCounts>> GetStudios(InternalItemsQuery query);
- QueryResult<Tuple<BaseItem, ItemCounts>> GetArtists(InternalItemsQuery query);
- QueryResult<Tuple<BaseItem, ItemCounts>> GetAlbumArtists(InternalItemsQuery query);
- QueryResult<Tuple<BaseItem, ItemCounts>> GetAllArtists(InternalItemsQuery query);
+ QueryResult<(BaseItem, ItemCounts)> GetGenres(InternalItemsQuery query);
+ QueryResult<(BaseItem, ItemCounts)> GetMusicGenres(InternalItemsQuery query);
+ QueryResult<(BaseItem, ItemCounts)> GetStudios(InternalItemsQuery query);
+ QueryResult<(BaseItem, ItemCounts)> GetArtists(InternalItemsQuery query);
+ QueryResult<(BaseItem, ItemCounts)> GetAlbumArtists(InternalItemsQuery query);
+ QueryResult<(BaseItem, ItemCounts)> GetAllArtists(InternalItemsQuery query);
List<string> GetMusicGenreNames();
List<string> GetStudioNames();
diff --git a/MediaBrowser.LocalMetadata/Providers/PlaylistXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/PlaylistXmlProvider.cs
index 4180a4f15..442a18cb9 100644
--- a/MediaBrowser.LocalMetadata/Providers/PlaylistXmlProvider.cs
+++ b/MediaBrowser.LocalMetadata/Providers/PlaylistXmlProvider.cs
@@ -9,7 +9,7 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.LocalMetadata.Providers
{
- class PlaylistXmlProvider : BaseXmlProvider<Playlist>
+ public class PlaylistXmlProvider : BaseXmlProvider<Playlist>
{
private readonly ILogger _logger;
private readonly IProviderManager _providerManager;
diff --git a/MediaBrowser.Model/Configuration/BaseApplicationConfiguration.cs b/MediaBrowser.Model/Configuration/BaseApplicationConfiguration.cs
index ce4ef1cfe..6a1a0f090 100644
--- a/MediaBrowser.Model/Configuration/BaseApplicationConfiguration.cs
+++ b/MediaBrowser.Model/Configuration/BaseApplicationConfiguration.cs
@@ -7,20 +7,6 @@ namespace MediaBrowser.Model.Configuration
/// </summary>
public class BaseApplicationConfiguration
{
- // TODO: @bond Remove?
- /// <summary>
- /// Gets or sets a value indicating whether [enable debug level logging].
- /// </summary>
- /// <value><c>true</c> if [enable debug level logging]; otherwise, <c>false</c>.</value>
- public bool EnableDebugLevelLogging { get; set; }
-
- /// <summary>
- /// Enable automatically and silently updating of the application
- /// </summary>
- /// <value><c>true</c> if [enable auto update]; otherwise, <c>false</c>.</value>
- public bool EnableAutoUpdate { get; set; }
-
- // TODO: @bond Remove?
/// <summary>
/// The number of days we should retain log files
/// </summary>
@@ -44,7 +30,6 @@ namespace MediaBrowser.Model.Configuration
/// </summary>
public BaseApplicationConfiguration()
{
- EnableAutoUpdate = true;
LogFileRetentionDays = 3;
}
}
diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs
index 6c6e09ab1..586e322e4 100644
--- a/MediaBrowser.Model/Dlna/StreamBuilder.cs
+++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs
@@ -297,7 +297,7 @@ namespace MediaBrowser.Model.Dlna
int? inputAudioChannels = audioStream?.Channels;
int? inputAudioBitrate = audioStream?.BitDepth;
int? inputAudioSampleRate = audioStream?.SampleRate;
- int? inputAudioBitDepth = audioStream.BitDepth;
+ int? inputAudioBitDepth = audioStream?.BitDepth;
if (directPlayMethods.Count() > 0)
{
diff --git a/MediaBrowser.Model/IO/IFileSystem.cs b/MediaBrowser.Model/IO/IFileSystem.cs
index 6c9b2bd88..e0771245f 100644
--- a/MediaBrowser.Model/IO/IFileSystem.cs
+++ b/MediaBrowser.Model/IO/IFileSystem.cs
@@ -113,8 +113,6 @@ namespace MediaBrowser.Model.IO
Stream GetFileStream(string path, FileOpenMode mode, FileAccessMode access, FileShareMode share,
FileOpenOptions fileOpenOptions);
- string DefaultDirectory { get; }
-
/// <summary>
/// Swaps the files.
/// </summary>
diff --git a/MediaBrowser.Model/Serialization/IJsonSerializer.cs b/MediaBrowser.Model/Serialization/IJsonSerializer.cs
index ae0cf6f36..18f51f652 100644
--- a/MediaBrowser.Model/Serialization/IJsonSerializer.cs
+++ b/MediaBrowser.Model/Serialization/IJsonSerializer.cs
@@ -15,6 +15,14 @@ namespace MediaBrowser.Model.Serialization
void SerializeToStream(object obj, Stream stream);
/// <summary>
+ /// Serializes to stream.
+ /// </summary>
+ /// <param name="obj">The obj.</param>
+ /// <param name="stream">The stream.</param>
+ /// <exception cref="ArgumentNullException">obj</exception>
+ void SerializeToStream<T>(T obj, Stream stream);
+
+ /// <summary>
/// Serializes to file.
/// </summary>
/// <param name="obj">The obj.</param>
diff --git a/MediaBrowser.Model/System/SystemInfo.cs b/MediaBrowser.Model/System/SystemInfo.cs
index d9ed68b27..581a1069c 100644
--- a/MediaBrowser.Model/System/SystemInfo.cs
+++ b/MediaBrowser.Model/System/SystemInfo.cs
@@ -60,12 +60,6 @@ namespace MediaBrowser.Model.System
/// <value><c>true</c> if this instance can self restart; otherwise, <c>false</c>.</value>
public bool CanSelfRestart { get; set; }
- /// <summary>
- /// Gets or sets a value indicating whether this instance can self update.
- /// </summary>
- /// <value><c>true</c> if this instance can self update; otherwise, <c>false</c>.</value>
- public bool CanSelfUpdate { get; set; }
-
public bool CanLaunchWebBrowser { get; set; }
/// <summary>
diff --git a/MediaBrowser.Providers/BoxSets/MovieDbBoxSetImageProvider.cs b/MediaBrowser.Providers/BoxSets/MovieDbBoxSetImageProvider.cs
index c6c1a2a94..4d12b2f4a 100644
--- a/MediaBrowser.Providers/BoxSets/MovieDbBoxSetImageProvider.cs
+++ b/MediaBrowser.Providers/BoxSets/MovieDbBoxSetImageProvider.cs
@@ -14,7 +14,7 @@ using MediaBrowser.Providers.Movies;
namespace MediaBrowser.Providers.BoxSets
{
- class MovieDbBoxSetImageProvider : IRemoteImageProvider, IHasOrder
+ public class MovieDbBoxSetImageProvider : IRemoteImageProvider, IHasOrder
{
private readonly IHttpClient _httpClient;
diff --git a/MediaBrowser.Providers/Manager/GenericPriorityQueue.cs b/MediaBrowser.Providers/Manager/GenericPriorityQueue.cs
deleted file mode 100644
index 10ff2515c..000000000
--- a/MediaBrowser.Providers/Manager/GenericPriorityQueue.cs
+++ /dev/null
@@ -1,402 +0,0 @@
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Runtime.CompilerServices;
-
-//TODO Fix namespace or replace
-namespace Priority_Queue
-{
- /// <summary>
- /// Credit: https://github.com/BlueRaja/High-Speed-Priority-Queue-for-C-Sharp
- /// A copy of StablePriorityQueue which also has generic priority-type
- /// </summary>
- /// <typeparam name="TItem">The values in the queue. Must extend the GenericPriorityQueue class</typeparam>
- /// <typeparam name="TPriority">The priority-type. Must extend IComparable&lt;TPriority&gt;</typeparam>
- public sealed class GenericPriorityQueue<TItem, TPriority> : IFixedSizePriorityQueue<TItem, TPriority>
- where TItem : GenericPriorityQueueNode<TPriority>
- where TPriority : IComparable<TPriority>
- {
- private int _numNodes;
- private TItem[] _nodes;
- private long _numNodesEverEnqueued;
-
- /// <summary>
- /// Instantiate a new Priority Queue
- /// </summary>
- /// <param name="maxNodes">The max nodes ever allowed to be enqueued (going over this will cause undefined behavior)</param>
- public GenericPriorityQueue(int maxNodes)
- {
-#if DEBUG
- if (maxNodes <= 0)
- {
- throw new InvalidOperationException("New queue size cannot be smaller than 1");
- }
-#endif
-
- _numNodes = 0;
- _nodes = new TItem[maxNodes + 1];
- _numNodesEverEnqueued = 0;
- }
-
- /// <summary>
- /// Returns the number of nodes in the queue.
- /// O(1)
- /// </summary>
- public int Count => _numNodes;
-
- /// <summary>
- /// Returns the maximum number of items that can be enqueued at once in this queue. Once you hit this number (ie. once Count == MaxSize),
- /// attempting to enqueue another item will cause undefined behavior. O(1)
- /// </summary>
- public int MaxSize => _nodes.Length - 1;
-
- /// <summary>
- /// Removes every node from the queue.
- /// O(n) (So, don't do this often!)
- /// </summary>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void Clear()
- {
- Array.Clear(_nodes, 1, _numNodes);
- _numNodes = 0;
- }
-
- /// <summary>
- /// Returns (in O(1)!) whether the given node is in the queue. O(1)
- /// </summary>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public bool Contains(TItem node)
- {
-#if DEBUG
- if (node == null)
- {
- throw new ArgumentNullException(nameof(node));
- }
- if (node.QueueIndex < 0 || node.QueueIndex >= _nodes.Length)
- {
- throw new InvalidOperationException("node.QueueIndex has been corrupted. Did you change it manually? Or add this node to another queue?");
- }
-#endif
-
- return (_nodes[node.QueueIndex] == node);
- }
-
- /// <summary>
- /// Enqueue a node to the priority queue. Lower values are placed in front. Ties are broken by first-in-first-out.
- /// If the queue is full, the result is undefined.
- /// If the node is already enqueued, the result is undefined.
- /// O(log n)
- /// </summary>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void Enqueue(TItem node, TPriority priority)
- {
-#if DEBUG
- if (node == null)
- {
- throw new ArgumentNullException(nameof(node));
- }
- if (_numNodes >= _nodes.Length - 1)
- {
- throw new InvalidOperationException("Queue is full - node cannot be added: " + node);
- }
- if (Contains(node))
- {
- throw new InvalidOperationException("Node is already enqueued: " + node);
- }
-#endif
-
- node.Priority = priority;
- _numNodes++;
- _nodes[_numNodes] = node;
- node.QueueIndex = _numNodes;
- node.InsertionIndex = _numNodesEverEnqueued++;
- CascadeUp(_nodes[_numNodes]);
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private void Swap(TItem node1, TItem node2)
- {
- //Swap the nodes
- _nodes[node1.QueueIndex] = node2;
- _nodes[node2.QueueIndex] = node1;
-
- //Swap their indicies
- int temp = node1.QueueIndex;
- node1.QueueIndex = node2.QueueIndex;
- node2.QueueIndex = temp;
- }
-
- //Performance appears to be slightly better when this is NOT inlined o_O
- private void CascadeUp(TItem node)
- {
- //aka Heapify-up
- int parent = node.QueueIndex / 2;
- while (parent >= 1)
- {
- var parentNode = _nodes[parent];
- if (HasHigherPriority(parentNode, node))
- break;
-
- //Node has lower priority value, so move it up the heap
- Swap(node, parentNode); //For some reason, this is faster with Swap() rather than (less..?) individual operations, like in CascadeDown()
-
- parent = node.QueueIndex / 2;
- }
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private void CascadeDown(TItem node)
- {
- //aka Heapify-down
- TItem newParent;
- int finalQueueIndex = node.QueueIndex;
- while (true)
- {
- newParent = node;
- int childLeftIndex = 2 * finalQueueIndex;
-
- //Check if the left-child is higher-priority than the current node
- if (childLeftIndex > _numNodes)
- {
- //This could be placed outside the loop, but then we'd have to check newParent != node twice
- node.QueueIndex = finalQueueIndex;
- _nodes[finalQueueIndex] = node;
- break;
- }
-
- var childLeft = _nodes[childLeftIndex];
- if (HasHigherPriority(childLeft, newParent))
- {
- newParent = childLeft;
- }
-
- //Check if the right-child is higher-priority than either the current node or the left child
- int childRightIndex = childLeftIndex + 1;
- if (childRightIndex <= _numNodes)
- {
- var childRight = _nodes[childRightIndex];
- if (HasHigherPriority(childRight, newParent))
- {
- newParent = childRight;
- }
- }
-
- //If either of the children has higher (smaller) priority, swap and continue cascading
- if (newParent != node)
- {
- //Move new parent to its new index. node will be moved once, at the end
- //Doing it this way is one less assignment operation than calling Swap()
- _nodes[finalQueueIndex] = newParent;
-
- int temp = newParent.QueueIndex;
- newParent.QueueIndex = finalQueueIndex;
- finalQueueIndex = temp;
- }
- else
- {
- //See note above
- node.QueueIndex = finalQueueIndex;
- _nodes[finalQueueIndex] = node;
- break;
- }
- }
- }
-
- /// <summary>
- /// Returns true if 'higher' has higher priority than 'lower', false otherwise.
- /// Note that calling HasHigherPriority(node, node) (ie. both arguments the same node) will return false
- /// </summary>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private bool HasHigherPriority(TItem higher, TItem lower)
- {
- var cmp = higher.Priority.CompareTo(lower.Priority);
- return (cmp < 0 || (cmp == 0 && higher.InsertionIndex < lower.InsertionIndex));
- }
-
- /// <summary>
- /// Removes the head of the queue (node with minimum priority; ties are broken by order of insertion), and returns it.
- /// If queue is empty, result is undefined
- /// O(log n)
- /// </summary>
- public bool TryDequeue(out TItem item)
- {
- if (_numNodes <= 0)
- {
- item = default(TItem);
- return false;
- }
-
-#if DEBUG
-
- if (!IsValidQueue())
- {
- throw new InvalidOperationException("Queue has been corrupted (Did you update a node priority manually instead of calling UpdatePriority()?" +
- "Or add the same node to two different queues?)");
- }
-#endif
-
- var returnMe = _nodes[1];
- Remove(returnMe);
- item = returnMe;
- return true;
- }
-
- /// <summary>
- /// Resize the queue so it can accept more nodes. All currently enqueued nodes are remain.
- /// Attempting to decrease the queue size to a size too small to hold the existing nodes results in undefined behavior
- /// O(n)
- /// </summary>
- public void Resize(int maxNodes)
- {
-#if DEBUG
- if (maxNodes <= 0)
- {
- throw new InvalidOperationException("Queue size cannot be smaller than 1");
- }
-
- if (maxNodes < _numNodes)
- {
- throw new InvalidOperationException("Called Resize(" + maxNodes + "), but current queue contains " + _numNodes + " nodes");
- }
-#endif
-
- TItem[] newArray = new TItem[maxNodes + 1];
- int highestIndexToCopy = Math.Min(maxNodes, _numNodes);
- for (int i = 1; i <= highestIndexToCopy; i++)
- {
- newArray[i] = _nodes[i];
- }
- _nodes = newArray;
- }
-
- /// <summary>
- /// Returns the head of the queue, without removing it (use Dequeue() for that).
- /// If the queue is empty, behavior is undefined.
- /// O(1)
- /// </summary>
- public TItem First
- {
- get
- {
-#if DEBUG
- if (_numNodes <= 0)
- {
- throw new InvalidOperationException("Cannot call .First on an empty queue");
- }
-#endif
-
- return _nodes[1];
- }
- }
-
- /// <summary>
- /// This method must be called on a node every time its priority changes while it is in the queue.
- /// <b>Forgetting to call this method will result in a corrupted queue!</b>
- /// Calling this method on a node not in the queue results in undefined behavior
- /// O(log n)
- /// </summary>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void UpdatePriority(TItem node, TPriority priority)
- {
-#if DEBUG
- if (node == null)
- {
- throw new ArgumentNullException(nameof(node));
- }
- if (!Contains(node))
- {
- throw new InvalidOperationException("Cannot call UpdatePriority() on a node which is not enqueued: " + node);
- }
-#endif
-
- node.Priority = priority;
- OnNodeUpdated(node);
- }
-
- private void OnNodeUpdated(TItem node)
- {
- //Bubble the updated node up or down as appropriate
- int parentIndex = node.QueueIndex / 2;
- var parentNode = _nodes[parentIndex];
-
- if (parentIndex > 0 && HasHigherPriority(node, parentNode))
- {
- CascadeUp(node);
- }
- else
- {
- //Note that CascadeDown will be called if parentNode == node (that is, node is the root)
- CascadeDown(node);
- }
- }
-
- /// <summary>
- /// Removes a node from the queue. The node does not need to be the head of the queue.
- /// If the node is not in the queue, the result is undefined. If unsure, check Contains() first
- /// O(log n)
- /// </summary>
- public void Remove(TItem node)
- {
-#if DEBUG
- if (node == null)
- {
- throw new ArgumentNullException(nameof(node));
- }
- if (!Contains(node))
- {
- throw new InvalidOperationException("Cannot call Remove() on a node which is not enqueued: " + node);
- }
-#endif
-
- //If the node is already the last node, we can remove it immediately
- if (node.QueueIndex == _numNodes)
- {
- _nodes[_numNodes] = null;
- _numNodes--;
- return;
- }
-
- //Swap the node with the last node
- var formerLastNode = _nodes[_numNodes];
- Swap(node, formerLastNode);
- _nodes[_numNodes] = null;
- _numNodes--;
-
- //Now bubble formerLastNode (which is no longer the last node) up or down as appropriate
- OnNodeUpdated(formerLastNode);
- }
-
- public IEnumerator<TItem> GetEnumerator()
- {
- for (int i = 1; i <= _numNodes; i++)
- yield return _nodes[i];
- }
-
- IEnumerator IEnumerable.GetEnumerator()
- {
- return GetEnumerator();
- }
-
- /// <summary>
- /// <b>Should not be called in production code.</b>
- /// Checks to make sure the queue is still in a valid state. Used for testing/debugging the queue.
- /// </summary>
- public bool IsValidQueue()
- {
- for (int i = 1; i < _nodes.Length; i++)
- {
- if (_nodes[i] != null)
- {
- int childLeftIndex = 2 * i;
- if (childLeftIndex < _nodes.Length && _nodes[childLeftIndex] != null && HasHigherPriority(_nodes[childLeftIndex], _nodes[i]))
- return false;
-
- int childRightIndex = childLeftIndex + 1;
- if (childRightIndex < _nodes.Length && _nodes[childRightIndex] != null && HasHigherPriority(_nodes[childRightIndex], _nodes[i]))
- return false;
- }
- }
- return true;
- }
- }
-}
diff --git a/MediaBrowser.Providers/Manager/GenericPriorityQueueNode.cs b/MediaBrowser.Providers/Manager/GenericPriorityQueueNode.cs
deleted file mode 100644
index b45ae0fd8..000000000
--- a/MediaBrowser.Providers/Manager/GenericPriorityQueueNode.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-namespace Priority_Queue
-{
- /// Credit: https://github.com/BlueRaja/High-Speed-Priority-Queue-for-C-Sharp
- public class GenericPriorityQueueNode<TPriority>
- {
- /// <summary>
- /// The Priority to insert this node at. Must be set BEFORE adding a node to the queue (ideally just once, in the node's constructor).
- /// Should not be manually edited once the node has been enqueued - use queue.UpdatePriority() instead
- /// </summary>
- public TPriority Priority { get; protected internal set; }
-
- /// <summary>
- /// Represents the current position in the queue
- /// </summary>
- public int QueueIndex { get; internal set; }
-
- /// <summary>
- /// Represents the order the node was inserted in
- /// </summary>
- public long InsertionIndex { get; internal set; }
- }
-}
diff --git a/MediaBrowser.Providers/Manager/IFixedSizePriorityQueue.cs b/MediaBrowser.Providers/Manager/IFixedSizePriorityQueue.cs
deleted file mode 100644
index 509d98e42..000000000
--- a/MediaBrowser.Providers/Manager/IFixedSizePriorityQueue.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-using System;
-
-namespace Priority_Queue
-{
- /// <summary>
- /// Credit: https://github.com/BlueRaja/High-Speed-Priority-Queue-for-C-Sharp
- /// A helper-interface only needed to make writing unit tests a bit easier (hence the 'internal' access modifier)
- /// </summary>
- internal interface IFixedSizePriorityQueue<TItem, in TPriority> : IPriorityQueue<TItem, TPriority>
- where TPriority : IComparable<TPriority>
- {
- /// <summary>
- /// Resize the queue so it can accept more nodes. All currently enqueued nodes are remain.
- /// Attempting to decrease the queue size to a size too small to hold the existing nodes results in undefined behavior
- /// </summary>
- void Resize(int maxNodes);
-
- /// <summary>
- /// Returns the maximum number of items that can be enqueued at once in this queue. Once you hit this number (ie. once Count == MaxSize),
- /// attempting to enqueue another item will cause undefined behavior.
- /// </summary>
- int MaxSize { get; }
- }
-}
diff --git a/MediaBrowser.Providers/Manager/IPriorityQueue.cs b/MediaBrowser.Providers/Manager/IPriorityQueue.cs
deleted file mode 100644
index dc319a7f8..000000000
--- a/MediaBrowser.Providers/Manager/IPriorityQueue.cs
+++ /dev/null
@@ -1,56 +0,0 @@
-using System;
-using System.Collections.Generic;
-
-namespace Priority_Queue
-{
- /// <summary>
- /// Credit: https://github.com/BlueRaja/High-Speed-Priority-Queue-for-C-Sharp
- /// The IPriorityQueue interface. This is mainly here for purists, and in case I decide to add more implementations later.
- /// For speed purposes, it is actually recommended that you *don't* access the priority queue through this interface, since the JIT can
- /// (theoretically?) optimize method calls from concrete-types slightly better.
- /// </summary>
- public interface IPriorityQueue<TItem, in TPriority> : IEnumerable<TItem>
- where TPriority : IComparable<TPriority>
- {
- /// <summary>
- /// Enqueue a node to the priority queue. Lower values are placed in front. Ties are broken by first-in-first-out.
- /// See implementation for how duplicates are handled.
- /// </summary>
- void Enqueue(TItem node, TPriority priority);
-
- /// <summary>
- /// Removes the head of the queue (node with minimum priority; ties are broken by order of insertion), and returns it.
- /// </summary>
- bool TryDequeue(out TItem item);
-
- /// <summary>
- /// Removes every node from the queue.
- /// </summary>
- void Clear();
-
- /// <summary>
- /// Returns whether the given node is in the queue.
- /// </summary>
- bool Contains(TItem node);
-
- /// <summary>
- /// Removes a node from the queue. The node does not need to be the head of the queue.
- /// </summary>
- void Remove(TItem node);
-
- /// <summary>
- /// Call this method to change the priority of a node.
- /// </summary>
- void UpdatePriority(TItem node, TPriority priority);
-
- /// <summary>
- /// Returns the head of the queue, without removing it (use Dequeue() for that).
- /// </summary>
- TItem First { get; }
-
- /// <summary>
- /// Returns the number of nodes in the queue.
- /// </summary>
- int Count { get; }
- }
-}
diff --git a/MediaBrowser.Providers/Manager/SimplePriorityQueue.cs b/MediaBrowser.Providers/Manager/SimplePriorityQueue.cs
deleted file mode 100644
index d064312cf..000000000
--- a/MediaBrowser.Providers/Manager/SimplePriorityQueue.cs
+++ /dev/null
@@ -1,247 +0,0 @@
-using System;
-using System.Collections;
-using System.Collections.Generic;
-
-namespace Priority_Queue
-{
- /// <summary>
- /// Credit: https://github.com/BlueRaja/High-Speed-Priority-Queue-for-C-Sharp
- /// A simplified priority queue implementation. Is stable, auto-resizes, and thread-safe, at the cost of being slightly slower than
- /// FastPriorityQueue
- /// </summary>
- /// <typeparam name="TItem">The type to enqueue</typeparam>
- /// <typeparam name="TPriority">The priority-type to use for nodes. Must extend IComparable&lt;TPriority&gt;</typeparam>
- public class SimplePriorityQueue<TItem, TPriority> : IPriorityQueue<TItem, TPriority>
- where TPriority : IComparable<TPriority>
- {
- private class SimpleNode : GenericPriorityQueueNode<TPriority>
- {
- public TItem Data { get; private set; }
-
- public SimpleNode(TItem data)
- {
- Data = data;
- }
- }
-
- private const int INITIAL_QUEUE_SIZE = 10;
- private readonly GenericPriorityQueue<SimpleNode, TPriority> _queue;
-
- public SimplePriorityQueue()
- {
- _queue = new GenericPriorityQueue<SimpleNode, TPriority>(INITIAL_QUEUE_SIZE);
- }
-
- /// <summary>
- /// Given an item of type T, returns the exist SimpleNode in the queue
- /// </summary>
- private SimpleNode GetExistingNode(TItem item)
- {
- var comparer = EqualityComparer<TItem>.Default;
- foreach (var node in _queue)
- {
- if (comparer.Equals(node.Data, item))
- {
- return node;
- }
- }
- throw new InvalidOperationException("Item cannot be found in queue: " + item);
- }
-
- /// <summary>
- /// Returns the number of nodes in the queue.
- /// O(1)
- /// </summary>
- public int Count
- {
- get
- {
- lock (_queue)
- {
- return _queue.Count;
- }
- }
- }
-
-
- /// <summary>
- /// Returns the head of the queue, without removing it (use Dequeue() for that).
- /// Throws an exception when the queue is empty.
- /// O(1)
- /// </summary>
- public TItem First
- {
- get
- {
- lock (_queue)
- {
- if (_queue.Count <= 0)
- {
- throw new InvalidOperationException("Cannot call .First on an empty queue");
- }
-
- SimpleNode first = _queue.First;
- return (first != null ? first.Data : default(TItem));
- }
- }
- }
-
- /// <summary>
- /// Removes every node from the queue.
- /// O(n)
- /// </summary>
- public void Clear()
- {
- lock (_queue)
- {
- _queue.Clear();
- }
- }
-
- /// <summary>
- /// Returns whether the given item is in the queue.
- /// O(n)
- /// </summary>
- public bool Contains(TItem item)
- {
- lock (_queue)
- {
- var comparer = EqualityComparer<TItem>.Default;
- foreach (var node in _queue)
- {
- if (comparer.Equals(node.Data, item))
- {
- return true;
- }
- }
- return false;
- }
- }
-
- /// <summary>
- /// Removes the head of the queue (node with minimum priority; ties are broken by order of insertion), and returns it.
- /// If queue is empty, throws an exception
- /// O(log n)
- /// </summary>
- public bool TryDequeue(out TItem item)
- {
- lock (_queue)
- {
- if (_queue.Count <= 0)
- {
- item = default(TItem);
- return false;
- }
-
- if (_queue.TryDequeue(out SimpleNode node))
- {
- item = node.Data;
- return true;
- }
-
- item = default(TItem);
- return false;
- }
- }
-
- /// <summary>
- /// Enqueue a node to the priority queue. Lower values are placed in front. Ties are broken by first-in-first-out.
- /// This queue automatically resizes itself, so there's no concern of the queue becoming 'full'.
- /// Duplicates are allowed.
- /// O(log n)
- /// </summary>
- public void Enqueue(TItem item, TPriority priority)
- {
- lock (_queue)
- {
- var node = new SimpleNode(item);
- if (_queue.Count == _queue.MaxSize)
- {
- _queue.Resize(_queue.MaxSize * 2 + 1);
- }
- _queue.Enqueue(node, priority);
- }
- }
-
- /// <summary>
- /// Removes an item from the queue. The item does not need to be the head of the queue.
- /// If the item is not in the queue, an exception is thrown. If unsure, check Contains() first.
- /// If multiple copies of the item are enqueued, only the first one is removed.
- /// O(n)
- /// </summary>
- public void Remove(TItem item)
- {
- lock (_queue)
- {
- try
- {
- _queue.Remove(GetExistingNode(item));
- }
- catch (InvalidOperationException ex)
- {
- throw new InvalidOperationException("Cannot call Remove() on a node which is not enqueued: " + item, ex);
- }
- }
- }
-
- /// <summary>
- /// Call this method to change the priority of an item.
- /// Calling this method on a item not in the queue will throw an exception.
- /// If the item is enqueued multiple times, only the first one will be updated.
- /// (If your requirements are complex enough that you need to enqueue the same item multiple times <i>and</i> be able
- /// to update all of them, please wrap your items in a wrapper class so they can be distinguished).
- /// O(n)
- /// </summary>
- public void UpdatePriority(TItem item, TPriority priority)
- {
- lock (_queue)
- {
- try
- {
- SimpleNode updateMe = GetExistingNode(item);
- _queue.UpdatePriority(updateMe, priority);
- }
- catch (InvalidOperationException ex)
- {
- throw new InvalidOperationException("Cannot call UpdatePriority() on a node which is not enqueued: " + item, ex);
- }
- }
- }
-
- public IEnumerator<TItem> GetEnumerator()
- {
- var queueData = new List<TItem>();
- lock (_queue)
- {
- //Copy to a separate list because we don't want to 'yield return' inside a lock
- foreach (var node in _queue)
- {
- queueData.Add(node.Data);
- }
- }
-
- return queueData.GetEnumerator();
- }
-
- IEnumerator IEnumerable.GetEnumerator()
- {
- return GetEnumerator();
- }
-
- public bool IsValidQueue()
- {
- lock (_queue)
- {
- return _queue.IsValidQueue();
- }
- }
- }
-
- /// <summary>
- /// A simplified priority queue implementation. Is stable, auto-resizes, and thread-safe, at the cost of being slightly slower than
- /// FastPriorityQueue
- /// This class is kept here for backwards compatibility. It's recommended you use Simple
- /// </summary>
- /// <typeparam name="TItem">The type to enqueue</typeparam>
- public class SimplePriorityQueue<TItem> : SimplePriorityQueue<TItem, float> { }
-}
diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj
index aadf31a7a..52a52efdc 100644
--- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj
+++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj
@@ -12,6 +12,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="2.2.0" />
+ <PackageReference Include="OptimizedPriorityQueue" Version="4.2.0" />
<PackageReference Include="PlaylistsNET" Version="1.0.2" />
<PackageReference Include="TvDbSharper" Version="2.0.0" />
</ItemGroup>
diff --git a/MediaBrowser.Providers/Movies/MovieDbImageProvider.cs b/MediaBrowser.Providers/Movies/MovieDbImageProvider.cs
index b9c5d7ce5..20b53d58a 100644
--- a/MediaBrowser.Providers/Movies/MovieDbImageProvider.cs
+++ b/MediaBrowser.Providers/Movies/MovieDbImageProvider.cs
@@ -16,7 +16,7 @@ using MediaBrowser.Model.Serialization;
namespace MediaBrowser.Providers.Movies
{
- class MovieDbImageProvider : IRemoteImageProvider, IHasOrder
+ public class MovieDbImageProvider : IRemoteImageProvider, IHasOrder
{
private readonly IJsonSerializer _jsonSerializer;
private readonly IHttpClient _httpClient;
diff --git a/MediaBrowser.Providers/Music/MusicVideoMetadataService.cs b/MediaBrowser.Providers/Music/MusicVideoMetadataService.cs
index 8b01ff342..93412306f 100644
--- a/MediaBrowser.Providers/Music/MusicVideoMetadataService.cs
+++ b/MediaBrowser.Providers/Music/MusicVideoMetadataService.cs
@@ -9,7 +9,7 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.Providers.Music
{
- class MusicVideoMetadataService : MetadataService<MusicVideo, MusicVideoInfo>
+ public class MusicVideoMetadataService : MetadataService<MusicVideo, MusicVideoInfo>
{
protected override void MergeData(MetadataResult<MusicVideo> source, MetadataResult<MusicVideo> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
diff --git a/MediaBrowser.Providers/Photos/PhotoAlbumMetadataService.cs b/MediaBrowser.Providers/Photos/PhotoAlbumMetadataService.cs
index fd969c7c2..993581cca 100644
--- a/MediaBrowser.Providers/Photos/PhotoAlbumMetadataService.cs
+++ b/MediaBrowser.Providers/Photos/PhotoAlbumMetadataService.cs
@@ -9,7 +9,7 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.Providers.Photos
{
- class PhotoAlbumMetadataService : MetadataService<PhotoAlbum, ItemLookupInfo>
+ public class PhotoAlbumMetadataService : MetadataService<PhotoAlbum, ItemLookupInfo>
{
protected override void MergeData(MetadataResult<PhotoAlbum> source, MetadataResult<PhotoAlbum> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
diff --git a/MediaBrowser.Providers/Photos/PhotoMetadataService.cs b/MediaBrowser.Providers/Photos/PhotoMetadataService.cs
index a430e1041..b739c5765 100644
--- a/MediaBrowser.Providers/Photos/PhotoMetadataService.cs
+++ b/MediaBrowser.Providers/Photos/PhotoMetadataService.cs
@@ -9,7 +9,7 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.Providers.Photos
{
- class PhotoMetadataService : MetadataService<Photo, ItemLookupInfo>
+ public class PhotoMetadataService : MetadataService<Photo, ItemLookupInfo>
{
protected override void MergeData(MetadataResult<Photo> source, MetadataResult<Photo> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
diff --git a/MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs b/MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs
index b28d2a548..30ce5c64c 100644
--- a/MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs
+++ b/MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs
@@ -11,7 +11,7 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.Providers.Playlists
{
- class PlaylistMetadataService : MetadataService<Playlist, ItemLookupInfo>
+ public class PlaylistMetadataService : MetadataService<Playlist, ItemLookupInfo>
{
protected override IList<BaseItem> GetChildrenForMetadataUpdates(Playlist item)
{
diff --git a/MediaBrowser.Providers/TV/Omdb/OmdbEpisodeProvider.cs b/MediaBrowser.Providers/TV/Omdb/OmdbEpisodeProvider.cs
index d0749405b..dee3030af 100644
--- a/MediaBrowser.Providers/TV/Omdb/OmdbEpisodeProvider.cs
+++ b/MediaBrowser.Providers/TV/Omdb/OmdbEpisodeProvider.cs
@@ -16,7 +16,7 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.Providers.TV.Omdb
{
- class OmdbEpisodeProvider :
+ public class OmdbEpisodeProvider :
IRemoteMetadataProvider<Episode, EpisodeInfo>,
IHasOrder
{
diff --git a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbEpisodeProvider.cs b/MediaBrowser.Providers/TV/TheMovieDb/MovieDbEpisodeProvider.cs
index 44590515e..3d7745085 100644
--- a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbEpisodeProvider.cs
+++ b/MediaBrowser.Providers/TV/TheMovieDb/MovieDbEpisodeProvider.cs
@@ -20,7 +20,7 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.Providers.TV.TheMovieDb
{
- class MovieDbEpisodeProvider :
+ public class MovieDbEpisodeProvider :
MovieDbProviderBase,
IRemoteMetadataProvider<Episode, EpisodeInfo>,
IHasOrder
diff --git a/MediaBrowser.WebDashboard/jellyfin-web b/MediaBrowser.WebDashboard/jellyfin-web
-Subproject c7ce1ac8eccd50f1bd759b30fbe60ea797ffe86
+Subproject b4842e325e9d7d708193b4a27060cfe4c978df5
diff --git a/MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs
index 21ee8f92f..a3e48d30d 100644
--- a/MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs
+++ b/MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs
@@ -13,7 +13,7 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.XbmcMetadata.Parsers
{
- class MovieNfoParser : BaseNfoParser<Video>
+ public class MovieNfoParser : BaseNfoParser<Video>
{
protected override bool SupportsUrlAfterClosingXmlTag => true;
diff --git a/SharedVersion.cs b/SharedVersion.cs
index 70c309674..294748b77 100644
--- a/SharedVersion.cs
+++ b/SharedVersion.cs
@@ -1,4 +1,4 @@
using System.Reflection;
-[assembly: AssemblyVersion("10.1.0")]
-[assembly: AssemblyFileVersion("10.1.0")]
+[assembly: AssemblyVersion("10.2.0")]
+[assembly: AssemblyFileVersion("10.2.0")]
diff --git a/deployment/debian-package-x64/pkg-src/changelog b/deployment/debian-package-x64/pkg-src/changelog
index 7f3f12b00..869dc4a5e 100644
--- a/deployment/debian-package-x64/pkg-src/changelog
+++ b/deployment/debian-package-x64/pkg-src/changelog
@@ -1,3 +1,111 @@
+jellyfin (10.2.0-2) unstable; urgency=medium
+
+ * jellyfin:
+ * PR452 Use EF Core for Activity database
+ * PR535 Clean up streambuilder
+ * PR655 Support trying local branches in submodule
+ * PR656 Do some logging in MediaInfoService
+ * PR657 Remove conditions that are always true/false
+ * PR661 Fix NullRef from progress report
+ * PR663 Use TagLibSharp Nuget package
+ * PR664 Revert "Fix segment_time_delta for ffmpeg 4.1"
+ * PR666 Add cross-platform build for arm64
+ * PR668 Return Audio objects from MusicAlbum.Tracks
+ * PR671 Set EnableRaisingEvents correctly
+ * PR672 Remove unconditional caching, modified since header and use ETags
+ * PR677 Fix arm32 Docker
+ * PR681 Fix Windows build script errors + pin ffmpeg to 4.0
+ * PR686 Disable some StyleCop warnings
+ * PR687 Fix some analyzer warnings
+ * PR689 Fix RPM package build for fedora
+ * PR702 Fix debug build on windows
+ * PR706 Make another docker layer reusable
+ * PR709 Fix always null expressions
+ * PR710 Fix a spelling mistake
+ * PR711 Remove remnants of system events
+ * PR713 Fix empty statement in DidlBuilder.cs
+ * PR716 Remove more compile time warnings
+ * PR721 Change image dimentions from double to int
+ * PR723 Minor improvements to db code
+ * PR724 Move Skia back into it's own project
+ * PR726 Clean up IFileSystem wrappers around stdlib.
+ * PR727 Change default aspect ratio to 2/3 from 0
+ * PR728 Use ffmpeg from jrottenberg/ffmpeg
+ * PR732 Reworked LocalizationManager to load data async
+ * PR733 Remove unused function
+ * PR734 Fix more analyzer warnings
+ * PR736 Start startup tasks async
+ * PR737 Add AssemblyInfo for Jellyfin.Drawing.Skia
+ * PR739 Change multi version logic for movies
+ * PR740 Remove code for pre-installed plugins & properly check if file exists
+ * PR756 Make cache dir configurable
+ * PR757 Fix default aspect ratio
+ * PR758 Add password field to initial setup
+ * PR764 Remove dead code, made some functions properly async
+ * PR769 Fix conditions where the ! was swallowed in #726
+ * PR774 reimplement support for plugin repository
+ * PR782 Remove commented file MediaBrowser.LocalMetadata.Savers.PersonXmlSaver
+ * PR783 Update builds to use #749 and #756
+ * PR788 Fix more warnings
+ * PR794 Remove MoreLINQ
+ * PR797 Fix all warnings
+ * PR798 Cleanup around the api endpoints
+ * PR800 Add CentOS and update rpm spec for the cachedir option
+ * PR802 Fix build error
+ * PR804 Handle new option parser properly
+ * PR805 Add weblate translation status to README
+ * PR807 Fix restart script in OS packages
+ * PR810 Fix loading of rating files
+ * PR812 Fix up the explicit docs links in the README
+ * PR819 Some small changes in Device.cs and DidlBuilder.cs
+ * PR822 Complete rename ImageSize -> ImageDimensions
+ * PR824 Improved Docker pkgbuild
+ * PR831 Move some arrays to generics
+ * PR833 Add await to GetCountries in LocalizationService
+ * PR834 Add donation badge and reorganize badges
+ * PR838 Quick style fix
+ * PR840 Fix more warnings
+ * PR841 Fix OC badge to all and add forum badge
+ * PR842 Use VAAPI-enabled ffmpeg
+ * PR852 Use SQLitePCL.pretty.netstandard on NuGet
+ * PR853 Fix poor handling of cache directories
+ * PR864: Add support for ZIP plugin archives
+ * PR868: Fix audio streaming via BaseProgressiveStreamingService
+ * PR869: Remove DLL support and require all packages/plugins to be zip archives
+ * PR872: Fix potential NullReferenceException
+ * PR890: Drop ETag and use Last-Modified header
+ * PR892: Add jellyfin-ffmpeg and versioning to package deps
+ * PR899: DLNA: Fix race condition leading to missing device names
+ * PR901: Properly dispose HttpWebResponse when the request failed to avoid 'too many open files'
+ * PR909: Fix docker arm builds
+ * PR910: Enhance Dockerfiles
+ * PR911: Checkout submodules in Docker Hub hook
+ * jellyfin-web:
+ * PR51 remove more code for sync and camera roll
+ * PR56 Use English for fallback translations and clean up language files
+ * PR58 Css slider fixes
+ * PR62 remove BOM markers
+ * PR65 Fix profile image not being shown on profile page
+ * PR73 Dev sync
+ * PR74 Add download menu option to media items
+ * PR75 User profile fixes
+ * PR76 Fix syntax error caused by deminification
+ * PR79 Remove unused Connect related from the frontend
+ * PR80 Remove games
+ * PR92 Added frontend support for a password field on setup
+ * PR94 Update british strings
+ * PR95 add display language option back
+ * PR112 Removed seasonal theme support
+ * PR116 Consolidate all strings into a single file per language
+ * PR117 Fix volume slider behavior
+ * PR118 Enable and fix PiP for Safari
+ * PR119 Make the toggle track visible on all themes
+ * PR121 Fix syntax error in site.js
+ * PR127 Change sharedcomponents module to core
+ * PR135 Make sure fallback culture is always available
+
+ -- Jellyfin Packaging Team <packaging@jellyfin.org> Fri, 15 Feb 2019 20:51:25 -0500
+
jellyfin (10.1.0-1) unstable; urgency=medium
* jellyfin:
diff --git a/deployment/debian-package-x64/pkg-src/control b/deployment/debian-package-x64/pkg-src/control
index 74bebeaf1..88d10438b 100644
--- a/deployment/debian-package-x64/pkg-src/control
+++ b/deployment/debian-package-x64/pkg-src/control
@@ -18,6 +18,11 @@ Replaces: mediabrowser, emby, emby-server-beta, jellyfin-dev, emby-server
Breaks: mediabrowser, emby, emby-server-beta, jellyfin-dev, emby-server
Conflicts: mediabrowser, emby, emby-server-beta, jellyfin-dev, emby-server
Architecture: any
-Depends: at, libsqlite3-0, ffmpeg, libfontconfig1, libfreetype6, libssl1.0.0 | libssl1.0.2
+Depends: at,
+ libsqlite3-0,
+ ffmpeg (<7:4.1) | jellyfin-ffmpeg,
+ libfontconfig1,
+ libfreetype6,
+ libssl1.0.0 | libssl1.0.2
Description: Jellyfin is a home media server.
It is built on top of other popular open source technologies such as Service Stack, jQuery, jQuery mobile, and Mono. It features a REST-based api with built-in documentation to facilitate client development. We also have client libraries for our api to enable rapid development.
diff --git a/deployment/fedora-package-x64/create_tarball.sh b/deployment/fedora-package-x64/create_tarball.sh
new file mode 100755
index 000000000..e8301c989
--- /dev/null
+++ b/deployment/fedora-package-x64/create_tarball.sh
@@ -0,0 +1,55 @@
+#!/usr/bin/env bash
+
+# shellcheck disable=SC1091
+source ../common.build.sh
+
+WORKDIR="$( pwd )"
+VERSION="$( sed -ne '/^Version:/s/.* *//p' "${WORKDIR}"/pkg-src/jellyfin.spec )"
+
+package_temporary_dir="${WORKDIR}/pkg-dist-tmp"
+pkg_src_dir="${WORKDIR}/pkg-src"
+
+GNU_TAR=1
+echo "Bundling all sources for RPM build."
+tar \
+--transform "s,^\.,jellyfin-${VERSION}," \
+--exclude='.git*' \
+--exclude='**/.git' \
+--exclude='**/.hg' \
+--exclude='**/.vs' \
+--exclude='**/.vscode' \
+--exclude='deployment' \
+--exclude='**/bin' \
+--exclude='**/obj' \
+--exclude='**/.nuget' \
+--exclude='*.deb' \
+--exclude='*.rpm' \
+-czf "$pkg_src_dir/jellyfin-${VERSION}.tar.gz" \
+-C "../.." ./ || GNU_TAR=0
+
+if [ $GNU_TAR -eq 0 ]; then
+ echo "The installed tar binary did not support --transform. Using workaround."
+ mkdir -p "${package_temporary_dir}/jellyfin"{,-"${VERSION}"}
+ # Not GNU tar
+ tar \
+ --exclude='.git*' \
+ --exclude='**/.git' \
+ --exclude='**/.hg' \
+ --exclude='**/.vs' \
+ --exclude='**/.vscode' \
+ --exclude='deployment' \
+ --exclude='**/bin' \
+ --exclude='**/obj' \
+ --exclude='**/.nuget' \
+ --exclude='*.deb' \
+ --exclude='*.rpm' \
+ -zcf \
+ "${package_temporary_dir}/jellyfin/jellyfin-${VERSION}.tar.gz" \
+ -C "../.." ./
+ echo "Extracting filtered package."
+ tar -xzf "${package_temporary_dir}/jellyfin/jellyfin-${VERSION}.tar.gz" -C "${package_temporary_dir}/jellyfin-${VERSION}"
+ echo "Removing filtered package."
+ rm -f "${package_temporary_dir}/jellyfin/jellyfin-${VERSION}.tar.gz"
+ echo "Repackaging package into final tarball."
+ tar -czf "${pkg_src_dir}/jellyfin-${VERSION}.tar.gz" -C "${package_temporary_dir}" "jellyfin-${VERSION}"
+fi
diff --git a/deployment/fedora-package-x64/package.sh b/deployment/fedora-package-x64/package.sh
index 74586417d..eed29aef3 100755
--- a/deployment/fedora-package-x64/package.sh
+++ b/deployment/fedora-package-x64/package.sh
@@ -21,52 +21,7 @@ else
docker_sudo=""
fi
-# Create RPM source archive
-GNU_TAR=1
-mkdir -p "${package_temporary_dir}"
-echo "Bundling all sources for RPM build."
-tar \
---transform "s,^\.,jellyfin-${VERSION}," \
---exclude='.git*' \
---exclude='**/.git' \
---exclude='**/.hg' \
---exclude='**/.vs' \
---exclude='**/.vscode' \
---exclude='deployment' \
---exclude='**/bin' \
---exclude='**/obj' \
---exclude='**/.nuget' \
---exclude='*.deb' \
---exclude='*.rpm' \
--czf "${pkg_src_dir}/jellyfin-${VERSION}.tar.gz" \
--C "../.." ./ || GNU_TAR=0
-
-if [ $GNU_TAR -eq 0 ]; then
- echo "The installed tar binary did not support --transform. Using workaround."
- mkdir -p "${package_temporary_dir}/jellyfin"
- # Not GNU tar
- tar \
- --exclude='.git*' \
- --exclude='**/.git' \
- --exclude='**/.hg' \
- --exclude='**/.vs' \
- --exclude='**/.vscode' \
- --exclude='deployment' \
- --exclude='**/bin' \
- --exclude='**/obj' \
- --exclude='**/.nuget' \
- --exclude='*.deb' \
- --exclude='*.rpm' \
- -zcf \
- "${package_temporary_dir}/jellyfin/jellyfin-${VERSION}.tar.gz" \
- -C "../.." ./
- echo "Extracting filtered package."
- tar -xzf "${package_temporary_dir}/jellyfin/jellyfin-${VERSION}.tar.gz" -C "${package_temporary_dir}/jellyfin-${VERSION}"
- echo "Removing filtered package."
- rm -f "${package_temporary_dir}/jellyfin/jellyfin-${VERSION}.tar.gz"
- echo "Repackaging package into final tarball."
- tar -czf "${pkg_src_dir}/jellyfin-${VERSION}.tar.gz" -C "${package_temporary_dir}" "jellyfin-${VERSION}"
-fi
+./create_tarball.sh
# Set up the build environment Docker image
${docker_sudo} docker build ../.. -t "${image_name}" -f ./Dockerfile
diff --git a/deployment/fedora-package-x64/pkg-src/jellyfin.spec b/deployment/fedora-package-x64/pkg-src/jellyfin.spec
index 851c40044..75821cb17 100644
--- a/deployment/fedora-package-x64/pkg-src/jellyfin.spec
+++ b/deployment/fedora-package-x64/pkg-src/jellyfin.spec
@@ -7,8 +7,8 @@
%endif
Name: jellyfin
-Version: 10.1.0
-Release: 1%{?dist}
+Version: 10.2.0
+Release: 2%{?dist}
Summary: The Free Software Media Browser
License: GPLv2
URL: https://jellyfin.media
@@ -140,6 +140,110 @@ fi
%systemd_postun_with_restart jellyfin.service
%changelog
+* Fri Feb 15 2019 Jellyfin Packaging Team <packaging@jellyfin.org>
+- jellyfin:
+- PR452 Use EF Core for Activity database
+- PR535 Clean up streambuilder
+- PR655 Support trying local branches in submodule
+- PR656 Do some logging in MediaInfoService
+- PR657 Remove conditions that are always true/false
+- PR661 Fix NullRef from progress report
+- PR663 Use TagLibSharp Nuget package
+- PR664 Revert "Fix segment_time_delta for ffmpeg 4.1"
+- PR666 Add cross-platform build for arm64
+- PR668 Return Audio objects from MusicAlbum.Tracks
+- PR671 Set EnableRaisingEvents correctly
+- PR672 Remove unconditional caching, modified since header and use ETags
+- PR677 Fix arm32 Docker
+- PR681 Fix Windows build script errors + pin ffmpeg to 4.0
+- PR686 Disable some StyleCop warnings
+- PR687 Fix some analyzer warnings
+- PR689 Fix RPM package build for fedora
+- PR702 Fix debug build on windows
+- PR706 Make another docker layer reusable
+- PR709 Fix always null expressions
+- PR710 Fix a spelling mistake
+- PR711 Remove remnants of system events
+- PR713 Fix empty statement in DidlBuilder.cs
+- PR716 Remove more compile time warnings
+- PR721 Change image dimentions from double to int
+- PR723 Minor improvements to db code
+- PR724 Move Skia back into it's own project
+- PR726 Clean up IFileSystem wrappers around stdlib.
+- PR727 Change default aspect ratio to 2/3 from 0
+- PR728 Use ffmpeg from jrottenberg/ffmpeg
+- PR732 Reworked LocalizationManager to load data async
+- PR733 Remove unused function
+- PR734 Fix more analyzer warnings
+- PR736 Start startup tasks async
+- PR737 Add AssemblyInfo for Jellyfin.Drawing.Skia
+- PR739 Change multi version logic for movies
+- PR740 Remove code for pre-installed plugins & properly check if file exists
+- PR756 Make cache dir configurable
+- PR757 Fix default aspect ratio
+- PR758 Add password field to initial setup
+- PR764 Remove dead code, made some functions properly async
+- PR769 Fix conditions where the ! was swallowed in #726
+- PR774 reimplement support for plugin repository
+- PR782 Remove commented file MediaBrowser.LocalMetadata.Savers.PersonXmlSaver
+- PR783 Update builds to use #749 and #756
+- PR788 Fix more warnings
+- PR794 Remove MoreLINQ
+- PR797 Fix all warnings
+- PR798 Cleanup around the api endpoints
+- PR800 Add CentOS and update rpm spec for the cachedir option
+- PR802 Fix build error
+- PR804 Handle new option parser properly
+- PR805 Add weblate translation status to README
+- PR807 Fix restart script in OS packages
+- PR810 Fix loading of rating files
+- PR812 Fix up the explicit docs links in the README
+- PR819 Some small changes in Device.cs and DidlBuilder.cs
+- PR822 Complete rename ImageSize -> ImageDimensions
+- PR824 Improved Docker pkgbuild
+- PR831 Move some arrays to generics
+- PR833 Add await to GetCountries in LocalizationService
+- PR834 Add donation badge and reorganize badges
+- PR838 Quick style fix
+- PR840 Fix more warnings
+- PR841 Fix OC badge to all and add forum badge
+- PR842 Use VAAPI-enabled ffmpeg
+- PR852 Use SQLitePCL.pretty.netstandard on NuGet
+- PR853 Fix poor handling of cache directories
+- PR864 Add support for ZIP plugin archives
+- PR868 Fix audio streaming via BaseProgressiveStreamingService
+- PR869 Remove DLL support and require all packages/plugins to be zip archives
+- PR872 Fix potential NullReferenceException
+- PR899: DLNA: Fix race condition leading to missing device names
+- PR890 Drop ETag and use Last-Modified header
+- PR892: Add jellyfin-ffmpeg and versioning to package deps
+- PR901: Properly dispose HttpWebResponse when the request failed to avoid 'too many open files'
+- PR909: Fix docker arm builds
+- PR910: Enhance Dockerfiles
+- PR911: Checkout submodules in Docker Hub hook
+- jellyfin-web:
+- PR51 remove more code for sync and camera roll
+- PR56 Use English for fallback translations and clean up language files
+- PR58 Css slider fixes
+- PR62 remove BOM markers
+- PR65 Fix profile image not being shown on profile page
+- PR73 Dev sync
+- PR74 Add download menu option to media items
+- PR75 User profile fixes
+- PR76 Fix syntax error caused by deminification
+- PR79 Remove unused Connect related from the frontend
+- PR80 Remove games
+- PR92 Added frontend support for a password field on setup
+- PR94 Update british strings
+- PR95 add display language option back
+- PR112 Removed seasonal theme support
+- PR116 Consolidate all strings into a single file per language
+- PR117 Fix volume slider behavior
+- PR118 Enable and fix PiP for Safari
+- PR119 Make the toggle track visible on all themes
+- PR121 Fix syntax error in site.js
+- PR127 Change sharedcomponents module to core
+- PR135 Make sure fallback culture is always available
* Sun Jan 20 2019 Jellyfin Packaging Team <packaging@jellyfin.org>
- jellyfin:
- PR335 Build scripts and build system consolidation.
diff --git a/deployment/win-x64/package.sh b/deployment/win-x64/package.sh
index befddb2e7..d21e3b532 100755
--- a/deployment/win-x64/package.sh
+++ b/deployment/win-x64/package.sh
@@ -1,4 +1,5 @@
#!/usr/bin/env bash
+set -x
package_win64() (
local NSSM_VERSION="nssm-2.24-101-g897c7ad"
local NSSM_URL="https://nssm.cc/ci/${NSSM_VERSION}.zip"
@@ -15,7 +16,7 @@ package_win64() (
wget ${NSSM_URL} -O ${TEMP_DIR}/nssm.zip
wget ${FFMPEG_URL} -O ${TEMP_DIR}/ffmpeg.zip
unzip ${TEMP_DIR}/nssm.zip -d $TEMP_DIR
- cp ${TEMP_DIR}/${NSSM_VERSION}}/win64/nssm.exe ${OUTPUT_DIR}/nssm.exe
+ cp ${TEMP_DIR}/${NSSM_VERSION}/win64/nssm.exe ${OUTPUT_DIR}/nssm.exe
unzip ${TEMP_DIR}/ffmpeg.zip -d $TEMP_DIR
cp ${TEMP_DIR}/${FFMPEG_VERSION}/bin/ffmpeg.exe ${OUTPUT_DIR}/ffmpeg.exe
cp ${TEMP_DIR}/${FFMPEG_VERSION}/bin/ffprobe.exe ${OUTPUT_DIR}/ffprobe.exe
diff --git a/hooks/pre_build b/hooks/pre_build
new file mode 100644
index 000000000..2fd6136c5
--- /dev/null
+++ b/hooks/pre_build
@@ -0,0 +1,6 @@
+#!/bin/bash
+git submodule update --init --recursive
+
+# Register qemu-*-static for all supported processors except the
+# current one, but also remove all registered binfmt_misc before
+docker run --rm --privileged multiarch/qemu-user-static:register --reset
diff --git a/jellyfin.ruleset b/jellyfin.ruleset
index 0f8c9aa02..4381349ca 100644
--- a/jellyfin.ruleset
+++ b/jellyfin.ruleset
@@ -3,11 +3,19 @@
<Rules AnalyzerId="StyleCop.Analyzers" RuleNamespace="StyleCop.Analyzers">
<!-- disable warning SA1101: Prefix local calls with 'this.' -->
<Rule Id="SA1101" Action="None" />
+ <!-- disable warning SA1130: Use lambda syntax -->
+ <Rule Id="SA1130" Action="None" />
<!-- disable warning SA1200: 'using' directive must appear within a namespace declaration -->
<Rule Id="SA1200" Action="None" />
<!-- disable warning SA1309: Fields must not begin with an underscore -->
<Rule Id="SA1309" Action="None" />
+ <!-- disable warning SA1512: Single-line comments must not be followed by blank line -->
+ <Rule Id="SA1512" Action="None" />
<!-- disable warning SA1633: The file header is missing or not located at the top of the file -->
<Rule Id="SA1633" Action="None" />
</Rules>
+ <Rules AnalyzerId="Microsoft.CodeAnalysis.FxCopAnalyzers" RuleNamespace="Microsoft.Design">
+ <!-- disable warning CA1054: Change the type of parameter url from string to System.Uri -->
+ <Rule Id="CA1054" Action="None" />
+ </Rules>
</RuleSet>