mirror of
https://github.com/jellyfin/jellyfin.git
synced 2025-12-15 21:43:03 +03:00
Compare commits
62 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a3d048a0d9 | ||
|
|
21a6c2e1e2 | ||
|
|
7873fe22fb | ||
|
|
1f5625d7f9 | ||
|
|
a0b053e4a1 | ||
|
|
3af63bf439 | ||
|
|
77f72dc607 | ||
|
|
16d9318e08 | ||
|
|
d8f865e93c | ||
|
|
3044dfc114 | ||
|
|
ace1e70c63 | ||
|
|
6a9a677111 | ||
|
|
cc35876f6b | ||
|
|
5611b2c038 | ||
|
|
163fb94bde | ||
|
|
5c4326daf4 | ||
|
|
2d369ca614 | ||
|
|
3c05079333 | ||
|
|
0c204f4706 | ||
|
|
1e3a524a7a | ||
|
|
99e22c499d | ||
|
|
dbbf97e588 | ||
|
|
4e9df69ffd | ||
|
|
7f38ef4c3c | ||
|
|
16549dead9 | ||
|
|
9bd1a9d19c | ||
|
|
67194994f9 | ||
|
|
c249e15f48 | ||
|
|
48ba5a9a30 | ||
|
|
dd13f8d16a | ||
|
|
b43a8a56dc | ||
|
|
3ec18f085e | ||
|
|
f2728b5a92 | ||
|
|
ee47a75f9f | ||
|
|
66e7e8bcd2 | ||
|
|
0de3a9465e | ||
|
|
b2f7417365 | ||
|
|
bf0c07abfe | ||
|
|
3a4cd01b13 | ||
|
|
7059761806 | ||
|
|
2cde59af44 | ||
|
|
52a850cc9b | ||
|
|
54435a1243 | ||
|
|
899be44388 | ||
|
|
354079862e | ||
|
|
01db9af821 | ||
|
|
9da2635d86 | ||
|
|
4caa597cde | ||
|
|
da34bd940e | ||
|
|
54efde4073 | ||
|
|
02cfa15582 | ||
|
|
0e171d794c | ||
|
|
d5f2384375 | ||
|
|
816b3f5c2e | ||
|
|
a234388552 | ||
|
|
30d38bd5c6 | ||
|
|
9f49fe2e99 | ||
|
|
f720a0fca2 | ||
|
|
aadff77531 | ||
|
|
89fc5aa11a | ||
|
|
cb91595a24 | ||
|
|
6233bae7f0 |
@@ -1,13 +1,13 @@
|
||||
parameters:
|
||||
- name: Packages
|
||||
type: object
|
||||
default: {}
|
||||
- name: LinuxImage
|
||||
type: string
|
||||
default: "ubuntu-latest"
|
||||
- name: DotNetSdkVersion
|
||||
type: string
|
||||
default: 3.1.100
|
||||
- name: Packages
|
||||
type: object
|
||||
default: {}
|
||||
- name: LinuxImage
|
||||
type: string
|
||||
default: "ubuntu-latest"
|
||||
- name: DotNetSdkVersion
|
||||
type: string
|
||||
default: 3.1.100
|
||||
|
||||
jobs:
|
||||
- job: CompatibilityCheck
|
||||
@@ -23,7 +23,7 @@ jobs:
|
||||
NugetPackageName: ${{ Package.value.NugetPackageName }}
|
||||
AssemblyFileName: ${{ Package.value.AssemblyFileName }}
|
||||
maxParallel: 2
|
||||
dependsOn: Build
|
||||
dependsOn: MainBuild
|
||||
steps:
|
||||
- checkout: none
|
||||
|
||||
@@ -33,13 +33,6 @@ jobs:
|
||||
packageType: sdk
|
||||
version: ${{ parameters.DotNetSdkVersion }}
|
||||
|
||||
- task: DotNetCoreCLI@2
|
||||
displayName: 'Install ABI CompatibilityChecker tool'
|
||||
inputs:
|
||||
command: custom
|
||||
custom: tool
|
||||
arguments: 'update compatibilitychecker -g'
|
||||
|
||||
- task: DownloadPipelineArtifact@2
|
||||
displayName: "Download New Assembly Build Artifact"
|
||||
inputs:
|
||||
@@ -79,11 +72,25 @@ jobs:
|
||||
overWrite: true
|
||||
flattenFolders: true
|
||||
|
||||
# The `--warnings-only` switch will swallow the return code and not emit any errors.
|
||||
- task: DotNetCoreCLI@2
|
||||
displayName: 'Execute ABI Compatibility Check Tool'
|
||||
- task: DownloadGitHubRelease@0
|
||||
displayName: "Download ABI Compatibility Check Tool"
|
||||
inputs:
|
||||
command: custom
|
||||
custom: compat
|
||||
arguments: 'current-release/$(AssemblyFileName) new-release/$(AssemblyFileName) --azure-pipelines --warnings-only'
|
||||
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)
|
||||
@@ -1,93 +1,101 @@
|
||||
parameters:
|
||||
LinuxImage: 'ubuntu-latest'
|
||||
RestoreBuildProjects: 'Jellyfin.Server/Jellyfin.Server.csproj'
|
||||
LinuxImage: "ubuntu-latest"
|
||||
RestoreBuildProjects: "Jellyfin.Server/Jellyfin.Server.csproj"
|
||||
DotNetSdkVersion: 3.1.100
|
||||
|
||||
jobs:
|
||||
- job: Build
|
||||
displayName: Build
|
||||
- job: MainBuild
|
||||
displayName: Main Build
|
||||
strategy:
|
||||
matrix:
|
||||
Release:
|
||||
BuildConfiguration: Release
|
||||
Debug:
|
||||
BuildConfiguration: Debug
|
||||
maxParallel: 2
|
||||
pool:
|
||||
vmImage: '${{ parameters.LinuxImage }}'
|
||||
vmImage: "${{ parameters.LinuxImage }}"
|
||||
steps:
|
||||
- checkout: self
|
||||
clean: true
|
||||
submodules: true
|
||||
persistCredentials: true
|
||||
|
||||
- task: DownloadPipelineArtifact@2
|
||||
displayName: 'Download Web Branch'
|
||||
condition: in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI', 'BuildCompletion')
|
||||
- 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:
|
||||
path: '$(Agent.TempDirectory)'
|
||||
artifact: 'jellyfin-web-production'
|
||||
source: 'specific'
|
||||
project: 'jellyfin'
|
||||
pipeline: 'Jellyfin Web'
|
||||
runBranch: variables['Build.SourceBranch']
|
||||
script: "git clone --single-branch --branch $(Build.SourceBranchName) --depth=1 https://github.com/jellyfin/jellyfin-web.git $(Agent.TempDirectory)/jellyfin-web"
|
||||
|
||||
- task: DownloadPipelineArtifact@2
|
||||
displayName: 'Download Web Target'
|
||||
condition: eq(variables['Build.Reason'], 'PullRequest')
|
||||
- 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:
|
||||
path: '$(Agent.TempDirectory)'
|
||||
artifact: 'jellyfin-web-production'
|
||||
source: 'specific'
|
||||
project: 'jellyfin'
|
||||
pipeline: 'Jellyfin Web'
|
||||
runBranch: variables['System.PullRequest.TargetBranch']
|
||||
script: "git clone --single-branch --branch $(System.PullRequest.TargetBranch) --depth 1 https://github.com/jellyfin/jellyfin-web.git $(Agent.TempDirectory)/jellyfin-web"
|
||||
|
||||
- task: ExtractFiles@1
|
||||
displayName: 'Extract Web Client'
|
||||
- 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:
|
||||
archiveFilePatterns: '$(Agent.TempDirectory)/*.zip'
|
||||
destinationFolder: '$(Build.SourcesDirectory)/MediaBrowser.WebDashboard'
|
||||
cleanDestinationFolder: false
|
||||
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'
|
||||
displayName: "Update DotNet"
|
||||
inputs:
|
||||
packageType: sdk
|
||||
version: ${{ parameters.DotNetSdkVersion }}
|
||||
|
||||
- task: DotNetCoreCLI@2
|
||||
displayName: 'Publish Server'
|
||||
displayName: "Publish Server"
|
||||
inputs:
|
||||
command: publish
|
||||
publishWebProjects: false
|
||||
projects: '${{ parameters.RestoreBuildProjects }}'
|
||||
arguments: '--configuration $(BuildConfiguration) --output $(Build.ArtifactStagingDirectory)'
|
||||
projects: "${{ parameters.RestoreBuildProjects }}"
|
||||
arguments: "--configuration $(BuildConfiguration) --output $(build.artifactstagingdirectory)"
|
||||
zipAfterPublish: false
|
||||
|
||||
- task: PublishPipelineArtifact@0
|
||||
displayName: 'Publish Artifact Naming'
|
||||
displayName: "Publish Artifact Naming"
|
||||
condition: and(succeeded(), eq(variables['BuildConfiguration'], 'Release'))
|
||||
inputs:
|
||||
targetPath: '$(build.ArtifactStagingDirectory)/Jellyfin.Server/Emby.Naming.dll'
|
||||
artifactName: 'Jellyfin.Naming'
|
||||
targetPath: "$(build.artifactstagingdirectory)/Jellyfin.Server/Emby.Naming.dll"
|
||||
artifactName: "Jellyfin.Naming"
|
||||
|
||||
- task: PublishPipelineArtifact@0
|
||||
displayName: 'Publish Artifact Controller'
|
||||
displayName: "Publish Artifact Controller"
|
||||
condition: and(succeeded(), eq(variables['BuildConfiguration'], 'Release'))
|
||||
inputs:
|
||||
targetPath: '$(build.ArtifactStagingDirectory)/Jellyfin.Server/MediaBrowser.Controller.dll'
|
||||
artifactName: 'Jellyfin.Controller'
|
||||
targetPath: "$(build.artifactstagingdirectory)/Jellyfin.Server/MediaBrowser.Controller.dll"
|
||||
artifactName: "Jellyfin.Controller"
|
||||
|
||||
- task: PublishPipelineArtifact@0
|
||||
displayName: 'Publish Artifact Model'
|
||||
displayName: "Publish Artifact Model"
|
||||
condition: and(succeeded(), eq(variables['BuildConfiguration'], 'Release'))
|
||||
inputs:
|
||||
targetPath: '$(build.ArtifactStagingDirectory)/Jellyfin.Server/MediaBrowser.Model.dll'
|
||||
artifactName: 'Jellyfin.Model'
|
||||
targetPath: "$(build.artifactstagingdirectory)/Jellyfin.Server/MediaBrowser.Model.dll"
|
||||
artifactName: "Jellyfin.Model"
|
||||
|
||||
- task: PublishPipelineArtifact@0
|
||||
displayName: 'Publish Artifact Common'
|
||||
displayName: "Publish Artifact Common"
|
||||
condition: and(succeeded(), eq(variables['BuildConfiguration'], 'Release'))
|
||||
inputs:
|
||||
targetPath: '$(build.ArtifactStagingDirectory)/Jellyfin.Server/MediaBrowser.Common.dll'
|
||||
artifactName: 'Jellyfin.Common'
|
||||
targetPath: "$(build.artifactstagingdirectory)/Jellyfin.Server/MediaBrowser.Common.dll"
|
||||
artifactName: "Jellyfin.Common"
|
||||
|
||||
@@ -1,163 +0,0 @@
|
||||
jobs:
|
||||
- job: BuildPackage
|
||||
displayName: 'Build Packages'
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
CentOS.amd64:
|
||||
BuildConfiguration: centos.amd64
|
||||
Fedora.amd64:
|
||||
BuildConfiguration: fedora.amd64
|
||||
Debian.amd64:
|
||||
BuildConfiguration: debian.amd64
|
||||
Debian.arm64:
|
||||
BuildConfiguration: debian.arm64
|
||||
Debian.armhf:
|
||||
BuildConfiguration: debian.armhf
|
||||
Ubuntu.amd64:
|
||||
BuildConfiguration: ubuntu.amd64
|
||||
Ubuntu.arm64:
|
||||
BuildConfiguration: ubuntu.arm64
|
||||
Ubuntu.armhf:
|
||||
BuildConfiguration: ubuntu.armhf
|
||||
Linux.amd64:
|
||||
BuildConfiguration: linux.amd64
|
||||
Windows.amd64:
|
||||
BuildConfiguration: windows.amd64
|
||||
MacOS:
|
||||
BuildConfiguration: macos
|
||||
Portable:
|
||||
BuildConfiguration: portable
|
||||
|
||||
pool:
|
||||
vmImage: 'ubuntu-latest'
|
||||
|
||||
steps:
|
||||
- script: 'docker build -f deployment/Dockerfile.$(BuildConfiguration) -t jellyfin-server-$(BuildConfiguration) deployment'
|
||||
displayName: 'Build Dockerfile'
|
||||
|
||||
- script: 'docker image ls -a && docker run -v $(pwd)/deployment/dist:/dist -v $(pwd):/jellyfin -e IS_UNSTABLE="yes" -e BUILD_ID=$(Build.BuildNumber) jellyfin-server-$(BuildConfiguration)'
|
||||
displayName: 'Run Dockerfile (unstable)'
|
||||
condition: startsWith(variables['Build.SourceBranch'], 'refs/heads/master')
|
||||
|
||||
- script: 'docker image ls -a && docker run -v $(pwd)/deployment/dist:/dist -v $(pwd):/jellyfin -e IS_UNSTABLE="no" -e BUILD_ID=$(Build.BuildNumber) jellyfin-server-$(BuildConfiguration)'
|
||||
displayName: 'Run Dockerfile (stable)'
|
||||
condition: startsWith(variables['Build.SourceBranch'], 'refs/tags')
|
||||
|
||||
- task: PublishPipelineArtifact@1
|
||||
displayName: 'Publish Release'
|
||||
inputs:
|
||||
targetPath: '$(Build.SourcesDirectory)/deployment/dist'
|
||||
artifactName: 'jellyfin-server-$(BuildConfiguration)'
|
||||
|
||||
- task: SSH@0
|
||||
displayName: 'Create target directory on repository server'
|
||||
inputs:
|
||||
sshEndpoint: repository
|
||||
runOptions: 'inline'
|
||||
inline: 'mkdir -p /srv/repository/incoming/azure/$(Build.BuildNumber)/$(BuildConfiguration)'
|
||||
|
||||
- task: CopyFilesOverSSH@0
|
||||
displayName: 'Upload artifacts to repository server'
|
||||
inputs:
|
||||
sshEndpoint: repository
|
||||
sourceFolder: '$(Build.SourcesDirectory)/deployment/dist'
|
||||
contents: '**'
|
||||
targetFolder: '/srv/repository/incoming/azure/$(Build.BuildNumber)/$(BuildConfiguration)'
|
||||
|
||||
- job: BuildDocker
|
||||
displayName: 'Build Docker'
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
amd64:
|
||||
BuildConfiguration: amd64
|
||||
arm64:
|
||||
BuildConfiguration: arm64
|
||||
armhf:
|
||||
BuildConfiguration: armhf
|
||||
|
||||
pool:
|
||||
vmImage: 'ubuntu-latest'
|
||||
|
||||
steps:
|
||||
- task: Docker@2
|
||||
displayName: 'Push Unstable Image'
|
||||
condition: startsWith(variables['Build.SourceBranch'], 'refs/heads/master')
|
||||
inputs:
|
||||
repository: 'jellyfin/jellyfin-server'
|
||||
command: buildAndPush
|
||||
buildContext: '.'
|
||||
Dockerfile: 'deployment/Dockerfile.docker.$(BuildConfiguration)'
|
||||
containerRegistry: Docker Hub
|
||||
tags: |
|
||||
unstable-$(Build.BuildNumber)-$(BuildConfiguration)
|
||||
unstable-$(BuildConfiguration)
|
||||
|
||||
- task: Docker@2
|
||||
displayName: 'Push Stable Image'
|
||||
condition: startsWith(variables['Build.SourceBranch'], 'refs/tags')
|
||||
inputs:
|
||||
repository: 'jellyfin/jellyfin-server'
|
||||
command: buildAndPush
|
||||
buildContext: '.'
|
||||
Dockerfile: 'deployment/Dockerfile.docker.$(BuildConfiguration)'
|
||||
containerRegistry: Docker Hub
|
||||
tags: |
|
||||
stable-$(Build.BuildNumber)-$(BuildConfiguration)
|
||||
stable-$(BuildConfiguration)
|
||||
|
||||
- job: CollectArtifacts
|
||||
displayName: 'Collect Artifacts'
|
||||
dependsOn:
|
||||
- BuildPackage
|
||||
- BuildDocker
|
||||
condition: and(succeeded('BuildPackage'), succeeded('BuildDocker'))
|
||||
|
||||
pool:
|
||||
vmImage: 'ubuntu-latest'
|
||||
|
||||
steps:
|
||||
- task: SSH@0
|
||||
displayName: 'Update Unstable Repository'
|
||||
condition: startsWith(variables['Build.SourceBranch'], 'refs/heads/master')
|
||||
inputs:
|
||||
sshEndpoint: repository
|
||||
runOptions: 'inline'
|
||||
inline: |
|
||||
sudo /srv/repository/collect-server.azure.sh /srv/repository/incoming/azure $(Build.BuildNumber) unstable
|
||||
rm $0
|
||||
exit
|
||||
|
||||
- task: SSH@0
|
||||
displayName: 'Update Stable Repository'
|
||||
condition: startsWith(variables['Build.SourceBranch'], 'refs/tags')
|
||||
inputs:
|
||||
sshEndpoint: repository
|
||||
runOptions: 'inline'
|
||||
inline: |
|
||||
sudo /srv/repository/collect-server.azure.sh /srv/repository/incoming/azure $(Build.BuildNumber)
|
||||
rm $0
|
||||
exit
|
||||
|
||||
- job: PublishNuget
|
||||
displayName: 'Publish NuGet packages'
|
||||
dependsOn:
|
||||
- BuildPackage
|
||||
condition: and(succeeded('BuildPackage'), startsWith(variables['Build.SourceBranch'], 'refs/tags'))
|
||||
|
||||
pool:
|
||||
vmImage: 'ubuntu-latest'
|
||||
|
||||
steps:
|
||||
- task: NuGetCommand@2
|
||||
inputs:
|
||||
command: 'pack'
|
||||
packagesToPack: Jellyfin.Data/Jellyfin.Data.csproj;MediaBrowser.Common/MediaBrowser.Common.csproj;MediaBrowser.Controller/MediaBrowser.Controller.csproj;MediaBrowser.Model/MediaBrowser.Model.csproj;Emby.Naming/Emby.Naming.csproj
|
||||
packDestination: '$(Build.ArtifactStagingDirectory)'
|
||||
|
||||
- task: NuGetCommand@2
|
||||
inputs:
|
||||
command: 'push'
|
||||
packagesToPush: '$(Build.ArtifactStagingDirectory)/**/*.nupkg'
|
||||
includeNugetOrg: 'true'
|
||||
@@ -1,25 +1,26 @@
|
||||
parameters:
|
||||
- name: ImageNames
|
||||
type: object
|
||||
default:
|
||||
Linux: "ubuntu-latest"
|
||||
Windows: "windows-latest"
|
||||
macOS: "macos-latest"
|
||||
- name: TestProjects
|
||||
type: string
|
||||
default: "tests/**/*Tests.csproj"
|
||||
- name: DotNetSdkVersion
|
||||
type: string
|
||||
default: 3.1.100
|
||||
- name: ImageNames
|
||||
type: object
|
||||
default:
|
||||
Linux: "ubuntu-latest"
|
||||
Windows: "windows-latest"
|
||||
macOS: "macos-latest"
|
||||
- name: TestProjects
|
||||
type: string
|
||||
default: "tests/**/*Tests.csproj"
|
||||
- name: DotNetSdkVersion
|
||||
type: string
|
||||
default: 3.1.100
|
||||
|
||||
jobs:
|
||||
- job: Test
|
||||
displayName: Test
|
||||
- job: MainTest
|
||||
displayName: Main Test
|
||||
strategy:
|
||||
matrix:
|
||||
${{ each imageName in parameters.ImageNames }}:
|
||||
${{ imageName.key }}:
|
||||
ImageName: ${{ imageName.value }}
|
||||
maxParallel: 3
|
||||
pool:
|
||||
vmImage: "$(ImageName)"
|
||||
steps:
|
||||
@@ -28,31 +29,14 @@ jobs:
|
||||
submodules: true
|
||||
persistCredentials: false
|
||||
|
||||
# This is required for the SonarCloud analyzer
|
||||
- task: UseDotNet@2
|
||||
displayName: "Install .NET Core SDK 2.1"
|
||||
condition: eq(variables['ImageName'], 'ubuntu-latest')
|
||||
inputs:
|
||||
packageType: sdk
|
||||
version: '2.1.805'
|
||||
|
||||
- task: UseDotNet@2
|
||||
displayName: "Update DotNet"
|
||||
inputs:
|
||||
packageType: sdk
|
||||
version: ${{ parameters.DotNetSdkVersion }}
|
||||
|
||||
- task: SonarCloudPrepare@1
|
||||
displayName: 'Prepare analysis on SonarCloud'
|
||||
condition: eq(variables['ImageName'], 'ubuntu-latest')
|
||||
enabled: false
|
||||
inputs:
|
||||
SonarCloud: 'Sonarcloud for Jellyfin'
|
||||
organization: 'jellyfin'
|
||||
projectKey: 'jellyfin_jellyfin'
|
||||
|
||||
- task: DotNetCoreCLI@2
|
||||
displayName: 'Run CLI Tests'
|
||||
displayName: Run .NET Core CLI tests
|
||||
inputs:
|
||||
command: "test"
|
||||
projects: ${{ parameters.TestProjects }}
|
||||
@@ -61,20 +45,9 @@ jobs:
|
||||
testRunTitle: $(Agent.JobName)
|
||||
workingDirectory: "$(Build.SourcesDirectory)"
|
||||
|
||||
- task: SonarCloudAnalyze@1
|
||||
displayName: 'Run Code Analysis'
|
||||
condition: eq(variables['ImageName'], 'ubuntu-latest')
|
||||
enabled: false
|
||||
|
||||
- task: SonarCloudPublish@1
|
||||
displayName: 'Publish Quality Gate Result'
|
||||
condition: eq(variables['ImageName'], 'ubuntu-latest')
|
||||
enabled: false
|
||||
|
||||
- task: Palmmedia.reportgenerator.reportgenerator-build-release-task.reportgenerator@4
|
||||
condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux')) # !! THIS is for V1 only V2 will/should support merging
|
||||
displayName: 'Run ReportGenerator'
|
||||
enabled: false
|
||||
displayName: ReportGenerator (merge)
|
||||
inputs:
|
||||
reports: "$(Agent.TempDirectory)/**/coverage.cobertura.xml"
|
||||
targetdir: "$(Agent.TempDirectory)/merged/"
|
||||
@@ -83,8 +56,7 @@ jobs:
|
||||
## V2 is already in the repository but it does not work "wrong number of segments" YAML error.
|
||||
- task: PublishCodeCoverageResults@1
|
||||
condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux')) # !! THIS is for V1 only V2 will/should support merging
|
||||
displayName: 'Publish Code Coverage'
|
||||
enabled: false
|
||||
displayName: Publish Code Coverage
|
||||
inputs:
|
||||
codeCoverageTool: "cobertura"
|
||||
#summaryFileLocation: '$(Agent.TempDirectory)/**/coverage.cobertura.xml' # !!THIS IS FOR V2
|
||||
|
||||
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"
|
||||
@@ -1,12 +1,12 @@
|
||||
name: $(Date:yyyyMMdd)$(Rev:.r)
|
||||
|
||||
variables:
|
||||
- name: TestProjects
|
||||
value: 'tests/**/*Tests.csproj'
|
||||
- name: RestoreBuildProjects
|
||||
value: 'Jellyfin.Server/Jellyfin.Server.csproj'
|
||||
- name: DotNetSdkVersion
|
||||
value: 3.1.100
|
||||
- name: TestProjects
|
||||
value: "tests/**/*Tests.csproj"
|
||||
- name: RestoreBuildProjects
|
||||
value: "Jellyfin.Server/Jellyfin.Server.csproj"
|
||||
- name: DotNetSdkVersion
|
||||
value: 3.1.100
|
||||
|
||||
pr:
|
||||
autoCancel: true
|
||||
@@ -15,22 +15,24 @@ trigger:
|
||||
batch: true
|
||||
|
||||
jobs:
|
||||
- ${{ if not(or(startsWith(variables['Build.SourceBranch'], 'refs/tags'), startsWith(variables['Build.SourceBranch'], 'refs/heads/master'))) }}:
|
||||
- template: azure-pipelines-main.yml
|
||||
parameters:
|
||||
LinuxImage: 'ubuntu-latest'
|
||||
LinuxImage: "ubuntu-latest"
|
||||
RestoreBuildProjects: $(RestoreBuildProjects)
|
||||
|
||||
- ${{ if not(or(startsWith(variables['Build.SourceBranch'], 'refs/tags'), startsWith(variables['Build.SourceBranch'], 'refs/heads/master'))) }}:
|
||||
- template: azure-pipelines-test.yml
|
||||
parameters:
|
||||
ImageNames:
|
||||
Linux: 'ubuntu-latest'
|
||||
Windows: 'windows-latest'
|
||||
macOS: 'macos-latest'
|
||||
Linux: "ubuntu-latest"
|
||||
Windows: "windows-latest"
|
||||
macOS: "macos-latest"
|
||||
|
||||
- ${{ if not(or(startsWith(variables['Build.SourceBranch'], 'refs/tags'), startsWith(variables['Build.SourceBranch'], 'refs/heads/master'))) }}:
|
||||
- template: azure-pipelines-abi.yml
|
||||
- template: azure-pipelines-windows.yml
|
||||
parameters:
|
||||
WindowsImage: "windows-latest"
|
||||
TestProjects: $(TestProjects)
|
||||
|
||||
- template: azure-pipelines-compat.yml
|
||||
parameters:
|
||||
Packages:
|
||||
Naming:
|
||||
@@ -45,7 +47,4 @@ jobs:
|
||||
Common:
|
||||
NugetPackageName: Jellyfin.Common
|
||||
AssemblyFileName: MediaBrowser.Common.dll
|
||||
LinuxImage: 'ubuntu-latest'
|
||||
|
||||
- ${{ if or(startsWith(variables['Build.SourceBranch'], 'refs/tags'), startsWith(variables['Build.SourceBranch'], 'refs/heads/master')) }}:
|
||||
- template: azure-pipelines-package.yml
|
||||
LinuxImage: "ubuntu-latest"
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
../fedora/Makefile
|
||||
59
.copr/Makefile
Normal file
59
.copr/Makefile
Normal file
@@ -0,0 +1,59 @@
|
||||
VERSION := $(shell sed -ne '/^Version:/s/.* *//p' \
|
||||
deployment/fedora-package-x64/pkg-src/jellyfin.spec)
|
||||
|
||||
deployment/fedora-package-x64/pkg-src/jellyfin-web-$(VERSION).tar.gz:
|
||||
curl -f -L -o deployment/fedora-package-x64/pkg-src/jellyfin-web-$(VERSION).tar.gz \
|
||||
https://github.com/jellyfin/jellyfin-web/archive/v$(VERSION).tar.gz \
|
||||
|| curl -f -L -o deployment/fedora-package-x64/pkg-src/jellyfin-web-$(VERSION).tar.gz \
|
||||
https://github.com/jellyfin/jellyfin-web/archive/master.tar.gz \
|
||||
|
||||
srpm: deployment/fedora-package-x64/pkg-src/jellyfin-web-$(VERSION).tar.gz
|
||||
cd deployment/fedora-package-x64; \
|
||||
SOURCE_DIR=../.. \
|
||||
WORKDIR="$${PWD}"; \
|
||||
package_temporary_dir="$${WORKDIR}/pkg-dist-tmp"; \
|
||||
pkg_src_dir="$${WORKDIR}/pkg-src"; \
|
||||
GNU_TAR=1; \
|
||||
tar \
|
||||
--transform "s,^\.,jellyfin-$(VERSION)," \
|
||||
--exclude='.git*' \
|
||||
--exclude='**/.git' \
|
||||
--exclude='**/.hg' \
|
||||
--exclude='**/.vs' \
|
||||
--exclude='**/.vscode' \
|
||||
--exclude='deployment' \
|
||||
--exclude='**/bin' \
|
||||
--exclude='**/obj' \
|
||||
--exclude='**/.nuget' \
|
||||
--exclude='*.deb' \
|
||||
--exclude='*.rpm' \
|
||||
-czf "pkg-src/jellyfin-$(VERSION).tar.gz" \
|
||||
-C $${SOURCE_DIR} ./ || GNU_TAR=0; \
|
||||
if [ $${GNU_TAR} -eq 0 ]; then \
|
||||
package_temporary_dir="$$(mktemp -d)"; \
|
||||
mkdir -p "$${package_temporary_dir}/jellyfin"; \
|
||||
tar \
|
||||
--exclude='.git*' \
|
||||
--exclude='**/.git' \
|
||||
--exclude='**/.hg' \
|
||||
--exclude='**/.vs' \
|
||||
--exclude='**/.vscode' \
|
||||
--exclude='deployment' \
|
||||
--exclude='**/bin' \
|
||||
--exclude='**/obj' \
|
||||
--exclude='**/.nuget' \
|
||||
--exclude='*.deb' \
|
||||
--exclude='*.rpm' \
|
||||
-czf "$${package_temporary_dir}/jellyfin/jellyfin-$(VERSION).tar.gz" \
|
||||
-C $${SOURCE_DIR} ./; \
|
||||
mkdir -p "$${package_temporary_dir}/jellyfin-$(VERSION)"; \
|
||||
tar -xzf "$${package_temporary_dir}/jellyfin/jellyfin-$(VERSION).tar.gz" \
|
||||
-C "$${package_temporary_dir}/jellyfin-$(VERSION); \
|
||||
rm -f "$${package_temporary_dir}/jellyfin/jellyfin-$(VERSION).tar.gz"; \
|
||||
tar -czf "$${SOURCE_DIR}/SOURCES/pkg-src/jellyfin-$(VERSION).tar.gz" \
|
||||
-C "$${package_temporary_dir}" "jellyfin-$(VERSION); \
|
||||
rm -rf $${package_temporary_dir}; \
|
||||
fi; \
|
||||
rpmbuild -bs pkg-src/jellyfin.spec \
|
||||
--define "_sourcedir $$PWD/pkg-src/" \
|
||||
--define "_srcrpmdir $(outdir)"
|
||||
@@ -13,7 +13,7 @@ charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
end_of_line = lf
|
||||
max_line_length = off
|
||||
max_line_length = null
|
||||
|
||||
# YAML indentation
|
||||
[*.{yml,yaml}]
|
||||
@@ -22,7 +22,6 @@ indent_size = 2
|
||||
# XML indentation
|
||||
[*.{csproj,xml}]
|
||||
indent_size = 2
|
||||
|
||||
###############################
|
||||
# .NET Coding Conventions #
|
||||
###############################
|
||||
@@ -52,12 +51,11 @@ dotnet_style_explicit_tuple_names = true:suggestion
|
||||
dotnet_style_null_propagation = true:suggestion
|
||||
dotnet_style_coalesce_expression = true:suggestion
|
||||
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent
|
||||
dotnet_style_prefer_inferred_tuple_names = true:suggestion
|
||||
dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
|
||||
dotnet_prefer_inferred_tuple_names = true:suggestion
|
||||
dotnet_prefer_inferred_anonymous_type_member_names = true:suggestion
|
||||
dotnet_style_prefer_auto_properties = true:silent
|
||||
dotnet_style_prefer_conditional_expression_over_assignment = true:silent
|
||||
dotnet_style_prefer_conditional_expression_over_return = true:silent
|
||||
|
||||
###############################
|
||||
# Naming Conventions #
|
||||
###############################
|
||||
@@ -69,7 +67,7 @@ dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.symbols = non
|
||||
dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.style = non_private_static_field_style
|
||||
|
||||
dotnet_naming_symbols.non_private_static_fields.applicable_kinds = field
|
||||
dotnet_naming_symbols.non_private_static_fields.applicable_accessibilities = public, protected, internal, protected_internal, private_protected
|
||||
dotnet_naming_symbols.non_private_static_fields.applicable_accessibilities = public, protected, internal, protected internal, private protected
|
||||
dotnet_naming_symbols.non_private_static_fields.required_modifiers = static
|
||||
|
||||
dotnet_naming_style.non_private_static_field_style.capitalization = pascal_case
|
||||
@@ -161,7 +159,6 @@ csharp_style_deconstructed_variable_declaration = true:suggestion
|
||||
csharp_prefer_simple_default_expression = true:suggestion
|
||||
csharp_style_pattern_local_over_anonymous_function = true:suggestion
|
||||
csharp_style_inlined_variable_declaration = true:suggestion
|
||||
|
||||
###############################
|
||||
# C# Formatting Rules #
|
||||
###############################
|
||||
@@ -192,3 +189,9 @@ csharp_space_between_method_call_empty_parameter_list_parentheses = false
|
||||
# Wrapping preferences
|
||||
csharp_preserve_single_line_statements = true
|
||||
csharp_preserve_single_line_blocks = true
|
||||
###############################
|
||||
# VB Coding Conventions #
|
||||
###############################
|
||||
[*.vb]
|
||||
# Modifier preferences
|
||||
visual_basic_preferred_modifier_order = Partial,Default,Private,Protected,Public,Friend,NotOverridable,Overridable,MustOverride,Overloads,Overrides,MustInherit,NotInheritable,Static,Shared,Shadows,ReadOnly,WriteOnly,Dim,Const,WithEvents,Widening,Narrowing,Custom,Async:suggestion
|
||||
|
||||
3
.github/CODEOWNERS
vendored
3
.github/CODEOWNERS
vendored
@@ -1,3 +0,0 @@
|
||||
# Joshua must review all changes to deployment and build.sh
|
||||
deployment/* @joshuaboniface
|
||||
build.sh @joshuaboniface
|
||||
13
.github/ISSUE_TEMPLATE/feature_request.md
vendored
13
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -1,13 +0,0 @@
|
||||
---
|
||||
name: Feature Request
|
||||
about: Request a new feature
|
||||
title: ''
|
||||
labels: feature-request
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
**PLEASE DO NOT OPEN FEATURE REQUEST ISSUES ON GITHUB**
|
||||
|
||||
**Feature requests should be opened on our dedicated [feature request](https://features.jellyfin.org/) hub so they can be appropriately discussed and prioritized.**
|
||||
|
||||
However, if you are willing to contribute to the project by adding a new feature yourself, then please ensure that you first review our [documentation](https://docs.jellyfin.org/general/contributing/development.html) on contributing code. Once you have reviewed the documentation, feel free to come back here and open an issue here outlining your proposed approach so that it can be documented, tracked, and discussed by other team members.
|
||||
9
.github/dependabot.yml
vendored
9
.github/dependabot.yml
vendored
@@ -1,9 +0,0 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: nuget
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: weekly
|
||||
time: '12:00'
|
||||
open-pull-requests-limit: 10
|
||||
|
||||
19
.gitignore
vendored
19
.gitignore
vendored
@@ -244,14 +244,14 @@ pip-log.txt
|
||||
#########################
|
||||
|
||||
# Artifacts for debian-x64
|
||||
debian/.debhelper/
|
||||
debian/*.debhelper
|
||||
debian/debhelper-build-stamp
|
||||
debian/files
|
||||
debian/jellyfin.substvars
|
||||
debian/jellyfin/
|
||||
deployment/debian-package-x64/pkg-src/.debhelper/
|
||||
deployment/debian-package-x64/pkg-src/*.debhelper
|
||||
deployment/debian-package-x64/pkg-src/debhelper-build-stamp
|
||||
deployment/debian-package-x64/pkg-src/files
|
||||
deployment/debian-package-x64/pkg-src/jellyfin.substvars
|
||||
deployment/debian-package-x64/pkg-src/jellyfin/
|
||||
# Don't ignore the debian/bin folder
|
||||
!debian/bin/
|
||||
!deployment/debian-package-x64/pkg-src/bin/
|
||||
|
||||
deployment/**/dist/
|
||||
deployment/**/pkg-dist/
|
||||
@@ -271,8 +271,3 @@ dist
|
||||
|
||||
# BenchmarkDotNet artifacts
|
||||
BenchmarkDotNet.Artifacts
|
||||
|
||||
# Ignore web artifacts from native builds
|
||||
web/
|
||||
web-src.*
|
||||
MediaBrowser.WebDashboard/jellyfin-web
|
||||
|
||||
14
.vscode/extensions.json
vendored
14
.vscode/extensions.json
vendored
@@ -1,14 +0,0 @@
|
||||
{
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
|
||||
// Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
|
||||
|
||||
// List of extensions which should be recommended for users of this workspace.
|
||||
"recommendations": [
|
||||
"ms-dotnettools.csharp",
|
||||
"editorconfig.editorconfig"
|
||||
],
|
||||
// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
|
||||
"unwantedRecommendations": [
|
||||
|
||||
]
|
||||
}
|
||||
12
.vscode/launch.json
vendored
12
.vscode/launch.json
vendored
@@ -1,6 +1,9 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
// Use IntelliSense to find out which attributes exist for C# debugging
|
||||
// Use hover for the description of the existing attributes
|
||||
// For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": ".NET Core Launch (console)",
|
||||
"type": "coreclr",
|
||||
@@ -21,8 +24,5 @@
|
||||
"request": "attach",
|
||||
"processId": "${command:pickProcess}"
|
||||
}
|
||||
],
|
||||
"env": {
|
||||
"DOTNET_CLI_TELEMETRY_OPTOUT": "1"
|
||||
}
|
||||
,]
|
||||
}
|
||||
|
||||
19
.vscode/tasks.json
vendored
19
.vscode/tasks.json
vendored
@@ -10,21 +10,6 @@
|
||||
"${workspaceFolder}/Jellyfin.Server/Jellyfin.Server.csproj"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "api tests",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"test",
|
||||
"${workspaceFolder}/tests/MediaBrowser.Api.Tests/MediaBrowser.Api.Tests.csproj"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
}
|
||||
],
|
||||
"options": {
|
||||
"env": {
|
||||
"DOTNET_CLI_TELEMETRY_OPTOUT": "1"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -7,7 +7,6 @@
|
||||
- [anthonylavado](https://github.com/anthonylavado)
|
||||
- [Artiume](https://github.com/Artiume)
|
||||
- [AThomsen](https://github.com/AThomsen)
|
||||
- [barronpm](https://github.com/barronpm)
|
||||
- [bilde2910](https://github.com/bilde2910)
|
||||
- [bfayers](https://github.com/bfayers)
|
||||
- [BnMcG](https://github.com/BnMcG)
|
||||
@@ -23,7 +22,6 @@
|
||||
- [cvium](https://github.com/cvium)
|
||||
- [dannymichel](https://github.com/dannymichel)
|
||||
- [DaveChild](https://github.com/DaveChild)
|
||||
- [Delgan](https://github.com/Delgan)
|
||||
- [dcrdev](https://github.com/dcrdev)
|
||||
- [dhartung](https://github.com/dhartung)
|
||||
- [dinki](https://github.com/dinki)
|
||||
@@ -93,7 +91,6 @@
|
||||
- [samuel9554](https://github.com/samuel9554)
|
||||
- [scheidleon](https://github.com/scheidleon)
|
||||
- [sebPomme](https://github.com/sebPomme)
|
||||
- [SegiH](https://github.com/SegiH)
|
||||
- [SenorSmartyPants](https://github.com/SenorSmartyPants)
|
||||
- [shemanaev](https://github.com/shemanaev)
|
||||
- [skaro13](https://github.com/skaro13)
|
||||
@@ -131,7 +128,6 @@
|
||||
- [XVicarious](https://github.com/XVicarious)
|
||||
- [YouKnowBlom](https://github.com/YouKnowBlom)
|
||||
- [KristupasSavickas](https://github.com/KristupasSavickas)
|
||||
- [Pusta](https://github.com/pusta)
|
||||
|
||||
# Emby Contributors
|
||||
|
||||
|
||||
13
Dockerfile
13
Dockerfile
@@ -1,11 +1,13 @@
|
||||
ARG DOTNET_VERSION=3.1
|
||||
|
||||
FROM node:alpine as web-builder
|
||||
ARG JELLYFIN_WEB_VERSION=master
|
||||
RUN apk add curl git zlib zlib-dev autoconf g++ make libpng-dev gifsicle alpine-sdk automake libtool make gcc musl-dev nasm python \
|
||||
&& curl -L https://github.com/jellyfin/jellyfin-web/archive/${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
|
||||
&& cd jellyfin-web-* \
|
||||
ARG JELLYFIN_WEB_VERSION=10.5.5
|
||||
RUN apk add curl git \
|
||||
&& git clone --branch release-10.5.z --single-branch https://github.com/jellyfin/jellyfin-web.git \
|
||||
&& cd jellyfin-web \
|
||||
&& git checkout tags/v${JELLYFIN_WEB_VERSION} \
|
||||
&& yarn install \
|
||||
&& yarn build \
|
||||
&& mv dist /dist
|
||||
|
||||
FROM mcr.microsoft.com/dotnet/core/sdk:${DOTNET_VERSION}-buster as builder
|
||||
@@ -48,9 +50,6 @@ RUN apt-get update \
|
||||
&& sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && locale-gen
|
||||
|
||||
ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1
|
||||
ENV LC_ALL en_US.UTF-8
|
||||
ENV LANG en_US.UTF-8
|
||||
ENV LANGUAGE en_US:en
|
||||
|
||||
EXPOSE 8096
|
||||
VOLUME /cache /config /media
|
||||
|
||||
@@ -6,11 +6,13 @@ ARG DOTNET_VERSION=3.1
|
||||
|
||||
|
||||
FROM node:alpine as web-builder
|
||||
ARG JELLYFIN_WEB_VERSION=master
|
||||
RUN apk add curl git zlib zlib-dev autoconf g++ make libpng-dev gifsicle alpine-sdk automake libtool make gcc musl-dev nasm python \
|
||||
&& curl -L https://github.com/jellyfin/jellyfin-web/archive/${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
|
||||
&& cd jellyfin-web-* \
|
||||
ARG JELLYFIN_WEB_VERSION=10.5.5
|
||||
RUN apk add curl git \
|
||||
&& git clone --branch release-10.5.z --single-branch https://github.com/jellyfin/jellyfin-web.git \
|
||||
&& cd jellyfin-web \
|
||||
&& git checkout tags/v${JELLYFIN_WEB_VERSION} \
|
||||
&& yarn install \
|
||||
&& yarn build \
|
||||
&& mv dist /dist
|
||||
|
||||
|
||||
@@ -52,22 +54,16 @@ RUN apt-get update \
|
||||
libraspberrypi0 \
|
||||
vainfo \
|
||||
libva2 \
|
||||
locales \
|
||||
&& apt-get remove curl gnupg -y \
|
||||
&& apt-get clean autoclean -y \
|
||||
&& apt-get autoremove -y \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& mkdir -p /cache /config /media \
|
||||
&& chmod 777 /cache /config /media \
|
||||
&& sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && locale-gen
|
||||
|
||||
&& chmod 777 /cache /config /media
|
||||
COPY --from=builder /jellyfin /jellyfin
|
||||
COPY --from=web-builder /dist /jellyfin/jellyfin-web
|
||||
|
||||
ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1
|
||||
ENV LC_ALL en_US.UTF-8
|
||||
ENV LANG en_US.UTF-8
|
||||
ENV LANGUAGE en_US:en
|
||||
|
||||
EXPOSE 8096
|
||||
VOLUME /cache /config /media
|
||||
|
||||
@@ -6,11 +6,13 @@ ARG DOTNET_VERSION=3.1
|
||||
|
||||
|
||||
FROM node:alpine as web-builder
|
||||
ARG JELLYFIN_WEB_VERSION=master
|
||||
RUN apk add curl git zlib zlib-dev autoconf g++ make libpng-dev gifsicle alpine-sdk automake libtool make gcc musl-dev nasm python \
|
||||
&& curl -L https://github.com/jellyfin/jellyfin-web/archive/${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
|
||||
&& cd jellyfin-web-* \
|
||||
ARG JELLYFIN_WEB_VERSION=10.5.5
|
||||
RUN apk add curl git \
|
||||
&& git clone --branch release-10.5.z --single-branch https://github.com/jellyfin/jellyfin-web.git \
|
||||
&& cd jellyfin-web \
|
||||
&& git checkout tags/v${JELLYFIN_WEB_VERSION} \
|
||||
&& yarn install \
|
||||
&& yarn build \
|
||||
&& mv dist /dist
|
||||
|
||||
|
||||
@@ -34,7 +36,7 @@ ARG APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE=DontWarn
|
||||
ENV NVIDIA_DRIVER_CAPABILITIES="compute,video,utility"
|
||||
|
||||
COPY --from=qemu /usr/bin/qemu-aarch64-static /usr/bin
|
||||
RUN apt-get update && apt-get install --no-install-recommends --no-install-suggests -y \
|
||||
RUN apt-get update && apt-get install --no-install-recommends --no-install-suggests -y \
|
||||
ffmpeg \
|
||||
libssl-dev \
|
||||
ca-certificates \
|
||||
@@ -42,21 +44,15 @@ RUN apt-get update && apt-get install --no-install-recommends --no-install-sugge
|
||||
libfreetype6 \
|
||||
libomxil-bellagio0 \
|
||||
libomxil-bellagio-bin \
|
||||
locales \
|
||||
&& apt-get clean autoclean -y \
|
||||
&& apt-get autoremove -y \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& mkdir -p /cache /config /media \
|
||||
&& chmod 777 /cache /config /media \
|
||||
&& sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && locale-gen
|
||||
|
||||
&& chmod 777 /cache /config /media
|
||||
COPY --from=builder /jellyfin /jellyfin
|
||||
COPY --from=web-builder /dist /jellyfin/jellyfin-web
|
||||
|
||||
ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1
|
||||
ENV LC_ALL en_US.UTF-8
|
||||
ENV LANG en_US.UTF-8
|
||||
ENV LANGUAGE en_US:en
|
||||
|
||||
EXPOSE 8096
|
||||
VOLUME /cache /config /media
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System.Buffers.Binary;
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace DvdLib
|
||||
@@ -14,12 +12,19 @@ namespace DvdLib
|
||||
|
||||
public override ushort ReadUInt16()
|
||||
{
|
||||
return BinaryPrimitives.ReadUInt16BigEndian(base.ReadBytes(2));
|
||||
return BitConverter.ToUInt16(ReadAndReverseBytes(2), 0);
|
||||
}
|
||||
|
||||
public override uint ReadUInt32()
|
||||
{
|
||||
return BinaryPrimitives.ReadUInt32BigEndian(base.ReadBytes(4));
|
||||
return BitConverter.ToUInt32(ReadAndReverseBytes(4), 0);
|
||||
}
|
||||
|
||||
private byte[] ReadAndReverseBytes(int count)
|
||||
{
|
||||
byte[] val = base.ReadBytes(count);
|
||||
Array.Reverse(val, 0, count);
|
||||
return val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,17 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<!-- ProjectGuid is only included as a requirement for SonarQube analysis -->
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>{713F42B5-878E-499D-A878-E4C652B1D5E8}</ProjectGuid>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="..\SharedVersion.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.1</TargetFramework>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System.IO;
|
||||
|
||||
namespace DvdLib.Ifo
|
||||
@@ -7,7 +5,6 @@ namespace DvdLib.Ifo
|
||||
public class Cell
|
||||
{
|
||||
public CellPlaybackInfo PlaybackInfo { get; private set; }
|
||||
|
||||
public CellPositionInfo PositionInfo { get; private set; }
|
||||
|
||||
internal void ParsePlayback(BinaryReader br)
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System.IO;
|
||||
|
||||
namespace DvdLib.Ifo
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System.IO;
|
||||
|
||||
namespace DvdLib.Ifo
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
namespace DvdLib.Ifo
|
||||
{
|
||||
public class Chapter
|
||||
{
|
||||
public ushort ProgramChainNumber { get; private set; }
|
||||
|
||||
public ushort ProgramNumber { get; private set; }
|
||||
|
||||
public uint ChapterNumber { get; private set; }
|
||||
|
||||
public Chapter(ushort pgcNum, ushort programNum, uint chapterNum)
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using MediaBrowser.Model.IO;
|
||||
|
||||
namespace DvdLib.Ifo
|
||||
{
|
||||
@@ -14,10 +13,13 @@ namespace DvdLib.Ifo
|
||||
|
||||
private ushort _titleCount;
|
||||
public readonly Dictionary<ushort, string> VTSPaths = new Dictionary<ushort, string>();
|
||||
public Dvd(string path)
|
||||
private readonly IFileSystem _fileSystem;
|
||||
|
||||
public Dvd(string path, IFileSystem fileSystem)
|
||||
{
|
||||
_fileSystem = fileSystem;
|
||||
Titles = new List<Title>();
|
||||
var allFiles = new DirectoryInfo(path).GetFiles(path, SearchOption.AllDirectories);
|
||||
var allFiles = _fileSystem.GetFiles(path, true).ToList();
|
||||
|
||||
var vmgPath = allFiles.FirstOrDefault(i => string.Equals(i.Name, "VIDEO_TS.IFO", StringComparison.OrdinalIgnoreCase)) ??
|
||||
allFiles.FirstOrDefault(i => string.Equals(i.Name, "VIDEO_TS.BUP", StringComparison.OrdinalIgnoreCase));
|
||||
@@ -31,7 +33,7 @@ namespace DvdLib.Ifo
|
||||
continue;
|
||||
}
|
||||
|
||||
var nums = ifo.Name.Split(new[] { '_' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
var nums = ifo.Name.Split(new [] { '_' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (nums.Length >= 2 && ushort.TryParse(nums[1], out var ifoNumber))
|
||||
{
|
||||
ReadVTS(ifoNumber, ifo.FullName);
|
||||
@@ -74,7 +76,7 @@ namespace DvdLib.Ifo
|
||||
}
|
||||
}
|
||||
|
||||
private void ReadVTS(ushort vtsNum, IReadOnlyList<FileInfo> allFiles)
|
||||
private void ReadVTS(ushort vtsNum, IEnumerable<FileSystemMetadata> allFiles)
|
||||
{
|
||||
var filename = string.Format("VTS_{0:00}_0.IFO", vtsNum);
|
||||
|
||||
@@ -117,19 +119,12 @@ namespace DvdLib.Ifo
|
||||
uint chapNum = 1;
|
||||
vtsFs.Seek(baseAddr + offsets[titleNum], SeekOrigin.Begin);
|
||||
var t = Titles.FirstOrDefault(vtst => vtst.IsVTSTitle(vtsNum, titleNum + 1));
|
||||
if (t == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (t == null) continue;
|
||||
|
||||
do
|
||||
{
|
||||
t.Chapters.Add(new Chapter(vtsRead.ReadUInt16(), vtsRead.ReadUInt16(), chapNum));
|
||||
if (titleNum + 1 < numTitles && vtsFs.Position == (baseAddr + offsets[titleNum + 1]))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (titleNum + 1 < numTitles && vtsFs.Position == (baseAddr + offsets[titleNum + 1])) break;
|
||||
chapNum++;
|
||||
}
|
||||
while (vtsFs.Position < (baseAddr + endaddr));
|
||||
@@ -154,10 +149,7 @@ namespace DvdLib.Ifo
|
||||
uint vtsPgcOffset = vtsRead.ReadUInt32();
|
||||
|
||||
var t = Titles.FirstOrDefault(vtst => vtst.IsVTSTitle(vtsNum, titleNum));
|
||||
if (t != null)
|
||||
{
|
||||
t.AddPgc(vtsRead, startByte + vtsPgcOffset, entryPgc, pgcNum);
|
||||
}
|
||||
if (t != null) t.AddPgc(vtsRead, startByte + vtsPgcOffset, entryPgc, pgcNum);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
|
||||
namespace DvdLib.Ifo
|
||||
@@ -15,14 +13,8 @@ namespace DvdLib.Ifo
|
||||
Second = GetBCDValue(data[2]);
|
||||
Frames = GetBCDValue((byte)(data[3] & 0x3F));
|
||||
|
||||
if ((data[3] & 0x80) != 0)
|
||||
{
|
||||
FrameRate = 30;
|
||||
}
|
||||
else if ((data[3] & 0x40) != 0)
|
||||
{
|
||||
FrameRate = 25;
|
||||
}
|
||||
if ((data[3] & 0x80) != 0) FrameRate = 30;
|
||||
else if ((data[3] & 0x40) != 0) FrameRate = 25;
|
||||
}
|
||||
|
||||
private static byte GetBCDValue(byte data)
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace DvdLib.Ifo
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
public IReadOnlyList<Cell> Cells { get; }
|
||||
public readonly List<Cell> Cells;
|
||||
|
||||
public Program(List<Cell> cells)
|
||||
{
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
@@ -22,9 +20,7 @@ namespace DvdLib.Ifo
|
||||
public readonly List<Cell> Cells;
|
||||
|
||||
public DvdTime PlaybackTime { get; private set; }
|
||||
|
||||
public UserOperation ProhibitedUserOperations { get; private set; }
|
||||
|
||||
public byte[] AudioStreamControl { get; private set; } // 8*2 entries
|
||||
public byte[] SubpictureStreamControl { get; private set; } // 32*4 entries
|
||||
|
||||
@@ -35,11 +31,9 @@ namespace DvdLib.Ifo
|
||||
private ushort _goupProgramNumber;
|
||||
|
||||
public ProgramPlaybackMode PlaybackMode { get; private set; }
|
||||
|
||||
public uint ProgramCount { get; private set; }
|
||||
|
||||
public byte StillTime { get; private set; }
|
||||
|
||||
public byte[] Palette { get; private set; } // 16*4 entries
|
||||
|
||||
private ushort _commandTableOffset;
|
||||
@@ -75,15 +69,8 @@ namespace DvdLib.Ifo
|
||||
|
||||
StillTime = br.ReadByte();
|
||||
byte pbMode = br.ReadByte();
|
||||
if (pbMode == 0)
|
||||
{
|
||||
PlaybackMode = ProgramPlaybackMode.Sequential;
|
||||
}
|
||||
else
|
||||
{
|
||||
PlaybackMode = ((pbMode & 0x80) == 0) ? ProgramPlaybackMode.Random : ProgramPlaybackMode.Shuffle;
|
||||
}
|
||||
|
||||
if (pbMode == 0) PlaybackMode = ProgramPlaybackMode.Sequential;
|
||||
else PlaybackMode = ((pbMode & 0x80) == 0) ? ProgramPlaybackMode.Random : ProgramPlaybackMode.Shuffle;
|
||||
ProgramCount = (uint)(pbMode & 0x7F);
|
||||
|
||||
Palette = br.ReadBytes(64);
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
@@ -8,11 +6,8 @@ namespace DvdLib.Ifo
|
||||
public class Title
|
||||
{
|
||||
public uint TitleNumber { get; private set; }
|
||||
|
||||
public uint AngleCount { get; private set; }
|
||||
|
||||
public ushort ChapterCount { get; private set; }
|
||||
|
||||
public byte VideoTitleSetNumber { get; private set; }
|
||||
|
||||
private ushort _parentalManagementMask;
|
||||
@@ -20,7 +15,6 @@ namespace DvdLib.Ifo
|
||||
private uint _vtsStartSector; // relative to start of entire disk
|
||||
|
||||
public ProgramChain EntryProgramChain { get; private set; }
|
||||
|
||||
public readonly List<ProgramChain> ProgramChains;
|
||||
|
||||
public readonly List<Chapter> Chapters;
|
||||
@@ -59,10 +53,7 @@ namespace DvdLib.Ifo
|
||||
var pgc = new ProgramChain(pgcNum);
|
||||
pgc.ParseHeader(br);
|
||||
ProgramChains.Add(pgc);
|
||||
if (entryPgc)
|
||||
{
|
||||
EntryProgramChain = pgc;
|
||||
}
|
||||
if (entryPgc) EntryProgramChain = pgc;
|
||||
|
||||
br.BaseStream.Seek(curPos, SeekOrigin.Begin);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
|
||||
namespace DvdLib.Ifo
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable SA1600
|
||||
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
@@ -152,7 +152,6 @@ namespace Emby.Dlna.Api
|
||||
return _resultFactory.GetStaticResult(Request, cacheKey, null, cacheLength, XMLContentType, () => Task.FromResult<Stream>(new MemoryStream(bytes)));
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
|
||||
public object Get(GetContentDirectory request)
|
||||
{
|
||||
var xml = ContentDirectory.GetServiceXml();
|
||||
@@ -160,7 +159,6 @@ namespace Emby.Dlna.Api
|
||||
return _resultFactory.GetResult(Request, xml, XMLContentType);
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
|
||||
public object Get(GetMediaReceiverRegistrar request)
|
||||
{
|
||||
var xml = MediaReceiverRegistrar.GetServiceXml();
|
||||
@@ -168,7 +166,6 @@ namespace Emby.Dlna.Api
|
||||
return _resultFactory.GetResult(Request, xml, XMLContentType);
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
|
||||
public object Get(GetConnnectionManager request)
|
||||
{
|
||||
var xml = ConnectionManager.GetServiceXml();
|
||||
@@ -317,37 +314,31 @@ namespace Emby.Dlna.Api
|
||||
return _resultFactory.GetStaticResult(Request, cacheKey, null, cacheLength, contentType, () => Task.FromResult(_dlnaManager.GetIcon(request.Filename).Stream));
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
|
||||
public object Subscribe(ProcessContentDirectoryEventRequest request)
|
||||
{
|
||||
return ProcessEventRequest(ContentDirectory);
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
|
||||
public object Subscribe(ProcessConnectionManagerEventRequest request)
|
||||
{
|
||||
return ProcessEventRequest(ConnectionManager);
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
|
||||
public object Subscribe(ProcessMediaReceiverRegistrarEventRequest request)
|
||||
{
|
||||
return ProcessEventRequest(MediaReceiverRegistrar);
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
|
||||
public object Unsubscribe(ProcessContentDirectoryEventRequest request)
|
||||
{
|
||||
return ProcessEventRequest(ContentDirectory);
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
|
||||
public object Unsubscribe(ProcessConnectionManagerEventRequest request)
|
||||
{
|
||||
return ProcessEventRequest(ConnectionManager);
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
|
||||
public object Unsubscribe(ProcessMediaReceiverRegistrarEventRequest request)
|
||||
{
|
||||
return ProcessEventRequest(MediaReceiverRegistrar);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable SA1600
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using MediaBrowser.Controller.Dlna;
|
||||
using MediaBrowser.Controller.Net;
|
||||
@@ -53,7 +53,6 @@ namespace Emby.Dlna.Api
|
||||
_dlnaManager = dlnaManager;
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
|
||||
public object Get(GetProfileInfos request)
|
||||
{
|
||||
return _dlnaManager.GetProfileInfos().ToArray();
|
||||
@@ -64,7 +63,6 @@ namespace Emby.Dlna.Api
|
||||
return _dlnaManager.GetProfile(request.Id);
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
|
||||
public object Get(GetDefaultProfile request)
|
||||
{
|
||||
return _dlnaManager.GetDefaultProfile();
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable SA1600
|
||||
|
||||
namespace Emby.Dlna.Common
|
||||
{
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable SA1600
|
||||
|
||||
using System.Globalization;
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable SA1600
|
||||
|
||||
namespace Emby.Dlna.Common
|
||||
{
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable SA1600
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable SA1600
|
||||
|
||||
using System;
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable SA1600
|
||||
|
||||
namespace Emby.Dlna.Configuration
|
||||
{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#nullable enable
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable SA1600
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Emby.Dlna.Configuration;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable SA1600
|
||||
|
||||
using System.Threading.Tasks;
|
||||
using Emby.Dlna.Service;
|
||||
@@ -15,11 +16,7 @@ namespace Emby.Dlna.ConnectionManager
|
||||
private readonly ILogger _logger;
|
||||
private readonly IServerConfigurationManager _config;
|
||||
|
||||
public ConnectionManager(
|
||||
IDlnaManager dlna,
|
||||
IServerConfigurationManager config,
|
||||
ILogger<ConnectionManager> logger,
|
||||
IHttpClient httpClient)
|
||||
public ConnectionManager(IDlnaManager dlna, IServerConfigurationManager config, ILogger logger, IHttpClient httpClient)
|
||||
: base(logger, httpClient)
|
||||
{
|
||||
_dlna = dlna;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable SA1600
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Emby.Dlna.Common;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable SA1600
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable SA1600
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Emby.Dlna.Common;
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable SA1600
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Emby.Dlna.Service;
|
||||
using Jellyfin.Data.Entities;
|
||||
using Jellyfin.Data.Enums;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Dlna;
|
||||
using MediaBrowser.Controller.Drawing;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.MediaEncoding;
|
||||
using MediaBrowser.Controller.TV;
|
||||
@@ -33,14 +32,13 @@ namespace Emby.Dlna.ContentDirectory
|
||||
private readonly IMediaEncoder _mediaEncoder;
|
||||
private readonly ITVSeriesManager _tvSeriesManager;
|
||||
|
||||
public ContentDirectory(
|
||||
IDlnaManager dlna,
|
||||
public ContentDirectory(IDlnaManager dlna,
|
||||
IUserDataManager userDataManager,
|
||||
IImageProcessor imageProcessor,
|
||||
ILibraryManager libraryManager,
|
||||
IServerConfigurationManager config,
|
||||
IUserManager userManager,
|
||||
ILogger<ContentDirectory> logger,
|
||||
ILogger logger,
|
||||
IHttpClient httpClient,
|
||||
ILocalizationManager localization,
|
||||
IMediaSourceManager mediaSourceManager,
|
||||
@@ -133,13 +131,18 @@ namespace Emby.Dlna.ContentDirectory
|
||||
|
||||
foreach (var user in _userManager.Users)
|
||||
{
|
||||
if (user.HasPermission(PermissionKind.IsAdministrator))
|
||||
if (user.Policy.IsAdministrator)
|
||||
{
|
||||
return user;
|
||||
}
|
||||
}
|
||||
|
||||
return _userManager.Users.FirstOrDefault();
|
||||
foreach (var user in _userManager.Users)
|
||||
{
|
||||
return user;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable SA1600
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Emby.Dlna.Common;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable SA1600
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -10,7 +11,6 @@ using System.Threading;
|
||||
using System.Xml;
|
||||
using Emby.Dlna.Didl;
|
||||
using Emby.Dlna.Service;
|
||||
using Jellyfin.Data.Entities;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Drawing;
|
||||
@@ -18,6 +18,7 @@ using MediaBrowser.Controller.Dto;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.Audio;
|
||||
using MediaBrowser.Controller.Entities.Movies;
|
||||
using MediaBrowser.Controller.Entities.TV;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.LiveTv;
|
||||
using MediaBrowser.Controller.MediaEncoding;
|
||||
@@ -28,12 +29,6 @@ using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.Globalization;
|
||||
using MediaBrowser.Model.Querying;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Book = MediaBrowser.Controller.Entities.Book;
|
||||
using Episode = MediaBrowser.Controller.Entities.TV.Episode;
|
||||
using Genre = MediaBrowser.Controller.Entities.Genre;
|
||||
using Movie = MediaBrowser.Controller.Entities.Movies.Movie;
|
||||
using MusicAlbum = MediaBrowser.Controller.Entities.Audio.MusicAlbum;
|
||||
using Series = MediaBrowser.Controller.Entities.TV.Series;
|
||||
|
||||
namespace Emby.Dlna.ContentDirectory
|
||||
{
|
||||
@@ -84,18 +79,7 @@ namespace Emby.Dlna.ContentDirectory
|
||||
_profile = profile;
|
||||
_config = config;
|
||||
|
||||
_didlBuilder = new DidlBuilder(
|
||||
profile,
|
||||
user,
|
||||
imageProcessor,
|
||||
serverAddress,
|
||||
accessToken,
|
||||
userDataManager,
|
||||
localization,
|
||||
mediaSourceManager,
|
||||
Logger,
|
||||
mediaEncoder,
|
||||
libraryManager);
|
||||
_didlBuilder = new DidlBuilder(profile, user, imageProcessor, serverAddress, accessToken, userDataManager, localization, mediaSourceManager, Logger, mediaEncoder);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -170,7 +154,7 @@ namespace Emby.Dlna.ContentDirectory
|
||||
{
|
||||
var id = sparams["ObjectID"];
|
||||
|
||||
var serverItem = GetItemFromObjectId(id);
|
||||
var serverItem = GetItemFromObjectId(id, _user);
|
||||
|
||||
var item = serverItem.Item;
|
||||
|
||||
@@ -293,7 +277,7 @@ namespace Emby.Dlna.ContentDirectory
|
||||
|
||||
DidlBuilder.WriteXmlRootAttributes(_profile, writer);
|
||||
|
||||
var serverItem = GetItemFromObjectId(id);
|
||||
var serverItem = GetItemFromObjectId(id, _user);
|
||||
var item = serverItem.Item;
|
||||
|
||||
|
||||
@@ -310,7 +294,7 @@ namespace Emby.Dlna.ContentDirectory
|
||||
else
|
||||
{
|
||||
var dlnaOptions = _config.GetDlnaConfiguration();
|
||||
_didlBuilder.WriteItemElement(writer, item, _user, null, null, deviceId, filter);
|
||||
_didlBuilder.WriteItemElement(dlnaOptions, writer, item, _user, null, null, deviceId, filter);
|
||||
}
|
||||
|
||||
provided++;
|
||||
@@ -337,7 +321,7 @@ namespace Emby.Dlna.ContentDirectory
|
||||
}
|
||||
else
|
||||
{
|
||||
_didlBuilder.WriteItemElement(writer, childItem, _user, item, serverItem.StubType, deviceId, filter);
|
||||
_didlBuilder.WriteItemElement(dlnaOptions, writer, childItem, _user, item, serverItem.StubType, deviceId, filter);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -404,7 +388,7 @@ namespace Emby.Dlna.ContentDirectory
|
||||
|
||||
DidlBuilder.WriteXmlRootAttributes(_profile, writer);
|
||||
|
||||
var serverItem = GetItemFromObjectId(sparams["ContainerID"]);
|
||||
var serverItem = GetItemFromObjectId(sparams["ContainerID"], _user);
|
||||
|
||||
var item = serverItem.Item;
|
||||
|
||||
@@ -423,7 +407,7 @@ namespace Emby.Dlna.ContentDirectory
|
||||
}
|
||||
else
|
||||
{
|
||||
_didlBuilder.WriteItemElement(writer, i, _user, item, serverItem.StubType, deviceId, filter);
|
||||
_didlBuilder.WriteItemElement(dlnaOptions, writer, i, _user, item, serverItem.StubType, deviceId, filter);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -466,12 +450,12 @@ namespace Emby.Dlna.ContentDirectory
|
||||
}
|
||||
else if (search.SearchType == SearchType.Playlist)
|
||||
{
|
||||
// items = items.OfType<Playlist>();
|
||||
//items = items.OfType<Playlist>();
|
||||
isFolder = true;
|
||||
}
|
||||
else if (search.SearchType == SearchType.MusicAlbum)
|
||||
{
|
||||
// items = items.OfType<MusicAlbum>();
|
||||
//items = items.OfType<MusicAlbum>();
|
||||
isFolder = true;
|
||||
}
|
||||
|
||||
@@ -529,11 +513,11 @@ namespace Emby.Dlna.ContentDirectory
|
||||
}
|
||||
else if (string.Equals(CollectionType.Folders, collectionFolder.CollectionType, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return GetFolders(user, startIndex, limit);
|
||||
return GetFolders(item, user, stubType, sort, startIndex, limit);
|
||||
}
|
||||
else if (string.Equals(CollectionType.LiveTv, collectionFolder.CollectionType, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return GetLiveTvChannels(user, sort, startIndex, limit);
|
||||
return GetLiveTvChannels(item, user, stubType, sort, startIndex, limit);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -564,7 +548,7 @@ namespace Emby.Dlna.ContentDirectory
|
||||
return ToResult(queryResult);
|
||||
}
|
||||
|
||||
private QueryResult<ServerItem> GetLiveTvChannels(User user, SortCriteria sort, int? startIndex, int? limit)
|
||||
private QueryResult<ServerItem> GetLiveTvChannels(BaseItem item, User user, StubType? stubType, SortCriteria sort, int? startIndex, int? limit)
|
||||
{
|
||||
var query = new InternalItemsQuery(user)
|
||||
{
|
||||
@@ -596,7 +580,7 @@ namespace Emby.Dlna.ContentDirectory
|
||||
|
||||
if (stubType.HasValue && stubType.Value == StubType.Playlists)
|
||||
{
|
||||
return GetMusicPlaylists(user, query);
|
||||
return GetMusicPlaylists(item, user, query);
|
||||
}
|
||||
|
||||
if (stubType.HasValue && stubType.Value == StubType.Albums)
|
||||
@@ -724,7 +708,7 @@ namespace Emby.Dlna.ContentDirectory
|
||||
|
||||
if (stubType.HasValue && stubType.Value == StubType.Collections)
|
||||
{
|
||||
return GetMovieCollections(user, query);
|
||||
return GetMovieCollections(item, user, query);
|
||||
}
|
||||
|
||||
if (stubType.HasValue && stubType.Value == StubType.Favorites)
|
||||
@@ -737,42 +721,46 @@ namespace Emby.Dlna.ContentDirectory
|
||||
return GetGenres(item, user, query);
|
||||
}
|
||||
|
||||
var array = new[]
|
||||
var list = new List<ServerItem>();
|
||||
|
||||
list.Add(new ServerItem(item)
|
||||
{
|
||||
new ServerItem(item)
|
||||
{
|
||||
StubType = StubType.ContinueWatching
|
||||
},
|
||||
new ServerItem(item)
|
||||
{
|
||||
StubType = StubType.Latest
|
||||
},
|
||||
new ServerItem(item)
|
||||
{
|
||||
StubType = StubType.Movies
|
||||
},
|
||||
new ServerItem(item)
|
||||
{
|
||||
StubType = StubType.Collections
|
||||
},
|
||||
new ServerItem(item)
|
||||
{
|
||||
StubType = StubType.Favorites
|
||||
},
|
||||
new ServerItem(item)
|
||||
{
|
||||
StubType = StubType.Genres
|
||||
}
|
||||
};
|
||||
StubType = StubType.ContinueWatching
|
||||
});
|
||||
|
||||
list.Add(new ServerItem(item)
|
||||
{
|
||||
StubType = StubType.Latest
|
||||
});
|
||||
|
||||
list.Add(new ServerItem(item)
|
||||
{
|
||||
StubType = StubType.Movies
|
||||
});
|
||||
|
||||
list.Add(new ServerItem(item)
|
||||
{
|
||||
StubType = StubType.Collections
|
||||
});
|
||||
|
||||
list.Add(new ServerItem(item)
|
||||
{
|
||||
StubType = StubType.Favorites
|
||||
});
|
||||
|
||||
list.Add(new ServerItem(item)
|
||||
{
|
||||
StubType = StubType.Genres
|
||||
});
|
||||
|
||||
return new QueryResult<ServerItem>
|
||||
{
|
||||
Items = array,
|
||||
TotalRecordCount = array.Length
|
||||
Items = list,
|
||||
TotalRecordCount = list.Count
|
||||
};
|
||||
}
|
||||
|
||||
private QueryResult<ServerItem> GetFolders(User user, int? startIndex, int? limit)
|
||||
private QueryResult<ServerItem> GetFolders(BaseItem item, User user, StubType? stubType, SortCriteria sort, int? startIndex, int? limit)
|
||||
{
|
||||
var folders = _libraryManager.GetUserRootFolder().GetChildren(user, true)
|
||||
.OrderBy(i => i.SortName)
|
||||
@@ -805,7 +793,7 @@ namespace Emby.Dlna.ContentDirectory
|
||||
|
||||
if (stubType.HasValue && stubType.Value == StubType.NextUp)
|
||||
{
|
||||
return GetNextUp(item, query);
|
||||
return GetNextUp(item, user, query);
|
||||
}
|
||||
|
||||
if (stubType.HasValue && stubType.Value == StubType.Latest)
|
||||
@@ -923,10 +911,10 @@ namespace Emby.Dlna.ContentDirectory
|
||||
return ToResult(result);
|
||||
}
|
||||
|
||||
private QueryResult<ServerItem> GetMovieCollections(User user, InternalItemsQuery query)
|
||||
private QueryResult<ServerItem> GetMovieCollections(BaseItem parent, User user, InternalItemsQuery query)
|
||||
{
|
||||
query.Recursive = true;
|
||||
// query.Parent = parent;
|
||||
//query.Parent = parent;
|
||||
query.SetUser(user);
|
||||
|
||||
query.IncludeItemTypes = new[] { typeof(BoxSet).Name };
|
||||
@@ -1118,10 +1106,10 @@ namespace Emby.Dlna.ContentDirectory
|
||||
return ToResult(result);
|
||||
}
|
||||
|
||||
private QueryResult<ServerItem> GetMusicPlaylists(User user, InternalItemsQuery query)
|
||||
private QueryResult<ServerItem> GetMusicPlaylists(BaseItem parent, User user, InternalItemsQuery query)
|
||||
{
|
||||
query.Parent = null;
|
||||
query.IncludeItemTypes = new[] { nameof(Playlist) };
|
||||
query.IncludeItemTypes = new[] { typeof(Playlist).Name };
|
||||
query.SetUser(user);
|
||||
query.Recursive = true;
|
||||
|
||||
@@ -1138,15 +1126,16 @@ namespace Emby.Dlna.ContentDirectory
|
||||
{
|
||||
UserId = user.Id,
|
||||
Limit = 50,
|
||||
IncludeItemTypes = new[] { nameof(Audio) },
|
||||
ParentId = parent?.Id ?? Guid.Empty,
|
||||
IncludeItemTypes = new[] { typeof(Audio).Name },
|
||||
ParentId = parent == null ? Guid.Empty : parent.Id,
|
||||
GroupItems = true
|
||||
|
||||
}, query.DtoOptions).Select(i => i.Item1 ?? i.Item2.FirstOrDefault()).Where(i => i != null).ToArray();
|
||||
|
||||
return ToResult(items);
|
||||
}
|
||||
|
||||
private QueryResult<ServerItem> GetNextUp(BaseItem parent, InternalItemsQuery query)
|
||||
private QueryResult<ServerItem> GetNextUp(BaseItem parent, User user, InternalItemsQuery query)
|
||||
{
|
||||
query.OrderBy = Array.Empty<(string, SortOrder)>();
|
||||
|
||||
@@ -1155,6 +1144,7 @@ namespace Emby.Dlna.ContentDirectory
|
||||
Limit = query.Limit,
|
||||
StartIndex = query.StartIndex,
|
||||
UserId = query.User.Id
|
||||
|
||||
}, new[] { parent }, query.DtoOptions);
|
||||
|
||||
return ToResult(result);
|
||||
@@ -1171,6 +1161,7 @@ namespace Emby.Dlna.ContentDirectory
|
||||
IncludeItemTypes = new[] { typeof(Episode).Name },
|
||||
ParentId = parent == null ? Guid.Empty : parent.Id,
|
||||
GroupItems = false
|
||||
|
||||
}, query.DtoOptions).Select(i => i.Item1 ?? i.Item2.FirstOrDefault()).Where(i => i != null).ToArray();
|
||||
|
||||
return ToResult(items);
|
||||
@@ -1180,14 +1171,14 @@ namespace Emby.Dlna.ContentDirectory
|
||||
{
|
||||
query.OrderBy = Array.Empty<(string, SortOrder)>();
|
||||
|
||||
var items = _userViewManager.GetLatestItems(
|
||||
new LatestItemsQuery
|
||||
var items = _userViewManager.GetLatestItems(new LatestItemsQuery
|
||||
{
|
||||
UserId = user.Id,
|
||||
Limit = 50,
|
||||
IncludeItemTypes = new[] { nameof(Movie) },
|
||||
ParentId = parent?.Id ?? Guid.Empty,
|
||||
IncludeItemTypes = new[] { typeof(Movie).Name },
|
||||
ParentId = parent == null ? Guid.Empty : parent.Id,
|
||||
GroupItems = true
|
||||
|
||||
}, query.DtoOptions).Select(i => i.Item1 ?? i.Item2.FirstOrDefault()).Where(i => i != null).ToArray();
|
||||
|
||||
return ToResult(items);
|
||||
@@ -1220,11 +1211,7 @@ namespace Emby.Dlna.ContentDirectory
|
||||
Recursive = true,
|
||||
ParentId = parentId,
|
||||
GenreIds = new[] { item.Id },
|
||||
IncludeItemTypes = new[]
|
||||
{
|
||||
nameof(Movie),
|
||||
nameof(Series)
|
||||
},
|
||||
IncludeItemTypes = new[] { typeof(Movie).Name, typeof(Series).Name },
|
||||
Limit = limit,
|
||||
StartIndex = startIndex,
|
||||
DtoOptions = GetDtoOptions()
|
||||
@@ -1303,15 +1290,15 @@ namespace Emby.Dlna.ContentDirectory
|
||||
return result;
|
||||
}
|
||||
|
||||
private ServerItem GetItemFromObjectId(string id)
|
||||
private ServerItem GetItemFromObjectId(string id, User user)
|
||||
{
|
||||
return DidlBuilder.IsIdRoot(id)
|
||||
|
||||
? new ServerItem(_libraryManager.GetUserRootFolder())
|
||||
: ParseItemId(id);
|
||||
: ParseItemId(id, user);
|
||||
}
|
||||
|
||||
private ServerItem ParseItemId(string id)
|
||||
private ServerItem ParseItemId(string id, User user)
|
||||
{
|
||||
StubType? stubType = null;
|
||||
|
||||
@@ -1357,7 +1344,6 @@ namespace Emby.Dlna.ContentDirectory
|
||||
internal class ServerItem
|
||||
{
|
||||
public BaseItem Item { get; set; }
|
||||
|
||||
public StubType? StubType { get; set; }
|
||||
|
||||
public ServerItem(BaseItem item)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable SA1600
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Emby.Dlna.Common;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable SA1600
|
||||
|
||||
using System.IO;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable SA1600
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable SA1600
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
@@ -6,13 +7,14 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
using Emby.Dlna.Configuration;
|
||||
using Emby.Dlna.ContentDirectory;
|
||||
using Jellyfin.Data.Entities;
|
||||
using MediaBrowser.Controller.Channels;
|
||||
using MediaBrowser.Controller.Drawing;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.Audio;
|
||||
using MediaBrowser.Controller.Entities.Movies;
|
||||
using MediaBrowser.Controller.Entities.TV;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.MediaEncoding;
|
||||
using MediaBrowser.Controller.Playlists;
|
||||
@@ -22,13 +24,6 @@ using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.Globalization;
|
||||
using MediaBrowser.Model.Net;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Episode = MediaBrowser.Controller.Entities.TV.Episode;
|
||||
using Genre = MediaBrowser.Controller.Entities.Genre;
|
||||
using Movie = MediaBrowser.Controller.Entities.Movies.Movie;
|
||||
using MusicAlbum = MediaBrowser.Controller.Entities.Audio.MusicAlbum;
|
||||
using Season = MediaBrowser.Controller.Entities.TV.Season;
|
||||
using Series = MediaBrowser.Controller.Entities.TV.Series;
|
||||
using XmlAttribute = MediaBrowser.Model.Dlna.XmlAttribute;
|
||||
|
||||
namespace Emby.Dlna.Didl
|
||||
{
|
||||
@@ -51,7 +46,6 @@ namespace Emby.Dlna.Didl
|
||||
private readonly IMediaSourceManager _mediaSourceManager;
|
||||
private readonly ILogger _logger;
|
||||
private readonly IMediaEncoder _mediaEncoder;
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
|
||||
public DidlBuilder(
|
||||
DeviceProfile profile,
|
||||
@@ -63,8 +57,7 @@ namespace Emby.Dlna.Didl
|
||||
ILocalizationManager localization,
|
||||
IMediaSourceManager mediaSourceManager,
|
||||
ILogger logger,
|
||||
IMediaEncoder mediaEncoder,
|
||||
ILibraryManager libraryManager)
|
||||
IMediaEncoder mediaEncoder)
|
||||
{
|
||||
_profile = profile;
|
||||
_user = user;
|
||||
@@ -76,7 +69,6 @@ namespace Emby.Dlna.Didl
|
||||
_mediaSourceManager = mediaSourceManager;
|
||||
_logger = logger;
|
||||
_mediaEncoder = mediaEncoder;
|
||||
_libraryManager = libraryManager;
|
||||
}
|
||||
|
||||
public static string NormalizeDlnaMediaUrl(string url)
|
||||
@@ -84,7 +76,7 @@ namespace Emby.Dlna.Didl
|
||||
return url + "&dlnaheaders=true";
|
||||
}
|
||||
|
||||
public string GetItemDidl(BaseItem item, User user, BaseItem context, string deviceId, Filter filter, StreamInfo streamInfo)
|
||||
public string GetItemDidl(DlnaOptions options, BaseItem item, User user, BaseItem context, string deviceId, Filter filter, StreamInfo streamInfo)
|
||||
{
|
||||
var settings = new XmlWriterSettings
|
||||
{
|
||||
@@ -98,21 +90,21 @@ namespace Emby.Dlna.Didl
|
||||
{
|
||||
using (var writer = XmlWriter.Create(builder, settings))
|
||||
{
|
||||
// writer.WriteStartDocument();
|
||||
//writer.WriteStartDocument();
|
||||
|
||||
writer.WriteStartElement(string.Empty, "DIDL-Lite", NS_DIDL);
|
||||
|
||||
writer.WriteAttributeString("xmlns", "dc", null, NS_DC);
|
||||
writer.WriteAttributeString("xmlns", "dlna", null, NS_DLNA);
|
||||
writer.WriteAttributeString("xmlns", "upnp", null, NS_UPNP);
|
||||
// didl.SetAttribute("xmlns:sec", NS_SEC);
|
||||
//didl.SetAttribute("xmlns:sec", NS_SEC);
|
||||
|
||||
WriteXmlRootAttributes(_profile, writer);
|
||||
|
||||
WriteItemElement(writer, item, user, context, null, deviceId, filter, streamInfo);
|
||||
WriteItemElement(options, writer, item, user, context, null, deviceId, filter, streamInfo);
|
||||
|
||||
writer.WriteFullEndElement();
|
||||
// writer.WriteEndDocument();
|
||||
//writer.WriteEndDocument();
|
||||
}
|
||||
|
||||
return builder.ToString();
|
||||
@@ -136,6 +128,7 @@ namespace Emby.Dlna.Didl
|
||||
}
|
||||
|
||||
public void WriteItemElement(
|
||||
DlnaOptions options,
|
||||
XmlWriter writer,
|
||||
BaseItem item,
|
||||
User user,
|
||||
@@ -172,23 +165,25 @@ namespace Emby.Dlna.Didl
|
||||
// refID?
|
||||
// storeAttribute(itemNode, object, ClassProperties.REF_ID, false);
|
||||
|
||||
if (item is IHasMediaSources)
|
||||
var hasMediaSources = item as IHasMediaSources;
|
||||
|
||||
if (hasMediaSources != null)
|
||||
{
|
||||
if (string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
AddAudioResource(writer, item, deviceId, filter, streamInfo);
|
||||
AddAudioResource(options, writer, item, deviceId, filter, streamInfo);
|
||||
}
|
||||
else if (string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
AddVideoResource(writer, item, deviceId, filter, streamInfo);
|
||||
AddVideoResource(options, writer, item, deviceId, filter, streamInfo);
|
||||
}
|
||||
}
|
||||
|
||||
AddCover(item, null, writer);
|
||||
AddCover(item, context, null, writer);
|
||||
writer.WriteFullEndElement();
|
||||
}
|
||||
|
||||
private void AddVideoResource(XmlWriter writer, BaseItem video, string deviceId, Filter filter, StreamInfo streamInfo = null)
|
||||
private void AddVideoResource(DlnaOptions options, XmlWriter writer, BaseItem video, string deviceId, Filter filter, StreamInfo streamInfo = null)
|
||||
{
|
||||
if (streamInfo == null)
|
||||
{
|
||||
@@ -232,7 +227,7 @@ namespace Emby.Dlna.Didl
|
||||
|
||||
foreach (var contentFeature in contentFeatureList)
|
||||
{
|
||||
AddVideoResource(writer, filter, contentFeature, streamInfo);
|
||||
AddVideoResource(writer, video, deviceId, filter, contentFeature, streamInfo);
|
||||
}
|
||||
|
||||
var subtitleProfiles = streamInfo.GetSubtitleProfiles(_mediaEncoder, false, _serverAddress, _accessToken);
|
||||
@@ -289,10 +284,7 @@ namespace Emby.Dlna.Didl
|
||||
else
|
||||
{
|
||||
writer.WriteStartElement(string.Empty, "res", NS_DIDL);
|
||||
var protocolInfo = string.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
"http-get:*:text/{0}:*",
|
||||
info.Format.ToLowerInvariant());
|
||||
var protocolInfo = string.Format("http-get:*:text/{0}:*", info.Format.ToLowerInvariant());
|
||||
writer.WriteAttributeString("protocolInfo", protocolInfo);
|
||||
|
||||
writer.WriteString(info.Url);
|
||||
@@ -302,7 +294,7 @@ namespace Emby.Dlna.Didl
|
||||
return true;
|
||||
}
|
||||
|
||||
private void AddVideoResource(XmlWriter writer, Filter filter, string contentFeatures, StreamInfo streamInfo)
|
||||
private void AddVideoResource(XmlWriter writer, BaseItem video, string deviceId, Filter filter, string contentFeatures, StreamInfo streamInfo)
|
||||
{
|
||||
writer.WriteStartElement(string.Empty, "res", NS_DIDL);
|
||||
|
||||
@@ -344,13 +336,7 @@ namespace Emby.Dlna.Didl
|
||||
{
|
||||
if (targetWidth.HasValue && targetHeight.HasValue)
|
||||
{
|
||||
writer.WriteAttributeString(
|
||||
"resolution",
|
||||
string.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
"{0}x{1}",
|
||||
targetWidth.Value,
|
||||
targetHeight.Value));
|
||||
writer.WriteAttributeString("resolution", string.Format("{0}x{1}", targetWidth.Value, targetHeight.Value));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -384,19 +370,17 @@ namespace Emby.Dlna.Didl
|
||||
streamInfo.TargetVideoCodecTag,
|
||||
streamInfo.IsTargetAVC);
|
||||
|
||||
var filename = url.Substring(0, url.IndexOf('?', StringComparison.Ordinal));
|
||||
var filename = url.Substring(0, url.IndexOf('?'));
|
||||
|
||||
var mimeType = mediaProfile == null || string.IsNullOrEmpty(mediaProfile.MimeType)
|
||||
? MimeTypes.GetMimeType(filename)
|
||||
: mediaProfile.MimeType;
|
||||
|
||||
writer.WriteAttributeString(
|
||||
"protocolInfo",
|
||||
string.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
"http-get:*:{0}:{1}",
|
||||
mimeType,
|
||||
contentFeatures));
|
||||
writer.WriteAttributeString("protocolInfo", string.Format(
|
||||
"http-get:*:{0}:{1}",
|
||||
mimeType,
|
||||
contentFeatures
|
||||
));
|
||||
|
||||
writer.WriteString(url);
|
||||
|
||||
@@ -409,24 +393,25 @@ namespace Emby.Dlna.Didl
|
||||
{
|
||||
switch (itemStubType.Value)
|
||||
{
|
||||
case StubType.Latest: return _localization.GetLocalizedString("Latest");
|
||||
case StubType.Playlists: return _localization.GetLocalizedString("Playlists");
|
||||
case StubType.AlbumArtists: return _localization.GetLocalizedString("HeaderAlbumArtists");
|
||||
case StubType.Albums: return _localization.GetLocalizedString("Albums");
|
||||
case StubType.Artists: return _localization.GetLocalizedString("Artists");
|
||||
case StubType.Songs: return _localization.GetLocalizedString("Songs");
|
||||
case StubType.Genres: return _localization.GetLocalizedString("Genres");
|
||||
case StubType.FavoriteAlbums: return _localization.GetLocalizedString("HeaderFavoriteAlbums");
|
||||
case StubType.FavoriteArtists: return _localization.GetLocalizedString("HeaderFavoriteArtists");
|
||||
case StubType.FavoriteSongs: return _localization.GetLocalizedString("HeaderFavoriteSongs");
|
||||
case StubType.Latest: return _localization.GetLocalizedString("Latest");
|
||||
case StubType.Playlists: return _localization.GetLocalizedString("Playlists");
|
||||
case StubType.AlbumArtists: return _localization.GetLocalizedString("HeaderAlbumArtists");
|
||||
case StubType.Albums: return _localization.GetLocalizedString("Albums");
|
||||
case StubType.Artists: return _localization.GetLocalizedString("Artists");
|
||||
case StubType.Songs: return _localization.GetLocalizedString("Songs");
|
||||
case StubType.Genres: return _localization.GetLocalizedString("Genres");
|
||||
case StubType.FavoriteAlbums: return _localization.GetLocalizedString("HeaderFavoriteAlbums");
|
||||
case StubType.FavoriteArtists: return _localization.GetLocalizedString("HeaderFavoriteArtists");
|
||||
case StubType.FavoriteSongs: return _localization.GetLocalizedString("HeaderFavoriteSongs");
|
||||
case StubType.ContinueWatching: return _localization.GetLocalizedString("HeaderContinueWatching");
|
||||
case StubType.Movies: return _localization.GetLocalizedString("Movies");
|
||||
case StubType.Collections: return _localization.GetLocalizedString("Collections");
|
||||
case StubType.Favorites: return _localization.GetLocalizedString("Favorites");
|
||||
case StubType.NextUp: return _localization.GetLocalizedString("HeaderNextUp");
|
||||
case StubType.FavoriteSeries: return _localization.GetLocalizedString("HeaderFavoriteShows");
|
||||
case StubType.Movies: return _localization.GetLocalizedString("Movies");
|
||||
case StubType.Collections: return _localization.GetLocalizedString("Collections");
|
||||
case StubType.Favorites: return _localization.GetLocalizedString("Favorites");
|
||||
case StubType.NextUp: return _localization.GetLocalizedString("HeaderNextUp");
|
||||
case StubType.FavoriteSeries: return _localization.GetLocalizedString("HeaderFavoriteShows");
|
||||
case StubType.FavoriteEpisodes: return _localization.GetLocalizedString("HeaderFavoriteEpisodes");
|
||||
case StubType.Series: return _localization.GetLocalizedString("Shows");
|
||||
case StubType.Series: return _localization.GetLocalizedString("Shows");
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -523,7 +508,7 @@ namespace Emby.Dlna.Didl
|
||||
|
||||
private bool NotNullOrWhiteSpace(string s) => !string.IsNullOrWhiteSpace(s);
|
||||
|
||||
private void AddAudioResource(XmlWriter writer, BaseItem audio, string deviceId, Filter filter, StreamInfo streamInfo = null)
|
||||
private void AddAudioResource(DlnaOptions options, XmlWriter writer, BaseItem audio, string deviceId, Filter filter, StreamInfo streamInfo = null)
|
||||
{
|
||||
writer.WriteStartElement(string.Empty, "res", NS_DIDL);
|
||||
|
||||
@@ -589,7 +574,7 @@ namespace Emby.Dlna.Didl
|
||||
targetSampleRate,
|
||||
targetAudioBitDepth);
|
||||
|
||||
var filename = url.Substring(0, url.IndexOf('?', StringComparison.Ordinal));
|
||||
var filename = url.Substring(0, url.IndexOf('?'));
|
||||
|
||||
var mimeType = mediaProfile == null || string.IsNullOrEmpty(mediaProfile.MimeType)
|
||||
? MimeTypes.GetMimeType(filename)
|
||||
@@ -605,13 +590,11 @@ namespace Emby.Dlna.Didl
|
||||
streamInfo.RunTimeTicks ?? 0,
|
||||
streamInfo.TranscodeSeekInfo);
|
||||
|
||||
writer.WriteAttributeString(
|
||||
"protocolInfo",
|
||||
string.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
"http-get:*:{0}:{1}",
|
||||
mimeType,
|
||||
contentFeatures));
|
||||
writer.WriteAttributeString("protocolInfo", string.Format(
|
||||
"http-get:*:{0}:{1}",
|
||||
mimeType,
|
||||
contentFeatures
|
||||
));
|
||||
|
||||
writer.WriteString(url);
|
||||
|
||||
@@ -634,7 +617,7 @@ namespace Emby.Dlna.Didl
|
||||
|
||||
var clientId = GetClientId(folder, stubType);
|
||||
|
||||
if (string.Equals(requestedId, "0", StringComparison.Ordinal))
|
||||
if (string.Equals(requestedId, "0"))
|
||||
{
|
||||
writer.WriteAttributeString("id", "0");
|
||||
writer.WriteAttributeString("parentID", "-1");
|
||||
@@ -663,7 +646,7 @@ namespace Emby.Dlna.Didl
|
||||
|
||||
AddGeneralProperties(folder, stubType, context, writer, filter);
|
||||
|
||||
AddCover(folder, stubType, writer);
|
||||
AddCover(folder, context, stubType, writer);
|
||||
|
||||
writer.WriteFullEndElement();
|
||||
}
|
||||
@@ -675,7 +658,7 @@ namespace Emby.Dlna.Didl
|
||||
return;
|
||||
}
|
||||
|
||||
XmlAttribute secAttribute = null;
|
||||
MediaBrowser.Model.Dlna.XmlAttribute secAttribute = null;
|
||||
foreach (var attribute in _profile.XmlRootAttributes)
|
||||
{
|
||||
if (string.Equals(attribute.Name, "xmlns:sec", StringComparison.OrdinalIgnoreCase))
|
||||
@@ -696,22 +679,19 @@ namespace Emby.Dlna.Didl
|
||||
|
||||
if (playbackPositionTicks > 0)
|
||||
{
|
||||
var elementValue = string.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
"BM={0}",
|
||||
Convert.ToInt32(TimeSpan.FromTicks(playbackPositionTicks).TotalSeconds));
|
||||
var elementValue = string.Format("BM={0}", Convert.ToInt32(TimeSpan.FromTicks(playbackPositionTicks).TotalSeconds).ToString(_usCulture));
|
||||
AddValue(writer, "sec", "dcmInfo", elementValue, secAttribute.Value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds fields used by both items and folders.
|
||||
/// Adds fields used by both items and folders
|
||||
/// </summary>
|
||||
private void AddCommonFields(BaseItem item, StubType? itemStubType, BaseItem context, XmlWriter writer, Filter filter)
|
||||
{
|
||||
// Don't filter on dc:title because not all devices will include it in the filter
|
||||
// MediaMonkey for example won't display content without a title
|
||||
// if (filter.Contains("dc:title"))
|
||||
//if (filter.Contains("dc:title"))
|
||||
{
|
||||
AddValue(writer, "dc", "title", GetDisplayName(item, itemStubType, context), NS_DC);
|
||||
}
|
||||
@@ -750,7 +730,7 @@ namespace Emby.Dlna.Didl
|
||||
AddValue(writer, "dc", "description", desc, NS_DC);
|
||||
}
|
||||
}
|
||||
// if (filter.Contains("upnp:longDescription"))
|
||||
//if (filter.Contains("upnp:longDescription"))
|
||||
//{
|
||||
// if (!string.IsNullOrWhiteSpace(item.Overview))
|
||||
// {
|
||||
@@ -765,7 +745,6 @@ namespace Emby.Dlna.Didl
|
||||
{
|
||||
AddValue(writer, "dc", "rating", item.OfficialRating, NS_DC);
|
||||
}
|
||||
|
||||
if (filter.Contains("upnp:rating"))
|
||||
{
|
||||
AddValue(writer, "upnp", "rating", item.OfficialRating, NS_UPNP);
|
||||
@@ -853,36 +832,37 @@ namespace Emby.Dlna.Didl
|
||||
|
||||
private void AddPeople(BaseItem item, XmlWriter writer)
|
||||
{
|
||||
if (!item.SupportsPeople)
|
||||
{
|
||||
return;
|
||||
}
|
||||
//var types = new[]
|
||||
//{
|
||||
// PersonType.Director,
|
||||
// PersonType.Writer,
|
||||
// PersonType.Producer,
|
||||
// PersonType.Composer,
|
||||
// "Creator"
|
||||
//};
|
||||
|
||||
var types = new[]
|
||||
{
|
||||
PersonType.Director,
|
||||
PersonType.Writer,
|
||||
PersonType.Producer,
|
||||
PersonType.Composer,
|
||||
"creator"
|
||||
};
|
||||
//var people = _libraryManager.GetPeople(item);
|
||||
|
||||
// Seeing some LG models locking up due content with large lists of people
|
||||
// The actual issue might just be due to processing a more metadata than it can handle
|
||||
var people = _libraryManager.GetPeople(
|
||||
new InternalPeopleQuery
|
||||
{
|
||||
ItemId = item.Id,
|
||||
Limit = 6
|
||||
});
|
||||
//var index = 0;
|
||||
|
||||
foreach (var actor in people)
|
||||
{
|
||||
var type = types.FirstOrDefault(i => string.Equals(i, actor.Type, StringComparison.OrdinalIgnoreCase) || string.Equals(i, actor.Role, StringComparison.OrdinalIgnoreCase))
|
||||
?? PersonType.Actor;
|
||||
//// Seeing some LG models locking up due content with large lists of people
|
||||
//// The actual issue might just be due to processing a more metadata than it can handle
|
||||
//var limit = 6;
|
||||
|
||||
AddValue(writer, "upnp", type.ToLowerInvariant(), actor.Name, NS_UPNP);
|
||||
}
|
||||
//foreach (var actor in people)
|
||||
//{
|
||||
// var type = types.FirstOrDefault(i => string.Equals(i, actor.Type, StringComparison.OrdinalIgnoreCase) || string.Equals(i, actor.Role, StringComparison.OrdinalIgnoreCase))
|
||||
// ?? PersonType.Actor;
|
||||
|
||||
// AddValue(writer, "upnp", type.ToLowerInvariant(), actor.Name, NS_UPNP);
|
||||
|
||||
// index++;
|
||||
|
||||
// if (index >= limit)
|
||||
// {
|
||||
// break;
|
||||
// }
|
||||
//}
|
||||
}
|
||||
|
||||
private void AddGeneralProperties(BaseItem item, StubType? itemStubType, BaseItem context, XmlWriter writer, Filter filter)
|
||||
@@ -959,7 +939,7 @@ namespace Emby.Dlna.Didl
|
||||
}
|
||||
}
|
||||
|
||||
private void AddCover(BaseItem item, StubType? stubType, XmlWriter writer)
|
||||
private void AddCover(BaseItem item, BaseItem context, StubType? stubType, XmlWriter writer)
|
||||
{
|
||||
ImageDownloadInfo imageInfo = GetImageInfo(item);
|
||||
|
||||
@@ -1001,10 +981,20 @@ namespace Emby.Dlna.Didl
|
||||
}
|
||||
|
||||
AddImageResElement(item, writer, 160, 160, "jpg", "JPEG_TN");
|
||||
|
||||
}
|
||||
|
||||
private void AddImageResElement(
|
||||
BaseItem item,
|
||||
private void AddEmbeddedImageAsCover(string name, XmlWriter writer)
|
||||
{
|
||||
writer.WriteStartElement("upnp", "albumArtURI", NS_UPNP);
|
||||
writer.WriteAttributeString("dlna", "profileID", NS_DLNA, _profile.AlbumArtPn);
|
||||
writer.WriteString(_serverAddress + "/Dlna/icons/people480.jpg");
|
||||
writer.WriteFullEndElement();
|
||||
|
||||
writer.WriteElementString("upnp", "icon", NS_UPNP, _serverAddress + "/Dlna/icons/people48.jpg");
|
||||
}
|
||||
|
||||
private void AddImageResElement(BaseItem item,
|
||||
XmlWriter writer,
|
||||
int maxWidth,
|
||||
int maxHeight,
|
||||
@@ -1030,17 +1020,13 @@ namespace Emby.Dlna.Didl
|
||||
var contentFeatures = new ContentFeatureBuilder(_profile)
|
||||
.BuildImageHeader(format, width, height, imageInfo.IsDirectStream, org_Pn);
|
||||
|
||||
writer.WriteAttributeString(
|
||||
"protocolInfo",
|
||||
string.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
"http-get:*:{0}:{1}",
|
||||
MimeTypes.GetMimeType("file." + format),
|
||||
contentFeatures));
|
||||
writer.WriteAttributeString("protocolInfo", string.Format(
|
||||
"http-get:*:{0}:{1}",
|
||||
MimeTypes.GetMimeType("file." + format),
|
||||
contentFeatures
|
||||
));
|
||||
|
||||
writer.WriteAttributeString(
|
||||
"resolution",
|
||||
string.Format(CultureInfo.InvariantCulture, "{0}x{1}", width, height));
|
||||
writer.WriteAttributeString("resolution", string.Format("{0}x{1}", width, height));
|
||||
|
||||
writer.WriteString(albumartUrlInfo.Url);
|
||||
|
||||
@@ -1053,12 +1039,10 @@ namespace Emby.Dlna.Didl
|
||||
{
|
||||
return GetImageInfo(item, ImageType.Primary);
|
||||
}
|
||||
|
||||
if (item.HasImage(ImageType.Thumb))
|
||||
{
|
||||
return GetImageInfo(item, ImageType.Thumb);
|
||||
}
|
||||
|
||||
if (item.HasImage(ImageType.Backdrop))
|
||||
{
|
||||
if (item is Channel)
|
||||
@@ -1138,24 +1122,25 @@ namespace Emby.Dlna.Didl
|
||||
|
||||
if (width == 0 || height == 0)
|
||||
{
|
||||
// _imageProcessor.GetImageSize(item, imageInfo);
|
||||
//_imageProcessor.GetImageSize(item, imageInfo);
|
||||
width = null;
|
||||
height = null;
|
||||
}
|
||||
|
||||
else if (width == -1 || height == -1)
|
||||
{
|
||||
width = null;
|
||||
height = null;
|
||||
}
|
||||
|
||||
// try
|
||||
//try
|
||||
//{
|
||||
// var size = _imageProcessor.GetImageSize(imageInfo);
|
||||
|
||||
// width = size.Width;
|
||||
// height = size.Height;
|
||||
//}
|
||||
// catch
|
||||
//catch
|
||||
//{
|
||||
|
||||
//}
|
||||
@@ -1219,9 +1204,7 @@ namespace Emby.Dlna.Didl
|
||||
|
||||
private ImageUrlInfo GetImageUrl(ImageDownloadInfo info, int maxWidth, int maxHeight, string format)
|
||||
{
|
||||
var url = string.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
"{0}/Items/{1}/Images/{2}/0/{3}/{4}/{5}/{6}/0/0",
|
||||
var url = string.Format("{0}/Items/{1}/Images/{2}/0/{3}/{4}/{5}/{6}/0/0",
|
||||
_serverAddress,
|
||||
info.ItemId.ToString("N", CultureInfo.InvariantCulture),
|
||||
info.Type,
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable SA1600
|
||||
|
||||
using System;
|
||||
using MediaBrowser.Model.Extensions;
|
||||
|
||||
namespace Emby.Dlna.Didl
|
||||
{
|
||||
@@ -12,6 +14,7 @@ namespace Emby.Dlna.Didl
|
||||
public Filter()
|
||||
: this("*")
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public Filter(string filter)
|
||||
@@ -25,7 +28,7 @@ namespace Emby.Dlna.Didl
|
||||
{
|
||||
// Don't bother with this. Some clients (media monkey) use the filter and then don't display very well when very little data comes back.
|
||||
return true;
|
||||
// return _all || ListHelper.ContainsIgnoreCase(_fields, field);
|
||||
//return _all || ListHelper.ContainsIgnoreCase(_fields, field);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable SA1600
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
@@ -53,6 +54,6 @@ namespace Emby.Dlna.Didl
|
||||
_encoding = encoding;
|
||||
}
|
||||
|
||||
public override Encoding Encoding => _encoding ?? base.Encoding;
|
||||
public override Encoding Encoding => (null == _encoding) ? base.Encoding : _encoding;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable SA1600
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -31,7 +32,7 @@ namespace Emby.Dlna
|
||||
private readonly IApplicationPaths _appPaths;
|
||||
private readonly IXmlSerializer _xmlSerializer;
|
||||
private readonly IFileSystem _fileSystem;
|
||||
private readonly ILogger<DlnaManager> _logger;
|
||||
private readonly ILogger _logger;
|
||||
private readonly IJsonSerializer _jsonSerializer;
|
||||
private readonly IServerApplicationHost _appHost;
|
||||
private static readonly Assembly _assembly = typeof(DlnaManager).Assembly;
|
||||
@@ -49,7 +50,7 @@ namespace Emby.Dlna
|
||||
_xmlSerializer = xmlSerializer;
|
||||
_fileSystem = fileSystem;
|
||||
_appPaths = appPaths;
|
||||
_logger = loggerFactory.CreateLogger<DlnaManager>();
|
||||
_logger = loggerFactory.CreateLogger("Dlna");
|
||||
_jsonSerializer = jsonSerializer;
|
||||
_appHost = appHost;
|
||||
}
|
||||
@@ -88,6 +89,7 @@ namespace Emby.Dlna
|
||||
.Select(i => i.Item2)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public DeviceProfile GetDefaultProfile()
|
||||
@@ -140,73 +142,55 @@ namespace Emby.Dlna
|
||||
if (!string.IsNullOrEmpty(profileInfo.DeviceDescription))
|
||||
{
|
||||
if (deviceInfo.DeviceDescription == null || !IsRegexMatch(deviceInfo.DeviceDescription, profileInfo.DeviceDescription))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(profileInfo.FriendlyName))
|
||||
{
|
||||
if (deviceInfo.FriendlyName == null || !IsRegexMatch(deviceInfo.FriendlyName, profileInfo.FriendlyName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(profileInfo.Manufacturer))
|
||||
{
|
||||
if (deviceInfo.Manufacturer == null || !IsRegexMatch(deviceInfo.Manufacturer, profileInfo.Manufacturer))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(profileInfo.ManufacturerUrl))
|
||||
{
|
||||
if (deviceInfo.ManufacturerUrl == null || !IsRegexMatch(deviceInfo.ManufacturerUrl, profileInfo.ManufacturerUrl))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(profileInfo.ModelDescription))
|
||||
{
|
||||
if (deviceInfo.ModelDescription == null || !IsRegexMatch(deviceInfo.ModelDescription, profileInfo.ModelDescription))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(profileInfo.ModelName))
|
||||
{
|
||||
if (deviceInfo.ModelName == null || !IsRegexMatch(deviceInfo.ModelName, profileInfo.ModelName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(profileInfo.ModelNumber))
|
||||
{
|
||||
if (deviceInfo.ModelNumber == null || !IsRegexMatch(deviceInfo.ModelNumber, profileInfo.ModelNumber))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(profileInfo.ModelUrl))
|
||||
{
|
||||
if (deviceInfo.ModelUrl == null || !IsRegexMatch(deviceInfo.ModelUrl, profileInfo.ModelUrl))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(profileInfo.SerialNumber))
|
||||
{
|
||||
if (deviceInfo.SerialNumber == null || !IsRegexMatch(deviceInfo.SerialNumber, profileInfo.SerialNumber))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -268,7 +252,7 @@ namespace Emby.Dlna
|
||||
return string.Equals(value, header.Value, StringComparison.OrdinalIgnoreCase);
|
||||
case HeaderMatchType.Substring:
|
||||
var isMatch = value.ToString().IndexOf(header.Value, StringComparison.OrdinalIgnoreCase) != -1;
|
||||
// _logger.LogDebug("IsMatch-Substring value: {0} testValue: {1} isMatch: {2}", value, header.Value, isMatch);
|
||||
//_logger.LogDebug("IsMatch-Substring value: {0} testValue: {1} isMatch: {2}", value, header.Value, isMatch);
|
||||
return isMatch;
|
||||
case HeaderMatchType.Regex:
|
||||
return Regex.IsMatch(value, header.Value, RegexOptions.IgnoreCase);
|
||||
@@ -456,7 +440,6 @@ namespace Emby.Dlna
|
||||
{
|
||||
throw new ArgumentException("Profile is missing Id");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(profile.Name))
|
||||
{
|
||||
throw new ArgumentException("Profile is missing Name");
|
||||
@@ -482,7 +465,6 @@ namespace Emby.Dlna
|
||||
{
|
||||
_profiles[path] = new Tuple<InternalProfileInfo, DeviceProfile>(GetInternalProfileInfo(_fileSystem.GetFileInfo(path), type), profile);
|
||||
}
|
||||
|
||||
SerializeToXml(profile, path);
|
||||
}
|
||||
|
||||
@@ -493,7 +475,7 @@ namespace Emby.Dlna
|
||||
|
||||
/// <summary>
|
||||
/// Recreates the object using serialization, to ensure it's not a subclass.
|
||||
/// If it's a subclass it may not serlialize properly to xml (different root element tag name).
|
||||
/// If it's a subclass it may not serlialize properly to xml (different root element tag name)
|
||||
/// </summary>
|
||||
/// <param name="profile"></param>
|
||||
/// <returns></returns>
|
||||
@@ -512,7 +494,6 @@ namespace Emby.Dlna
|
||||
class InternalProfileInfo
|
||||
{
|
||||
internal DeviceProfileInfo Info { get; set; }
|
||||
|
||||
internal string Path { get; set; }
|
||||
}
|
||||
|
||||
@@ -586,9 +567,9 @@ namespace Emby.Dlna
|
||||
new Foobar2000Profile(),
|
||||
new SharpSmartTvProfile(),
|
||||
new MediaMonkeyProfile(),
|
||||
// new Windows81Profile(),
|
||||
// new WindowsMediaCenterProfile(),
|
||||
// new WindowsPhoneProfile(),
|
||||
//new Windows81Profile(),
|
||||
//new WindowsMediaCenterProfile(),
|
||||
//new WindowsPhoneProfile(),
|
||||
new DirectTvProfile(),
|
||||
new DishHopperJoeyProfile(),
|
||||
new DefaultProfile(),
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<!-- ProjectGuid is only included as a requirement for SonarQube analysis -->
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>{805844AB-E92F-45E6-9D99-4F6D48D129A5}</ProjectGuid>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="..\SharedVersion.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable SA1600
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable SA1600
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
@@ -31,26 +32,18 @@ namespace Emby.Dlna.Eventing
|
||||
public EventSubscriptionResponse RenewEventSubscription(string subscriptionId, string notificationType, string requestedTimeoutString, string callbackUrl)
|
||||
{
|
||||
var subscription = GetSubscription(subscriptionId, false);
|
||||
if (subscription != null)
|
||||
{
|
||||
subscription.TimeoutSeconds = ParseTimeout(requestedTimeoutString) ?? 300;
|
||||
int timeoutSeconds = subscription.TimeoutSeconds;
|
||||
subscription.SubscriptionTime = DateTime.UtcNow;
|
||||
|
||||
_logger.LogDebug(
|
||||
"Renewing event subscription for {0} with timeout of {1} to {2}",
|
||||
subscription.NotificationType,
|
||||
timeoutSeconds,
|
||||
subscription.CallbackUrl);
|
||||
subscription.TimeoutSeconds = ParseTimeout(requestedTimeoutString) ?? 300;
|
||||
int timeoutSeconds = subscription.TimeoutSeconds;
|
||||
subscription.SubscriptionTime = DateTime.UtcNow;
|
||||
|
||||
return GetEventSubscriptionResponse(subscriptionId, requestedTimeoutString, timeoutSeconds);
|
||||
}
|
||||
_logger.LogDebug(
|
||||
"Renewing event subscription for {0} with timeout of {1} to {2}",
|
||||
subscription.NotificationType,
|
||||
timeoutSeconds,
|
||||
subscription.CallbackUrl);
|
||||
|
||||
return new EventSubscriptionResponse
|
||||
{
|
||||
Content = string.Empty,
|
||||
ContentType = "text/plain"
|
||||
};
|
||||
return GetEventSubscriptionResponse(subscriptionId, requestedTimeoutString, timeoutSeconds);
|
||||
}
|
||||
|
||||
public EventSubscriptionResponse CreateEventSubscription(string notificationType, string requestedTimeoutString, string callbackUrl)
|
||||
@@ -158,7 +151,6 @@ namespace Emby.Dlna.Eventing
|
||||
builder.Append("</" + key + ">");
|
||||
builder.Append("</e:property>");
|
||||
}
|
||||
|
||||
builder.Append("</e:propertyset>");
|
||||
|
||||
var options = new HttpRequestOptions
|
||||
@@ -178,6 +170,7 @@ namespace Emby.Dlna.Eventing
|
||||
{
|
||||
using (await _httpClient.SendAsync(options, new HttpMethod("NOTIFY")).ConfigureAwait(false))
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable SA1600
|
||||
|
||||
using System;
|
||||
|
||||
@@ -7,13 +8,10 @@ namespace Emby.Dlna.Eventing
|
||||
public class EventSubscription
|
||||
{
|
||||
public string Id { get; set; }
|
||||
|
||||
public string CallbackUrl { get; set; }
|
||||
|
||||
public string NotificationType { get; set; }
|
||||
|
||||
public DateTime SubscriptionTime { get; set; }
|
||||
|
||||
public int TimeoutSeconds { get; set; }
|
||||
|
||||
public long TriggerCount { get; set; }
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable SA1600
|
||||
|
||||
namespace Emby.Dlna
|
||||
{
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable SA1600
|
||||
|
||||
namespace Emby.Dlna
|
||||
{
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable SA1600
|
||||
|
||||
namespace Emby.Dlna
|
||||
{
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable SA1600
|
||||
|
||||
namespace Emby.Dlna
|
||||
{
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable SA1600
|
||||
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable SA1600
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Net.Sockets;
|
||||
using System.Globalization;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Emby.Dlna.PlayTo;
|
||||
@@ -26,15 +27,17 @@ using MediaBrowser.Model.System;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Rssdp;
|
||||
using Rssdp.Infrastructure;
|
||||
using OperatingSystem = MediaBrowser.Common.System.OperatingSystem;
|
||||
using OperatingSystem = MediaBrowser.Common.System.OperatingSystem;
|
||||
|
||||
namespace Emby.Dlna.Main
|
||||
{
|
||||
public class DlnaEntryPoint : IServerEntryPoint, IRunBeforeStartup
|
||||
{
|
||||
private readonly IServerConfigurationManager _config;
|
||||
private readonly ILogger<DlnaEntryPoint> _logger;
|
||||
private readonly ILogger _logger;
|
||||
private readonly IServerApplicationHost _appHost;
|
||||
|
||||
private PlayToManager _manager;
|
||||
private readonly ISessionManager _sessionManager;
|
||||
private readonly IHttpClient _httpClient;
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
@@ -45,25 +48,23 @@ namespace Emby.Dlna.Main
|
||||
private readonly ILocalizationManager _localization;
|
||||
private readonly IMediaSourceManager _mediaSourceManager;
|
||||
private readonly IMediaEncoder _mediaEncoder;
|
||||
|
||||
private readonly IDeviceDiscovery _deviceDiscovery;
|
||||
|
||||
private SsdpDevicePublisher _Publisher;
|
||||
|
||||
private readonly ISocketFactory _socketFactory;
|
||||
private readonly INetworkManager _networkManager;
|
||||
private readonly object _syncLock = new object();
|
||||
|
||||
private PlayToManager _manager;
|
||||
private SsdpDevicePublisher _publisher;
|
||||
private ISsdpCommunicationsServer _communicationsServer;
|
||||
|
||||
internal IContentDirectory ContentDirectory { get; private set; }
|
||||
|
||||
internal IConnectionManager ConnectionManager { get; private set; }
|
||||
|
||||
internal IMediaReceiverRegistrar MediaReceiverRegistrar { get; private set; }
|
||||
|
||||
public static DlnaEntryPoint Current;
|
||||
|
||||
public DlnaEntryPoint(
|
||||
IServerConfigurationManager config,
|
||||
public DlnaEntryPoint(IServerConfigurationManager config,
|
||||
ILoggerFactory loggerFactory,
|
||||
IServerApplicationHost appHost,
|
||||
ISessionManager sessionManager,
|
||||
@@ -97,7 +98,7 @@ namespace Emby.Dlna.Main
|
||||
_mediaEncoder = mediaEncoder;
|
||||
_socketFactory = socketFactory;
|
||||
_networkManager = networkManager;
|
||||
_logger = loggerFactory.CreateLogger<DlnaEntryPoint>();
|
||||
_logger = loggerFactory.CreateLogger("Dlna");
|
||||
|
||||
ContentDirectory = new ContentDirectory.ContentDirectory(
|
||||
dlnaManager,
|
||||
@@ -106,7 +107,7 @@ namespace Emby.Dlna.Main
|
||||
libraryManager,
|
||||
config,
|
||||
userManager,
|
||||
loggerFactory.CreateLogger<ContentDirectory.ContentDirectory>(),
|
||||
_logger,
|
||||
httpClient,
|
||||
localizationManager,
|
||||
mediaSourceManager,
|
||||
@@ -114,16 +115,9 @@ namespace Emby.Dlna.Main
|
||||
mediaEncoder,
|
||||
tvSeriesManager);
|
||||
|
||||
ConnectionManager = new ConnectionManager.ConnectionManager(
|
||||
dlnaManager,
|
||||
config,
|
||||
loggerFactory.CreateLogger<ConnectionManager.ConnectionManager>(),
|
||||
httpClient);
|
||||
ConnectionManager = new ConnectionManager.ConnectionManager(dlnaManager, config, _logger, httpClient);
|
||||
|
||||
MediaReceiverRegistrar = new MediaReceiverRegistrar.MediaReceiverRegistrar(
|
||||
loggerFactory.CreateLogger<MediaReceiverRegistrar.MediaReceiverRegistrar>(),
|
||||
httpClient,
|
||||
config);
|
||||
MediaReceiverRegistrar = new MediaReceiverRegistrar.MediaReceiverRegistrar(_logger, httpClient, config);
|
||||
Current = this;
|
||||
}
|
||||
|
||||
@@ -131,20 +125,20 @@ namespace Emby.Dlna.Main
|
||||
{
|
||||
await ((DlnaManager)_dlnaManager).InitProfilesAsync().ConfigureAwait(false);
|
||||
|
||||
await ReloadComponents().ConfigureAwait(false);
|
||||
ReloadComponents();
|
||||
|
||||
_config.NamedConfigurationUpdated += OnNamedConfigurationUpdated;
|
||||
_config.NamedConfigurationUpdated += _config_NamedConfigurationUpdated;
|
||||
}
|
||||
|
||||
private async void OnNamedConfigurationUpdated(object sender, ConfigurationUpdateEventArgs e)
|
||||
void _config_NamedConfigurationUpdated(object sender, ConfigurationUpdateEventArgs e)
|
||||
{
|
||||
if (string.Equals(e.Key, "dlna", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
await ReloadComponents().ConfigureAwait(false);
|
||||
ReloadComponents();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ReloadComponents()
|
||||
private async void ReloadComponents()
|
||||
{
|
||||
var options = _config.GetDlnaConfiguration();
|
||||
|
||||
@@ -178,7 +172,7 @@ namespace Emby.Dlna.Main
|
||||
var enableMultiSocketBinding = OperatingSystem.Id == OperatingSystemId.Windows ||
|
||||
OperatingSystem.Id == OperatingSystemId.Linux;
|
||||
|
||||
_communicationsServer = new SsdpCommunicationsServer(_socketFactory, _networkManager, _logger, enableMultiSocketBinding)
|
||||
_communicationsServer = new SsdpCommunicationsServer(_config, _socketFactory, _networkManager, _logger, enableMultiSocketBinding)
|
||||
{
|
||||
IsShared = true
|
||||
};
|
||||
@@ -229,22 +223,20 @@ namespace Emby.Dlna.Main
|
||||
return;
|
||||
}
|
||||
|
||||
if (_publisher != null)
|
||||
if (_Publisher != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_publisher = new SsdpDevicePublisher(_communicationsServer, _networkManager, OperatingSystem.Name, Environment.OSVersion.VersionString, _config.GetDlnaConfiguration().SendOnlyMatchedHost)
|
||||
{
|
||||
LogFunction = LogMessage,
|
||||
SupportPnpRootDevice = false
|
||||
};
|
||||
_Publisher = new SsdpDevicePublisher(_communicationsServer, _networkManager, OperatingSystem.Name, Environment.OSVersion.VersionString, _config.GetDlnaConfiguration().SendOnlyMatchedHost);
|
||||
_Publisher.LogFunction = LogMessage;
|
||||
_Publisher.SupportPnpRootDevice = false;
|
||||
|
||||
await RegisterServerEndpoints().ConfigureAwait(false);
|
||||
|
||||
_publisher.StartBroadcastingAliveMessages(TimeSpan.FromSeconds(options.BlastAliveMessageIntervalSeconds));
|
||||
_Publisher.StartBroadcastingAliveMessages(TimeSpan.FromSeconds(options.BlastAliveMessageIntervalSeconds));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -262,14 +254,8 @@ namespace Emby.Dlna.Main
|
||||
{
|
||||
if (address.AddressFamily == AddressFamily.InterNetworkV6)
|
||||
{
|
||||
// Not supporting IPv6 right now
|
||||
continue;
|
||||
}
|
||||
|
||||
// Limit to LAN addresses only
|
||||
if (!_networkManager.IsAddressInSubnets(address, true, true))
|
||||
{
|
||||
continue;
|
||||
// Not support IPv6 right now
|
||||
continue;
|
||||
}
|
||||
|
||||
var fullService = "urn:schemas-upnp-org:device:MediaServer:1";
|
||||
@@ -281,7 +267,7 @@ namespace Emby.Dlna.Main
|
||||
|
||||
var device = new SsdpRootDevice
|
||||
{
|
||||
CacheLifetime = TimeSpan.FromSeconds(1800), // How long SSDP clients can cache this info.
|
||||
CacheLifetime = TimeSpan.FromSeconds(1800), //How long SSDP clients can cache this info.
|
||||
Location = uri, // Must point to the URL that serves your devices UPnP description document.
|
||||
Address = address,
|
||||
SubnetMask = _networkManager.GetLocalIpSubnetMask(address),
|
||||
@@ -293,13 +279,13 @@ namespace Emby.Dlna.Main
|
||||
};
|
||||
|
||||
SetProperies(device, fullService);
|
||||
_publisher.AddDevice(device);
|
||||
_Publisher.AddDevice(device);
|
||||
|
||||
var embeddedDevices = new[]
|
||||
{
|
||||
"urn:schemas-upnp-org:service:ContentDirectory:1",
|
||||
"urn:schemas-upnp-org:service:ConnectionManager:1",
|
||||
// "urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:1"
|
||||
//"urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:1"
|
||||
};
|
||||
|
||||
foreach (var subDevice in embeddedDevices)
|
||||
@@ -325,13 +311,12 @@ namespace Emby.Dlna.Main
|
||||
{
|
||||
guid = text.GetMD5();
|
||||
}
|
||||
|
||||
return guid.ToString("N", CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
private void SetProperies(SsdpDevice device, string fullDeviceType)
|
||||
{
|
||||
var service = fullDeviceType.Replace("urn:", string.Empty, StringComparison.OrdinalIgnoreCase).Replace(":1", string.Empty, StringComparison.OrdinalIgnoreCase);
|
||||
var service = fullDeviceType.Replace("urn:", string.Empty).Replace(":1", string.Empty);
|
||||
|
||||
var serviceParts = service.Split(':');
|
||||
|
||||
@@ -342,6 +327,7 @@ namespace Emby.Dlna.Main
|
||||
device.DeviceType = serviceParts[2];
|
||||
}
|
||||
|
||||
private readonly object _syncLock = new object();
|
||||
private void StartPlayToManager()
|
||||
{
|
||||
lock (_syncLock)
|
||||
@@ -353,8 +339,7 @@ namespace Emby.Dlna.Main
|
||||
|
||||
try
|
||||
{
|
||||
_manager = new PlayToManager(
|
||||
_logger,
|
||||
_manager = new PlayToManager(_logger,
|
||||
_sessionManager,
|
||||
_libraryManager,
|
||||
_userManager,
|
||||
@@ -393,7 +378,6 @@ namespace Emby.Dlna.Main
|
||||
{
|
||||
_logger.LogError(ex, "Error disposing PlayTo manager");
|
||||
}
|
||||
|
||||
_manager = null;
|
||||
}
|
||||
}
|
||||
@@ -420,11 +404,11 @@ namespace Emby.Dlna.Main
|
||||
|
||||
public void DisposeDevicePublisher()
|
||||
{
|
||||
if (_publisher != null)
|
||||
if (_Publisher != null)
|
||||
{
|
||||
_logger.LogInformation("Disposing SsdpDevicePublisher");
|
||||
_publisher.Dispose();
|
||||
_publisher = null;
|
||||
_Publisher.Dispose();
|
||||
_Publisher = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable SA1600
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable SA1600
|
||||
|
||||
using System.Threading.Tasks;
|
||||
using Emby.Dlna.Service;
|
||||
@@ -12,10 +13,7 @@ namespace Emby.Dlna.MediaReceiverRegistrar
|
||||
{
|
||||
private readonly IServerConfigurationManager _config;
|
||||
|
||||
public MediaReceiverRegistrar(
|
||||
ILogger<MediaReceiverRegistrar> logger,
|
||||
IHttpClient httpClient,
|
||||
IServerConfigurationManager config)
|
||||
public MediaReceiverRegistrar(ILogger logger, IHttpClient httpClient, IServerConfigurationManager config)
|
||||
: base(logger, httpClient)
|
||||
{
|
||||
_config = config;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable SA1600
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Emby.Dlna.Common;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable SA1600
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Emby.Dlna.Common;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable SA1600
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -19,6 +20,8 @@ namespace Emby.Dlna.PlayTo
|
||||
{
|
||||
public class Device : IDisposable
|
||||
{
|
||||
#region Fields & Properties
|
||||
|
||||
private Timer _timer;
|
||||
|
||||
public DeviceInfo Properties { get; set; }
|
||||
@@ -32,10 +35,9 @@ namespace Emby.Dlna.PlayTo
|
||||
{
|
||||
get
|
||||
{
|
||||
RefreshVolumeIfNeeded().GetAwaiter().GetResult();
|
||||
RefreshVolumeIfNeeded();
|
||||
return _volume;
|
||||
}
|
||||
|
||||
set => _volume = value;
|
||||
}
|
||||
|
||||
@@ -51,10 +53,10 @@ namespace Emby.Dlna.PlayTo
|
||||
|
||||
public bool IsStopped => TransportState == TRANSPORTSTATE.STOPPED;
|
||||
|
||||
#endregion
|
||||
|
||||
private readonly IHttpClient _httpClient;
|
||||
|
||||
private readonly ILogger _logger;
|
||||
|
||||
private readonly IServerConfigurationManager _config;
|
||||
|
||||
public Action OnDeviceUnavailable { get; set; }
|
||||
@@ -75,25 +77,25 @@ namespace Emby.Dlna.PlayTo
|
||||
|
||||
private DateTime _lastVolumeRefresh;
|
||||
private bool _volumeRefreshActive;
|
||||
private Task RefreshVolumeIfNeeded()
|
||||
private void RefreshVolumeIfNeeded()
|
||||
{
|
||||
if (_volumeRefreshActive
|
||||
&& DateTime.UtcNow >= _lastVolumeRefresh.AddSeconds(5))
|
||||
{
|
||||
_lastVolumeRefresh = DateTime.UtcNow;
|
||||
return RefreshVolume();
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task RefreshVolume(CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (_disposed)
|
||||
if (!_volumeRefreshActive)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (DateTime.UtcNow >= _lastVolumeRefresh.AddSeconds(5))
|
||||
{
|
||||
_lastVolumeRefresh = DateTime.UtcNow;
|
||||
RefreshVolume(CancellationToken.None);
|
||||
}
|
||||
}
|
||||
|
||||
private async void RefreshVolume(CancellationToken cancellationToken)
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
await GetVolume(cancellationToken).ConfigureAwait(false);
|
||||
@@ -140,6 +142,8 @@ namespace Emby.Dlna.PlayTo
|
||||
}
|
||||
}
|
||||
|
||||
#region Commanding
|
||||
|
||||
public Task VolumeDown(CancellationToken cancellationToken)
|
||||
{
|
||||
var sendVolume = Math.Max(Volume - 5, 0);
|
||||
@@ -208,9 +212,7 @@ namespace Emby.Dlna.PlayTo
|
||||
|
||||
var command = rendererCommands.ServiceActions.FirstOrDefault(c => c.Name == "SetMute");
|
||||
if (command == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var service = GetServiceRenderingControl();
|
||||
|
||||
@@ -231,7 +233,7 @@ namespace Emby.Dlna.PlayTo
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets volume on a scale of 0-100.
|
||||
/// Sets volume on a scale of 0-100
|
||||
/// </summary>
|
||||
public async Task SetVolume(int value, CancellationToken cancellationToken)
|
||||
{
|
||||
@@ -239,9 +241,7 @@ namespace Emby.Dlna.PlayTo
|
||||
|
||||
var command = rendererCommands.ServiceActions.FirstOrDefault(c => c.Name == "SetVolume");
|
||||
if (command == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var service = GetServiceRenderingControl();
|
||||
|
||||
@@ -264,9 +264,7 @@ namespace Emby.Dlna.PlayTo
|
||||
|
||||
var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "Seek");
|
||||
if (command == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var service = GetAvTransportService();
|
||||
|
||||
@@ -291,9 +289,7 @@ namespace Emby.Dlna.PlayTo
|
||||
|
||||
var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "SetAVTransportURI");
|
||||
if (command == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var dictionary = new Dictionary<string, string>
|
||||
{
|
||||
@@ -351,12 +347,7 @@ namespace Emby.Dlna.PlayTo
|
||||
throw new InvalidOperationException("Unable to find service");
|
||||
}
|
||||
|
||||
return new SsdpHttpClient(_httpClient).SendCommandAsync(
|
||||
Properties.BaseUrl,
|
||||
service,
|
||||
command.Name,
|
||||
avCommands.BuildPost(command, service.ServiceType, 1),
|
||||
cancellationToken: cancellationToken);
|
||||
return new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, avCommands.BuildPost(command, service.ServiceType, 1));
|
||||
}
|
||||
|
||||
public async Task SetPlay(CancellationToken cancellationToken)
|
||||
@@ -406,8 +397,11 @@ namespace Emby.Dlna.PlayTo
|
||||
RestartTimer(true);
|
||||
}
|
||||
|
||||
private int _connectFailureCount;
|
||||
#endregion
|
||||
|
||||
#region Get data
|
||||
|
||||
private int _connectFailureCount;
|
||||
private async void TimerCallback(object sender)
|
||||
{
|
||||
if (_disposed)
|
||||
@@ -460,9 +454,7 @@ namespace Emby.Dlna.PlayTo
|
||||
_connectFailureCount = 0;
|
||||
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// If we're not playing anything make sure we don't get data more often than neccessry to keep the Session alive
|
||||
if (transportState.Value == TRANSPORTSTATE.STOPPED)
|
||||
@@ -482,9 +474,7 @@ namespace Emby.Dlna.PlayTo
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_logger.LogError(ex, "Error updating device info for {DeviceName}", Properties.Name);
|
||||
|
||||
@@ -500,7 +490,6 @@ namespace Emby.Dlna.PlayTo
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
RestartTimerInactive();
|
||||
}
|
||||
}
|
||||
@@ -527,12 +516,8 @@ namespace Emby.Dlna.PlayTo
|
||||
return;
|
||||
}
|
||||
|
||||
var result = await new SsdpHttpClient(_httpClient).SendCommandAsync(
|
||||
Properties.BaseUrl,
|
||||
service,
|
||||
command.Name,
|
||||
rendererCommands.BuildPost(command, service.ServiceType),
|
||||
cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||
var result = await new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType), true)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (result == null || result.Document == null)
|
||||
{
|
||||
@@ -577,17 +562,11 @@ namespace Emby.Dlna.PlayTo
|
||||
return;
|
||||
}
|
||||
|
||||
var result = await new SsdpHttpClient(_httpClient).SendCommandAsync(
|
||||
Properties.BaseUrl,
|
||||
service,
|
||||
command.Name,
|
||||
rendererCommands.BuildPost(command, service.ServiceType),
|
||||
cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||
var result = await new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType), true)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (result == null || result.Document == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var valueNode = result.Document.Descendants(uPnpNamespaces.RenderingControl + "GetMuteResponse")
|
||||
.Select(i => i.Element("CurrentMute"))
|
||||
@@ -610,12 +589,8 @@ namespace Emby.Dlna.PlayTo
|
||||
return null;
|
||||
}
|
||||
|
||||
var result = await new SsdpHttpClient(_httpClient).SendCommandAsync(
|
||||
Properties.BaseUrl,
|
||||
service,
|
||||
command.Name,
|
||||
avCommands.BuildPost(command, service.ServiceType),
|
||||
cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||
var result = await new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, avCommands.BuildPost(command, service.ServiceType), false)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (result == null || result.Document == null)
|
||||
{
|
||||
@@ -625,7 +600,7 @@ namespace Emby.Dlna.PlayTo
|
||||
var transportState =
|
||||
result.Document.Descendants(uPnpNamespaces.AvTransport + "GetTransportInfoResponse").Select(i => i.Element("CurrentTransportState")).FirstOrDefault(i => i != null);
|
||||
|
||||
var transportStateValue = transportState?.Value;
|
||||
var transportStateValue = transportState == null ? null : transportState.Value;
|
||||
|
||||
if (transportStateValue != null
|
||||
&& Enum.TryParse(transportStateValue, true, out TRANSPORTSTATE state))
|
||||
@@ -652,12 +627,8 @@ namespace Emby.Dlna.PlayTo
|
||||
|
||||
var rendererCommands = await GetRenderingProtocolAsync(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var result = await new SsdpHttpClient(_httpClient).SendCommandAsync(
|
||||
Properties.BaseUrl,
|
||||
service,
|
||||
command.Name,
|
||||
rendererCommands.BuildPost(command, service.ServiceType),
|
||||
cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||
var result = await new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType), false)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (result == null || result.Document == null)
|
||||
{
|
||||
@@ -719,12 +690,8 @@ namespace Emby.Dlna.PlayTo
|
||||
|
||||
var rendererCommands = await GetRenderingProtocolAsync(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var result = await new SsdpHttpClient(_httpClient).SendCommandAsync(
|
||||
Properties.BaseUrl,
|
||||
service,
|
||||
command.Name,
|
||||
rendererCommands.BuildPost(command, service.ServiceType),
|
||||
cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||
var result = await new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType), false)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (result == null || result.Document == null)
|
||||
{
|
||||
@@ -759,7 +726,7 @@ namespace Emby.Dlna.PlayTo
|
||||
|
||||
if (track == null)
|
||||
{
|
||||
// If track is null, some vendors do this, use GetMediaInfo instead
|
||||
//If track is null, some vendors do this, use GetMediaInfo instead
|
||||
return (true, null);
|
||||
}
|
||||
|
||||
@@ -803,6 +770,7 @@ namespace Emby.Dlna.PlayTo
|
||||
}
|
||||
catch (XmlException)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// first try to add a root node with a dlna namesapce
|
||||
@@ -814,6 +782,7 @@ namespace Emby.Dlna.PlayTo
|
||||
}
|
||||
catch (XmlException)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// some devices send back invalid xml
|
||||
@@ -823,6 +792,7 @@ namespace Emby.Dlna.PlayTo
|
||||
}
|
||||
catch (XmlException)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -877,6 +847,10 @@ namespace Emby.Dlna.PlayTo
|
||||
return new string[4];
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region From XML
|
||||
|
||||
private async Task<TransportCommands> GetAVProtocolAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
if (AvCommands != null)
|
||||
@@ -1071,6 +1045,8 @@ namespace Emby.Dlna.PlayTo
|
||||
return new Device(deviceProperties, httpClient, logger, config);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
|
||||
private static DeviceIcon CreateIcon(XElement element)
|
||||
{
|
||||
@@ -1194,6 +1170,8 @@ namespace Emby.Dlna.PlayTo
|
||||
});
|
||||
}
|
||||
|
||||
#region IDisposable
|
||||
|
||||
bool _disposed;
|
||||
|
||||
public void Dispose()
|
||||
@@ -1220,6 +1198,8 @@ namespace Emby.Dlna.PlayTo
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("{0} - {1}", Properties.Name, Properties.BaseUrl);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable SA1600
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Emby.Dlna.Common;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable SA1600
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -7,7 +8,6 @@ using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Emby.Dlna.Didl;
|
||||
using Jellyfin.Data.Entities;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Controller.Dlna;
|
||||
using MediaBrowser.Controller.Drawing;
|
||||
@@ -23,14 +23,11 @@ using MediaBrowser.Model.Globalization;
|
||||
using MediaBrowser.Model.Session;
|
||||
using Microsoft.AspNetCore.WebUtilities;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Photo = MediaBrowser.Controller.Entities.Photo;
|
||||
|
||||
namespace Emby.Dlna.PlayTo
|
||||
{
|
||||
public class PlayToController : ISessionController, IDisposable
|
||||
{
|
||||
private static readonly CultureInfo _usCulture = CultureInfo.ReadOnly(new CultureInfo("en-US"));
|
||||
|
||||
private Device _device;
|
||||
private readonly SessionInfo _session;
|
||||
private readonly ISessionManager _sessionManager;
|
||||
@@ -49,10 +46,9 @@ namespace Emby.Dlna.PlayTo
|
||||
private readonly string _serverAddress;
|
||||
private readonly string _accessToken;
|
||||
|
||||
private readonly List<PlaylistItem> _playlist = new List<PlaylistItem>();
|
||||
private int _currentPlaylistIndex;
|
||||
public bool IsSessionActive => !_disposed && _device != null;
|
||||
|
||||
private bool _disposed;
|
||||
public bool SupportsMediaControl => IsSessionActive;
|
||||
|
||||
public PlayToController(
|
||||
SessionInfo session,
|
||||
@@ -88,22 +84,18 @@ namespace Emby.Dlna.PlayTo
|
||||
_mediaEncoder = mediaEncoder;
|
||||
}
|
||||
|
||||
public bool IsSessionActive => !_disposed && _device != null;
|
||||
|
||||
public bool SupportsMediaControl => IsSessionActive;
|
||||
|
||||
public void Init(Device device)
|
||||
{
|
||||
_device = device;
|
||||
_device.OnDeviceUnavailable = OnDeviceUnavailable;
|
||||
_device.PlaybackStart += OnDevicePlaybackStart;
|
||||
_device.PlaybackProgress += OnDevicePlaybackProgress;
|
||||
_device.PlaybackStopped += OnDevicePlaybackStopped;
|
||||
_device.MediaChanged += OnDeviceMediaChanged;
|
||||
_device.PlaybackStart += _device_PlaybackStart;
|
||||
_device.PlaybackProgress += _device_PlaybackProgress;
|
||||
_device.PlaybackStopped += _device_PlaybackStopped;
|
||||
_device.MediaChanged += _device_MediaChanged;
|
||||
|
||||
_device.Start();
|
||||
|
||||
_deviceDiscovery.DeviceLeft += OnDeviceDiscoveryDeviceLeft;
|
||||
_deviceDiscovery.DeviceLeft += _deviceDiscovery_DeviceLeft;
|
||||
}
|
||||
|
||||
private void OnDeviceUnavailable()
|
||||
@@ -119,7 +111,7 @@ namespace Emby.Dlna.PlayTo
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDeviceDiscoveryDeviceLeft(object sender, GenericEventArgs<UpnpDeviceInfo> e)
|
||||
void _deviceDiscovery_DeviceLeft(object sender, GenericEventArgs<UpnpDeviceInfo> e)
|
||||
{
|
||||
var info = e.Argument;
|
||||
|
||||
@@ -134,7 +126,7 @@ namespace Emby.Dlna.PlayTo
|
||||
}
|
||||
}
|
||||
|
||||
private async void OnDeviceMediaChanged(object sender, MediaChangedEventArgs e)
|
||||
async void _device_MediaChanged(object sender, MediaChangedEventArgs e)
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
@@ -146,18 +138,15 @@ namespace Emby.Dlna.PlayTo
|
||||
var streamInfo = StreamParams.ParseFromUrl(e.OldMediaInfo.Url, _libraryManager, _mediaSourceManager);
|
||||
if (streamInfo.Item != null)
|
||||
{
|
||||
var positionTicks = GetProgressPositionTicks(streamInfo);
|
||||
var positionTicks = GetProgressPositionTicks(e.OldMediaInfo, streamInfo);
|
||||
|
||||
await ReportPlaybackStopped(streamInfo, positionTicks).ConfigureAwait(false);
|
||||
ReportPlaybackStopped(e.OldMediaInfo, streamInfo, positionTicks);
|
||||
}
|
||||
|
||||
streamInfo = StreamParams.ParseFromUrl(e.NewMediaInfo.Url, _libraryManager, _mediaSourceManager);
|
||||
if (streamInfo.Item == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (streamInfo.Item == null) return;
|
||||
|
||||
var newItemProgress = GetProgressInfo(streamInfo);
|
||||
var newItemProgress = GetProgressInfo(e.NewMediaInfo, streamInfo);
|
||||
|
||||
await _sessionManager.OnPlaybackStart(newItemProgress).ConfigureAwait(false);
|
||||
}
|
||||
@@ -167,7 +156,7 @@ namespace Emby.Dlna.PlayTo
|
||||
}
|
||||
}
|
||||
|
||||
private async void OnDevicePlaybackStopped(object sender, PlaybackStoppedEventArgs e)
|
||||
async void _device_PlaybackStopped(object sender, PlaybackStoppedEventArgs e)
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
@@ -178,14 +167,11 @@ namespace Emby.Dlna.PlayTo
|
||||
{
|
||||
var streamInfo = StreamParams.ParseFromUrl(e.MediaInfo.Url, _libraryManager, _mediaSourceManager);
|
||||
|
||||
if (streamInfo.Item == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (streamInfo.Item == null) return;
|
||||
|
||||
var positionTicks = GetProgressPositionTicks(streamInfo);
|
||||
var positionTicks = GetProgressPositionTicks(e.MediaInfo, streamInfo);
|
||||
|
||||
await ReportPlaybackStopped(streamInfo, positionTicks).ConfigureAwait(false);
|
||||
ReportPlaybackStopped(e.MediaInfo, streamInfo, positionTicks);
|
||||
|
||||
var mediaSource = await streamInfo.GetMediaSource(CancellationToken.None).ConfigureAwait(false);
|
||||
|
||||
@@ -193,7 +179,7 @@ namespace Emby.Dlna.PlayTo
|
||||
(_device.Duration == null ? (long?)null : _device.Duration.Value.Ticks) :
|
||||
mediaSource.RunTimeTicks;
|
||||
|
||||
var playedToCompletion = positionTicks.HasValue && positionTicks.Value == 0;
|
||||
var playedToCompletion = (positionTicks.HasValue && positionTicks.Value == 0);
|
||||
|
||||
if (!playedToCompletion && duration.HasValue && positionTicks.HasValue)
|
||||
{
|
||||
@@ -209,7 +195,7 @@ namespace Emby.Dlna.PlayTo
|
||||
}
|
||||
else
|
||||
{
|
||||
_playlist.Clear();
|
||||
Playlist.Clear();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -218,7 +204,7 @@ namespace Emby.Dlna.PlayTo
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ReportPlaybackStopped(StreamParams streamInfo, long? positionTicks)
|
||||
private async void ReportPlaybackStopped(uBaseObject mediaInfo, StreamParams streamInfo, long? positionTicks)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -228,6 +214,7 @@ namespace Emby.Dlna.PlayTo
|
||||
SessionId = _session.Id,
|
||||
PositionTicks = positionTicks,
|
||||
MediaSourceId = streamInfo.MediaSourceId
|
||||
|
||||
}).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -236,7 +223,7 @@ namespace Emby.Dlna.PlayTo
|
||||
}
|
||||
}
|
||||
|
||||
private async void OnDevicePlaybackStart(object sender, PlaybackStartEventArgs e)
|
||||
async void _device_PlaybackStart(object sender, PlaybackStartEventArgs e)
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
@@ -249,7 +236,7 @@ namespace Emby.Dlna.PlayTo
|
||||
|
||||
if (info.Item != null)
|
||||
{
|
||||
var progress = GetProgressInfo(info);
|
||||
var progress = GetProgressInfo(e.MediaInfo, info);
|
||||
|
||||
await _sessionManager.OnPlaybackStart(progress).ConfigureAwait(false);
|
||||
}
|
||||
@@ -260,7 +247,7 @@ namespace Emby.Dlna.PlayTo
|
||||
}
|
||||
}
|
||||
|
||||
private async void OnDevicePlaybackProgress(object sender, PlaybackProgressEventArgs e)
|
||||
async void _device_PlaybackProgress(object sender, PlaybackProgressEventArgs e)
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
@@ -280,7 +267,7 @@ namespace Emby.Dlna.PlayTo
|
||||
|
||||
if (info.Item != null)
|
||||
{
|
||||
var progress = GetProgressInfo(info);
|
||||
var progress = GetProgressInfo(e.MediaInfo, info);
|
||||
|
||||
await _sessionManager.OnPlaybackProgress(progress).ConfigureAwait(false);
|
||||
}
|
||||
@@ -291,7 +278,7 @@ namespace Emby.Dlna.PlayTo
|
||||
}
|
||||
}
|
||||
|
||||
private long? GetProgressPositionTicks(StreamParams info)
|
||||
private long? GetProgressPositionTicks(uBaseObject mediaInfo, StreamParams info)
|
||||
{
|
||||
var ticks = _device.Position.Ticks;
|
||||
|
||||
@@ -303,13 +290,13 @@ namespace Emby.Dlna.PlayTo
|
||||
return ticks;
|
||||
}
|
||||
|
||||
private PlaybackStartInfo GetProgressInfo(StreamParams info)
|
||||
private PlaybackStartInfo GetProgressInfo(uBaseObject mediaInfo, StreamParams info)
|
||||
{
|
||||
return new PlaybackStartInfo
|
||||
{
|
||||
ItemId = info.ItemId,
|
||||
SessionId = _session.Id,
|
||||
PositionTicks = GetProgressPositionTicks(info),
|
||||
PositionTicks = GetProgressPositionTicks(mediaInfo, info),
|
||||
IsMuted = _device.IsMuted,
|
||||
IsPaused = _device.IsPaused,
|
||||
MediaSourceId = info.MediaSourceId,
|
||||
@@ -324,7 +311,9 @@ namespace Emby.Dlna.PlayTo
|
||||
};
|
||||
}
|
||||
|
||||
public Task SendPlayCommand(PlayRequest command, CancellationToken cancellationToken)
|
||||
#region SendCommands
|
||||
|
||||
public async Task SendPlayCommand(PlayRequest command, CancellationToken cancellationToken)
|
||||
{
|
||||
_logger.LogDebug("{0} - Received PlayRequest: {1}", this._session.DeviceName, command.PlayCommand);
|
||||
|
||||
@@ -362,12 +351,11 @@ namespace Emby.Dlna.PlayTo
|
||||
|
||||
if (command.PlayCommand == PlayCommand.PlayLast)
|
||||
{
|
||||
_playlist.AddRange(playlist);
|
||||
Playlist.AddRange(playlist);
|
||||
}
|
||||
|
||||
if (command.PlayCommand == PlayCommand.PlayNext)
|
||||
{
|
||||
_playlist.AddRange(playlist);
|
||||
Playlist.AddRange(playlist);
|
||||
}
|
||||
|
||||
if (!command.ControllingUserId.Equals(Guid.Empty))
|
||||
@@ -376,7 +364,7 @@ namespace Emby.Dlna.PlayTo
|
||||
_session.DeviceName, _session.RemoteEndPoint, user);
|
||||
}
|
||||
|
||||
return PlayItems(playlist, cancellationToken);
|
||||
await PlayItems(playlist).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private Task SendPlaystateCommand(PlaystateRequest command, CancellationToken cancellationToken)
|
||||
@@ -384,7 +372,7 @@ namespace Emby.Dlna.PlayTo
|
||||
switch (command.Command)
|
||||
{
|
||||
case PlaystateCommand.Stop:
|
||||
_playlist.Clear();
|
||||
Playlist.Clear();
|
||||
return _device.SetStop(CancellationToken.None);
|
||||
|
||||
case PlaystateCommand.Pause:
|
||||
@@ -400,10 +388,10 @@ namespace Emby.Dlna.PlayTo
|
||||
return Seek(command.SeekPositionTicks ?? 0);
|
||||
|
||||
case PlaystateCommand.NextTrack:
|
||||
return SetPlaylistIndex(_currentPlaylistIndex + 1, cancellationToken);
|
||||
return SetPlaylistIndex(_currentPlaylistIndex + 1);
|
||||
|
||||
case PlaystateCommand.PreviousTrack:
|
||||
return SetPlaylistIndex(_currentPlaylistIndex - 1, cancellationToken);
|
||||
return SetPlaylistIndex(_currentPlaylistIndex - 1);
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
@@ -425,7 +413,6 @@ namespace Emby.Dlna.PlayTo
|
||||
await _device.SetAvTransport(newItem.StreamUrl, GetDlnaHeaders(newItem), newItem.Didl, CancellationToken.None).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
await SeekAfterTransportChange(newPosition, CancellationToken.None).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -440,6 +427,14 @@ namespace Emby.Dlna.PlayTo
|
||||
return info.IsDirectStream;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Playlist
|
||||
|
||||
private int _currentPlaylistIndex;
|
||||
private readonly List<PlaylistItem> _playlist = new List<PlaylistItem>();
|
||||
private List<PlaylistItem> Playlist => _playlist;
|
||||
|
||||
private void AddItemFromId(Guid id, List<BaseItem> list)
|
||||
{
|
||||
var item = _libraryManager.GetItemById(id);
|
||||
@@ -449,13 +444,7 @@ namespace Emby.Dlna.PlayTo
|
||||
}
|
||||
}
|
||||
|
||||
private PlaylistItem CreatePlaylistItem(
|
||||
BaseItem item,
|
||||
User user,
|
||||
long startPostionTicks,
|
||||
string mediaSourceId,
|
||||
int? audioStreamIndex,
|
||||
int? subtitleStreamIndex)
|
||||
private PlaylistItem CreatePlaylistItem(BaseItem item, User user, long startPostionTicks, string mediaSourceId, int? audioStreamIndex, int? subtitleStreamIndex)
|
||||
{
|
||||
var deviceInfo = _device.Properties;
|
||||
|
||||
@@ -463,7 +452,7 @@ namespace Emby.Dlna.PlayTo
|
||||
_dlnaManager.GetDefaultProfile();
|
||||
|
||||
var mediaSources = item is IHasMediaSources
|
||||
? _mediaSourceManager.GetStaticMediaSources(item, true, user)
|
||||
? (_mediaSourceManager.GetStaticMediaSources(item, true, user))
|
||||
: new List<MediaSourceInfo>();
|
||||
|
||||
var playlistItem = GetPlaylistItem(item, mediaSources, profile, _session.DeviceId, mediaSourceId, audioStreamIndex, subtitleStreamIndex);
|
||||
@@ -471,19 +460,8 @@ namespace Emby.Dlna.PlayTo
|
||||
|
||||
playlistItem.StreamUrl = DidlBuilder.NormalizeDlnaMediaUrl(playlistItem.StreamInfo.ToUrl(_serverAddress, _accessToken));
|
||||
|
||||
var itemXml = new DidlBuilder(
|
||||
profile,
|
||||
user,
|
||||
_imageProcessor,
|
||||
_serverAddress,
|
||||
_accessToken,
|
||||
_userDataManager,
|
||||
_localization,
|
||||
_mediaSourceManager,
|
||||
_logger,
|
||||
_mediaEncoder,
|
||||
_libraryManager)
|
||||
.GetItemDidl(item, user, null, _session.DeviceId, new Filter(), playlistItem.StreamInfo);
|
||||
var itemXml = new DidlBuilder(profile, user, _imageProcessor, _serverAddress, _accessToken, _userDataManager, _localization, _mediaSourceManager, _logger, _mediaEncoder)
|
||||
.GetItemDidl(_config.GetDlnaConfiguration(), item, user, null, _session.DeviceId, new Filter(), playlistItem.StreamInfo);
|
||||
|
||||
playlistItem.Didl = itemXml;
|
||||
|
||||
@@ -593,31 +571,30 @@ namespace Emby.Dlna.PlayTo
|
||||
/// Plays the items.
|
||||
/// </summary>
|
||||
/// <param name="items">The items.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns><c>true</c> on success.</returns>
|
||||
private async Task<bool> PlayItems(IEnumerable<PlaylistItem> items, CancellationToken cancellationToken = default)
|
||||
/// <returns></returns>
|
||||
private async Task<bool> PlayItems(IEnumerable<PlaylistItem> items)
|
||||
{
|
||||
_playlist.Clear();
|
||||
_playlist.AddRange(items);
|
||||
_logger.LogDebug("{0} - Playing {1} items", _session.DeviceName, _playlist.Count);
|
||||
Playlist.Clear();
|
||||
Playlist.AddRange(items);
|
||||
_logger.LogDebug("{0} - Playing {1} items", _session.DeviceName, Playlist.Count);
|
||||
|
||||
await SetPlaylistIndex(0, cancellationToken).ConfigureAwait(false);
|
||||
await SetPlaylistIndex(0).ConfigureAwait(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
private async Task SetPlaylistIndex(int index, CancellationToken cancellationToken = default)
|
||||
private async Task SetPlaylistIndex(int index)
|
||||
{
|
||||
if (index < 0 || index >= _playlist.Count)
|
||||
if (index < 0 || index >= Playlist.Count)
|
||||
{
|
||||
_playlist.Clear();
|
||||
await _device.SetStop(cancellationToken).ConfigureAwait(false);
|
||||
Playlist.Clear();
|
||||
await _device.SetStop(CancellationToken.None);
|
||||
return;
|
||||
}
|
||||
|
||||
_currentPlaylistIndex = index;
|
||||
var currentitem = _playlist[index];
|
||||
var currentitem = Playlist[index];
|
||||
|
||||
await _device.SetAvTransport(currentitem.StreamUrl, GetDlnaHeaders(currentitem), currentitem.Didl, cancellationToken).ConfigureAwait(false);
|
||||
await _device.SetAvTransport(currentitem.StreamUrl, GetDlnaHeaders(currentitem), currentitem.Didl, CancellationToken.None);
|
||||
|
||||
var streamInfo = currentitem.StreamInfo;
|
||||
if (streamInfo.StartPositionTicks > 0 && EnableClientSideSeek(streamInfo))
|
||||
@@ -626,7 +603,10 @@ namespace Emby.Dlna.PlayTo
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
#endregion
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
@@ -645,17 +625,19 @@ namespace Emby.Dlna.PlayTo
|
||||
_device.Dispose();
|
||||
}
|
||||
|
||||
_device.PlaybackStart -= OnDevicePlaybackStart;
|
||||
_device.PlaybackProgress -= OnDevicePlaybackProgress;
|
||||
_device.PlaybackStopped -= OnDevicePlaybackStopped;
|
||||
_device.MediaChanged -= OnDeviceMediaChanged;
|
||||
_deviceDiscovery.DeviceLeft -= OnDeviceDiscoveryDeviceLeft;
|
||||
_device.PlaybackStart -= _device_PlaybackStart;
|
||||
_device.PlaybackProgress -= _device_PlaybackProgress;
|
||||
_device.PlaybackStopped -= _device_PlaybackStopped;
|
||||
_device.MediaChanged -= _device_MediaChanged;
|
||||
_deviceDiscovery.DeviceLeft -= _deviceDiscovery_DeviceLeft;
|
||||
_device.OnDeviceUnavailable = null;
|
||||
_device = null;
|
||||
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
private static readonly CultureInfo _usCulture = CultureInfo.ReadOnly(new CultureInfo("en-US"));
|
||||
|
||||
private Task SendGeneralCommand(GeneralCommand command, CancellationToken cancellationToken)
|
||||
{
|
||||
if (Enum.TryParse(command.Name, true, out GeneralCommandType commandType))
|
||||
@@ -714,7 +696,6 @@ namespace Emby.Dlna.PlayTo
|
||||
|
||||
throw new ArgumentException("Volume argument cannot be null");
|
||||
}
|
||||
|
||||
default:
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
@@ -733,7 +714,7 @@ namespace Emby.Dlna.PlayTo
|
||||
|
||||
if (info.Item != null)
|
||||
{
|
||||
var newPosition = GetProgressPositionTicks(info) ?? 0;
|
||||
var newPosition = GetProgressPositionTicks(media, info) ?? 0;
|
||||
|
||||
var user = !_session.UserId.Equals(Guid.Empty) ? _userManager.GetUserById(_session.UserId) : null;
|
||||
var newItem = CreatePlaylistItem(info.Item, user, newPosition, info.MediaSourceId, newIndex, info.SubtitleStreamIndex);
|
||||
@@ -758,7 +739,7 @@ namespace Emby.Dlna.PlayTo
|
||||
|
||||
if (info.Item != null)
|
||||
{
|
||||
var newPosition = GetProgressPositionTicks(info) ?? 0;
|
||||
var newPosition = GetProgressPositionTicks(media, info) ?? 0;
|
||||
|
||||
var user = !_session.UserId.Equals(Guid.Empty) ? _userManager.GetUserById(_session.UserId) : null;
|
||||
var newItem = CreatePlaylistItem(info.Item, user, newPosition, info.MediaSourceId, info.AudioStreamIndex, newIndex);
|
||||
@@ -800,15 +781,12 @@ namespace Emby.Dlna.PlayTo
|
||||
public int? SubtitleStreamIndex { get; set; }
|
||||
|
||||
public string DeviceProfileId { get; set; }
|
||||
|
||||
public string DeviceId { get; set; }
|
||||
|
||||
public string MediaSourceId { get; set; }
|
||||
|
||||
public string LiveStreamId { get; set; }
|
||||
|
||||
public BaseItem Item { get; set; }
|
||||
|
||||
private MediaSourceInfo MediaSource;
|
||||
|
||||
private IMediaSourceManager _mediaSourceManager;
|
||||
@@ -875,11 +853,8 @@ namespace Emby.Dlna.PlayTo
|
||||
return request;
|
||||
}
|
||||
|
||||
var index = url.IndexOf('?', StringComparison.Ordinal);
|
||||
if (index == -1)
|
||||
{
|
||||
return request;
|
||||
}
|
||||
var index = url.IndexOf('?');
|
||||
if (index == -1) return request;
|
||||
|
||||
var query = url.Substring(index + 1);
|
||||
Dictionary<string, string> values = QueryHelpers.ParseQuery(query).ToDictionary(kv => kv.Key, kv => kv.Value.ToString());
|
||||
@@ -926,8 +901,7 @@ namespace Emby.Dlna.PlayTo
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task SendMessage<T>(string name, Guid messageId, T data, CancellationToken cancellationToken)
|
||||
public Task SendMessage<T>(string name, string messageId, T data, ISessionController[] allControllers, CancellationToken cancellationToken)
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
@@ -943,12 +917,10 @@ namespace Emby.Dlna.PlayTo
|
||||
{
|
||||
return SendPlayCommand(data as PlayRequest, cancellationToken);
|
||||
}
|
||||
|
||||
if (string.Equals(name, "PlayState", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return SendPlaystateCommand(data as PlaystateRequest, cancellationToken);
|
||||
}
|
||||
|
||||
if (string.Equals(name, "GeneralCommand", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return SendGeneralCommand(data as GeneralCommand, cancellationToken);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable SA1600
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
@@ -23,7 +24,7 @@ using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Emby.Dlna.PlayTo
|
||||
{
|
||||
public sealed class PlayToManager : IDisposable
|
||||
public class PlayToManager : IDisposable
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly ISessionManager _sessionManager;
|
||||
@@ -78,15 +79,9 @@ namespace Emby.Dlna.PlayTo
|
||||
|
||||
var info = e.Argument;
|
||||
|
||||
if (!info.Headers.TryGetValue("USN", out string usn))
|
||||
{
|
||||
usn = string.Empty;
|
||||
}
|
||||
if (!info.Headers.TryGetValue("USN", out string usn)) usn = string.Empty;
|
||||
|
||||
if (!info.Headers.TryGetValue("NT", out string nt))
|
||||
{
|
||||
nt = string.Empty;
|
||||
}
|
||||
if (!info.Headers.TryGetValue("NT", out string nt)) nt = string.Empty;
|
||||
|
||||
string location = info.Location.ToString();
|
||||
|
||||
@@ -94,7 +89,7 @@ namespace Emby.Dlna.PlayTo
|
||||
if (usn.IndexOf("MediaRenderer:", StringComparison.OrdinalIgnoreCase) == -1 &&
|
||||
nt.IndexOf("MediaRenderer:", StringComparison.OrdinalIgnoreCase) == -1)
|
||||
{
|
||||
// _logger.LogDebug("Upnp device {0} does not contain a MediaRenderer device (0).", location);
|
||||
//_logger.LogDebug("Upnp device {0} does not contain a MediaRenderer device (0).", location);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -118,6 +113,7 @@ namespace Emby.Dlna.PlayTo
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -138,7 +134,6 @@ namespace Emby.Dlna.PlayTo
|
||||
usn = usn.Substring(index);
|
||||
found = true;
|
||||
}
|
||||
|
||||
index = usn.IndexOf("::", StringComparison.OrdinalIgnoreCase);
|
||||
if (index != -1)
|
||||
{
|
||||
@@ -190,8 +185,7 @@ namespace Emby.Dlna.PlayTo
|
||||
serverAddress = _appHost.GetLocalApiUrl(info.LocalIpAddress);
|
||||
}
|
||||
|
||||
controller = new PlayToController(
|
||||
sessionInfo,
|
||||
controller = new PlayToController(sessionInfo,
|
||||
_sessionManager,
|
||||
_libraryManager,
|
||||
_logger,
|
||||
@@ -238,7 +232,6 @@ namespace Emby.Dlna.PlayTo
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
_deviceDiscovery.DeviceDiscovered -= OnDeviceDiscoveryDeviceDiscovered;
|
||||
@@ -249,10 +242,8 @@ namespace Emby.Dlna.PlayTo
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
_sessionLock.Dispose();
|
||||
_disposeCancellationTokenSource.Dispose();
|
||||
}
|
||||
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable SA1600
|
||||
|
||||
using System;
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable SA1600
|
||||
|
||||
using System;
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable SA1600
|
||||
|
||||
using System;
|
||||
|
||||
@@ -12,7 +13,6 @@ namespace Emby.Dlna.PlayTo
|
||||
public class MediaChangedEventArgs : EventArgs
|
||||
{
|
||||
public uBaseObject OldMediaInfo { get; set; }
|
||||
|
||||
public uBaseObject NewMediaInfo { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable SA1600
|
||||
|
||||
using MediaBrowser.Model.Dlna;
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable SA1600
|
||||
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable SA1600
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
@@ -32,15 +33,18 @@ namespace Emby.Dlna.PlayTo
|
||||
DeviceService service,
|
||||
string command,
|
||||
string postData,
|
||||
string header = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
bool logRequest = true,
|
||||
string header = null)
|
||||
{
|
||||
var cancellationToken = CancellationToken.None;
|
||||
|
||||
var url = NormalizeServiceUrl(baseUrl, service.ControlUrl);
|
||||
using (var response = await PostSoapDataAsync(
|
||||
url,
|
||||
$"\"{service.ServiceType}#{command}\"",
|
||||
postData,
|
||||
header,
|
||||
logRequest,
|
||||
cancellationToken)
|
||||
.ConfigureAwait(false))
|
||||
using (var stream = response.Content)
|
||||
@@ -60,7 +64,7 @@ namespace Emby.Dlna.PlayTo
|
||||
return serviceUrl;
|
||||
}
|
||||
|
||||
if (!serviceUrl.StartsWith("/", StringComparison.Ordinal))
|
||||
if (!serviceUrl.StartsWith("/"))
|
||||
{
|
||||
serviceUrl = "/" + serviceUrl;
|
||||
}
|
||||
@@ -91,6 +95,7 @@ namespace Emby.Dlna.PlayTo
|
||||
|
||||
using (await _httpClient.SendAsync(options, new HttpMethod("SUBSCRIBE")).ConfigureAwait(false))
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,6 +128,7 @@ namespace Emby.Dlna.PlayTo
|
||||
string soapAction,
|
||||
string postData,
|
||||
string header,
|
||||
bool logRequest,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
if (soapAction[0] != '\"')
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable SA1600
|
||||
|
||||
namespace Emby.Dlna.PlayTo
|
||||
{
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable SA1600
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable SA1600
|
||||
|
||||
using System;
|
||||
using System.Xml.Linq;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable SA1600
|
||||
|
||||
using System;
|
||||
|
||||
@@ -44,12 +45,10 @@ namespace Emby.Dlna.PlayTo
|
||||
{
|
||||
return MediaBrowser.Model.Entities.MediaType.Audio;
|
||||
}
|
||||
|
||||
if (classType.IndexOf(MediaBrowser.Model.Entities.MediaType.Video, StringComparison.Ordinal) != -1)
|
||||
{
|
||||
return MediaBrowser.Model.Entities.MediaType.Video;
|
||||
}
|
||||
|
||||
if (classType.IndexOf("image", StringComparison.Ordinal) != -1)
|
||||
{
|
||||
return MediaBrowser.Model.Entities.MediaType.Photo;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable SA1600
|
||||
|
||||
using System.Xml.Linq;
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable SA1600
|
||||
|
||||
using System.Linq;
|
||||
using MediaBrowser.Model.Dlna;
|
||||
@@ -164,7 +165,7 @@ namespace Emby.Dlna.Profiles
|
||||
|
||||
public void AddXmlRootAttribute(string name, string value)
|
||||
{
|
||||
var atts = XmlRootAttributes ?? System.Array.Empty<XmlAttribute>();
|
||||
var atts = XmlRootAttributes ?? new XmlAttribute[] { };
|
||||
var list = atts.ToList();
|
||||
|
||||
list.Add(new XmlAttribute
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable SA1600
|
||||
|
||||
using MediaBrowser.Model.Dlna;
|
||||
|
||||
@@ -28,7 +29,7 @@ namespace Emby.Dlna.Profiles
|
||||
},
|
||||
};
|
||||
|
||||
ResponseProfiles = System.Array.Empty<ResponseProfile>();
|
||||
ResponseProfiles = new ResponseProfile[] { };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable SA1600
|
||||
|
||||
using MediaBrowser.Model.Dlna;
|
||||
|
||||
@@ -123,7 +124,7 @@ namespace Emby.Dlna.Profiles
|
||||
}
|
||||
};
|
||||
|
||||
ResponseProfiles = System.Array.Empty<ResponseProfile>();
|
||||
ResponseProfiles = new ResponseProfile[] { };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable SA1600
|
||||
|
||||
using MediaBrowser.Model.Dlna;
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable SA1600
|
||||
|
||||
using MediaBrowser.Model.Dlna;
|
||||
|
||||
@@ -72,7 +73,7 @@ namespace Emby.Dlna.Profiles
|
||||
}
|
||||
};
|
||||
|
||||
ResponseProfiles = System.Array.Empty<ResponseProfile>();
|
||||
ResponseProfiles = new ResponseProfile[] { };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable SA1600
|
||||
|
||||
using MediaBrowser.Model.Dlna;
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable SA1600
|
||||
|
||||
using MediaBrowser.Model.Dlna;
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable SA1600
|
||||
|
||||
using MediaBrowser.Model.Dlna;
|
||||
|
||||
@@ -37,7 +38,7 @@ namespace Emby.Dlna.Profiles
|
||||
},
|
||||
};
|
||||
|
||||
ResponseProfiles = System.Array.Empty<ResponseProfile>();
|
||||
ResponseProfiles = new ResponseProfile[] { };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable SA1600
|
||||
|
||||
using System;
|
||||
using MediaBrowser.Model.Dlna;
|
||||
|
||||
namespace Emby.Dlna.Profiles
|
||||
@@ -38,7 +38,7 @@ namespace Emby.Dlna.Profiles
|
||||
}
|
||||
};
|
||||
|
||||
ResponseProfiles = Array.Empty<ResponseProfile>();
|
||||
ResponseProfiles = new ResponseProfile[] { };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable SA1600
|
||||
|
||||
using MediaBrowser.Model.Dlna;
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable SA1600
|
||||
|
||||
using MediaBrowser.Model.Dlna;
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable SA1600
|
||||
|
||||
using MediaBrowser.Model.Dlna;
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable SA1600
|
||||
|
||||
using MediaBrowser.Model.Dlna;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable SA1600
|
||||
|
||||
using System;
|
||||
using MediaBrowser.Model.Dlna;
|
||||
|
||||
namespace Emby.Dlna.Profiles
|
||||
@@ -224,7 +224,7 @@ namespace Emby.Dlna.Profiles
|
||||
}
|
||||
};
|
||||
|
||||
ResponseProfiles = Array.Empty<ResponseProfile>();
|
||||
ResponseProfiles = new ResponseProfile[] { };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable SA1600
|
||||
|
||||
using System;
|
||||
using MediaBrowser.Model.Dlna;
|
||||
|
||||
namespace Emby.Dlna.Profiles
|
||||
@@ -224,7 +224,7 @@ namespace Emby.Dlna.Profiles
|
||||
}
|
||||
};
|
||||
|
||||
ResponseProfiles = Array.Empty<ResponseProfile>();
|
||||
ResponseProfiles = new ResponseProfile[] { };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma warning disable CS1591
|
||||
#pragma warning disable SA1600
|
||||
|
||||
using System;
|
||||
using MediaBrowser.Model.Dlna;
|
||||
|
||||
namespace Emby.Dlna.Profiles
|
||||
@@ -212,7 +212,7 @@ namespace Emby.Dlna.Profiles
|
||||
}
|
||||
};
|
||||
|
||||
ResponseProfiles = Array.Empty<ResponseProfile>();
|
||||
ResponseProfiles = new ResponseProfile[] { };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user