Compare commits

..

62 Commits

Author SHA1 Message Date
Joshua M. Boniface
a3d048a0d9 Merge pull request #2990 from mark-monteiro/create-missing-folders
Create Missing Data Folders

(cherry picked from commit 00d8983d7c)
Signed-off-by: Joshua M. Boniface <joshua@boniface.me>
2020-04-26 16:03:58 -04:00
Joshua M. Boniface
21a6c2e1e2 Bump version to 10.5.5 2020-04-26 15:25:49 -04:00
Joshua M. Boniface
7873fe22fb Merge pull request #2940 from balu92/master
Fix missing colons

(cherry picked from commit 4fa6d1ccee)
Signed-off-by: Joshua M. Boniface <joshua@boniface.me>
2020-04-26 15:23:21 -04:00
Joshua M. Boniface
1f5625d7f9 Merge pull request #2906 from randrey/dlna-nullref-fix
Fix InvalidOperationException while browsing via DLNA client.

(cherry picked from commit ca4b6836c1)
Signed-off-by: Joshua M. Boniface <joshua@boniface.me>
2020-04-26 15:18:34 -04:00
Joshua M. Boniface
a0b053e4a1 Merge pull request #2798 from JustAMan/fix-livetv-again
Make localhost LiveTV restreams always use plain HTTP port

(cherry picked from commit f502c89331)
Signed-off-by: Joshua M. Boniface <joshua@boniface.me>
2020-04-26 15:15:38 -04:00
dkanada
3af63bf439 Merge pull request #2936 from anthonylavado/fix-etags-right
Remove JsonIgnore from the DateLastSaved property of BaseItem

(cherry picked from commit 2f6dd258e6)
Signed-off-by: Joshua M. Boniface <joshua@boniface.me>
2020-04-26 14:54:15 -04:00
Bond-009
77f72dc607 Merge pull request #2915 from randrey/imdbid-length-fix
Fix imdbid regex

(cherry picked from commit 6f866a7fdc)
Signed-off-by: Joshua M. Boniface <joshua@boniface.me>
2020-04-26 14:54:11 -04:00
dkanada
16d9318e08 Merge pull request #2910 from randrey/dlna-extra-mime-types
Additional mime types for DLNA (VLC)

(cherry picked from commit c35e6ac39a)
Signed-off-by: Joshua M. Boniface <joshua@boniface.me>
2020-04-26 14:53:16 -04:00
dkanada
d8f865e93c Merge pull request #2903 from randrey/dlna-albumart-fix
Fix DLNA clients displaying wrong album art.

(cherry picked from commit 1cc5d6745a)
Signed-off-by: Joshua M. Boniface <joshua@boniface.me>
2020-04-26 14:53:15 -04:00
dkanada
3044dfc114 Merge pull request #2864 from JustAMan/fix-caching
Make Last-Modified and If-Modified-Since headers follow the spec

(cherry picked from commit 1f28d49fc7)
Signed-off-by: Joshua M. Boniface <joshua@boniface.me>
2020-04-26 14:53:14 -04:00
Joshua M. Boniface
ace1e70c63 Merge pull request #2849 from lyonzy/patch-1
Handle null outputFileExtension in GetOutputFilePath

(cherry picked from commit 5c669d7ad7)
Signed-off-by: Joshua M. Boniface <joshua@boniface.me>
2020-04-26 14:53:14 -04:00
dkanada
6a9a677111 Merge pull request #2848 from ZadenRB/startup-endpoint-parameters
Fix casing of JSON in Jellyfin API

(cherry picked from commit 167e96d212)
Signed-off-by: Joshua M. Boniface <joshua@boniface.me>
2020-04-26 14:53:12 -04:00
Andrew Rabert
cc35876f6b Merge pull request #2813 from nyanmisaka/docker
Switch to jellyfin-ffmpeg with integrated driver in docker build

(cherry picked from commit 1d4763a246)
Signed-off-by: Joshua M. Boniface <joshua@boniface.me>
2020-04-26 14:52:55 -04:00
Bond-009
5611b2c038 Merge pull request #2745 from Artiume/patch-6
Force Audio Transcoding for LiveTV Transcoding

