mirror of
https://github.com/jellyfin/jellyfin.git
synced 2025-07-09 03:04:24 -04:00
Merge remote-tracking branch 'jellyfin/master'
This commit is contained in:
commit
918df5e352
96
.ci/azure-pipelines-compat.yml
Normal file
96
.ci/azure-pipelines-compat.yml
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
parameters:
|
||||||
|
- name: Packages
|
||||||
|
type: object
|
||||||
|
default: {}
|
||||||
|
- name: LinuxImage
|
||||||
|
type: string
|
||||||
|
default: "ubuntu-latest"
|
||||||
|
- name: DotNetSdkVersion
|
||||||
|
type: string
|
||||||
|
default: 3.1.100
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
- job: CompatibilityCheck
|
||||||
|
displayName: Compatibility Check
|
||||||
|
pool:
|
||||||
|
vmImage: "${{ parameters.LinuxImage }}"
|
||||||
|
# only execute for pull requests
|
||||||
|
condition: and(succeeded(), variables['System.PullRequest.PullRequestNumber'])
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
${{ each Package in parameters.Packages }}:
|
||||||
|
${{ Package.key }}:
|
||||||
|
NugetPackageName: ${{ Package.value.NugetPackageName }}
|
||||||
|
AssemblyFileName: ${{ Package.value.AssemblyFileName }}
|
||||||
|
maxParallel: 2
|
||||||
|
dependsOn: MainBuild
|
||||||
|
steps:
|
||||||
|
- checkout: none
|
||||||
|
|
||||||
|
- task: UseDotNet@2
|
||||||
|
displayName: "Update DotNet"
|
||||||
|
inputs:
|
||||||
|
packageType: sdk
|
||||||
|
version: ${{ parameters.DotNetSdkVersion }}
|
||||||
|
|
||||||
|
- task: DownloadPipelineArtifact@2
|
||||||
|
displayName: "Download New Assembly Build Artifact"
|
||||||
|
inputs:
|
||||||
|
source: "current"
|
||||||
|
artifact: "$(NugetPackageName)"
|
||||||
|
path: "$(System.ArtifactsDirectory)/new-artifacts"
|
||||||
|
runVersion: "latest"
|
||||||
|
|
||||||
|
- task: CopyFiles@2
|
||||||
|
displayName: "Copy New Assembly Build Artifact"
|
||||||
|
inputs:
|
||||||
|
sourceFolder: $(System.ArtifactsDirectory)/new-artifacts
|
||||||
|
contents: "**/*.dll"
|
||||||
|
targetFolder: $(System.ArtifactsDirectory)/new-release
|
||||||
|
cleanTargetFolder: true
|
||||||
|
overWrite: true
|
||||||
|
flattenFolders: true
|
||||||
|
|
||||||
|
- task: DownloadPipelineArtifact@2
|
||||||
|
displayName: "Download Reference Assembly Build Artifact"
|
||||||
|
inputs:
|
||||||
|
source: "specific"
|
||||||
|
artifact: "$(NugetPackageName)"
|
||||||
|
path: "$(System.ArtifactsDirectory)/current-artifacts"
|
||||||
|
project: "$(System.TeamProjectId)"
|
||||||
|
pipeline: "$(System.DefinitionId)"
|
||||||
|
runVersion: "latestFromBranch"
|
||||||
|
runBranch: "refs/heads/$(System.PullRequest.TargetBranch)"
|
||||||
|
|
||||||
|
- task: CopyFiles@2
|
||||||
|
displayName: "Copy Reference Assembly Build Artifact"
|
||||||
|
inputs:
|
||||||
|
sourceFolder: $(System.ArtifactsDirectory)/current-artifacts
|
||||||
|
contents: "**/*.dll"
|
||||||
|
targetFolder: $(System.ArtifactsDirectory)/current-release
|
||||||
|
cleanTargetFolder: true
|
||||||
|
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"
|
||||||
|
inputs:
|
||||||
|
script: "dotnet tools/CompatibilityCheckerCLI.dll current-release/$(AssemblyFileName) new-release/$(AssemblyFileName) --azure-pipelines --warnings-only"
|
||||||
|
workingDirectory: $(System.ArtifactsDirectory)
|
101
.ci/azure-pipelines-main.yml
Normal file
101
.ci/azure-pipelines-main.yml
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
parameters:
|
||||||
|
LinuxImage: "ubuntu-latest"
|
||||||
|
RestoreBuildProjects: "Jellyfin.Server/Jellyfin.Server.csproj"
|
||||||
|
DotNetSdkVersion: 3.1.100
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
- job: MainBuild
|
||||||
|
displayName: Main Build
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
Release:
|
||||||
|
BuildConfiguration: Release
|
||||||
|
Debug:
|
||||||
|
BuildConfiguration: Debug
|
||||||
|
maxParallel: 2
|
||||||
|
pool:
|
||||||
|
vmImage: "${{ parameters.LinuxImage }}"
|
||||||
|
steps:
|
||||||
|
- checkout: self
|
||||||
|
clean: true
|
||||||
|
submodules: true
|
||||||
|
persistCredentials: true
|
||||||
|
|
||||||
|
- task: CmdLine@2
|
||||||
|
displayName: "Clone Web Client (Master, Release, or Tag)"
|
||||||
|
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'))
|
||||||
|
inputs:
|
||||||
|
script: "git clone --single-branch --branch $(Build.SourceBranchName) --depth=1 https://github.com/jellyfin/jellyfin-web.git $(Agent.TempDirectory)/jellyfin-web"
|
||||||
|
|
||||||
|
- task: CmdLine@2
|
||||||
|
displayName: "Clone Web Client (PR)"
|
||||||
|
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'))
|
||||||
|
inputs:
|
||||||
|
script: "git clone --single-branch --branch $(System.PullRequest.TargetBranch) --depth 1 https://github.com/jellyfin/jellyfin-web.git $(Agent.TempDirectory)/jellyfin-web"
|
||||||
|
|
||||||
|
- 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'))
|
||||||
|
inputs:
|
||||||
|
versionSpec: "10.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
|
||||||
|
|
||||||
|
- task: UseDotNet@2
|
||||||
|
displayName: "Update DotNet"
|
||||||
|
inputs:
|
||||||
|
packageType: sdk
|
||||||
|
version: ${{ parameters.DotNetSdkVersion }}
|
||||||
|
|
||||||
|
- task: DotNetCoreCLI@2
|
||||||
|
displayName: "Publish Server"
|
||||||
|
inputs:
|
||||||
|
command: publish
|
||||||
|
publishWebProjects: false
|
||||||
|
projects: "${{ parameters.RestoreBuildProjects }}"
|
||||||
|
arguments: "--configuration $(BuildConfiguration) --output $(build.artifactstagingdirectory)"
|
||||||
|
zipAfterPublish: false
|
||||||
|
|
||||||
|
- task: PublishPipelineArtifact@0
|
||||||
|
displayName: "Publish Artifact Naming"
|
||||||
|
condition: and(succeeded(), eq(variables['BuildConfiguration'], 'Release'))
|
||||||
|
inputs:
|
||||||
|
targetPath: "$(build.artifactstagingdirectory)/Jellyfin.Server/Emby.Naming.dll"
|
||||||
|
artifactName: "Jellyfin.Naming"
|
||||||
|
|
||||||
|
- task: PublishPipelineArtifact@0
|
||||||
|
displayName: "Publish Artifact Controller"
|
||||||
|
condition: and(succeeded(), eq(variables['BuildConfiguration'], 'Release'))
|
||||||
|
inputs:
|
||||||
|
targetPath: "$(build.artifactstagingdirectory)/Jellyfin.Server/MediaBrowser.Controller.dll"
|
||||||
|
artifactName: "Jellyfin.Controller"
|
||||||
|
|
||||||
|
- task: PublishPipelineArtifact@0
|
||||||
|
displayName: "Publish Artifact Model"
|
||||||
|
condition: and(succeeded(), eq(variables['BuildConfiguration'], 'Release'))
|
||||||
|
inputs:
|
||||||
|
targetPath: "$(build.artifactstagingdirectory)/Jellyfin.Server/MediaBrowser.Model.dll"
|
||||||
|
artifactName: "Jellyfin.Model"
|
||||||
|
|
||||||
|
- task: PublishPipelineArtifact@0
|
||||||
|
displayName: "Publish Artifact Common"
|
||||||
|
condition: and(succeeded(), eq(variables['BuildConfiguration'], 'Release'))
|
||||||
|
inputs:
|
||||||
|
targetPath: "$(build.artifactstagingdirectory)/Jellyfin.Server/MediaBrowser.Common.dll"
|
||||||
|
artifactName: "Jellyfin.Common"
|
65
.ci/azure-pipelines-test.yml
Normal file
65
.ci/azure-pipelines-test.yml
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
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
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
- job: MainTest
|
||||||
|
displayName: Main Test
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
${{ each imageName in parameters.ImageNames }}:
|
||||||
|
${{ imageName.key }}:
|
||||||
|
ImageName: ${{ imageName.value }}
|
||||||
|
maxParallel: 3
|
||||||
|
pool:
|
||||||
|
vmImage: "$(ImageName)"
|
||||||
|
steps:
|
||||||
|
- checkout: self
|
||||||
|
clean: true
|
||||||
|
submodules: true
|
||||||
|
persistCredentials: false
|
||||||
|
|
||||||
|
- task: UseDotNet@2
|
||||||
|
displayName: "Update DotNet"
|
||||||
|
inputs:
|
||||||
|
packageType: sdk
|
||||||
|
version: ${{ parameters.DotNetSdkVersion }}
|
||||||
|
|
||||||
|
- task: DotNetCoreCLI@2
|
||||||
|
displayName: Run .NET Core CLI tests
|
||||||
|
inputs:
|
||||||
|
command: "test"
|
||||||
|
projects: ${{ parameters.TestProjects }}
|
||||||
|
arguments: '--configuration Release --collect:"XPlat Code Coverage" --settings tests/coverletArgs.runsettings --verbosity minimal "-p:GenerateDocumentationFile=False"'
|
||||||
|
publishTestResults: true
|
||||||
|
testRunTitle: $(Agent.JobName)
|
||||||
|
workingDirectory: "$(Build.SourcesDirectory)"
|
||||||
|
|
||||||
|
- 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)
|
||||||
|
inputs:
|
||||||
|
reports: "$(Agent.TempDirectory)/**/coverage.cobertura.xml"
|
||||||
|
targetdir: "$(Agent.TempDirectory)/merged/"
|
||||||
|
reporttypes: "Cobertura"
|
||||||
|
|
||||||
|
## 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
|
||||||
|
inputs:
|
||||||
|
codeCoverageTool: "cobertura"
|
||||||
|
#summaryFileLocation: '$(Agent.TempDirectory)/**/coverage.cobertura.xml' # !!THIS IS FOR V2
|
||||||
|
summaryFileLocation: "$(Agent.TempDirectory)/merged/**.xml"
|
||||||
|
pathToSources: $(Build.SourcesDirectory)
|
||||||
|
failIfCoverageEmpty: true
|
82
.ci/azure-pipelines-windows.yml
Normal file
82
.ci/azure-pipelines-windows.yml
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
parameters:
|
||||||
|
WindowsImage: "windows-latest"
|
||||||
|
TestProjects: "tests/**/*Tests.csproj"
|
||||||
|
DotNetSdkVersion: 3.1.100
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
- job: PublishWindows
|
||||||
|
displayName: Publish Windows
|
||||||
|
pool:
|
||||||
|
vmImage: ${{ parameters.WindowsImage }}
|
||||||
|
steps:
|
||||||
|
- checkout: self
|
||||||
|
clean: true
|
||||||
|
submodules: true
|
||||||
|
persistCredentials: true
|
||||||
|
|
||||||
|
- task: CmdLine@2
|
||||||
|
displayName: "Clone Web Client (Master, Release, or Tag)"
|
||||||
|
condition: and(succeeded(), or(contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master'), contains(variables['Build.SourceBranch'], 'tag')), 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"
|
||||||
|
|
||||||
|
- task: CmdLine@2
|
||||||
|
displayName: "Clone Web Client (PR)"
|
||||||
|
condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master')), in(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"
|
||||||
|
|
||||||
|
- 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')), in(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI', 'BuildCompletion'))
|
||||||
|
inputs:
|
||||||
|
versionSpec: "10.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')), 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')), 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
|
||||||
|
|
||||||
|
- task: CmdLine@2
|
||||||
|
displayName: "Clone UX Repository"
|
||||||
|
inputs:
|
||||||
|
script: git clone --depth=1 https://github.com/jellyfin/jellyfin-ux $(Agent.TempDirectory)\jellyfin-ux
|
||||||
|
|
||||||
|
- task: PowerShell@2
|
||||||
|
displayName: "Build NSIS Installer"
|
||||||
|
inputs:
|
||||||
|
targetType: "filePath"
|
||||||
|
filePath: ./deployment/windows/build-jellyfin.ps1
|
||||||
|
arguments: -InstallFFMPEG -InstallNSSM -MakeNSIS -InstallTrayApp -UXLocation $(Agent.TempDirectory)\jellyfin-ux -InstallLocation $(build.artifactstagingdirectory)
|
||||||
|
errorActionPreference: "stop"
|
||||||
|
workingDirectory: $(Build.SourcesDirectory)
|
||||||
|
|
||||||
|
- task: CopyFiles@2
|
||||||
|
displayName: "Copy NSIS Installer"
|
||||||
|
inputs:
|
||||||
|
sourceFolder: $(Build.SourcesDirectory)/deployment/windows/
|
||||||
|
contents: "jellyfin*.exe"
|
||||||
|
targetFolder: $(System.ArtifactsDirectory)/setup
|
||||||
|
cleanTargetFolder: true
|
||||||
|
overWrite: true
|
||||||
|
flattenFolders: true
|
||||||
|
|
||||||
|
- task: PublishPipelineArtifact@0
|
||||||
|
displayName: "Publish Artifact Setup"
|
||||||
|
condition: succeeded()
|
||||||
|
inputs:
|
||||||
|
targetPath: "$(build.artifactstagingdirectory)/setup"
|
||||||
|
artifactName: "Jellyfin Server Setup"
|
@ -2,9 +2,11 @@ name: $(Date:yyyyMMdd)$(Rev:.r)
|
|||||||
|
|
||||||
variables:
|
variables:
|
||||||
- name: TestProjects
|
- name: TestProjects
|
||||||
value: 'tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj'
|
value: "tests/**/*Tests.csproj"
|
||||||
- name: RestoreBuildProjects
|
- name: RestoreBuildProjects
|
||||||
value: 'Jellyfin.Server/Jellyfin.Server.csproj'
|
value: "Jellyfin.Server/Jellyfin.Server.csproj"
|
||||||
|
- name: DotNetSdkVersion
|
||||||
|
value: 3.1.100
|
||||||
|
|
||||||
pr:
|
pr:
|
||||||
autoCancel: true
|
autoCancel: true
|
||||||
@ -13,232 +15,26 @@ trigger:
|
|||||||
batch: true
|
batch: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
- job: main_build
|
- template: azure-pipelines-main.yml
|
||||||
displayName: Main Build
|
parameters:
|
||||||
pool:
|
LinuxImage: "ubuntu-latest"
|
||||||
vmImage: ubuntu-latest
|
RestoreBuildProjects: $(RestoreBuildProjects)
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
Release:
|
|
||||||
BuildConfiguration: Release
|
|
||||||
Debug:
|
|
||||||
BuildConfiguration: Debug
|
|
||||||
maxParallel: 2
|
|
||||||
steps:
|
|
||||||
- checkout: self
|
|
||||||
clean: true
|
|
||||||
submodules: true
|
|
||||||
persistCredentials: true
|
|
||||||
|
|
||||||
- task: CmdLine@2
|
- template: azure-pipelines-test.yml
|
||||||
displayName: "Clone Web Client (Master, Release, or Tag)"
|
parameters:
|
||||||
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'))
|
ImageNames:
|
||||||
inputs:
|
Linux: "ubuntu-latest"
|
||||||
script: 'git clone --single-branch --branch $(Build.SourceBranchName) --depth=1 https://github.com/jellyfin/jellyfin-web.git $(Agent.TempDirectory)/jellyfin-web'
|
Windows: "windows-latest"
|
||||||
|
macOS: "macos-latest"
|
||||||
|
|
||||||
- task: CmdLine@2
|
- template: azure-pipelines-windows.yml
|
||||||
displayName: "Clone Web Client (PR)"
|
parameters:
|
||||||
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'))
|
WindowsImage: "windows-latest"
|
||||||
inputs:
|
TestProjects: $(TestProjects)
|
||||||
script: 'git clone --single-branch --branch $(System.PullRequest.TargetBranch) --depth 1 https://github.com/jellyfin/jellyfin-web.git $(Agent.TempDirectory)/jellyfin-web'
|
|
||||||
|
|
||||||
- task: NodeTool@0
|
- template: azure-pipelines-compat.yml
|
||||||
displayName: 'Install Node'
|
parameters:
|
||||||
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'))
|
Packages:
|
||||||
inputs:
|
|
||||||
versionSpec: '10.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 # Optional
|
|
||||||
contents: '**'
|
|
||||||
targetFolder: $(Build.SourcesDirectory)/MediaBrowser.WebDashboard/jellyfin-web
|
|
||||||
cleanTargetFolder: true # Optional
|
|
||||||
overWrite: true # Optional
|
|
||||||
flattenFolders: false # Optional
|
|
||||||
|
|
||||||
- task: UseDotNet@2
|
|
||||||
displayName: 'Update DotNet'
|
|
||||||
inputs:
|
|
||||||
packageType: sdk
|
|
||||||
version: 3.1.100
|
|
||||||
|
|
||||||
- task: DotNetCoreCLI@2
|
|
||||||
displayName: 'Publish Server'
|
|
||||||
inputs:
|
|
||||||
command: publish
|
|
||||||
publishWebProjects: false
|
|
||||||
projects: '$(RestoreBuildProjects)'
|
|
||||||
arguments: '--configuration $(BuildConfiguration) --output $(build.artifactstagingdirectory)'
|
|
||||||
zipAfterPublish: false
|
|
||||||
|
|
||||||
- task: PublishPipelineArtifact@0
|
|
||||||
displayName: 'Publish Artifact Naming'
|
|
||||||
condition: and(eq(variables['BuildConfiguration'], 'Release'), succeeded())
|
|
||||||
inputs:
|
|
||||||
targetPath: '$(build.artifactstagingdirectory)/Jellyfin.Server/Emby.Naming.dll'
|
|
||||||
artifactName: 'Jellyfin.Naming'
|
|
||||||
|
|
||||||
- task: PublishPipelineArtifact@0
|
|
||||||
displayName: 'Publish Artifact Controller'
|
|
||||||
condition: and(eq(variables['BuildConfiguration'], 'Release'), succeeded())
|
|
||||||
inputs:
|
|
||||||
targetPath: '$(build.artifactstagingdirectory)/Jellyfin.Server/MediaBrowser.Controller.dll'
|
|
||||||
artifactName: 'Jellyfin.Controller'
|
|
||||||
|
|
||||||
- task: PublishPipelineArtifact@0
|
|
||||||
displayName: 'Publish Artifact Model'
|
|
||||||
condition: and(eq(variables['BuildConfiguration'], 'Release'), succeeded())
|
|
||||||
inputs:
|
|
||||||
targetPath: '$(build.artifactstagingdirectory)/Jellyfin.Server/MediaBrowser.Model.dll'
|
|
||||||
artifactName: 'Jellyfin.Model'
|
|
||||||
|
|
||||||
- task: PublishPipelineArtifact@0
|
|
||||||
displayName: 'Publish Artifact Common'
|
|
||||||
condition: and(eq(variables['BuildConfiguration'], 'Release'), succeeded())
|
|
||||||
inputs:
|
|
||||||
targetPath: '$(build.artifactstagingdirectory)/Jellyfin.Server/MediaBrowser.Common.dll'
|
|
||||||
artifactName: 'Jellyfin.Common'
|
|
||||||
|
|
||||||
- job: main_test
|
|
||||||
displayName: Main Test
|
|
||||||
pool:
|
|
||||||
vmImage: windows-latest
|
|
||||||
steps:
|
|
||||||
- checkout: self
|
|
||||||
clean: true
|
|
||||||
submodules: true
|
|
||||||
persistCredentials: false
|
|
||||||
|
|
||||||
- task: DotNetCoreCLI@2
|
|
||||||
displayName: Build
|
|
||||||
inputs:
|
|
||||||
command: build
|
|
||||||
publishWebProjects: false
|
|
||||||
projects: '$(TestProjects)'
|
|
||||||
arguments: '--configuration $(BuildConfiguration)'
|
|
||||||
zipAfterPublish: false
|
|
||||||
|
|
||||||
- task: VisualStudioTestPlatformInstaller@1
|
|
||||||
inputs:
|
|
||||||
packageFeedSelector: 'nugetOrg' # Options: nugetOrg, customFeed, netShare
|
|
||||||
versionSelector: 'latestPreRelease' # Required when packageFeedSelector == NugetOrg || PackageFeedSelector == CustomFeed# Options: latestPreRelease, latestStable, specificVersion
|
|
||||||
|
|
||||||
- task: VSTest@2
|
|
||||||
inputs:
|
|
||||||
testSelector: 'testAssemblies' # Options: testAssemblies, testPlan, testRun
|
|
||||||
testAssemblyVer2: | # Required when testSelector == TestAssemblies
|
|
||||||
**\bin\$(BuildConfiguration)\**\*test*.dll
|
|
||||||
!**\obj\**
|
|
||||||
!**\xunit.runner.visualstudio.testadapter.dll
|
|
||||||
!**\xunit.runner.visualstudio.dotnetcore.testadapter.dll
|
|
||||||
searchFolder: '$(System.DefaultWorkingDirectory)'
|
|
||||||
runInParallel: True # Optional
|
|
||||||
runTestsInIsolation: True # Optional
|
|
||||||
codeCoverageEnabled: True # Optional
|
|
||||||
configuration: 'Debug' # Optional
|
|
||||||
publishRunAttachments: true # Optional
|
|
||||||
|
|
||||||
- job: main_build_win
|
|
||||||
displayName: Publish Windows
|
|
||||||
pool:
|
|
||||||
vmImage: windows-latest
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
Release:
|
|
||||||
BuildConfiguration: Release
|
|
||||||
maxParallel: 2
|
|
||||||
steps:
|
|
||||||
- checkout: self
|
|
||||||
clean: true
|
|
||||||
submodules: true
|
|
||||||
persistCredentials: true
|
|
||||||
|
|
||||||
- task: CmdLine@2
|
|
||||||
displayName: "Clone Web Client (Master, Release, or Tag)"
|
|
||||||
condition: and(succeeded(), or(contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master'), contains(variables['Build.SourceBranch'], 'tag')) ,eq(variables['BuildConfiguration'], 'Release'), 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'
|
|
||||||
|
|
||||||
- task: CmdLine@2
|
|
||||||
displayName: "Clone Web Client (PR)"
|
|
||||||
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'))
|
|
||||||
inputs:
|
|
||||||
script: 'git clone --single-branch --branch $(System.PullRequest.TargetBranch) --depth 1 https://github.com/jellyfin/jellyfin-web.git $(Agent.TempDirectory)/jellyfin-web'
|
|
||||||
|
|
||||||
- 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'))
|
|
||||||
inputs:
|
|
||||||
versionSpec: '10.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 # Optional
|
|
||||||
contents: '**'
|
|
||||||
targetFolder: $(Build.SourcesDirectory)/MediaBrowser.WebDashboard/jellyfin-web
|
|
||||||
cleanTargetFolder: true # Optional
|
|
||||||
overWrite: true # Optional
|
|
||||||
flattenFolders: false # Optional
|
|
||||||
|
|
||||||
- task: CmdLine@2
|
|
||||||
displayName: 'Clone UX Repository'
|
|
||||||
inputs:
|
|
||||||
script: git clone --depth=1 https://github.com/jellyfin/jellyfin-ux $(Agent.TempDirectory)\jellyfin-ux
|
|
||||||
|
|
||||||
- task: PowerShell@2
|
|
||||||
displayName: 'Build NSIS Installer'
|
|
||||||
inputs:
|
|
||||||
targetType: 'filePath' # Optional. Options: filePath, inline
|
|
||||||
filePath: ./deployment/windows/build-jellyfin.ps1 # Required when targetType == FilePath
|
|
||||||
arguments: -InstallFFMPEG -InstallNSSM -MakeNSIS -InstallTrayApp -UXLocation $(Agent.TempDirectory)\jellyfin-ux -InstallLocation $(build.artifactstagingdirectory)
|
|
||||||
errorActionPreference: 'stop' # Optional. Options: stop, continue, silentlyContinue
|
|
||||||
workingDirectory: $(Build.SourcesDirectory) # Optional
|
|
||||||
|
|
||||||
- task: CopyFiles@2
|
|
||||||
displayName: 'Copy NSIS Installer'
|
|
||||||
inputs:
|
|
||||||
sourceFolder: $(Build.SourcesDirectory)/deployment/windows/ # Optional
|
|
||||||
contents: 'jellyfin*.exe'
|
|
||||||
targetFolder: $(System.ArtifactsDirectory)/setup
|
|
||||||
cleanTargetFolder: true # Optional
|
|
||||||
overWrite: true # Optional
|
|
||||||
flattenFolders: true # Optional
|
|
||||||
|
|
||||||
- task: PublishPipelineArtifact@0
|
|
||||||
displayName: 'Publish Artifact Setup'
|
|
||||||
condition: and(eq(variables['BuildConfiguration'], 'Release'), succeeded())
|
|
||||||
inputs:
|
|
||||||
targetPath: '$(build.artifactstagingdirectory)/setup'
|
|
||||||
artifactName: 'Jellyfin Server Setup'
|
|
||||||
|
|
||||||
- job: dotnet_compat
|
|
||||||
displayName: Compatibility Check
|
|
||||||
pool:
|
|
||||||
vmImage: ubuntu-latest
|
|
||||||
dependsOn: main_build
|
|
||||||
# only execute for pull requests
|
|
||||||
condition: and(succeeded(), variables['System.PullRequest.PullRequestNumber'])
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
Naming:
|
Naming:
|
||||||
NugetPackageName: Jellyfin.Naming
|
NugetPackageName: Jellyfin.Naming
|
||||||
AssemblyFileName: Emby.Naming.dll
|
AssemblyFileName: Emby.Naming.dll
|
||||||
@ -251,74 +47,4 @@ jobs:
|
|||||||
Common:
|
Common:
|
||||||
NugetPackageName: Jellyfin.Common
|
NugetPackageName: Jellyfin.Common
|
||||||
AssemblyFileName: MediaBrowser.Common.dll
|
AssemblyFileName: MediaBrowser.Common.dll
|
||||||
maxParallel: 2
|
LinuxImage: "ubuntu-latest"
|
||||||
steps:
|
|
||||||
- checkout: none
|
|
||||||
|
|
||||||
- task: UseDotNet@2
|
|
||||||
displayName: 'Update DotNet'
|
|
||||||
inputs:
|
|
||||||
packageType: sdk
|
|
||||||
version: 3.1.100
|
|
||||||
|
|
||||||
- task: DownloadPipelineArtifact@2
|
|
||||||
displayName: 'Download New Assembly Build Artifact'
|
|
||||||
inputs:
|
|
||||||
source: 'current' # Options: current, specific
|
|
||||||
artifact: '$(NugetPackageName)' # Optional
|
|
||||||
path: '$(System.ArtifactsDirectory)/new-artifacts'
|
|
||||||
runVersion: 'latest' # Required when source == Specific. Options: latest, latestFromBranch, specific
|
|
||||||
|
|
||||||
- task: CopyFiles@2
|
|
||||||
displayName: 'Copy New Assembly Build Artifact'
|
|
||||||
inputs:
|
|
||||||
sourceFolder: $(System.ArtifactsDirectory)/new-artifacts # Optional
|
|
||||||
contents: '**/*.dll'
|
|
||||||
targetFolder: $(System.ArtifactsDirectory)/new-release
|
|
||||||
cleanTargetFolder: true # Optional
|
|
||||||
overWrite: true # Optional
|
|
||||||
flattenFolders: true # Optional
|
|
||||||
|
|
||||||
- task: DownloadPipelineArtifact@2
|
|
||||||
displayName: 'Download Reference Assembly Build Artifact'
|
|
||||||
inputs:
|
|
||||||
source: 'specific' # Options: current, specific
|
|
||||||
artifact: '$(NugetPackageName)' # Optional
|
|
||||||
path: '$(System.ArtifactsDirectory)/current-artifacts'
|
|
||||||
project: '$(System.TeamProjectId)' # Required when source == Specific
|
|
||||||
pipeline: '$(System.DefinitionId)' # Required when source == Specific
|
|
||||||
runVersion: 'latestFromBranch' # Required when source == Specific. Options: latest, latestFromBranch, specific
|
|
||||||
runBranch: 'refs/heads/$(System.PullRequest.TargetBranch)' # Required when source == Specific && runVersion == LatestFromBranch
|
|
||||||
|
|
||||||
- task: CopyFiles@2
|
|
||||||
displayName: 'Copy Reference Assembly Build Artifact'
|
|
||||||
inputs:
|
|
||||||
sourceFolder: $(System.ArtifactsDirectory)/current-artifacts # Optional
|
|
||||||
contents: '**/*.dll'
|
|
||||||
targetFolder: $(System.ArtifactsDirectory)/current-release
|
|
||||||
cleanTargetFolder: true # Optional
|
|
||||||
overWrite: true # Optional
|
|
||||||
flattenFolders: true # Optional
|
|
||||||
|
|
||||||
- task: DownloadGitHubRelease@0
|
|
||||||
displayName: 'Download ABI Compatibility Check Tool'
|
|
||||||
inputs:
|
|
||||||
connection: Jellyfin Release Download
|
|
||||||
userRepository: EraYaN/dotnet-compatibility
|
|
||||||
defaultVersionType: 'latest' # Options: latest, specificVersion, specificTag
|
|
||||||
itemPattern: '**-ci.zip' # Optional
|
|
||||||
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'
|
|
||||||
inputs:
|
|
||||||
script: 'dotnet tools/CompatibilityCheckerCLI.dll current-release/$(AssemblyFileName) new-release/$(AssemblyFileName) --azure-pipelines --warnings-only'
|
|
||||||
workingDirectory: $(System.ArtifactsDirectory) # Optional
|
|
||||||
|
@ -1,46 +0,0 @@
|
|||||||
name: Nightly-$(date:yyyyMMdd).$(rev:r)
|
|
||||||
|
|
||||||
variables:
|
|
||||||
- name: Version
|
|
||||||
value: '1.0.0'
|
|
||||||
|
|
||||||
trigger: none
|
|
||||||
pr: none
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
- job: publish_artifacts_nightly
|
|
||||||
displayName: Publish Artifacts Nightly
|
|
||||||
pool:
|
|
||||||
vmImage: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- checkout: none
|
|
||||||
- task: DownloadPipelineArtifact@2
|
|
||||||
displayName: Download the Windows Setup Artifact
|
|
||||||
inputs:
|
|
||||||
source: 'specific' # Options: current, specific
|
|
||||||
artifact: 'Jellyfin Server Setup' # Optional
|
|
||||||
path: '$(System.ArtifactsDirectory)/win-installer'
|
|
||||||
project: '$(System.TeamProjectId)' # Required when source == Specific
|
|
||||||
pipelineId: 1 # Required when source == Specific
|
|
||||||
runVersion: 'latestFromBranch' # Required when source == Specific. Options: latest, latestFromBranch, specific
|
|
||||||
runBranch: 'refs/heads/master' # Required when source == Specific && runVersion == LatestFromBranch
|
|
||||||
|
|
||||||
- task: SSH@0
|
|
||||||
displayName: 'Create Drop directory'
|
|
||||||
inputs:
|
|
||||||
sshEndpoint: 'Jellyfin Build Server'
|
|
||||||
commands: 'mkdir -p /srv/incoming/jellyfin_$(Version)/win-installer && ln -s /srv/incoming/jellyfin_$(Version) /srv/incoming/jellyfin_nightly_azure_upload'
|
|
||||||
|
|
||||||
- task: CopyFilesOverSSH@0
|
|
||||||
displayName: 'Copy the Windows Setup to the Repo'
|
|
||||||
inputs:
|
|
||||||
sshEndpoint: 'Jellyfin Build Server'
|
|
||||||
sourceFolder: '$(System.ArtifactsDirectory)/win-installer'
|
|
||||||
contents: 'jellyfin_*.exe'
|
|
||||||
targetFolder: '/srv/incoming/jellyfin_nightly_azure_upload/win-installer'
|
|
||||||
|
|
||||||
- task: SSH@0
|
|
||||||
displayName: 'Clean up SCP symlink'
|
|
||||||
inputs:
|
|
||||||
sshEndpoint: 'Jellyfin Build Server'
|
|
||||||
commands: 'rm -f /srv/incoming/jellyfin_nightly_azure_upload'
|
|
@ -1,48 +0,0 @@
|
|||||||
name: Release-$(Version)-$(date:yyyyMMdd).$(rev:r)
|
|
||||||
|
|
||||||
variables:
|
|
||||||
- name: Version
|
|
||||||
value: '1.0.0'
|
|
||||||
- name: UsedRunId
|
|
||||||
value: 0
|
|
||||||
|
|
||||||
trigger: none
|
|
||||||
pr: none
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
- job: publish_artifacts_release
|
|
||||||
displayName: Publish Artifacts Release
|
|
||||||
pool:
|
|
||||||
vmImage: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- checkout: none
|
|
||||||
- task: DownloadPipelineArtifact@2
|
|
||||||
displayName: Download the Windows Setup Artifact
|
|
||||||
inputs:
|
|
||||||
source: 'specific' # Options: current, specific
|
|
||||||
artifact: 'Jellyfin Server Setup' # Optional
|
|
||||||
path: '$(System.ArtifactsDirectory)/win-installer'
|
|
||||||
project: '$(System.TeamProjectId)' # Required when source == Specific
|
|
||||||
pipelineId: 1 # Required when source == Specific
|
|
||||||
runVersion: 'specific' # Required when source == Specific. Options: latest, latestFromBranch, specific
|
|
||||||
runId: $(UsedRunId)
|
|
||||||
|
|
||||||
- task: SSH@0
|
|
||||||
displayName: 'Create Drop directory'
|
|
||||||
inputs:
|
|
||||||
sshEndpoint: 'Jellyfin Build Server'
|
|
||||||
commands: 'mkdir -p /srv/incoming/jellyfin_$(Version)/win-installer && ln -s /srv/incoming/jellyfin_$(Version) /srv/incoming/jellyfin_release_azure_upload'
|
|
||||||
|
|
||||||
- task: CopyFilesOverSSH@0
|
|
||||||
displayName: 'Copy the Windows Setup to the Repo'
|
|
||||||
inputs:
|
|
||||||
sshEndpoint: 'Jellyfin Build Server'
|
|
||||||
sourceFolder: '$(System.ArtifactsDirectory)/win-installer'
|
|
||||||
contents: 'jellyfin_*.exe'
|
|
||||||
targetFolder: '/srv/incoming/jellyfin_release_azure_upload/win-installer'
|
|
||||||
|
|
||||||
- task: SSH@0
|
|
||||||
displayName: 'Clean up SCP symlink'
|
|
||||||
inputs:
|
|
||||||
sshEndpoint: 'Jellyfin Build Server'
|
|
||||||
commands: 'rm -f /srv/incoming/jellyfin_release_azure_upload'
|
|
6
.github/ISSUE_TEMPLATE/media_playback.md
vendored
6
.github/ISSUE_TEMPLATE/media_playback.md
vendored
@ -11,7 +11,10 @@ assignees: ''
|
|||||||
<!-- Use the Media Info tool (set to text format, download here: https://mediaarea.net/en/MediaInfo) or copy the info from the web ui for the file with the playback issue. -->
|
<!-- Use the Media Info tool (set to text format, download here: https://mediaarea.net/en/MediaInfo) or copy the info from the web ui for the file with the playback issue. -->
|
||||||
|
|
||||||
**Logs**
|
**Logs**
|
||||||
<!-- Please paste any log message from during the playback issue, for example the ffmpeg command line can be very useful. -->
|
<!-- Please paste any log messages from during the playback issue. -->
|
||||||
|
|
||||||
|
**FFmpeg Logs**
|
||||||
|
<!-- Please paste any FFmpeg logs if remuxing or transcoding appears to be part of the issue. -->
|
||||||
|
|
||||||
**Stats for Nerds Screenshots**
|
**Stats for Nerds Screenshots**
|
||||||
<!-- If available, add screenshots of the stats for nerds screen to help show the issue problem. -->
|
<!-- If available, add screenshots of the stats for nerds screen to help show the issue problem. -->
|
||||||
@ -29,4 +32,3 @@ assignees: ''
|
|||||||
- Client: [e.g. Web/Browser, webOS, Android, Android TV, Electron]
|
- Client: [e.g. Web/Browser, webOS, Android, Android TV, Electron]
|
||||||
- Browser (if Web client): [e.g. Firefox, Chrome, Safari]
|
- Browser (if Web client): [e.g. Firefox, Chrome, Safari]
|
||||||
- Client and Browser Version: [e.g. 10.3.4 and 68.0]
|
- Client and Browser Version: [e.g. 10.3.4 and 68.0]
|
||||||
|
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
- [nevado](https://github.com/nevado)
|
- [nevado](https://github.com/nevado)
|
||||||
- [mark-monteiro](https://github.com/mark-monteiro)
|
- [mark-monteiro](https://github.com/mark-monteiro)
|
||||||
- [ullmie02](https://github.com/ullmie02)
|
- [ullmie02](https://github.com/ullmie02)
|
||||||
|
- [pR0Ps](https://github.com/pR0Ps)
|
||||||
|
|
||||||
# Emby Contributors
|
# Emby Contributors
|
||||||
|
|
||||||
|
@ -14,7 +14,9 @@ FROM mcr.microsoft.com/dotnet/core/sdk:${DOTNET_VERSION}-buster as builder
|
|||||||
WORKDIR /repo
|
WORKDIR /repo
|
||||||
COPY . .
|
COPY . .
|
||||||
ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
|
ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
|
||||||
RUN dotnet publish Jellyfin.Server --configuration Release --output="/jellyfin" --self-contained --runtime linux-x64 "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none"
|
# because of changes in docker and systemd we need to not build in parallel at the moment
|
||||||
|
# see https://success.docker.com/article/how-to-reserve-resource-temporarily-unavailable-errors-due-to-tasksmax-setting
|
||||||
|
RUN dotnet publish Jellyfin.Server --disable-parallel --configuration Release --output="/jellyfin" --self-contained --runtime linux-x64 "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none"
|
||||||
|
|
||||||
FROM jellyfin/ffmpeg:${FFMPEG_VERSION} as ffmpeg
|
FROM jellyfin/ffmpeg:${FFMPEG_VERSION} as ffmpeg
|
||||||
FROM debian:buster-slim
|
FROM debian:buster-slim
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
# Requires binfm_misc registration
|
|
||||||
# https://github.com/multiarch/qemu-user-static#binfmt_misc-register
|
|
||||||
ARG DOTNET_VERSION=3.1
|
ARG DOTNET_VERSION=3.1
|
||||||
|
|
||||||
|
|
||||||
@ -23,11 +21,10 @@ RUN find . -type d -name obj | xargs -r rm -r
|
|||||||
RUN dotnet publish Jellyfin.Server --configuration Release --output="/jellyfin" --self-contained --runtime linux-arm "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none"
|
RUN dotnet publish Jellyfin.Server --configuration Release --output="/jellyfin" --self-contained --runtime linux-arm "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none"
|
||||||
|
|
||||||
|
|
||||||
FROM multiarch/qemu-user-static:x86_64-arm as qemu
|
FROM debian:buster-slim
|
||||||
FROM debian:stretch-slim-arm32v7
|
|
||||||
COPY --from=qemu /usr/bin/qemu-arm-static /usr/bin
|
|
||||||
RUN apt-get update \
|
RUN apt-get update \
|
||||||
&& apt-get install --no-install-recommends --no-install-suggests -y ffmpeg \
|
&& apt-get install --no-install-recommends --no-install-suggests -y ffmpeg \
|
||||||
|
libssl-dev \
|
||||||
&& rm -rf /var/lib/apt/lists/* \
|
&& rm -rf /var/lib/apt/lists/* \
|
||||||
&& mkdir -p /cache /config /media \
|
&& mkdir -p /cache /config /media \
|
||||||
&& chmod 777 /cache /config /media
|
&& chmod 777 /cache /config /media
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
# Requires binfm_misc registration
|
|
||||||
# https://github.com/multiarch/qemu-user-static#binfmt_misc-register
|
|
||||||
ARG DOTNET_VERSION=3.1
|
ARG DOTNET_VERSION=3.1
|
||||||
|
|
||||||
|
|
||||||
@ -23,11 +21,10 @@ RUN find . -type d -name obj | xargs -r rm -r
|
|||||||
RUN dotnet publish Jellyfin.Server --configuration Release --output="/jellyfin" --self-contained --runtime linux-arm64 "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none"
|
RUN dotnet publish Jellyfin.Server --configuration Release --output="/jellyfin" --self-contained --runtime linux-arm64 "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none"
|
||||||
|
|
||||||
|
|
||||||
FROM multiarch/qemu-user-static:x86_64-aarch64 as qemu
|
FROM debian:buster-slim
|
||||||
FROM debian:stretch-slim-arm64v8
|
|
||||||
COPY --from=qemu /usr/bin/qemu-aarch64-static /usr/bin
|
|
||||||
RUN apt-get update \
|
RUN apt-get update \
|
||||||
&& apt-get install --no-install-recommends --no-install-suggests -y ffmpeg \
|
&& apt-get install --no-install-recommends --no-install-suggests -y ffmpeg \
|
||||||
|
libssl-dev \
|
||||||
&& rm -rf /var/lib/apt/lists/* \
|
&& rm -rf /var/lib/apt/lists/* \
|
||||||
&& mkdir -p /cache /config /media \
|
&& mkdir -p /cache /config /media \
|
||||||
&& chmod 777 /cache /config /media
|
&& chmod 777 /cache /config /media
|
||||||
|
@ -42,7 +42,7 @@ namespace DvdLib.Ifo
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
using (var vmgFs = _fileSystem.GetFileStream(vmgPath.FullName, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read))
|
using (var vmgFs = new FileStream(vmgPath.FullName, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||||
{
|
{
|
||||||
using (var vmgRead = new BigEndianBinaryReader(vmgFs))
|
using (var vmgRead = new BigEndianBinaryReader(vmgFs))
|
||||||
{
|
{
|
||||||
@ -95,7 +95,7 @@ namespace DvdLib.Ifo
|
|||||||
{
|
{
|
||||||
VTSPaths[vtsNum] = vtsPath;
|
VTSPaths[vtsNum] = vtsPath;
|
||||||
|
|
||||||
using (var vtsFs = _fileSystem.GetFileStream(vtsPath, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read))
|
using (var vtsFs = new FileStream(vtsPath, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||||
{
|
{
|
||||||
using (var vtsRead = new BigEndianBinaryReader(vtsFs))
|
using (var vtsRead = new BigEndianBinaryReader(vtsFs))
|
||||||
{
|
{
|
||||||
|
@ -170,32 +170,32 @@ namespace Emby.Dlna.Api
|
|||||||
return _resultFactory.GetResult(Request, xml, XMLContentType);
|
return _resultFactory.GetResult(Request, xml, XMLContentType);
|
||||||
}
|
}
|
||||||
|
|
||||||
public object Post(ProcessMediaReceiverRegistrarControlRequest request)
|
public async Task<object> Post(ProcessMediaReceiverRegistrarControlRequest request)
|
||||||
{
|
{
|
||||||
var response = PostAsync(request.RequestStream, MediaReceiverRegistrar);
|
var response = await PostAsync(request.RequestStream, MediaReceiverRegistrar).ConfigureAwait(false);
|
||||||
|
|
||||||
return _resultFactory.GetResult(Request, response.Xml, XMLContentType);
|
return _resultFactory.GetResult(Request, response.Xml, XMLContentType);
|
||||||
}
|
}
|
||||||
|
|
||||||
public object Post(ProcessContentDirectoryControlRequest request)
|
public async Task<object> Post(ProcessContentDirectoryControlRequest request)
|
||||||
{
|
{
|
||||||
var response = PostAsync(request.RequestStream, ContentDirectory);
|
var response = await PostAsync(request.RequestStream, ContentDirectory).ConfigureAwait(false);
|
||||||
|
|
||||||
return _resultFactory.GetResult(Request, response.Xml, XMLContentType);
|
return _resultFactory.GetResult(Request, response.Xml, XMLContentType);
|
||||||
}
|
}
|
||||||
|
|
||||||
public object Post(ProcessConnectionManagerControlRequest request)
|
public async Task<object> Post(ProcessConnectionManagerControlRequest request)
|
||||||
{
|
{
|
||||||
var response = PostAsync(request.RequestStream, ConnectionManager);
|
var response = await PostAsync(request.RequestStream, ConnectionManager).ConfigureAwait(false);
|
||||||
|
|
||||||
return _resultFactory.GetResult(Request, response.Xml, XMLContentType);
|
return _resultFactory.GetResult(Request, response.Xml, XMLContentType);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ControlResponse PostAsync(Stream requestStream, IUpnpService service)
|
private Task<ControlResponse> PostAsync(Stream requestStream, IUpnpService service)
|
||||||
{
|
{
|
||||||
var id = GetPathValue(2).ToString();
|
var id = GetPathValue(2).ToString();
|
||||||
|
|
||||||
return service.ProcessControlRequest(new ControlRequest
|
return service.ProcessControlRequestAsync(new ControlRequest
|
||||||
{
|
{
|
||||||
Headers = Request.Headers,
|
Headers = Request.Headers,
|
||||||
InputXml = requestStream,
|
InputXml = requestStream,
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
using Emby.Dlna.Service;
|
using Emby.Dlna.Service;
|
||||||
using MediaBrowser.Common.Net;
|
using MediaBrowser.Common.Net;
|
||||||
using MediaBrowser.Controller.Configuration;
|
using MediaBrowser.Controller.Configuration;
|
||||||
@ -20,17 +21,19 @@ namespace Emby.Dlna.ConnectionManager
|
|||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public string GetServiceXml()
|
public string GetServiceXml()
|
||||||
{
|
{
|
||||||
return new ConnectionManagerXmlBuilder().GetXml();
|
return new ConnectionManagerXmlBuilder().GetXml();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ControlResponse ProcessControlRequest(ControlRequest request)
|
/// <inheritdoc />
|
||||||
|
public Task<ControlResponse> ProcessControlRequestAsync(ControlRequest request)
|
||||||
{
|
{
|
||||||
var profile = _dlna.GetProfile(request.Headers) ??
|
var profile = _dlna.GetProfile(request.Headers) ??
|
||||||
_dlna.GetDefaultProfile();
|
_dlna.GetDefaultProfile();
|
||||||
|
|
||||||
return new ControlHandler(_config, _logger, profile).ProcessControlRequest(request);
|
return new ControlHandler(_config, _logger, profile).ProcessControlRequestAsync(request);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Emby.Dlna.Service;
|
using Emby.Dlna.Service;
|
||||||
using MediaBrowser.Common.Net;
|
using MediaBrowser.Common.Net;
|
||||||
using MediaBrowser.Controller.Configuration;
|
using MediaBrowser.Controller.Configuration;
|
||||||
@ -66,12 +67,14 @@ namespace Emby.Dlna.ContentDirectory
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public string GetServiceXml()
|
public string GetServiceXml()
|
||||||
{
|
{
|
||||||
return new ContentDirectoryXmlBuilder().GetXml();
|
return new ContentDirectoryXmlBuilder().GetXml();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ControlResponse ProcessControlRequest(ControlRequest request)
|
/// <inheritdoc />
|
||||||
|
public Task<ControlResponse> ProcessControlRequestAsync(ControlRequest request)
|
||||||
{
|
{
|
||||||
var profile = _dlna.GetProfile(request.Headers) ??
|
var profile = _dlna.GetProfile(request.Headers) ??
|
||||||
_dlna.GetDefaultProfile();
|
_dlna.GetDefaultProfile();
|
||||||
@ -96,7 +99,7 @@ namespace Emby.Dlna.ContentDirectory
|
|||||||
_userViewManager,
|
_userViewManager,
|
||||||
_mediaEncoder,
|
_mediaEncoder,
|
||||||
_tvSeriesManager)
|
_tvSeriesManager)
|
||||||
.ProcessControlRequest(request);
|
.ProcessControlRequestAsync(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
private User GetUser(DeviceProfile profile)
|
private User GetUser(DeviceProfile profile)
|
||||||
|
@ -76,7 +76,7 @@ namespace Emby.Dlna.ContentDirectory
|
|||||||
_profile = profile;
|
_profile = profile;
|
||||||
_config = config;
|
_config = config;
|
||||||
|
|
||||||
_didlBuilder = new DidlBuilder(profile, user, imageProcessor, serverAddress, accessToken, userDataManager, localization, mediaSourceManager, _logger, mediaEncoder);
|
_didlBuilder = new DidlBuilder(profile, user, imageProcessor, serverAddress, accessToken, userDataManager, localization, mediaSourceManager, Logger, mediaEncoder);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override IEnumerable<KeyValuePair<string, string>> GetResult(string methodName, IDictionary<string, string> methodParams)
|
protected override IEnumerable<KeyValuePair<string, string>> GetResult(string methodName, IDictionary<string, string> methodParams)
|
||||||
@ -771,11 +771,11 @@ namespace Emby.Dlna.ContentDirectory
|
|||||||
})
|
})
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
|
||||||
return new QueryResult<ServerItem>
|
return ApplyPaging(new QueryResult<ServerItem>
|
||||||
{
|
{
|
||||||
Items = folders,
|
Items = folders,
|
||||||
TotalRecordCount = folders.Length
|
TotalRecordCount = folders.Length
|
||||||
};
|
}, startIndex, limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
private QueryResult<ServerItem> GetTvFolders(BaseItem item, User user, StubType? stubType, SortCriteria sort, int? startIndex, int? limit)
|
private QueryResult<ServerItem> GetTvFolders(BaseItem item, User user, StubType? stubType, SortCriteria sort, int? startIndex, int? limit)
|
||||||
@ -1336,7 +1336,7 @@ namespace Emby.Dlna.ContentDirectory
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.LogError("Error parsing item Id: {id}. Returning user root folder.", id);
|
Logger.LogError("Error parsing item Id: {id}. Returning user root folder.", id);
|
||||||
|
|
||||||
return new ServerItem(_libraryManager.GetUserRootFolder());
|
return new ServerItem(_libraryManager.GetUserRootFolder());
|
||||||
}
|
}
|
||||||
|
@ -385,7 +385,7 @@ namespace Emby.Dlna
|
|||||||
{
|
{
|
||||||
Directory.CreateDirectory(systemProfilesPath);
|
Directory.CreateDirectory(systemProfilesPath);
|
||||||
|
|
||||||
using (var fileStream = _fileSystem.GetFileStream(path, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read))
|
using (var fileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read))
|
||||||
{
|
{
|
||||||
await stream.CopyToAsync(fileStream);
|
await stream.CopyToAsync(fileStream);
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Emby.Dlna
|
namespace Emby.Dlna
|
||||||
{
|
{
|
||||||
public interface IUpnpService
|
public interface IUpnpService
|
||||||
@ -13,6 +15,6 @@ namespace Emby.Dlna
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="request">The request.</param>
|
/// <param name="request">The request.</param>
|
||||||
/// <returns>ControlResponse.</returns>
|
/// <returns>ControlResponse.</returns>
|
||||||
ControlResponse ProcessControlRequest(ControlRequest request);
|
Task<ControlResponse> ProcessControlRequestAsync(ControlRequest request);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
using Emby.Dlna.Service;
|
using Emby.Dlna.Service;
|
||||||
using MediaBrowser.Common.Net;
|
using MediaBrowser.Common.Net;
|
||||||
using MediaBrowser.Controller.Configuration;
|
using MediaBrowser.Controller.Configuration;
|
||||||
@ -15,17 +16,19 @@ namespace Emby.Dlna.MediaReceiverRegistrar
|
|||||||
_config = config;
|
_config = config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public string GetServiceXml()
|
public string GetServiceXml()
|
||||||
{
|
{
|
||||||
return new MediaReceiverRegistrarXmlBuilder().GetXml();
|
return new MediaReceiverRegistrarXmlBuilder().GetXml();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ControlResponse ProcessControlRequest(ControlRequest request)
|
/// <inheritdoc />
|
||||||
|
public Task<ControlResponse> ProcessControlRequestAsync(ControlRequest request)
|
||||||
{
|
{
|
||||||
return new ControlHandler(
|
return new ControlHandler(
|
||||||
_config,
|
_config,
|
||||||
Logger)
|
Logger)
|
||||||
.ProcessControlRequest(request);
|
.ProcessControlRequestAsync(request);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
using Emby.Dlna.Didl;
|
using Emby.Dlna.Didl;
|
||||||
using MediaBrowser.Controller.Configuration;
|
using MediaBrowser.Controller.Configuration;
|
||||||
@ -15,44 +16,34 @@ namespace Emby.Dlna.Service
|
|||||||
{
|
{
|
||||||
private const string NS_SOAPENV = "http://schemas.xmlsoap.org/soap/envelope/";
|
private const string NS_SOAPENV = "http://schemas.xmlsoap.org/soap/envelope/";
|
||||||
|
|
||||||
protected readonly IServerConfigurationManager Config;
|
protected IServerConfigurationManager Config { get; }
|
||||||
protected readonly ILogger _logger;
|
protected ILogger Logger { get; }
|
||||||
|
|
||||||
protected BaseControlHandler(IServerConfigurationManager config, ILogger logger)
|
protected BaseControlHandler(IServerConfigurationManager config, ILogger logger)
|
||||||
{
|
{
|
||||||
Config = config;
|
Config = config;
|
||||||
_logger = logger;
|
Logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ControlResponse ProcessControlRequest(ControlRequest request)
|
public async Task<ControlResponse> ProcessControlRequestAsync(ControlRequest request)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var enableDebugLogging = Config.GetDlnaConfiguration().EnableDebugLog;
|
LogRequest(request);
|
||||||
|
|
||||||
if (enableDebugLogging)
|
|
||||||
{
|
|
||||||
LogRequest(request);
|
|
||||||
}
|
|
||||||
|
|
||||||
var response = ProcessControlRequestInternal(request);
|
|
||||||
|
|
||||||
if (enableDebugLogging)
|
|
||||||
{
|
|
||||||
LogResponse(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
var response = await ProcessControlRequestInternalAsync(request).ConfigureAwait(false);
|
||||||
|
LogResponse(response);
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.LogError(ex, "Error processing control request");
|
Logger.LogError(ex, "Error processing control request");
|
||||||
|
|
||||||
return new ControlErrorHandler().GetResponse(ex);
|
return ControlErrorHandler.GetResponse(ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ControlResponse ProcessControlRequestInternal(ControlRequest request)
|
private async Task<ControlResponse> ProcessControlRequestInternalAsync(ControlRequest request)
|
||||||
{
|
{
|
||||||
ControlRequestInfo requestInfo = null;
|
ControlRequestInfo requestInfo = null;
|
||||||
|
|
||||||
@ -63,16 +54,17 @@ namespace Emby.Dlna.Service
|
|||||||
ValidationType = ValidationType.None,
|
ValidationType = ValidationType.None,
|
||||||
CheckCharacters = false,
|
CheckCharacters = false,
|
||||||
IgnoreProcessingInstructions = true,
|
IgnoreProcessingInstructions = true,
|
||||||
IgnoreComments = true
|
IgnoreComments = true,
|
||||||
|
Async = true
|
||||||
};
|
};
|
||||||
|
|
||||||
using (var reader = XmlReader.Create(streamReader, readerSettings))
|
using (var reader = XmlReader.Create(streamReader, readerSettings))
|
||||||
{
|
{
|
||||||
requestInfo = ParseRequest(reader);
|
requestInfo = await ParseRequestAsync(reader).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.LogDebug("Received control request {0}", requestInfo.LocalName);
|
Logger.LogDebug("Received control request {0}", requestInfo.LocalName);
|
||||||
|
|
||||||
var result = GetResult(requestInfo.LocalName, requestInfo.Headers);
|
var result = GetResult(requestInfo.LocalName, requestInfo.Headers);
|
||||||
|
|
||||||
@ -114,17 +106,15 @@ namespace Emby.Dlna.Service
|
|||||||
IsSuccessful = true
|
IsSuccessful = true
|
||||||
};
|
};
|
||||||
|
|
||||||
//logger.LogDebug(xml);
|
|
||||||
|
|
||||||
controlResponse.Headers.Add("EXT", string.Empty);
|
controlResponse.Headers.Add("EXT", string.Empty);
|
||||||
|
|
||||||
return controlResponse;
|
return controlResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ControlRequestInfo ParseRequest(XmlReader reader)
|
private async Task<ControlRequestInfo> ParseRequestAsync(XmlReader reader)
|
||||||
{
|
{
|
||||||
reader.MoveToContent();
|
await reader.MoveToContentAsync().ConfigureAwait(false);
|
||||||
reader.Read();
|
await reader.ReadAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
// Loop through each element
|
// Loop through each element
|
||||||
while (!reader.EOF && reader.ReadState == ReadState.Interactive)
|
while (!reader.EOF && reader.ReadState == ReadState.Interactive)
|
||||||
@ -139,37 +129,38 @@ namespace Emby.Dlna.Service
|
|||||||
{
|
{
|
||||||
using (var subReader = reader.ReadSubtree())
|
using (var subReader = reader.ReadSubtree())
|
||||||
{
|
{
|
||||||
return ParseBodyTag(subReader);
|
return await ParseBodyTagAsync(subReader).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
reader.Read();
|
await reader.ReadAsync().ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
reader.Skip();
|
await reader.SkipAsync().ConfigureAwait(false);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
reader.Read();
|
await reader.ReadAsync().ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ControlRequestInfo();
|
return new ControlRequestInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
private ControlRequestInfo ParseBodyTag(XmlReader reader)
|
private async Task<ControlRequestInfo> ParseBodyTagAsync(XmlReader reader)
|
||||||
{
|
{
|
||||||
var result = new ControlRequestInfo();
|
var result = new ControlRequestInfo();
|
||||||
|
|
||||||
reader.MoveToContent();
|
await reader.MoveToContentAsync().ConfigureAwait(false);
|
||||||
reader.Read();
|
await reader.ReadAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
// Loop through each element
|
// Loop through each element
|
||||||
while (!reader.EOF && reader.ReadState == ReadState.Interactive)
|
while (!reader.EOF && reader.ReadState == ReadState.Interactive)
|
||||||
@ -183,28 +174,28 @@ namespace Emby.Dlna.Service
|
|||||||
{
|
{
|
||||||
using (var subReader = reader.ReadSubtree())
|
using (var subReader = reader.ReadSubtree())
|
||||||
{
|
{
|
||||||
ParseFirstBodyChild(subReader, result.Headers);
|
await ParseFirstBodyChildAsync(subReader, result.Headers).ConfigureAwait(false);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
reader.Read();
|
await reader.ReadAsync().ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
reader.Read();
|
await reader.ReadAsync().ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ParseFirstBodyChild(XmlReader reader, IDictionary<string, string> headers)
|
private async Task ParseFirstBodyChildAsync(XmlReader reader, IDictionary<string, string> headers)
|
||||||
{
|
{
|
||||||
reader.MoveToContent();
|
await reader.MoveToContentAsync().ConfigureAwait(false);
|
||||||
reader.Read();
|
await reader.ReadAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
// Loop through each element
|
// Loop through each element
|
||||||
while (!reader.EOF && reader.ReadState == ReadState.Interactive)
|
while (!reader.EOF && reader.ReadState == ReadState.Interactive)
|
||||||
@ -212,20 +203,20 @@ namespace Emby.Dlna.Service
|
|||||||
if (reader.NodeType == XmlNodeType.Element)
|
if (reader.NodeType == XmlNodeType.Element)
|
||||||
{
|
{
|
||||||
// TODO: Should we be doing this here, or should it be handled earlier when decoding the request?
|
// TODO: Should we be doing this here, or should it be handled earlier when decoding the request?
|
||||||
headers[reader.LocalName.RemoveDiacritics()] = reader.ReadElementContentAsString();
|
headers[reader.LocalName.RemoveDiacritics()] = await reader.ReadElementContentAsStringAsync().ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
reader.Read();
|
await reader.ReadAsync().ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ControlRequestInfo
|
private class ControlRequestInfo
|
||||||
{
|
{
|
||||||
public string LocalName;
|
public string LocalName { get; set; }
|
||||||
public string NamespaceURI;
|
public string NamespaceURI { get; set; }
|
||||||
public IDictionary<string, string> Headers = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
public Dictionary<string, string> Headers { get; } = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract IEnumerable<KeyValuePair<string, string>> GetResult(string methodName, IDictionary<string, string> methodParams);
|
protected abstract IEnumerable<KeyValuePair<string, string>> GetResult(string methodName, IDictionary<string, string> methodParams);
|
||||||
@ -237,10 +228,7 @@ namespace Emby.Dlna.Service
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var originalHeaders = request.Headers;
|
Logger.LogDebug("Control request. Headers: {@Headers}", request.Headers);
|
||||||
var headers = string.Join(", ", originalHeaders.Select(i => string.Format("{0}={1}", i.Key, i.Value)).ToArray());
|
|
||||||
|
|
||||||
_logger.LogDebug("Control request. Headers: {0}", headers);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LogResponse(ControlResponse response)
|
private void LogResponse(ControlResponse response)
|
||||||
@ -250,11 +238,7 @@ namespace Emby.Dlna.Service
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var originalHeaders = response.Headers;
|
Logger.LogDebug("Control response. Headers: {@Headers}\n{Xml}", response.Headers, response.Xml);
|
||||||
var headers = string.Join(", ", originalHeaders.Select(i => string.Format("{0}={1}", i.Key, i.Value)).ToArray());
|
|
||||||
//builder.Append(response.Xml);
|
|
||||||
|
|
||||||
_logger.LogDebug("Control response. Headers: {0}", headers);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,11 +6,11 @@ using Emby.Dlna.Didl;
|
|||||||
|
|
||||||
namespace Emby.Dlna.Service
|
namespace Emby.Dlna.Service
|
||||||
{
|
{
|
||||||
public class ControlErrorHandler
|
public static class ControlErrorHandler
|
||||||
{
|
{
|
||||||
private const string NS_SOAPENV = "http://schemas.xmlsoap.org/soap/envelope/";
|
private const string NS_SOAPENV = "http://schemas.xmlsoap.org/soap/envelope/";
|
||||||
|
|
||||||
public ControlResponse GetResponse(Exception ex)
|
public static ControlResponse GetResponse(Exception ex)
|
||||||
{
|
{
|
||||||
var settings = new XmlWriterSettings
|
var settings = new XmlWriterSettings
|
||||||
{
|
{
|
||||||
|
@ -14,7 +14,6 @@ using MediaBrowser.Controller.MediaEncoding;
|
|||||||
using MediaBrowser.Controller.Providers;
|
using MediaBrowser.Controller.Providers;
|
||||||
using MediaBrowser.Model.Drawing;
|
using MediaBrowser.Model.Drawing;
|
||||||
using MediaBrowser.Model.Entities;
|
using MediaBrowser.Model.Entities;
|
||||||
using MediaBrowser.Model.Extensions;
|
|
||||||
using MediaBrowser.Model.IO;
|
using MediaBrowser.Model.IO;
|
||||||
using MediaBrowser.Model.Net;
|
using MediaBrowser.Model.Net;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
@ -129,7 +128,7 @@ namespace Emby.Drawing
|
|||||||
{
|
{
|
||||||
var file = await ProcessImage(options).ConfigureAwait(false);
|
var file = await ProcessImage(options).ConfigureAwait(false);
|
||||||
|
|
||||||
using (var fileStream = _fileSystem.GetFileStream(file.Item1, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read, true))
|
using (var fileStream = new FileStream(file.Item1, FileMode.Open, FileAccess.Read, FileShare.Read, IODefaults.FileStreamBufferSize, true))
|
||||||
{
|
{
|
||||||
await fileStream.CopyToAsync(toStream).ConfigureAwait(false);
|
await fileStream.CopyToAsync(toStream).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ using System;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using Emby.Naming.Video;
|
using Emby.Naming.Video;
|
||||||
|
using MediaBrowser.Model.Entities;
|
||||||
|
|
||||||
namespace Emby.Naming.Common
|
namespace Emby.Naming.Common
|
||||||
{
|
{
|
||||||
@ -176,13 +177,12 @@ namespace Emby.Naming.Common
|
|||||||
|
|
||||||
CleanDateTimes = new[]
|
CleanDateTimes = new[]
|
||||||
{
|
{
|
||||||
@"(.+[^ _\,\.\(\)\[\]\-])[ _\.\(\)\[\]\-]+(19[0-9][0-9]|20[0-1][0-9])([ _\,\.\(\)\[\]\-][^0-9]|$)"
|
@"(.+[^_\,\.\(\)\[\]\-])[_\.\(\)\[\]\-](19\d{2}|20\d{2})([ _\,\.\(\)\[\]\-][^0-9]|).*(19\d{2}|20\d{2})*"
|
||||||
};
|
};
|
||||||
|
|
||||||
CleanStrings = new[]
|
CleanStrings = new[]
|
||||||
{
|
{
|
||||||
@"[ _\,\.\(\)\[\]\-](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|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|\[.*\])([ _\,\.\(\)\[\]\-]|$)",
|
|
||||||
@"(\[.*\])"
|
@"(\[.*\])"
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -339,7 +339,7 @@ namespace Emby.Naming.Common
|
|||||||
|
|
||||||
// *** End Kodi Standard Naming
|
// *** End Kodi Standard Naming
|
||||||
|
|
||||||
// [bar] Foo - 1 [baz]
|
// [bar] Foo - 1 [baz]
|
||||||
new EpisodeExpression(@".*?(\[.*?\])+.*?(?<seriesname>[\w\s]+?)[-\s_]+(?<epnumber>\d+).*$")
|
new EpisodeExpression(@".*?(\[.*?\])+.*?(?<seriesname>[\w\s]+?)[-\s_]+(?<epnumber>\d+).*$")
|
||||||
{
|
{
|
||||||
IsNamed = true
|
IsNamed = true
|
||||||
@ -423,126 +423,126 @@ namespace Emby.Naming.Common
|
|||||||
{
|
{
|
||||||
new ExtraRule
|
new ExtraRule
|
||||||
{
|
{
|
||||||
ExtraType = "trailer",
|
ExtraType = ExtraType.Trailer,
|
||||||
RuleType = ExtraRuleType.Filename,
|
RuleType = ExtraRuleType.Filename,
|
||||||
Token = "trailer",
|
Token = "trailer",
|
||||||
MediaType = MediaType.Video
|
MediaType = MediaType.Video
|
||||||
},
|
},
|
||||||
new ExtraRule
|
new ExtraRule
|
||||||
{
|
{
|
||||||
ExtraType = "trailer",
|
ExtraType = ExtraType.Trailer,
|
||||||
RuleType = ExtraRuleType.Suffix,
|
RuleType = ExtraRuleType.Suffix,
|
||||||
Token = "-trailer",
|
Token = "-trailer",
|
||||||
MediaType = MediaType.Video
|
MediaType = MediaType.Video
|
||||||
},
|
},
|
||||||
new ExtraRule
|
new ExtraRule
|
||||||
{
|
{
|
||||||
ExtraType = "trailer",
|
ExtraType = ExtraType.Trailer,
|
||||||
RuleType = ExtraRuleType.Suffix,
|
RuleType = ExtraRuleType.Suffix,
|
||||||
Token = ".trailer",
|
Token = ".trailer",
|
||||||
MediaType = MediaType.Video
|
MediaType = MediaType.Video
|
||||||
},
|
},
|
||||||
new ExtraRule
|
new ExtraRule
|
||||||
{
|
{
|
||||||
ExtraType = "trailer",
|
ExtraType = ExtraType.Trailer,
|
||||||
RuleType = ExtraRuleType.Suffix,
|
RuleType = ExtraRuleType.Suffix,
|
||||||
Token = "_trailer",
|
Token = "_trailer",
|
||||||
MediaType = MediaType.Video
|
MediaType = MediaType.Video
|
||||||
},
|
},
|
||||||
new ExtraRule
|
new ExtraRule
|
||||||
{
|
{
|
||||||
ExtraType = "trailer",
|
ExtraType = ExtraType.Trailer,
|
||||||
RuleType = ExtraRuleType.Suffix,
|
RuleType = ExtraRuleType.Suffix,
|
||||||
Token = " trailer",
|
Token = " trailer",
|
||||||
MediaType = MediaType.Video
|
MediaType = MediaType.Video
|
||||||
},
|
},
|
||||||
new ExtraRule
|
new ExtraRule
|
||||||
{
|
{
|
||||||
ExtraType = "sample",
|
ExtraType = ExtraType.Sample,
|
||||||
RuleType = ExtraRuleType.Filename,
|
RuleType = ExtraRuleType.Filename,
|
||||||
Token = "sample",
|
Token = "sample",
|
||||||
MediaType = MediaType.Video
|
MediaType = MediaType.Video
|
||||||
},
|
},
|
||||||
new ExtraRule
|
new ExtraRule
|
||||||
{
|
{
|
||||||
ExtraType = "sample",
|
ExtraType = ExtraType.Sample,
|
||||||
RuleType = ExtraRuleType.Suffix,
|
RuleType = ExtraRuleType.Suffix,
|
||||||
Token = "-sample",
|
Token = "-sample",
|
||||||
MediaType = MediaType.Video
|
MediaType = MediaType.Video
|
||||||
},
|
},
|
||||||
new ExtraRule
|
new ExtraRule
|
||||||
{
|
{
|
||||||
ExtraType = "sample",
|
ExtraType = ExtraType.Sample,
|
||||||
RuleType = ExtraRuleType.Suffix,
|
RuleType = ExtraRuleType.Suffix,
|
||||||
Token = ".sample",
|
Token = ".sample",
|
||||||
MediaType = MediaType.Video
|
MediaType = MediaType.Video
|
||||||
},
|
},
|
||||||
new ExtraRule
|
new ExtraRule
|
||||||
{
|
{
|
||||||
ExtraType = "sample",
|
ExtraType = ExtraType.Sample,
|
||||||
RuleType = ExtraRuleType.Suffix,
|
RuleType = ExtraRuleType.Suffix,
|
||||||
Token = "_sample",
|
Token = "_sample",
|
||||||
MediaType = MediaType.Video
|
MediaType = MediaType.Video
|
||||||
},
|
},
|
||||||
new ExtraRule
|
new ExtraRule
|
||||||
{
|
{
|
||||||
ExtraType = "sample",
|
ExtraType = ExtraType.Sample,
|
||||||
RuleType = ExtraRuleType.Suffix,
|
RuleType = ExtraRuleType.Suffix,
|
||||||
Token = " sample",
|
Token = " sample",
|
||||||
MediaType = MediaType.Video
|
MediaType = MediaType.Video
|
||||||
},
|
},
|
||||||
new ExtraRule
|
new ExtraRule
|
||||||
{
|
{
|
||||||
ExtraType = "themesong",
|
ExtraType = ExtraType.ThemeSong,
|
||||||
RuleType = ExtraRuleType.Filename,
|
RuleType = ExtraRuleType.Filename,
|
||||||
Token = "theme",
|
Token = "theme",
|
||||||
MediaType = MediaType.Audio
|
MediaType = MediaType.Audio
|
||||||
},
|
},
|
||||||
new ExtraRule
|
new ExtraRule
|
||||||
{
|
{
|
||||||
ExtraType = "scene",
|
ExtraType = ExtraType.Scene,
|
||||||
RuleType = ExtraRuleType.Suffix,
|
RuleType = ExtraRuleType.Suffix,
|
||||||
Token = "-scene",
|
Token = "-scene",
|
||||||
MediaType = MediaType.Video
|
MediaType = MediaType.Video
|
||||||
},
|
},
|
||||||
new ExtraRule
|
new ExtraRule
|
||||||
{
|
{
|
||||||
ExtraType = "clip",
|
ExtraType = ExtraType.Clip,
|
||||||
RuleType = ExtraRuleType.Suffix,
|
RuleType = ExtraRuleType.Suffix,
|
||||||
Token = "-clip",
|
Token = "-clip",
|
||||||
MediaType = MediaType.Video
|
MediaType = MediaType.Video
|
||||||
},
|
},
|
||||||
new ExtraRule
|
new ExtraRule
|
||||||
{
|
{
|
||||||
ExtraType = "interview",
|
ExtraType = ExtraType.Interview,
|
||||||
RuleType = ExtraRuleType.Suffix,
|
RuleType = ExtraRuleType.Suffix,
|
||||||
Token = "-interview",
|
Token = "-interview",
|
||||||
MediaType = MediaType.Video
|
MediaType = MediaType.Video
|
||||||
},
|
},
|
||||||
new ExtraRule
|
new ExtraRule
|
||||||
{
|
{
|
||||||
ExtraType = "behindthescenes",
|
ExtraType = ExtraType.BehindTheScenes,
|
||||||
RuleType = ExtraRuleType.Suffix,
|
RuleType = ExtraRuleType.Suffix,
|
||||||
Token = "-behindthescenes",
|
Token = "-behindthescenes",
|
||||||
MediaType = MediaType.Video
|
MediaType = MediaType.Video
|
||||||
},
|
},
|
||||||
new ExtraRule
|
new ExtraRule
|
||||||
{
|
{
|
||||||
ExtraType = "deletedscene",
|
ExtraType = ExtraType.DeletedScene,
|
||||||
RuleType = ExtraRuleType.Suffix,
|
RuleType = ExtraRuleType.Suffix,
|
||||||
Token = "-deleted",
|
Token = "-deleted",
|
||||||
MediaType = MediaType.Video
|
MediaType = MediaType.Video
|
||||||
},
|
},
|
||||||
new ExtraRule
|
new ExtraRule
|
||||||
{
|
{
|
||||||
ExtraType = "featurette",
|
ExtraType = ExtraType.Clip,
|
||||||
RuleType = ExtraRuleType.Suffix,
|
RuleType = ExtraRuleType.Suffix,
|
||||||
Token = "-featurette",
|
Token = "-featurette",
|
||||||
MediaType = MediaType.Video
|
MediaType = MediaType.Video
|
||||||
},
|
},
|
||||||
new ExtraRule
|
new ExtraRule
|
||||||
{
|
{
|
||||||
ExtraType = "short",
|
ExtraType = ExtraType.Clip,
|
||||||
RuleType = ExtraRuleType.Suffix,
|
RuleType = ExtraRuleType.Suffix,
|
||||||
Token = "-short",
|
Token = "-short",
|
||||||
MediaType = MediaType.Video
|
MediaType = MediaType.Video
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
|
|
||||||
<!-- Code Analyzers-->
|
<!-- Code Analyzers-->
|
||||||
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
|
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||||
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.7" PrivateAssets="All" />
|
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.8" PrivateAssets="All" />
|
||||||
<PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" />
|
<PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" />
|
||||||
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="All" />
|
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="All" />
|
||||||
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" />
|
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" />
|
||||||
|
@ -31,7 +31,6 @@ namespace Emby.Naming.Subtitles
|
|||||||
}
|
}
|
||||||
|
|
||||||
var flags = GetFlags(path);
|
var flags = GetFlags(path);
|
||||||
|
|
||||||
var info = new SubtitleInfo
|
var info = new SubtitleInfo
|
||||||
{
|
{
|
||||||
Path = path,
|
Path = path,
|
||||||
@ -45,7 +44,7 @@ namespace Emby.Naming.Subtitles
|
|||||||
// Should have a name, language and file extension
|
// Should have a name, language and file extension
|
||||||
if (parts.Count >= 3)
|
if (parts.Count >= 3)
|
||||||
{
|
{
|
||||||
info.Language = parts[parts.Count - 2];
|
info.Language = parts[^2];
|
||||||
}
|
}
|
||||||
|
|
||||||
return info;
|
return info;
|
||||||
|
@ -131,7 +131,7 @@ namespace Emby.Naming.TV
|
|||||||
var endingNumberGroup = match.Groups["endingepnumber"];
|
var endingNumberGroup = match.Groups["endingepnumber"];
|
||||||
if (endingNumberGroup.Success)
|
if (endingNumberGroup.Success)
|
||||||
{
|
{
|
||||||
// Will only set EndingEpsiodeNumber if the captured number is not followed by additional numbers
|
// Will only set EndingEpisodeNumber if the captured number is not followed by additional numbers
|
||||||
// or a 'p' or 'i' as what you would get with a pixel resolution specification.
|
// or a 'p' or 'i' as what you would get with a pixel resolution specification.
|
||||||
// It avoids erroneous parsing of something like "series-s09e14-1080p.mkv" as a multi-episode from E14 to E108
|
// It avoids erroneous parsing of something like "series-s09e14-1080p.mkv" as a multi-episode from E14 to E108
|
||||||
int nextIndex = endingNumberGroup.Index + endingNumberGroup.Length;
|
int nextIndex = endingNumberGroup.Index + endingNumberGroup.Length;
|
||||||
|
@ -1,89 +1,48 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
#pragma warning disable SA1600
|
#pragma warning disable SA1600
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
using System;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using Emby.Naming.Common;
|
|
||||||
|
|
||||||
namespace Emby.Naming.Video
|
namespace Emby.Naming.Video
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// <see href="http://kodi.wiki/view/Advancedsettings.xml#video" />.
|
/// <see href="http://kodi.wiki/view/Advancedsettings.xml#video" />.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class CleanDateTimeParser
|
public static class CleanDateTimeParser
|
||||||
{
|
{
|
||||||
private readonly NamingOptions _options;
|
public static CleanDateTimeResult Clean(string name, IReadOnlyList<Regex> cleanDateTimeRegexes)
|
||||||
|
|
||||||
public CleanDateTimeParser(NamingOptions options)
|
|
||||||
{
|
{
|
||||||
_options = options;
|
CleanDateTimeResult result = new CleanDateTimeResult(name);
|
||||||
}
|
var len = cleanDateTimeRegexes.Count;
|
||||||
|
for (int i = 0; i < len; i++)
|
||||||
public CleanDateTimeResult Clean(string name)
|
|
||||||
{
|
|
||||||
var originalName = name;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
var extension = Path.GetExtension(name) ?? string.Empty;
|
if (TryClean(name, cleanDateTimeRegexes[i], ref result))
|
||||||
// Check supported extensions
|
|
||||||
if (!_options.VideoFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase)
|
|
||||||
&& !_options.AudioFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase))
|
|
||||||
{
|
{
|
||||||
// Dummy up a file extension because the expressions will fail without one
|
return result;
|
||||||
// This is tricky because we can't just check Path.GetExtension for empty
|
|
||||||
// If the input is "St. Vincent (2014)", it will produce ". Vincent (2014)" as the extension
|
|
||||||
name += ".mkv";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (ArgumentException)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
var result = _options.CleanDateTimeRegexes.Select(i => Clean(name, i))
|
return result;
|
||||||
.FirstOrDefault(i => i.HasChanged) ??
|
|
||||||
new CleanDateTimeResult { Name = originalName };
|
|
||||||
|
|
||||||
if (result.HasChanged)
|
|
||||||
{
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make a second pass, running clean string first
|
|
||||||
var cleanStringResult = new CleanStringParser().Clean(name, _options.CleanStringRegexes);
|
|
||||||
|
|
||||||
if (!cleanStringResult.HasChanged)
|
|
||||||
{
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
return _options.CleanDateTimeRegexes.Select(i => Clean(cleanStringResult.Name, i))
|
|
||||||
.FirstOrDefault(i => i.HasChanged) ??
|
|
||||||
result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static CleanDateTimeResult Clean(string name, Regex expression)
|
private static bool TryClean(string name, Regex expression, ref CleanDateTimeResult result)
|
||||||
{
|
{
|
||||||
var result = new CleanDateTimeResult();
|
|
||||||
|
|
||||||
var match = expression.Match(name);
|
var match = expression.Match(name);
|
||||||
|
|
||||||
if (match.Success
|
if (match.Success
|
||||||
&& match.Groups.Count == 4
|
&& match.Groups.Count == 5
|
||||||
&& match.Groups[1].Success
|
&& match.Groups[1].Success
|
||||||
&& match.Groups[2].Success
|
&& match.Groups[2].Success
|
||||||
&& int.TryParse(match.Groups[2].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var year))
|
&& int.TryParse(match.Groups[2].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var year))
|
||||||
{
|
{
|
||||||
name = match.Groups[1].Value;
|
result = new CleanDateTimeResult(match.Groups[1].Value.TrimEnd(), year);
|
||||||
result.Year = year;
|
return true;
|
||||||
result.HasChanged = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
result.Name = name;
|
return false;
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,26 +1,33 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
#pragma warning disable SA1600
|
#pragma warning disable SA1600
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
namespace Emby.Naming.Video
|
namespace Emby.Naming.Video
|
||||||
{
|
{
|
||||||
public class CleanDateTimeResult
|
public readonly struct CleanDateTimeResult
|
||||||
{
|
{
|
||||||
|
public CleanDateTimeResult(string name, int? year)
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
Year = year;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CleanDateTimeResult(string name)
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
Year = null;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the name.
|
/// Gets the name.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The name.</value>
|
/// <value>The name.</value>
|
||||||
public string Name { get; set; }
|
public string Name { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the year.
|
/// Gets the year.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The year.</value>
|
/// <value>The year.</value>
|
||||||
public int? Year { get; set; }
|
public int? Year { get; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets a value indicating whether this instance has changed.
|
|
||||||
/// </summary>
|
|
||||||
/// <value><c>true</c> if this instance has changed; otherwise, <c>false</c>.</value>
|
|
||||||
public bool HasChanged { get; set; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,52 +1,45 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
#pragma warning disable SA1600
|
#pragma warning disable SA1600
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
namespace Emby.Naming.Video
|
namespace Emby.Naming.Video
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// http://kodi.wiki/view/Advancedsettings.xml#video
|
/// <see href="http://kodi.wiki/view/Advancedsettings.xml#video" />.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class CleanStringParser
|
public static class CleanStringParser
|
||||||
{
|
{
|
||||||
public CleanStringResult Clean(string name, IEnumerable<Regex> expressions)
|
public static bool TryClean(string name, IReadOnlyList<Regex> expressions, out ReadOnlySpan<char> newName)
|
||||||
{
|
{
|
||||||
var hasChanged = false;
|
var len = expressions.Count;
|
||||||
|
for (int i = 0; i < len; i++)
|
||||||
foreach (var exp in expressions)
|
|
||||||
{
|
{
|
||||||
var result = Clean(name, exp);
|
if (TryClean(name, expressions[i], out newName))
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(result.Name))
|
|
||||||
{
|
{
|
||||||
name = result.Name;
|
return true;
|
||||||
hasChanged = hasChanged || result.HasChanged;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new CleanStringResult
|
newName = ReadOnlySpan<char>.Empty;
|
||||||
{
|
return false;
|
||||||
Name = name,
|
|
||||||
HasChanged = hasChanged
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static CleanStringResult Clean(string name, Regex expression)
|
private static bool TryClean(string name, Regex expression, out ReadOnlySpan<char> newName)
|
||||||
{
|
{
|
||||||
var result = new CleanStringResult();
|
|
||||||
|
|
||||||
var match = expression.Match(name);
|
var match = expression.Match(name);
|
||||||
|
int index = match.Index;
|
||||||
if (match.Success)
|
if (match.Success && index != 0)
|
||||||
{
|
{
|
||||||
result.HasChanged = true;
|
newName = name.AsSpan().Slice(0, match.Index);
|
||||||
name = name.Substring(0, match.Index);
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
result.Name = name;
|
newName = string.Empty;
|
||||||
return result;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,20 +0,0 @@
|
|||||||
#pragma warning disable CS1591
|
|
||||||
#pragma warning disable SA1600
|
|
||||||
|
|
||||||
namespace Emby.Naming.Video
|
|
||||||
{
|
|
||||||
public class CleanStringResult
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the name.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The name.</value>
|
|
||||||
public string Name { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets a value indicating whether this instance has changed.
|
|
||||||
/// </summary>
|
|
||||||
/// <value><c>true</c> if this instance has changed; otherwise, <c>false</c>.</value>
|
|
||||||
public bool HasChanged { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
@ -23,7 +23,7 @@ namespace Emby.Naming.Video
|
|||||||
{
|
{
|
||||||
return _options.VideoExtraRules
|
return _options.VideoExtraRules
|
||||||
.Select(i => GetExtraInfo(path, i))
|
.Select(i => GetExtraInfo(path, i))
|
||||||
.FirstOrDefault(i => !string.IsNullOrEmpty(i.ExtraType)) ?? new ExtraResult();
|
.FirstOrDefault(i => i.ExtraType != null) ?? new ExtraResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
private ExtraResult GetExtraInfo(string path, ExtraRule rule)
|
private ExtraResult GetExtraInfo(string path, ExtraRule rule)
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
#pragma warning disable SA1600
|
#pragma warning disable SA1600
|
||||||
|
|
||||||
|
using MediaBrowser.Model.Entities;
|
||||||
|
|
||||||
namespace Emby.Naming.Video
|
namespace Emby.Naming.Video
|
||||||
{
|
{
|
||||||
public class ExtraResult
|
public class ExtraResult
|
||||||
@ -9,7 +11,7 @@ namespace Emby.Naming.Video
|
|||||||
/// Gets or sets the type of the extra.
|
/// Gets or sets the type of the extra.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The type of the extra.</value>
|
/// <value>The type of the extra.</value>
|
||||||
public string ExtraType { get; set; }
|
public ExtraType? ExtraType { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the rule.
|
/// Gets or sets the rule.
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
#pragma warning disable SA1600
|
#pragma warning disable SA1600
|
||||||
|
|
||||||
using Emby.Naming.Common;
|
using MediaBrowser.Model.Entities;
|
||||||
|
using MediaType = Emby.Naming.Common.MediaType;
|
||||||
|
|
||||||
namespace Emby.Naming.Video
|
namespace Emby.Naming.Video
|
||||||
{
|
{
|
||||||
@ -17,7 +18,7 @@ namespace Emby.Naming.Video
|
|||||||
/// Gets or sets the type of the extra.
|
/// Gets or sets the type of the extra.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The type of the extra.</value>
|
/// <value>The type of the extra.</value>
|
||||||
public string ExtraType { get; set; }
|
public ExtraType ExtraType { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the type of the rule.
|
/// Gets or sets the type of the rule.
|
||||||
|
@ -14,14 +14,14 @@ namespace Emby.Naming.Video
|
|||||||
{
|
{
|
||||||
if (path == null)
|
if (path == null)
|
||||||
{
|
{
|
||||||
return default(StubResult);
|
return default;
|
||||||
}
|
}
|
||||||
|
|
||||||
var extension = Path.GetExtension(path);
|
var extension = Path.GetExtension(path);
|
||||||
|
|
||||||
if (!options.StubFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase))
|
if (!options.StubFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
return default(StubResult);
|
return default;
|
||||||
}
|
}
|
||||||
|
|
||||||
var result = new StubResult()
|
var result = new StubResult()
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
using MediaBrowser.Model.Entities;
|
||||||
|
|
||||||
namespace Emby.Naming.Video
|
namespace Emby.Naming.Video
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -30,10 +32,10 @@ namespace Emby.Naming.Video
|
|||||||
public int? Year { get; set; }
|
public int? Year { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the type of the extra, e.g. trailer, theme song, behing the scenes, etc.
|
/// Gets or sets the type of the extra, e.g. trailer, theme song, behind the scenes, etc.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The type of the extra.</value>
|
/// <value>The type of the extra.</value>
|
||||||
public string ExtraType { get; set; }
|
public ExtraType? ExtraType { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the extra rule.
|
/// Gets or sets the extra rule.
|
||||||
|
@ -7,6 +7,7 @@ using System.IO;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using Emby.Naming.Common;
|
using Emby.Naming.Common;
|
||||||
|
using MediaBrowser.Model.Entities;
|
||||||
using MediaBrowser.Model.IO;
|
using MediaBrowser.Model.IO;
|
||||||
|
|
||||||
namespace Emby.Naming.Video
|
namespace Emby.Naming.Video
|
||||||
@ -32,7 +33,7 @@ namespace Emby.Naming.Video
|
|||||||
// Filter out all extras, otherwise they could cause stacks to not be resolved
|
// Filter out all extras, otherwise they could cause stacks to not be resolved
|
||||||
// See the unit test TestStackedWithTrailer
|
// See the unit test TestStackedWithTrailer
|
||||||
var nonExtras = videoInfos
|
var nonExtras = videoInfos
|
||||||
.Where(i => string.IsNullOrEmpty(i.ExtraType))
|
.Where(i => i.ExtraType == null)
|
||||||
.Select(i => new FileSystemMetadata
|
.Select(i => new FileSystemMetadata
|
||||||
{
|
{
|
||||||
FullName = i.Path,
|
FullName = i.Path,
|
||||||
@ -79,7 +80,7 @@ namespace Emby.Naming.Video
|
|||||||
}
|
}
|
||||||
|
|
||||||
var standaloneMedia = remainingFiles
|
var standaloneMedia = remainingFiles
|
||||||
.Where(i => string.IsNullOrEmpty(i.ExtraType))
|
.Where(i => i.ExtraType == null)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
foreach (var media in standaloneMedia)
|
foreach (var media in standaloneMedia)
|
||||||
@ -148,7 +149,7 @@ namespace Emby.Naming.Video
|
|||||||
if (list.Count == 1)
|
if (list.Count == 1)
|
||||||
{
|
{
|
||||||
var trailers = remainingFiles
|
var trailers = remainingFiles
|
||||||
.Where(i => string.Equals(i.ExtraType, "trailer", StringComparison.OrdinalIgnoreCase))
|
.Where(i => i.ExtraType == ExtraType.Trailer)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
list[0].Extras.AddRange(trailers);
|
list[0].Extras.AddRange(trailers);
|
||||||
@ -229,7 +230,7 @@ namespace Emby.Naming.Video
|
|||||||
}
|
}
|
||||||
|
|
||||||
return remainingFiles
|
return remainingFiles
|
||||||
.Where(i => !string.IsNullOrEmpty(i.ExtraType))
|
.Where(i => i.ExtraType == null)
|
||||||
.Where(i => baseNames.Any(b => i.FileNameWithoutExtension.StartsWith(b, StringComparison.OrdinalIgnoreCase)))
|
.Where(i => baseNames.Any(b => i.FileNameWithoutExtension.StartsWith(b, StringComparison.OrdinalIgnoreCase)))
|
||||||
.ToList();
|
.ToList();
|
||||||
}
|
}
|
||||||
|
@ -94,9 +94,10 @@ namespace Emby.Naming.Video
|
|||||||
{
|
{
|
||||||
var cleanDateTimeResult = CleanDateTime(name);
|
var cleanDateTimeResult = CleanDateTime(name);
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(extraResult.ExtraType))
|
if (extraResult.ExtraType == null
|
||||||
|
&& TryCleanString(cleanDateTimeResult.Name, out ReadOnlySpan<char> newName))
|
||||||
{
|
{
|
||||||
name = CleanString(cleanDateTimeResult.Name).Name;
|
name = newName.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
year = cleanDateTimeResult.Year;
|
year = cleanDateTimeResult.Year;
|
||||||
@ -130,14 +131,14 @@ namespace Emby.Naming.Video
|
|||||||
return _options.StubFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase);
|
return _options.StubFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase);
|
||||||
}
|
}
|
||||||
|
|
||||||
public CleanStringResult CleanString(string name)
|
public bool TryCleanString(string name, out ReadOnlySpan<char> newName)
|
||||||
{
|
{
|
||||||
return new CleanStringParser().Clean(name, _options.CleanStringRegexes);
|
return CleanStringParser.TryClean(name, _options.CleanStringRegexes, out newName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public CleanDateTimeResult CleanDateTime(string name)
|
public CleanDateTimeResult CleanDateTime(string name)
|
||||||
{
|
{
|
||||||
return new CleanDateTimeParser(_options).Clean(name);
|
return CleanDateTimeParser.Clean(name, _options.CleanDateTimeRegexes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,9 +26,10 @@
|
|||||||
|
|
||||||
<!-- Code Analyzers-->
|
<!-- Code Analyzers-->
|
||||||
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
|
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||||
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.7" />
|
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.8" PrivateAssets="All" />
|
||||||
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" />
|
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="All" />
|
||||||
<PackageReference Include="SerilogAnalyzer" Version="0.15.0" />
|
<PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" />
|
||||||
|
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
#pragma warning disable SA1600
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
#pragma warning disable SA1600
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
#pragma warning disable SA1600
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
#pragma warning disable SA1600
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
@ -177,11 +178,7 @@ namespace Emby.Server.Implementations
|
|||||||
/// Gets the plugins.
|
/// Gets the plugins.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The plugins.</value>
|
/// <value>The plugins.</value>
|
||||||
public IPlugin[] Plugins
|
public IReadOnlyList<IPlugin> Plugins => _plugins;
|
||||||
{
|
|
||||||
get => _plugins;
|
|
||||||
protected set => _plugins = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the logger factory.
|
/// Gets or sets the logger factory.
|
||||||
@ -602,7 +599,7 @@ namespace Emby.Server.Implementations
|
|||||||
HttpsPort = ServerConfiguration.DefaultHttpsPort;
|
HttpsPort = ServerConfiguration.DefaultHttpsPort;
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonSerializer = new JsonSerializer(FileSystemManager);
|
JsonSerializer = new JsonSerializer();
|
||||||
|
|
||||||
if (Plugins != null)
|
if (Plugins != null)
|
||||||
{
|
{
|
||||||
@ -1056,7 +1053,7 @@ namespace Emby.Server.Implementations
|
|||||||
}
|
}
|
||||||
|
|
||||||
ConfigurationManager.AddParts(GetExports<IConfigurationFactory>());
|
ConfigurationManager.AddParts(GetExports<IConfigurationFactory>());
|
||||||
Plugins = GetExports<IPlugin>()
|
_plugins = GetExports<IPlugin>()
|
||||||
.Select(LoadPlugin)
|
.Select(LoadPlugin)
|
||||||
.Where(i => i != null)
|
.Where(i => i != null)
|
||||||
.ToArray();
|
.ToArray();
|
||||||
@ -1705,9 +1702,9 @@ namespace Emby.Server.Implementations
|
|||||||
/// <param name="plugin">The plugin.</param>
|
/// <param name="plugin">The plugin.</param>
|
||||||
public void RemovePlugin(IPlugin plugin)
|
public void RemovePlugin(IPlugin plugin)
|
||||||
{
|
{
|
||||||
var list = Plugins.ToList();
|
var list = _plugins.ToList();
|
||||||
list.Remove(plugin);
|
list.Remove(plugin);
|
||||||
Plugins = list.ToArray();
|
_plugins = list.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
#pragma warning disable SA1600
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using MediaBrowser.Common.Configuration;
|
using MediaBrowser.Common.Configuration;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
#pragma warning disable SA1600
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
#pragma warning disable SA1600
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
#pragma warning disable SA1600
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
#pragma warning disable SA1600
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
#pragma warning disable SA1600
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
#pragma warning disable SA1600
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
#pragma warning disable SA1600
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
@ -121,7 +121,7 @@ namespace Emby.Server.Implementations.Cryptography
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Releases unmanaged and - optionally - managed resources.
|
/// Releases unmanaged and - optionally - managed resources.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
|
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
|
||||||
protected virtual void Dispose(bool disposing)
|
protected virtual void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
if (_disposed)
|
if (_disposed)
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
#pragma warning disable SA1600
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
#pragma warning disable SA1600
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
#pragma warning disable SA1600
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
#pragma warning disable SA1600
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
#pragma warning disable SA1600
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
#pragma warning disable SA1600
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
#pragma warning disable SA1600
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
#pragma warning disable SA1600
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
#pragma warning disable SA1600
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@ -242,7 +243,7 @@ namespace Emby.Server.Implementations.Devices
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using (var fs = _fileSystem.GetFileStream(path, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read))
|
using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read))
|
||||||
{
|
{
|
||||||
await stream.CopyToAsync(fs).ConfigureAwait(false);
|
await stream.CopyToAsync(fs).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
#pragma warning disable SA1600
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
#pragma warning disable SA1600
|
||||||
|
|
||||||
using MediaBrowser.Model.Diagnostics;
|
using MediaBrowser.Model.Diagnostics;
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
#pragma warning disable SA1600
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
@ -29,13 +29,13 @@
|
|||||||
<PackageReference Include="Microsoft.AspNetCore.ResponseCompression" Version="2.2.0" />
|
<PackageReference Include="Microsoft.AspNetCore.ResponseCompression" Version="2.2.0" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="2.2.0" />
|
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="2.2.0" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.WebSockets" Version="2.2.1" />
|
<PackageReference Include="Microsoft.AspNetCore.WebSockets" Version="2.2.1" />
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.0.1" />
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="3.0.1" />
|
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="3.1.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.0.1" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.0" />
|
||||||
<PackageReference Include="Mono.Nat" Version="2.0.0" />
|
<PackageReference Include="Mono.Nat" Version="2.0.0" />
|
||||||
<PackageReference Include="ServiceStack.Text.Core" Version="5.7.0" />
|
<PackageReference Include="ServiceStack.Text.Core" Version="5.7.0" />
|
||||||
<PackageReference Include="sharpcompress" Version="0.24.0" />
|
<PackageReference Include="sharpcompress" Version="0.24.0" />
|
||||||
<PackageReference Include="SQLitePCL.pretty.netstandard" Version="2.0.1" />
|
<PackageReference Include="SQLitePCL.pretty.netstandard" Version="2.1.0" />
|
||||||
<PackageReference Include="System.Interactive.Async" Version="4.0.0" />
|
<PackageReference Include="System.Interactive.Async" Version="4.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
@ -51,10 +51,10 @@
|
|||||||
|
|
||||||
<!-- Code Analyzers-->
|
<!-- Code Analyzers-->
|
||||||
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
|
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||||
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.7" />
|
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.8" PrivateAssets="All" />
|
||||||
<PackageReference Include="SerilogAnalyzer" Version="0.15.0" />
|
<PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" />
|
||||||
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" />
|
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="All" />
|
||||||
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" />
|
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
#pragma warning disable SA1600
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
#pragma warning disable SA1600
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
#pragma warning disable SA1600
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
#pragma warning disable SA1600
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Emby.Server.Implementations.Udp;
|
using Emby.Server.Implementations.Udp;
|
||||||
using MediaBrowser.Controller;
|
using MediaBrowser.Controller;
|
||||||
@ -12,7 +13,7 @@ namespace Emby.Server.Implementations.EntryPoints
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Class UdpServerEntryPoint.
|
/// Class UdpServerEntryPoint.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class UdpServerEntryPoint : IServerEntryPoint
|
public sealed class UdpServerEntryPoint : IServerEntryPoint
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The port of the UDP server.
|
/// The port of the UDP server.
|
||||||
@ -31,61 +32,44 @@ namespace Emby.Server.Implementations.EntryPoints
|
|||||||
/// The UDP server.
|
/// The UDP server.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private UdpServer _udpServer;
|
private UdpServer _udpServer;
|
||||||
|
private CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
|
||||||
|
private bool _disposed = false;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="UdpServerEntryPoint" /> class.
|
/// Initializes a new instance of the <see cref="UdpServerEntryPoint" /> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public UdpServerEntryPoint(
|
public UdpServerEntryPoint(
|
||||||
ILogger logger,
|
ILogger<UdpServerEntryPoint> logger,
|
||||||
IServerApplicationHost appHost,
|
IServerApplicationHost appHost)
|
||||||
IJsonSerializer json,
|
|
||||||
ISocketFactory socketFactory)
|
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_appHost = appHost;
|
_appHost = appHost;
|
||||||
_json = json;
|
|
||||||
_socketFactory = socketFactory;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public Task RunAsync()
|
public async Task RunAsync()
|
||||||
{
|
{
|
||||||
var udpServer = new UdpServer(_logger, _appHost, _json, _socketFactory);
|
_udpServer = new UdpServer(_logger, _appHost);
|
||||||
|
_udpServer.Start(PortNumber, _cancellationTokenSource.Token);
|
||||||
try
|
|
||||||
{
|
|
||||||
udpServer.Start(PortNumber);
|
|
||||||
|
|
||||||
_udpServer = udpServer;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Failed to start UDP Server");
|
|
||||||
}
|
|
||||||
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
Dispose(true);
|
if (_disposed)
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Releases unmanaged and - optionally - managed resources.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
|
|
||||||
protected virtual void Dispose(bool dispose)
|
|
||||||
{
|
|
||||||
if (dispose)
|
|
||||||
{
|
{
|
||||||
if (_udpServer != null)
|
return;
|
||||||
{
|
|
||||||
_udpServer.Dispose();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_cancellationTokenSource.Cancel();
|
||||||
|
_udpServer.Dispose();
|
||||||
|
|
||||||
|
_cancellationTokenSource = null;
|
||||||
|
_udpServer = null;
|
||||||
|
|
||||||
|
_disposed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
#pragma warning disable SA1600
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
@ -197,7 +197,7 @@ namespace Emby.Server.Implementations.HttpClientManager
|
|||||||
if (File.Exists(responseCachePath)
|
if (File.Exists(responseCachePath)
|
||||||
&& _fileSystem.GetLastWriteTimeUtc(responseCachePath).Add(cacheLength) > DateTime.UtcNow)
|
&& _fileSystem.GetLastWriteTimeUtc(responseCachePath).Add(cacheLength) > DateTime.UtcNow)
|
||||||
{
|
{
|
||||||
var stream = _fileSystem.GetFileStream(responseCachePath, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read, true);
|
var stream = new FileStream(responseCachePath, FileMode.Open, FileAccess.Read, FileShare.Read, IODefaults.FileStreamBufferSize, true);
|
||||||
|
|
||||||
return new HttpResponseInfo
|
return new HttpResponseInfo
|
||||||
{
|
{
|
||||||
@ -220,7 +220,7 @@ namespace Emby.Server.Implementations.HttpClientManager
|
|||||||
FileMode.Create,
|
FileMode.Create,
|
||||||
FileAccess.Write,
|
FileAccess.Write,
|
||||||
FileShare.None,
|
FileShare.None,
|
||||||
StreamDefaults.DefaultFileStreamBufferSize,
|
IODefaults.FileStreamBufferSize,
|
||||||
true))
|
true))
|
||||||
{
|
{
|
||||||
await response.Content.CopyToAsync(fileStream).ConfigureAwait(false);
|
await response.Content.CopyToAsync(fileStream).ConfigureAwait(false);
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
#pragma warning disable SA1600
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@ -71,7 +72,7 @@ namespace Emby.Server.Implementations.HttpServer
|
|||||||
SetRangeValues();
|
SetRangeValues();
|
||||||
}
|
}
|
||||||
|
|
||||||
FileShare = FileShareMode.Read;
|
FileShare = FileShare.Read;
|
||||||
Cookies = new List<Cookie>();
|
Cookies = new List<Cookie>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,7 +94,7 @@ namespace Emby.Server.Implementations.HttpServer
|
|||||||
|
|
||||||
public List<Cookie> Cookies { get; private set; }
|
public List<Cookie> Cookies { get; private set; }
|
||||||
|
|
||||||
public FileShareMode FileShare { get; set; }
|
public FileShare FileShare { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the options.
|
/// Gets the options.
|
||||||
@ -221,17 +222,17 @@ namespace Emby.Server.Implementations.HttpServer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task TransmitFile(Stream stream, string path, long offset, long count, FileShareMode fileShareMode, CancellationToken cancellationToken)
|
public async Task TransmitFile(Stream stream, string path, long offset, long count, FileShare fileShare, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var fileOpenOptions = FileOpenOptions.SequentialScan;
|
var fileOptions = FileOptions.SequentialScan;
|
||||||
|
|
||||||
// use non-async filestream along with read due to https://github.com/dotnet/corefx/issues/6039
|
// use non-async filestream along with read due to https://github.com/dotnet/corefx/issues/6039
|
||||||
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||||
{
|
{
|
||||||
fileOpenOptions |= FileOpenOptions.Asynchronous;
|
fileOptions |= FileOptions.Asynchronous;
|
||||||
}
|
}
|
||||||
|
|
||||||
using (var fs = _fileSystem.GetFileStream(path, FileOpenMode.Open, FileAccessMode.Read, fileShareMode, fileOpenOptions))
|
using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read, fileShare, IODefaults.FileStreamBufferSize, fileOptions))
|
||||||
{
|
{
|
||||||
if (offset > 0)
|
if (offset > 0)
|
||||||
{
|
{
|
||||||
@ -244,7 +245,7 @@ namespace Emby.Server.Implementations.HttpServer
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await fs.CopyToAsync(stream, StreamDefaults.DefaultCopyToBufferSize, cancellationToken).ConfigureAwait(false);
|
await fs.CopyToAsync(stream, IODefaults.CopyToBufferSize, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
#pragma warning disable SA1600
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@ -218,7 +219,6 @@ namespace Emby.Server.Implementations.HttpServer
|
|||||||
case FileNotFoundException _:
|
case FileNotFoundException _:
|
||||||
case ResourceNotFoundException _: return 404;
|
case ResourceNotFoundException _: return 404;
|
||||||
case MethodNotAllowedException _: return 405;
|
case MethodNotAllowedException _: return 405;
|
||||||
case RemoteServiceUnavailableException _: return 502;
|
|
||||||
default: return 500;
|
default: return 500;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
#pragma warning disable SA1600
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@ -439,7 +440,7 @@ namespace Emby.Server.Implementations.HttpServer
|
|||||||
|
|
||||||
public Task<object> GetStaticFileResult(IRequest requestContext,
|
public Task<object> GetStaticFileResult(IRequest requestContext,
|
||||||
string path,
|
string path,
|
||||||
FileShareMode fileShare = FileShareMode.Read)
|
FileShare fileShare = FileShare.Read)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(path))
|
if (string.IsNullOrEmpty(path))
|
||||||
{
|
{
|
||||||
@ -463,7 +464,7 @@ namespace Emby.Server.Implementations.HttpServer
|
|||||||
throw new ArgumentException("Path can't be empty.", nameof(options));
|
throw new ArgumentException("Path can't be empty.", nameof(options));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fileShare != FileShareMode.Read && fileShare != FileShareMode.ReadWrite)
|
if (fileShare != FileShare.Read && fileShare != FileShare.ReadWrite)
|
||||||
{
|
{
|
||||||
throw new ArgumentException("FileShare must be either Read or ReadWrite");
|
throw new ArgumentException("FileShare must be either Read or ReadWrite");
|
||||||
}
|
}
|
||||||
@ -491,9 +492,9 @@ namespace Emby.Server.Implementations.HttpServer
|
|||||||
/// <param name="path">The path.</param>
|
/// <param name="path">The path.</param>
|
||||||
/// <param name="fileShare">The file share.</param>
|
/// <param name="fileShare">The file share.</param>
|
||||||
/// <returns>Stream.</returns>
|
/// <returns>Stream.</returns>
|
||||||
private Stream GetFileStream(string path, FileShareMode fileShare)
|
private Stream GetFileStream(string path, FileShare fileShare)
|
||||||
{
|
{
|
||||||
return _fileSystem.GetFileStream(path, FileOpenMode.Open, FileAccessMode.Read, fileShare);
|
return new FileStream(path, FileMode.Open, FileAccess.Read, fileShare);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<object> GetStaticResult(IRequest requestContext,
|
public Task<object> GetStaticResult(IRequest requestContext,
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
#pragma warning disable SA1600
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
#pragma warning disable SA1600
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
#pragma warning disable SA1600
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
#pragma warning disable SA1600
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
#pragma warning disable SA1600
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
#pragma warning disable SA1600
|
||||||
|
|
||||||
namespace Emby.Server.Implementations.IO
|
namespace Emby.Server.Implementations.IO
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
#pragma warning disable SA1600
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
#pragma warning disable SA1600
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
#pragma warning disable SA1600
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
@ -16,7 +17,7 @@ using OperatingSystem = MediaBrowser.Common.System.OperatingSystem;
|
|||||||
namespace Emby.Server.Implementations.IO
|
namespace Emby.Server.Implementations.IO
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Class ManagedFileSystem
|
/// Class ManagedFileSystem.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class ManagedFileSystem : IFileSystem
|
public class ManagedFileSystem : IFileSystem
|
||||||
{
|
{
|
||||||
@ -79,20 +80,20 @@ namespace Emby.Server.Implementations.IO
|
|||||||
|
|
||||||
public virtual string MakeAbsolutePath(string folderPath, string filePath)
|
public virtual string MakeAbsolutePath(string folderPath, string filePath)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(filePath)
|
// path is actually a stream
|
||||||
// stream
|
if (string.IsNullOrWhiteSpace(filePath) || filePath.Contains("://", StringComparison.Ordinal))
|
||||||
|| filePath.Contains("://"))
|
|
||||||
{
|
{
|
||||||
return filePath;
|
return filePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (filePath.Length > 3 && filePath[1] == ':' && filePath[2] == '/')
|
if (filePath.Length > 3 && filePath[1] == ':' && filePath[2] == '/')
|
||||||
{
|
{
|
||||||
return filePath; // absolute local path
|
// absolute local path
|
||||||
|
return filePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
// unc path
|
// unc path
|
||||||
if (filePath.StartsWith("\\\\"))
|
if (filePath.StartsWith("\\\\", StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
return filePath;
|
return filePath;
|
||||||
}
|
}
|
||||||
@ -100,13 +101,16 @@ namespace Emby.Server.Implementations.IO
|
|||||||
var firstChar = filePath[0];
|
var firstChar = filePath[0];
|
||||||
if (firstChar == '/')
|
if (firstChar == '/')
|
||||||
{
|
{
|
||||||
// For this we don't really know.
|
// for this we don't really know
|
||||||
return filePath;
|
return filePath;
|
||||||
}
|
}
|
||||||
if (firstChar == '\\') //relative path
|
|
||||||
|
// relative path
|
||||||
|
if (firstChar == '\\')
|
||||||
{
|
{
|
||||||
filePath = filePath.Substring(1);
|
filePath = filePath.Substring(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return Path.GetFullPath(Path.Combine(folderPath, filePath));
|
return Path.GetFullPath(Path.Combine(folderPath, filePath));
|
||||||
@ -130,11 +134,7 @@ namespace Emby.Server.Implementations.IO
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="shortcutPath">The shortcut path.</param>
|
/// <param name="shortcutPath">The shortcut path.</param>
|
||||||
/// <param name="target">The target.</param>
|
/// <param name="target">The target.</param>
|
||||||
/// <exception cref="ArgumentNullException">
|
/// <exception cref="ArgumentNullException">The shortcutPath or target is null.</exception>
|
||||||
/// shortcutPath
|
|
||||||
/// or
|
|
||||||
/// target
|
|
||||||
/// </exception>
|
|
||||||
public virtual void CreateShortcut(string shortcutPath, string target)
|
public virtual void CreateShortcut(string shortcutPath, string target)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(shortcutPath))
|
if (string.IsNullOrEmpty(shortcutPath))
|
||||||
@ -280,11 +280,11 @@ namespace Emby.Server.Implementations.IO
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Takes a filename and removes invalid characters
|
/// Takes a filename and removes invalid characters.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="filename">The filename.</param>
|
/// <param name="filename">The filename.</param>
|
||||||
/// <returns>System.String.</returns>
|
/// <returns>System.String.</returns>
|
||||||
/// <exception cref="ArgumentNullException">filename</exception>
|
/// <exception cref="ArgumentNullException">The filename is null.</exception>
|
||||||
public virtual string GetValidFilename(string filename)
|
public virtual string GetValidFilename(string filename)
|
||||||
{
|
{
|
||||||
var builder = new StringBuilder(filename);
|
var builder = new StringBuilder(filename);
|
||||||
@ -365,90 +365,9 @@ namespace Emby.Server.Implementations.IO
|
|||||||
return GetLastWriteTimeUtc(GetFileSystemInfo(path));
|
return GetLastWriteTimeUtc(GetFileSystemInfo(path));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the file stream.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="path">The path.</param>
|
|
||||||
/// <param name="mode">The mode.</param>
|
|
||||||
/// <param name="access">The access.</param>
|
|
||||||
/// <param name="share">The share.</param>
|
|
||||||
/// <param name="isAsync">if set to <c>true</c> [is asynchronous].</param>
|
|
||||||
/// <returns>FileStream.</returns>
|
|
||||||
public virtual Stream GetFileStream(string path, FileOpenMode mode, FileAccessMode access, FileShareMode share, bool isAsync = false)
|
|
||||||
{
|
|
||||||
if (isAsync)
|
|
||||||
{
|
|
||||||
return GetFileStream(path, mode, access, share, FileOpenOptions.Asynchronous);
|
|
||||||
}
|
|
||||||
|
|
||||||
return GetFileStream(path, mode, access, share, FileOpenOptions.None);
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual Stream GetFileStream(string path, FileOpenMode mode, FileAccessMode access, FileShareMode share, FileOpenOptions fileOpenOptions)
|
|
||||||
=> new FileStream(path, GetFileMode(mode), GetFileAccess(access), GetFileShare(share), 4096, GetFileOptions(fileOpenOptions));
|
|
||||||
|
|
||||||
private static FileOptions GetFileOptions(FileOpenOptions mode)
|
|
||||||
{
|
|
||||||
var val = (int)mode;
|
|
||||||
return (FileOptions)val;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static FileMode GetFileMode(FileOpenMode mode)
|
|
||||||
{
|
|
||||||
switch (mode)
|
|
||||||
{
|
|
||||||
//case FileOpenMode.Append:
|
|
||||||
// return FileMode.Append;
|
|
||||||
case FileOpenMode.Create:
|
|
||||||
return FileMode.Create;
|
|
||||||
case FileOpenMode.CreateNew:
|
|
||||||
return FileMode.CreateNew;
|
|
||||||
case FileOpenMode.Open:
|
|
||||||
return FileMode.Open;
|
|
||||||
case FileOpenMode.OpenOrCreate:
|
|
||||||
return FileMode.OpenOrCreate;
|
|
||||||
//case FileOpenMode.Truncate:
|
|
||||||
// return FileMode.Truncate;
|
|
||||||
default:
|
|
||||||
throw new Exception("Unrecognized FileOpenMode");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static FileAccess GetFileAccess(FileAccessMode mode)
|
|
||||||
{
|
|
||||||
switch (mode)
|
|
||||||
{
|
|
||||||
//case FileAccessMode.ReadWrite:
|
|
||||||
// return FileAccess.ReadWrite;
|
|
||||||
case FileAccessMode.Write:
|
|
||||||
return FileAccess.Write;
|
|
||||||
case FileAccessMode.Read:
|
|
||||||
return FileAccess.Read;
|
|
||||||
default:
|
|
||||||
throw new Exception("Unrecognized FileAccessMode");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static FileShare GetFileShare(FileShareMode mode)
|
|
||||||
{
|
|
||||||
switch (mode)
|
|
||||||
{
|
|
||||||
case FileShareMode.ReadWrite:
|
|
||||||
return FileShare.ReadWrite;
|
|
||||||
case FileShareMode.Write:
|
|
||||||
return FileShare.Write;
|
|
||||||
case FileShareMode.Read:
|
|
||||||
return FileShare.Read;
|
|
||||||
case FileShareMode.None:
|
|
||||||
return FileShare.None;
|
|
||||||
default:
|
|
||||||
throw new Exception("Unrecognized FileShareMode");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual void SetHidden(string path, bool isHidden)
|
public virtual void SetHidden(string path, bool isHidden)
|
||||||
{
|
{
|
||||||
if (OperatingSystem.Id != MediaBrowser.Model.System.OperatingSystemId.Windows)
|
if (OperatingSystem.Id != OperatingSystemId.Windows)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -472,7 +391,7 @@ namespace Emby.Server.Implementations.IO
|
|||||||
|
|
||||||
public virtual void SetReadOnly(string path, bool isReadOnly)
|
public virtual void SetReadOnly(string path, bool isReadOnly)
|
||||||
{
|
{
|
||||||
if (OperatingSystem.Id != MediaBrowser.Model.System.OperatingSystemId.Windows)
|
if (OperatingSystem.Id != OperatingSystemId.Windows)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -496,7 +415,7 @@ namespace Emby.Server.Implementations.IO
|
|||||||
|
|
||||||
public virtual void SetAttributes(string path, bool isHidden, bool isReadOnly)
|
public virtual void SetAttributes(string path, bool isHidden, bool isReadOnly)
|
||||||
{
|
{
|
||||||
if (OperatingSystem.Id != MediaBrowser.Model.System.OperatingSystemId.Windows)
|
if (OperatingSystem.Id != OperatingSystemId.Windows)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -779,7 +698,7 @@ namespace Emby.Server.Implementations.IO
|
|||||||
|
|
||||||
public virtual void SetExecutable(string path)
|
public virtual void SetExecutable(string path)
|
||||||
{
|
{
|
||||||
if (OperatingSystem.Id == MediaBrowser.Model.System.OperatingSystemId.Darwin)
|
if (OperatingSystem.Id == OperatingSystemId.Darwin)
|
||||||
{
|
{
|
||||||
RunProcess("chmod", "+x \"" + path + "\"", Path.GetDirectoryName(path));
|
RunProcess("chmod", "+x \"" + path + "\"", Path.GetDirectoryName(path));
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
#pragma warning disable SA1600
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
#pragma warning disable SA1600
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Buffers;
|
using System.Buffers;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
#pragma warning disable SA1600
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
@ -70,9 +70,9 @@ namespace Emby.Server.Implementations.Library
|
|||||||
byte[] calculatedHash = _cryptographyProvider.ComputeHash(
|
byte[] calculatedHash = _cryptographyProvider.ComputeHash(
|
||||||
readyHash.Id,
|
readyHash.Id,
|
||||||
passwordbytes,
|
passwordbytes,
|
||||||
readyHash.Salt);
|
readyHash.Salt.ToArray());
|
||||||
|
|
||||||
if (calculatedHash.SequenceEqual(readyHash.Hash))
|
if (readyHash.Hash.SequenceEqual(calculatedHash))
|
||||||
{
|
{
|
||||||
success = true;
|
success = true;
|
||||||
}
|
}
|
||||||
@ -148,17 +148,18 @@ namespace Emby.Server.Implementations.Library
|
|||||||
|
|
||||||
// TODO: make use of iterations parameter?
|
// TODO: make use of iterations parameter?
|
||||||
PasswordHash passwordHash = PasswordHash.Parse(user.Password);
|
PasswordHash passwordHash = PasswordHash.Parse(user.Password);
|
||||||
|
var salt = passwordHash.Salt.ToArray();
|
||||||
return new PasswordHash(
|
return new PasswordHash(
|
||||||
passwordHash.Id,
|
passwordHash.Id,
|
||||||
_cryptographyProvider.ComputeHash(
|
_cryptographyProvider.ComputeHash(
|
||||||
passwordHash.Id,
|
passwordHash.Id,
|
||||||
Encoding.UTF8.GetBytes(str),
|
Encoding.UTF8.GetBytes(str),
|
||||||
passwordHash.Salt),
|
salt),
|
||||||
passwordHash.Salt,
|
salt,
|
||||||
passwordHash.Parameters.ToDictionary(x => x.Key, y => y.Value)).ToString();
|
passwordHash.Parameters.ToDictionary(x => x.Key, y => y.Value)).ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] GetHashed(User user, string str)
|
public ReadOnlySpan<byte> GetHashed(User user, string str)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(user.Password))
|
if (string.IsNullOrEmpty(user.Password))
|
||||||
{
|
{
|
||||||
@ -170,7 +171,7 @@ namespace Emby.Server.Implementations.Library
|
|||||||
return _cryptographyProvider.ComputeHash(
|
return _cryptographyProvider.ComputeHash(
|
||||||
passwordHash.Id,
|
passwordHash.Id,
|
||||||
Encoding.UTF8.GetBytes(str),
|
Encoding.UTF8.GetBytes(str),
|
||||||
passwordHash.Salt);
|
passwordHash.Salt.ToArray());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
#pragma warning disable SA1600
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
#pragma warning disable SA1600
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
@ -35,7 +36,6 @@ using MediaBrowser.Controller.Sorting;
|
|||||||
using MediaBrowser.Model.Configuration;
|
using MediaBrowser.Model.Configuration;
|
||||||
using MediaBrowser.Model.Dto;
|
using MediaBrowser.Model.Dto;
|
||||||
using MediaBrowser.Model.Entities;
|
using MediaBrowser.Model.Entities;
|
||||||
using MediaBrowser.Model.Extensions;
|
|
||||||
using MediaBrowser.Model.IO;
|
using MediaBrowser.Model.IO;
|
||||||
using MediaBrowser.Model.Library;
|
using MediaBrowser.Model.Library;
|
||||||
using MediaBrowser.Model.Net;
|
using MediaBrowser.Model.Net;
|
||||||
@ -53,6 +53,9 @@ namespace Emby.Server.Implementations.Library
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class LibraryManager : ILibraryManager
|
public class LibraryManager : ILibraryManager
|
||||||
{
|
{
|
||||||
|
private NamingOptions _namingOptions;
|
||||||
|
private string[] _videoFileExtensions;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the postscan tasks.
|
/// Gets or sets the postscan tasks.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -2507,21 +2510,11 @@ namespace Emby.Server.Implementations.Library
|
|||||||
}
|
}
|
||||||
|
|
||||||
public NamingOptions GetNamingOptions()
|
public NamingOptions GetNamingOptions()
|
||||||
{
|
|
||||||
return GetNamingOptionsInternal();
|
|
||||||
}
|
|
||||||
|
|
||||||
private NamingOptions _namingOptions;
|
|
||||||
private string[] _videoFileExtensions;
|
|
||||||
|
|
||||||
private NamingOptions GetNamingOptionsInternal()
|
|
||||||
{
|
{
|
||||||
if (_namingOptions == null)
|
if (_namingOptions == null)
|
||||||
{
|
{
|
||||||
var options = new NamingOptions();
|
_namingOptions = new NamingOptions();
|
||||||
|
_videoFileExtensions = _namingOptions.VideoFileExtensions;
|
||||||
_namingOptions = options;
|
|
||||||
_videoFileExtensions = _namingOptions.VideoFileExtensions.ToArray();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return _namingOptions;
|
return _namingOptions;
|
||||||
@ -2532,11 +2525,10 @@ namespace Emby.Server.Implementations.Library
|
|||||||
var resolver = new VideoResolver(GetNamingOptions());
|
var resolver = new VideoResolver(GetNamingOptions());
|
||||||
|
|
||||||
var result = resolver.CleanDateTime(name);
|
var result = resolver.CleanDateTime(name);
|
||||||
var cleanName = resolver.CleanString(result.Name);
|
|
||||||
|
|
||||||
return new ItemLookupInfo
|
return new ItemLookupInfo
|
||||||
{
|
{
|
||||||
Name = cleanName.Name,
|
Name = resolver.TryCleanString(result.Name, out var newName) ? newName.ToString() : result.Name,
|
||||||
Year = result.Year
|
Year = result.Year
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -2558,7 +2550,7 @@ namespace Emby.Server.Implementations.Library
|
|||||||
|
|
||||||
if (currentVideo != null)
|
if (currentVideo != null)
|
||||||
{
|
{
|
||||||
files.AddRange(currentVideo.Extras.Where(i => string.Equals(i.ExtraType, "trailer", StringComparison.OrdinalIgnoreCase)).Select(i => _fileSystem.GetFileInfo(i.Path)));
|
files.AddRange(currentVideo.Extras.Where(i => i.ExtraType == ExtraType.Trailer).Select(i => _fileSystem.GetFileInfo(i.Path)));
|
||||||
}
|
}
|
||||||
|
|
||||||
var resolvers = new IItemResolver[]
|
var resolvers = new IItemResolver[]
|
||||||
@ -2608,7 +2600,7 @@ namespace Emby.Server.Implementations.Library
|
|||||||
|
|
||||||
if (currentVideo != null)
|
if (currentVideo != null)
|
||||||
{
|
{
|
||||||
files.AddRange(currentVideo.Extras.Where(i => !string.Equals(i.ExtraType, "trailer", StringComparison.OrdinalIgnoreCase)).Select(i => _fileSystem.GetFileInfo(i.Path)));
|
files.AddRange(currentVideo.Extras.Where(i => i.ExtraType != ExtraType.Trailer).Select(i => _fileSystem.GetFileInfo(i.Path)));
|
||||||
}
|
}
|
||||||
|
|
||||||
return ResolvePaths(files, directoryService, null, new LibraryOptions(), null)
|
return ResolvePaths(files, directoryService, null, new LibraryOptions(), null)
|
||||||
@ -2712,7 +2704,7 @@ namespace Emby.Server.Implementations.Library
|
|||||||
|
|
||||||
if (!string.Equals(newPath, path, StringComparison.Ordinal))
|
if (!string.Equals(newPath, path, StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
if (to.IndexOf('/') != -1)
|
if (to.IndexOf('/', StringComparison.Ordinal) != -1)
|
||||||
{
|
{
|
||||||
newPath = newPath.Replace('\\', '/');
|
newPath = newPath.Replace('\\', '/');
|
||||||
}
|
}
|
||||||
@ -2733,30 +2725,7 @@ namespace Emby.Server.Implementations.Library
|
|||||||
|
|
||||||
var result = resolver.GetExtraInfo(item.Path);
|
var result = resolver.GetExtraInfo(item.Path);
|
||||||
|
|
||||||
if (string.Equals(result.ExtraType, "deletedscene", StringComparison.OrdinalIgnoreCase))
|
item.ExtraType = result.ExtraType;
|
||||||
{
|
|
||||||
item.ExtraType = ExtraType.DeletedScene;
|
|
||||||
}
|
|
||||||
else if (string.Equals(result.ExtraType, "behindthescenes", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
item.ExtraType = ExtraType.BehindTheScenes;
|
|
||||||
}
|
|
||||||
else if (string.Equals(result.ExtraType, "interview", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
item.ExtraType = ExtraType.Interview;
|
|
||||||
}
|
|
||||||
else if (string.Equals(result.ExtraType, "scene", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
item.ExtraType = ExtraType.Scene;
|
|
||||||
}
|
|
||||||
else if (string.Equals(result.ExtraType, "sample", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
item.ExtraType = ExtraType.Sample;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
item.ExtraType = ExtraType.Clip;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<PersonInfo> GetPeople(InternalPeopleQuery query)
|
public List<PersonInfo> GetPeople(InternalPeopleQuery query)
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
#pragma warning disable SA1600
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
#pragma warning disable SA1600
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@ -145,7 +146,7 @@ namespace Emby.Server.Implementations.Library
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<List<MediaSourceInfo>> GetPlayackMediaSources(BaseItem item, User user, bool allowMediaProbe, bool enablePathSubstitution, CancellationToken cancellationToken)
|
public async Task<List<MediaSourceInfo>> GetPlaybackMediaSources(BaseItem item, User user, bool allowMediaProbe, bool enablePathSubstitution, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var mediaSources = GetStaticMediaSources(item, enablePathSubstitution, user);
|
var mediaSources = GetStaticMediaSources(item, enablePathSubstitution, user);
|
||||||
|
|
||||||
@ -307,7 +308,7 @@ namespace Emby.Server.Implementations.Library
|
|||||||
return await GetLiveStream(liveStreamId, cancellationToken).ConfigureAwait(false);
|
return await GetLiveStream(liveStreamId, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
var sources = await GetPlayackMediaSources(item, null, false, enablePathSubstitution, cancellationToken).ConfigureAwait(false);
|
var sources = await GetPlaybackMediaSources(item, null, false, enablePathSubstitution, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
return sources.FirstOrDefault(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase));
|
return sources.FirstOrDefault(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase));
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
#pragma warning disable SA1600
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
#pragma warning disable SA1600
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
#pragma warning disable SA1600
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
#pragma warning disable SA1600
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
#pragma warning disable SA1600
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
#pragma warning disable SA1600
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
#pragma warning disable SA1600
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
#pragma warning disable SA1600
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user