diff --git a/.ci/azure-pipelines-compat.yml b/.ci/azure-pipelines-abi.yml
similarity index 69%
rename from .ci/azure-pipelines-compat.yml
rename to .ci/azure-pipelines-abi.yml
index de60d2ccb1..635aa759ca 100644
--- a/.ci/azure-pipelines-compat.yml
+++ b/.ci/azure-pipelines-abi.yml
@@ -1,13 +1,13 @@
parameters:
- - name: Packages
- type: object
- default: {}
- - name: LinuxImage
- type: string
- default: "ubuntu-latest"
- - name: DotNetSdkVersion
- type: string
- default: 3.1.100
+- name: Packages
+ type: object
+ default: {}
+- name: LinuxImage
+ type: string
+ default: "ubuntu-latest"
+- name: DotNetSdkVersion
+ type: string
+ default: 3.1.100
jobs:
- job: CompatibilityCheck
@@ -33,6 +33,13 @@ jobs:
packageType: sdk
version: ${{ parameters.DotNetSdkVersion }}
+ - task: DotNetCoreCLI@2
+ displayName: 'Install ABI CompatibilityChecker tool'
+ inputs:
+ command: custom
+ custom: tool
+ arguments: 'update compatibilitychecker -g'
+
- task: DownloadPipelineArtifact@2
displayName: "Download New Assembly Build Artifact"
inputs:
@@ -72,25 +79,11 @@ jobs:
overWrite: true
flattenFolders: true
- - task: DownloadGitHubRelease@0
- displayName: "Download ABI Compatibility Check Tool"
- inputs:
- connection: Jellyfin Release Download
- userRepository: EraYaN/dotnet-compatibility
- defaultVersionType: "latest"
- itemPattern: "**-ci.zip"
- downloadPath: "$(System.ArtifactsDirectory)"
-
- - task: ExtractFiles@1
- displayName: "Extract ABI Compatibility Check Tool"
- inputs:
- archiveFilePatterns: "$(System.ArtifactsDirectory)/*-ci.zip"
- destinationFolder: $(System.ArtifactsDirectory)/tools
- cleanDestinationFolder: true
-
# The `--warnings-only` switch will swallow the return code and not emit any errors.
- - task: CmdLine@2
- displayName: "Execute ABI Compatibility Check Tool"
+ - task: DotNetCoreCLI@2
+ displayName: 'Execute ABI Compatibility Check Tool'
inputs:
- script: "dotnet tools/CompatibilityCheckerCLI.dll current-release/$(AssemblyFileName) new-release/$(AssemblyFileName) --azure-pipelines --warnings-only"
+ command: custom
+ custom: compat
+ arguments: 'current-release/$(AssemblyFileName) new-release/$(AssemblyFileName) --azure-pipelines --warnings-only'
workingDirectory: $(System.ArtifactsDirectory)
diff --git a/.ci/azure-pipelines-main.yml b/.ci/azure-pipelines-main.yml
index d155624abb..2a1c0e6f22 100644
--- a/.ci/azure-pipelines-main.yml
+++ b/.ci/azure-pipelines-main.yml
@@ -1,6 +1,6 @@
parameters:
- LinuxImage: "ubuntu-latest"
- RestoreBuildProjects: "Jellyfin.Server/Jellyfin.Server.csproj"
+ LinuxImage: 'ubuntu-latest'
+ RestoreBuildProjects: 'Jellyfin.Server/Jellyfin.Server.csproj'
DotNetSdkVersion: 3.1.100
jobs:
@@ -13,88 +13,81 @@ jobs:
Debug:
BuildConfiguration: Debug
pool:
- vmImage: "${{ parameters.LinuxImage }}"
+ vmImage: '${{ parameters.LinuxImage }}'
steps:
- checkout: self
clean: true
submodules: true
persistCredentials: true
- - task: CmdLine@2
- displayName: "Clone Web Branch"
- condition: and(succeeded(), or(contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')), eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI', 'BuildCompletion'))
+ - task: DownloadPipelineArtifact@2
+ displayName: 'Download Web Branch'
+ condition: in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI', 'BuildCompletion')
inputs:
- script: "git clone --single-branch --branch $(Build.SourceBranchName) --depth=1 https://github.com/jellyfin/jellyfin-web.git $(Agent.TempDirectory)/jellyfin-web"
+ path: '$(Agent.TempDirectory)'
+ artifact: 'jellyfin-web-production'
+ source: 'specific'
+ project: 'jellyfin'
+ pipeline: 'Jellyfin Web'
+ runBranch: variables['Build.SourceBranch']
- - task: CmdLine@2
- displayName: "Clone Web Target"
- condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master')), eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest'))
+ - task: DownloadPipelineArtifact@2
+ displayName: 'Download Web Target'
+ condition: eq(variables['Build.Reason'], 'PullRequest')
inputs:
- script: "git clone --single-branch --branch $(System.PullRequest.TargetBranch) --depth 1 https://github.com/jellyfin/jellyfin-web.git $(Agent.TempDirectory)/jellyfin-web"
+ path: '$(Agent.TempDirectory)'
+ artifact: 'jellyfin-web-production'
+ source: 'specific'
+ project: 'jellyfin'
+ pipeline: 'Jellyfin Web'
+ runBranch: variables['System.PullRequest.TargetBranch']
- - task: NodeTool@0
- displayName: "Install Node"
- condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master'), contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')), eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI', 'BuildCompletion'))
+ - task: ExtractFiles@1
+ displayName: 'Extract Web Client'
inputs:
- versionSpec: "12.x"
-
- - task: CmdLine@2
- displayName: "Build Web Client"
- condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master'), contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')), eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI', 'BuildCompletion'))
- inputs:
- script: yarn install
- workingDirectory: $(Agent.TempDirectory)/jellyfin-web
-
- - task: CopyFiles@2
- displayName: "Copy Web Client"
- condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master'), contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')), eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI', 'BuildCompletion'))
- inputs:
- sourceFolder: $(Agent.TempDirectory)/jellyfin-web/dist
- contents: "**"
- targetFolder: $(Build.SourcesDirectory)/MediaBrowser.WebDashboard/jellyfin-web
- cleanTargetFolder: true
- overWrite: true
- flattenFolders: false
+ archiveFilePatterns: '$(Agent.TempDirectory)/*.zip'
+ destinationFolder: '$(Build.SourcesDirectory)/MediaBrowser.WebDashboard'
+ cleanDestinationFolder: false
- task: UseDotNet@2
- displayName: "Update DotNet"
+ displayName: 'Update DotNet'
inputs:
packageType: sdk
version: ${{ parameters.DotNetSdkVersion }}
- task: DotNetCoreCLI@2
- displayName: "Publish Server"
+ displayName: 'Publish Server'
inputs:
command: publish
publishWebProjects: false
- projects: "${{ parameters.RestoreBuildProjects }}"
- arguments: "--configuration $(BuildConfiguration) --output $(Build.ArtifactStagingDirectory)"
+ projects: '${{ parameters.RestoreBuildProjects }}'
+ arguments: '--configuration $(BuildConfiguration) --output $(Build.ArtifactStagingDirectory)'
zipAfterPublish: false
- task: PublishPipelineArtifact@0
- displayName: "Publish Artifact Naming"
+ displayName: 'Publish Artifact Naming'
condition: and(succeeded(), eq(variables['BuildConfiguration'], 'Release'))
inputs:
- targetPath: "$(build.ArtifactStagingDirectory)/Jellyfin.Server/Emby.Naming.dll"
- artifactName: "Jellyfin.Naming"
+ targetPath: '$(build.ArtifactStagingDirectory)/Jellyfin.Server/Emby.Naming.dll'
+ artifactName: 'Jellyfin.Naming'
- task: PublishPipelineArtifact@0
- displayName: "Publish Artifact Controller"
+ displayName: 'Publish Artifact Controller'
condition: and(succeeded(), eq(variables['BuildConfiguration'], 'Release'))
inputs:
- targetPath: "$(build.ArtifactStagingDirectory)/Jellyfin.Server/MediaBrowser.Controller.dll"
- artifactName: "Jellyfin.Controller"
+ targetPath: '$(build.ArtifactStagingDirectory)/Jellyfin.Server/MediaBrowser.Controller.dll'
+ artifactName: 'Jellyfin.Controller'
- task: PublishPipelineArtifact@0
- displayName: "Publish Artifact Model"
+ displayName: 'Publish Artifact Model'
condition: and(succeeded(), eq(variables['BuildConfiguration'], 'Release'))
inputs:
- targetPath: "$(build.ArtifactStagingDirectory)/Jellyfin.Server/MediaBrowser.Model.dll"
- artifactName: "Jellyfin.Model"
+ targetPath: '$(build.ArtifactStagingDirectory)/Jellyfin.Server/MediaBrowser.Model.dll'
+ artifactName: 'Jellyfin.Model'
- task: PublishPipelineArtifact@0
- displayName: "Publish Artifact Common"
+ displayName: 'Publish Artifact Common'
condition: and(succeeded(), eq(variables['BuildConfiguration'], 'Release'))
inputs:
- targetPath: "$(build.ArtifactStagingDirectory)/Jellyfin.Server/MediaBrowser.Common.dll"
- artifactName: "Jellyfin.Common"
+ targetPath: '$(build.ArtifactStagingDirectory)/Jellyfin.Server/MediaBrowser.Common.dll'
+ artifactName: 'Jellyfin.Common'
diff --git a/.ci/azure-pipelines-test.yml b/.ci/azure-pipelines-test.yml
index 4455632e15..a3c7f8526c 100644
--- a/.ci/azure-pipelines-test.yml
+++ b/.ci/azure-pipelines-test.yml
@@ -1,26 +1,25 @@
parameters:
- - name: ImageNames
- type: object
- default:
- Linux: "ubuntu-latest"
- Windows: "windows-latest"
- macOS: "macos-latest"
- - name: TestProjects
- type: string
- default: "tests/**/*Tests.csproj"
- - name: DotNetSdkVersion
- type: string
- default: 3.1.100
+- name: ImageNames
+ type: object
+ default:
+ Linux: "ubuntu-latest"
+ Windows: "windows-latest"
+ macOS: "macos-latest"
+- name: TestProjects
+ type: string
+ default: "tests/**/*Tests.csproj"
+- name: DotNetSdkVersion
+ type: string
+ default: 3.1.100
jobs:
- - job: MainTest
- displayName: Main Test
+ - job: Test
+ displayName: Test
strategy:
matrix:
${{ each imageName in parameters.ImageNames }}:
${{ imageName.key }}:
ImageName: ${{ imageName.value }}
- maxParallel: 3
pool:
vmImage: "$(ImageName)"
steps:
@@ -29,14 +28,31 @@ jobs:
submodules: true
persistCredentials: false
+ # This is required for the SonarCloud analyzer
+ - task: UseDotNet@2
+ displayName: "Install .NET Core SDK 2.1"
+ condition: eq(variables['ImageName'], 'ubuntu-latest')
+ inputs:
+ packageType: sdk
+ version: '2.1.805'
+
- task: UseDotNet@2
displayName: "Update DotNet"
inputs:
packageType: sdk
version: ${{ parameters.DotNetSdkVersion }}
+ - task: SonarCloudPrepare@1
+ displayName: 'Prepare analysis on SonarCloud'
+ condition: eq(variables['ImageName'], 'ubuntu-latest')
+ enabled: false
+ inputs:
+ SonarCloud: 'Sonarcloud for Jellyfin'
+ organization: 'jellyfin'
+ projectKey: 'jellyfin_jellyfin'
+
- task: DotNetCoreCLI@2
- displayName: Run .NET Core CLI tests
+ displayName: 'Run CLI Tests'
inputs:
command: "test"
projects: ${{ parameters.TestProjects }}
@@ -45,9 +61,20 @@ jobs:
testRunTitle: $(Agent.JobName)
workingDirectory: "$(Build.SourcesDirectory)"
+ - task: SonarCloudAnalyze@1
+ displayName: 'Run Code Analysis'
+ condition: eq(variables['ImageName'], 'ubuntu-latest')
+ enabled: false
+
+ - task: SonarCloudPublish@1
+ displayName: 'Publish Quality Gate Result'
+ condition: eq(variables['ImageName'], 'ubuntu-latest')
+ enabled: false
+
- task: Palmmedia.reportgenerator.reportgenerator-build-release-task.reportgenerator@4
condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux')) # !! THIS is for V1 only V2 will/should support merging
- displayName: ReportGenerator (merge)
+ displayName: 'Run ReportGenerator'
+ enabled: false
inputs:
reports: "$(Agent.TempDirectory)/**/coverage.cobertura.xml"
targetdir: "$(Agent.TempDirectory)/merged/"
@@ -56,7 +83,8 @@ jobs:
## V2 is already in the repository but it does not work "wrong number of segments" YAML error.
- task: PublishCodeCoverageResults@1
condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux')) # !! THIS is for V1 only V2 will/should support merging
- displayName: Publish Code Coverage
+ displayName: 'Publish Code Coverage'
+ enabled: false
inputs:
codeCoverageTool: "cobertura"
#summaryFileLocation: '$(Agent.TempDirectory)/**/coverage.cobertura.xml' # !!THIS IS FOR V2
diff --git a/.ci/azure-pipelines.yml b/.ci/azure-pipelines.yml
index 3522cbf006..3283121e2e 100644
--- a/.ci/azure-pipelines.yml
+++ b/.ci/azure-pipelines.yml
@@ -1,12 +1,12 @@
name: $(Date:yyyyMMdd)$(Rev:.r)
variables:
- - name: TestProjects
- value: "tests/**/*Tests.csproj"
- - name: RestoreBuildProjects
- value: "Jellyfin.Server/Jellyfin.Server.csproj"
- - name: DotNetSdkVersion
- value: 3.1.100
+- name: TestProjects
+ value: 'tests/**/*Tests.csproj'
+- name: RestoreBuildProjects
+ value: 'Jellyfin.Server/Jellyfin.Server.csproj'
+- name: DotNetSdkVersion
+ value: 3.1.100
pr:
autoCancel: true
@@ -17,17 +17,17 @@ trigger:
jobs:
- template: azure-pipelines-main.yml
parameters:
- LinuxImage: "ubuntu-latest"
+ LinuxImage: 'ubuntu-latest'
RestoreBuildProjects: $(RestoreBuildProjects)
- template: azure-pipelines-test.yml
parameters:
ImageNames:
- Linux: "ubuntu-latest"
- Windows: "windows-latest"
- macOS: "macos-latest"
+ Linux: 'ubuntu-latest'
+ Windows: 'windows-latest'
+ macOS: 'macos-latest'
- - template: azure-pipelines-compat.yml
+ - template: azure-pipelines-abi.yml
parameters:
Packages:
Naming:
@@ -42,4 +42,4 @@ jobs:
Common:
NugetPackageName: Jellyfin.Common
AssemblyFileName: MediaBrowser.Common.dll
- LinuxImage: "ubuntu-latest"
+ LinuxImage: 'ubuntu-latest'
diff --git a/.copr/Makefile b/.copr/Makefile
deleted file mode 100644
index ba330ada95..0000000000
--- a/.copr/Makefile
+++ /dev/null
@@ -1,59 +0,0 @@
-VERSION := $(shell sed -ne '/^Version:/s/.* *//p' \
- deployment/fedora-package-x64/pkg-src/jellyfin.spec)
-
-deployment/fedora-package-x64/pkg-src/jellyfin-web-$(VERSION).tar.gz:
- curl -f -L -o deployment/fedora-package-x64/pkg-src/jellyfin-web-$(VERSION).tar.gz \
- https://github.com/jellyfin/jellyfin-web/archive/v$(VERSION).tar.gz \
- || curl -f -L -o deployment/fedora-package-x64/pkg-src/jellyfin-web-$(VERSION).tar.gz \
- https://github.com/jellyfin/jellyfin-web/archive/master.tar.gz \
-
-srpm: deployment/fedora-package-x64/pkg-src/jellyfin-web-$(VERSION).tar.gz
- cd deployment/fedora-package-x64; \
- SOURCE_DIR=../.. \
- WORKDIR="$${PWD}"; \
- package_temporary_dir="$${WORKDIR}/pkg-dist-tmp"; \
- pkg_src_dir="$${WORKDIR}/pkg-src"; \
- GNU_TAR=1; \
- 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/jellyfin-$(VERSION).tar.gz" \
- -C $${SOURCE_DIR} ./ || GNU_TAR=0; \
- if [ $${GNU_TAR} -eq 0 ]; then \
- package_temporary_dir="$$(mktemp -d)"; \
- mkdir -p "$${package_temporary_dir}/jellyfin"; \
- tar \
- --exclude='.git*' \
- --exclude='**/.git' \
- --exclude='**/.hg' \
- --exclude='**/.vs' \
- --exclude='**/.vscode' \
- --exclude='deployment' \
- --exclude='**/bin' \
- --exclude='**/obj' \
- --exclude='**/.nuget' \
- --exclude='*.deb' \
- --exclude='*.rpm' \
- -czf "$${package_temporary_dir}/jellyfin/jellyfin-$(VERSION).tar.gz" \
- -C $${SOURCE_DIR} ./; \
- mkdir -p "$${package_temporary_dir}/jellyfin-$(VERSION)"; \
- tar -xzf "$${package_temporary_dir}/jellyfin/jellyfin-$(VERSION).tar.gz" \
- -C "$${package_temporary_dir}/jellyfin-$(VERSION); \
- rm -f "$${package_temporary_dir}/jellyfin/jellyfin-$(VERSION).tar.gz"; \
- tar -czf "$${SOURCE_DIR}/SOURCES/pkg-src/jellyfin-$(VERSION).tar.gz" \
- -C "$${package_temporary_dir}" "jellyfin-$(VERSION); \
- rm -rf $${package_temporary_dir}; \
- fi; \
- rpmbuild -bs pkg-src/jellyfin.spec \
- --define "_sourcedir $$PWD/pkg-src/" \
- --define "_srcrpmdir $(outdir)"
diff --git a/.copr/Makefile b/.copr/Makefile
new file mode 120000
index 0000000000..ec3c90dfd9
--- /dev/null
+++ b/.copr/Makefile
@@ -0,0 +1 @@
+../fedora/Makefile
\ No newline at end of file
diff --git a/.editorconfig b/.editorconfig
index dc9aaa3ed5..b84e563efa 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -13,7 +13,7 @@ charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
end_of_line = lf
-max_line_length = null
+max_line_length = off
# YAML indentation
[*.{yml,yaml}]
@@ -22,6 +22,7 @@ indent_size = 2
# XML indentation
[*.{csproj,xml}]
indent_size = 2
+
###############################
# .NET Coding Conventions #
###############################
@@ -51,11 +52,12 @@ dotnet_style_explicit_tuple_names = true:suggestion
dotnet_style_null_propagation = true:suggestion
dotnet_style_coalesce_expression = true:suggestion
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent
-dotnet_prefer_inferred_tuple_names = true:suggestion
-dotnet_prefer_inferred_anonymous_type_member_names = true:suggestion
+dotnet_style_prefer_inferred_tuple_names = true:suggestion
+dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
dotnet_style_prefer_auto_properties = true:silent
dotnet_style_prefer_conditional_expression_over_assignment = true:silent
dotnet_style_prefer_conditional_expression_over_return = true:silent
+
###############################
# Naming Conventions #
###############################
@@ -67,7 +69,7 @@ dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.symbols = non
dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.style = non_private_static_field_style
dotnet_naming_symbols.non_private_static_fields.applicable_kinds = field
-dotnet_naming_symbols.non_private_static_fields.applicable_accessibilities = public, protected, internal, protected internal, private protected
+dotnet_naming_symbols.non_private_static_fields.applicable_accessibilities = public, protected, internal, protected_internal, private_protected
dotnet_naming_symbols.non_private_static_fields.required_modifiers = static
dotnet_naming_style.non_private_static_field_style.capitalization = pascal_case
@@ -159,6 +161,7 @@ csharp_style_deconstructed_variable_declaration = true:suggestion
csharp_prefer_simple_default_expression = true:suggestion
csharp_style_pattern_local_over_anonymous_function = true:suggestion
csharp_style_inlined_variable_declaration = true:suggestion
+
###############################
# C# Formatting Rules #
###############################
@@ -189,9 +192,3 @@ csharp_space_between_method_call_empty_parameter_list_parentheses = false
# Wrapping preferences
csharp_preserve_single_line_statements = true
csharp_preserve_single_line_blocks = true
-###############################
-# VB Coding Conventions #
-###############################
-[*.vb]
-# Modifier preferences
-visual_basic_preferred_modifier_order = Partial,Default,Private,Protected,Public,Friend,NotOverridable,Overridable,MustOverride,Overloads,Overrides,MustInherit,NotInheritable,Static,Shared,Shadows,ReadOnly,WriteOnly,Dim,Const,WithEvents,Widening,Narrowing,Custom,Async:suggestion
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
new file mode 100644
index 0000000000..e902dc7124
--- /dev/null
+++ b/.github/CODEOWNERS
@@ -0,0 +1,3 @@
+# Joshua must review all changes to deployment and build.sh
+deployment/* @joshuaboniface
+build.sh @joshuaboniface
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000000..0874cae2e3
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,9 @@
+version: 2
+updates:
+- package-ecosystem: nuget
+ directory: "/"
+ schedule:
+ interval: weekly
+ time: '12:00'
+ open-pull-requests-limit: 10
+
diff --git a/.gitignore b/.gitignore
index 523c45a7e6..46f036ad91 100644
--- a/.gitignore
+++ b/.gitignore
@@ -245,14 +245,14 @@ pip-log.txt
#########################
# Artifacts for debian-x64
-deployment/debian-package-x64/pkg-src/.debhelper/
-deployment/debian-package-x64/pkg-src/*.debhelper
-deployment/debian-package-x64/pkg-src/debhelper-build-stamp
-deployment/debian-package-x64/pkg-src/files
-deployment/debian-package-x64/pkg-src/jellyfin.substvars
-deployment/debian-package-x64/pkg-src/jellyfin/
+debian/.debhelper/
+debian/*.debhelper
+debian/debhelper-build-stamp
+debian/files
+debian/jellyfin.substvars
+debian/jellyfin/
# Don't ignore the debian/bin folder
-!deployment/debian-package-x64/pkg-src/bin/
+!debian/bin/
deployment/**/dist/
deployment/**/pkg-dist/
@@ -272,3 +272,8 @@ dist
# BenchmarkDotNet artifacts
BenchmarkDotNet.Artifacts
+
+# Ignore web artifacts from native builds
+web/
+web-src.*
+MediaBrowser.WebDashboard/jellyfin-web/
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
index ac517e10c6..2289fd9914 100644
--- a/.vscode/tasks.json
+++ b/.vscode/tasks.json
@@ -10,6 +10,16 @@
"${workspaceFolder}/Jellyfin.Server/Jellyfin.Server.csproj"
],
"problemMatcher": "$msCompile"
+ },
+ {
+ "label": "api tests",
+ "command": "dotnet",
+ "type": "process",
+ "args": [
+ "test",
+ "${workspaceFolder}/tests/MediaBrowser.Api.Tests/MediaBrowser.Api.Tests.csproj"
+ ],
+ "problemMatcher": "$msCompile"
}
]
-}
\ No newline at end of file
+}
diff --git a/Dockerfile b/Dockerfile
index 6e834d4e0b..d3fb138a81 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -2,7 +2,7 @@ ARG DOTNET_VERSION=3.1
FROM node:alpine as web-builder
ARG JELLYFIN_WEB_VERSION=master
-RUN apk add curl git zlib zlib-dev autoconf g++ make libpng-dev gifsicle alpine-sdk automake libtool make gcc musl-dev nasm \
+RUN apk add curl git zlib zlib-dev autoconf g++ make libpng-dev gifsicle alpine-sdk automake libtool make gcc musl-dev nasm python \
&& curl -L https://github.com/jellyfin/jellyfin-web/archive/${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
&& cd jellyfin-web-* \
&& yarn install \
diff --git a/Dockerfile.arm b/Dockerfile.arm
index 39beaa4791..59b8a8c982 100644
--- a/Dockerfile.arm
+++ b/Dockerfile.arm
@@ -38,7 +38,7 @@ COPY --from=qemu /usr/bin/qemu-arm-static /usr/bin
RUN apt-get update \
&& apt-get install --no-install-recommends --no-install-suggests -y ca-certificates gnupg curl && \
curl -ks https://repo.jellyfin.org/debian/jellyfin_team.gpg.key | apt-key add - && \
- curl -s https://keyserver.ubuntu.com/pks/lookup?op=get\&search=0x6587ffd6536b8826e88a62547876ae518cbcf2f2 | apt-key add - && \
+ curl -ks https://keyserver.ubuntu.com/pks/lookup?op=get\&search=0x6587ffd6536b8826e88a62547876ae518cbcf2f2 | apt-key add - && \
echo 'deb [arch=armhf] https://repo.jellyfin.org/debian buster main' > /etc/apt/sources.list.d/jellyfin.list && \
echo "deb http://ppa.launchpad.net/ubuntu-raspi2/ppa/ubuntu bionic main">> /etc/apt/sources.list.d/raspbins.list && \
apt-get update && \
diff --git a/DvdLib/BigEndianBinaryReader.cs b/DvdLib/BigEndianBinaryReader.cs
index 473005b556..b3aad85cec 100644
--- a/DvdLib/BigEndianBinaryReader.cs
+++ b/DvdLib/BigEndianBinaryReader.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Buffers.Binary;
using System.IO;
diff --git a/DvdLib/DvdLib.csproj b/DvdLib/DvdLib.csproj
index 36f949616a..64d041cb05 100644
--- a/DvdLib/DvdLib.csproj
+++ b/DvdLib/DvdLib.csproj
@@ -1,5 +1,10 @@
+
+
+ {713F42B5-878E-499D-A878-E4C652B1D5E8}
+
+
@@ -8,6 +13,7 @@
netstandard2.1
false
true
+ true
diff --git a/DvdLib/Ifo/Cell.cs b/DvdLib/Ifo/Cell.cs
index 268ab897ee..2eab400f7d 100644
--- a/DvdLib/Ifo/Cell.cs
+++ b/DvdLib/Ifo/Cell.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.IO;
namespace DvdLib.Ifo
diff --git a/DvdLib/Ifo/CellPlaybackInfo.cs b/DvdLib/Ifo/CellPlaybackInfo.cs
index e588e51ac0..6e33a0ec5a 100644
--- a/DvdLib/Ifo/CellPlaybackInfo.cs
+++ b/DvdLib/Ifo/CellPlaybackInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.IO;
namespace DvdLib.Ifo
diff --git a/DvdLib/Ifo/CellPositionInfo.cs b/DvdLib/Ifo/CellPositionInfo.cs
index 2b973e0830..216aa0f77a 100644
--- a/DvdLib/Ifo/CellPositionInfo.cs
+++ b/DvdLib/Ifo/CellPositionInfo.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.IO;
namespace DvdLib.Ifo
diff --git a/DvdLib/Ifo/Chapter.cs b/DvdLib/Ifo/Chapter.cs
index bd3bd97040..1e69429f82 100644
--- a/DvdLib/Ifo/Chapter.cs
+++ b/DvdLib/Ifo/Chapter.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
namespace DvdLib.Ifo
{
public class Chapter
diff --git a/DvdLib/Ifo/Dvd.cs b/DvdLib/Ifo/Dvd.cs
index 5af58a2dc8..ca20baa73f 100644
--- a/DvdLib/Ifo/Dvd.cs
+++ b/DvdLib/Ifo/Dvd.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.IO;
diff --git a/DvdLib/Ifo/DvdTime.cs b/DvdLib/Ifo/DvdTime.cs
index 3688089ec7..978af90c2e 100644
--- a/DvdLib/Ifo/DvdTime.cs
+++ b/DvdLib/Ifo/DvdTime.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
namespace DvdLib.Ifo
diff --git a/DvdLib/Ifo/Program.cs b/DvdLib/Ifo/Program.cs
index af08afa356..3d94fa7dc1 100644
--- a/DvdLib/Ifo/Program.cs
+++ b/DvdLib/Ifo/Program.cs
@@ -1,10 +1,12 @@
+#pragma warning disable CS1591
+
using System.Collections.Generic;
namespace DvdLib.Ifo
{
public class Program
{
- public readonly List Cells;
+ public IReadOnlyList Cells { get; }
public Program(List cells)
{
diff --git a/DvdLib/Ifo/ProgramChain.cs b/DvdLib/Ifo/ProgramChain.cs
index 7b003005b9..4860360afd 100644
--- a/DvdLib/Ifo/ProgramChain.cs
+++ b/DvdLib/Ifo/ProgramChain.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Collections.Generic;
using System.IO;
using System.Linq;
diff --git a/DvdLib/Ifo/Title.cs b/DvdLib/Ifo/Title.cs
index 335e929928..abf806d2c0 100644
--- a/DvdLib/Ifo/Title.cs
+++ b/DvdLib/Ifo/Title.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System.Collections.Generic;
using System.IO;
diff --git a/DvdLib/Ifo/UserOperation.cs b/DvdLib/Ifo/UserOperation.cs
index 757a5a05db..5d111ebc06 100644
--- a/DvdLib/Ifo/UserOperation.cs
+++ b/DvdLib/Ifo/UserOperation.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
namespace DvdLib.Ifo
diff --git a/Emby.Dlna/ConfigurationExtension.cs b/Emby.Dlna/ConfigurationExtension.cs
index e224d10bd3..dba9019677 100644
--- a/Emby.Dlna/ConfigurationExtension.cs
+++ b/Emby.Dlna/ConfigurationExtension.cs
@@ -1,3 +1,4 @@
+#nullable enable
#pragma warning disable CS1591
using System.Collections.Generic;
diff --git a/Emby.Dlna/ContentDirectory/ContentDirectory.cs b/Emby.Dlna/ContentDirectory/ContentDirectory.cs
index 64cd308a2a..66805b7c88 100644
--- a/Emby.Dlna/ContentDirectory/ContentDirectory.cs
+++ b/Emby.Dlna/ContentDirectory/ContentDirectory.cs
@@ -1,6 +1,7 @@
#pragma warning disable CS1591
using System;
+using System.Linq;
using System.Threading.Tasks;
using Emby.Dlna.Service;
using MediaBrowser.Common.Net;
@@ -136,12 +137,7 @@ namespace Emby.Dlna.ContentDirectory
}
}
- foreach (var user in _userManager.Users)
- {
- return user;
- }
-
- return null;
+ return _userManager.Users.FirstOrDefault();
}
}
}
diff --git a/Emby.Dlna/Didl/DidlBuilder.cs b/Emby.Dlna/Didl/DidlBuilder.cs
index 59951f6d9b..f7d840c623 100644
--- a/Emby.Dlna/Didl/DidlBuilder.cs
+++ b/Emby.Dlna/Didl/DidlBuilder.cs
@@ -425,57 +425,99 @@ namespace Emby.Dlna.Didl
}
}
- if (item is Episode episode && context is Season season)
+ return item is Episode episode
+ ? GetEpisodeDisplayName(episode, context)
+ : item.Name;
+ }
+
+ ///
+ /// Gets episode display name appropriate for the given context.
+ ///
+ ///
+ /// If context is a season, this will return a string containing just episode number and name.
+ /// Otherwise the result will include series nams and season number.
+ ///
+ /// The episode.
+ /// Current context.
+ /// Formatted name of the episode.
+ private string GetEpisodeDisplayName(Episode episode, BaseItem context)
+ {
+ string[] components;
+
+ if (context is Season season)
{
// This is a special embedded within a season
- if (item.ParentIndexNumber.HasValue && item.ParentIndexNumber.Value == 0
+ if (episode.ParentIndexNumber.HasValue && episode.ParentIndexNumber.Value == 0
&& season.IndexNumber.HasValue && season.IndexNumber.Value != 0)
{
return string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("ValueSpecialEpisodeName"),
- item.Name);
+ episode.Name);
}
- if (item.IndexNumber.HasValue)
- {
- var number = item.IndexNumber.Value.ToString("00", CultureInfo.InvariantCulture);
+ // inside a season use simple format (ex. '12 - Episode Name')
+ var epNumberName = GetEpisodeIndexFullName(episode);
+ components = new[] { epNumberName, episode.Name };
+ }
+ else
+ {
+ // outside a season include series and season details (ex. 'TV Show - S05E11 - Episode Name')
+ var epNumberName = GetEpisodeNumberDisplayName(episode);
+ components = new[] { episode.SeriesName, epNumberName, episode.Name };
+ }
- if (episode.IndexNumberEnd.HasValue)
- {
- number += "-" + episode.IndexNumberEnd.Value.ToString("00", CultureInfo.InvariantCulture);
- }
+ return string.Join(" - ", components.Where(NotNullOrWhiteSpace));
+ }
- return number + " - " + item.Name;
- }
- }
- else if (item is Episode ep)
+ ///
+ /// Gets complete episode number.
+ ///
+ /// The episode.
+ /// For single episodes returns just the number. For double episodes - current and ending numbers.
+ private string GetEpisodeIndexFullName(Episode episode)
+ {
+ var name = string.Empty;
+ if (episode.IndexNumber.HasValue)
{
- var parent = ep.GetParent();
- var name = parent.Name + " - ";
+ name += episode.IndexNumber.Value.ToString("00", CultureInfo.InvariantCulture);
- if (ep.ParentIndexNumber.HasValue)
- {
- name += "S" + ep.ParentIndexNumber.Value.ToString("00", CultureInfo.InvariantCulture);
- }
- else if (!item.IndexNumber.HasValue)
+ if (episode.IndexNumberEnd.HasValue)
{
- return name + " - " + item.Name;
+ name += "-" + episode.IndexNumberEnd.Value.ToString("00", CultureInfo.InvariantCulture);
}
+ }
- name += "E" + ep.IndexNumber.Value.ToString("00", CultureInfo.InvariantCulture);
- if (ep.IndexNumberEnd.HasValue)
- {
- name += "-" + ep.IndexNumberEnd.Value.ToString("00", CultureInfo.InvariantCulture);
- }
+ return name;
+ }
- name += " - " + item.Name;
- return name;
+ ///
+ /// Gets episode number formatted as 'S##E##'.
+ ///
+ /// The episode.
+ /// Formatted episode number.
+ private string GetEpisodeNumberDisplayName(Episode episode)
+ {
+ var name = string.Empty;
+ var seasonNumber = episode.Season?.IndexNumber;
+
+ if (seasonNumber.HasValue)
+ {
+ name = "S" + seasonNumber.Value.ToString("00", CultureInfo.InvariantCulture);
+ }
+
+ var indexName = GetEpisodeIndexFullName(episode);
+
+ if (!string.IsNullOrWhiteSpace(indexName))
+ {
+ name += "E" + indexName;
}
- return item.Name;
+ return name;
}
+ private bool NotNullOrWhiteSpace(string s) => !string.IsNullOrWhiteSpace(s);
+
private void AddAudioResource(XmlWriter writer, BaseItem audio, string deviceId, Filter filter, StreamInfo streamInfo = null)
{
writer.WriteStartElement(string.Empty, "res", NS_DIDL);
diff --git a/Emby.Dlna/DlnaManager.cs b/Emby.Dlna/DlnaManager.cs
index 10f881fe76..a85e5c35e7 100644
--- a/Emby.Dlna/DlnaManager.cs
+++ b/Emby.Dlna/DlnaManager.cs
@@ -31,7 +31,7 @@ namespace Emby.Dlna
private readonly IApplicationPaths _appPaths;
private readonly IXmlSerializer _xmlSerializer;
private readonly IFileSystem _fileSystem;
- private readonly ILogger _logger;
+ private readonly ILogger _logger;
private readonly IJsonSerializer _jsonSerializer;
private readonly IServerApplicationHost _appHost;
private static readonly Assembly _assembly = typeof(DlnaManager).Assembly;
@@ -49,7 +49,7 @@ namespace Emby.Dlna
_xmlSerializer = xmlSerializer;
_fileSystem = fileSystem;
_appPaths = appPaths;
- _logger = loggerFactory.CreateLogger("Dlna");
+ _logger = loggerFactory.CreateLogger();
_jsonSerializer = jsonSerializer;
_appHost = appHost;
}
diff --git a/Emby.Dlna/Emby.Dlna.csproj b/Emby.Dlna/Emby.Dlna.csproj
index 0cabe43d51..42a5f95c14 100644
--- a/Emby.Dlna/Emby.Dlna.csproj
+++ b/Emby.Dlna/Emby.Dlna.csproj
@@ -1,5 +1,10 @@
+
+
+ {805844AB-E92F-45E6-9D99-4F6D48D129A5}
+
+
diff --git a/Emby.Dlna/Main/DlnaEntryPoint.cs b/Emby.Dlna/Main/DlnaEntryPoint.cs
index c5d60b2a05..47b235e59f 100644
--- a/Emby.Dlna/Main/DlnaEntryPoint.cs
+++ b/Emby.Dlna/Main/DlnaEntryPoint.cs
@@ -33,7 +33,7 @@ namespace Emby.Dlna.Main
public class DlnaEntryPoint : IServerEntryPoint, IRunBeforeStartup
{
private readonly IServerConfigurationManager _config;
- private readonly ILogger _logger;
+ private readonly ILogger _logger;
private readonly IServerApplicationHost _appHost;
private PlayToManager _manager;
@@ -65,7 +65,8 @@ namespace Emby.Dlna.Main
public static DlnaEntryPoint Current;
- public DlnaEntryPoint(IServerConfigurationManager config,
+ public DlnaEntryPoint(
+ IServerConfigurationManager config,
ILoggerFactory loggerFactory,
IServerApplicationHost appHost,
ISessionManager sessionManager,
@@ -99,7 +100,7 @@ namespace Emby.Dlna.Main
_mediaEncoder = mediaEncoder;
_socketFactory = socketFactory;
_networkManager = networkManager;
- _logger = loggerFactory.CreateLogger("Dlna");
+ _logger = loggerFactory.CreateLogger();
ContentDirectory = new ContentDirectory.ContentDirectory(
dlnaManager,
@@ -133,20 +134,20 @@ namespace Emby.Dlna.Main
{
await ((DlnaManager)_dlnaManager).InitProfilesAsync().ConfigureAwait(false);
- ReloadComponents();
+ await ReloadComponents().ConfigureAwait(false);
- _config.NamedConfigurationUpdated += _config_NamedConfigurationUpdated;
+ _config.NamedConfigurationUpdated += OnNamedConfigurationUpdated;
}
- void _config_NamedConfigurationUpdated(object sender, ConfigurationUpdateEventArgs e)
+ private async void OnNamedConfigurationUpdated(object sender, ConfigurationUpdateEventArgs e)
{
if (string.Equals(e.Key, "dlna", StringComparison.OrdinalIgnoreCase))
{
- ReloadComponents();
+ await ReloadComponents().ConfigureAwait(false);
}
}
- private async void ReloadComponents()
+ private async Task ReloadComponents()
{
var options = _config.GetDlnaConfiguration();
@@ -347,7 +348,8 @@ namespace Emby.Dlna.Main
try
{
- _manager = new PlayToManager(_logger,
+ _manager = new PlayToManager(
+ _logger,
_sessionManager,
_libraryManager,
_userManager,
diff --git a/Emby.Dlna/PlayTo/Device.cs b/Emby.Dlna/PlayTo/Device.cs
index 6abc3a82c3..c7431d1437 100644
--- a/Emby.Dlna/PlayTo/Device.cs
+++ b/Emby.Dlna/PlayTo/Device.cs
@@ -34,7 +34,7 @@ namespace Emby.Dlna.PlayTo
{
get
{
- RefreshVolumeIfNeeded();
+ RefreshVolumeIfNeeded().GetAwaiter().GetResult();
return _volume;
}
set => _volume = value;
@@ -76,24 +76,24 @@ namespace Emby.Dlna.PlayTo
private DateTime _lastVolumeRefresh;
private bool _volumeRefreshActive;
- private void RefreshVolumeIfNeeded()
+ private Task RefreshVolumeIfNeeded()
{
- if (!_volumeRefreshActive)
- {
- return;
- }
-
- if (DateTime.UtcNow >= _lastVolumeRefresh.AddSeconds(5))
+ if (_volumeRefreshActive
+ && DateTime.UtcNow >= _lastVolumeRefresh.AddSeconds(5))
{
_lastVolumeRefresh = DateTime.UtcNow;
- RefreshVolume(CancellationToken.None);
+ return RefreshVolume();
}
+
+ return Task.CompletedTask;
}
- private async void RefreshVolume(CancellationToken cancellationToken)
+ private async Task RefreshVolume(CancellationToken cancellationToken = default)
{
if (_disposed)
+ {
return;
+ }
try
{
diff --git a/Emby.Dlna/PlayTo/PlayToController.cs b/Emby.Dlna/PlayTo/PlayToController.cs
index 43e9830540..7403a2a16a 100644
--- a/Emby.Dlna/PlayTo/PlayToController.cs
+++ b/Emby.Dlna/PlayTo/PlayToController.cs
@@ -146,11 +146,14 @@ namespace Emby.Dlna.PlayTo
{
var positionTicks = GetProgressPositionTicks(streamInfo);
- ReportPlaybackStopped(streamInfo, positionTicks);
+ await ReportPlaybackStopped(streamInfo, positionTicks).ConfigureAwait(false);
}
streamInfo = StreamParams.ParseFromUrl(e.NewMediaInfo.Url, _libraryManager, _mediaSourceManager);
- if (streamInfo.Item == null) return;
+ if (streamInfo.Item == null)
+ {
+ return;
+ }
var newItemProgress = GetProgressInfo(streamInfo);
@@ -173,11 +176,14 @@ namespace Emby.Dlna.PlayTo
{
var streamInfo = StreamParams.ParseFromUrl(e.MediaInfo.Url, _libraryManager, _mediaSourceManager);
- if (streamInfo.Item == null) return;
+ if (streamInfo.Item == null)
+ {
+ return;
+ }
var positionTicks = GetProgressPositionTicks(streamInfo);
- ReportPlaybackStopped(streamInfo, positionTicks);
+ await ReportPlaybackStopped(streamInfo, positionTicks).ConfigureAwait(false);
var mediaSource = await streamInfo.GetMediaSource(CancellationToken.None).ConfigureAwait(false);
@@ -185,7 +191,7 @@ namespace Emby.Dlna.PlayTo
(_device.Duration == null ? (long?)null : _device.Duration.Value.Ticks) :
mediaSource.RunTimeTicks;
- var playedToCompletion = (positionTicks.HasValue && positionTicks.Value == 0);
+ var playedToCompletion = positionTicks.HasValue && positionTicks.Value == 0;
if (!playedToCompletion && duration.HasValue && positionTicks.HasValue)
{
@@ -210,7 +216,7 @@ namespace Emby.Dlna.PlayTo
}
}
- private async void ReportPlaybackStopped(StreamParams streamInfo, long? positionTicks)
+ private async Task ReportPlaybackStopped(StreamParams streamInfo, long? positionTicks)
{
try
{
@@ -220,7 +226,6 @@ namespace Emby.Dlna.PlayTo
SessionId = _session.Id,
PositionTicks = positionTicks,
MediaSourceId = streamInfo.MediaSourceId
-
}).ConfigureAwait(false);
}
catch (Exception ex)
@@ -908,7 +913,8 @@ namespace Emby.Dlna.PlayTo
return 0;
}
- public Task SendMessage(string name, string messageId, T data, ISessionController[] allControllers, CancellationToken cancellationToken)
+ ///
+ public Task SendMessage(string name, Guid messageId, T data, CancellationToken cancellationToken)
{
if (_disposed)
{
@@ -924,10 +930,12 @@ namespace Emby.Dlna.PlayTo
{
return SendPlayCommand(data as PlayRequest, cancellationToken);
}
+
if (string.Equals(name, "PlayState", StringComparison.OrdinalIgnoreCase))
{
return SendPlaystateCommand(data as PlaystateRequest, cancellationToken);
}
+
if (string.Equals(name, "GeneralCommand", StringComparison.OrdinalIgnoreCase))
{
return SendGeneralCommand(data as GeneralCommand, cancellationToken);
diff --git a/Emby.Dlna/PlayTo/PlayToManager.cs b/Emby.Dlna/PlayTo/PlayToManager.cs
index bbedd1485c..9b0339e5d9 100644
--- a/Emby.Dlna/PlayTo/PlayToManager.cs
+++ b/Emby.Dlna/PlayTo/PlayToManager.cs
@@ -184,7 +184,8 @@ namespace Emby.Dlna.PlayTo
serverAddress = _appHost.GetLocalApiUrl(info.LocalIpAddress);
}
- controller = new PlayToController(sessionInfo,
+ controller = new PlayToController(
+ sessionInfo,
_sessionManager,
_libraryManager,
_logger,
diff --git a/Emby.Dlna/Profiles/DefaultProfile.cs b/Emby.Dlna/Profiles/DefaultProfile.cs
index d10804b228..2347ebd0d3 100644
--- a/Emby.Dlna/Profiles/DefaultProfile.cs
+++ b/Emby.Dlna/Profiles/DefaultProfile.cs
@@ -12,7 +12,7 @@ namespace Emby.Dlna.Profiles
{
Name = "Generic Device";
- ProtocolInfo = "http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*";
+ ProtocolInfo = "http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*";
Manufacturer = "Jellyfin";
ModelDescription = "UPnP/AV 1.0 Compliant Media Server";
diff --git a/Emby.Dlna/Profiles/Xml/Default.xml b/Emby.Dlna/Profiles/Xml/Default.xml
index daac4135a2..9460f9d5a1 100644
--- a/Emby.Dlna/Profiles/Xml/Default.xml
+++ b/Emby.Dlna/Profiles/Xml/Default.xml
@@ -21,7 +21,7 @@
140000000
192000
- http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*
+ http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*
0
false
false
diff --git a/Emby.Dlna/Profiles/Xml/Denon AVR.xml b/Emby.Dlna/Profiles/Xml/Denon AVR.xml
index c76cd9a898..571786906c 100644
--- a/Emby.Dlna/Profiles/Xml/Denon AVR.xml
+++ b/Emby.Dlna/Profiles/Xml/Denon AVR.xml
@@ -26,7 +26,7 @@
140000000
192000
- http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*
+ http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*
0
false
false
diff --git a/Emby.Dlna/Profiles/Xml/DirecTV HD-DVR.xml b/Emby.Dlna/Profiles/Xml/DirecTV HD-DVR.xml
index f2ce68ab5d..eea0febfdc 100644
--- a/Emby.Dlna/Profiles/Xml/DirecTV HD-DVR.xml
+++ b/Emby.Dlna/Profiles/Xml/DirecTV HD-DVR.xml
@@ -27,7 +27,7 @@
140000000
192000
- http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*
+ http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*
10
true
true
diff --git a/Emby.Dlna/Profiles/Xml/LG Smart TV.xml b/Emby.Dlna/Profiles/Xml/LG Smart TV.xml
index a0f0e0ee8a..20f5ba79bf 100644
--- a/Emby.Dlna/Profiles/Xml/LG Smart TV.xml
+++ b/Emby.Dlna/Profiles/Xml/LG Smart TV.xml
@@ -27,7 +27,7 @@
140000000
192000
- http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*
+ http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*
10
false
false
diff --git a/Emby.Dlna/Profiles/Xml/Linksys DMA2100.xml b/Emby.Dlna/Profiles/Xml/Linksys DMA2100.xml
index 55910c77f2..d01e3a145e 100644
--- a/Emby.Dlna/Profiles/Xml/Linksys DMA2100.xml
+++ b/Emby.Dlna/Profiles/Xml/Linksys DMA2100.xml
@@ -25,7 +25,7 @@
140000000
192000
- http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*
+ http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*
0
false
false
diff --git a/Emby.Dlna/Profiles/Xml/Marantz.xml b/Emby.Dlna/Profiles/Xml/Marantz.xml
index a6345ab3f3..0cc9c86e87 100644
--- a/Emby.Dlna/Profiles/Xml/Marantz.xml
+++ b/Emby.Dlna/Profiles/Xml/Marantz.xml
@@ -27,7 +27,7 @@
140000000
192000
- http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*
+ http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*
0
false
false
diff --git a/Emby.Dlna/Profiles/Xml/MediaMonkey.xml b/Emby.Dlna/Profiles/Xml/MediaMonkey.xml
index 2c2c3a1de4..9d5ddc3d1a 100644
--- a/Emby.Dlna/Profiles/Xml/MediaMonkey.xml
+++ b/Emby.Dlna/Profiles/Xml/MediaMonkey.xml
@@ -27,7 +27,7 @@
140000000
192000
- http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*
+ http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*
0
false
false
diff --git a/Emby.Dlna/Profiles/Xml/Panasonic Viera.xml b/Emby.Dlna/Profiles/Xml/Panasonic Viera.xml
index 934f0550d3..8f766853bb 100644
--- a/Emby.Dlna/Profiles/Xml/Panasonic Viera.xml
+++ b/Emby.Dlna/Profiles/Xml/Panasonic Viera.xml
@@ -28,7 +28,7 @@
140000000
192000
- http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*
+ http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*
10
false
false
diff --git a/Emby.Dlna/Profiles/Xml/Popcorn Hour.xml b/Emby.Dlna/Profiles/Xml/Popcorn Hour.xml
index eab220fae9..aa881d0147 100644
--- a/Emby.Dlna/Profiles/Xml/Popcorn Hour.xml
+++ b/Emby.Dlna/Profiles/Xml/Popcorn Hour.xml
@@ -21,7 +21,7 @@
140000000
192000
- http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*
+ http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*
0
false
false
diff --git a/Emby.Dlna/Profiles/Xml/Samsung Smart TV.xml b/Emby.Dlna/Profiles/Xml/Samsung Smart TV.xml
index 3e6f56e5bb..7160a9c2eb 100644
--- a/Emby.Dlna/Profiles/Xml/Samsung Smart TV.xml
+++ b/Emby.Dlna/Profiles/Xml/Samsung Smart TV.xml
@@ -27,7 +27,7 @@
140000000
192000
- http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*
+ http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*
0
false
false
diff --git a/Emby.Dlna/Profiles/Xml/Sharp Smart TV.xml b/Emby.Dlna/Profiles/Xml/Sharp Smart TV.xml
index 74240b8435..c9b907e580 100644
--- a/Emby.Dlna/Profiles/Xml/Sharp Smart TV.xml
+++ b/Emby.Dlna/Profiles/Xml/Sharp Smart TV.xml
@@ -27,7 +27,7 @@
140000000
192000
- http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*
+ http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*
0
true
true
diff --git a/Emby.Dlna/Profiles/Xml/Sony Bravia (2011).xml b/Emby.Dlna/Profiles/Xml/Sony Bravia (2011).xml
index 49819ccfdb..e516ff512d 100644
--- a/Emby.Dlna/Profiles/Xml/Sony Bravia (2011).xml
+++ b/Emby.Dlna/Profiles/Xml/Sony Bravia (2011).xml
@@ -29,7 +29,7 @@
192000
10
- http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*
+ http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*
0
false
false
diff --git a/Emby.Dlna/Profiles/Xml/Sony Bravia (2012).xml b/Emby.Dlna/Profiles/Xml/Sony Bravia (2012).xml
index aaad7b342c..88bd1c2f53 100644
--- a/Emby.Dlna/Profiles/Xml/Sony Bravia (2012).xml
+++ b/Emby.Dlna/Profiles/Xml/Sony Bravia (2012).xml
@@ -29,7 +29,7 @@
192000
10
- http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*
+ http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*
0
false
false
diff --git a/Emby.Dlna/Profiles/Xml/Sony Bravia (2013).xml b/Emby.Dlna/Profiles/Xml/Sony Bravia (2013).xml
index 8e2e8dbaa4..3ca9893cdc 100644
--- a/Emby.Dlna/Profiles/Xml/Sony Bravia (2013).xml
+++ b/Emby.Dlna/Profiles/Xml/Sony Bravia (2013).xml
@@ -29,7 +29,7 @@
192000
10
- http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*
+ http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*
0
false
false
diff --git a/Emby.Dlna/Profiles/Xml/Sony Bravia (2014).xml b/Emby.Dlna/Profiles/Xml/Sony Bravia (2014).xml
index 17a6135e1f..8804a75dfa 100644
--- a/Emby.Dlna/Profiles/Xml/Sony Bravia (2014).xml
+++ b/Emby.Dlna/Profiles/Xml/Sony Bravia (2014).xml
@@ -29,7 +29,7 @@
192000
10
- http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*
+ http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*
0
false
false
diff --git a/Emby.Dlna/Profiles/Xml/Sony PlayStation 3.xml b/Emby.Dlna/Profiles/Xml/Sony PlayStation 3.xml
index df385135cd..bafa44b828 100644
--- a/Emby.Dlna/Profiles/Xml/Sony PlayStation 3.xml
+++ b/Emby.Dlna/Profiles/Xml/Sony PlayStation 3.xml
@@ -29,7 +29,7 @@
192000
10
- http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*
+ http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*
0
false
false
diff --git a/Emby.Dlna/Profiles/Xml/Sony PlayStation 4.xml b/Emby.Dlna/Profiles/Xml/Sony PlayStation 4.xml
index 20f50f6b63..eb8e645b31 100644
--- a/Emby.Dlna/Profiles/Xml/Sony PlayStation 4.xml
+++ b/Emby.Dlna/Profiles/Xml/Sony PlayStation 4.xml
@@ -29,7 +29,7 @@
192000
10
- http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*
+ http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*
0
false
false
diff --git a/Emby.Dlna/Profiles/Xml/WDTV Live.xml b/Emby.Dlna/Profiles/Xml/WDTV Live.xml
index 05380e33a6..ccb74ee646 100644
--- a/Emby.Dlna/Profiles/Xml/WDTV Live.xml
+++ b/Emby.Dlna/Profiles/Xml/WDTV Live.xml
@@ -28,7 +28,7 @@
140000000
192000
- http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*
+ http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*
5
false
false
diff --git a/Emby.Dlna/Profiles/Xml/Xbox One.xml b/Emby.Dlna/Profiles/Xml/Xbox One.xml
index 5f5cf1af31..26a65bbd44 100644
--- a/Emby.Dlna/Profiles/Xml/Xbox One.xml
+++ b/Emby.Dlna/Profiles/Xml/Xbox One.xml
@@ -28,7 +28,7 @@
140000000
192000
- http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*
+ http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*
40
false
false
diff --git a/Emby.Dlna/Profiles/Xml/foobar2000.xml b/Emby.Dlna/Profiles/Xml/foobar2000.xml
index f3eedb35c6..5ce75ace55 100644
--- a/Emby.Dlna/Profiles/Xml/foobar2000.xml
+++ b/Emby.Dlna/Profiles/Xml/foobar2000.xml
@@ -27,7 +27,7 @@
140000000
192000
- http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*
+ http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*
0
false
false
diff --git a/Emby.Dlna/Service/BaseService.cs b/Emby.Dlna/Service/BaseService.cs
index 3704bedcd5..8794ec26a8 100644
--- a/Emby.Dlna/Service/BaseService.cs
+++ b/Emby.Dlna/Service/BaseService.cs
@@ -17,7 +17,7 @@ namespace Emby.Dlna.Service
Logger = logger;
HttpClient = httpClient;
- EventManager = new EventManager(Logger, HttpClient);
+ EventManager = new EventManager(logger, HttpClient);
}
public EventSubscriptionResponse CancelEventSubscription(string subscriptionId)
diff --git a/Emby.Dlna/Ssdp/DeviceDiscovery.cs b/Emby.Dlna/Ssdp/DeviceDiscovery.cs
index f95b8ce7de..ab5e56ab07 100644
--- a/Emby.Dlna/Ssdp/DeviceDiscovery.cs
+++ b/Emby.Dlna/Ssdp/DeviceDiscovery.cs
@@ -100,15 +100,13 @@ namespace Emby.Dlna.Ssdp
var headers = headerDict.ToDictionary(i => i.Key, i => i.Value.Value.FirstOrDefault(), StringComparer.OrdinalIgnoreCase);
- var args = new GenericEventArgs
- {
- Argument = new UpnpDeviceInfo
+ var args = new GenericEventArgs(
+ new UpnpDeviceInfo
{
Location = e.DiscoveredDevice.DescriptionLocation,
Headers = headers,
LocalIpAddress = e.LocalIpAddress
- }
- };
+ });
DeviceDiscoveredInternal?.Invoke(this, args);
}
@@ -121,14 +119,12 @@ namespace Emby.Dlna.Ssdp
var headers = headerDict.ToDictionary(i => i.Key, i => i.Value.Value.FirstOrDefault(), StringComparer.OrdinalIgnoreCase);
- var args = new GenericEventArgs
- {
- Argument = new UpnpDeviceInfo
+ var args = new GenericEventArgs(
+ new UpnpDeviceInfo
{
Location = e.DiscoveredDevice.DescriptionLocation,
Headers = headers
- }
- };
+ });
DeviceLeft?.Invoke(this, args);
}
diff --git a/Emby.Dlna/Ssdp/Extensions.cs b/Emby.Dlna/Ssdp/Extensions.cs
index 10c1f321be..613d332b2d 100644
--- a/Emby.Dlna/Ssdp/Extensions.cs
+++ b/Emby.Dlna/Ssdp/Extensions.cs
@@ -1,5 +1,6 @@
#pragma warning disable CS1591
+using System.Linq;
using System.Xml.Linq;
namespace Emby.Dlna.Ssdp
@@ -10,24 +11,17 @@ namespace Emby.Dlna.Ssdp
{
var node = container.Element(name);
- return node == null ? null : node.Value;
+ return node?.Value;
}
public static string GetAttributeValue(this XElement container, XName name)
{
var node = container.Attribute(name);
- return node == null ? null : node.Value;
+ return node?.Value;
}
public static string GetDescendantValue(this XElement container, XName name)
- {
- foreach (var node in container.Descendants(name))
- {
- return node.Value;
- }
-
- return null;
- }
+ => container.Descendants(name).FirstOrDefault()?.Value;
}
}
diff --git a/Emby.Drawing/Emby.Drawing.csproj b/Emby.Drawing/Emby.Drawing.csproj
index b7090b2629..092f8580a6 100644
--- a/Emby.Drawing/Emby.Drawing.csproj
+++ b/Emby.Drawing/Emby.Drawing.csproj
@@ -1,10 +1,16 @@
+
+
+ {08FFF49B-F175-4807-A2B5-73B0EBD9F716}
+
+
netstandard2.1
false
true
true
+ enable
diff --git a/Emby.Drawing/ImageProcessor.cs b/Emby.Drawing/ImageProcessor.cs
index eca4b56eb9..5b0f004d1d 100644
--- a/Emby.Drawing/ImageProcessor.cs
+++ b/Emby.Drawing/ImageProcessor.cs
@@ -8,7 +8,6 @@ using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Model.Drawing;
using MediaBrowser.Model.Entities;
@@ -29,12 +28,11 @@ namespace Emby.Drawing
private static readonly HashSet _transparentImageTypes
= new HashSet(StringComparer.OrdinalIgnoreCase) { ".png", ".webp", ".gif" };
- private readonly ILogger _logger;
+ private readonly ILogger _logger;
private readonly IFileSystem _fileSystem;
private readonly IServerApplicationPaths _appPaths;
private readonly IImageEncoder _imageEncoder;
- private readonly Func _libraryManager;
- private readonly Func _mediaEncoder;
+ private readonly IMediaEncoder _mediaEncoder;
private bool _disposed = false;
@@ -45,20 +43,17 @@ namespace Emby.Drawing
/// The server application paths.
/// The filesystem.
/// The image encoder.
- /// The library manager.
/// The media encoder.
public ImageProcessor(
ILogger logger,
IServerApplicationPaths appPaths,
IFileSystem fileSystem,
IImageEncoder imageEncoder,
- Func libraryManager,
- Func mediaEncoder)
+ IMediaEncoder mediaEncoder)
{
_logger = logger;
_fileSystem = fileSystem;
_imageEncoder = imageEncoder;
- _libraryManager = libraryManager;
_mediaEncoder = mediaEncoder;
_appPaths = appPaths;
}
@@ -119,28 +114,11 @@ namespace Emby.Drawing
=> _transparentImageTypes.Contains(Path.GetExtension(path));
///
- public async Task<(string path, string mimeType, DateTime dateModified)> ProcessImage(ImageProcessingOptions options)
+ public async Task<(string path, string? mimeType, DateTime dateModified)> ProcessImage(ImageProcessingOptions options)
{
- if (options == null)
- {
- throw new ArgumentNullException(nameof(options));
- }
-
- var libraryManager = _libraryManager();
-
ItemImageInfo originalImage = options.Image;
BaseItem item = options.Item;
- if (!originalImage.IsLocalFile)
- {
- if (item == null)
- {
- item = libraryManager.GetItemById(options.ItemId);
- }
-
- originalImage = await libraryManager.ConvertImageToLocal(item, originalImage, options.ImageIndex).ConfigureAwait(false);
- }
-
string originalImagePath = originalImage.Path;
DateTime dateModified = originalImage.DateModified;
ImageDimensions? originalImageSize = null;
@@ -252,7 +230,7 @@ namespace Emby.Drawing
return ImageFormat.Jpg;
}
- private string GetMimeType(ImageFormat format, string path)
+ private string? GetMimeType(ImageFormat format, string path)
=> format switch
{
ImageFormat.Bmp => MimeTypes.GetMimeType("i.bmp"),
@@ -312,10 +290,6 @@ namespace Emby.Drawing
///
public ImageDimensions GetImageDimensions(BaseItem item, ItemImageInfo info)
- => GetImageDimensions(item, info, true);
-
- ///
- public ImageDimensions GetImageDimensions(BaseItem item, ItemImageInfo info, bool updateItem)
{
int width = info.Width;
int height = info.Height;
@@ -326,17 +300,12 @@ namespace Emby.Drawing
}
string path = info.Path;
- _logger.LogInformation("Getting image size for item {ItemType} {Path}", item.GetType().Name, path);
+ _logger.LogDebug("Getting image size for item {ItemType} {Path}", item.GetType().Name, path);
ImageDimensions size = GetImageDimensions(path);
info.Width = size.Width;
info.Height = size.Height;
- if (updateItem)
- {
- _libraryManager().UpdateImages(item);
- }
-
return size;
}
@@ -344,6 +313,27 @@ namespace Emby.Drawing
public ImageDimensions GetImageDimensions(string path)
=> _imageEncoder.GetImageSize(path);
+ ///
+ public string GetImageBlurHash(string path)
+ {
+ var size = GetImageDimensions(path);
+ if (size.Width <= 0 || size.Height <= 0)
+ {
+ return string.Empty;
+ }
+
+ // We want tiles to be as close to square as possible, and to *mostly* keep under 16 tiles for performance.
+ // One tile is (width / xComp) x (height / yComp) pixels, which means that ideally yComp = xComp * height / width.
+ // See more at https://github.com/woltapp/blurhash/#how-do-i-pick-the-number-of-x-and-y-components
+ float xCompF = MathF.Sqrt(16.0f * size.Width / size.Height);
+ float yCompF = xCompF * size.Height / size.Width;
+
+ int xComp = Math.Min((int)xCompF + 1, 9);
+ int yComp = Math.Min((int)yCompF + 1, 9);
+
+ return _imageEncoder.GetImageBlurHash(xComp, yComp, path);
+ }
+
///
public string GetImageCacheTag(BaseItem item, ItemImageInfo image)
=> (item.Path + image.DateModified.Ticks).GetMD5().ToString("N", CultureInfo.InvariantCulture);
@@ -351,19 +341,12 @@ namespace Emby.Drawing
///
public string GetImageCacheTag(BaseItem item, ChapterInfo chapter)
{
- try
+ return GetImageCacheTag(item, new ItemImageInfo
{
- return GetImageCacheTag(item, new ItemImageInfo
- {
- Path = chapter.ImagePath,
- Type = ImageType.Chapter,
- DateModified = chapter.ImageDateModified
- });
- }
- catch
- {
- return null;
- }
+ Path = chapter.ImagePath,
+ Type = ImageType.Chapter,
+ DateModified = chapter.ImageDateModified
+ });
}
private async Task<(string path, DateTime dateModified)> GetSupportedImage(string originalImagePath, DateTime dateModified)
@@ -384,13 +367,13 @@ namespace Emby.Drawing
{
string filename = (originalImagePath + dateModified.Ticks.ToString(CultureInfo.InvariantCulture)).GetMD5().ToString("N", CultureInfo.InvariantCulture);
- string cacheExtension = _mediaEncoder().SupportsEncoder("libwebp") ? ".webp" : ".png";
+ string cacheExtension = _mediaEncoder.SupportsEncoder("libwebp") ? ".webp" : ".png";
var outputPath = Path.Combine(_appPaths.ImageCachePath, "converted-images", filename + cacheExtension);
var file = _fileSystem.GetFileInfo(outputPath);
if (!file.Exists)
{
- await _mediaEncoder().ConvertImage(originalImagePath, outputPath).ConfigureAwait(false);
+ await _mediaEncoder.ConvertImage(originalImagePath, outputPath).ConfigureAwait(false);
dateModified = _fileSystem.GetLastWriteTimeUtc(outputPath);
}
else
diff --git a/Emby.Drawing/NullImageEncoder.cs b/Emby.Drawing/NullImageEncoder.cs
index 5af7f16225..bbb5c17162 100644
--- a/Emby.Drawing/NullImageEncoder.cs
+++ b/Emby.Drawing/NullImageEncoder.cs
@@ -42,5 +42,11 @@ namespace Emby.Drawing
{
throw new NotImplementedException();
}
+
+ ///
+ public string GetImageBlurHash(int xComp, int yComp, string path)
+ {
+ throw new NotImplementedException();
+ }
}
}
diff --git a/Emby.Naming/Audio/AlbumParser.cs b/Emby.Naming/Audio/AlbumParser.cs
index 33f4468d9f..b63be3a647 100644
--- a/Emby.Naming/Audio/AlbumParser.cs
+++ b/Emby.Naming/Audio/AlbumParser.cs
@@ -1,9 +1,9 @@
+#nullable enable
#pragma warning disable CS1591
using System;
using System.Globalization;
using System.IO;
-using System.Linq;
using System.Text.RegularExpressions;
using Emby.Naming.Common;
@@ -21,8 +21,7 @@ namespace Emby.Naming.Audio
public bool IsMultiPart(string path)
{
var filename = Path.GetFileName(path);
-
- if (string.IsNullOrEmpty(filename))
+ if (filename.Length == 0)
{
return false;
}
@@ -39,18 +38,22 @@ namespace Emby.Naming.Audio
filename = filename.Replace(')', ' ');
filename = Regex.Replace(filename, @"\s+", " ");
- filename = filename.TrimStart();
+ ReadOnlySpan trimmedFilename = filename.TrimStart();
foreach (var prefix in _options.AlbumStackingPrefixes)
{
- if (filename.IndexOf(prefix, StringComparison.OrdinalIgnoreCase) != 0)
+ if (!trimmedFilename.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
{
continue;
}
- var tmp = filename.Substring(prefix.Length);
+ var tmp = trimmedFilename.Slice(prefix.Length).Trim();
- tmp = tmp.Trim().Split(' ').FirstOrDefault() ?? string.Empty;
+ int index = tmp.IndexOf(' ');
+ if (index != -1)
+ {
+ tmp = tmp.Slice(0, index);
+ }
if (int.TryParse(tmp, NumberStyles.Integer, CultureInfo.InvariantCulture, out _))
{
diff --git a/Emby.Naming/Audio/AudioFileParser.cs b/Emby.Naming/Audio/AudioFileParser.cs
index 25d5f8735e..6b2f4be93e 100644
--- a/Emby.Naming/Audio/AudioFileParser.cs
+++ b/Emby.Naming/Audio/AudioFileParser.cs
@@ -1,3 +1,4 @@
+#nullable enable
#pragma warning disable CS1591
using System;
@@ -11,7 +12,7 @@ namespace Emby.Naming.Audio
{
public static bool IsAudioFile(string path, NamingOptions options)
{
- var extension = Path.GetExtension(path) ?? string.Empty;
+ var extension = Path.GetExtension(path);
return options.AudioFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase);
}
}
diff --git a/Emby.Naming/Common/EpisodeExpression.cs b/Emby.Naming/Common/EpisodeExpression.cs
index 07de728514..ed6ba8881c 100644
--- a/Emby.Naming/Common/EpisodeExpression.cs
+++ b/Emby.Naming/Common/EpisodeExpression.cs
@@ -23,11 +23,6 @@ namespace Emby.Naming.Common
{
}
- public EpisodeExpression()
- : this(null)
- {
- }
-
public string Expression
{
get => _expression;
@@ -48,6 +43,6 @@ namespace Emby.Naming.Common
public string[] DateTimeFormats { get; set; }
- public Regex Regex => _regex ?? (_regex = new Regex(Expression, RegexOptions.IgnoreCase | RegexOptions.Compiled));
+ public Regex Regex => _regex ??= new Regex(Expression, RegexOptions.IgnoreCase | RegexOptions.Compiled);
}
}
diff --git a/Emby.Naming/Common/NamingOptions.cs b/Emby.Naming/Common/NamingOptions.cs
index a2d75d0b8d..1b343790e8 100644
--- a/Emby.Naming/Common/NamingOptions.cs
+++ b/Emby.Naming/Common/NamingOptions.cs
@@ -142,7 +142,7 @@ namespace Emby.Naming.Common
CleanStrings = new[]
{
- @"[ _\,\.\(\)\[\]\-](3d|sbs|tab|hsbs|htab|mvc|HDR|HDC|UHD|UltraHD|4k|ac3|dts|custom|dc|divx|divx5|dsr|dsrip|dutch|dvd|dvdrip|dvdscr|dvdscreener|screener|dvdivx|cam|fragment|fs|hdtv|hdrip|hdtvrip|internal|limited|multisubs|ntsc|ogg|ogm|pal|pdtv|proper|repack|rerip|retail|cd[1-9]|r3|r5|bd5|se|svcd|swedish|german|read.nfo|nfofix|unrated|ws|telesync|ts|telecine|tc|brrip|bdrip|480p|480i|576p|576i|720p|720i|1080p|1080i|2160p|hrhd|hrhdtv|hddvd|bluray|x264|h264|xvid|xvidvd|xxx|www.www|\[.*\])([ _\,\.\(\)\[\]\-]|$)",
+ @"[ _\,\.\(\)\[\]\-](3d|sbs|tab|hsbs|htab|mvc|HDR|HDC|UHD|UltraHD|4k|ac3|dts|custom|dc|divx|divx5|dsr|dsrip|dutch|dvd|dvdrip|dvdscr|dvdscreener|screener|dvdivx|cam|fragment|fs|hdtv|hdrip|hdtvrip|internal|limited|multisubs|ntsc|ogg|ogm|pal|pdtv|proper|repack|rerip|retail|cd[1-9]|r3|r5|bd5|bd|se|svcd|swedish|german|read.nfo|nfofix|unrated|ws|telesync|ts|telecine|tc|brrip|bdrip|480p|480i|576p|576i|720p|720i|1080p|1080i|2160p|hrhd|hrhdtv|hddvd|bluray|x264|h264|xvid|xvidvd|xxx|www.www|\[.*\])([ _\,\.\(\)\[\]\-]|$)",
@"(\[.*\])"
};
diff --git a/Emby.Naming/Emby.Naming.csproj b/Emby.Naming/Emby.Naming.csproj
index 4e08170a47..c017e76c74 100644
--- a/Emby.Naming/Emby.Naming.csproj
+++ b/Emby.Naming/Emby.Naming.csproj
@@ -1,5 +1,10 @@
+
+
+ {E5AF7B26-2239-4CE0-B477-0AA2018EDAA2}
+
+
netstandard2.1
false
diff --git a/Emby.Naming/Subtitles/SubtitleParser.cs b/Emby.Naming/Subtitles/SubtitleParser.cs
index 88ec3e2d60..24e59f90a3 100644
--- a/Emby.Naming/Subtitles/SubtitleParser.cs
+++ b/Emby.Naming/Subtitles/SubtitleParser.cs
@@ -1,3 +1,4 @@
+#nullable enable
#pragma warning disable CS1591
using System;
@@ -16,11 +17,11 @@ namespace Emby.Naming.Subtitles
_options = options;
}
- public SubtitleInfo ParseFile(string path)
+ public SubtitleInfo? ParseFile(string path)
{
- if (string.IsNullOrEmpty(path))
+ if (path.Length == 0)
{
- throw new ArgumentNullException(nameof(path));
+ throw new ArgumentException("File path can't be empty.", nameof(path));
}
var extension = Path.GetExtension(path);
@@ -52,11 +53,6 @@ namespace Emby.Naming.Subtitles
private string[] GetFlags(string path)
{
- if (string.IsNullOrEmpty(path))
- {
- throw new ArgumentNullException(nameof(path));
- }
-
// Note: the tags need be be surrounded be either a space ( ), hyphen -, dot . or underscore _.
var file = Path.GetFileName(path);
diff --git a/Emby.Naming/Video/VideoListResolver.cs b/Emby.Naming/Video/VideoListResolver.cs
index 7f755fd25e..948fe037b5 100644
--- a/Emby.Naming/Video/VideoListResolver.cs
+++ b/Emby.Naming/Video/VideoListResolver.cs
@@ -227,7 +227,7 @@ namespace Emby.Naming.Video
}
return remainingFiles
- .Where(i => i.ExtraType == null)
+ .Where(i => i.ExtraType != null)
.Where(i => baseNames.Any(b =>
i.FileNameWithoutExtension.StartsWith(b, StringComparison.OrdinalIgnoreCase)))
.ToList();
diff --git a/Emby.Naming/Video/VideoResolver.cs b/Emby.Naming/Video/VideoResolver.cs
index 0b75a8cce9..b4aee614b0 100644
--- a/Emby.Naming/Video/VideoResolver.cs
+++ b/Emby.Naming/Video/VideoResolver.cs
@@ -89,14 +89,14 @@ namespace Emby.Naming.Video
if (parseName)
{
var cleanDateTimeResult = CleanDateTime(name);
+ name = cleanDateTimeResult.Name;
+ year = cleanDateTimeResult.Year;
if (extraResult.ExtraType == null
- && TryCleanString(cleanDateTimeResult.Name, out ReadOnlySpan newName))
+ && TryCleanString(name, out ReadOnlySpan newName))
{
name = newName.ToString();
}
-
- year = cleanDateTimeResult.Year;
}
return new VideoFileInfo
diff --git a/Emby.Notifications/Emby.Notifications.csproj b/Emby.Notifications/Emby.Notifications.csproj
index e6bf785bff..1d430a5e58 100644
--- a/Emby.Notifications/Emby.Notifications.csproj
+++ b/Emby.Notifications/Emby.Notifications.csproj
@@ -1,5 +1,10 @@
+
+
+ {2E030C33-6923-4530-9E54-FA29FA6AD1A9}
+
+
netstandard2.1
false
diff --git a/Emby.Notifications/NotificationEntryPoint.cs b/Emby.Notifications/NotificationEntryPoint.cs
index befecc570b..b923fd26ce 100644
--- a/Emby.Notifications/NotificationEntryPoint.cs
+++ b/Emby.Notifications/NotificationEntryPoint.cs
@@ -25,7 +25,7 @@ namespace Emby.Notifications
///
public class NotificationEntryPoint : IServerEntryPoint
{
- private readonly ILogger _logger;
+ private readonly ILogger _logger;
private readonly IActivityManager _activityManager;
private readonly ILocalizationManager _localization;
private readonly INotificationManager _notificationManager;
@@ -143,7 +143,7 @@ namespace Emby.Notifications
var notification = new NotificationRequest
{
- Description = "Please see jellyfin.media for details.",
+ Description = "Please see jellyfin.org for details.",
NotificationType = type,
Name = _localization.GetLocalizedString("NewVersionIsAvailable")
};
diff --git a/Emby.Notifications/NotificationManager.cs b/Emby.Notifications/NotificationManager.cs
index 639a5e1aad..2792a8f0b5 100644
--- a/Emby.Notifications/NotificationManager.cs
+++ b/Emby.Notifications/NotificationManager.cs
@@ -21,7 +21,7 @@ namespace Emby.Notifications
///
public class NotificationManager : INotificationManager
{
- private readonly ILogger _logger;
+ private readonly ILogger _logger;
private readonly IUserManager _userManager;
private readonly IServerConfigurationManager _config;
diff --git a/Emby.Photos/Emby.Photos.csproj b/Emby.Photos/Emby.Photos.csproj
index cc3fbb43f9..dbe01257f4 100644
--- a/Emby.Photos/Emby.Photos.csproj
+++ b/Emby.Photos/Emby.Photos.csproj
@@ -1,4 +1,10 @@
+
+
+
+ {89AB4548-770D-41FD-A891-8DAFF44F452C}
+
+
diff --git a/Emby.Photos/PhotoProvider.cs b/Emby.Photos/PhotoProvider.cs
index 63631e512b..4071e4e547 100644
--- a/Emby.Photos/PhotoProvider.cs
+++ b/Emby.Photos/PhotoProvider.cs
@@ -22,7 +22,7 @@ namespace Emby.Photos
///
public class PhotoProvider : ICustomMetadataProvider, IForcedProvider, IHasItemChangeMonitor
{
- private readonly ILogger _logger;
+ private readonly ILogger _logger;
private readonly IImageProcessor _imageProcessor;
// These are causing taglib to hang
@@ -104,7 +104,7 @@ namespace Emby.Photos
item.Overview = image.ImageTag.Comment;
if (!string.IsNullOrWhiteSpace(image.ImageTag.Title)
- && !item.LockedFields.Contains(MetadataFields.Name))
+ && !item.LockedFields.Contains(MetadataField.Name))
{
item.Name = image.ImageTag.Title;
}
@@ -160,7 +160,7 @@ namespace Emby.Photos
try
{
- var size = _imageProcessor.GetImageDimensions(item, img, false);
+ var size = _imageProcessor.GetImageDimensions(item, img);
if (size.Width > 0 && size.Height > 0)
{
diff --git a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs
index 0925d72a6d..6917efefa9 100644
--- a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs
+++ b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs
@@ -4,11 +4,10 @@ using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
using MediaBrowser.Common.Plugins;
using MediaBrowser.Common.Updates;
using MediaBrowser.Controller.Authentication;
-using MediaBrowser.Controller.Devices;
-using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Plugins;
using MediaBrowser.Controller.Session;
@@ -30,7 +29,7 @@ namespace Emby.Server.Implementations.Activity
///
public sealed class ActivityLogEntryPoint : IServerEntryPoint
{
- private readonly ILogger _logger;
+ private readonly ILogger _logger;
private readonly IInstallationManager _installationManager;
private readonly ISessionManager _sessionManager;
private readonly ITaskManager _taskManager;
@@ -38,14 +37,12 @@ namespace Emby.Server.Implementations.Activity
private readonly ILocalizationManager _localization;
private readonly ISubtitleManager _subManager;
private readonly IUserManager _userManager;
- private readonly IDeviceManager _deviceManager;
///
/// Initializes a new instance of the class.
///
/// The logger.
/// The session manager.
- /// The device manager.
/// The task manager.
/// The activity manager.
/// The localization manager.
@@ -55,7 +52,6 @@ namespace Emby.Server.Implementations.Activity
public ActivityLogEntryPoint(
ILogger logger,
ISessionManager sessionManager,
- IDeviceManager deviceManager,
ITaskManager taskManager,
IActivityManager activityManager,
ILocalizationManager localization,
@@ -65,7 +61,6 @@ namespace Emby.Server.Implementations.Activity
{
_logger = logger;
_sessionManager = sessionManager;
- _deviceManager = deviceManager;
_taskManager = taskManager;
_activityManager = activityManager;
_localization = localization;
@@ -99,52 +94,38 @@ namespace Emby.Server.Implementations.Activity
_userManager.UserPolicyUpdated += OnUserPolicyUpdated;
_userManager.UserLockedOut += OnUserLockedOut;
- _deviceManager.CameraImageUploaded += OnCameraImageUploaded;
-
return Task.CompletedTask;
}
- private void OnCameraImageUploaded(object sender, GenericEventArgs e)
- {
- CreateLogEntry(new ActivityLogEntry
- {
- Name = string.Format(
- CultureInfo.InvariantCulture,
- _localization.GetLocalizedString("CameraImageUploadedFrom"),
- e.Argument.Device.Name),
- Type = NotificationType.CameraImageUploaded.ToString()
- });
- }
-
- private void OnUserLockedOut(object sender, GenericEventArgs e)
+ private async void OnUserLockedOut(object sender, GenericEventArgs e)
{
- CreateLogEntry(new ActivityLogEntry
- {
- Name = string.Format(
- CultureInfo.InvariantCulture,
- _localization.GetLocalizedString("UserLockedOutWithName"),
- e.Argument.Name),
- Type = NotificationType.UserLockedOut.ToString(),
- UserId = e.Argument.Id
- });
+ await CreateLogEntry(new ActivityLog(
+ string.Format(
+ CultureInfo.InvariantCulture,
+ _localization.GetLocalizedString("UserLockedOutWithName"),
+ e.Argument.Name),
+ NotificationType.UserLockedOut.ToString(),
+ e.Argument.Id))
+ .ConfigureAwait(false);
}
- private void OnSubtitleDownloadFailure(object sender, SubtitleDownloadFailureEventArgs e)
+ private async void OnSubtitleDownloadFailure(object sender, SubtitleDownloadFailureEventArgs e)
{
- CreateLogEntry(new ActivityLogEntry
- {
- Name = string.Format(
+ await CreateLogEntry(new ActivityLog(
+ string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("SubtitleDownloadFailureFromForItem"),
e.Provider,
- Emby.Notifications.NotificationEntryPoint.GetItemName(e.Item)),
- Type = "SubtitleDownloadFailure",
+ Notifications.NotificationEntryPoint.GetItemName(e.Item)),
+ "SubtitleDownloadFailure",
+ Guid.Empty)
+ {
ItemId = e.Item.Id.ToString("N", CultureInfo.InvariantCulture),
ShortOverview = e.Exception.Message
- });
+ }).ConfigureAwait(false);
}
- private void OnPlaybackStopped(object sender, PlaybackStopEventArgs e)
+ private async void OnPlaybackStopped(object sender, PlaybackStopEventArgs e)
{
var item = e.MediaInfo;
@@ -167,20 +148,19 @@ namespace Emby.Server.Implementations.Activity
var user = e.Users[0];
- CreateLogEntry(new ActivityLogEntry
- {
- Name = string.Format(
+ await CreateLogEntry(new ActivityLog(
+ string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("UserStoppedPlayingItemWithValues"),
user.Name,
GetItemName(item),
e.DeviceName),
- Type = GetPlaybackStoppedNotificationType(item.MediaType),
- UserId = user.Id
- });
+ GetPlaybackStoppedNotificationType(item.MediaType),
+ user.Id))
+ .ConfigureAwait(false);
}
- private void OnPlaybackStart(object sender, PlaybackProgressEventArgs e)
+ private async void OnPlaybackStart(object sender, PlaybackProgressEventArgs e)
{
var item = e.MediaInfo;
@@ -203,17 +183,16 @@ namespace Emby.Server.Implementations.Activity
var user = e.Users.First();
- CreateLogEntry(new ActivityLogEntry
- {
- Name = string.Format(
+ await CreateLogEntry(new ActivityLog(
+ string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("UserStartedPlayingItemWithValues"),
user.Name,
GetItemName(item),
e.DeviceName),
- Type = GetPlaybackNotificationType(item.MediaType),
- UserId = user.Id
- });
+ GetPlaybackNotificationType(item.MediaType),
+ user.Id))
+ .ConfigureAwait(false);
}
private static string GetItemName(BaseItemDto item)
@@ -263,230 +242,209 @@ namespace Emby.Server.Implementations.Activity
return null;
}
- private void OnSessionEnded(object sender, SessionEventArgs e)
+ private async void OnSessionEnded(object sender, SessionEventArgs e)
{
- string name;
var session = e.SessionInfo;
if (string.IsNullOrEmpty(session.UserName))
{
- name = string.Format(
- CultureInfo.InvariantCulture,
- _localization.GetLocalizedString("DeviceOfflineWithName"),
- session.DeviceName);
-
- // Causing too much spam for now
return;
}
- else
- {
- name = string.Format(
+
+ await CreateLogEntry(new ActivityLog(
+ string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("UserOfflineFromDevice"),
session.UserName,
- session.DeviceName);
- }
-
- CreateLogEntry(new ActivityLogEntry
+ session.DeviceName),
+ "SessionEnded",
+ session.UserId)
{
- Name = name,
- Type = "SessionEnded",
ShortOverview = string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("LabelIpAddressValue"),
session.RemoteEndPoint),
- UserId = session.UserId
- });
+ }).ConfigureAwait(false);
}
- private void OnAuthenticationSucceeded(object sender, GenericEventArgs e)
+ private async void OnAuthenticationSucceeded(object sender, GenericEventArgs e)
{
var user = e.Argument.User;
- CreateLogEntry(new ActivityLogEntry
- {
- Name = string.Format(
+ await CreateLogEntry(new ActivityLog(
+ string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("AuthenticationSucceededWithUserName"),
user.Name),
- Type = "AuthenticationSucceeded",
+ "AuthenticationSucceeded",
+ user.Id)
+ {
ShortOverview = string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("LabelIpAddressValue"),
e.Argument.SessionInfo.RemoteEndPoint),
- UserId = user.Id
- });
+ }).ConfigureAwait(false);
}
- private void OnAuthenticationFailed(object sender, GenericEventArgs e)
+ private async void OnAuthenticationFailed(object sender, GenericEventArgs e)
{
- CreateLogEntry(new ActivityLogEntry
- {
- Name = string.Format(
+ await CreateLogEntry(new ActivityLog(
+ string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("FailedLoginAttemptWithUserName"),
e.Argument.Username),
- Type = "AuthenticationFailed",
+ "AuthenticationFailed",
+ Guid.Empty)
+ {
+ LogSeverity = LogLevel.Error,
ShortOverview = string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("LabelIpAddressValue"),
e.Argument.RemoteEndPoint),
- Severity = LogLevel.Error
- });
+ }).ConfigureAwait(false);
}
- private void OnUserPolicyUpdated(object sender, GenericEventArgs e)
+ private async void OnUserPolicyUpdated(object sender, GenericEventArgs e)
{
- CreateLogEntry(new ActivityLogEntry
- {
- Name = string.Format(
+ await CreateLogEntry(new ActivityLog(
+ string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("UserPolicyUpdatedWithName"),
e.Argument.Name),
- Type = "UserPolicyUpdated",
- UserId = e.Argument.Id
- });
+ "UserPolicyUpdated",
+ e.Argument.Id))
+ .ConfigureAwait(false);
}
- private void OnUserDeleted(object sender, GenericEventArgs e)
+ private async void OnUserDeleted(object sender, GenericEventArgs e)
{
- CreateLogEntry(new ActivityLogEntry
- {
- Name = string.Format(
+ await CreateLogEntry(new ActivityLog(
+ string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("UserDeletedWithName"),
e.Argument.Name),
- Type = "UserDeleted"
- });
+ "UserDeleted",
+ Guid.Empty))
+ .ConfigureAwait(false);
}
- private void OnUserPasswordChanged(object sender, GenericEventArgs e)
+ private async void OnUserPasswordChanged(object sender, GenericEventArgs e)
{
- CreateLogEntry(new ActivityLogEntry
- {
- Name = string.Format(
+ await CreateLogEntry(new ActivityLog(
+ string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("UserPasswordChangedWithName"),
e.Argument.Name),
- Type = "UserPasswordChanged",
- UserId = e.Argument.Id
- });
+ "UserPasswordChanged",
+ e.Argument.Id))
+ .ConfigureAwait(false);
}
- private void OnUserCreated(object sender, GenericEventArgs e)
+ private async void OnUserCreated(object sender, GenericEventArgs e)
{
- CreateLogEntry(new ActivityLogEntry
- {
- Name = string.Format(
+ await CreateLogEntry(new ActivityLog(
+ string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("UserCreatedWithName"),
e.Argument.Name),
- Type = "UserCreated",
- UserId = e.Argument.Id
- });
+ "UserCreated",
+ e.Argument.Id))
+ .ConfigureAwait(false);
}
- private void OnSessionStarted(object sender, SessionEventArgs e)
+ private async void OnSessionStarted(object sender, SessionEventArgs e)
{
- string name;
var session = e.SessionInfo;
if (string.IsNullOrEmpty(session.UserName))
{
- name = string.Format(
- CultureInfo.InvariantCulture,
- _localization.GetLocalizedString("DeviceOnlineWithName"),
- session.DeviceName);
-
- // Causing too much spam for now
return;
}
- else
- {
- name = string.Format(
+
+ await CreateLogEntry(new ActivityLog(
+ string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("UserOnlineFromDevice"),
session.UserName,
- session.DeviceName);
- }
-
- CreateLogEntry(new ActivityLogEntry
+ session.DeviceName),
+ "SessionStarted",
+ session.UserId)
{
- Name = name,
- Type = "SessionStarted",
ShortOverview = string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("LabelIpAddressValue"),
- session.RemoteEndPoint),
- UserId = session.UserId
- });
+ session.RemoteEndPoint)
+ }).ConfigureAwait(false);
}
- private void OnPluginUpdated(object sender, GenericEventArgs<(IPlugin, VersionInfo)> e)
+ private async void OnPluginUpdated(object sender, InstallationInfo e)
{
- CreateLogEntry(new ActivityLogEntry
- {
- Name = string.Format(
+ await CreateLogEntry(new ActivityLog(
+ string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("PluginUpdatedWithName"),
- e.Argument.Item1.Name),
- Type = NotificationType.PluginUpdateInstalled.ToString(),
+ e.Name),
+ NotificationType.PluginUpdateInstalled.ToString(),
+ Guid.Empty)
+ {
ShortOverview = string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("VersionNumber"),
- e.Argument.Item2.version),
- Overview = e.Argument.Item2.changelog
- });
+ e.Version),
+ Overview = e.Changelog
+ }).ConfigureAwait(false);
}
- private void OnPluginUninstalled(object sender, GenericEventArgs e)
+ private async void OnPluginUninstalled(object sender, IPlugin e)
{
- CreateLogEntry(new ActivityLogEntry
- {
- Name = string.Format(
+ await CreateLogEntry(new ActivityLog(
+ string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("PluginUninstalledWithName"),
- e.Argument.Name),
- Type = NotificationType.PluginUninstalled.ToString()
- });
+ e.Name),
+ NotificationType.PluginUninstalled.ToString(),
+ Guid.Empty))
+ .ConfigureAwait(false);
}
- private void OnPluginInstalled(object sender, GenericEventArgs e)
+ private async void OnPluginInstalled(object sender, InstallationInfo e)
{
- CreateLogEntry(new ActivityLogEntry
- {
- Name = string.Format(
+ await CreateLogEntry(new ActivityLog(
+ string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("PluginInstalledWithName"),
- e.Argument.name),
- Type = NotificationType.PluginInstalled.ToString(),
+ e.Name),
+ NotificationType.PluginInstalled.ToString(),
+ Guid.Empty)
+ {
ShortOverview = string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("VersionNumber"),
- e.Argument.version)
- });
+ e.Version)
+ }).ConfigureAwait(false);
}
- private void OnPackageInstallationFailed(object sender, InstallationFailedEventArgs e)
+ private async void OnPackageInstallationFailed(object sender, InstallationFailedEventArgs e)
{
var installationInfo = e.InstallationInfo;
- CreateLogEntry(new ActivityLogEntry
- {
- Name = string.Format(
+ await CreateLogEntry(new ActivityLog(
+ string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("NameInstallFailed"),
installationInfo.Name),
- Type = NotificationType.InstallationFailed.ToString(),
+ NotificationType.InstallationFailed.ToString(),
+ Guid.Empty)
+ {
ShortOverview = string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("VersionNumber"),
installationInfo.Version),
Overview = e.Exception.Message
- });
+ }).ConfigureAwait(false);
}
- private void OnTaskCompleted(object sender, TaskCompletionEventArgs e)
+ private async void OnTaskCompleted(object sender, TaskCompletionEventArgs e)
{
var result = e.Result;
var task = e.Task;
@@ -517,22 +475,20 @@ namespace Emby.Server.Implementations.Activity
vals.Add(e.Result.LongErrorMessage);
}
- CreateLogEntry(new ActivityLogEntry
+ await CreateLogEntry(new ActivityLog(
+ string.Format(CultureInfo.InvariantCulture, _localization.GetLocalizedString("ScheduledTaskFailedWithName"), task.Name),
+ NotificationType.TaskFailed.ToString(),
+ Guid.Empty)
{
- Name = string.Format(
- CultureInfo.InvariantCulture,
- _localization.GetLocalizedString("ScheduledTaskFailedWithName"),
- task.Name),
- Type = NotificationType.TaskFailed.ToString(),
+ LogSeverity = LogLevel.Error,
Overview = string.Join(Environment.NewLine, vals),
- ShortOverview = runningTime,
- Severity = LogLevel.Error
- });
+ ShortOverview = runningTime
+ }).ConfigureAwait(false);
}
}
- private void CreateLogEntry(ActivityLogEntry entry)
- => _activityManager.Create(entry);
+ private async Task CreateLogEntry(ActivityLog entry)
+ => await _activityManager.CreateAsync(entry).ConfigureAwait(false);
///
public void Dispose()
@@ -559,8 +515,6 @@ namespace Emby.Server.Implementations.Activity
_userManager.UserDeleted -= OnUserDeleted;
_userManager.UserPolicyUpdated -= OnUserPolicyUpdated;
_userManager.UserLockedOut -= OnUserLockedOut;
-
- _deviceManager.CameraImageUploaded -= OnCameraImageUploaded;
}
///
@@ -580,7 +534,7 @@ namespace Emby.Server.Implementations.Activity
{
int years = days / DaysInYear;
values.Add(CreateValueString(years, "year"));
- days = days % DaysInYear;
+ days %= DaysInYear;
}
// Number of months
diff --git a/Emby.Server.Implementations/Activity/ActivityManager.cs b/Emby.Server.Implementations/Activity/ActivityManager.cs
deleted file mode 100644
index c1d8dd8da8..0000000000
--- a/Emby.Server.Implementations/Activity/ActivityManager.cs
+++ /dev/null
@@ -1,62 +0,0 @@
-#pragma warning disable CS1591
-
-using System;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Model.Activity;
-using MediaBrowser.Model.Events;
-using MediaBrowser.Model.Querying;
-using Microsoft.Extensions.Logging;
-
-namespace Emby.Server.Implementations.Activity
-{
- public class ActivityManager : IActivityManager
- {
- private readonly IActivityRepository _repo;
- private readonly IUserManager _userManager;
-
- public ActivityManager(IActivityRepository repo, IUserManager userManager)
- {
- _repo = repo;
- _userManager = userManager;
- }
-
- public event EventHandler> EntryCreated;
-
- public void Create(ActivityLogEntry entry)
- {
- entry.Date = DateTime.UtcNow;
-
- _repo.Create(entry);
-
- EntryCreated?.Invoke(this, new GenericEventArgs(entry));
- }
-
- public QueryResult GetActivityLogEntries(DateTime? minDate, bool? hasUserId, int? startIndex, int? limit)
- {
- var result = _repo.GetActivityLogEntries(minDate, hasUserId, startIndex, limit);
-
- foreach (var item in result.Items)
- {
- if (item.UserId == Guid.Empty)
- {
- continue;
- }
-
- var user = _userManager.GetUserById(item.UserId);
-
- if (user != null)
- {
- var dto = _userManager.GetUserDto(user);
- item.UserPrimaryImageTag = dto.PrimaryImageTag;
- }
- }
-
- return result;
- }
-
- public QueryResult GetActivityLogEntries(DateTime? minDate, int? startIndex, int? limit)
- {
- return GetActivityLogEntries(minDate, null, startIndex, limit);
- }
- }
-}
diff --git a/Emby.Server.Implementations/Activity/ActivityRepository.cs b/Emby.Server.Implementations/Activity/ActivityRepository.cs
deleted file mode 100644
index 26fc229f73..0000000000
--- a/Emby.Server.Implementations/Activity/ActivityRepository.cs
+++ /dev/null
@@ -1,317 +0,0 @@
-#pragma warning disable CS1591
-
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.Linq;
-using Emby.Server.Implementations.Data;
-using MediaBrowser.Controller;
-using MediaBrowser.Model.Activity;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Querying;
-using Microsoft.Extensions.Logging;
-using SQLitePCL.pretty;
-
-namespace Emby.Server.Implementations.Activity
-{
- public class ActivityRepository : BaseSqliteRepository, IActivityRepository
- {
- private const string BaseActivitySelectText = "select Id, Name, Overview, ShortOverview, Type, ItemId, UserId, DateCreated, LogSeverity from ActivityLog";
-
- private readonly IFileSystem _fileSystem;
-
- public ActivityRepository(ILogger logger, IServerApplicationPaths appPaths, IFileSystem fileSystem)
- : base(logger)
- {
- DbFilePath = Path.Combine(appPaths.DataPath, "activitylog.db");
- _fileSystem = fileSystem;
- }
-
- public void Initialize()
- {
- try
- {
- InitializeInternal();
- }
- catch (Exception ex)
- {
- Logger.LogError(ex, "Error loading database file. Will reset and retry.");
-
- _fileSystem.DeleteFile(DbFilePath);
-
- InitializeInternal();
- }
- }
-
- private void InitializeInternal()
- {
- using (var connection = GetConnection())
- {
- connection.RunQueries(new[]
- {
- "create table if not exists ActivityLog (Id INTEGER PRIMARY KEY, Name TEXT NOT NULL, Overview TEXT, ShortOverview TEXT, Type TEXT NOT NULL, ItemId TEXT, UserId TEXT, DateCreated DATETIME NOT NULL, LogSeverity TEXT NOT NULL)",
- "drop index if exists idx_ActivityLogEntries"
- });
-
- TryMigrate(connection);
- }
- }
-
- private void TryMigrate(ManagedConnection connection)
- {
- try
- {
- if (TableExists(connection, "ActivityLogEntries"))
- {
- connection.RunQueries(new[]
- {
- "INSERT INTO ActivityLog (Name, Overview, ShortOverview, Type, ItemId, UserId, DateCreated, LogSeverity) SELECT Name, Overview, ShortOverview, Type, ItemId, UserId, DateCreated, LogSeverity FROM ActivityLogEntries",
- "drop table if exists ActivityLogEntries"
- });
- }
- }
- catch (Exception ex)
- {
- Logger.LogError(ex, "Error migrating activity log database");
- }
- }
-
- public void Create(ActivityLogEntry entry)
- {
- if (entry == null)
- {
- throw new ArgumentNullException(nameof(entry));
- }
-
- using (var connection = GetConnection())
- {
- connection.RunInTransaction(
- db =>
- {
- using (var statement = db.PrepareStatement("insert into ActivityLog (Name, Overview, ShortOverview, Type, ItemId, UserId, DateCreated, LogSeverity) values (@Name, @Overview, @ShortOverview, @Type, @ItemId, @UserId, @DateCreated, @LogSeverity)"))
- {
- statement.TryBind("@Name", entry.Name);
-
- statement.TryBind("@Overview", entry.Overview);
- statement.TryBind("@ShortOverview", entry.ShortOverview);
- statement.TryBind("@Type", entry.Type);
- statement.TryBind("@ItemId", entry.ItemId);
-
- if (entry.UserId.Equals(Guid.Empty))
- {
- statement.TryBindNull("@UserId");
- }
- else
- {
- statement.TryBind("@UserId", entry.UserId.ToString("N", CultureInfo.InvariantCulture));
- }
-
- statement.TryBind("@DateCreated", entry.Date.ToDateTimeParamValue());
- statement.TryBind("@LogSeverity", entry.Severity.ToString());
-
- statement.MoveNext();
- }
- },
- TransactionMode);
- }
- }
-
- public void Update(ActivityLogEntry entry)
- {
- if (entry == null)
- {
- throw new ArgumentNullException(nameof(entry));
- }
-
- using (var connection = GetConnection())
- {
- connection.RunInTransaction(
- db =>
- {
- using (var statement = db.PrepareStatement("Update ActivityLog set Name=@Name,Overview=@Overview,ShortOverview=@ShortOverview,Type=@Type,ItemId=@ItemId,UserId=@UserId,DateCreated=@DateCreated,LogSeverity=@LogSeverity where Id=@Id"))
- {
- statement.TryBind("@Id", entry.Id);
-
- statement.TryBind("@Name", entry.Name);
- statement.TryBind("@Overview", entry.Overview);
- statement.TryBind("@ShortOverview", entry.ShortOverview);
- statement.TryBind("@Type", entry.Type);
- statement.TryBind("@ItemId", entry.ItemId);
-
- if (entry.UserId.Equals(Guid.Empty))
- {
- statement.TryBindNull("@UserId");
- }
- else
- {
- statement.TryBind("@UserId", entry.UserId.ToString("N", CultureInfo.InvariantCulture));
- }
-
- statement.TryBind("@DateCreated", entry.Date.ToDateTimeParamValue());
- statement.TryBind("@LogSeverity", entry.Severity.ToString());
-
- statement.MoveNext();
- }
- },
- TransactionMode);
- }
- }
-
- public QueryResult GetActivityLogEntries(DateTime? minDate, bool? hasUserId, int? startIndex, int? limit)
- {
- var commandText = BaseActivitySelectText;
- var whereClauses = new List();
-
- if (minDate.HasValue)
- {
- whereClauses.Add("DateCreated>=@DateCreated");
- }
-
- if (hasUserId.HasValue)
- {
- if (hasUserId.Value)
- {
- whereClauses.Add("UserId not null");
- }
- else
- {
- whereClauses.Add("UserId is null");
- }
- }
-
- var whereTextWithoutPaging = whereClauses.Count == 0 ?
- string.Empty :
- " where " + string.Join(" AND ", whereClauses.ToArray());
-
- if (startIndex.HasValue && startIndex.Value > 0)
- {
- var pagingWhereText = whereClauses.Count == 0 ?
- string.Empty :
- " where " + string.Join(" AND ", whereClauses.ToArray());
-
- whereClauses.Add(
- string.Format(
- CultureInfo.InvariantCulture,
- "Id NOT IN (SELECT Id FROM ActivityLog {0} ORDER BY DateCreated DESC LIMIT {1})",
- pagingWhereText,
- startIndex.Value));
- }
-
- var whereText = whereClauses.Count == 0 ?
- string.Empty :
- " where " + string.Join(" AND ", whereClauses.ToArray());
-
- commandText += whereText;
-
- commandText += " ORDER BY DateCreated DESC";
-
- if (limit.HasValue)
- {
- commandText += " LIMIT " + limit.Value.ToString(CultureInfo.InvariantCulture);
- }
-
- var statementTexts = new[]
- {
- commandText,
- "select count (Id) from ActivityLog" + whereTextWithoutPaging
- };
-
- var list = new List();
- var result = new QueryResult();
-
- using (var connection = GetConnection(true))
- {
- connection.RunInTransaction(
- db =>
- {
- var statements = PrepareAll(db, statementTexts).ToList();
-
- using (var statement = statements[0])
- {
- if (minDate.HasValue)
- {
- statement.TryBind("@DateCreated", minDate.Value.ToDateTimeParamValue());
- }
-
- foreach (var row in statement.ExecuteQuery())
- {
- list.Add(GetEntry(row));
- }
- }
-
- using (var statement = statements[1])
- {
- if (minDate.HasValue)
- {
- statement.TryBind("@DateCreated", minDate.Value.ToDateTimeParamValue());
- }
-
- result.TotalRecordCount = statement.ExecuteQuery().SelectScalarInt().First();
- }
- },
- ReadTransactionMode);
- }
-
- result.Items = list;
- return result;
- }
-
- private static ActivityLogEntry GetEntry(IReadOnlyList reader)
- {
- var index = 0;
-
- var info = new ActivityLogEntry
- {
- Id = reader[index].ToInt64()
- };
-
- index++;
- if (reader[index].SQLiteType != SQLiteType.Null)
- {
- info.Name = reader[index].ToString();
- }
-
- index++;
- if (reader[index].SQLiteType != SQLiteType.Null)
- {
- info.Overview = reader[index].ToString();
- }
-
- index++;
- if (reader[index].SQLiteType != SQLiteType.Null)
- {
- info.ShortOverview = reader[index].ToString();
- }
-
- index++;
- if (reader[index].SQLiteType != SQLiteType.Null)
- {
- info.Type = reader[index].ToString();
- }
-
- index++;
- if (reader[index].SQLiteType != SQLiteType.Null)
- {
- info.ItemId = reader[index].ToString();
- }
-
- index++;
- if (reader[index].SQLiteType != SQLiteType.Null)
- {
- info.UserId = new Guid(reader[index].ToString());
- }
-
- index++;
- info.Date = reader[index].ReadDateTime();
-
- index++;
- if (reader[index].SQLiteType != SQLiteType.Null)
- {
- info.Severity = Enum.Parse(reader[index].ToString(), true);
- }
-
- return info;
- }
- }
-}
diff --git a/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs b/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs
index bc47817438..2adc1d6c34 100644
--- a/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs
+++ b/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs
@@ -15,6 +15,11 @@ namespace Emby.Server.Implementations.AppBase
///
/// Initializes a new instance of the class.
///
+ /// The program data path.
+ /// The log directory path.
+ /// The configuration directory path.
+ /// The cache directory path.
+ /// The web directory path.
protected BaseApplicationPaths(
string programDataPath,
string logDirectoryPath,
diff --git a/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs b/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs
index 080cfbbd1a..d4a8268b97 100644
--- a/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs
+++ b/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs
@@ -53,7 +53,7 @@ namespace Emby.Server.Implementations.AppBase
CommonApplicationPaths = applicationPaths;
XmlSerializer = xmlSerializer;
_fileSystem = fileSystem;
- Logger = loggerFactory.CreateLogger(GetType().Name);
+ Logger = loggerFactory.CreateLogger();
UpdateCachePath();
}
@@ -83,7 +83,7 @@ namespace Emby.Server.Implementations.AppBase
/// Gets the logger.
///
/// The logger.
- protected ILogger Logger { get; private set; }
+ protected ILogger Logger { get; private set; }
///
/// Gets the XML serializer.
diff --git a/Emby.Server.Implementations/AppBase/ConfigurationHelper.cs b/Emby.Server.Implementations/AppBase/ConfigurationHelper.cs
index 854d7b4cbf..0b681fddfc 100644
--- a/Emby.Server.Implementations/AppBase/ConfigurationHelper.cs
+++ b/Emby.Server.Implementations/AppBase/ConfigurationHelper.cs
@@ -36,24 +36,22 @@ namespace Emby.Server.Implementations.AppBase
configuration = Activator.CreateInstance(type);
}
- using (var stream = new MemoryStream())
- {
- xmlSerializer.SerializeToStream(configuration, stream);
-
- // Take the object we just got and serialize it back to bytes
- var newBytes = stream.ToArray();
+ using var stream = new MemoryStream();
+ xmlSerializer.SerializeToStream(configuration, stream);
- // If the file didn't exist before, or if something has changed, re-save
- if (buffer == null || !buffer.SequenceEqual(newBytes))
- {
- Directory.CreateDirectory(Path.GetDirectoryName(path));
+ // Take the object we just got and serialize it back to bytes
+ var newBytes = stream.ToArray();
- // Save it after load in case we got new items
- File.WriteAllBytes(path, newBytes);
- }
+ // If the file didn't exist before, or if something has changed, re-save
+ if (buffer == null || !buffer.SequenceEqual(newBytes))
+ {
+ Directory.CreateDirectory(Path.GetDirectoryName(path));
- return configuration;
+ // Save it after load in case we got new items
+ File.WriteAllBytes(path, newBytes);
}
+
+ return configuration;
}
}
}
diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs
index d6d1538182..184c6f399b 100644
--- a/Emby.Server.Implementations/ApplicationHost.cs
+++ b/Emby.Server.Implementations/ApplicationHost.cs
@@ -22,7 +22,6 @@ using Emby.Dlna.Ssdp;
using Emby.Drawing;
using Emby.Notifications;
using Emby.Photos;
-using Emby.Server.Implementations.Activity;
using Emby.Server.Implementations.Archiving;
using Emby.Server.Implementations.Channels;
using Emby.Server.Implementations.Collections;
@@ -44,9 +43,9 @@ using Emby.Server.Implementations.Security;
using Emby.Server.Implementations.Serialization;
using Emby.Server.Implementations.Services;
using Emby.Server.Implementations.Session;
-using Emby.Server.Implementations.SocketSharp;
using Emby.Server.Implementations.TV;
using Emby.Server.Implementations.Updates;
+using Emby.Server.Implementations.SyncPlay;
using MediaBrowser.Api;
using MediaBrowser.Common;
using MediaBrowser.Common.Configuration;
@@ -80,13 +79,12 @@ using MediaBrowser.Controller.Session;
using MediaBrowser.Controller.Sorting;
using MediaBrowser.Controller.Subtitles;
using MediaBrowser.Controller.TV;
+using MediaBrowser.Controller.SyncPlay;
using MediaBrowser.LocalMetadata.Savers;
using MediaBrowser.MediaEncoding.BdInfo;
-using MediaBrowser.Model.Activity;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Cryptography;
using MediaBrowser.Model.Dlna;
-using MediaBrowser.Model.Events;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.MediaInfo;
@@ -95,7 +93,6 @@ using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Services;
using MediaBrowser.Model.System;
using MediaBrowser.Model.Tasks;
-using MediaBrowser.Model.Updates;
using MediaBrowser.Providers.Chapters;
using MediaBrowser.Providers.Manager;
using MediaBrowser.Providers.Plugins.TheTvdb;
@@ -103,10 +100,9 @@ using MediaBrowser.Providers.Subtitles;
using MediaBrowser.WebDashboard.Api;
using MediaBrowser.XbmcMetadata.Providers;
using Microsoft.AspNetCore.Http;
-using Microsoft.AspNetCore.Http.Extensions;
-using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
+using Prometheus.DotNetRuntime;
using OperatingSystem = MediaBrowser.Common.System.OperatingSystem;
namespace Emby.Server.Implementations
@@ -121,14 +117,20 @@ namespace Emby.Server.Implementations
///
private static readonly string[] _relevantEnvVarPrefixes = { "JELLYFIN_", "DOTNET_", "ASPNETCORE_" };
- private SqliteUserRepository _userRepository;
- private SqliteDisplayPreferencesRepository _displayPreferencesRepository;
+ private readonly IFileSystem _fileSystemManager;
+ private readonly INetworkManager _networkManager;
+ private readonly IXmlSerializer _xmlSerializer;
+ private readonly IStartupOptions _startupOptions;
+
+ private IMediaEncoder _mediaEncoder;
+ private ISessionManager _sessionManager;
+ private IHttpServer _httpServer;
+ private IHttpClient _httpClient;
///
/// Gets a value indicating whether this instance can self restart.
///
- /// true if this instance can self restart; otherwise, false.
- public abstract bool CanSelfRestart { get; }
+ public bool CanSelfRestart => _startupOptions.RestartPath != null;
public virtual bool CanLaunchWebBrowser
{
@@ -139,7 +141,7 @@ namespace Emby.Server.Implementations
return false;
}
- if (StartupOptions.IsService)
+ if (_startupOptions.IsService)
{
return false;
}
@@ -171,7 +173,7 @@ namespace Emby.Server.Implementations
///
/// Gets the logger.
///
- protected ILogger Logger { get; }
+ protected ILogger Logger { get; }
private IPlugin[] _plugins;
@@ -209,8 +211,6 @@ namespace Emby.Server.Implementations
/// The configuration manager.
protected IConfigurationManager ConfigurationManager { get; set; }
- public IFileSystem FileSystemManager { get; set; }
-
///
/// Gets or sets the service provider.
///
@@ -232,110 +232,6 @@ namespace Emby.Server.Implementations
/// The server configuration manager.
public IServerConfigurationManager ServerConfigurationManager => (IServerConfigurationManager)ConfigurationManager;
- ///
- /// Gets or sets the user manager.
- ///
- /// The user manager.
- public IUserManager UserManager { get; set; }
-
- ///
- /// Gets or sets the library manager.
- ///
- /// The library manager.
- internal ILibraryManager LibraryManager { get; set; }
-
- ///
- /// Gets or sets the directory watchers.
- ///
- /// The directory watchers.
- private ILibraryMonitor LibraryMonitor { get; set; }
-
- ///
- /// Gets or sets the provider manager.
- ///
- /// The provider manager.
- private IProviderManager ProviderManager { get; set; }
-
- ///
- /// Gets or sets the HTTP server.
- ///
- /// The HTTP server.
- private IHttpServer HttpServer { get; set; }
-
- private IDtoService DtoService { get; set; }
-
- public IImageProcessor ImageProcessor { get; set; }
-
- ///
- /// Gets or sets the media encoder.
- ///
- /// The media encoder.
- private IMediaEncoder MediaEncoder { get; set; }
-
- private ISubtitleEncoder SubtitleEncoder { get; set; }
-
- private ISessionManager SessionManager { get; set; }
-
- private ILiveTvManager LiveTvManager { get; set; }
-
- public LocalizationManager LocalizationManager { get; set; }
-
- private IEncodingManager EncodingManager { get; set; }
-
- private IChannelManager ChannelManager { get; set; }
-
- ///
- /// Gets or sets the user data repository.
- ///
- /// The user data repository.
- private IUserDataManager UserDataManager { get; set; }
-
- internal SqliteItemRepository ItemRepository { get; set; }
-
- private INotificationManager NotificationManager { get; set; }
-
- private ISubtitleManager SubtitleManager { get; set; }
-
- private IChapterManager ChapterManager { get; set; }
-
- private IDeviceManager DeviceManager { get; set; }
-
- internal IUserViewManager UserViewManager { get; set; }
-
- private IAuthenticationRepository AuthenticationRepository { get; set; }
-
- private ITVSeriesManager TVSeriesManager { get; set; }
-
- private ICollectionManager CollectionManager { get; set; }
-
- private IMediaSourceManager MediaSourceManager { get; set; }
-
- ///
- /// Gets the installation manager.
- ///
- /// The installation manager.
- protected IInstallationManager InstallationManager { get; private set; }
-
- protected IAuthService AuthService { get; private set; }
-
- public IStartupOptions StartupOptions { get; }
-
- internal IImageEncoder ImageEncoder { get; private set; }
-
- protected readonly IXmlSerializer XmlSerializer;
-
- protected ISocketFactory SocketFactory { get; private set; }
-
- protected ITaskManager TaskManager { get; private set; }
-
- public IHttpClient HttpClient { get; private set; }
-
- protected INetworkManager NetworkManager { get; set; }
-
- public IJsonSerializer JsonSerializer { get; private set; }
-
- protected IIsoManager IsoManager { get; private set; }
-
///
/// Initializes a new instance of the class.
///
@@ -344,29 +240,39 @@ namespace Emby.Server.Implementations
ILoggerFactory loggerFactory,
IStartupOptions options,
IFileSystem fileSystem,
- IImageEncoder imageEncoder,
INetworkManager networkManager)
{
- XmlSerializer = new MyXmlSerializer();
+ _xmlSerializer = new MyXmlSerializer();
- NetworkManager = networkManager;
+ _networkManager = networkManager;
networkManager.LocalSubnetsFn = GetConfiguredLocalSubnets;
ApplicationPaths = applicationPaths;
LoggerFactory = loggerFactory;
- FileSystemManager = fileSystem;
+ _fileSystemManager = fileSystem;
- ConfigurationManager = new ServerConfigurationManager(ApplicationPaths, LoggerFactory, XmlSerializer, FileSystemManager);
+ ConfigurationManager = new ServerConfigurationManager(ApplicationPaths, LoggerFactory, _xmlSerializer, _fileSystemManager);
- Logger = LoggerFactory.CreateLogger("App");
+ Logger = LoggerFactory.CreateLogger();
- StartupOptions = options;
+ _startupOptions = options;
- ImageEncoder = imageEncoder;
+ // Initialize runtime stat collection
+ if (ServerConfigurationManager.Configuration.EnableMetrics)
+ {
+ DotNetRuntimeStatsBuilder.Default().StartCollecting();
+ }
fileSystem.AddShortcutHandler(new MbLinkShortcutHandler(fileSystem));
- NetworkManager.NetworkChanged += OnNetworkChanged;
+ _networkManager.NetworkChanged += OnNetworkChanged;
+
+ CertificateInfo = new CertificateInfo
+ {
+ Path = ServerConfigurationManager.Configuration.CertificatePath,
+ Password = ServerConfigurationManager.Configuration.CertificatePassword
+ };
+ Certificate = GetCertificate(CertificateInfo);
}
public string ExpandVirtualPath(string path)
@@ -434,10 +340,7 @@ namespace Emby.Server.Implementations
}
}
- ///
- /// Gets the name.
- ///
- /// The name.
+ ///
public string Name => ApplicationProductName;
///
@@ -527,7 +430,7 @@ namespace Emby.Server.Implementations
ConfigurationManager.ConfigurationUpdated += OnConfigurationUpdated;
- MediaEncoder.SetFFmpegPath();
+ _mediaEncoder.SetFFmpegPath();
Logger.LogInformation("ServerId: {0}", SystemId);
@@ -539,7 +442,7 @@ namespace Emby.Server.Implementations
Logger.LogInformation("Executed all pre-startup entry points in {Elapsed:g}", stopWatch.Elapsed);
Logger.LogInformation("Core startup complete");
- HttpServer.GlobalResponse = null;
+ _httpServer.GlobalResponse = null;
stopWatch.Restart();
await Task.WhenAll(StartEntryPoints(entryPoints, false)).ConfigureAwait(false);
@@ -563,7 +466,7 @@ namespace Emby.Server.Implementations
}
///
- public async Task InitAsync(IServiceCollection serviceCollection, IConfiguration startupConfig)
+ public void Init(IServiceCollection serviceCollection)
{
HttpPort = ServerConfigurationManager.Configuration.HttpServerPortNumber;
HttpsPort = ServerConfigurationManager.Configuration.HttpsPortNumber;
@@ -575,8 +478,6 @@ namespace Emby.Server.Implementations
HttpsPort = ServerConfiguration.DefaultHttpsPort;
}
- JsonSerializer = new JsonSerializer();
-
if (Plugins != null)
{
var pluginBuilder = new StringBuilder();
@@ -596,41 +497,19 @@ namespace Emby.Server.Implementations
DiscoverTypes();
- await RegisterServices(serviceCollection, startupConfig).ConfigureAwait(false);
+ RegisterServices(serviceCollection);
}
- public async Task ExecuteWebsocketHandlerAsync(HttpContext context, Func next)
- {
- if (!context.WebSockets.IsWebSocketRequest)
- {
- await next().ConfigureAwait(false);
- return;
- }
-
- await HttpServer.ProcessWebSocketRequest(context).ConfigureAwait(false);
- }
-
- public async Task ExecuteHttpHandlerAsync(HttpContext context, Func next)
- {
- if (context.WebSockets.IsWebSocketRequest)
- {
- await next().ConfigureAwait(false);
- return;
- }
-
- var request = context.Request;
- var response = context.Response;
- var localPath = context.Request.Path.ToString();
-
- var req = new WebSocketSharpRequest(request, response, request.Path, LoggerFactory.CreateLogger());
- await HttpServer.RequestHandler(req, request.GetDisplayUrl(), request.Host.ToString(), localPath, context.RequestAborted).ConfigureAwait(false);
- }
+ public Task ExecuteHttpHandlerAsync(HttpContext context, Func next)
+ => _httpServer.RequestHandler(context);
///
/// Registers services/resources with the service collection that will be available via DI.
///
- protected async Task RegisterServices(IServiceCollection serviceCollection, IConfiguration startupConfig)
+ protected virtual void RegisterServices(IServiceCollection serviceCollection)
{
+ serviceCollection.AddSingleton(_startupOptions);
+
serviceCollection.AddMemoryCache();
serviceCollection.AddSingleton(ConfigurationManager);
@@ -638,240 +517,159 @@ namespace Emby.Server.Implementations
serviceCollection.AddSingleton(ApplicationPaths);
- serviceCollection.AddSingleton(JsonSerializer);
-
- // TODO: Support for injecting ILogger should be deprecated in favour of ILogger and this removed
- serviceCollection.AddSingleton(Logger);
+ serviceCollection.AddSingleton();
- serviceCollection.AddSingleton(FileSystemManager);
+ serviceCollection.AddSingleton(_fileSystemManager);
serviceCollection.AddSingleton();
- HttpClient = new HttpClientManager.HttpClientManager(
- ApplicationPaths,
- LoggerFactory.CreateLogger(),
- FileSystemManager,
- () => ApplicationUserAgent);
- serviceCollection.AddSingleton(HttpClient);
+ serviceCollection.AddSingleton();
- serviceCollection.AddSingleton(NetworkManager);
+ serviceCollection.AddSingleton(_networkManager);
- IsoManager = new IsoManager();
- serviceCollection.AddSingleton(IsoManager);
+ serviceCollection.AddSingleton();
- TaskManager = new TaskManager(ApplicationPaths, JsonSerializer, LoggerFactory, FileSystemManager);
- serviceCollection.AddSingleton(TaskManager);
+ serviceCollection.AddSingleton();
- serviceCollection.AddSingleton(XmlSerializer);
+ serviceCollection.AddSingleton(_xmlSerializer);
- serviceCollection.AddSingleton(typeof(IStreamHelper), typeof(StreamHelper));
+ serviceCollection.AddSingleton();
- var cryptoProvider = new CryptographyProvider();
- serviceCollection.AddSingleton(cryptoProvider);
+ serviceCollection.AddSingleton();
- SocketFactory = new SocketFactory();
- serviceCollection.AddSingleton(SocketFactory);
+ serviceCollection.AddSingleton();
- serviceCollection.AddSingleton(typeof(IInstallationManager), typeof(InstallationManager));
+ serviceCollection.AddSingleton();
- serviceCollection.AddSingleton(typeof(IZipClient), typeof(ZipClient));
+ serviceCollection.AddSingleton();
- serviceCollection.AddSingleton(typeof(IHttpResultFactory), typeof(HttpResultFactory));
+ serviceCollection.AddSingleton();
serviceCollection.AddSingleton(this);
serviceCollection.AddSingleton(ApplicationPaths);
serviceCollection.AddSingleton(ServerConfigurationManager);
- LocalizationManager = new LocalizationManager(ServerConfigurationManager, JsonSerializer, LoggerFactory.CreateLogger());
- await LocalizationManager.LoadAll().ConfigureAwait(false);
- serviceCollection.AddSingleton(LocalizationManager);
+ serviceCollection.AddSingleton();
- serviceCollection.AddSingleton(new BdInfoExaminer(FileSystemManager));
+ serviceCollection.AddSingleton();
- UserDataManager = new UserDataManager(LoggerFactory, ServerConfigurationManager, () => UserManager);
- serviceCollection.AddSingleton(UserDataManager);
+ serviceCollection.AddSingleton();
+ serviceCollection.AddSingleton();
- _displayPreferencesRepository = new SqliteDisplayPreferencesRepository(
- LoggerFactory.CreateLogger(),
- ApplicationPaths,
- FileSystemManager);
- serviceCollection.AddSingleton(_displayPreferencesRepository);
+ serviceCollection.AddSingleton();
- ItemRepository = new SqliteItemRepository(ServerConfigurationManager, this, LoggerFactory.CreateLogger(), LocalizationManager);
- serviceCollection.AddSingleton(ItemRepository);
+ serviceCollection.AddSingleton();
- AuthenticationRepository = GetAuthenticationRepository();
- serviceCollection.AddSingleton(AuthenticationRepository);
+ serviceCollection.AddSingleton();
- _userRepository = GetUserRepository();
+ serviceCollection.AddSingleton();
- UserManager = new UserManager(
- LoggerFactory.CreateLogger(),
- _userRepository,
- XmlSerializer,
- NetworkManager,
- () => ImageProcessor,
- () => DtoService,
- this,
- JsonSerializer,
- FileSystemManager,
- cryptoProvider);
+ // TODO: Refactor to eliminate the circular dependency here so that Lazy isn't required
+ serviceCollection.AddTransient(provider => new Lazy(provider.GetRequiredService));
+ serviceCollection.AddSingleton();
- serviceCollection.AddSingleton(UserManager);
+ // TODO: Refactor to eliminate the circular dependency here so that Lazy isn't required
+ // TODO: Add StartupOptions.FFmpegPath to IConfiguration and remove this custom activation
+ serviceCollection.AddTransient(provider => new Lazy(provider.GetRequiredService));
+ serviceCollection.AddSingleton(provider =>
+ ActivatorUtilities.CreateInstance(provider, _startupOptions.FFmpegPath ?? string.Empty));
- MediaEncoder = new MediaBrowser.MediaEncoding.Encoder.MediaEncoder(
- LoggerFactory.CreateLogger(),
- ServerConfigurationManager,
- FileSystemManager,
- LocalizationManager,
- () => SubtitleEncoder,
- startupConfig,
- StartupOptions.FFmpegPath);
- serviceCollection.AddSingleton(MediaEncoder);
+ // TODO: Refactor to eliminate the circular dependencies here so that Lazy isn't required
+ serviceCollection.AddTransient(provider => new Lazy(provider.GetRequiredService));
+ serviceCollection.AddTransient(provider => new Lazy(provider.GetRequiredService));
+ serviceCollection.AddTransient(provider => new Lazy(provider.GetRequiredService));
+ serviceCollection.AddSingleton();
- LibraryManager = new LibraryManager(this, LoggerFactory, TaskManager, UserManager, ServerConfigurationManager, UserDataManager, () => LibraryMonitor, FileSystemManager, () => ProviderManager, () => UserViewManager, MediaEncoder);
- serviceCollection.AddSingleton(LibraryManager);
+ serviceCollection.AddSingleton();
- var musicManager = new MusicManager(LibraryManager);
- serviceCollection.AddSingleton(musicManager);
+ serviceCollection.AddSingleton();
- LibraryMonitor = new LibraryMonitor(LoggerFactory, LibraryManager, ServerConfigurationManager, FileSystemManager);
- serviceCollection.AddSingleton(LibraryMonitor);
-
- serviceCollection.AddSingleton(new SearchEngine(LoggerFactory, LibraryManager, UserManager));
-
- CertificateInfo = GetCertificateInfo(true);
- Certificate = GetCertificate(CertificateInfo);
+ serviceCollection.AddSingleton();
serviceCollection.AddSingleton();
- serviceCollection.AddSingleton();
serviceCollection.AddSingleton();
- ImageProcessor = new ImageProcessor(LoggerFactory.CreateLogger(), ServerConfigurationManager.ApplicationPaths, FileSystemManager, ImageEncoder, () => LibraryManager, () => MediaEncoder);
- serviceCollection.AddSingleton(ImageProcessor);
-
- TVSeriesManager = new TVSeriesManager(UserManager, UserDataManager, LibraryManager, ServerConfigurationManager);
- serviceCollection.AddSingleton(TVSeriesManager);
-
- 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);
- serviceCollection.AddSingleton(MediaSourceManager);
-
- SubtitleManager = new SubtitleManager(LoggerFactory, FileSystemManager, LibraryMonitor, MediaSourceManager, LocalizationManager);
- serviceCollection.AddSingleton(SubtitleManager);
-
- ProviderManager = new ProviderManager(HttpClient, SubtitleManager, ServerConfigurationManager, LibraryMonitor, LoggerFactory, FileSystemManager, ApplicationPaths, () => LibraryManager, JsonSerializer);
- serviceCollection.AddSingleton(ProviderManager);
-
- DtoService = new DtoService(LoggerFactory, LibraryManager, UserDataManager, ItemRepository, ImageProcessor, ProviderManager, this, () => MediaSourceManager, () => LiveTvManager);
- serviceCollection.AddSingleton(DtoService);
-
- ChannelManager = new ChannelManager(
- UserManager,
- DtoService,
- LibraryManager,
- LoggerFactory.CreateLogger(),
- ServerConfigurationManager,
- FileSystemManager,
- UserDataManager,
- JsonSerializer,
- ProviderManager);
- serviceCollection.AddSingleton(ChannelManager);
-
- SessionManager = new SessionManager(
- LoggerFactory.CreateLogger(),
- UserDataManager,
- LibraryManager,
- UserManager,
- musicManager,
- DtoService,
- ImageProcessor,
- this,
- AuthenticationRepository,
- DeviceManager,
- MediaSourceManager);
- serviceCollection.AddSingleton(SessionManager);
-
- serviceCollection.AddSingleton(
- new DlnaManager(XmlSerializer, FileSystemManager, ApplicationPaths, LoggerFactory, JsonSerializer, this));
-
- CollectionManager = new CollectionManager(LibraryManager, ApplicationPaths, LocalizationManager, FileSystemManager, LibraryMonitor, LoggerFactory, ProviderManager);
- serviceCollection.AddSingleton(CollectionManager);
-
- serviceCollection.AddSingleton(typeof(IPlaylistManager), typeof(PlaylistManager));
-
- LiveTvManager = new LiveTvManager(this, ServerConfigurationManager, LoggerFactory, ItemRepository, ImageProcessor, UserDataManager, DtoService, UserManager, LibraryManager, TaskManager, LocalizationManager, JsonSerializer, FileSystemManager, () => ChannelManager);
- serviceCollection.AddSingleton(LiveTvManager);
-
- UserViewManager = new UserViewManager(LibraryManager, LocalizationManager, UserManager, ChannelManager, LiveTvManager, ServerConfigurationManager);
- serviceCollection.AddSingleton(UserViewManager);
-
- NotificationManager = new NotificationManager(
- LoggerFactory.CreateLogger(),
- UserManager,
- ServerConfigurationManager);
- serviceCollection.AddSingleton(NotificationManager);
-
- serviceCollection.AddSingleton(new DeviceDiscovery(ServerConfigurationManager));
-
- ChapterManager = new ChapterManager(ItemRepository);
- serviceCollection.AddSingleton(ChapterManager);
-
- EncodingManager = new MediaEncoder.EncodingManager(
- LoggerFactory.CreateLogger(),
- FileSystemManager,
- MediaEncoder,
- ChapterManager,
- LibraryManager);
- serviceCollection.AddSingleton(EncodingManager);
-
- var activityLogRepo = GetActivityLogRepository();
- serviceCollection.AddSingleton(activityLogRepo);
- serviceCollection.AddSingleton(new ActivityManager(activityLogRepo, UserManager));
-
- var authContext = new AuthorizationContext(AuthenticationRepository, UserManager);
- serviceCollection.AddSingleton(authContext);
- serviceCollection.AddSingleton(new SessionContext(UserManager, authContext, SessionManager));
-
- AuthService = new AuthService(LoggerFactory.CreateLogger(), authContext, ServerConfigurationManager, SessionManager, NetworkManager);
- serviceCollection.AddSingleton(AuthService);
-
- SubtitleEncoder = new MediaBrowser.MediaEncoding.Subtitles.SubtitleEncoder(
- LibraryManager,
- LoggerFactory.CreateLogger(),
- ApplicationPaths,
- FileSystemManager,
- MediaEncoder,
- HttpClient,
- MediaSourceManager);
- serviceCollection.AddSingleton(SubtitleEncoder);
-
- serviceCollection.AddSingleton(typeof(IResourceFileManager), typeof(ResourceFileManager));
- serviceCollection.AddSingleton();
+ serviceCollection.AddSingleton();
- serviceCollection.AddSingleton(typeof(IAttachmentExtractor), typeof(MediaBrowser.MediaEncoding.Attachments.AttachmentExtractor));
+ serviceCollection.AddSingleton();
- _displayPreferencesRepository.Initialize();
+ serviceCollection.AddSingleton();
- var userDataRepo = new SqliteUserDataRepository(LoggerFactory.CreateLogger(), ApplicationPaths);
+ serviceCollection.AddSingleton();
- SetStaticProperties();
+ serviceCollection.AddSingleton();
+
+ serviceCollection.AddSingleton();
+
+ // TODO: Refactor to eliminate the circular dependency here so that Lazy isn't required
+ serviceCollection.AddTransient(provider => new Lazy(provider.GetRequiredService));
+ serviceCollection.AddSingleton();
+
+ serviceCollection.AddSingleton();
+
+ serviceCollection.AddSingleton();
+
+ serviceCollection.AddSingleton();
+
+ serviceCollection.AddSingleton();
+
+ serviceCollection.AddSingleton();
+
+ serviceCollection.AddSingleton();
+
+ serviceCollection.AddSingleton();
+ serviceCollection.AddSingleton();
+
+ serviceCollection.AddSingleton();
- ((UserManager)UserManager).Initialize();
+ serviceCollection.AddSingleton();
- ((UserDataManager)UserDataManager).Repository = userDataRepo;
- ItemRepository.Initialize(userDataRepo, UserManager);
- ((LibraryManager)LibraryManager).ItemRepository = ItemRepository;
+ serviceCollection.AddSingleton();
+
+ serviceCollection.AddSingleton();
+
+ serviceCollection.AddSingleton();
+
+ serviceCollection.AddSingleton();
+ serviceCollection.AddSingleton();
+
+ serviceCollection.AddSingleton();
+
+ serviceCollection.AddSingleton();
+
+ serviceCollection.AddSingleton();
+ serviceCollection.AddSingleton();
+
+ serviceCollection.AddSingleton();
}
///
/// Create services registered with the service container that need to be initialized at application startup.
///
- public void InitializeServices()
+ /// A task representing the service initialization operation.
+ public async Task InitializeServices()
{
- HttpServer = Resolve();
+ var localizationManager = (LocalizationManager)Resolve();
+ await localizationManager.LoadAll().ConfigureAwait(false);
+
+ _mediaEncoder = Resolve();
+ _sessionManager = Resolve();
+ _httpServer = Resolve();
+ _httpClient = Resolve();
+
+ ((SqliteDisplayPreferencesRepository)Resolve()).Initialize();
+ ((AuthenticationRepository)Resolve()).Initialize();
+ ((SqliteUserRepository)Resolve()).Initialize();
+
+ SetStaticProperties();
+
+ var userManager = (UserManager)Resolve();
+ userManager.Initialize();
+
+ var userDataRepo = (SqliteUserDataRepository)Resolve();
+ ((SqliteItemRepository)Resolve()).Initialize(userDataRepo, userManager);
+
+ FindParts();
}
public static void LogEnvironmentInfo(ILogger logger, IApplicationPaths appPaths)
@@ -940,75 +738,38 @@ namespace Emby.Server.Implementations
}
}
- ///
- /// Gets the user repository.
- ///
- /// .
- private SqliteUserRepository GetUserRepository()
- {
- var repo = new SqliteUserRepository(
- LoggerFactory.CreateLogger(),
- ApplicationPaths);
-
- repo.Initialize();
-
- return repo;
- }
-
- private IAuthenticationRepository GetAuthenticationRepository()
- {
- var repo = new AuthenticationRepository(LoggerFactory, ServerConfigurationManager);
-
- repo.Initialize();
-
- return repo;
- }
-
- private IActivityRepository GetActivityLogRepository()
- {
- var repo = new ActivityRepository(LoggerFactory.CreateLogger(), ServerConfigurationManager.ApplicationPaths, FileSystemManager);
-
- repo.Initialize();
-
- return repo;
- }
-
///
/// Dirty hacks.
///
private void SetStaticProperties()
{
- ItemRepository.ImageProcessor = ImageProcessor;
-
// For now there's no real way to inject these properly
- BaseItem.Logger = LoggerFactory.CreateLogger("BaseItem");
+ BaseItem.Logger = Resolve>();
BaseItem.ConfigurationManager = ServerConfigurationManager;
- BaseItem.LibraryManager = LibraryManager;
- BaseItem.ProviderManager = ProviderManager;
- BaseItem.LocalizationManager = LocalizationManager;
- BaseItem.ItemRepository = ItemRepository;
- User.UserManager = UserManager;
- BaseItem.FileSystem = FileSystemManager;
- BaseItem.UserDataManager = UserDataManager;
- BaseItem.ChannelManager = ChannelManager;
- Video.LiveTvManager = LiveTvManager;
- Folder.UserViewManager = UserViewManager;
- UserView.TVSeriesManager = TVSeriesManager;
- UserView.CollectionManager = CollectionManager;
- BaseItem.MediaSourceManager = MediaSourceManager;
- CollectionFolder.XmlSerializer = XmlSerializer;
- CollectionFolder.JsonSerializer = JsonSerializer;
+ BaseItem.LibraryManager = Resolve();
+ BaseItem.ProviderManager = Resolve();
+ BaseItem.LocalizationManager = Resolve();
+ BaseItem.ItemRepository = Resolve();
+ User.UserManager = Resolve();
+ BaseItem.FileSystem = _fileSystemManager;
+ BaseItem.UserDataManager = Resolve();
+ BaseItem.ChannelManager = Resolve();
+ Video.LiveTvManager = Resolve();
+ Folder.UserViewManager = Resolve();
+ UserView.TVSeriesManager = Resolve();
+ UserView.CollectionManager = Resolve();
+ BaseItem.MediaSourceManager = Resolve();
+ CollectionFolder.XmlSerializer = _xmlSerializer;
+ CollectionFolder.JsonSerializer = Resolve();
CollectionFolder.ApplicationHost = this;
- AuthenticatedAttribute.AuthService = AuthService;
+ AuthenticatedAttribute.AuthService = Resolve();
}
///
- /// Finds the parts.
+ /// Finds plugin components and register them with the appropriate services.
///
- public void FindParts()
+ private void FindParts()
{
- InstallationManager = ServiceProvider.GetService();
-
if (!ServerConfigurationManager.Configuration.IsPortAuthorized)
{
ServerConfigurationManager.Configuration.IsPortAuthorized = true;
@@ -1021,34 +782,34 @@ namespace Emby.Server.Implementations
.Where(i => i != null)
.ToArray();
- HttpServer.Init(GetExportTypes(), GetExports(), GetUrlPrefixes());
+ _httpServer.Init(GetExportTypes(), GetExports(), GetUrlPrefixes());
- LibraryManager.AddParts(
+ Resolve().AddParts(
GetExports(),
GetExports(),
GetExports(),
GetExports(),
GetExports());
- ProviderManager.AddParts(
+ Resolve().AddParts(
GetExports(),
GetExports(),
GetExports(),
GetExports(),
GetExports());
- LiveTvManager.AddParts(GetExports(), GetExports(), GetExports());
+ Resolve().AddParts(GetExports(), GetExports(), GetExports());
- SubtitleManager.AddParts(GetExports());
+ Resolve().AddParts(GetExports());
- ChannelManager.AddParts(GetExports());
+ Resolve().AddParts(GetExports());
- MediaSourceManager.AddParts(GetExports());
+ Resolve().AddParts(GetExports());
- NotificationManager.AddParts(GetExports(), GetExports());
- UserManager.AddParts(GetExports(), GetExports());
+ Resolve().AddParts(GetExports(), GetExports());
+ Resolve().AddParts(GetExports(), GetExports());
- IsoManager.AddParts(GetExports());
+ Resolve().AddParts(GetExports());
}
private IPlugin LoadPlugin(IPlugin plugin)
@@ -1155,16 +916,6 @@ namespace Emby.Server.Implementations
});
}
- private CertificateInfo GetCertificateInfo(bool generateCertificate)
- {
- // Custom cert
- return new CertificateInfo
- {
- Path = ServerConfigurationManager.Configuration.CertificatePath,
- Password = ServerConfigurationManager.Configuration.CertificatePassword
- };
- }
-
///
/// Called when [configuration updated].
///
@@ -1191,14 +942,13 @@ namespace Emby.Server.Implementations
}
}
- if (!HttpServer.UrlPrefixes.SequenceEqual(GetUrlPrefixes(), StringComparer.OrdinalIgnoreCase))
+ if (!_httpServer.UrlPrefixes.SequenceEqual(GetUrlPrefixes(), StringComparer.OrdinalIgnoreCase))
{
requiresRestart = true;
}
var currentCertPath = CertificateInfo?.Path;
- var newCertInfo = GetCertificateInfo(false);
- var newCertPath = newCertInfo?.Path;
+ var newCertPath = ServerConfigurationManager.Configuration.CertificatePath;
if (!string.Equals(currentCertPath, newCertPath, StringComparison.OrdinalIgnoreCase))
{
@@ -1251,7 +1001,7 @@ namespace Emby.Server.Implementations
{
try
{
- await SessionManager.SendServerRestartNotification(CancellationToken.None).ConfigureAwait(false);
+ await _sessionManager.SendServerRestartNotification(CancellationToken.None).ConfigureAwait(false);
}
catch (Exception ex)
{
@@ -1355,7 +1105,7 @@ namespace Emby.Server.Implementations
IsShuttingDown = IsShuttingDown,
Version = ApplicationVersionString,
WebSocketPortNumber = HttpPort,
- CompletedInstallations = InstallationManager.CompletedInstallations.ToArray(),
+ CompletedInstallations = Resolve().CompletedInstallations.ToArray(),
Id = SystemId,
ProgramDataPath = ApplicationPaths.ProgramDataPath,
WebPath = ApplicationPaths.WebPath,
@@ -1363,9 +1113,6 @@ namespace Emby.Server.Implementations
ItemsByNamePath = ApplicationPaths.InternalMetadataPath,
InternalMetadataPath = ApplicationPaths.InternalMetadataPath,
CachePath = ApplicationPaths.CachePath,
- HttpServerPortNumber = HttpPort,
- SupportsHttps = SupportsHttps,
- HttpsPortNumber = HttpsPort,
OperatingSystem = OperatingSystem.Id.ToString(),
OperatingSystemDisplayName = OperatingSystem.Name,
CanSelfRestart = CanSelfRestart,
@@ -1375,14 +1122,14 @@ namespace Emby.Server.Implementations
ServerName = FriendlyName,
LocalAddress = localAddress,
SupportsLibraryMonitor = true,
- EncoderLocation = MediaEncoder.EncoderLocation,
+ EncoderLocation = _mediaEncoder.EncoderLocation,
SystemArchitecture = RuntimeInformation.OSArchitecture,
- PackageName = StartupOptions.PackageName
+ PackageName = _startupOptions.PackageName
};
}
public IEnumerable GetWakeOnLanInfo()
- => NetworkManager.GetMacAddresses()
+ => _networkManager.GetMacAddresses()
.Select(i => new WakeOnLanInfo(i))
.ToList();
@@ -1401,23 +1148,22 @@ namespace Emby.Server.Implementations
};
}
- public bool EnableHttps => SupportsHttps && ServerConfigurationManager.Configuration.EnableHttps;
-
- public bool SupportsHttps => Certificate != null || ServerConfigurationManager.Configuration.IsBehindProxy;
+ ///
+ public bool ListenWithHttps => Certificate != null && ServerConfigurationManager.Configuration.EnableHttps;
+ ///
public async Task GetLocalApiUrl(CancellationToken cancellationToken)
{
try
{
// Return the first matched address, if found, or the first known local address
var addresses = await GetLocalIpAddressesInternal(false, 1, cancellationToken).ConfigureAwait(false);
-
- foreach (var address in addresses)
+ if (addresses.Count == 0)
{
- return GetLocalApiUrl(address);
+ return null;
}
- return null;
+ return GetLocalApiUrl(addresses.First());
}
catch (Exception ex)
{
@@ -1460,22 +1206,24 @@ namespace Emby.Server.Implementations
return GetLocalApiUrl(ipAddress.ToString());
}
- ///
- public string GetLocalApiUrl(ReadOnlySpan host)
+ ///
+ public string GetLoopbackHttpApiUrl()
{
- var url = new StringBuilder(64);
- url.Append(EnableHttps ? "https://" : "http://")
- .Append(host)
- .Append(':')
- .Append(EnableHttps ? HttpsPort : HttpPort);
-
- string baseUrl = ServerConfigurationManager.Configuration.BaseUrl;
- if (baseUrl.Length != 0)
- {
- url.Append(baseUrl);
- }
+ return GetLocalApiUrl("127.0.0.1", Uri.UriSchemeHttp, HttpPort);
+ }
- return url.ToString();
+ ///
+ public string GetLocalApiUrl(ReadOnlySpan host, string scheme = null, int? port = null)
+ {
+ // NOTE: If no BaseUrl is set then UriBuilder appends a trailing slash, but if there is no BaseUrl it does
+ // not. For consistency, always trim the trailing slash.
+ return new UriBuilder
+ {
+ Scheme = scheme ?? (ListenWithHttps ? Uri.UriSchemeHttps : Uri.UriSchemeHttp),
+ Host = host.ToString(),
+ Port = port ?? (ListenWithHttps ? HttpsPort : HttpPort),
+ Path = ServerConfigurationManager.Configuration.BaseUrl
+ }.ToString().TrimEnd('/');
}
public Task> GetLocalIpAddresses(CancellationToken cancellationToken)
@@ -1494,7 +1242,7 @@ namespace Emby.Server.Implementations
if (addresses.Count == 0)
{
- addresses.AddRange(NetworkManager.GetLocalIpAddresses(ServerConfigurationManager.Configuration.IgnoreVirtualInterfaces));
+ addresses.AddRange(_networkManager.GetLocalIpAddresses(ServerConfigurationManager.Configuration.IgnoreVirtualInterfaces));
}
var resultList = new List();
@@ -1509,7 +1257,7 @@ namespace Emby.Server.Implementations
}
}
- var valid = await IsIpAddressValidAsync(address, cancellationToken).ConfigureAwait(false);
+ var valid = await IsLocalIpAddressValidAsync(address, cancellationToken).ConfigureAwait(false);
if (valid)
{
resultList.Add(address);
@@ -1543,7 +1291,7 @@ namespace Emby.Server.Implementations
private readonly ConcurrentDictionary _validAddressResults = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase);
- private async Task IsIpAddressValidAsync(IPAddress address, CancellationToken cancellationToken)
+ private async Task IsLocalIpAddressValidAsync(IPAddress address, CancellationToken cancellationToken)
{
if (address.Equals(IPAddress.Loopback)
|| address.Equals(IPAddress.IPv6Loopback))
@@ -1551,8 +1299,7 @@ namespace Emby.Server.Implementations
return true;
}
- var apiUrl = GetLocalApiUrl(address);
- apiUrl += "/system/ping";
+ var apiUrl = GetLocalApiUrl(address) + "/system/ping";
if (_validAddressResults.TryGetValue(apiUrl, out var cachedResult))
{
@@ -1561,7 +1308,7 @@ namespace Emby.Server.Implementations
try
{
- using (var response = await HttpClient.SendAsync(
+ using (var response = await _httpClient.SendAsync(
new HttpRequestOptions
{
Url = apiUrl,
@@ -1614,7 +1361,7 @@ namespace Emby.Server.Implementations
try
{
- await SessionManager.SendServerShutdownNotification(CancellationToken.None).ConfigureAwait(false);
+ await _sessionManager.SendServerShutdownNotification(CancellationToken.None).ConfigureAwait(false);
}
catch (Exception ex)
{
@@ -1735,14 +1482,8 @@ namespace Emby.Server.Implementations
Logger.LogError(ex, "Error disposing {Type}", part.GetType().Name);
}
}
-
- _userRepository?.Dispose();
- _displayPreferencesRepository?.Dispose();
}
- _userRepository = null;
- _displayPreferencesRepository = null;
-
_disposed = true;
}
}
diff --git a/Emby.Server.Implementations/Archiving/ZipClient.cs b/Emby.Server.Implementations/Archiving/ZipClient.cs
index e01495e192..591ae547d6 100644
--- a/Emby.Server.Implementations/Archiving/ZipClient.cs
+++ b/Emby.Server.Implementations/Archiving/ZipClient.cs
@@ -22,10 +22,8 @@ namespace Emby.Server.Implementations.Archiving
/// | | |