(cherry picked from commit 31769bda28)
Signed-off-by: Joshua M. Boniface <joshua@boniface.me>
2020-04-26 14:51:03 -04:00
Anthony Lavado
163fb94bde Merge pull request #2935 from jellyfin/revert-2928-fix-etags
Revert "Fix eTags DateLastSaved"
2020-04-23 17:30:41 -04:00
Anthony Lavado
5c4326daf4 Revert "Fix eTags DateLastSaved" 2020-04-20 02:27:02 -04:00
Anthony Lavado
2d369ca614 Merge pull request #2928 from anthonylavado/fix-etags
Fix eTags DateLastSaved
2020-04-19 17:50:50 -04:00
Anthony Lavado
3c05079333 Remove JsonIgnore from the DateLastSaved property of BaseItem 2020-04-19 16:43:52 -04:00
dkanada
0c204f4706 Merge pull request #2868 from JustAMan/fix-10.5-build
Fix passing web branch
2020-04-18 18:15:22 +09:00
Bond-009
1e3a524a7a Merge pull request #2834 from mark-monteiro/add-nuget-config
Add nuget.config file

(cherry picked from commit 7731aba6f4)
Signed-off-by: Joshua M. Boniface <joshua@boniface.me>
2020-04-14 17:32:13 -04:00
Vasily
99e22c499d Fix passing web branch 2020-04-14 14:33:27 +03:00
Joshua M. Boniface
dbbf97e588 Fix version in spec 2020-04-13 00:24:43 -04:00
Joshua M. Boniface
4e9df69ffd Merge pull request #2847 from mark-monteiro/fix-build
Fix compilation error in HttpListenerHost
2020-04-12 21:35:43 -04:00
Mark Monteiro
7f38ef4c3c Fix compilation error in HttpListenerHost 2020-04-12 20:49:54 -04:00
Joshua M. Boniface
16549dead9 Bump version to 10.5.4 2020-04-12 19:24:56 -04:00
Joshua M. Boniface
9bd1a9d19c Merge pull request #2715 from nyanmisaka/libfdk-aac
Prefer to use libfdk_aac encoder for better audio quality when it is available

(cherry picked from commit bf92694f8b)
Signed-off-by: Joshua M. Boniface <joshua@boniface.me>
2020-04-12 19:19:00 -04:00
Joshua M. Boniface
67194994f9 Merge pull request #2783 from JustAMan/better-cancel-msg
Add logging of URL being processed when logging an error

(cherry picked from commit 2be6550db4)
Signed-off-by: Joshua M. Boniface <joshua@boniface.me>
2020-04-12 19:18:22 -04:00
Vasily
c249e15f48 Merge pull request #2782 from JustAMan/fix-ssa-delivery
Fix support for attachments with baseURL set

(cherry picked from commit 6386b9b1b9)
Signed-off-by: Joshua M. Boniface <joshua@boniface.me>
2020-04-12 16:51:11 -04:00
Anthony Lavado
48ba5a9a30 Merge pull request #2779 from KristupasSavickas/fix-docker-arm-ffmpeg-path
Fix ffmpeg path on ARM docker image

(cherry picked from commit 6d98c0b62a)
Signed-off-by: Joshua M. Boniface <joshua@boniface.me>
2020-04-12 16:50:27 -04:00
Bond-009
dd13f8d16a Merge pull request #2821 from nyanmisaka/mpeg4
Fix MPEG4 broken on VAAPI

(cherry picked from commit 84dba64644)
Signed-off-by: Joshua M. Boniface <joshua@boniface.me>
2020-04-12 15:53:12 -04:00
dkanada
b43a8a56dc Merge pull request #2796 from JustAMan/fix-transcode-reasons
Make codec check in profile examine profile type first

(cherry picked from commit aeedd06f51)
Signed-off-by: Joshua M. Boniface <joshua@boniface.me>
2020-04-12 15:52:39 -04:00
Bond-009
3ec18f085e Merge pull request #2785 from nyanmisaka/mpge4-profile15
Fix MPEG4 packback error regression on vaapi

(cherry picked from commit b16b58bc57)
Signed-off-by: Joshua M. Boniface <joshua@boniface.me>
2020-04-12 15:51:47 -04:00
dkanada
f2728b5a92 Merge pull request #2758 from Bond-009/plugininstalled
Remove PluginInstalled

(cherry picked from commit 0cd7cd611e)
Signed-off-by: Joshua M. Boniface <joshua@boniface.me>
2020-04-12 15:50:09 -04:00
Bond-009
ee47a75f9f Merge pull request #2721 from PrplHaz4/patch-2
Separate Channels permissions from All Libraries

(cherry picked from commit 3a98ad8255)
Signed-off-by: Joshua M. Boniface <joshua@boniface.me>
2020-04-12 15:49:32 -04:00
Joshua M. Boniface
66e7e8bcd2 Make the portable packages build the right tag
Will change for 10.5.4, but quickfix for now
2020-04-05 15:20:08 -04:00
Joshua M. Boniface
0de3a9465e Fix version in RPM spec 2020-04-05 14:58:16 -04:00
Vasily
b2f7417365 Merge pull request #2559 from whooo/295-fix
Add descriptive TV episode titles for DLNA browsing

(cherry picked from commit a37b69a493)
Signed-off-by: Joshua M. Boniface <joshua@boniface.me>
2020-04-05 12:51:29 -04:00
Vasily
bf0c07abfe Merge pull request #2503 from nyanmisaka/vaapi
Fix various bugs in HWA subtitle burn-in

(cherry picked from commit 9aefb41512)
Signed-off-by: Joshua M. Boniface <joshua@boniface.me>
2020-04-05 12:51:08 -04:00
Bond-009
3a4cd01b13 Merge pull request #2740 from JustAMan/fix-livetv
Fix GetLocalApiUrl for cases with https enabled

(cherry picked from commit b3283e37f2)
Signed-off-by: Joshua M. Boniface <joshua@boniface.me>
2020-04-05 12:44:41 -04:00
Anthony Lavado
7059761806 Merge pull request #2730 from Bond-009/plugincrash
Try to not crash on unsupported plugin load

(cherry picked from commit c9919f4633)
Signed-off-by: Joshua M. Boniface <joshua@boniface.me>
2020-04-05 12:44:12 -04:00
dkanada
2cde59af44 Merge pull request #2723 from jairbubbles/updateSkiaForArm
Update Jellyfin.SkiaSharp.NativeAssets.LinuxArm to version 1.68.1

(cherry picked from commit 58900bb57e)
Signed-off-by: Joshua M. Boniface <joshua@boniface.me>
2020-04-05 12:43:51 -04:00
Anthony Lavado
52a850cc9b Merge pull request #2720 from dkanada/music
Fix custom musicbrainz servers

(cherry picked from commit 8c8a396cd6)
Signed-off-by: Joshua M. Boniface <joshua@boniface.me>
2020-04-05 12:43:32 -04:00
dkanada
54435a1243 Merge pull request #2712 from joshuaboniface/fix-ldap-issues
Revert #2146 ordering change

(cherry picked from commit 9e82e7c847)
Signed-off-by: Joshua M. Boniface <joshua@boniface.me>
2020-04-05 12:42:56 -04:00
Bond-009
899be44388 Merge pull request #2674 from JustAMan/fix-attachments-scan
Make variables binding correspond with column names

(cherry picked from commit 10050a55a5)
Signed-off-by: Joshua M. Boniface <joshua@boniface.me>
2020-04-05 12:42:30 -04:00
dkanada
354079862e Merge pull request #2668 from mark-monteiro/fix-application-host-dispose
Fix ApplicationHost Dispose() method

(cherry picked from commit 622106467e)
Signed-off-by: Joshua M. Boniface <joshua@boniface.me>
2020-04-05 12:41:53 -04:00
Bond-009
01db9af821 Merge pull request #2655 from lfoust/tvdbruntimefix
Fix FormatException when mapping TVDB series

(cherry picked from commit 632323969f)
Signed-off-by: Joshua M. Boniface <joshua@boniface.me>
2020-04-05 12:41:28 -04:00
Bond-009
9da2635d86 Merge pull request #2653 from iwalton3/fix-embedded-subtitles
Fix embedded mkv subtitles.

(cherry picked from commit 1345e699fa)
Signed-off-by: Joshua M. Boniface <joshua@boniface.me>
2020-04-05 12:40:59 -04:00
Joshua M. Boniface
4caa597cde Bump version to 10.5.3
Include quick robustness fix in Docker container clone too.
2020-04-05 12:40:29 -04:00
dkanada
da34bd940e Merge pull request #2478 from JustAMan/fix-search-order
Fix ordering of search results

(cherry picked from commit 0d9787dfb4)
Signed-off-by: Joshua M. Boniface <joshua@boniface.me>
2020-03-22 15:25:11 -04:00
Joshua M. Boniface
54efde4073 Merge pull request #2642 from mark-monteiro/fix-extras
Add missing null check when retrieving extras

(cherry picked from commit 425bd2b01b)
Signed-off-by: Joshua M. Boniface <joshua@boniface.me>
2020-03-22 15:24:54 -04:00
Joshua M. Boniface
02cfa15582 Quickly fix failing curl in ARM Dockerfile 2020-03-22 15:02:03 -04:00
Joshua M. Boniface
0e171d794c Bump version to 10.5.2 2020-03-22 12:13:59 -04:00
dkanada
d5f2384375 Merge pull request #2617 from Shawmon/wasm-mimetype
add wasm mimetype

(cherry picked from commit af5d3e8eae)
Signed-off-by: Joshua M. Boniface <joshua@boniface.me>
2020-03-22 11:48:18 -04:00
dkanada
816b3f5c2e Merge pull request #2622 from Artiume/patch-5
Fix Release 10.5.z
2020-03-21 16:26:16 +09:00
artiume
a234388552 Fix arm64 2020-03-17 10:34:25 -04:00
artiume
30d38bd5c6 Update armhf 2020-03-17 10:33:06 -04:00
artiume
9f49fe2e99 Fix Release 10.5.z 2020-03-17 09:38:22 -04:00
Joshua M. Boniface
f720a0fca2 Fix mangled Dockerfiles 2020-03-16 09:30:01 -04:00
Joshua M. Boniface
aadff77531 Merge pull request #2607 from joshuaboniface/fix-fedora
Correct BuildRequires and NodeJS for Fedora/CentOS

(cherry picked from commit ef4dfd4461)
Signed-off-by: Joshua M. Boniface <joshua@boniface.me>
2020-03-15 23:06:22 -04:00
dkanada
89fc5aa11a Merge pull request #2582 from Bond-009/subs
Fix subtitles

(cherry picked from commit 6960f0af67)
Signed-off-by: Joshua M. Boniface <joshua@boniface.me>
2020-03-15 23:05:41 -04:00
dkanada
cb91595a24 Merge pull request #2541 from joshuaboniface/fix-docker-arm
Fix curl for Jellyfin GPG key

(cherry picked from commit bf34105af3)
Signed-off-by: Joshua M. Boniface <joshua@boniface.me>
2020-03-15 23:05:13 -04:00
Joshua M. Boniface
6233bae7f0 Bump version to 10.5.1 2020-03-15 23:04:07 -04:00
1540 changed files with 19184 additions and 33777 deletions

View File

@@ -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)

View File

@@ -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"

View File

@@ -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'

View File

@@ -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

View 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"

View File

@@ -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"

View File

@@ -1 +0,0 @@
../fedora/Makefile

59
.copr/Makefile Normal file
View 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)"

View File

@@ -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
View File

@@ -1,3 +0,0 @@
# Joshua must review all changes to deployment and build.sh
deployment/* @joshuaboniface
build.sh @joshuaboniface

View File

@@ -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.

View File

@@ -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
View File

@@ -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

View File

@@ -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
View File

@@ -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
View File

@@ -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"
}
}
}
]
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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;
}
}
}

View File

@@ -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>

View File

@@ -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)

View File

@@ -1,5 +1,3 @@
#pragma warning disable CS1591
using System.IO;
namespace DvdLib.Ifo

View File

@@ -1,5 +1,3 @@
#pragma warning disable CS1591
using System.IO;
namespace DvdLib.Ifo

View File

@@ -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)

View File

@@ -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);
}
}
}

View File

@@ -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)

View File

@@ -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)
{

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -1,5 +1,3 @@
#pragma warning disable CS1591
using System;
namespace DvdLib.Ifo

View File

@@ -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);

View File

@@ -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();

View File

@@ -1,4 +1,5 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
namespace Emby.Dlna.Common
{

View File

@@ -1,4 +1,5 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System.Globalization;

View File

@@ -1,4 +1,5 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
namespace Emby.Dlna.Common
{

View File

@@ -1,4 +1,5 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System.Collections.Generic;

View File

@@ -1,4 +1,5 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System;

View File

@@ -1,4 +1,5 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
namespace Emby.Dlna.Configuration
{

View File

@@ -1,5 +1,5 @@
#nullable enable
#pragma warning disable CS1591
#pragma warning disable SA1600
using System.Collections.Generic;
using Emby.Dlna.Configuration;

View File

@@ -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;

View File

@@ -1,4 +1,5 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System.Collections.Generic;
using Emby.Dlna.Common;

View File

@@ -1,4 +1,5 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System;
using System.Collections.Generic;

View File

@@ -1,4 +1,5 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System.Collections.Generic;
using Emby.Dlna.Common;

View File

@@ -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;
}
}
}

View File

@@ -1,4 +1,5 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System.Collections.Generic;
using Emby.Dlna.Common;

View File

@@ -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)

View File

@@ -1,4 +1,5 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System.Collections.Generic;
using Emby.Dlna.Common;

View File

@@ -1,4 +1,5 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System.IO;
using Microsoft.AspNetCore.Http;

View File

@@ -1,4 +1,5 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System.Collections.Generic;

View File

@@ -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,

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}

View File

@@ -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(),

View File

@@ -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>

View File

@@ -1,4 +1,5 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System.Collections.Generic;

View File

@@ -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)

View File

@@ -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; }

View File

@@ -1,4 +1,5 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
namespace Emby.Dlna
{

View File

@@ -1,4 +1,5 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
namespace Emby.Dlna
{

View File

@@ -1,4 +1,5 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
namespace Emby.Dlna
{

View File

@@ -1,4 +1,5 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
namespace Emby.Dlna
{

View File

@@ -1,4 +1,5 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System.Threading.Tasks;

View File

@@ -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;
}
}
}

View File

@@ -1,4 +1,5 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System;
using System.Collections.Generic;

View File

@@ -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;

View File

@@ -1,4 +1,5 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System.Collections.Generic;
using Emby.Dlna.Common;

View File

@@ -1,4 +1,5 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System.Collections.Generic;
using Emby.Dlna.Common;

View File

@@ -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);

View File

@@ -1,4 +1,5 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System.Collections.Generic;
using Emby.Dlna.Common;

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -1,4 +1,5 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System;

View File

@@ -1,4 +1,5 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System;

View File

@@ -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; }
}
}

View File

@@ -1,4 +1,5 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using MediaBrowser.Model.Dlna;

View File

@@ -1,4 +1,5 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System.IO;
using System.Linq;

View File

@@ -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] != '\"')

View File

@@ -1,4 +1,5 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
namespace Emby.Dlna.PlayTo
{

View File

@@ -1,4 +1,5 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System;
using System.Collections.Generic;

View File

@@ -1,4 +1,5 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System;
using System.Xml.Linq;

View File

@@ -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;

View File

@@ -1,4 +1,5 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System.Xml.Linq;

View File

@@ -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

View File

@@ -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[] { };
}
}
}

View File

@@ -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[] { };
}
}
}

View File

@@ -1,4 +1,5 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using MediaBrowser.Model.Dlna;

View File

@@ -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[] { };
}
}
}

View File

@@ -1,4 +1,5 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using MediaBrowser.Model.Dlna;

View File

@@ -1,4 +1,5 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using MediaBrowser.Model.Dlna;

View File

@@ -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[] { };
}
}
}

View File

@@ -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[] { };
}
}
}

View File

@@ -1,4 +1,5 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using MediaBrowser.Model.Dlna;

View File

@@ -1,4 +1,5 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using MediaBrowser.Model.Dlna;

View File

@@ -1,4 +1,5 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using MediaBrowser.Model.Dlna;

View File

@@ -1,4 +1,5 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using MediaBrowser.Model.Dlna;

View File

@@ -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[] { };
}
}
}

View File

@@ -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[] { };
}
}
}

View File

@@ -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