Compare commits

..

1 Commits

Author SHA1 Message Date
Vasily 52c399d4d1 Add back taglib-sharp submodule
This fixes v10.1.0 build
2019-01-25 23:46:25 +03:00
2048 changed files with 89870 additions and 68614 deletions
-89
View File
@@ -1,89 +0,0 @@
parameters:
- name: Packages
type: object
default: {}
- name: LinuxImage
type: string
default: "ubuntu-latest"
- name: DotNetSdkVersion
type: string
default: 3.1.100
jobs:
- job: CompatibilityCheck
displayName: Compatibility Check
pool:
vmImage: "${{ parameters.LinuxImage }}"
# only execute for pull requests
condition: and(succeeded(), variables['System.PullRequest.PullRequestNumber'])
strategy:
matrix:
${{ each Package in parameters.Packages }}:
${{ Package.key }}:
NugetPackageName: ${{ Package.value.NugetPackageName }}
AssemblyFileName: ${{ Package.value.AssemblyFileName }}
maxParallel: 2
dependsOn: Build
steps:
- checkout: none
- task: UseDotNet@2
displayName: "Update DotNet"
inputs:
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:
source: "current"
artifact: "$(NugetPackageName)"
path: "$(System.ArtifactsDirectory)/new-artifacts"
runVersion: "latest"
- task: CopyFiles@2
displayName: "Copy New Assembly Build Artifact"
inputs:
sourceFolder: $(System.ArtifactsDirectory)/new-artifacts
contents: "**/*.dll"
targetFolder: $(System.ArtifactsDirectory)/new-release
cleanTargetFolder: true
overWrite: true
flattenFolders: true
- task: DownloadPipelineArtifact@2
displayName: "Download Reference Assembly Build Artifact"
inputs:
source: "specific"
artifact: "$(NugetPackageName)"
path: "$(System.ArtifactsDirectory)/current-artifacts"
project: "$(System.TeamProjectId)"
pipeline: "$(System.DefinitionId)"
runVersion: "latestFromBranch"
runBranch: "refs/heads/$(System.PullRequest.TargetBranch)"
- task: CopyFiles@2
displayName: "Copy Reference Assembly Build Artifact"
inputs:
sourceFolder: $(System.ArtifactsDirectory)/current-artifacts
contents: "**/*.dll"
targetFolder: $(System.ArtifactsDirectory)/current-release
cleanTargetFolder: true
overWrite: true
flattenFolders: true
# The `--warnings-only` switch will swallow the return code and not emit any errors.
- task: DotNetCoreCLI@2
displayName: 'Execute ABI Compatibility Check Tool'
inputs:
command: custom
custom: compat
arguments: 'current-release/$(AssemblyFileName) new-release/$(AssemblyFileName) --azure-pipelines --warnings-only'
workingDirectory: $(System.ArtifactsDirectory)
-93
View File
@@ -1,93 +0,0 @@
parameters:
LinuxImage: 'ubuntu-latest'
RestoreBuildProjects: 'Jellyfin.Server/Jellyfin.Server.csproj'
DotNetSdkVersion: 3.1.100
jobs:
- job: Build
displayName: Build
strategy:
matrix:
Release:
BuildConfiguration: Release
Debug:
BuildConfiguration: Debug
pool:
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')
inputs:
path: '$(Agent.TempDirectory)'
artifact: 'jellyfin-web-production'
source: 'specific'
project: 'jellyfin'
pipeline: 'Jellyfin Web'
runBranch: variables['Build.SourceBranch']
- task: DownloadPipelineArtifact@2
displayName: 'Download Web Target'
condition: eq(variables['Build.Reason'], 'PullRequest')
inputs:
path: '$(Agent.TempDirectory)'
artifact: 'jellyfin-web-production'
source: 'specific'
project: 'jellyfin'
pipeline: 'Jellyfin Web'
runBranch: variables['System.PullRequest.TargetBranch']
- task: ExtractFiles@1
displayName: 'Extract Web Client'
inputs:
archiveFilePatterns: '$(Agent.TempDirectory)/*.zip'
destinationFolder: '$(Build.SourcesDirectory)/MediaBrowser.WebDashboard'
cleanDestinationFolder: false
- task: UseDotNet@2
displayName: 'Update DotNet'
inputs:
packageType: sdk
version: ${{ parameters.DotNetSdkVersion }}
- task: DotNetCoreCLI@2
displayName: 'Publish Server'
inputs:
command: publish
publishWebProjects: false
projects: '${{ parameters.RestoreBuildProjects }}'
arguments: '--configuration $(BuildConfiguration) --output $(Build.ArtifactStagingDirectory)'
zipAfterPublish: false
- task: PublishPipelineArtifact@0
displayName: 'Publish Artifact Naming'
condition: and(succeeded(), eq(variables['BuildConfiguration'], 'Release'))
inputs:
targetPath: '$(build.ArtifactStagingDirectory)/Jellyfin.Server/Emby.Naming.dll'
artifactName: 'Jellyfin.Naming'
- task: PublishPipelineArtifact@0
displayName: 'Publish Artifact Controller'
condition: and(succeeded(), eq(variables['BuildConfiguration'], 'Release'))
inputs:
targetPath: '$(build.ArtifactStagingDirectory)/Jellyfin.Server/MediaBrowser.Controller.dll'
artifactName: 'Jellyfin.Controller'
- task: PublishPipelineArtifact@0
displayName: 'Publish Artifact Model'
condition: and(succeeded(), eq(variables['BuildConfiguration'], 'Release'))
inputs:
targetPath: '$(build.ArtifactStagingDirectory)/Jellyfin.Server/MediaBrowser.Model.dll'
artifactName: 'Jellyfin.Model'
- task: PublishPipelineArtifact@0
displayName: 'Publish Artifact Common'
condition: and(succeeded(), eq(variables['BuildConfiguration'], 'Release'))
inputs:
targetPath: '$(build.ArtifactStagingDirectory)/Jellyfin.Server/MediaBrowser.Common.dll'
artifactName: 'Jellyfin.Common'
-163
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'
-93
View File
@@ -1,93 +0,0 @@
parameters:
- name: ImageNames
type: object
default:
Linux: "ubuntu-latest"
Windows: "windows-latest"
macOS: "macos-latest"
- name: TestProjects
type: string
default: "tests/**/*Tests.csproj"
- name: DotNetSdkVersion
type: string
default: 3.1.100
jobs:
- job: Test
displayName: Test
strategy:
matrix:
${{ each imageName in parameters.ImageNames }}:
${{ imageName.key }}:
ImageName: ${{ imageName.value }}
pool:
vmImage: "$(ImageName)"
steps:
- checkout: self
clean: true
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'
inputs:
command: "test"
projects: ${{ parameters.TestProjects }}
arguments: '--configuration Release --collect:"XPlat Code Coverage" --settings tests/coverletArgs.runsettings --verbosity minimal "-p:GenerateDocumentationFile=False"'
publishTestResults: true
testRunTitle: $(Agent.JobName)
workingDirectory: "$(Build.SourcesDirectory)"
- task: 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
inputs:
reports: "$(Agent.TempDirectory)/**/coverage.cobertura.xml"
targetdir: "$(Agent.TempDirectory)/merged/"
reporttypes: "Cobertura"
## V2 is already in the repository but it does not work "wrong number of segments" YAML error.
- task: PublishCodeCoverageResults@1
condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux')) # !! THIS is for V1 only V2 will/should support merging
displayName: 'Publish Code Coverage'
enabled: false
inputs:
codeCoverageTool: "cobertura"
#summaryFileLocation: '$(Agent.TempDirectory)/**/coverage.cobertura.xml' # !!THIS IS FOR V2
summaryFileLocation: "$(Agent.TempDirectory)/merged/**.xml"
pathToSources: $(Build.SourcesDirectory)
failIfCoverageEmpty: true
-51
View File
@@ -1,51 +0,0 @@
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
pr:
autoCancel: true
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'
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'
- ${{ if not(or(startsWith(variables['Build.SourceBranch'], 'refs/tags'), startsWith(variables['Build.SourceBranch'], 'refs/heads/master'))) }}:
- template: azure-pipelines-abi.yml
parameters:
Packages:
Naming:
NugetPackageName: Jellyfin.Naming
AssemblyFileName: Emby.Naming.dll
Controller:
NugetPackageName: Jellyfin.Controller
AssemblyFileName: MediaBrowser.Controller.dll
Model:
NugetPackageName: Jellyfin.Model
AssemblyFileName: MediaBrowser.Model.dll
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
-1
View File
@@ -1 +0,0 @@
../fedora/Makefile
-1
View File
@@ -8,4 +8,3 @@ README.md
deployment/*/dist deployment/*/dist
deployment/*/pkg-dist deployment/*/pkg-dist
deployment/collect-dist/ deployment/collect-dist/
ci/
+2 -20
View File
@@ -1,30 +1,12 @@
---
kind: pipeline kind: pipeline
name: build-debug name: build
steps: steps:
- name: submodules - name: submodules
image: docker:git image: docker:git
commands: commands:
- git submodule update --init --recursive - git submodule update --init --recursive
- name: build - name: build
image: microsoft/dotnet:2-sdk image: microsoft/dotnet:2-sdk
commands: commands:
- dotnet publish "Jellyfin.Server" --configuration Debug --output "../ci/ci-debug" - dotnet publish --configuration release --output /release Jellyfin.Server
---
kind: pipeline
name: build-release
steps:
- name: submodules
image: docker:git
commands:
- git submodule update --init --recursive
- name: build
image: microsoft/dotnet:2-sdk
commands:
- dotnet publish "Jellyfin.Server" --configuration Release --output "../ci/ci-release"
+18 -81
View File
@@ -13,16 +13,11 @@ charset = utf-8
trim_trailing_whitespace = true trim_trailing_whitespace = true
insert_final_newline = true insert_final_newline = true
end_of_line = lf end_of_line = lf
max_line_length = off max_line_length = null
# YAML indentation
[*.{yml,yaml}]
indent_size = 2
# XML indentation # XML indentation
[*.{csproj,xml}] [*.{csproj,xml}]
indent_size = 2 indent_size = 2
############################### ###############################
# .NET Coding Conventions # # .NET Coding Conventions #
############################### ###############################
@@ -52,86 +47,23 @@ dotnet_style_explicit_tuple_names = true:suggestion
dotnet_style_null_propagation = true:suggestion dotnet_style_null_propagation = true:suggestion
dotnet_style_coalesce_expression = true:suggestion dotnet_style_coalesce_expression = true:suggestion
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent dotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent
dotnet_style_prefer_inferred_tuple_names = true:suggestion dotnet_prefer_inferred_tuple_names = true:suggestion
dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion dotnet_prefer_inferred_anonymous_type_member_names = true:suggestion
dotnet_style_prefer_auto_properties = true:silent dotnet_style_prefer_auto_properties = true:silent
dotnet_style_prefer_conditional_expression_over_assignment = true:silent dotnet_style_prefer_conditional_expression_over_assignment = true:silent
dotnet_style_prefer_conditional_expression_over_return = true:silent dotnet_style_prefer_conditional_expression_over_return = true:silent
############################### ###############################
# Naming Conventions # # Naming Conventions #
############################### ###############################
# Style Definitions (From Roslyn) # Style Definitions
dotnet_naming_style.pascal_case_style.capitalization = pascal_case
# Non-private static fields are PascalCase # Use PascalCase for constant fields
dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.severity = suggestion dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.symbols = non_private_static_fields dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields
dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.style = non_private_static_field_style dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style
dotnet_naming_symbols.constant_fields.applicable_kinds = field
dotnet_naming_symbols.non_private_static_fields.applicable_kinds = field dotnet_naming_symbols.constant_fields.applicable_accessibilities = *
dotnet_naming_symbols.non_private_static_fields.applicable_accessibilities = public, protected, internal, protected_internal, private_protected dotnet_naming_symbols.constant_fields.required_modifiers = const
dotnet_naming_symbols.non_private_static_fields.required_modifiers = static
dotnet_naming_style.non_private_static_field_style.capitalization = pascal_case
# Constants are PascalCase
dotnet_naming_rule.constants_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.constants_should_be_pascal_case.symbols = constants
dotnet_naming_rule.constants_should_be_pascal_case.style = constant_style
dotnet_naming_symbols.constants.applicable_kinds = field, local
dotnet_naming_symbols.constants.required_modifiers = const
dotnet_naming_style.constant_style.capitalization = pascal_case
# Static fields are camelCase and start with s_
dotnet_naming_rule.static_fields_should_be_camel_case.severity = suggestion
dotnet_naming_rule.static_fields_should_be_camel_case.symbols = static_fields
dotnet_naming_rule.static_fields_should_be_camel_case.style = static_field_style
dotnet_naming_symbols.static_fields.applicable_kinds = field
dotnet_naming_symbols.static_fields.required_modifiers = static
dotnet_naming_style.static_field_style.capitalization = camel_case
dotnet_naming_style.static_field_style.required_prefix = _
# Instance fields are camelCase and start with _
dotnet_naming_rule.instance_fields_should_be_camel_case.severity = suggestion
dotnet_naming_rule.instance_fields_should_be_camel_case.symbols = instance_fields
dotnet_naming_rule.instance_fields_should_be_camel_case.style = instance_field_style
dotnet_naming_symbols.instance_fields.applicable_kinds = field
dotnet_naming_style.instance_field_style.capitalization = camel_case
dotnet_naming_style.instance_field_style.required_prefix = _
# Locals and parameters are camelCase
dotnet_naming_rule.locals_should_be_camel_case.severity = suggestion
dotnet_naming_rule.locals_should_be_camel_case.symbols = locals_and_parameters
dotnet_naming_rule.locals_should_be_camel_case.style = camel_case_style
dotnet_naming_symbols.locals_and_parameters.applicable_kinds = parameter, local
dotnet_naming_style.camel_case_style.capitalization = camel_case
# Local functions are PascalCase
dotnet_naming_rule.local_functions_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.local_functions_should_be_pascal_case.symbols = local_functions
dotnet_naming_rule.local_functions_should_be_pascal_case.style = local_function_style
dotnet_naming_symbols.local_functions.applicable_kinds = local_function
dotnet_naming_style.local_function_style.capitalization = pascal_case
# By default, name items with PascalCase
dotnet_naming_rule.members_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.members_should_be_pascal_case.symbols = all_members
dotnet_naming_rule.members_should_be_pascal_case.style = pascal_case_style
dotnet_naming_symbols.all_members.applicable_kinds = *
dotnet_naming_style.pascal_case_style.capitalization = pascal_case
############################### ###############################
# C# Coding Conventions # # C# Coding Conventions #
############################### ###############################
@@ -161,7 +93,6 @@ csharp_style_deconstructed_variable_declaration = true:suggestion
csharp_prefer_simple_default_expression = true:suggestion csharp_prefer_simple_default_expression = true:suggestion
csharp_style_pattern_local_over_anonymous_function = true:suggestion csharp_style_pattern_local_over_anonymous_function = true:suggestion
csharp_style_inlined_variable_declaration = true:suggestion csharp_style_inlined_variable_declaration = true:suggestion
############################### ###############################
# C# Formatting Rules # # C# Formatting Rules #
############################### ###############################
@@ -192,3 +123,9 @@ csharp_space_between_method_call_empty_parameter_list_parentheses = false
# Wrapping preferences # Wrapping preferences
csharp_preserve_single_line_statements = true csharp_preserve_single_line_statements = true
csharp_preserve_single_line_blocks = 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
-4
View File
@@ -1,5 +1 @@
* text=auto eol=lf
*.png binary
*.jpg binary
CONTRIBUTORS.md merge=union CONTRIBUTORS.md merge=union
-3
View File
@@ -1,3 +0,0 @@
# Joshua must review all changes to deployment and build.sh
deployment/* @joshuaboniface
build.sh @joshuaboniface
+11 -19
View File
@@ -8,36 +8,28 @@ assignees: ''
--- ---
**Describe the bug** **Describe the bug**
<!-- A clear and concise description of what the bug is. --> A clear and concise description of what the bug is.
**System (please complete the following information):**
- OS: [e.g. Debian, Windows]
- Virtualization: [e.g. Docker, KVM, LXC]
- Clients: [Browser, Android, Fire Stick, etc.]
- Browser: [e.g. Firefox 72, Chrome 80, Safari 13]
- Jellyfin Version: [e.g. 10.4.3, nightly 20191231]
- Playback: [Direct Play, Remux, Direct Stream, Transcode]
- Installed Plugins: [e.g. none, Fanart, Anime, etc.]
- Reverse Proxy: [e.g. none, nginx, apache, etc.]
- Base URL: [e.g. none, yes: /example]
- Networking: [e.g. Host, Bridge/NAT]
- Storage: [e.g. local, NFS, cloud]
**To Reproduce** **To Reproduce**
<!-- Steps to reproduce the behavior: --> Steps to reproduce the behavior:
1. Go to '...' 1. Go to '...'
2. Click on '....' 2. Click on '....'
3. Scroll down to '....' 3. Scroll down to '....'
4. See error 4. See error
**Expected behavior** **Expected behavior**
<!-- A clear and concise description of what you expected to happen. --> A clear and concise description of what you expected to happen.
**Logs** **Logs**
<!-- Please paste any log errors. --> Please paste any log errors.
**Screenshots** **Screenshots**
<!-- If applicable, add screenshots to help explain your problem. --> If applicable, add screenshots to help explain your problem.
**System (please complete the following information):**
- OS: [e.g. Docker, Debian, Windows]
- Browser: [e.g. Firefox, Chrome, Safari]
- Jellyfin Version: [e.g. 10.0.1]
**Additional context** **Additional context**
<!-- Add any other context about the problem here. --> Add any other context about the problem here.
@@ -0,0 +1,20 @@
---
name: Enhancement request
about: Suggest an modification to an existing feature
title: ''
labels: enhancement
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
+8 -7
View File
@@ -1,13 +1,14 @@
--- ---
name: Feature Request name: Feature request
about: Request a new feature about: Suggest a new feature
title: '' title: ''
labels: feature-request labels: feature
assignees: '' assignees: ''
--- ---
**PLEASE DO NOT OPEN FEATURE REQUEST ISSUES ON GITHUB** **Describe the feature you'd like**
A clear and concise description of what you want to happen.
**Feature requests should be opened on our dedicated [feature request](https://features.jellyfin.org/) hub so they can be appropriately discussed and prioritized.** **Additional context**
Add any other context or screenshots about the feature request here.
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.
-34
View File
@@ -1,34 +0,0 @@
---
name: Media playback issue
about: Create a media playback issue report
title: ''
labels: mediaplayback
assignees: ''
---
**Media Info of the file**
<!-- Use the Media Info tool (set to text format, download here: https://mediaarea.net/en/MediaInfo) or copy the info from the web ui for the file with the playback issue. -->
**Logs**
<!-- Please paste any log messages from during the playback issue. -->
**FFmpeg Logs**
<!-- Please paste any FFmpeg logs if remuxing or transcoding appears to be part of the issue. -->
**Stats for Nerds Screenshots**
<!-- If available, add screenshots of the stats for nerds screen to help show the issue problem. -->
**Server System (please complete the following information):**
- OS: [e.g. Docker on Linux, Docker on Windows, Debian, Windows]
- Jellyfin Version: [e.g. 10.0.1]
- Hardware settings & device: [e.g. NVENC on GTX1060, VAAPI on Intel i7 8700K]
- Reverse proxy: [e.g. no, nginx, apache, etc.]
- Other hardware notes: [e.g. Media mounted in CIFS/SMB share, Media mounted from Google Drive]
**Client System (please complete the following information):**
- Device: [e.g. Apple iPhone XS, Xbox One S, LG OLED55C8, Samsung Galaxy Note9, Custom HTPC]
- OS: [e.g. iOS, Android, Windows, macOS]
- Client: [e.g. Web/Browser, webOS, Android, Android TV, Electron]
- Browser (if Web client): [e.g. Firefox, Chrome, Safari]
- Client and Browser Version: [e.g. 10.3.4 and 68.0]
-9
View File
@@ -1,9 +0,0 @@
version: 2
updates:
- package-ecosystem: nuget
directory: "/"
schedule:
interval: weekly
time: '12:00'
open-pull-requests-limit: 10
+4 -6
View File
@@ -1,11 +1,9 @@
<!--
Ensure your title is short, descriptive, and in the imperative mood (Fix X, Change Y, instead of Fixed X, Changed Y). Ensure your title is short, descriptive, and in the imperative mood (Fix X, Change Y, instead of Fixed X, Changed Y).
For a good inspiration of what to write in commit messages and PRs please review https://chris.beams.io/posts/git-commit/ and our documentation. For a good inspiration of what to write in commit messages and PRs please review https://chris.beams.io/posts/git-commit/ and our https://jellyfin.readthedocs.io/en/latest/developer-docs/contributing/ page.
-->
**Changes** **Changes**
<!-- Describe your changes here in 1-5 sentences. --> Describe your changes here in 1-5 sentences.
**Issues** **Issues**
<!-- Tag any issues that this PR solves here. Tag any issues that this PR solves here.
ex. Fixes # --> Fixes #
-25
View File
@@ -1,25 +0,0 @@
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 120
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 21
# Issues with these labels will never be considered stale
exemptLabels:
- regression
- security
- dotnet-3.0-future
- roadmap
- future
- feature
- enhancement
- confirmed
# Label to use when marking an issue as stale
staleLabel: stale
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
This issue has gone 120 days without comment. To avoid abandoned issues, it will be closed in 21 days if there are no new comments.
If you're the original submitter of this issue, please comment confirming if this issue still affects you in the latest release or nightlies, or close the issue if it has been fixed. If you're another user also affected by this bug, please comment confirming so. Either action will remove the stale label.
This bot exists to prevent issues from becoming stale and forgotten. Jellyfin is always moving forward, and bugs are often fixed as side effects of other changes. We therefore ask that bug report authors remain vigilant about their issues to ensure they are closed if fixed, or re-confirmed - perhaps with fresh logs or reproduction examples - regularly. If you have any questions you can reach us on [Matrix or Social Media](https://docs.jellyfin.org/general/getting-help.html).
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false
+12 -24
View File
@@ -239,19 +239,24 @@ pip-log.txt
########## ##########
.idea/ .idea/
##########
# Visual Studio Code
##########
.vscode/
######################### #########################
# Build artifacts # Build artifacts
######################### #########################
# Artifacts for debian-x64 # Artifacts for debian-x64
debian/.debhelper/ deployment/debian-package-x64/pkg-src/.debhelper/
debian/*.debhelper deployment/debian-package-x64/pkg-src/*.debhelper
debian/debhelper-build-stamp deployment/debian-package-x64/pkg-src/debhelper-build-stamp
debian/files deployment/debian-package-x64/pkg-src/files
debian/jellyfin.substvars deployment/debian-package-x64/pkg-src/jellyfin.substvars
debian/jellyfin/ deployment/debian-package-x64/pkg-src/jellyfin/
# Don't ignore the debian/bin folder # Don't ignore the debian/bin folder
!debian/bin/ !deployment/debian-package-x64/pkg-src/bin/
deployment/**/dist/ deployment/**/dist/
deployment/**/pkg-dist/ deployment/**/pkg-dist/
@@ -259,20 +264,3 @@ deployment/**/pkg-dist-tmp/
deployment/collect-dist/ deployment/collect-dist/
jellyfin_version.ini jellyfin_version.ini
ci/
# Doxygen
doc/
# Deployment artifacts
dist
*.exe
# BenchmarkDotNet artifacts
BenchmarkDotNet.Artifacts
# Ignore web artifacts from native builds
web/
web-src.*
MediaBrowser.WebDashboard/jellyfin-web
+6
View File
@@ -0,0 +1,6 @@
[submodule "ThirdParty/taglib-sharp"]
path = ThirdParty/taglib-sharp
url = https://github.com/mono/taglib-sharp
[submodule "MediaBrowser.WebDashboard/jellyfin-web"]
path = MediaBrowser.WebDashboard/jellyfin-web
url = https://github.com/jellyfin/jellyfin-web.git
-14
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": [
]
}
+8 -8
View File
@@ -1,13 +1,16 @@
{ {
"version": "0.2.0", // Use IntelliSense to find out which attributes exist for C# debugging
"configurations": [ // 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)", "name": ".NET Core Launch (console)",
"type": "coreclr", "type": "coreclr",
"request": "launch", "request": "launch",
"preLaunchTask": "build", "preLaunchTask": "build",
// If you have changed target frameworks, make sure to update the program path. // If you have changed target frameworks, make sure to update the program path.
"program": "${workspaceFolder}/Jellyfin.Server/bin/Debug/netcoreapp3.1/jellyfin.dll", "program": "${workspaceFolder}/Jellyfin.Server/bin/Debug/netcoreapp2.1/jellyfin.dll",
"args": [], "args": [],
"cwd": "${workspaceFolder}/Jellyfin.Server", "cwd": "${workspaceFolder}/Jellyfin.Server",
// For more information about the 'console' field, see https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md#console-terminal-window // For more information about the 'console' field, see https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md#console-terminal-window
@@ -21,8 +24,5 @@
"request": "attach", "request": "attach",
"processId": "${command:pickProcess}" "processId": "${command:pickProcess}"
} }
], ,]
"env": { }
"DOTNET_CLI_TELEMETRY_OPTOUT": "1"
}
}
+2 -17
View File
@@ -10,21 +10,6 @@
"${workspaceFolder}/Jellyfin.Server/Jellyfin.Server.csproj" "${workspaceFolder}/Jellyfin.Server/Jellyfin.Server.csproj"
], ],
"problemMatcher": "$msCompile" "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"
}
}
}
+16
View File
@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<Compile Include="..\SharedVersion.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" />
</ItemGroup>
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
</Project>
+33
View File
@@ -0,0 +1,33 @@
namespace BDInfo
{
class BDInfoSettings
{
public static bool GenerateStreamDiagnostics => true;
public static bool EnableSSIF => true;
public static bool AutosaveReport => false;
public static bool GenerateFrameDataFile => false;
public static bool FilterLoopingPlaylists => true;
public static bool FilterShortPlaylists => false;
public static int FilterShortPlaylistsValue => 0;
public static bool UseImagePrefix => false;
public static string UseImagePrefixValue => null;
/// <summary>
/// Setting this to false throws an IComparer error on some discs.
/// </summary>
public static bool KeepStreamOrder => true;
public static bool GenerateTextSummary => false;
public static string LastPath => string.Empty;
}
}
+456
View File
@@ -0,0 +1,456 @@
//============================================================================
// BDInfo - Blu-ray Video and Audio Analysis Tool
// Copyright © 2010 Cinema Squid
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//=============================================================================
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using MediaBrowser.Model.IO;
namespace BDInfo
{
public class BDROM
{
public FileSystemMetadata DirectoryRoot = null;
public FileSystemMetadata DirectoryBDMV = null;
public FileSystemMetadata DirectoryBDJO = null;
public FileSystemMetadata DirectoryCLIPINF = null;
public FileSystemMetadata DirectoryPLAYLIST = null;
public FileSystemMetadata DirectorySNP = null;
public FileSystemMetadata DirectorySSIF = null;
public FileSystemMetadata DirectorySTREAM = null;
public string VolumeLabel = null;
public ulong Size = 0;
public bool IsBDPlus = false;
public bool IsBDJava = false;
public bool IsDBOX = false;
public bool IsPSP = false;
public bool Is3D = false;
public bool Is50Hz = false;
private readonly IFileSystem _fileSystem;
public Dictionary<string, TSPlaylistFile> PlaylistFiles =
new Dictionary<string, TSPlaylistFile>();
public Dictionary<string, TSStreamClipFile> StreamClipFiles =
new Dictionary<string, TSStreamClipFile>();
public Dictionary<string, TSStreamFile> StreamFiles =
new Dictionary<string, TSStreamFile>();
public Dictionary<string, TSInterleavedFile> InterleavedFiles =
new Dictionary<string, TSInterleavedFile>();
public delegate bool OnStreamClipFileScanError(
TSStreamClipFile streamClipFile, Exception ex);
public event OnStreamClipFileScanError StreamClipFileScanError;
public delegate bool OnStreamFileScanError(
TSStreamFile streamClipFile, Exception ex);
public event OnStreamFileScanError StreamFileScanError;
public delegate bool OnPlaylistFileScanError(
TSPlaylistFile playlistFile, Exception ex);
public event OnPlaylistFileScanError PlaylistFileScanError;
public BDROM(string path, IFileSystem fileSystem)
{
if (string.IsNullOrEmpty(path))
{
throw new ArgumentNullException(nameof(path));
}
_fileSystem = fileSystem;
//
// Locate BDMV directories.
//
DirectoryBDMV =
GetDirectoryBDMV(path);
if (DirectoryBDMV == null)
{
throw new Exception("Unable to locate BD structure.");
}
DirectoryRoot =
_fileSystem.GetDirectoryInfo(_fileSystem.GetDirectoryName(DirectoryBDMV.FullName));
DirectoryBDJO =
GetDirectory("BDJO", DirectoryBDMV, 0);
DirectoryCLIPINF =
GetDirectory("CLIPINF", DirectoryBDMV, 0);
DirectoryPLAYLIST =
GetDirectory("PLAYLIST", DirectoryBDMV, 0);
DirectorySNP =
GetDirectory("SNP", DirectoryRoot, 0);
DirectorySTREAM =
GetDirectory("STREAM", DirectoryBDMV, 0);
DirectorySSIF =
GetDirectory("SSIF", DirectorySTREAM, 0);
if (DirectoryCLIPINF == null
|| DirectoryPLAYLIST == null)
{
throw new Exception("Unable to locate BD structure.");
}
//
// Initialize basic disc properties.
//
VolumeLabel = GetVolumeLabel(DirectoryRoot);
Size = (ulong)GetDirectorySize(DirectoryRoot);
if (null != GetDirectory("BDSVM", DirectoryRoot, 0))
{
IsBDPlus = true;
}
if (null != GetDirectory("SLYVM", DirectoryRoot, 0))
{
IsBDPlus = true;
}
if (null != GetDirectory("ANYVM", DirectoryRoot, 0))
{
IsBDPlus = true;
}
if (DirectoryBDJO != null &&
_fileSystem.GetFilePaths(DirectoryBDJO.FullName).Any())
{
IsBDJava = true;
}
if (DirectorySNP != null &&
GetFilePaths(DirectorySNP.FullName, ".mnv").Any())
{
IsPSP = true;
}
if (DirectorySSIF != null &&
_fileSystem.GetFilePaths(DirectorySSIF.FullName).Any())
{
Is3D = true;
}
if (_fileSystem.FileExists(Path.Combine(DirectoryRoot.FullName, "FilmIndex.xml")))
{
IsDBOX = true;
}
//
// Initialize file lists.
//
if (DirectoryPLAYLIST != null)
{
FileSystemMetadata[] files = GetFiles(DirectoryPLAYLIST.FullName, ".mpls").ToArray();
foreach (var file in files)
{
PlaylistFiles.Add(
file.Name.ToUpper(), new TSPlaylistFile(this, file, _fileSystem));
}
}
if (DirectorySTREAM != null)
{
FileSystemMetadata[] files = GetFiles(DirectorySTREAM.FullName, ".m2ts").ToArray();
foreach (var file in files)
{
StreamFiles.Add(
file.Name.ToUpper(), new TSStreamFile(file, _fileSystem));
}
}
if (DirectoryCLIPINF != null)
{
FileSystemMetadata[] files = GetFiles(DirectoryCLIPINF.FullName, ".clpi").ToArray();
foreach (var file in files)
{
StreamClipFiles.Add(
file.Name.ToUpper(), new TSStreamClipFile(file, _fileSystem));
}
}
if (DirectorySSIF != null)
{
FileSystemMetadata[] files = GetFiles(DirectorySSIF.FullName, ".ssif").ToArray();
foreach (var file in files)
{
InterleavedFiles.Add(
file.Name.ToUpper(), new TSInterleavedFile(file));
}
}
}
private IEnumerable<FileSystemMetadata> GetFiles(string path, string extension)
{
return _fileSystem.GetFiles(path, new[] { extension }, false, false);
}
private IEnumerable<string> GetFilePaths(string path, string extension)
{
return _fileSystem.GetFilePaths(path, new[] { extension }, false, false);
}
public void Scan()
{
var errorStreamClipFiles = new List<TSStreamClipFile>();
foreach (var streamClipFile in StreamClipFiles.Values)
{
try
{
streamClipFile.Scan();
}
catch (Exception ex)
{
errorStreamClipFiles.Add(streamClipFile);
if (StreamClipFileScanError != null)
{
if (StreamClipFileScanError(streamClipFile, ex))
{
continue;
}
else
{
break;
}
}
else throw;
}
}
foreach (var streamFile in StreamFiles.Values)
{
string ssifName = Path.GetFileNameWithoutExtension(streamFile.Name) + ".SSIF";
if (InterleavedFiles.ContainsKey(ssifName))
{
streamFile.InterleavedFile = InterleavedFiles[ssifName];
}
}
TSStreamFile[] streamFiles = new TSStreamFile[StreamFiles.Count];
StreamFiles.Values.CopyTo(streamFiles, 0);
Array.Sort(streamFiles, CompareStreamFiles);
var errorPlaylistFiles = new List<TSPlaylistFile>();
foreach (var playlistFile in PlaylistFiles.Values)
{
try
{
playlistFile.Scan(StreamFiles, StreamClipFiles);
}
catch (Exception ex)
{
errorPlaylistFiles.Add(playlistFile);
if (PlaylistFileScanError != null)
{
if (PlaylistFileScanError(playlistFile, ex))
{
continue;
}
else
{
break;
}
}
else throw;
}
}
var errorStreamFiles = new List<TSStreamFile>();
foreach (var streamFile in streamFiles)
{
try
{
var playlists = new List<TSPlaylistFile>();
foreach (var playlist in PlaylistFiles.Values)
{
foreach (var streamClip in playlist.StreamClips)
{
if (streamClip.Name == streamFile.Name)
{
playlists.Add(playlist);
break;
}
}
}
streamFile.Scan(playlists, false);
}
catch (Exception ex)
{
errorStreamFiles.Add(streamFile);
if (StreamFileScanError != null)
{
if (StreamFileScanError(streamFile, ex))
{
continue;
}
else
{
break;
}
}
else throw;
}
}
foreach (var playlistFile in PlaylistFiles.Values)
{
playlistFile.Initialize();
if (!Is50Hz)
{
foreach (var videoStream in playlistFile.VideoStreams)
{
if (videoStream.FrameRate == TSFrameRate.FRAMERATE_25 ||
videoStream.FrameRate == TSFrameRate.FRAMERATE_50)
{
Is50Hz = true;
}
}
}
}
}
private FileSystemMetadata GetDirectoryBDMV(
string path)
{
if (string.IsNullOrEmpty(path))
{
throw new ArgumentNullException(nameof(path));
}
FileSystemMetadata dir = _fileSystem.GetDirectoryInfo(path);
while (dir != null)
{
if (string.Equals(dir.Name, "BDMV", StringComparison.OrdinalIgnoreCase))
{
return dir;
}
var parentFolder = _fileSystem.GetDirectoryName(dir.FullName);
if (string.IsNullOrEmpty(parentFolder))
{
dir = null;
}
else
{
dir = _fileSystem.GetDirectoryInfo(parentFolder);
}
}
return GetDirectory("BDMV", _fileSystem.GetDirectoryInfo(path), 0);
}
private FileSystemMetadata GetDirectory(
string name,
FileSystemMetadata dir,
int searchDepth)
{
if (dir != null)
{
FileSystemMetadata[] children = _fileSystem.GetDirectories(dir.FullName).ToArray();
foreach (var child in children)
{
if (string.Equals(child.Name, name, StringComparison.OrdinalIgnoreCase))
{
return child;
}
}
if (searchDepth > 0)
{
foreach (var child in children)
{
GetDirectory(
name, child, searchDepth - 1);
}
}
}
return null;
}
private long GetDirectorySize(FileSystemMetadata directoryInfo)
{
long size = 0;
//if (!ExcludeDirs.Contains(directoryInfo.Name.ToUpper())) // TODO: Keep?
{
FileSystemMetadata[] pathFiles = _fileSystem.GetFiles(directoryInfo.FullName).ToArray();
foreach (var pathFile in pathFiles)
{
if (pathFile.Extension.ToUpper() == ".SSIF")
{
continue;
}
size += pathFile.Length;
}
FileSystemMetadata[] pathChildren = _fileSystem.GetDirectories(directoryInfo.FullName).ToArray();
foreach (var pathChild in pathChildren)
{
size += GetDirectorySize(pathChild);
}
}
return size;
}
private string GetVolumeLabel(FileSystemMetadata dir)
{
return dir.Name;
}
public int CompareStreamFiles(
TSStreamFile x,
TSStreamFile y)
{
// TODO: Use interleaved file sizes
if ((x == null || x.FileInfo == null) && (y == null || y.FileInfo == null))
{
return 0;
}
else if ((x == null || x.FileInfo == null) && (y != null && y.FileInfo != null))
{
return 1;
}
else if ((x != null || x.FileInfo != null) && (y == null || y.FileInfo == null))
{
return -1;
}
else
{
if (x.FileInfo.Length > y.FileInfo.Length)
{
return 1;
}
else if (y.FileInfo.Length > x.FileInfo.Length)
{
return -1;
}
else
{
return 0;
}
}
}
}
}
+493
View File
@@ -0,0 +1,493 @@
//============================================================================
// BDInfo - Blu-ray Video and Audio Analysis Tool
// Copyright © 2010 Cinema Squid
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//=============================================================================
namespace BDInfo
{
public abstract class LanguageCodes
{
public static string GetName(string code)
{
switch (code)
{
case "abk": return "Abkhazian";
case "ace": return "Achinese";
case "ach": return "Acoli";
case "ada": return "Adangme";
case "aar": return "Afar";
case "afh": return "Afrihili";
case "afr": return "Afrikaans";
case "afa": return "Afro-Asiatic (Other)";
case "aka": return "Akan";
case "akk": return "Akkadian";
case "alb": return "Albanian";
case "sqi": return "Albanian";
case "ale": return "Aleut";
case "alg": return "Algonquian languages";
case "tut": return "Altaic (Other)";
case "amh": return "Amharic";
case "apa": return "Apache languages";
case "ara": return "Arabic";
case "arc": return "Aramaic";
case "arp": return "Arapaho";
case "arn": return "Araucanian";
case "arw": return "Arawak";
case "arm": return "Armenian";
case "hye": return "Armenian";
case "art": return "Artificial (Other)";
case "asm": return "Assamese";
case "ath": return "Athapascan languages";
case "aus": return "Australian languages";
case "map": return "Austronesian (Other)";
case "ava": return "Avaric";
case "ave": return "Avestan";
case "awa": return "Awadhi";
case "aym": return "Aymara";
case "aze": return "Azerbaijani";
case "ban": return "Balinese";
case "bat": return "Baltic (Other)";
case "bal": return "Baluchi";
case "bam": return "Bambara";
case "bai": return "Bamileke languages";
case "bad": return "Banda";
case "bnt": return "Bantu (Other)";
case "bas": return "Basa";
case "bak": return "Bashkir";
case "baq": return "Basque";
case "eus": return "Basque";
case "btk": return "Batak (Indonesia)";
case "bej": return "Beja";
case "bel": return "Belarusian";
case "bem": return "Bemba";
case "ben": return "Bengali";
case "ber": return "Berber (Other)";
case "bho": return "Bhojpuri";
case "bih": return "Bihari";
case "bik": return "Bikol";
case "bin": return "Bini";
case "bis": return "Bislama";
case "bos": return "Bosnian";
case "bra": return "Braj";
case "bre": return "Breton";
case "bug": return "Buginese";
case "bul": return "Bulgarian";
case "bua": return "Buriat";
case "bur": return "Burmese";
case "mya": return "Burmese";
case "cad": return "Caddo";
case "car": return "Carib";
case "cat": return "Catalan";
case "cau": return "Caucasian (Other)";
case "ceb": return "Cebuano";
case "cel": return "Celtic (Other)";
case "cai": return "Central American Indian (Other)";
case "chg": return "Chagatai";
case "cmc": return "Chamic languages";
case "cha": return "Chamorro";
case "che": return "Chechen";
case "chr": return "Cherokee";
case "chy": return "Cheyenne";
case "chb": return "Chibcha";
case "chi": return "Chinese";
case "zho": return "Chinese";
case "chn": return "Chinook jargon";
case "chp": return "Chipewyan";
case "cho": return "Choctaw";
case "chu": return "Church Slavic";
case "chk": return "Chuukese";
case "chv": return "Chuvash";
case "cop": return "Coptic";
case "cor": return "Cornish";
case "cos": return "Corsican";
case "cre": return "Cree";
case "mus": return "Creek";
case "crp": return "Creoles and pidgins (Other)";
case "cpe": return "Creoles and pidgins,";
case "cpf": return "Creoles and pidgins,";
case "cpp": return "Creoles and pidgins,";
case "scr": return "Croatian";
case "hrv": return "Croatian";
case "cus": return "Cushitic (Other)";
case "cze": return "Czech";
case "ces": return "Czech";
case "dak": return "Dakota";
case "dan": return "Danish";
case "day": return "Dayak";
case "del": return "Delaware";
case "din": return "Dinka";
case "div": return "Divehi";
case "doi": return "Dogri";
case "dgr": return "Dogrib";
case "dra": return "Dravidian (Other)";
case "dua": return "Duala";
case "dut": return "Dutch";
case "nld": return "Dutch";
case "dum": return "Dutch, Middle (ca. 1050-1350)";
case "dyu": return "Dyula";
case "dzo": return "Dzongkha";
case "efi": return "Efik";
case "egy": return "Egyptian (Ancient)";
case "eka": return "Ekajuk";
case "elx": return "Elamite";
case "eng": return "English";
case "enm": return "English, Middle (1100-1500)";
case "ang": return "English, Old (ca.450-1100)";
case "epo": return "Esperanto";
case "est": return "Estonian";
case "ewe": return "Ewe";
case "ewo": return "Ewondo";
case "fan": return "Fang";
case "fat": return "Fanti";
case "fao": return "Faroese";
case "fij": return "Fijian";
case "fin": return "Finnish";
case "fiu": return "Finno-Ugrian (Other)";
case "fon": return "Fon";
case "fre": return "French";
case "fra": return "French";
case "frm": return "French, Middle (ca.1400-1600)";
case "fro": return "French, Old (842-ca.1400)";
case "fry": return "Frisian";
case "fur": return "Friulian";
case "ful": return "Fulah";
case "gaa": return "Ga";
case "glg": return "Gallegan";
case "lug": return "Ganda";
case "gay": return "Gayo";
case "gba": return "Gbaya";
case "gez": return "Geez";
case "geo": return "Georgian";
case "kat": return "Georgian";
case "ger": return "German";
case "deu": return "German";
case "nds": return "Saxon";
case "gmh": return "German, Middle High (ca.1050-1500)";
case "goh": return "German, Old High (ca.750-1050)";
case "gem": return "Germanic (Other)";
case "gil": return "Gilbertese";
case "gon": return "Gondi";
case "gor": return "Gorontalo";
case "got": return "Gothic";
case "grb": return "Grebo";
case "grc": return "Greek, Ancient (to 1453)";
case "gre": return "Greek";
case "ell": return "Greek";
case "grn": return "Guarani";
case "guj": return "Gujarati";
case "gwi": return "Gwich´in";
case "hai": return "Haida";
case "hau": return "Hausa";
case "haw": return "Hawaiian";
case "heb": return "Hebrew";
case "her": return "Herero";
case "hil": return "Hiligaynon";
case "him": return "Himachali";
case "hin": return "Hindi";
case "hmo": return "Hiri Motu";
case "hit": return "Hittite";
case "hmn": return "Hmong";
case "hun": return "Hungarian";
case "hup": return "Hupa";
case "iba": return "Iban";
case "ice": return "Icelandic";
case "isl": return "Icelandic";
case "ibo": return "Igbo";
case "ijo": return "Ijo";
case "ilo": return "Iloko";
case "inc": return "Indic (Other)";
case "ine": return "Indo-European (Other)";
case "ind": return "Indonesian";
case "ina": return "Interlingua (International";
case "ile": return "Interlingue";
case "iku": return "Inuktitut";
case "ipk": return "Inupiaq";
case "ira": return "Iranian (Other)";
case "gle": return "Irish";
case "mga": return "Irish, Middle (900-1200)";
case "sga": return "Irish, Old (to 900)";
case "iro": return "Iroquoian languages";
case "ita": return "Italian";
case "jpn": return "Japanese";
case "jav": return "Javanese";
case "jrb": return "Judeo-Arabic";
case "jpr": return "Judeo-Persian";
case "kab": return "Kabyle";
case "kac": return "Kachin";
case "kal": return "Kalaallisut";
case "kam": return "Kamba";
case "kan": return "Kannada";
case "kau": return "Kanuri";
case "kaa": return "Kara-Kalpak";
case "kar": return "Karen";
case "kas": return "Kashmiri";
case "kaw": return "Kawi";
case "kaz": return "Kazakh";
case "kha": return "Khasi";
case "khm": return "Khmer";
case "khi": return "Khoisan (Other)";
case "kho": return "Khotanese";
case "kik": return "Kikuyu";
case "kmb": return "Kimbundu";
case "kin": return "Kinyarwanda";
case "kir": return "Kirghiz";
case "kom": return "Komi";
case "kon": return "Kongo";
case "kok": return "Konkani";
case "kor": return "Korean";
case "kos": return "Kosraean";
case "kpe": return "Kpelle";
case "kro": return "Kru";
case "kua": return "Kuanyama";
case "kum": return "Kumyk";
case "kur": return "Kurdish";
case "kru": return "Kurukh";
case "kut": return "Kutenai";
case "lad": return "Ladino";
case "lah": return "Lahnda";
case "lam": return "Lamba";
case "lao": return "Lao";
case "lat": return "Latin";
case "lav": return "Latvian";
case "ltz": return "Letzeburgesch";
case "lez": return "Lezghian";
case "lin": return "Lingala";
case "lit": return "Lithuanian";
case "loz": return "Lozi";
case "lub": return "Luba-Katanga";
case "lua": return "Luba-Lulua";
case "lui": return "Luiseno";
case "lun": return "Lunda";
case "luo": return "Luo (Kenya and Tanzania)";
case "lus": return "Lushai";
case "mac": return "Macedonian";
case "mkd": return "Macedonian";
case "mad": return "Madurese";
case "mag": return "Magahi";
case "mai": return "Maithili";
case "mak": return "Makasar";
case "mlg": return "Malagasy";
case "may": return "Malay";
case "msa": return "Malay";
case "mal": return "Malayalam";
case "mlt": return "Maltese";
case "mnc": return "Manchu";
case "mdr": return "Mandar";
case "man": return "Mandingo";
case "mni": return "Manipuri";
case "mno": return "Manobo languages";
case "glv": return "Manx";
case "mao": return "Maori";
case "mri": return "Maori";
case "mar": return "Marathi";
case "chm": return "Mari";
case "mah": return "Marshall";
case "mwr": return "Marwari";
case "mas": return "Masai";
case "myn": return "Mayan languages";
case "men": return "Mende";
case "mic": return "Micmac";
case "min": return "Minangkabau";
case "mis": return "Miscellaneous languages";
case "moh": return "Mohawk";
case "mol": return "Moldavian";
case "mkh": return "Mon-Khmer (Other)";
case "lol": return "Mongo";
case "mon": return "Mongolian";
case "mos": return "Mossi";
case "mul": return "Multiple languages";
case "mun": return "Munda languages";
case "nah": return "Nahuatl";
case "nau": return "Nauru";
case "nav": return "Navajo";
case "nde": return "Ndebele, North";
case "nbl": return "Ndebele, South";
case "ndo": return "Ndonga";
case "nep": return "Nepali";
case "new": return "Newari";
case "nia": return "Nias";
case "nic": return "Niger-Kordofanian (Other)";
case "ssa": return "Nilo-Saharan (Other)";
case "niu": return "Niuean";
case "non": return "Norse, Old";
case "nai": return "North American Indian (Other)";
case "sme": return "Northern Sami";
case "nor": return "Norwegian";
case "nob": return "Norwegian Bokmål";
case "nno": return "Norwegian Nynorsk";
case "nub": return "Nubian languages";
case "nym": return "Nyamwezi";
case "nya": return "Nyanja";
case "nyn": return "Nyankole";
case "nyo": return "Nyoro";
case "nzi": return "Nzima";
case "oci": return "Occitan";
case "oji": return "Ojibwa";
case "ori": return "Oriya";
case "orm": return "Oromo";
case "osa": return "Osage";
case "oss": return "Ossetian";
case "oto": return "Otomian languages";
case "pal": return "Pahlavi";
case "pau": return "Palauan";
case "pli": return "Pali";
case "pam": return "Pampanga";
case "pag": return "Pangasinan";
case "pan": return "Panjabi";
case "pap": return "Papiamento";
case "paa": return "Papuan (Other)";
case "per": return "Persian";
case "fas": return "Persian";
case "peo": return "Persian, Old (ca.600-400 B.C.)";
case "phi": return "Philippine (Other)";
case "phn": return "Phoenician";
case "pon": return "Pohnpeian";
case "pol": return "Polish";
case "por": return "Portuguese";
case "pra": return "Prakrit languages";
case "pro": return "Provençal";
case "pus": return "Pushto";
case "que": return "Quechua";
case "roh": return "Raeto-Romance";
case "raj": return "Rajasthani";
case "rap": return "Rapanui";
case "rar": return "Rarotongan";
case "roa": return "Romance (Other)";
case "rum": return "Romanian";
case "ron": return "Romanian";
case "rom": return "Romany";
case "run": return "Rundi";
case "rus": return "Russian";
case "sal": return "Salishan languages";
case "sam": return "Samaritan Aramaic";
case "smi": return "Sami languages (Other)";
case "smo": return "Samoan";
case "sad": return "Sandawe";
case "sag": return "Sango";
case "san": return "Sanskrit";
case "sat": return "Santali";
case "srd": return "Sardinian";
case "sas": return "Sasak";
case "sco": return "Scots";
case "gla": return "Gaelic";
case "sel": return "Selkup";
case "sem": return "Semitic (Other)";
case "scc": return "Serbian";
case "srp": return "Serbian";
case "srr": return "Serer";
case "shn": return "Shan";
case "sna": return "Shona";
case "sid": return "Sidamo";
case "sgn": return "Sign languages";
case "bla": return "Siksika";
case "snd": return "Sindhi";
case "sin": return "Sinhalese";
case "sit": return "Sino-Tibetan (Other)";
case "sio": return "Siouan languages";
case "den": return "Slave (Athapascan)";
case "sla": return "Slavic (Other)";
case "slo": return "Slovak";
case "slk": return "Slovak";
case "slv": return "Slovenian";
case "sog": return "Sogdian";
case "som": return "Somali";
case "son": return "Songhai";
case "snk": return "Soninke";
case "wen": return "Sorbian languages";
case "nso": return "Sotho, Northern";
case "sot": return "Sotho, Southern";
case "sai": return "South American Indian (Other)";
case "spa": return "Spanish";
case "suk": return "Sukuma";
case "sux": return "Sumerian";
case "sun": return "Sundanese";
case "sus": return "Susu";
case "swa": return "Swahili";
case "ssw": return "Swati";
case "swe": return "Swedish";
case "syr": return "Syriac";
case "tgl": return "Tagalog";
case "tah": return "Tahitian";
case "tai": return "Tai (Other)";
case "tgk": return "Tajik";
case "tmh": return "Tamashek";
case "tam": return "Tamil";
case "tat": return "Tatar";
case "tel": return "Telugu";
case "ter": return "Tereno";
case "tet": return "Tetum";
case "tha": return "Thai";
case "tib": return "Tibetan";
case "bod": return "Tibetan";
case "tig": return "Tigre";
case "tir": return "Tigrinya";
case "tem": return "Timne";
case "tiv": return "Tiv";
case "tli": return "Tlingit";
case "tpi": return "Tok Pisin";
case "tkl": return "Tokelau";
case "tog": return "Tonga (Nyasa)";
case "ton": return "Tonga (Tonga Islands)";
case "tsi": return "Tsimshian";
case "tso": return "Tsonga";
case "tsn": return "Tswana";
case "tum": return "Tumbuka";
case "tur": return "Turkish";
case "ota": return "Turkish, Ottoman (1500-1928)";
case "tuk": return "Turkmen";
case "tvl": return "Tuvalu";
case "tyv": return "Tuvinian";
case "twi": return "Twi";
case "uga": return "Ugaritic";
case "uig": return "Uighur";
case "ukr": return "Ukrainian";
case "umb": return "Umbundu";
case "und": return "Undetermined";
case "urd": return "Urdu";
case "uzb": return "Uzbek";
case "vai": return "Vai";
case "ven": return "Venda";
case "vie": return "Vietnamese";
case "vol": return "Volapük";
case "vot": return "Votic";
case "wak": return "Wakashan languages";
case "wal": return "Walamo";
case "war": return "Waray";
case "was": return "Washo";
case "wel": return "Welsh";
case "cym": return "Welsh";
case "wol": return "Wolof";
case "xho": return "Xhosa";
case "sah": return "Yakut";
case "yao": return "Yao";
case "yap": return "Yapese";
case "yid": return "Yiddish";
case "yor": return "Yoruba";
case "ypk": return "Yupik languages";
case "znd": return "Zande";
case "zap": return "Zapotec";
case "zen": return "Zenaga";
case "zha": return "Zhuang";
case "zul": return "Zulu";
case "zun": return "Zuni";
default: return code;
}
}
}
}
+21
View File
@@ -0,0 +1,21 @@
using System.Reflection;
using System.Resources;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("BDInfo")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Jellyfin Project")]
[assembly: AssemblyProduct("Jellyfin: The Free Software Media System")]
[assembly: AssemblyCopyright("Copyright © 2016 CinemaSquid. Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License Version 2")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: NeutralResourcesLanguage("en")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
+5
View File
@@ -0,0 +1,5 @@
The source is taken from the BDRom folder of this project:
http://www.cinemasquid.com/blu-ray/tools/bdinfo
BDInfoSettings was taken from the FormSettings class, and changed so that the settings all return defaults.
+309
View File
@@ -0,0 +1,309 @@
//============================================================================
// BDInfo - Blu-ray Video and Audio Analysis Tool
// Copyright © 2010 Cinema Squid
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//=============================================================================
#undef DEBUG
using System.IO;
namespace BDInfo
{
public abstract class TSCodecAC3
{
private static byte[] eac3_blocks = new byte[] { 1, 2, 3, 6 };
public static void Scan(
TSAudioStream stream,
TSStreamBuffer buffer,
ref string tag)
{
if (stream.IsInitialized) return;
byte[] sync = buffer.ReadBytes(2);
if (sync == null ||
sync[0] != 0x0B ||
sync[1] != 0x77)
{
return;
}
int sr_code = 0;
int frame_size = 0;
int frame_size_code = 0;
int channel_mode = 0;
int lfe_on = 0;
int dial_norm = 0;
int num_blocks = 0;
byte[] hdr = buffer.ReadBytes(4);
int bsid = (hdr[3] & 0xF8) >> 3;
buffer.Seek(-4, SeekOrigin.Current);
if (bsid <= 10)
{
byte[] crc = buffer.ReadBytes(2);
sr_code = buffer.ReadBits(2);
frame_size_code = buffer.ReadBits(6);
bsid = buffer.ReadBits(5);
int bsmod = buffer.ReadBits(3);
channel_mode = buffer.ReadBits(3);
int cmixlev = 0;
if (((channel_mode & 0x1) > 0) && (channel_mode != 0x1))
{
cmixlev = buffer.ReadBits(2);
}
int surmixlev = 0;
if ((channel_mode & 0x4) > 0)
{
surmixlev = buffer.ReadBits(2);
}
int dsurmod = 0;
if (channel_mode == 0x2)
{
dsurmod = buffer.ReadBits(2);
if (dsurmod == 0x2)
{
stream.AudioMode = TSAudioMode.Surround;
}
}
lfe_on = buffer.ReadBits(1);
dial_norm = buffer.ReadBits(5);
int compr = 0;
if (1 == buffer.ReadBits(1))
{
compr = buffer.ReadBits(8);
}
int langcod = 0;
if (1 == buffer.ReadBits(1))
{
langcod = buffer.ReadBits(8);
}
int mixlevel = 0;
int roomtyp = 0;
if (1 == buffer.ReadBits(1))
{
mixlevel = buffer.ReadBits(5);
roomtyp = buffer.ReadBits(2);
}
if (channel_mode == 0)
{
int dialnorm2 = buffer.ReadBits(5);
int compr2 = 0;
if (1 == buffer.ReadBits(1))
{
compr2 = buffer.ReadBits(8);
}
int langcod2 = 0;
if (1 == buffer.ReadBits(1))
{
langcod2 = buffer.ReadBits(8);
}
int mixlevel2 = 0;
int roomtyp2 = 0;
if (1 == buffer.ReadBits(1))
{
mixlevel2 = buffer.ReadBits(5);
roomtyp2 = buffer.ReadBits(2);
}
}
int copyrightb = buffer.ReadBits(1);
int origbs = buffer.ReadBits(1);
if (bsid == 6)
{
if (1 == buffer.ReadBits(1))
{
int dmixmod = buffer.ReadBits(2);
int ltrtcmixlev = buffer.ReadBits(3);
int ltrtsurmixlev = buffer.ReadBits(3);
int lorocmixlev = buffer.ReadBits(3);
int lorosurmixlev = buffer.ReadBits(3);
}
if (1 == buffer.ReadBits(1))
{
int dsurexmod = buffer.ReadBits(2);
int dheadphonmod = buffer.ReadBits(2);
if (dheadphonmod == 0x2)
{
// TODO
}
int adconvtyp = buffer.ReadBits(1);
int xbsi2 = buffer.ReadBits(8);
int encinfo = buffer.ReadBits(1);
if (dsurexmod == 2)
{
stream.AudioMode = TSAudioMode.Extended;
}
}
}
}
else
{
int frame_type = buffer.ReadBits(2);
int substreamid = buffer.ReadBits(3);
frame_size = (buffer.ReadBits(11) + 1) << 1;
sr_code = buffer.ReadBits(2);
if (sr_code == 3)
{
sr_code = buffer.ReadBits(2);
}
else
{
num_blocks = buffer.ReadBits(2);
}
channel_mode = buffer.ReadBits(3);
lfe_on = buffer.ReadBits(1);
}
switch (channel_mode)
{
case 0: // 1+1
stream.ChannelCount = 2;
if (stream.AudioMode == TSAudioMode.Unknown)
{
stream.AudioMode = TSAudioMode.DualMono;
}
break;
case 1: // 1/0
stream.ChannelCount = 1;
break;
case 2: // 2/0
stream.ChannelCount = 2;
if (stream.AudioMode == TSAudioMode.Unknown)
{
stream.AudioMode = TSAudioMode.Stereo;
}
break;
case 3: // 3/0
stream.ChannelCount = 3;
break;
case 4: // 2/1
stream.ChannelCount = 3;
break;
case 5: // 3/1
stream.ChannelCount = 4;
break;
case 6: // 2/2
stream.ChannelCount = 4;
break;
case 7: // 3/2
stream.ChannelCount = 5;
break;
default:
stream.ChannelCount = 0;
break;
}
switch (sr_code)
{
case 0:
stream.SampleRate = 48000;
break;
case 1:
stream.SampleRate = 44100;
break;
case 2:
stream.SampleRate = 32000;
break;
default:
stream.SampleRate = 0;
break;
}
if (bsid <= 10)
{
switch (frame_size_code >> 1)
{
case 18:
stream.BitRate = 640000;
break;
case 17:
stream.BitRate = 576000;
break;
case 16:
stream.BitRate = 512000;
break;
case 15:
stream.BitRate = 448000;
break;
case 14:
stream.BitRate = 384000;
break;
case 13:
stream.BitRate = 320000;
break;
case 12:
stream.BitRate = 256000;
break;
case 11:
stream.BitRate = 224000;
break;
case 10:
stream.BitRate = 192000;
break;
case 9:
stream.BitRate = 160000;
break;
case 8:
stream.BitRate = 128000;
break;
case 7:
stream.BitRate = 112000;
break;
case 6:
stream.BitRate = 96000;
break;
case 5:
stream.BitRate = 80000;
break;
case 4:
stream.BitRate = 64000;
break;
case 3:
stream.BitRate = 56000;
break;
case 2:
stream.BitRate = 48000;
break;
case 1:
stream.BitRate = 40000;
break;
case 0:
stream.BitRate = 32000;
break;
default:
stream.BitRate = 0;
break;
}
}
else
{
stream.BitRate = (long)
(4.0 * frame_size * stream.SampleRate / (num_blocks * 256));
}
stream.LFE = lfe_on;
if (stream.StreamType != TSStreamType.AC3_PLUS_AUDIO &&
stream.StreamType != TSStreamType.AC3_PLUS_SECONDARY_AUDIO)
{
stream.DialNorm = dial_norm - 31;
}
stream.IsVBR = false;
stream.IsInitialized = true;
}
}
}
+148
View File
@@ -0,0 +1,148 @@
//============================================================================
// BDInfo - Blu-ray Video and Audio Analysis Tool
// Copyright © 2010 Cinema Squid
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//=============================================================================
namespace BDInfo
{
public abstract class TSCodecAVC
{
public static void Scan(
TSVideoStream stream,
TSStreamBuffer buffer,
ref string tag)
{
uint parse = 0;
byte accessUnitDelimiterParse = 0;
byte sequenceParameterSetParse = 0;
string profile = null;
string level = null;
byte constraintSet0Flag = 0;
byte constraintSet1Flag = 0;
byte constraintSet2Flag = 0;
byte constraintSet3Flag = 0;
for (int i = 0; i < buffer.Length; i++)
{
parse = (parse << 8) + buffer.ReadByte();
if (parse == 0x00000109)
{
accessUnitDelimiterParse = 1;
}
else if (accessUnitDelimiterParse > 0)
{
--accessUnitDelimiterParse;
if (accessUnitDelimiterParse == 0)
{
switch ((parse & 0xFF) >> 5)
{
case 0: // I
case 3: // SI
case 5: // I, SI
tag = "I";
break;
case 1: // I, P
case 4: // SI, SP
case 6: // I, SI, P, SP
tag = "P";
break;
case 2: // I, P, B
case 7: // I, SI, P, SP, B
tag = "B";
break;
}
if (stream.IsInitialized) return;
}
}
else if (parse == 0x00000127 || parse == 0x00000167)
{
sequenceParameterSetParse = 3;
}
else if (sequenceParameterSetParse > 0)
{
--sequenceParameterSetParse;
switch (sequenceParameterSetParse)
{
case 2:
switch (parse & 0xFF)
{
case 66:
profile = "Baseline Profile";
break;
case 77:
profile = "Main Profile";
break;
case 88:
profile = "Extended Profile";
break;
case 100:
profile = "High Profile";
break;
case 110:
profile = "High 10 Profile";
break;
case 122:
profile = "High 4:2:2 Profile";
break;
case 144:
profile = "High 4:4:4 Profile";
break;
default:
profile = "Unknown Profile";
break;
}
break;
case 1:
constraintSet0Flag = (byte)
((parse & 0x80) >> 7);
constraintSet1Flag = (byte)
((parse & 0x40) >> 6);
constraintSet2Flag = (byte)
((parse & 0x20) >> 5);
constraintSet3Flag = (byte)
((parse & 0x10) >> 4);
break;
case 0:
byte b = (byte)(parse & 0xFF);
if (b == 11 && constraintSet3Flag == 1)
{
level = "1b";
}
else
{
level = string.Format(
"{0:D}.{1:D}",
b / 10, (b - ((b / 10) * 10)));
}
stream.EncodingProfile = string.Format(
"{0} {1}", profile, level);
stream.IsVBR = true;
stream.IsInitialized = true;
break;
}
}
}
return;
}
}
}
+159
View File
@@ -0,0 +1,159 @@
//============================================================================
// BDInfo - Blu-ray Video and Audio Analysis Tool
// Copyright © 2010 Cinema Squid
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//=============================================================================
namespace BDInfo
{
public abstract class TSCodecDTS
{
private static int[] dca_sample_rates =
{
0, 8000, 16000, 32000, 0, 0, 11025, 22050, 44100, 0, 0,
12000, 24000, 48000, 96000, 192000
};
private static int[] dca_bit_rates =
{
32000, 56000, 64000, 96000, 112000, 128000,
192000, 224000, 256000, 320000, 384000,
448000, 512000, 576000, 640000, 768000,
896000, 1024000, 1152000, 1280000, 1344000,
1408000, 1411200, 1472000, 1509000, 1920000,
2048000, 3072000, 3840000, 1/*open*/, 2/*variable*/, 3/*lossless*/
};
private static int[] dca_channels =
{
1, 2, 2, 2, 2, 3, 3, 4, 4, 5, 6, 6, 6, 7, 8, 8
};
private static int[] dca_bits_per_sample =
{
16, 16, 20, 20, 0, 24, 24
};
public static void Scan(
TSAudioStream stream,
TSStreamBuffer buffer,
long bitrate,
ref string tag)
{
if (stream.IsInitialized) return;
bool syncFound = false;
uint sync = 0;
for (int i = 0; i < buffer.Length; i++)
{
sync = (sync << 8) + buffer.ReadByte();
if (sync == 0x7FFE8001)
{
syncFound = true;
break;
}
}
if (!syncFound) return;
int frame_type = buffer.ReadBits(1);
int samples_deficit = buffer.ReadBits(5);
int crc_present = buffer.ReadBits(1);
int sample_blocks = buffer.ReadBits(7);
int frame_size = buffer.ReadBits(14);
if (frame_size < 95)
{
return;
}
int amode = buffer.ReadBits(6);
int sample_rate = buffer.ReadBits(4);
if (sample_rate < 0 || sample_rate >= dca_sample_rates.Length)
{
return;
}
int bit_rate = buffer.ReadBits(5);
if (bit_rate < 0 || bit_rate >= dca_bit_rates.Length)
{
return;
}
int downmix = buffer.ReadBits(1);
int dynrange = buffer.ReadBits(1);
int timestamp = buffer.ReadBits(1);
int aux_data = buffer.ReadBits(1);
int hdcd = buffer.ReadBits(1);
int ext_descr = buffer.ReadBits(3);
int ext_coding = buffer.ReadBits(1);
int aspf = buffer.ReadBits(1);
int lfe = buffer.ReadBits(2);
int predictor_history = buffer.ReadBits(1);
if (crc_present == 1)
{
int crc = buffer.ReadBits(16);
}
int multirate_inter = buffer.ReadBits(1);
int version = buffer.ReadBits(4);
int copy_history = buffer.ReadBits(2);
int source_pcm_res = buffer.ReadBits(3);
int front_sum = buffer.ReadBits(1);
int surround_sum = buffer.ReadBits(1);
int dialog_norm = buffer.ReadBits(4);
if (source_pcm_res < 0 || source_pcm_res >= dca_bits_per_sample.Length)
{
return;
}
int subframes = buffer.ReadBits(4);
int total_channels = buffer.ReadBits(3) + 1 + ext_coding;
stream.SampleRate = dca_sample_rates[sample_rate];
stream.ChannelCount = total_channels;
stream.LFE = (lfe > 0 ? 1 : 0);
stream.BitDepth = dca_bits_per_sample[source_pcm_res];
stream.DialNorm = -dialog_norm;
if ((source_pcm_res & 0x1) == 0x1)
{
stream.AudioMode = TSAudioMode.Extended;
}
stream.BitRate = (uint)dca_bit_rates[bit_rate];
switch (stream.BitRate)
{
case 1:
if (bitrate > 0)
{
stream.BitRate = bitrate;
stream.IsVBR = false;
stream.IsInitialized = true;
}
else
{
stream.BitRate = 0;
}
break;
case 2:
case 3:
stream.IsVBR = true;
stream.IsInitialized = true;
break;
default:
stream.IsVBR = false;
stream.IsInitialized = true;
break;
}
}
}
}
+246
View File
@@ -0,0 +1,246 @@
//============================================================================
// BDInfo - Blu-ray Video and Audio Analysis Tool
// Copyright © 2010 Cinema Squid
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//=============================================================================
namespace BDInfo
{
public abstract class TSCodecDTSHD
{
private static int[] SampleRates = new int[]
{ 0x1F40, 0x3E80, 0x7D00, 0x0FA00, 0x1F400, 0x5622, 0x0AC44, 0x15888, 0x2B110, 0x56220, 0x2EE0, 0x5DC0, 0x0BB80, 0x17700, 0x2EE00, 0x5DC00 };
public static void Scan(
TSAudioStream stream,
TSStreamBuffer buffer,
long bitrate,
ref string tag)
{
if (stream.IsInitialized &&
(stream.StreamType == TSStreamType.DTS_HD_SECONDARY_AUDIO ||
(stream.CoreStream != null &&
stream.CoreStream.IsInitialized))) return;
bool syncFound = false;
uint sync = 0;
for (int i = 0; i < buffer.Length; i++)
{
sync = (sync << 8) + buffer.ReadByte();
if (sync == 0x64582025)
{
syncFound = true;
break;
}
}
if (!syncFound)
{
tag = "CORE";
if (stream.CoreStream == null)
{
stream.CoreStream = new TSAudioStream();
stream.CoreStream.StreamType = TSStreamType.DTS_AUDIO;
}
if (!stream.CoreStream.IsInitialized)
{
buffer.BeginRead();
TSCodecDTS.Scan(stream.CoreStream, buffer, bitrate, ref tag);
}
return;
}
tag = "HD";
int temp1 = buffer.ReadBits(8);
int nuSubStreamIndex = buffer.ReadBits(2);
int nuExtSSHeaderSize = 0;
int nuExtSSFSize = 0;
int bBlownUpHeader = buffer.ReadBits(1);
if (1 == bBlownUpHeader)
{
nuExtSSHeaderSize = buffer.ReadBits(12) + 1;
nuExtSSFSize = buffer.ReadBits(20) + 1;
}
else
{
nuExtSSHeaderSize = buffer.ReadBits(8) + 1;
nuExtSSFSize = buffer.ReadBits(16) + 1;
}
int nuNumAudioPresent = 1;
int nuNumAssets = 1;
int bStaticFieldsPresent = buffer.ReadBits(1);
if (1 == bStaticFieldsPresent)
{
int nuRefClockCode = buffer.ReadBits(2);
int nuExSSFrameDurationCode = buffer.ReadBits(3) + 1;
long nuTimeStamp = 0;
if (1 == buffer.ReadBits(1))
{
nuTimeStamp = (buffer.ReadBits(18) << 18) + buffer.ReadBits(18);
}
nuNumAudioPresent = buffer.ReadBits(3) + 1;
nuNumAssets = buffer.ReadBits(3) + 1;
int[] nuActiveExSSMask = new int[nuNumAudioPresent];
for (int i = 0; i < nuNumAudioPresent; i++)
{
nuActiveExSSMask[i] = buffer.ReadBits(nuSubStreamIndex + 1); //?
}
for (int i = 0; i < nuNumAudioPresent; i++)
{
for (int j = 0; j < nuSubStreamIndex + 1; j++)
{
if (((j + 1) % 2) == 1)
{
int mask = buffer.ReadBits(8);
}
}
}
if (1 == buffer.ReadBits(1))
{
int nuMixMetadataAdjLevel = buffer.ReadBits(2);
int nuBits4MixOutMask = buffer.ReadBits(2) * 4 + 4;
int nuNumMixOutConfigs = buffer.ReadBits(2) + 1;
int[] nuMixOutChMask = new int[nuNumMixOutConfigs];
for (int i = 0; i < nuNumMixOutConfigs; i++)
{
nuMixOutChMask[i] = buffer.ReadBits(nuBits4MixOutMask);
}
}
}
int[] AssetSizes = new int[nuNumAssets];
for (int i = 0; i < nuNumAssets; i++)
{
if (1 == bBlownUpHeader)
{
AssetSizes[i] = buffer.ReadBits(20) + 1;
}
else
{
AssetSizes[i] = buffer.ReadBits(16) + 1;
}
}
for (int i = 0; i < nuNumAssets; i++)
{
long bufferPosition = buffer.Position;
int nuAssetDescriptorFSIZE = buffer.ReadBits(9) + 1;
int DescriptorDataForAssetIndex = buffer.ReadBits(3);
if (1 == bStaticFieldsPresent)
{
int AssetTypeDescrPresent = buffer.ReadBits(1);
if (1 == AssetTypeDescrPresent)
{
int AssetTypeDescriptor = buffer.ReadBits(4);
}
int LanguageDescrPresent = buffer.ReadBits(1);
if (1 == LanguageDescrPresent)
{
int LanguageDescriptor = buffer.ReadBits(24);
}
int bInfoTextPresent = buffer.ReadBits(1);
if (1 == bInfoTextPresent)
{
int nuInfoTextByteSize = buffer.ReadBits(10) + 1;
int[] InfoText = new int[nuInfoTextByteSize];
for (int j = 0; j < nuInfoTextByteSize; j++)
{
InfoText[j] = buffer.ReadBits(8);
}
}
int nuBitResolution = buffer.ReadBits(5) + 1;
int nuMaxSampleRate = buffer.ReadBits(4);
int nuTotalNumChs = buffer.ReadBits(8) + 1;
int bOne2OneMapChannels2Speakers = buffer.ReadBits(1);
int nuSpkrActivityMask = 0;
if (1 == bOne2OneMapChannels2Speakers)
{
int bEmbeddedStereoFlag = 0;
if (nuTotalNumChs > 2)
{
bEmbeddedStereoFlag = buffer.ReadBits(1);
}
int bEmbeddedSixChFlag = 0;
if (nuTotalNumChs > 6)
{
bEmbeddedSixChFlag = buffer.ReadBits(1);
}
int bSpkrMaskEnabled = buffer.ReadBits(1);
int nuNumBits4SAMask = 0;
if (1 == bSpkrMaskEnabled)
{
nuNumBits4SAMask = buffer.ReadBits(2);
nuNumBits4SAMask = nuNumBits4SAMask * 4 + 4;
nuSpkrActivityMask = buffer.ReadBits(nuNumBits4SAMask);
}
// TODO...
}
stream.SampleRate = SampleRates[nuMaxSampleRate];
stream.BitDepth = nuBitResolution;
stream.LFE = 0;
if ((nuSpkrActivityMask & 0x8) == 0x8)
{
++stream.LFE;
}
if ((nuSpkrActivityMask & 0x1000) == 0x1000)
{
++stream.LFE;
}
stream.ChannelCount = nuTotalNumChs - stream.LFE;
}
if (nuNumAssets > 1)
{
// TODO...
break;
}
}
// TODO
if (stream.CoreStream != null)
{
var coreStream = (TSAudioStream)stream.CoreStream;
if (coreStream.AudioMode == TSAudioMode.Extended &&
stream.ChannelCount == 5)
{
stream.AudioMode = TSAudioMode.Extended;
}
/*
if (coreStream.DialNorm != 0)
{
stream.DialNorm = coreStream.DialNorm;
}
*/
}
if (stream.StreamType == TSStreamType.DTS_HD_MASTER_AUDIO)
{
stream.IsVBR = true;
stream.IsInitialized = true;
}
else if (bitrate > 0)
{
stream.IsVBR = false;
stream.BitRate = bitrate;
if (stream.CoreStream != null)
{
stream.BitRate += stream.CoreStream.BitRate;
stream.IsInitialized = true;
}
stream.IsInitialized = (stream.BitRate > 0 ? true : false);
}
}
}
}
+123
View File
@@ -0,0 +1,123 @@
//============================================================================
// BDInfo - Blu-ray Video and Audio Analysis Tool
// Copyright © 2010 Cinema Squid
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//=============================================================================
namespace BDInfo
{
public abstract class TSCodecLPCM
{
public static void Scan(
TSAudioStream stream,
TSStreamBuffer buffer,
ref string tag)
{
if (stream.IsInitialized) return;
byte[] header = buffer.ReadBytes(4);
int flags = (header[2] << 8) + header[3];
switch ((flags & 0xF000) >> 12)
{
case 1: // 1/0/0
stream.ChannelCount = 1;
stream.LFE = 0;
break;
case 3: // 2/0/0
stream.ChannelCount = 2;
stream.LFE = 0;
break;
case 4: // 3/0/0
stream.ChannelCount = 3;
stream.LFE = 0;
break;
case 5: // 2/1/0
stream.ChannelCount = 3;
stream.LFE = 0;
break;
case 6: // 3/1/0
stream.ChannelCount = 4;
stream.LFE = 0;
break;
case 7: // 2/2/0
stream.ChannelCount = 4;
stream.LFE = 0;
break;
case 8: // 3/2/0
stream.ChannelCount = 5;
stream.LFE = 0;
break;
case 9: // 3/2/1
stream.ChannelCount = 5;
stream.LFE = 1;
break;
case 10: // 3/4/0
stream.ChannelCount = 7;
stream.LFE = 0;
break;
case 11: // 3/4/1
stream.ChannelCount = 7;
stream.LFE = 1;
break;
default:
stream.ChannelCount = 0;
stream.LFE = 0;
break;
}
switch ((flags & 0xC0) >> 6)
{
case 1:
stream.BitDepth = 16;
break;
case 2:
stream.BitDepth = 20;
break;
case 3:
stream.BitDepth = 24;
break;
default:
stream.BitDepth = 0;
break;
}
switch ((flags & 0xF00) >> 8)
{
case 1:
stream.SampleRate = 48000;
break;
case 4:
stream.SampleRate = 96000;
break;
case 5:
stream.SampleRate = 192000;
break;
default:
stream.SampleRate = 0;
break;
}
stream.BitRate = (uint)
(stream.SampleRate * stream.BitDepth *
(stream.ChannelCount + stream.LFE));
stream.IsVBR = false;
stream.IsInitialized = true;
}
}
}
+208
View File
@@ -0,0 +1,208 @@
//============================================================================
// BDInfo - Blu-ray Video and Audio Analysis Tool
// Copyright © 2010 Cinema Squid
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//=============================================================================
#undef DEBUG
namespace BDInfo
{
public abstract class TSCodecMPEG2
{
public static void Scan(
TSVideoStream stream,
TSStreamBuffer buffer,
ref string tag)
{
int parse = 0;
int pictureParse = 0;
int sequenceHeaderParse = 0;
int extensionParse = 0;
int sequenceExtensionParse = 0;
for (int i = 0; i < buffer.Length; i++)
{
parse = (parse << 8) + buffer.ReadByte();
if (parse == 0x00000100)
{
pictureParse = 2;
}
else if (parse == 0x000001B3)
{
sequenceHeaderParse = 7;
}
else if (sequenceHeaderParse > 0)
{
--sequenceHeaderParse;
switch (sequenceHeaderParse)
{
#if DEBUG
case 6:
break;
case 5:
break;
case 4:
stream.Width =
(int)((parse & 0xFFF000) >> 12);
stream.Height =
(int)(parse & 0xFFF);
break;
case 3:
stream.AspectRatio =
(TSAspectRatio)((parse & 0xF0) >> 4);
switch ((parse & 0xF0) >> 4)
{
case 0: // Forbidden
break;
case 1: // Square
break;
case 2: // 4:3
break;
case 3: // 16:9
break;
case 4: // 2.21:1
break;
default: // Reserved
break;
}
switch (parse & 0xF)
{
case 0: // Forbidden
break;
case 1: // 23.976
stream.FrameRateEnumerator = 24000;
stream.FrameRateDenominator = 1001;
break;
case 2: // 24
stream.FrameRateEnumerator = 24000;
stream.FrameRateDenominator = 1000;
break;
case 3: // 25
stream.FrameRateEnumerator = 25000;
stream.FrameRateDenominator = 1000;
break;
case 4: // 29.97
stream.FrameRateEnumerator = 30000;
stream.FrameRateDenominator = 1001;
break;
case 5: // 30
stream.FrameRateEnumerator = 30000;
stream.FrameRateDenominator = 1000;
break;
case 6: // 50
stream.FrameRateEnumerator = 50000;
stream.FrameRateDenominator = 1000;
break;
case 7: // 59.94
stream.FrameRateEnumerator = 60000;
stream.FrameRateDenominator = 1001;
break;
case 8: // 60
stream.FrameRateEnumerator = 60000;
stream.FrameRateDenominator = 1000;
break;
default: // Reserved
stream.FrameRateEnumerator = 0;
stream.FrameRateDenominator = 0;
break;
}
break;
case 2:
break;
case 1:
break;
#endif
case 0:
#if DEBUG
stream.BitRate =
(((parse & 0xFFFFC0) >> 6) * 200);
#endif
stream.IsVBR = true;
stream.IsInitialized = true;
break;
}
}
else if (pictureParse > 0)
{
--pictureParse;
if (pictureParse == 0)
{
switch ((parse & 0x38) >> 3)
{
case 1:
tag = "I";
break;
case 2:
tag = "P";
break;
case 3:
tag = "B";
break;
default:
break;
}
if (stream.IsInitialized) return;
}
}
else if (parse == 0x000001B5)
{
extensionParse = 1;
}
else if (extensionParse > 0)
{
--extensionParse;
if (extensionParse == 0)
{
if ((parse & 0xF0) == 0x10)
{
sequenceExtensionParse = 1;
}
}
}
else if (sequenceExtensionParse > 0)
{
--sequenceExtensionParse;
#if DEBUG
if (sequenceExtensionParse == 0)
{
uint sequenceExtension =
((parse & 0x8) >> 3);
if (sequenceExtension == 0)
{
stream.IsInterlaced = true;
}
else
{
stream.IsInterlaced = false;
}
}
#endif
}
}
}
}
}
+36
View File
@@ -0,0 +1,36 @@
//============================================================================
// BDInfo - Blu-ray Video and Audio Analysis Tool
// Copyright © 2010 Cinema Squid
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//=============================================================================
namespace BDInfo
{
// TODO: Do something more interesting here...
public abstract class TSCodecMVC
{
public static void Scan(
TSVideoStream stream,
TSStreamBuffer buffer,
ref string tag)
{
stream.IsVBR = true;
stream.IsInitialized = true;
}
}
}
+186
View File
@@ -0,0 +1,186 @@
//============================================================================
// BDInfo - Blu-ray Video and Audio Analysis Tool
// Copyright © 2010 Cinema Squid
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//=============================================================================
namespace BDInfo
{
public abstract class TSCodecTrueHD
{
public static void Scan(
TSAudioStream stream,
TSStreamBuffer buffer,
ref string tag)
{
if (stream.IsInitialized &&
stream.CoreStream != null &&
stream.CoreStream.IsInitialized) return;
bool syncFound = false;
uint sync = 0;
for (int i = 0; i < buffer.Length; i++)
{
sync = (sync << 8) + buffer.ReadByte();
if (sync == 0xF8726FBA)
{
syncFound = true;
break;
}
}
if (!syncFound)
{
tag = "CORE";
if (stream.CoreStream == null)
{
stream.CoreStream = new TSAudioStream();
stream.CoreStream.StreamType = TSStreamType.AC3_AUDIO;
}
if (!stream.CoreStream.IsInitialized)
{
buffer.BeginRead();
TSCodecAC3.Scan(stream.CoreStream, buffer, ref tag);
}
return;
}
tag = "HD";
int ratebits = buffer.ReadBits(4);
if (ratebits != 0xF)
{
stream.SampleRate =
(((ratebits & 8) > 0 ? 44100 : 48000) << (ratebits & 7));
}
int temp1 = buffer.ReadBits(8);
int channels_thd_stream1 = buffer.ReadBits(5);
int temp2 = buffer.ReadBits(2);
stream.ChannelCount = 0;
stream.LFE = 0;
int c_LFE2 = buffer.ReadBits(1);
if (c_LFE2 == 1)
{
stream.LFE += 1;
}
int c_Cvh = buffer.ReadBits(1);
if (c_Cvh == 1)
{
stream.ChannelCount += 1;
}
int c_LRw = buffer.ReadBits(1);
if (c_LRw == 1)
{
stream.ChannelCount += 2;
}
int c_LRsd = buffer.ReadBits(1);
if (c_LRsd == 1)
{
stream.ChannelCount += 2;
}
int c_Ts = buffer.ReadBits(1);
if (c_Ts == 1)
{
stream.ChannelCount += 1;
}
int c_Cs = buffer.ReadBits(1);
if (c_Cs == 1)
{
stream.ChannelCount += 1;
}
int c_LRrs = buffer.ReadBits(1);
if (c_LRrs == 1)
{
stream.ChannelCount += 2;
}
int c_LRc = buffer.ReadBits(1);
if (c_LRc == 1)
{
stream.ChannelCount += 2;
}
int c_LRvh = buffer.ReadBits(1);
if (c_LRvh == 1)
{
stream.ChannelCount += 2;
}
int c_LRs = buffer.ReadBits(1);
if (c_LRs == 1)
{
stream.ChannelCount += 2;
}
int c_LFE = buffer.ReadBits(1);
if (c_LFE == 1)
{
stream.LFE += 1;
}
int c_C = buffer.ReadBits(1);
if (c_C == 1)
{
stream.ChannelCount += 1;
}
int c_LR = buffer.ReadBits(1);
if (c_LR == 1)
{
stream.ChannelCount += 2;
}
int access_unit_size = 40 << (ratebits & 7);
int access_unit_size_pow2 = 64 << (ratebits & 7);
int a1 = buffer.ReadBits(16);
int a2 = buffer.ReadBits(16);
int a3 = buffer.ReadBits(16);
int is_vbr = buffer.ReadBits(1);
int peak_bitrate = buffer.ReadBits(15);
peak_bitrate = (peak_bitrate * stream.SampleRate) >> 4;
double peak_bitdepth =
(double)peak_bitrate /
(stream.ChannelCount + stream.LFE) /
stream.SampleRate;
if (peak_bitdepth > 14)
{
stream.BitDepth = 24;
}
else
{
stream.BitDepth = 16;
}
#if DEBUG
System.Diagnostics.Debug.WriteLine(string.Format(
"{0}\t{1}\t{2:F2}",
stream.PID, peak_bitrate, peak_bitdepth));
#endif
/*
// TODO: Get THD dialnorm from metadata
if (stream.CoreStream != null)
{
TSAudioStream coreStream = (TSAudioStream)stream.CoreStream;
if (coreStream.DialNorm != 0)
{
stream.DialNorm = coreStream.DialNorm;
}
}
*/
stream.IsVBR = true;
stream.IsInitialized = true;
}
}
}
+131
View File
@@ -0,0 +1,131 @@
//============================================================================
// BDInfo - Blu-ray Video and Audio Analysis Tool
// Copyright © 2010 Cinema Squid
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//=============================================================================
namespace BDInfo
{
public abstract class TSCodecVC1
{
public static void Scan(
TSVideoStream stream,
TSStreamBuffer buffer,
ref string tag)
{
int parse = 0;
byte frameHeaderParse = 0;
byte sequenceHeaderParse = 0;
bool isInterlaced = false;
for (int i = 0; i < buffer.Length; i++)
{
parse = (parse << 8) + buffer.ReadByte();
if (parse == 0x0000010D)
{
frameHeaderParse = 4;
}
else if (frameHeaderParse > 0)
{
--frameHeaderParse;
if (frameHeaderParse == 0)
{
uint pictureType = 0;
if (isInterlaced)
{
if ((parse & 0x80000000) == 0)
{
pictureType =
(uint)((parse & 0x78000000) >> 13);
}
else
{
pictureType =
(uint)((parse & 0x3c000000) >> 12);
}
}
else
{
pictureType =
(uint)((parse & 0xf0000000) >> 14);
}
if ((pictureType & 0x20000) == 0)
{
tag = "P";
}
else if ((pictureType & 0x10000) == 0)
{
tag = "B";
}
else if ((pictureType & 0x8000) == 0)
{
tag = "I";
}
else if ((pictureType & 0x4000) == 0)
{
tag = "BI";
}
else
{
tag = null;
}
if (stream.IsInitialized) return;
}
}
else if (parse == 0x0000010F)
{
sequenceHeaderParse = 6;
}
else if (sequenceHeaderParse > 0)
{
--sequenceHeaderParse;
switch (sequenceHeaderParse)
{
case 5:
int profileLevel = ((parse & 0x38) >> 3);
if (((parse & 0xC0) >> 6) == 3)
{
stream.EncodingProfile = string.Format(
"Advanced Profile {0}", profileLevel);
}
else
{
stream.EncodingProfile = string.Format(
"Main Profile {0}", profileLevel);
}
break;
case 0:
if (((parse & 0x40) >> 6) > 0)
{
isInterlaced = true;
}
else
{
isInterlaced = false;
}
break;
}
stream.IsVBR = true;
stream.IsInitialized = true;
}
}
}
}
}
+37
View File
@@ -0,0 +1,37 @@
//============================================================================
// BDInfo - Blu-ray Video and Audio Analysis Tool
// Copyright © 2010 Cinema Squid
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//=============================================================================
using MediaBrowser.Model.IO;
// TODO: Do more interesting things here...
namespace BDInfo
{
public class TSInterleavedFile
{
public FileSystemMetadata FileInfo = null;
public string Name = null;
public TSInterleavedFile(FileSystemMetadata fileInfo)
{
FileInfo = fileInfo;
Name = fileInfo.Name.ToUpper();
}
}
}
File diff suppressed because it is too large Load Diff
+780
View File
@@ -0,0 +1,780 @@
//============================================================================
// BDInfo - Blu-ray Video and Audio Analysis Tool
// Copyright © 2010 Cinema Squid
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//=============================================================================
using System;
using System.Collections.Generic;
namespace BDInfo
{
public enum TSStreamType : byte
{
Unknown = 0,
MPEG1_VIDEO = 0x01,
MPEG2_VIDEO = 0x02,
AVC_VIDEO = 0x1b,
MVC_VIDEO = 0x20,
VC1_VIDEO = 0xea,
MPEG1_AUDIO = 0x03,
MPEG2_AUDIO = 0x04,
LPCM_AUDIO = 0x80,
AC3_AUDIO = 0x81,
AC3_PLUS_AUDIO = 0x84,
AC3_PLUS_SECONDARY_AUDIO = 0xA1,
AC3_TRUE_HD_AUDIO = 0x83,
DTS_AUDIO = 0x82,
DTS_HD_AUDIO = 0x85,
DTS_HD_SECONDARY_AUDIO = 0xA2,
DTS_HD_MASTER_AUDIO = 0x86,
PRESENTATION_GRAPHICS = 0x90,
INTERACTIVE_GRAPHICS = 0x91,
SUBTITLE = 0x92
}
public enum TSVideoFormat : byte
{
Unknown = 0,
VIDEOFORMAT_480i = 1,
VIDEOFORMAT_576i = 2,
VIDEOFORMAT_480p = 3,
VIDEOFORMAT_1080i = 4,
VIDEOFORMAT_720p = 5,
VIDEOFORMAT_1080p = 6,
VIDEOFORMAT_576p = 7,
}
public enum TSFrameRate : byte
{
Unknown = 0,
FRAMERATE_23_976 = 1,
FRAMERATE_24 = 2,
FRAMERATE_25 = 3,
FRAMERATE_29_97 = 4,
FRAMERATE_50 = 6,
FRAMERATE_59_94 = 7
}
public enum TSChannelLayout : byte
{
Unknown = 0,
CHANNELLAYOUT_MONO = 1,
CHANNELLAYOUT_STEREO = 3,
CHANNELLAYOUT_MULTI = 6,
CHANNELLAYOUT_COMBO = 12
}
public enum TSSampleRate : byte
{
Unknown = 0,
SAMPLERATE_48 = 1,
SAMPLERATE_96 = 4,
SAMPLERATE_192 = 5,
SAMPLERATE_48_192 = 12,
SAMPLERATE_48_96 = 14
}
public enum TSAspectRatio
{
Unknown = 0,
ASPECT_4_3 = 2,
ASPECT_16_9 = 3,
ASPECT_2_21 = 4
}
public class TSDescriptor
{
public byte Name;
public byte[] Value;
public TSDescriptor(byte name, byte length)
{
Name = name;
Value = new byte[length];
}
public TSDescriptor Clone()
{
var descriptor =
new TSDescriptor(Name, (byte)Value.Length);
Value.CopyTo(descriptor.Value, 0);
return descriptor;
}
}
public abstract class TSStream
{
public TSStream()
{
}
public override string ToString()
{
return string.Format("{0} ({1})", CodecShortName, PID);
}
public ushort PID;
public TSStreamType StreamType;
public List<TSDescriptor> Descriptors = null;
public long BitRate = 0;
public long ActiveBitRate = 0;
public bool IsVBR = false;
public bool IsInitialized = false;
public string LanguageName;
public bool IsHidden = false;
public ulong PayloadBytes = 0;
public ulong PacketCount = 0;
public double PacketSeconds = 0;
public int AngleIndex = 0;
public ulong PacketSize => PacketCount * 192;
private string _LanguageCode;
public string LanguageCode
{
get => _LanguageCode;
set
{
_LanguageCode = value;
LanguageName = LanguageCodes.GetName(value);
}
}
public bool IsVideoStream
{
get
{
switch (StreamType)
{
case TSStreamType.MPEG1_VIDEO:
case TSStreamType.MPEG2_VIDEO:
case TSStreamType.AVC_VIDEO:
case TSStreamType.MVC_VIDEO:
case TSStreamType.VC1_VIDEO:
return true;
default:
return false;
}
}
}
public bool IsAudioStream
{
get
{
switch (StreamType)
{
case TSStreamType.MPEG1_AUDIO:
case TSStreamType.MPEG2_AUDIO:
case TSStreamType.LPCM_AUDIO:
case TSStreamType.AC3_AUDIO:
case TSStreamType.AC3_PLUS_AUDIO:
case TSStreamType.AC3_PLUS_SECONDARY_AUDIO:
case TSStreamType.AC3_TRUE_HD_AUDIO:
case TSStreamType.DTS_AUDIO:
case TSStreamType.DTS_HD_AUDIO:
case TSStreamType.DTS_HD_SECONDARY_AUDIO:
case TSStreamType.DTS_HD_MASTER_AUDIO:
return true;
default:
return false;
}
}
}
public bool IsGraphicsStream
{
get
{
switch (StreamType)
{
case TSStreamType.PRESENTATION_GRAPHICS:
case TSStreamType.INTERACTIVE_GRAPHICS:
return true;
default:
return false;
}
}
}
public bool IsTextStream
{
get
{
switch (StreamType)
{
case TSStreamType.SUBTITLE:
return true;
default:
return false;
}
}
}
public string CodecName
{
get
{
switch (StreamType)
{
case TSStreamType.MPEG1_VIDEO:
return "MPEG-1 Video";
case TSStreamType.MPEG2_VIDEO:
return "MPEG-2 Video";
case TSStreamType.AVC_VIDEO:
return "MPEG-4 AVC Video";
case TSStreamType.MVC_VIDEO:
return "MPEG-4 MVC Video";
case TSStreamType.VC1_VIDEO:
return "VC-1 Video";
case TSStreamType.MPEG1_AUDIO:
return "MP1 Audio";
case TSStreamType.MPEG2_AUDIO:
return "MP2 Audio";
case TSStreamType.LPCM_AUDIO:
return "LPCM Audio";
case TSStreamType.AC3_AUDIO:
if (((TSAudioStream)this).AudioMode == TSAudioMode.Extended)
return "Dolby Digital EX Audio";
else
return "Dolby Digital Audio";
case TSStreamType.AC3_PLUS_AUDIO:
case TSStreamType.AC3_PLUS_SECONDARY_AUDIO:
return "Dolby Digital Plus Audio";
case TSStreamType.AC3_TRUE_HD_AUDIO:
return "Dolby TrueHD Audio";
case TSStreamType.DTS_AUDIO:
if (((TSAudioStream)this).AudioMode == TSAudioMode.Extended)
return "DTS-ES Audio";
else
return "DTS Audio";
case TSStreamType.DTS_HD_AUDIO:
return "DTS-HD High-Res Audio";
case TSStreamType.DTS_HD_SECONDARY_AUDIO:
return "DTS Express";
case TSStreamType.DTS_HD_MASTER_AUDIO:
return "DTS-HD Master Audio";
case TSStreamType.PRESENTATION_GRAPHICS:
return "Presentation Graphics";
case TSStreamType.INTERACTIVE_GRAPHICS:
return "Interactive Graphics";
case TSStreamType.SUBTITLE:
return "Subtitle";
default:
return "UNKNOWN";
}
}
}
public string CodecAltName
{
get
{
switch (StreamType)
{
case TSStreamType.MPEG1_VIDEO:
return "MPEG-1";
case TSStreamType.MPEG2_VIDEO:
return "MPEG-2";
case TSStreamType.AVC_VIDEO:
return "AVC";
case TSStreamType.MVC_VIDEO:
return "MVC";
case TSStreamType.VC1_VIDEO:
return "VC-1";
case TSStreamType.MPEG1_AUDIO:
return "MP1";
case TSStreamType.MPEG2_AUDIO:
return "MP2";
case TSStreamType.LPCM_AUDIO:
return "LPCM";
case TSStreamType.AC3_AUDIO:
return "DD AC3";
case TSStreamType.AC3_PLUS_AUDIO:
case TSStreamType.AC3_PLUS_SECONDARY_AUDIO:
return "DD AC3+";
case TSStreamType.AC3_TRUE_HD_AUDIO:
return "Dolby TrueHD";
case TSStreamType.DTS_AUDIO:
return "DTS";
case TSStreamType.DTS_HD_AUDIO:
return "DTS-HD Hi-Res";
case TSStreamType.DTS_HD_SECONDARY_AUDIO:
return "DTS Express";
case TSStreamType.DTS_HD_MASTER_AUDIO:
return "DTS-HD Master";
case TSStreamType.PRESENTATION_GRAPHICS:
return "PGS";
case TSStreamType.INTERACTIVE_GRAPHICS:
return "IGS";
case TSStreamType.SUBTITLE:
return "SUB";
default:
return "UNKNOWN";
}
}
}
public string CodecShortName
{
get
{
switch (StreamType)
{
case TSStreamType.MPEG1_VIDEO:
return "MPEG-1";
case TSStreamType.MPEG2_VIDEO:
return "MPEG-2";
case TSStreamType.AVC_VIDEO:
return "AVC";
case TSStreamType.MVC_VIDEO:
return "MVC";
case TSStreamType.VC1_VIDEO:
return "VC-1";
case TSStreamType.MPEG1_AUDIO:
return "MP1";
case TSStreamType.MPEG2_AUDIO:
return "MP2";
case TSStreamType.LPCM_AUDIO:
return "LPCM";
case TSStreamType.AC3_AUDIO:
if (((TSAudioStream)this).AudioMode == TSAudioMode.Extended)
return "AC3-EX";
else
return "AC3";
case TSStreamType.AC3_PLUS_AUDIO:
case TSStreamType.AC3_PLUS_SECONDARY_AUDIO:
return "AC3+";
case TSStreamType.AC3_TRUE_HD_AUDIO:
return "TrueHD";
case TSStreamType.DTS_AUDIO:
if (((TSAudioStream)this).AudioMode == TSAudioMode.Extended)
return "DTS-ES";
else
return "DTS";
case TSStreamType.DTS_HD_AUDIO:
return "DTS-HD HR";
case TSStreamType.DTS_HD_SECONDARY_AUDIO:
return "DTS Express";
case TSStreamType.DTS_HD_MASTER_AUDIO:
return "DTS-HD MA";
case TSStreamType.PRESENTATION_GRAPHICS:
return "PGS";
case TSStreamType.INTERACTIVE_GRAPHICS:
return "IGS";
case TSStreamType.SUBTITLE:
return "SUB";
default:
return "UNKNOWN";
}
}
}
public virtual string Description => "";
public abstract TSStream Clone();
protected void CopyTo(TSStream stream)
{
stream.PID = PID;
stream.StreamType = StreamType;
stream.IsVBR = IsVBR;
stream.BitRate = BitRate;
stream.IsInitialized = IsInitialized;
stream.LanguageCode = _LanguageCode;
if (Descriptors != null)
{
stream.Descriptors = new List<TSDescriptor>();
foreach (var descriptor in Descriptors)
{
stream.Descriptors.Add(descriptor.Clone());
}
}
}
}
public class TSVideoStream : TSStream
{
public TSVideoStream()
{
}
public int Width;
public int Height;
public bool IsInterlaced;
public int FrameRateEnumerator;
public int FrameRateDenominator;
public TSAspectRatio AspectRatio;
public string EncodingProfile;
private TSVideoFormat _VideoFormat;
public TSVideoFormat VideoFormat
{
get => _VideoFormat;
set
{
_VideoFormat = value;
switch (value)
{
case TSVideoFormat.VIDEOFORMAT_480i:
Height = 480;
IsInterlaced = true;
break;
case TSVideoFormat.VIDEOFORMAT_480p:
Height = 480;
IsInterlaced = false;
break;
case TSVideoFormat.VIDEOFORMAT_576i:
Height = 576;
IsInterlaced = true;
break;
case TSVideoFormat.VIDEOFORMAT_576p:
Height = 576;
IsInterlaced = false;
break;
case TSVideoFormat.VIDEOFORMAT_720p:
Height = 720;
IsInterlaced = false;
break;
case TSVideoFormat.VIDEOFORMAT_1080i:
Height = 1080;
IsInterlaced = true;
break;
case TSVideoFormat.VIDEOFORMAT_1080p:
Height = 1080;
IsInterlaced = false;
break;
}
}
}
private TSFrameRate _FrameRate;
public TSFrameRate FrameRate
{
get => _FrameRate;
set
{
_FrameRate = value;
switch (value)
{
case TSFrameRate.FRAMERATE_23_976:
FrameRateEnumerator = 24000;
FrameRateDenominator = 1001;
break;
case TSFrameRate.FRAMERATE_24:
FrameRateEnumerator = 24000;
FrameRateDenominator = 1000;
break;
case TSFrameRate.FRAMERATE_25:
FrameRateEnumerator = 25000;
FrameRateDenominator = 1000;
break;
case TSFrameRate.FRAMERATE_29_97:
FrameRateEnumerator = 30000;
FrameRateDenominator = 1001;
break;
case TSFrameRate.FRAMERATE_50:
FrameRateEnumerator = 50000;
FrameRateDenominator = 1000;
break;
case TSFrameRate.FRAMERATE_59_94:
FrameRateEnumerator = 60000;
FrameRateDenominator = 1001;
break;
}
}
}
public override string Description
{
get
{
string description = "";
if (Height > 0)
{
description += string.Format("{0:D}{1} / ",
Height,
IsInterlaced ? "i" : "p");
}
if (FrameRateEnumerator > 0 &&
FrameRateDenominator > 0)
{
if (FrameRateEnumerator % FrameRateDenominator == 0)
{
description += string.Format("{0:D} fps / ",
FrameRateEnumerator / FrameRateDenominator);
}
else
{
description += string.Format("{0:F3} fps / ",
(double)FrameRateEnumerator / FrameRateDenominator);
}
}
if (AspectRatio == TSAspectRatio.ASPECT_4_3)
{
description += "4:3 / ";
}
else if (AspectRatio == TSAspectRatio.ASPECT_16_9)
{
description += "16:9 / ";
}
if (EncodingProfile != null)
{
description += EncodingProfile + " / ";
}
if (description.EndsWith(" / "))
{
description = description.Substring(0, description.Length - 3);
}
return description;
}
}
public override TSStream Clone()
{
var stream = new TSVideoStream();
CopyTo(stream);
stream.VideoFormat = _VideoFormat;
stream.FrameRate = _FrameRate;
stream.Width = Width;
stream.Height = Height;
stream.IsInterlaced = IsInterlaced;
stream.FrameRateEnumerator = FrameRateEnumerator;
stream.FrameRateDenominator = FrameRateDenominator;
stream.AspectRatio = AspectRatio;
stream.EncodingProfile = EncodingProfile;
return stream;
}
}
public enum TSAudioMode
{
Unknown,
DualMono,
Stereo,
Surround,
Extended
}
public class TSAudioStream : TSStream
{
public TSAudioStream()
{
}
public int SampleRate;
public int ChannelCount;
public int BitDepth;
public int LFE;
public int DialNorm;
public TSAudioMode AudioMode;
public TSAudioStream CoreStream;
public TSChannelLayout ChannelLayout;
public static int ConvertSampleRate(
TSSampleRate sampleRate)
{
switch (sampleRate)
{
case TSSampleRate.SAMPLERATE_48:
return 48000;
case TSSampleRate.SAMPLERATE_96:
case TSSampleRate.SAMPLERATE_48_96:
return 96000;
case TSSampleRate.SAMPLERATE_192:
case TSSampleRate.SAMPLERATE_48_192:
return 192000;
}
return 0;
}
public string ChannelDescription
{
get
{
if (ChannelLayout == TSChannelLayout.CHANNELLAYOUT_MONO &&
ChannelCount == 2)
{
}
string description = "";
if (ChannelCount > 0)
{
description += string.Format(
"{0:D}.{1:D}",
ChannelCount, LFE);
}
else
{
switch (ChannelLayout)
{
case TSChannelLayout.CHANNELLAYOUT_MONO:
description += "1.0";
break;
case TSChannelLayout.CHANNELLAYOUT_STEREO:
description += "2.0";
break;
case TSChannelLayout.CHANNELLAYOUT_MULTI:
description += "5.1";
break;
}
}
if (AudioMode == TSAudioMode.Extended)
{
if (StreamType == TSStreamType.AC3_AUDIO)
{
description += "-EX";
}
if (StreamType == TSStreamType.DTS_AUDIO ||
StreamType == TSStreamType.DTS_HD_AUDIO ||
StreamType == TSStreamType.DTS_HD_MASTER_AUDIO)
{
description += "-ES";
}
}
return description;
}
}
public override string Description
{
get
{
string description = ChannelDescription;
if (SampleRate > 0)
{
description += string.Format(
" / {0:D} kHz", SampleRate / 1000);
}
if (BitRate > 0)
{
description += string.Format(
" / {0:D} kbps", (uint)Math.Round((double)BitRate / 1000));
}
if (BitDepth > 0)
{
description += string.Format(
" / {0:D}-bit", BitDepth);
}
if (DialNorm != 0)
{
description += string.Format(
" / DN {0}dB", DialNorm);
}
if (ChannelCount == 2)
{
switch (AudioMode)
{
case TSAudioMode.DualMono:
description += " / Dual Mono";
break;
case TSAudioMode.Surround:
description += " / Dolby Surround";
break;
}
}
if (description.EndsWith(" / "))
{
description = description.Substring(0, description.Length - 3);
}
if (CoreStream != null)
{
string codec = "";
switch (CoreStream.StreamType)
{
case TSStreamType.AC3_AUDIO:
codec = "AC3 Embedded";
break;
case TSStreamType.DTS_AUDIO:
codec = "DTS Core";
break;
}
description += string.Format(
" ({0}: {1})",
codec,
CoreStream.Description);
}
return description;
}
}
public override TSStream Clone()
{
var stream = new TSAudioStream();
CopyTo(stream);
stream.SampleRate = SampleRate;
stream.ChannelLayout = ChannelLayout;
stream.ChannelCount = ChannelCount;
stream.BitDepth = BitDepth;
stream.LFE = LFE;
stream.DialNorm = DialNorm;
stream.AudioMode = AudioMode;
if (CoreStream != null)
{
stream.CoreStream = (TSAudioStream)CoreStream.Clone();
}
return stream;
}
}
public class TSGraphicsStream : TSStream
{
public TSGraphicsStream()
{
IsVBR = true;
IsInitialized = true;
}
public override TSStream Clone()
{
var stream = new TSGraphicsStream();
CopyTo(stream);
return stream;
}
}
public class TSTextStream : TSStream
{
public TSTextStream()
{
IsVBR = true;
IsInitialized = true;
}
public override TSStream Clone()
{
var stream = new TSTextStream();
CopyTo(stream);
return stream;
}
}
}
+130
View File
@@ -0,0 +1,130 @@
//============================================================================
// BDInfo - Blu-ray Video and Audio Analysis Tool
// Copyright © 2010 Cinema Squid
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//=============================================================================
using System;
using System.Collections.Specialized;
using System.IO;
namespace BDInfo
{
public class TSStreamBuffer
{
private MemoryStream Stream = new MemoryStream();
private int SkipBits = 0;
private byte[] Buffer;
private int BufferLength = 0;
public int TransferLength = 0;
public TSStreamBuffer()
{
Buffer = new byte[4096];
Stream = new MemoryStream(Buffer);
}
public long Length => (long)BufferLength;
public long Position => Stream.Position;
public void Add(
byte[] buffer,
int offset,
int length)
{
TransferLength += length;
if (BufferLength + length >= Buffer.Length)
{
length = Buffer.Length - BufferLength;
}
if (length > 0)
{
Array.Copy(buffer, offset, Buffer, BufferLength, length);
BufferLength += length;
}
}
public void Seek(
long offset,
SeekOrigin loc)
{
Stream.Seek(offset, loc);
}
public void Reset()
{
BufferLength = 0;
TransferLength = 0;
}
public void BeginRead()
{
SkipBits = 0;
Stream.Seek(0, SeekOrigin.Begin);
}
public void EndRead()
{
}
public byte[] ReadBytes(int bytes)
{
if (Stream.Position + bytes >= BufferLength)
{
return null;
}
byte[] value = new byte[bytes];
Stream.Read(value, 0, bytes);
return value;
}
public byte ReadByte()
{
return (byte)Stream.ReadByte();
}
public int ReadBits(int bits)
{
long pos = Stream.Position;
int shift = 24;
int data = 0;
for (int i = 0; i < 4; i++)
{
if (pos + i >= BufferLength) break;
data += (Stream.ReadByte() << shift);
shift -= 8;
}
var vector = new BitVector32(data);
int value = 0;
for (int i = SkipBits; i < SkipBits + bits; i++)
{
value <<= 1;
value += (vector[1 << (32 - i - 1)] ? 1 : 0);
}
SkipBits += bits;
Stream.Seek(pos + (SkipBits >> 3), SeekOrigin.Begin);
SkipBits = SkipBits % 8;
return value;
}
}
}
+107
View File
@@ -0,0 +1,107 @@
//============================================================================
// BDInfo - Blu-ray Video and Audio Analysis Tool
// Copyright © 2010 Cinema Squid
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//=============================================================================
using System;
using System.Collections.Generic;
namespace BDInfo
{
public class TSStreamClip
{
public int AngleIndex = 0;
public string Name;
public double TimeIn;
public double TimeOut;
public double RelativeTimeIn;
public double RelativeTimeOut;
public double Length;
public ulong FileSize = 0;
public ulong InterleavedFileSize = 0;
public ulong PayloadBytes = 0;
public ulong PacketCount = 0;
public double PacketSeconds = 0;
public List<double> Chapters = new List<double>();
public TSStreamFile StreamFile = null;
public TSStreamClipFile StreamClipFile = null;
public TSStreamClip(
TSStreamFile streamFile,
TSStreamClipFile streamClipFile)
{
if (streamFile != null)
{
Name = streamFile.Name;
StreamFile = streamFile;
FileSize = (ulong)StreamFile.FileInfo.Length;
if (StreamFile.InterleavedFile != null)
{
InterleavedFileSize = (ulong)StreamFile.InterleavedFile.FileInfo.Length;
}
}
StreamClipFile = streamClipFile;
}
public string DisplayName
{
get
{
if (StreamFile != null &&
StreamFile.InterleavedFile != null &&
BDInfoSettings.EnableSSIF)
{
return StreamFile.InterleavedFile.Name;
}
return Name;
}
}
public ulong PacketSize => PacketCount * 192;
public ulong PacketBitRate
{
get
{
if (PacketSeconds > 0)
{
return (ulong)Math.Round(((PacketSize * 8.0) / PacketSeconds));
}
return 0;
}
}
public bool IsCompatible(TSStreamClip clip)
{
foreach (var stream1 in StreamFile.Streams.Values)
{
if (clip.StreamFile.Streams.ContainsKey(stream1.PID))
{
var stream2 = clip.StreamFile.Streams[stream1.PID];
if (stream1.StreamType != stream2.StreamType)
{
return false;
}
}
}
return true;
}
}
}
+246
View File
@@ -0,0 +1,246 @@
//============================================================================
// BDInfo - Blu-ray Video and Audio Analysis Tool
// Copyright © 2010 Cinema Squid
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//=============================================================================
#undef DEBUG
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using MediaBrowser.Model.IO;
namespace BDInfo
{
public class TSStreamClipFile
{
private readonly IFileSystem _fileSystem;
public FileSystemMetadata FileInfo = null;
public string FileType = null;
public bool IsValid = false;
public string Name = null;
public Dictionary<ushort, TSStream> Streams =
new Dictionary<ushort, TSStream>();
public TSStreamClipFile(FileSystemMetadata fileInfo, IFileSystem fileSystem)
{
FileInfo = fileInfo;
_fileSystem = fileSystem;
Name = fileInfo.Name.ToUpper();
}
public void Scan()
{
Stream fileStream = null;
BinaryReader fileReader = null;
try
{
#if DEBUG
Debug.WriteLine(string.Format(
"Scanning {0}...", Name));
#endif
Streams.Clear();
fileStream = _fileSystem.OpenRead(FileInfo.FullName);
fileReader = new BinaryReader(fileStream);
byte[] data = new byte[fileStream.Length];
fileReader.Read(data, 0, data.Length);
byte[] fileType = new byte[8];
Array.Copy(data, 0, fileType, 0, fileType.Length);
FileType = Encoding.ASCII.GetString(fileType, 0, fileType.Length);
if (FileType != "HDMV0100" &&
FileType != "HDMV0200")
{
throw new Exception(string.Format(
"Clip info file {0} has an unknown file type {1}.",
FileInfo.Name, FileType));
}
#if DEBUG
Debug.WriteLine(string.Format(
"\tFileType: {0}", FileType));
#endif
int clipIndex =
((int)data[12] << 24) +
((int)data[13] << 16) +
((int)data[14] << 8) +
((int)data[15]);
int clipLength =
((int)data[clipIndex] << 24) +
((int)data[clipIndex + 1] << 16) +
((int)data[clipIndex + 2] << 8) +
((int)data[clipIndex + 3]);
byte[] clipData = new byte[clipLength];
Array.Copy(data, clipIndex + 4, clipData, 0, clipData.Length);
int streamCount = clipData[8];
#if DEBUG
Debug.WriteLine(string.Format(
"\tStreamCount: {0}", streamCount));
#endif
int streamOffset = 10;
for (int streamIndex = 0;
streamIndex < streamCount;
streamIndex++)
{
TSStream stream = null;
ushort PID = (ushort)
((clipData[streamOffset] << 8) +
clipData[streamOffset + 1]);
streamOffset += 2;
var streamType = (TSStreamType)
clipData[streamOffset + 1];
switch (streamType)
{
case TSStreamType.MVC_VIDEO:
// TODO
break;
case TSStreamType.AVC_VIDEO:
case TSStreamType.MPEG1_VIDEO:
case TSStreamType.MPEG2_VIDEO:
case TSStreamType.VC1_VIDEO:
{
var videoFormat = (TSVideoFormat)
(clipData[streamOffset + 2] >> 4);
var frameRate = (TSFrameRate)
(clipData[streamOffset + 2] & 0xF);
var aspectRatio = (TSAspectRatio)
(clipData[streamOffset + 3] >> 4);
stream = new TSVideoStream();
((TSVideoStream)stream).VideoFormat = videoFormat;
((TSVideoStream)stream).AspectRatio = aspectRatio;
((TSVideoStream)stream).FrameRate = frameRate;
#if DEBUG
Debug.WriteLine(string.Format(
"\t{0} {1} {2} {3} {4}",
PID,
streamType,
videoFormat,
frameRate,
aspectRatio));
#endif
}
break;
case TSStreamType.AC3_AUDIO:
case TSStreamType.AC3_PLUS_AUDIO:
case TSStreamType.AC3_PLUS_SECONDARY_AUDIO:
case TSStreamType.AC3_TRUE_HD_AUDIO:
case TSStreamType.DTS_AUDIO:
case TSStreamType.DTS_HD_AUDIO:
case TSStreamType.DTS_HD_MASTER_AUDIO:
case TSStreamType.DTS_HD_SECONDARY_AUDIO:
case TSStreamType.LPCM_AUDIO:
case TSStreamType.MPEG1_AUDIO:
case TSStreamType.MPEG2_AUDIO:
{
byte[] languageBytes = new byte[3];
Array.Copy(clipData, streamOffset + 3,
languageBytes, 0, languageBytes.Length);
string languageCode = Encoding.ASCII.GetString(languageBytes, 0, languageBytes.Length);
var channelLayout = (TSChannelLayout)
(clipData[streamOffset + 2] >> 4);
var sampleRate = (TSSampleRate)
(clipData[streamOffset + 2] & 0xF);
stream = new TSAudioStream();
((TSAudioStream)stream).LanguageCode = languageCode;
((TSAudioStream)stream).ChannelLayout = channelLayout;
((TSAudioStream)stream).SampleRate = TSAudioStream.ConvertSampleRate(sampleRate);
((TSAudioStream)stream).LanguageCode = languageCode;
#if DEBUG
Debug.WriteLine(string.Format(
"\t{0} {1} {2} {3} {4}",
PID,
streamType,
languageCode,
channelLayout,
sampleRate));
#endif
}
break;
case TSStreamType.INTERACTIVE_GRAPHICS:
case TSStreamType.PRESENTATION_GRAPHICS:
{
byte[] languageBytes = new byte[3];
Array.Copy(clipData, streamOffset + 2,
languageBytes, 0, languageBytes.Length);
string languageCode = Encoding.ASCII.GetString(languageBytes, 0, languageBytes.Length);
stream = new TSGraphicsStream();
stream.LanguageCode = languageCode;
#if DEBUG
Debug.WriteLine(string.Format(
"\t{0} {1} {2}",
PID,
streamType,
languageCode));
#endif
}
break;
case TSStreamType.SUBTITLE:
{
byte[] languageBytes = new byte[3];
Array.Copy(clipData, streamOffset + 3,
languageBytes, 0, languageBytes.Length);
string languageCode = Encoding.ASCII.GetString(languageBytes, 0, languageBytes.Length);
#if DEBUG
Debug.WriteLine(string.Format(
"\t{0} {1} {2}",
PID,
streamType,
languageCode));
#endif
stream = new TSTextStream();
stream.LanguageCode = languageCode;
}
break;
}
if (stream != null)
{
stream.PID = PID;
stream.StreamType = streamType;
Streams.Add(PID, stream);
}
streamOffset += clipData[streamOffset] + 1;
}
IsValid = true;
}
finally
{
if (fileReader != null) fileReader.Dispose();
if (fileStream != null) fileStream.Dispose();
}
}
}
}
File diff suppressed because it is too large Load Diff
+12 -129
View File
@@ -1,137 +1,20 @@
# Jellyfin Contributors # Jellyfin Contributors
- [97carmine](https://github.com/97carmine) - [JoshuaBoniface](https://github.com/joshuaboniface)
- [Abbe98](https://github.com/Abbe98)
- [agrenott](https://github.com/agrenott)
- [AndreCarvalho](https://github.com/AndreCarvalho)
- [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)
- [Bond-009](https://github.com/Bond-009)
- [brianjmurrell](https://github.com/brianjmurrell)
- [bugfixin](https://github.com/bugfixin)
- [chaosinnovator](https://github.com/chaosinnovator)
- [ckcr4lyf](https://github.com/ckcr4lyf)
- [crankdoofus](https://github.com/crankdoofus)
- [crobibero](https://github.com/crobibero)
- [cromefire](https://github.com/cromefire)
- [cryptobank](https://github.com/cryptobank)
- [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)
- [dkanada](https://github.com/dkanada)
- [dlahoti](https://github.com/dlahoti)
- [dmitrylyzo](https://github.com/dmitrylyzo)
- [DMouse10462](https://github.com/DMouse10462)
- [DrPandemic](https://github.com/DrPandemic)
- [EraYaN](https://github.com/EraYaN)
- [escabe](https://github.com/escabe)
- [excelite](https://github.com/excelite)
- [fasheng](https://github.com/fasheng)
- [ferferga](https://github.com/ferferga)
- [fhriley](https://github.com/fhriley)
- [flemse](https://github.com/flemse)
- [Froghut](https://github.com/Froghut)
- [fruhnow](https://github.com/fruhnow)
- [geilername](https://github.com/geilername)
- [gnattu](https://github.com/gnattu)
- [grafixeyehero](https://github.com/grafixeyehero)
- [h1nk](https://github.com/h1nk)
- [hawken93](https://github.com/hawken93)
- [HelloWorld017](https://github.com/HelloWorld017)
- [jftuga](https://github.com/jftuga)
- [joern-h](https://github.com/joern-h)
- [joshuaboniface](https://github.com/joshuaboniface)
- [JustAMan](https://github.com/JustAMan)
- [justinfenn](https://github.com/justinfenn)
- [KerryRJ](https://github.com/KerryRJ)
- [Larvitar](https://github.com/Larvitar)
- [LeoVerto](https://github.com/LeoVerto)
- [Liggy](https://github.com/Liggy)
- [LogicalPhallacy](https://github.com/LogicalPhallacy)
- [loli10K](https://github.com/loli10K)
- [lostmypillow](https://github.com/lostmypillow)
- [Lynxy](https://github.com/Lynxy)
- [ManfredRichthofen](https://github.com/ManfredRichthofen)
- [Marenz](https://github.com/Marenz)
- [marius-luca-87](https://github.com/marius-luca-87)
- [mark-monteiro](https://github.com/mark-monteiro)
- [Matt07211](https://github.com/Matt07211)
- [mcarlton00](https://github.com/mcarlton00)
- [mitchfizz05](https://github.com/mitchfizz05)
- [MrTimscampi](https://github.com/MrTimscampi)
- [n8225](https://github.com/n8225)
- [Narfinger](https://github.com/Narfinger)
- [NathanPickard](https://github.com/NathanPickard)
- [neilsb](https://github.com/neilsb)
- [nevado](https://github.com/nevado)
- [Nickbert7](https://github.com/Nickbert7)
- [nvllsvm](https://github.com/nvllsvm) - [nvllsvm](https://github.com/nvllsvm)
- [nyanmisaka](https://github.com/nyanmisaka) - [JustAMan](https://github.com/JustAMan)
- [oddstr13](https://github.com/oddstr13) - [dcrdev](https://github.com/dcrdev)
- [petermcneil](https://github.com/petermcneil) - [EraYaN](https://github.com/EraYaN)
- [Phlogi](https://github.com/Phlogi) - [flemse](https://github.com/flemse)
- [pjeanjean](https://github.com/pjeanjean) - [bfayers](https://github.com/bfayers)
- [ploughpuff](https://github.com/ploughpuff) - [Bond_009](https://github.com/Bond-009)
- [pR0Ps](https://github.com/pR0Ps) - [AnthonyLavado](https://github.com/anthonylavado)
- [PrplHaz4](https://github.com/PrplHaz4)
- [RazeLighter777](https://github.com/RazeLighter777)
- [redSpoutnik](https://github.com/redSpoutnik)
- [ringmatter](https://github.com/ringmatter)
- [ryan-hartzell](https://github.com/ryan-hartzell)
- [s0urcelab](https://github.com/s0urcelab)
- [sachk](https://github.com/sachk)
- [sammyrc34](https://github.com/sammyrc34)
- [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)
- [sl1288](https://github.com/sl1288)
- [sorinyo2004](https://github.com/sorinyo2004)
- [sparky8251](https://github.com/sparky8251) - [sparky8251](https://github.com/sparky8251)
- [stanionascu](https://github.com/stanionascu) - [LeoVerto](https://github.com/LeoVerto)
- [stevehayles](https://github.com/stevehayles) - [grafixeyehero](https://github.com/grafixeyehero)
- [SuperSandro2000](https://github.com/SuperSandro2000) - [cvium](https://github.com/cvium)
- [tbraeutigam](https://github.com/tbraeutigam)
- [teacupx](https://github.com/teacupx)
- [Terror-Gene](https://github.com/Terror-Gene)
- [ThatNerdyPikachu](https://github.com/ThatNerdyPikachu)
- [ThibaultNocchi](https://github.com/ThibaultNocchi)
- [thornbill](https://github.com/thornbill)
- [ThreeFive-O](https://github.com/ThreeFive-O)
- [TrisMcC](https://github.com/TrisMcC)
- [trumblejoe](https://github.com/trumblejoe)
- [TtheCreator](https://github.com/TtheCreator)
- [twinkybot](https://github.com/twinkybot)
- [Ullmie02](https://github.com/Ullmie02)
- [Unhelpful](https://github.com/Unhelpful)
- [viaregio](https://github.com/viaregio)
- [vitorsemeano](https://github.com/vitorsemeano)
- [voodoos](https://github.com/voodoos)
- [whooo](https://github.com/whooo)
- [WiiPlayer2](https://github.com/WiiPlayer2)
- [WillWill56](https://github.com/WillWill56)
- [wtayl0r](https://github.com/wtayl0r) - [wtayl0r](https://github.com/wtayl0r)
- [Wuerfelbecher](https://github.com/Wuerfelbecher) - [TtheCreator](https://github.com/Tthecreator)
- [Wunax](https://github.com/Wunax)
- [WWWesten](https://github.com/WWWesten)
- [WX9yMOXWId](https://github.com/WX9yMOXWId)
- [xosdy](https://github.com/xosdy)
- [XVicarious](https://github.com/XVicarious)
- [YouKnowBlom](https://github.com/YouKnowBlom)
- [KristupasSavickas](https://github.com/KristupasSavickas)
- [Pusta](https://github.com/pusta)
# Emby Contributors # Emby Contributors
+29 -52
View File
@@ -1,60 +1,37 @@
ARG DOTNET_VERSION=3.1 ARG DOTNET_VERSION=2
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-* \
&& yarn install \
&& mv dist /dist
FROM mcr.microsoft.com/dotnet/core/sdk:${DOTNET_VERSION}-buster as builder # Download ffmpeg first to allow quicker rebuild of other layers
FROM alpine as ffmpeg
ARG FFMPEG_URL=https://www.johnvansickle.com/ffmpeg/old-releases/ffmpeg-4.0.3-64bit-static.tar.xz
RUN wget ${FFMPEG_URL} -O - | tar Jxf - \
&& mkdir ffmpeg-bin \
&& mv ffmpeg*/ffmpeg ffmpeg-bin \
&& mv ffmpeg*/ffprobe ffmpeg-bin
FROM microsoft/dotnet:${DOTNET_VERSION}-sdk as builder
WORKDIR /repo WORKDIR /repo
COPY . . COPY . .
ENV DOTNET_CLI_TELEMETRY_OPTOUT=1 RUN export DOTNET_CLI_TELEMETRY_OPTOUT=1 \
# because of changes in docker and systemd we need to not build in parallel at the moment && dotnet clean \
# see https://success.docker.com/article/how-to-reserve-resource-temporarily-unavailable-errors-due-to-tasksmax-setting && dotnet publish \
RUN dotnet publish Jellyfin.Server --disable-parallel --configuration Release --output="/jellyfin" --self-contained --runtime linux-x64 "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none" --configuration release \
--output /jellyfin \
Jellyfin.Server
FROM debian:buster-slim
# https://askubuntu.com/questions/972516/debian-frontend-environment-variable
ARG DEBIAN_FRONTEND="noninteractive"
# http://stackoverflow.com/questions/48162574/ddg#49462622
ARG APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE=DontWarn
# https://github.com/NVIDIA/nvidia-docker/wiki/Installation-(Native-GPU-Support)
ENV NVIDIA_DRIVER_CAPABILITIES="compute,video,utility"
FROM microsoft/dotnet:${DOTNET_VERSION}-runtime
COPY --from=builder /jellyfin /jellyfin COPY --from=builder /jellyfin /jellyfin
COPY --from=web-builder /dist /jellyfin/jellyfin-web COPY --from=ffmpeg /ffmpeg-bin/* /usr/bin/
# Install dependencies:
# mesa-va-drivers: needed for AMD VAAPI
RUN apt-get update \
&& apt-get install --no-install-recommends --no-install-suggests -y ca-certificates gnupg wget apt-transport-https \
&& wget -O - https://repo.jellyfin.org/jellyfin_team.gpg.key | apt-key add - \
&& echo "deb [arch=$( dpkg --print-architecture )] https://repo.jellyfin.org/$( awk -F'=' '/^ID=/{ print $NF }' /etc/os-release ) $( awk -F'=' '/^VERSION_CODENAME=/{ print $NF }' /etc/os-release ) main" | tee /etc/apt/sources.list.d/jellyfin.list \
&& apt-get update \
&& apt-get install --no-install-recommends --no-install-suggests -y \
mesa-va-drivers \
jellyfin-ffmpeg \
openssl \
locales \
&& apt-get remove gnupg wget apt-transport-https -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
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 EXPOSE 8096
VOLUME /cache /config /media VOLUME /config /media
ENTRYPOINT ["./jellyfin/jellyfin", \
"--datadir", "/config", \ # libfontconfig1 is required for Skia
"--cachedir", "/cache", \ RUN apt-get update \
"--ffmpeg", "/usr/lib/jellyfin-ffmpeg/ffmpeg"] && apt-get install --no-install-recommends --no-install-suggests -y \
libfontconfig1 \
&& apt-get clean autoclean \
&& apt-get autoremove \
&& rm -rf /var/lib/{apt,dpkg,cache,log}
ENTRYPOINT dotnet /jellyfin/jellyfin.dll -programdata /config
+16 -69
View File
@@ -1,77 +1,24 @@
# DESIGNED FOR BUILDING ON AMD64 ONLY ARG DOTNET_VERSION=3.0
#####################################
# Requires binfm_misc registration
# https://github.com/multiarch/qemu-user-static#binfmt_misc-register
ARG DOTNET_VERSION=3.1
FROM node:alpine as web-builder FROM microsoft/dotnet:${DOTNET_VERSION}-sdk-stretch-arm32v7 as 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-* \
&& yarn install \
&& mv dist /dist
FROM mcr.microsoft.com/dotnet/core/sdk:${DOTNET_VERSION} as builder
WORKDIR /repo WORKDIR /repo
COPY . . COPY . .
ENV DOTNET_CLI_TELEMETRY_OPTOUT=1 #TODO Remove or update the sed line when we update dotnet version.
# Discard objs - may cause failures if exists RUN export DOTNET_CLI_TELEMETRY_OPTOUT=1 \
RUN find . -type d -name obj | xargs -r rm -r && find . -type f -exec sed -i 's/netcoreapp2.1/netcoreapp3.0/g' {} \; \
# Build && dotnet clean -maxcpucount:1 \
RUN dotnet publish Jellyfin.Server --configuration Release --output="/jellyfin" --self-contained --runtime linux-arm "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none" && dotnet publish \
-maxcpucount:1 \
--configuration release \
--output /jellyfin \
Jellyfin.Server
FROM multiarch/qemu-user-static:x86_64-arm as qemu FROM microsoft/dotnet:${DOTNET_VERSION}-runtime-stretch-slim-arm32v7
FROM arm32v7/debian:buster-slim
# https://askubuntu.com/questions/972516/debian-frontend-environment-variable
ARG DEBIAN_FRONTEND="noninteractive"
# http://stackoverflow.com/questions/48162574/ddg#49462622
ARG APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE=DontWarn
# https://github.com/NVIDIA/nvidia-docker/wiki/Installation-(Native-GPU-Support)
ENV NVIDIA_DRIVER_CAPABILITIES="compute,video,utility"
COPY --from=qemu /usr/bin/qemu-arm-static /usr/bin
RUN apt-get update \
&& apt-get install --no-install-recommends --no-install-suggests -y ca-certificates gnupg curl && \
curl -ks https://repo.jellyfin.org/debian/jellyfin_team.gpg.key | apt-key add - && \
curl -ks https://keyserver.ubuntu.com/pks/lookup?op=get\&search=0x6587ffd6536b8826e88a62547876ae518cbcf2f2 | apt-key add - && \
echo 'deb [arch=armhf] https://repo.jellyfin.org/debian buster main' > /etc/apt/sources.list.d/jellyfin.list && \
echo "deb http://ppa.launchpad.net/ubuntu-raspi2/ppa/ubuntu bionic main">> /etc/apt/sources.list.d/raspbins.list && \
apt-get update && \
apt-get install --no-install-recommends --no-install-suggests -y \
jellyfin-ffmpeg \
libssl-dev \
libfontconfig1 \
libfreetype6 \
libomxil-bellagio0 \
libomxil-bellagio-bin \
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
COPY --from=builder /jellyfin /jellyfin 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 EXPOSE 8096
VOLUME /cache /config /media RUN apt-get update \
ENTRYPOINT ["./jellyfin/jellyfin", \ && apt-get install -y ffmpeg
"--datadir", "/config", \ VOLUME /config /media
"--cachedir", "/cache", \ ENTRYPOINT dotnet /jellyfin/jellyfin.dll -programdata /config
"--ffmpeg", "/usr/lib/jellyfin-ffmpeg/ffmpeg"]
+24 -57
View File
@@ -1,66 +1,33 @@
# DESIGNED FOR BUILDING ON AMD64 ONLY # Requires binfm_misc registration for aarch64
#####################################
# Requires binfm_misc registration
# https://github.com/multiarch/qemu-user-static#binfmt_misc-register # https://github.com/multiarch/qemu-user-static#binfmt_misc-register
ARG DOTNET_VERSION=3.1 ARG DOTNET_VERSION=3.0
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-* \
&& yarn install \
&& mv dist /dist
FROM mcr.microsoft.com/dotnet/core/sdk:${DOTNET_VERSION} as builder
WORKDIR /repo
COPY . .
ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
# Discard objs - may cause failures if exists
RUN find . -type d -name obj | xargs -r rm -r
# Build
RUN dotnet publish Jellyfin.Server --configuration Release --output="/jellyfin" --self-contained --runtime linux-arm64 "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none"
FROM multiarch/qemu-user-static:x86_64-aarch64 as qemu FROM multiarch/qemu-user-static:x86_64-aarch64 as qemu
FROM arm64v8/debian:buster-slim FROM alpine as qemu_extract
COPY --from=qemu /usr/bin qemu_user_static.tgz
RUN tar -xzvf qemu_user_static.tgz
# https://askubuntu.com/questions/972516/debian-frontend-environment-variable
ARG DEBIAN_FRONTEND="noninteractive"
# http://stackoverflow.com/questions/48162574/ddg#49462622
ARG APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE=DontWarn
# https://github.com/NVIDIA/nvidia-docker/wiki/Installation-(Native-GPU-Support)
ENV NVIDIA_DRIVER_CAPABILITIES="compute,video,utility"
COPY --from=qemu /usr/bin/qemu-aarch64-static /usr/bin FROM microsoft/dotnet:${DOTNET_VERSION}-sdk-stretch-arm64v8 as builder
RUN apt-get update && apt-get install --no-install-recommends --no-install-suggests -y \ COPY --from=qemu_extract qemu-* /usr/bin
ffmpeg \ WORKDIR /repo
libssl-dev \ COPY . .
ca-certificates \ #TODO Remove or update the sed line when we update dotnet version.
libfontconfig1 \ RUN export DOTNET_CLI_TELEMETRY_OPTOUT=1 \
libfreetype6 \ && find . -type f -exec sed -i 's/netcoreapp2.1/netcoreapp3.0/g' {} \; \
libomxil-bellagio0 \ && dotnet clean \
libomxil-bellagio-bin \ && dotnet publish \
locales \ --configuration release \
&& apt-get clean autoclean -y \ --output /jellyfin \
&& apt-get autoremove -y \ Jellyfin.Server
&& 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
FROM microsoft/dotnet:${DOTNET_VERSION}-runtime-stretch-slim-arm64v8
COPY --from=qemu_extract qemu-* /usr/bin
COPY --from=builder /jellyfin /jellyfin 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 EXPOSE 8096
VOLUME /cache /config /media RUN apt-get update \
ENTRYPOINT ["./jellyfin/jellyfin", \ && apt-get install -y ffmpeg
"--datadir", "/config", \ VOLUME /config /media
"--cachedir", "/cache", \ ENTRYPOINT dotnet /jellyfin/jellyfin.dll -programdata /config
"--ffmpeg", "/usr/bin/ffmpeg"]
+10 -5
View File
@@ -1,6 +1,4 @@
#pragma warning disable CS1591 using System;
using System.Buffers.Binary;
using System.IO; using System.IO;
namespace DvdLib namespace DvdLib
@@ -14,12 +12,19 @@ namespace DvdLib
public override ushort ReadUInt16() public override ushort ReadUInt16()
{ {
return BinaryPrimitives.ReadUInt16BigEndian(base.ReadBytes(2)); return BitConverter.ToUInt16(ReadAndReverseBytes(2), 0);
} }
public override uint ReadUInt32() 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;
} }
} }
} }
+5 -8
View File
@@ -1,19 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk"> <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> <ItemGroup>
<Compile Include="..\SharedVersion.cs" /> <Compile Include="..\SharedVersion.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" />
</ItemGroup>
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework> <TargetFramework>netstandard2.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup> </PropertyGroup>
</Project> </Project>
+36
View File
@@ -0,0 +1,36 @@
namespace DvdLib.Ifo
{
public enum AudioCodec
{
AC3 = 0,
MPEG1 = 2,
MPEG2ext = 3,
LPCM = 4,
DTS = 6,
}
public enum ApplicationMode
{
Unspecified = 0,
Karaoke = 1,
Surround = 2,
}
public class AudioAttributes
{
public readonly AudioCodec Codec;
public readonly bool MultichannelExtensionPresent;
public readonly ApplicationMode Mode;
public readonly byte QuantDRC;
public readonly byte SampleRate;
public readonly byte Channels;
public readonly ushort LanguageCode;
public readonly byte LanguageExtension;
public readonly byte CodeExtension;
}
public class MultiChannelExtension
{
}
}
-3
View File
@@ -1,5 +1,3 @@
#pragma warning disable CS1591
using System.IO; using System.IO;
namespace DvdLib.Ifo namespace DvdLib.Ifo
@@ -7,7 +5,6 @@ namespace DvdLib.Ifo
public class Cell public class Cell
{ {
public CellPlaybackInfo PlaybackInfo { get; private set; } public CellPlaybackInfo PlaybackInfo { get; private set; }
public CellPositionInfo PositionInfo { get; private set; } public CellPositionInfo PositionInfo { get; private set; }
internal void ParsePlayback(BinaryReader br) internal void ParsePlayback(BinaryReader br)
-2
View File
@@ -1,5 +1,3 @@
#pragma warning disable CS1591
using System.IO; using System.IO;
namespace DvdLib.Ifo namespace DvdLib.Ifo
-2
View File
@@ -1,5 +1,3 @@
#pragma warning disable CS1591
using System.IO; using System.IO;
namespace DvdLib.Ifo namespace DvdLib.Ifo
-4
View File
@@ -1,13 +1,9 @@
#pragma warning disable CS1591
namespace DvdLib.Ifo namespace DvdLib.Ifo
{ {
public class Chapter public class Chapter
{ {
public ushort ProgramChainNumber { get; private set; } public ushort ProgramChainNumber { get; private set; }
public ushort ProgramNumber { get; private set; } public ushort ProgramNumber { get; private set; }
public uint ChapterNumber { get; private set; } public uint ChapterNumber { get; private set; }
public Chapter(ushort pgcNum, ushort programNum, uint chapterNum) public Chapter(ushort pgcNum, ushort programNum, uint chapterNum)
+20 -28
View File
@@ -1,9 +1,8 @@
#pragma warning disable CS1591
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using MediaBrowser.Model.IO;
namespace DvdLib.Ifo namespace DvdLib.Ifo
{ {
@@ -14,33 +13,36 @@ namespace DvdLib.Ifo
private ushort _titleCount; private ushort _titleCount;
public readonly Dictionary<ushort, string> VTSPaths = new Dictionary<ushort, string>(); 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>(); 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)) ?? 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)); allFiles.FirstOrDefault(i => string.Equals(i.Name, "VIDEO_TS.BUP", StringComparison.OrdinalIgnoreCase));
if (vmgPath == null) if (vmgPath == null)
{ {
foreach (var ifo in allFiles) var allIfos = allFiles.Where(i => string.Equals(i.Extension, ".ifo", StringComparison.OrdinalIgnoreCase));
{
if (!string.Equals(ifo.Extension, ".ifo", StringComparison.OrdinalIgnoreCase))
{
continue;
}
var nums = ifo.Name.Split(new[] { '_' }, StringSplitOptions.RemoveEmptyEntries); foreach (var ifo in allIfos)
if (nums.Length >= 2 && ushort.TryParse(nums[1], out var ifoNumber)) {
var num = ifo.Name.Split('_').ElementAtOrDefault(1);
var numbersRead = new List<ushort>();
if (!string.IsNullOrEmpty(num) && ushort.TryParse(num, out var ifoNumber) && !numbersRead.Contains(ifoNumber))
{ {
ReadVTS(ifoNumber, ifo.FullName); ReadVTS(ifoNumber, ifo.FullName);
numbersRead.Add(ifoNumber);
} }
} }
} }
else else
{ {
using (var vmgFs = new FileStream(vmgPath.FullName, FileMode.Open, FileAccess.Read, FileShare.Read)) using (var vmgFs = _fileSystem.GetFileStream(vmgPath.FullName, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read))
{ {
using (var vmgRead = new BigEndianBinaryReader(vmgFs)) using (var vmgRead = new BigEndianBinaryReader(vmgFs))
{ {
@@ -74,7 +76,7 @@ namespace DvdLib.Ifo
} }
} }
private void ReadVTS(ushort vtsNum, IReadOnlyList<FileInfo> allFiles) private void ReadVTS(ushort vtsNum, List<FileSystemMetadata> allFiles)
{ {
var filename = string.Format("VTS_{0:00}_0.IFO", vtsNum); var filename = string.Format("VTS_{0:00}_0.IFO", vtsNum);
@@ -93,7 +95,7 @@ namespace DvdLib.Ifo
{ {
VTSPaths[vtsNum] = vtsPath; VTSPaths[vtsNum] = vtsPath;
using (var vtsFs = new FileStream(vtsPath, FileMode.Open, FileAccess.Read, FileShare.Read)) using (var vtsFs = _fileSystem.GetFileStream(vtsPath, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read))
{ {
using (var vtsRead = new BigEndianBinaryReader(vtsFs)) using (var vtsRead = new BigEndianBinaryReader(vtsFs))
{ {
@@ -117,19 +119,12 @@ namespace DvdLib.Ifo
uint chapNum = 1; uint chapNum = 1;
vtsFs.Seek(baseAddr + offsets[titleNum], SeekOrigin.Begin); vtsFs.Seek(baseAddr + offsets[titleNum], SeekOrigin.Begin);
var t = Titles.FirstOrDefault(vtst => vtst.IsVTSTitle(vtsNum, titleNum + 1)); var t = Titles.FirstOrDefault(vtst => vtst.IsVTSTitle(vtsNum, titleNum + 1));
if (t == null) if (t == null) continue;
{
continue;
}
do do
{ {
t.Chapters.Add(new Chapter(vtsRead.ReadUInt16(), vtsRead.ReadUInt16(), chapNum)); t.Chapters.Add(new Chapter(vtsRead.ReadUInt16(), vtsRead.ReadUInt16(), chapNum));
if (titleNum + 1 < numTitles && vtsFs.Position == (baseAddr + offsets[titleNum + 1])) if (titleNum + 1 < numTitles && vtsFs.Position == (baseAddr + offsets[titleNum + 1])) break;
{
break;
}
chapNum++; chapNum++;
} }
while (vtsFs.Position < (baseAddr + endaddr)); while (vtsFs.Position < (baseAddr + endaddr));
@@ -154,10 +149,7 @@ namespace DvdLib.Ifo
uint vtsPgcOffset = vtsRead.ReadUInt32(); uint vtsPgcOffset = vtsRead.ReadUInt32();
var t = Titles.FirstOrDefault(vtst => vtst.IsVTSTitle(vtsNum, titleNum)); var t = Titles.FirstOrDefault(vtst => vtst.IsVTSTitle(vtsNum, titleNum));
if (t != null) if (t != null) t.AddPgc(vtsRead, startByte + vtsPgcOffset, entryPgc, pgcNum);
{
t.AddPgc(vtsRead, startByte + vtsPgcOffset, entryPgc, pgcNum);
}
} }
} }
} }
+2 -10
View File
@@ -1,5 +1,3 @@
#pragma warning disable CS1591
using System; using System;
namespace DvdLib.Ifo namespace DvdLib.Ifo
@@ -15,14 +13,8 @@ namespace DvdLib.Ifo
Second = GetBCDValue(data[2]); Second = GetBCDValue(data[2]);
Frames = GetBCDValue((byte)(data[3] & 0x3F)); Frames = GetBCDValue((byte)(data[3] & 0x3F));
if ((data[3] & 0x80) != 0) if ((data[3] & 0x80) != 0) FrameRate = 30;
{ else if ((data[3] & 0x40) != 0) FrameRate = 25;
FrameRate = 30;
}
else if ((data[3] & 0x40) != 0)
{
FrameRate = 25;
}
} }
private static byte GetBCDValue(byte data) private static byte GetBCDValue(byte data)
+17
View File
@@ -0,0 +1,17 @@
using System.Collections.Generic;
namespace DvdLib.Ifo
{
public class ProgramChainCommandTable
{
public readonly ushort LastByteAddress;
public readonly List<VirtualMachineCommand> PreCommands;
public readonly List<VirtualMachineCommand> PostCommands;
public readonly List<VirtualMachineCommand> CellCommands;
}
public class VirtualMachineCommand
{
public readonly byte[] Command;
}
}
+1 -3
View File
@@ -1,12 +1,10 @@
#pragma warning disable CS1591
using System.Collections.Generic; using System.Collections.Generic;
namespace DvdLib.Ifo namespace DvdLib.Ifo
{ {
public class Program public class Program
{ {
public IReadOnlyList<Cell> Cells { get; } public readonly List<Cell> Cells;
public Program(List<Cell> cells) public Program(List<Cell> cells)
{ {
+6 -15
View File
@@ -1,5 +1,3 @@
#pragma warning disable CS1591
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@@ -22,27 +20,27 @@ namespace DvdLib.Ifo
public readonly List<Cell> Cells; public readonly List<Cell> Cells;
public DvdTime PlaybackTime { get; private set; } public DvdTime PlaybackTime { get; private set; }
public UserOperation ProhibitedUserOperations { get; private set; } public UserOperation ProhibitedUserOperations { get; private set; }
public byte[] AudioStreamControl { get; private set; } // 8*2 entries public byte[] AudioStreamControl { get; private set; } // 8*2 entries
public byte[] SubpictureStreamControl { get; private set; } // 32*4 entries public byte[] SubpictureStreamControl { get; private set; } // 32*4 entries
private ushort _nextProgramNumber; private ushort _nextProgramNumber;
public readonly ProgramChain Next;
private ushort _prevProgramNumber; private ushort _prevProgramNumber;
public readonly ProgramChain Previous;
private ushort _goupProgramNumber; private ushort _goupProgramNumber;
public readonly ProgramChain Goup; // ?? maybe Group
public ProgramPlaybackMode PlaybackMode { get; private set; } public ProgramPlaybackMode PlaybackMode { get; private set; }
public uint ProgramCount { get; private set; } public uint ProgramCount { get; private set; }
public byte StillTime { get; private set; } public byte StillTime { get; private set; }
public byte[] Palette { get; private set; } // 16*4 entries public byte[] Palette { get; private set; } // 16*4 entries
private ushort _commandTableOffset; private ushort _commandTableOffset;
public readonly ProgramChainCommandTable CommandTable;
private ushort _programMapOffset; private ushort _programMapOffset;
private ushort _cellPlaybackOffset; private ushort _cellPlaybackOffset;
@@ -75,15 +73,8 @@ namespace DvdLib.Ifo
StillTime = br.ReadByte(); StillTime = br.ReadByte();
byte pbMode = br.ReadByte(); byte pbMode = br.ReadByte();
if (pbMode == 0) if (pbMode == 0) PlaybackMode = ProgramPlaybackMode.Sequential;
{ else PlaybackMode = ((pbMode & 0x80) == 0) ? ProgramPlaybackMode.Random : ProgramPlaybackMode.Shuffle;
PlaybackMode = ProgramPlaybackMode.Sequential;
}
else
{
PlaybackMode = ((pbMode & 0x80) == 0) ? ProgramPlaybackMode.Random : ProgramPlaybackMode.Shuffle;
}
ProgramCount = (uint)(pbMode & 0x7F); ProgramCount = (uint)(pbMode & 0x7F);
Palette = br.ReadBytes(64); Palette = br.ReadBytes(64);
+1 -10
View File
@@ -1,5 +1,3 @@
#pragma warning disable CS1591
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
@@ -8,11 +6,8 @@ namespace DvdLib.Ifo
public class Title public class Title
{ {
public uint TitleNumber { get; private set; } public uint TitleNumber { get; private set; }
public uint AngleCount { get; private set; } public uint AngleCount { get; private set; }
public ushort ChapterCount { get; private set; } public ushort ChapterCount { get; private set; }
public byte VideoTitleSetNumber { get; private set; } public byte VideoTitleSetNumber { get; private set; }
private ushort _parentalManagementMask; private ushort _parentalManagementMask;
@@ -20,7 +15,6 @@ namespace DvdLib.Ifo
private uint _vtsStartSector; // relative to start of entire disk private uint _vtsStartSector; // relative to start of entire disk
public ProgramChain EntryProgramChain { get; private set; } public ProgramChain EntryProgramChain { get; private set; }
public readonly List<ProgramChain> ProgramChains; public readonly List<ProgramChain> ProgramChains;
public readonly List<Chapter> Chapters; public readonly List<Chapter> Chapters;
@@ -59,10 +53,7 @@ namespace DvdLib.Ifo
var pgc = new ProgramChain(pgcNum); var pgc = new ProgramChain(pgcNum);
pgc.ParseHeader(br); pgc.ParseHeader(br);
ProgramChains.Add(pgc); ProgramChains.Add(pgc);
if (entryPgc) if (entryPgc) EntryProgramChain = pgc;
{
EntryProgramChain = pgc;
}
br.BaseStream.Seek(curPos, SeekOrigin.Begin); br.BaseStream.Seek(curPos, SeekOrigin.Begin);
} }
-2
View File
@@ -1,5 +1,3 @@
#pragma warning disable CS1591
using System; using System;
namespace DvdLib.Ifo namespace DvdLib.Ifo
+46
View File
@@ -0,0 +1,46 @@
namespace DvdLib.Ifo
{
public enum VideoCodec
{
MPEG1 = 0,
MPEG2 = 1,
}
public enum VideoFormat
{
NTSC = 0,
PAL = 1,
}
public enum AspectRatio
{
ar4to3 = 0,
ar16to9 = 3
}
public enum FilmMode
{
None = -1,
Camera = 0,
Film = 1,
}
public class VideoAttributes
{
public readonly VideoCodec Codec;
public readonly VideoFormat Format;
public readonly AspectRatio Aspect;
public readonly bool AutomaticPanScan;
public readonly bool AutomaticLetterBox;
public readonly bool Line21CCField1;
public readonly bool Line21CCField2;
public readonly int Width;
public readonly int Height;
public readonly bool Letterboxed;
public readonly FilmMode FilmMode;
public VideoAttributes()
{
}
}
}
+2 -2
View File
@@ -9,8 +9,8 @@ using System.Runtime.InteropServices;
[assembly: AssemblyDescription("")] [assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")] [assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Jellyfin Project")] [assembly: AssemblyCompany("Jellyfin Project")]
[assembly: AssemblyProduct("Jellyfin Server")] [assembly: AssemblyProduct("Jellyfin: The Free Software Media System")]
[assembly: AssemblyCopyright("Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License")] [assembly: AssemblyCopyright("Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License Version 2")]
[assembly: AssemblyTrademark("")] [assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")] [assembly: AssemblyCulture("")]
[assembly: NeutralResourcesLanguage("en")] [assembly: NeutralResourcesLanguage("en")]
+44 -123
View File
@@ -1,13 +1,11 @@
#pragma warning disable CS1591
using System; using System;
using System.Diagnostics.CodeAnalysis; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Emby.Dlna.Main; using Emby.Dlna.Main;
using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Services; using MediaBrowser.Model.Services;
@@ -110,13 +108,12 @@ namespace Emby.Dlna.Api
public class DlnaServerService : IService, IRequiresRequest public class DlnaServerService : IService, IRequiresRequest
{ {
private readonly IDlnaManager _dlnaManager;
private const string XMLContentType = "text/xml; charset=UTF-8"; private const string XMLContentType = "text/xml; charset=UTF-8";
private readonly IDlnaManager _dlnaManager;
private readonly IHttpResultFactory _resultFactory;
private readonly IServerConfigurationManager _configurationManager;
public IRequest Request { get; set; } public IRequest Request { get; set; }
private IHttpResultFactory _resultFactory;
private IContentDirectory ContentDirectory => DlnaEntryPoint.Current.ContentDirectory; private IContentDirectory ContentDirectory => DlnaEntryPoint.Current.ContentDirectory;
@@ -124,14 +121,10 @@ namespace Emby.Dlna.Api
private IMediaReceiverRegistrar MediaReceiverRegistrar => DlnaEntryPoint.Current.MediaReceiverRegistrar; private IMediaReceiverRegistrar MediaReceiverRegistrar => DlnaEntryPoint.Current.MediaReceiverRegistrar;
public DlnaServerService( public DlnaServerService(IDlnaManager dlnaManager, IHttpResultFactory httpResultFactory)
IDlnaManager dlnaManager,
IHttpResultFactory httpResultFactory,
IServerConfigurationManager configurationManager)
{ {
_dlnaManager = dlnaManager; _dlnaManager = dlnaManager;
_resultFactory = httpResultFactory; _resultFactory = httpResultFactory;
_configurationManager = configurationManager;
} }
private string GetHeader(string name) private string GetHeader(string name)
@@ -143,7 +136,7 @@ namespace Emby.Dlna.Api
{ {
var url = Request.AbsoluteUri; var url = Request.AbsoluteUri;
var serverAddress = url.Substring(0, url.IndexOf("/dlna/", StringComparison.OrdinalIgnoreCase)); var serverAddress = url.Substring(0, url.IndexOf("/dlna/", StringComparison.OrdinalIgnoreCase));
var xml = _dlnaManager.GetServerDescriptionXml(Request.Headers, request.UuId, serverAddress); var xml = _dlnaManager.GetServerDescriptionXml(Request.Headers.ToDictionary(), request.UuId, serverAddress);
var cacheLength = TimeSpan.FromDays(1); var cacheLength = TimeSpan.FromDays(1);
var cacheKey = Request.RawUrl.GetMD5(); var cacheKey = Request.RawUrl.GetMD5();
@@ -152,164 +145,98 @@ namespace Emby.Dlna.Api
return _resultFactory.GetStaticResult(Request, cacheKey, null, cacheLength, XMLContentType, () => Task.FromResult<Stream>(new MemoryStream(bytes))); 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) public object Get(GetContentDirectory request)
{ {
var xml = ContentDirectory.GetServiceXml(); var xml = ContentDirectory.GetServiceXml(Request.Headers.ToDictionary());
return _resultFactory.GetResult(Request, xml, XMLContentType); return _resultFactory.GetResult(Request, xml, XMLContentType);
} }
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
public object Get(GetMediaReceiverRegistrar request) public object Get(GetMediaReceiverRegistrar request)
{ {
var xml = MediaReceiverRegistrar.GetServiceXml(); var xml = MediaReceiverRegistrar.GetServiceXml(Request.Headers.ToDictionary());
return _resultFactory.GetResult(Request, xml, XMLContentType); return _resultFactory.GetResult(Request, xml, XMLContentType);
} }
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
public object Get(GetConnnectionManager request) public object Get(GetConnnectionManager request)
{ {
var xml = ConnectionManager.GetServiceXml(); var xml = ConnectionManager.GetServiceXml(Request.Headers.ToDictionary());
return _resultFactory.GetResult(Request, xml, XMLContentType); return _resultFactory.GetResult(Request, xml, XMLContentType);
} }
public async Task<object> Post(ProcessMediaReceiverRegistrarControlRequest request) public object Post(ProcessMediaReceiverRegistrarControlRequest request)
{ {
var response = await PostAsync(request.RequestStream, MediaReceiverRegistrar).ConfigureAwait(false); var response = PostAsync(request.RequestStream, MediaReceiverRegistrar);
return _resultFactory.GetResult(Request, response.Xml, XMLContentType); return _resultFactory.GetResult(Request, response.Xml, XMLContentType);
} }
public async Task<object> Post(ProcessContentDirectoryControlRequest request) public object Post(ProcessContentDirectoryControlRequest request)
{ {
var response = await PostAsync(request.RequestStream, ContentDirectory).ConfigureAwait(false); var response = PostAsync(request.RequestStream, ContentDirectory);
return _resultFactory.GetResult(Request, response.Xml, XMLContentType); return _resultFactory.GetResult(Request, response.Xml, XMLContentType);
} }
public async Task<object> Post(ProcessConnectionManagerControlRequest request) public object Post(ProcessConnectionManagerControlRequest request)
{ {
var response = await PostAsync(request.RequestStream, ConnectionManager).ConfigureAwait(false); var response = PostAsync(request.RequestStream, ConnectionManager);
return _resultFactory.GetResult(Request, response.Xml, XMLContentType); return _resultFactory.GetResult(Request, response.Xml, XMLContentType);
} }
private Task<ControlResponse> PostAsync(Stream requestStream, IUpnpService service) private ControlResponse PostAsync(Stream requestStream, IUpnpService service)
{ {
var id = GetPathValue(2).ToString(); var id = GetPathValue(2);
return service.ProcessControlRequestAsync(new ControlRequest return service.ProcessControlRequest(new ControlRequest
{ {
Headers = Request.Headers, Headers = Request.Headers.ToDictionary(),
InputXml = requestStream, InputXml = requestStream,
TargetServerUuId = id, TargetServerUuId = id,
RequestedUrl = Request.AbsoluteUri RequestedUrl = Request.AbsoluteUri
}); });
} }
// Copied from MediaBrowser.Api/BaseApiService.cs protected string GetPathValue(int index)
// TODO: Remove code duplication
/// <summary>
/// Gets the path segment at the specified index.
/// </summary>
/// <param name="index">The index of the path segment.</param>
/// <returns>The path segment at the specified index.</returns>
/// <exception cref="IndexOutOfRangeException" >Path doesn't contain enough segments.</exception>
/// <exception cref="InvalidDataException" >Path doesn't start with the base url.</exception>
protected internal ReadOnlySpan<char> GetPathValue(int index)
{ {
static void ThrowIndexOutOfRangeException() var pathInfo = Parse(Request.PathInfo);
=> throw new IndexOutOfRangeException("Path doesn't contain enough segments."); var first = pathInfo[0];
static void ThrowInvalidDataException() // backwards compatibility
=> throw new InvalidDataException("Path doesn't start with the base url."); // TODO: Work out what this is doing.
if (string.Equals(first, "mediabrowser", StringComparison.OrdinalIgnoreCase) ||
ReadOnlySpan<char> path = Request.PathInfo; string.Equals(first, "emby", StringComparison.OrdinalIgnoreCase) ||
string.Equals(first, "jellyfin", StringComparison.OrdinalIgnoreCase))
// Remove the protocol part from the url
int pos = path.LastIndexOf("://");
if (pos != -1)
{ {
path = path.Slice(pos + 3); index++;
} }
// Remove the query string return pathInfo[index];
pos = path.LastIndexOf('?'); }
if (pos != -1)
private List<string> Parse(string pathUri)
{
var actionParts = pathUri.Split(new[] { "://" }, StringSplitOptions.None);
var pathInfo = actionParts[actionParts.Length - 1];
var optionsPos = pathInfo.LastIndexOf('?');
if (optionsPos != -1)
{ {
path = path.Slice(0, pos); pathInfo = pathInfo.Substring(0, optionsPos);
} }
// Remove the domain var args = pathInfo.Split('/');
pos = path.IndexOf('/');
if (pos != -1)
{
path = path.Slice(pos);
}
// Remove base url return args.Skip(1).ToList();
string baseUrl = _configurationManager.Configuration.BaseUrl;
int baseUrlLen = baseUrl.Length;
if (baseUrlLen != 0)
{
if (path.StartsWith(baseUrl, StringComparison.OrdinalIgnoreCase))
{
path = path.Slice(baseUrlLen);
}
else
{
// The path doesn't start with the base url,
// how did we get here?
ThrowInvalidDataException();
}
}
// Remove leading /
path = path.Slice(1);
// Backwards compatibility
const string Emby = "emby/";
if (path.StartsWith(Emby, StringComparison.OrdinalIgnoreCase))
{
path = path.Slice(Emby.Length);
}
const string MediaBrowser = "mediabrowser/";
if (path.StartsWith(MediaBrowser, StringComparison.OrdinalIgnoreCase))
{
path = path.Slice(MediaBrowser.Length);
}
// Skip segments until we are at the right index
for (int i = 0; i < index; i++)
{
pos = path.IndexOf('/');
if (pos == -1)
{
ThrowIndexOutOfRangeException();
}
path = path.Slice(pos + 1);
}
// Remove the rest
pos = path.IndexOf('/');
if (pos != -1)
{
path = path.Slice(0, pos);
}
return path;
} }
public object Get(GetIcon request) public object Get(GetIcon request)
{ {
var contentType = "image/" + Path.GetExtension(request.Filename) var contentType = "image/" + Path.GetExtension(request.Filename).TrimStart('.').ToLower();
.TrimStart('.')
.ToLowerInvariant();
var cacheLength = TimeSpan.FromDays(365); var cacheLength = TimeSpan.FromDays(365);
var cacheKey = Request.RawUrl.GetMD5(); var cacheKey = Request.RawUrl.GetMD5();
@@ -317,37 +244,31 @@ namespace Emby.Dlna.Api
return _resultFactory.GetStaticResult(Request, cacheKey, null, cacheLength, contentType, () => Task.FromResult(_dlnaManager.GetIcon(request.Filename).Stream)); 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) public object Subscribe(ProcessContentDirectoryEventRequest request)
{ {
return ProcessEventRequest(ContentDirectory); return ProcessEventRequest(ContentDirectory);
} }
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
public object Subscribe(ProcessConnectionManagerEventRequest request) public object Subscribe(ProcessConnectionManagerEventRequest request)
{ {
return ProcessEventRequest(ConnectionManager); return ProcessEventRequest(ConnectionManager);
} }
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
public object Subscribe(ProcessMediaReceiverRegistrarEventRequest request) public object Subscribe(ProcessMediaReceiverRegistrarEventRequest request)
{ {
return ProcessEventRequest(MediaReceiverRegistrar); return ProcessEventRequest(MediaReceiverRegistrar);
} }
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
public object Unsubscribe(ProcessContentDirectoryEventRequest request) public object Unsubscribe(ProcessContentDirectoryEventRequest request)
{ {
return ProcessEventRequest(ContentDirectory); return ProcessEventRequest(ContentDirectory);
} }
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
public object Unsubscribe(ProcessConnectionManagerEventRequest request) public object Unsubscribe(ProcessConnectionManagerEventRequest request)
{ {
return ProcessEventRequest(ConnectionManager); return ProcessEventRequest(ConnectionManager);
} }
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
public object Unsubscribe(ProcessMediaReceiverRegistrarEventRequest request) public object Unsubscribe(ProcessMediaReceiverRegistrarEventRequest request)
{ {
return ProcessEventRequest(MediaReceiverRegistrar); return ProcessEventRequest(MediaReceiverRegistrar);
-5
View File
@@ -1,6 +1,3 @@
#pragma warning disable CS1591
using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Net;
@@ -53,7 +50,6 @@ namespace Emby.Dlna.Api
_dlnaManager = dlnaManager; _dlnaManager = dlnaManager;
} }
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
public object Get(GetProfileInfos request) public object Get(GetProfileInfos request)
{ {
return _dlnaManager.GetProfileInfos().ToArray(); return _dlnaManager.GetProfileInfos().ToArray();
@@ -64,7 +60,6 @@ namespace Emby.Dlna.Api
return _dlnaManager.GetProfile(request.Id); return _dlnaManager.GetProfile(request.Id);
} }
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
public object Get(GetDefaultProfile request) public object Get(GetDefaultProfile request)
{ {
return _dlnaManager.GetDefaultProfile(); return _dlnaManager.GetDefaultProfile();
-1
View File
@@ -1,4 +1,3 @@
#pragma warning disable CS1591
namespace Emby.Dlna.Common namespace Emby.Dlna.Common
{ {
+1 -9
View File
@@ -1,6 +1,3 @@
#pragma warning disable CS1591
using System.Globalization;
namespace Emby.Dlna.Common namespace Emby.Dlna.Common
{ {
@@ -16,14 +13,9 @@ namespace Emby.Dlna.Common
public string Depth { get; set; } public string Depth { get; set; }
/// <inheritdoc />
public override string ToString() public override string ToString()
{ {
return string.Format( return string.Format("{0}x{1}", Height, Width);
CultureInfo.InvariantCulture,
"{0}x{1}",
Height,
Width);
} }
} }
} }
+3 -3
View File
@@ -1,4 +1,3 @@
#pragma warning disable CS1591
namespace Emby.Dlna.Common namespace Emby.Dlna.Common
{ {
@@ -14,8 +13,9 @@ namespace Emby.Dlna.Common
public string EventSubUrl { get; set; } public string EventSubUrl { get; set; }
/// <inheritdoc />
public override string ToString() public override string ToString()
=> ServiceId; {
return string.Format("{0}", ServiceId);
}
} }
} }
+5 -8
View File
@@ -1,24 +1,21 @@
#pragma warning disable CS1591
using System.Collections.Generic; using System.Collections.Generic;
namespace Emby.Dlna.Common namespace Emby.Dlna.Common
{ {
public class ServiceAction public class ServiceAction
{ {
public ServiceAction()
{
ArgumentList = new List<Argument>();
}
public string Name { get; set; } public string Name { get; set; }
public List<Argument> ArgumentList { get; set; } public List<Argument> ArgumentList { get; set; }
/// <inheritdoc />
public override string ToString() public override string ToString()
{ {
return Name; return Name;
} }
public ServiceAction()
{
ArgumentList = new List<Argument>();
}
} }
} }
+8 -9
View File
@@ -1,16 +1,9 @@
#pragma warning disable CS1591
using System; using System;
namespace Emby.Dlna.Common namespace Emby.Dlna.Common
{ {
public class StateVariable public class StateVariable
{ {
public StateVariable()
{
AllowedValues = Array.Empty<string>();
}
public string Name { get; set; } public string Name { get; set; }
public string DataType { get; set; } public string DataType { get; set; }
@@ -19,8 +12,14 @@ namespace Emby.Dlna.Common
public string[] AllowedValues { get; set; } public string[] AllowedValues { get; set; }
/// <inheritdoc />
public override string ToString() public override string ToString()
=> Name; {
return Name;
}
public StateVariable()
{
AllowedValues = Array.Empty<string>();
}
} }
} }
+8 -18
View File
@@ -1,33 +1,23 @@
#pragma warning disable CS1591
namespace Emby.Dlna.Configuration namespace Emby.Dlna.Configuration
{ {
public class DlnaOptions public class DlnaOptions
{ {
public bool EnablePlayTo { get; set; }
public bool EnableServer { get; set; }
public bool EnableDebugLog { get; set; }
public bool BlastAliveMessages { get; set; }
public int ClientDiscoveryIntervalSeconds { get; set; }
public int BlastAliveMessageIntervalSeconds { get; set; }
public string DefaultUserId { get; set; }
public DlnaOptions() public DlnaOptions()
{ {
EnablePlayTo = true; EnablePlayTo = true;
EnableServer = true; EnableServer = true;
BlastAliveMessages = true; BlastAliveMessages = true;
SendOnlyMatchedHost = true;
ClientDiscoveryIntervalSeconds = 60; ClientDiscoveryIntervalSeconds = 60;
BlastAliveMessageIntervalSeconds = 1800; BlastAliveMessageIntervalSeconds = 1800;
} }
public bool EnablePlayTo { get; set; }
public bool EnableServer { get; set; }
public bool EnableDebugLog { get; set; }
public bool BlastAliveMessages { get; set; }
public bool SendOnlyMatchedHost { get; set; }
public int ClientDiscoveryIntervalSeconds { get; set; }
public int BlastAliveMessageIntervalSeconds { get; set; }
public string DefaultUserId { get; set; }
} }
} }
-3
View File
@@ -1,6 +1,3 @@
#nullable enable
#pragma warning disable CS1591
using System.Collections.Generic; using System.Collections.Generic;
using Emby.Dlna.Configuration; using Emby.Dlna.Configuration;
using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Configuration;
@@ -1,10 +1,9 @@
#pragma warning disable CS1591 using System.Collections.Generic;
using System.Threading.Tasks;
using Emby.Dlna.Service; using Emby.Dlna.Service;
using MediaBrowser.Common.Net; using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Dlna;
using MediaBrowser.Model.Xml;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace Emby.Dlna.ConnectionManager namespace Emby.Dlna.ConnectionManager
@@ -14,32 +13,28 @@ namespace Emby.Dlna.ConnectionManager
private readonly IDlnaManager _dlna; private readonly IDlnaManager _dlna;
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly IServerConfigurationManager _config; private readonly IServerConfigurationManager _config;
protected readonly IXmlReaderSettingsFactory XmlReaderSettingsFactory;
public ConnectionManager( public ConnectionManager(IDlnaManager dlna, IServerConfigurationManager config, ILogger logger, IHttpClient httpClient, IXmlReaderSettingsFactory xmlReaderSettingsFactory)
IDlnaManager dlna,
IServerConfigurationManager config,
ILogger<ConnectionManager> logger,
IHttpClient httpClient)
: base(logger, httpClient) : base(logger, httpClient)
{ {
_dlna = dlna; _dlna = dlna;
_config = config; _config = config;
_logger = logger; _logger = logger;
XmlReaderSettingsFactory = xmlReaderSettingsFactory;
} }
/// <inheritdoc /> public string GetServiceXml(IDictionary<string, string> headers)
public string GetServiceXml()
{ {
return new ConnectionManagerXmlBuilder().GetXml(); return new ConnectionManagerXmlBuilder().GetXml();
} }
/// <inheritdoc /> public ControlResponse ProcessControlRequest(ControlRequest request)
public Task<ControlResponse> ProcessControlRequestAsync(ControlRequest request)
{ {
var profile = _dlna.GetProfile(request.Headers) ?? var profile = _dlna.GetProfile(request.Headers) ??
_dlna.GetDefaultProfile(); _dlna.GetDefaultProfile();
return new ControlHandler(_config, _logger, profile).ProcessControlRequestAsync(request); return new ControlHandler(_config, _logger, XmlReaderSettingsFactory, profile).ProcessControlRequest(request);
} }
} }
} }
@@ -1,5 +1,3 @@
#pragma warning disable CS1591
using System.Collections.Generic; using System.Collections.Generic;
using Emby.Dlna.Common; using Emby.Dlna.Common;
using Emby.Dlna.Service; using Emby.Dlna.Service;
+14 -16
View File
@@ -1,12 +1,10 @@
#pragma warning disable CS1591
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Xml;
using Emby.Dlna.Service; using Emby.Dlna.Service;
using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Xml;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace Emby.Dlna.ConnectionManager namespace Emby.Dlna.ConnectionManager
@@ -15,28 +13,28 @@ namespace Emby.Dlna.ConnectionManager
{ {
private readonly DeviceProfile _profile; private readonly DeviceProfile _profile;
public ControlHandler(IServerConfigurationManager config, ILogger logger, DeviceProfile profile) protected override IEnumerable<KeyValuePair<string, string>> GetResult(string methodName, IDictionary<string, string> methodParams)
: base(config, logger)
{
_profile = profile;
}
/// <inheritdoc />
protected override void WriteResult(string methodName, IDictionary<string, string> methodParams, XmlWriter xmlWriter)
{ {
if (string.Equals(methodName, "GetProtocolInfo", StringComparison.OrdinalIgnoreCase)) if (string.Equals(methodName, "GetProtocolInfo", StringComparison.OrdinalIgnoreCase))
{ {
HandleGetProtocolInfo(xmlWriter); return HandleGetProtocolInfo();
return;
} }
throw new ResourceNotFoundException("Unexpected control request name: " + methodName); throw new ResourceNotFoundException("Unexpected control request name: " + methodName);
} }
private void HandleGetProtocolInfo(XmlWriter xmlWriter) private IEnumerable<KeyValuePair<string, string>> HandleGetProtocolInfo()
{ {
xmlWriter.WriteElementString("Source", _profile.ProtocolInfo); return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
xmlWriter.WriteElementString("Sink", string.Empty); {
{ "Source", _profile.ProtocolInfo },
{ "Sink", "" }
};
}
public ControlHandler(IServerConfigurationManager config, ILogger logger, IXmlReaderSettingsFactory xmlReaderSettingsFactory, DeviceProfile profile) : base(config, logger, xmlReaderSettingsFactory)
{
_profile = profile;
} }
} }
} }
@@ -1,5 +1,3 @@
#pragma warning disable CS1591
using System.Collections.Generic; using System.Collections.Generic;
using Emby.Dlna.Common; using Emby.Dlna.Common;
+23 -25
View File
@@ -1,20 +1,17 @@
#pragma warning disable CS1591
using System; using System;
using System.Linq; using System.Collections.Generic;
using System.Threading.Tasks;
using Emby.Dlna.Service; using Emby.Dlna.Service;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
using MediaBrowser.Common.Net; using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Controller.TV; using MediaBrowser.Controller.TV;
using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Xml;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace Emby.Dlna.ContentDirectory namespace Emby.Dlna.ContentDirectory
@@ -31,22 +28,17 @@ namespace Emby.Dlna.ContentDirectory
private readonly IMediaSourceManager _mediaSourceManager; private readonly IMediaSourceManager _mediaSourceManager;
private readonly IUserViewManager _userViewManager; private readonly IUserViewManager _userViewManager;
private readonly IMediaEncoder _mediaEncoder; private readonly IMediaEncoder _mediaEncoder;
protected readonly IXmlReaderSettingsFactory XmlReaderSettingsFactory;
private readonly ITVSeriesManager _tvSeriesManager; private readonly ITVSeriesManager _tvSeriesManager;
public ContentDirectory( public ContentDirectory(IDlnaManager dlna,
IDlnaManager dlna,
IUserDataManager userDataManager, IUserDataManager userDataManager,
IImageProcessor imageProcessor, IImageProcessor imageProcessor,
ILibraryManager libraryManager, ILibraryManager libraryManager,
IServerConfigurationManager config, IServerConfigurationManager config,
IUserManager userManager, IUserManager userManager,
ILogger<ContentDirectory> logger, ILogger logger,
IHttpClient httpClient, IHttpClient httpClient, ILocalizationManager localization, IMediaSourceManager mediaSourceManager, IUserViewManager userViewManager, IMediaEncoder mediaEncoder, IXmlReaderSettingsFactory xmlReaderSettingsFactory, ITVSeriesManager tvSeriesManager)
ILocalizationManager localization,
IMediaSourceManager mediaSourceManager,
IUserViewManager userViewManager,
IMediaEncoder mediaEncoder,
ITVSeriesManager tvSeriesManager)
: base(logger, httpClient) : base(logger, httpClient)
{ {
_dlna = dlna; _dlna = dlna;
@@ -59,6 +51,7 @@ namespace Emby.Dlna.ContentDirectory
_mediaSourceManager = mediaSourceManager; _mediaSourceManager = mediaSourceManager;
_userViewManager = userViewManager; _userViewManager = userViewManager;
_mediaEncoder = mediaEncoder; _mediaEncoder = mediaEncoder;
XmlReaderSettingsFactory = xmlReaderSettingsFactory;
_tvSeriesManager = tvSeriesManager; _tvSeriesManager = tvSeriesManager;
} }
@@ -72,19 +65,18 @@ namespace Emby.Dlna.ContentDirectory
} }
} }
/// <inheritdoc /> public string GetServiceXml(IDictionary<string, string> headers)
public string GetServiceXml()
{ {
return new ContentDirectoryXmlBuilder().GetXml(); return new ContentDirectoryXmlBuilder().GetXml();
} }
/// <inheritdoc /> public ControlResponse ProcessControlRequest(ControlRequest request)
public Task<ControlResponse> ProcessControlRequestAsync(ControlRequest request)
{ {
var profile = _dlna.GetProfile(request.Headers) ?? var profile = _dlna.GetProfile(request.Headers) ??
_dlna.GetDefaultProfile(); _dlna.GetDefaultProfile();
var serverAddress = request.RequestedUrl.Substring(0, request.RequestedUrl.IndexOf("/dlna", StringComparison.OrdinalIgnoreCase)); var serverAddress = request.RequestedUrl.Substring(0, request.RequestedUrl.IndexOf("/dlna", StringComparison.OrdinalIgnoreCase));
string accessToken = null;
var user = GetUser(profile); var user = GetUser(profile);
@@ -93,7 +85,7 @@ namespace Emby.Dlna.ContentDirectory
_libraryManager, _libraryManager,
profile, profile,
serverAddress, serverAddress,
null, accessToken,
_imageProcessor, _imageProcessor,
_userDataManager, _userDataManager,
user, user,
@@ -103,15 +95,16 @@ namespace Emby.Dlna.ContentDirectory
_mediaSourceManager, _mediaSourceManager,
_userViewManager, _userViewManager,
_mediaEncoder, _mediaEncoder,
XmlReaderSettingsFactory,
_tvSeriesManager) _tvSeriesManager)
.ProcessControlRequestAsync(request); .ProcessControlRequest(request);
} }
private User GetUser(DeviceProfile profile) private User GetUser(DeviceProfile profile)
{ {
if (!string.IsNullOrEmpty(profile.UserId)) if (!string.IsNullOrEmpty(profile.UserId))
{ {
var user = _userManager.GetUserById(Guid.Parse(profile.UserId)); var user = _userManager.GetUserById(profile.UserId);
if (user != null) if (user != null)
{ {
@@ -123,7 +116,7 @@ namespace Emby.Dlna.ContentDirectory
if (!string.IsNullOrEmpty(userId)) if (!string.IsNullOrEmpty(userId))
{ {
var user = _userManager.GetUserById(Guid.Parse(userId)); var user = _userManager.GetUserById(userId);
if (user != null) if (user != null)
{ {
@@ -133,13 +126,18 @@ namespace Emby.Dlna.ContentDirectory
foreach (var user in _userManager.Users) foreach (var user in _userManager.Users)
{ {
if (user.HasPermission(PermissionKind.IsAdministrator)) if (user.Policy.IsAdministrator)
{ {
return user; return user;
} }
} }
return _userManager.Users.FirstOrDefault(); foreach (var user in _userManager.Users)
{
return user;
}
return null;
} }
} }
} }
@@ -1,5 +1,3 @@
#pragma warning disable CS1591
using System.Collections.Generic; using System.Collections.Generic;
using Emby.Dlna.Common; using Emby.Dlna.Common;
using Emby.Dlna.Service; using Emby.Dlna.Service;
+289 -316
View File
@@ -1,5 +1,3 @@
#pragma warning disable CS1591
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
@@ -10,7 +8,6 @@ using System.Threading;
using System.Xml; using System.Xml;
using Emby.Dlna.Didl; using Emby.Dlna.Didl;
using Emby.Dlna.Service; using Emby.Dlna.Service;
using Jellyfin.Data.Entities;
using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Drawing;
@@ -18,6 +15,7 @@ using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.MediaEncoding;
@@ -27,13 +25,8 @@ using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Querying; using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Xml;
using Microsoft.Extensions.Logging; 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 namespace Emby.Dlna.ContentDirectory
{ {
@@ -52,28 +45,14 @@ namespace Emby.Dlna.ContentDirectory
private const string NS_UPNP = "urn:schemas-upnp-org:metadata-1-0/upnp/"; private const string NS_UPNP = "urn:schemas-upnp-org:metadata-1-0/upnp/";
private readonly int _systemUpdateId; private readonly int _systemUpdateId;
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
private readonly DidlBuilder _didlBuilder; private readonly DidlBuilder _didlBuilder;
private readonly DeviceProfile _profile; private readonly DeviceProfile _profile;
public ControlHandler( public ControlHandler(ILogger logger, ILibraryManager libraryManager, DeviceProfile profile, string serverAddress, string accessToken, IImageProcessor imageProcessor, IUserDataManager userDataManager, User user, int systemUpdateId, IServerConfigurationManager config, ILocalizationManager localization, IMediaSourceManager mediaSourceManager, IUserViewManager userViewManager, IMediaEncoder mediaEncoder, IXmlReaderSettingsFactory xmlReaderSettingsFactory, ITVSeriesManager tvSeriesManager)
ILogger logger, : base(config, logger, xmlReaderSettingsFactory)
ILibraryManager libraryManager,
DeviceProfile profile,
string serverAddress,
string accessToken,
IImageProcessor imageProcessor,
IUserDataManager userDataManager,
User user,
int systemUpdateId,
IServerConfigurationManager config,
ILocalizationManager localization,
IMediaSourceManager mediaSourceManager,
IUserViewManager userViewManager,
IMediaEncoder mediaEncoder,
ITVSeriesManager tvSeriesManager)
: base(config, logger)
{ {
_libraryManager = libraryManager; _libraryManager = libraryManager;
_userDataManager = userDataManager; _userDataManager = userDataManager;
@@ -84,143 +63,117 @@ namespace Emby.Dlna.ContentDirectory
_profile = profile; _profile = profile;
_config = config; _config = config;
_didlBuilder = new DidlBuilder( _didlBuilder = new DidlBuilder(profile, user, imageProcessor, serverAddress, accessToken, userDataManager, localization, mediaSourceManager, _logger, libraryManager, mediaEncoder);
profile,
user,
imageProcessor,
serverAddress,
accessToken,
userDataManager,
localization,
mediaSourceManager,
Logger,
mediaEncoder,
libraryManager);
} }
/// <inheritdoc /> protected override IEnumerable<KeyValuePair<string, string>> GetResult(string methodName, IDictionary<string, string> methodParams)
protected override void WriteResult(string methodName, IDictionary<string, string> methodParams, XmlWriter xmlWriter)
{ {
const string DeviceId = "test"; var deviceId = "test";
var user = _user;
if (string.Equals(methodName, "GetSearchCapabilities", StringComparison.OrdinalIgnoreCase)) if (string.Equals(methodName, "GetSearchCapabilities", StringComparison.OrdinalIgnoreCase))
{ return HandleGetSearchCapabilities();
HandleGetSearchCapabilities(xmlWriter);
return;
}
if (string.Equals(methodName, "GetSortCapabilities", StringComparison.OrdinalIgnoreCase)) if (string.Equals(methodName, "GetSortCapabilities", StringComparison.OrdinalIgnoreCase))
{ return HandleGetSortCapabilities();
HandleGetSortCapabilities(xmlWriter);
return;
}
if (string.Equals(methodName, "GetSortExtensionCapabilities", StringComparison.OrdinalIgnoreCase)) if (string.Equals(methodName, "GetSortExtensionCapabilities", StringComparison.OrdinalIgnoreCase))
{ return HandleGetSortExtensionCapabilities();
HandleGetSortExtensionCapabilities(xmlWriter);
return;
}
if (string.Equals(methodName, "GetSystemUpdateID", StringComparison.OrdinalIgnoreCase)) if (string.Equals(methodName, "GetSystemUpdateID", StringComparison.OrdinalIgnoreCase))
{ return HandleGetSystemUpdateID();
HandleGetSystemUpdateID(xmlWriter);
return;
}
if (string.Equals(methodName, "Browse", StringComparison.OrdinalIgnoreCase)) if (string.Equals(methodName, "Browse", StringComparison.OrdinalIgnoreCase))
{ return HandleBrowse(methodParams, user, deviceId);
HandleBrowse(xmlWriter, methodParams, DeviceId);
return;
}
if (string.Equals(methodName, "X_GetFeatureList", StringComparison.OrdinalIgnoreCase)) if (string.Equals(methodName, "X_GetFeatureList", StringComparison.OrdinalIgnoreCase))
{ return HandleXGetFeatureList();
HandleXGetFeatureList(xmlWriter);
return;
}
if (string.Equals(methodName, "GetFeatureList", StringComparison.OrdinalIgnoreCase)) if (string.Equals(methodName, "GetFeatureList", StringComparison.OrdinalIgnoreCase))
{ return HandleGetFeatureList();
HandleGetFeatureList(xmlWriter);
return;
}
if (string.Equals(methodName, "X_SetBookmark", StringComparison.OrdinalIgnoreCase)) if (string.Equals(methodName, "X_SetBookmark", StringComparison.OrdinalIgnoreCase))
{ return HandleXSetBookmark(methodParams, user);
HandleXSetBookmark(methodParams);
return;
}
if (string.Equals(methodName, "Search", StringComparison.OrdinalIgnoreCase)) if (string.Equals(methodName, "Search", StringComparison.OrdinalIgnoreCase))
{ return HandleSearch(methodParams, user, deviceId);
HandleSearch(xmlWriter, methodParams, DeviceId);
return;
}
if (string.Equals(methodName, "X_BrowseByLetter", StringComparison.OrdinalIgnoreCase)) if (string.Equals(methodName, "X_BrowseByLetter", StringComparison.OrdinalIgnoreCase))
{ return HandleX_BrowseByLetter(methodParams, user, deviceId);
HandleXBrowseByLetter(xmlWriter, methodParams, DeviceId);
return;
}
throw new ResourceNotFoundException("Unexpected control request name: " + methodName); throw new ResourceNotFoundException("Unexpected control request name: " + methodName);
} }
private void HandleXSetBookmark(IDictionary<string, string> sparams) private IEnumerable<KeyValuePair<string, string>> HandleXSetBookmark(IDictionary<string, string> sparams, User user)
{ {
var id = sparams["ObjectID"]; var id = sparams["ObjectID"];
var serverItem = GetItemFromObjectId(id); var serverItem = GetItemFromObjectId(id, user);
var item = serverItem.Item; var item = serverItem.Item;
var newbookmark = int.Parse(sparams["PosSecond"], CultureInfo.InvariantCulture); var newbookmark = int.Parse(sparams["PosSecond"], _usCulture);
var userdata = _userDataManager.GetUserData(_user, item); var userdata = _userDataManager.GetUserData(user, item);
userdata.PlaybackPositionTicks = TimeSpan.FromSeconds(newbookmark).Ticks; userdata.PlaybackPositionTicks = TimeSpan.FromSeconds(newbookmark).Ticks;
_userDataManager.SaveUserData(_user, item, userdata, UserDataSaveReason.TogglePlayed, _userDataManager.SaveUserData(user, item, userdata, UserDataSaveReason.TogglePlayed,
CancellationToken.None); CancellationToken.None);
return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
} }
private void HandleGetSearchCapabilities(XmlWriter xmlWriter) private IEnumerable<KeyValuePair<string, string>> HandleGetSearchCapabilities()
{ {
xmlWriter.WriteElementString( return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
"SearchCaps", {
"res@resolution,res@size,res@duration,dc:title,dc:creator,upnp:actor,upnp:artist,upnp:genre,upnp:album,dc:date,upnp:class,@id,@refID,@protocolInfo,upnp:author,dc:description,pv:avKeywords"); { "SearchCaps", "res@resolution,res@size,res@duration,dc:title,dc:creator,upnp:actor,upnp:artist,upnp:genre,upnp:album,dc:date,upnp:class,@id,@refID,@protocolInfo,upnp:author,dc:description,pv:avKeywords" }
};
} }
private void HandleGetSortCapabilities(XmlWriter xmlWriter) private IEnumerable<KeyValuePair<string, string>> HandleGetSortCapabilities()
{ {
xmlWriter.WriteElementString( return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
"SortCaps", {
"res@duration,res@size,res@bitrate,dc:date,dc:title,dc:size,upnp:album,upnp:artist,upnp:albumArtist,upnp:episodeNumber,upnp:genre,upnp:originalTrackNumber,upnp:rating"); { "SortCaps", "res@duration,res@size,res@bitrate,dc:date,dc:title,dc:size,upnp:album,upnp:artist,upnp:albumArtist,upnp:episodeNumber,upnp:genre,upnp:originalTrackNumber,upnp:rating" }
};
} }
private void HandleGetSortExtensionCapabilities(XmlWriter xmlWriter) private IEnumerable<KeyValuePair<string, string>> HandleGetSortExtensionCapabilities()
{ {
xmlWriter.WriteElementString( return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
"SortExtensionCaps", {
"res@duration,res@size,res@bitrate,dc:date,dc:title,dc:size,upnp:album,upnp:artist,upnp:albumArtist,upnp:episodeNumber,upnp:genre,upnp:originalTrackNumber,upnp:rating"); { "SortExtensionCaps", "res@duration,res@size,res@bitrate,dc:date,dc:title,dc:size,upnp:album,upnp:artist,upnp:albumArtist,upnp:episodeNumber,upnp:genre,upnp:originalTrackNumber,upnp:rating" }
};
} }
private void HandleGetSystemUpdateID(XmlWriter xmlWriter) private IEnumerable<KeyValuePair<string, string>> HandleGetSystemUpdateID()
{ {
xmlWriter.WriteElementString("Id", _systemUpdateId.ToString(CultureInfo.InvariantCulture)); var headers = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
headers.Add("Id", _systemUpdateId.ToString(_usCulture));
return headers;
} }
private void HandleGetFeatureList(XmlWriter xmlWriter) private IEnumerable<KeyValuePair<string, string>> HandleGetFeatureList()
{ {
xmlWriter.WriteElementString("FeatureList", WriteFeatureListXml()); return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
{ "FeatureList", GetFeatureListXml() }
};
} }
private void HandleXGetFeatureList(XmlWriter xmlWriter) private IEnumerable<KeyValuePair<string, string>> HandleXGetFeatureList()
=> HandleGetFeatureList(xmlWriter); {
return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
private string WriteFeatureListXml() {
{ "FeatureList", GetFeatureListXml() }
};
}
private string GetFeatureListXml()
{ {
// TODO: clean this up
var builder = new StringBuilder(); var builder = new StringBuilder();
builder.Append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); builder.Append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
@@ -247,7 +200,7 @@ namespace Emby.Dlna.ContentDirectory
return defaultValue; return defaultValue;
} }
private void HandleBrowse(XmlWriter xmlWriter, IDictionary<string, string> sparams, string deviceId) private IEnumerable<KeyValuePair<string, string>> HandleBrowse(IDictionary<string, string> sparams, User user, string deviceId)
{ {
var id = sparams["ObjectID"]; var id = sparams["ObjectID"];
var flag = sparams["BrowseFlag"]; var flag = sparams["BrowseFlag"];
@@ -271,95 +224,100 @@ namespace Emby.Dlna.ContentDirectory
start = startVal; start = startVal;
} }
var settings = new XmlWriterSettings
{
Encoding = Encoding.UTF8,
CloseOutput = false,
OmitXmlDeclaration = true,
ConformanceLevel = ConformanceLevel.Fragment
};
StringWriter builder = new StringWriterWithEncoding(Encoding.UTF8);
int totalCount; int totalCount;
using (StringWriter builder = new StringWriterWithEncoding(Encoding.UTF8)) var dlnaOptions = _config.GetDlnaConfiguration();
using (var writer = XmlWriter.Create(builder, settings))
{ {
var settings = new XmlWriterSettings() //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);
DidlBuilder.WriteXmlRootAttributes(_profile, writer);
var serverItem = GetItemFromObjectId(id, user);
var item = serverItem.Item;
if (string.Equals(flag, "BrowseMetadata"))
{ {
Encoding = Encoding.UTF8, totalCount = 1;
CloseOutput = false,
OmitXmlDeclaration = true,
ConformanceLevel = ConformanceLevel.Fragment
};
using (var writer = XmlWriter.Create(builder, settings)) if (item.IsDisplayedAsFolder || serverItem.StubType.HasValue)
{
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);
DidlBuilder.WriteXmlRootAttributes(_profile, writer);
var serverItem = GetItemFromObjectId(id);
var item = serverItem.Item;
if (string.Equals(flag, "BrowseMetadata", StringComparison.Ordinal))
{ {
totalCount = 1; var childrenResult = (GetUserItems(item, serverItem.StubType, user, sortCriteria, start, requestedCount));
if (item.IsDisplayedAsFolder || serverItem.StubType.HasValue) _didlBuilder.WriteFolderElement(writer, item, serverItem.StubType, null, childrenResult.TotalRecordCount, filter, id);
{
var childrenResult = GetUserItems(item, serverItem.StubType, _user, sortCriteria, start, requestedCount);
_didlBuilder.WriteFolderElement(writer, item, serverItem.StubType, null, childrenResult.TotalRecordCount, filter, id);
}
else
{
var dlnaOptions = _config.GetDlnaConfiguration();
_didlBuilder.WriteItemElement(writer, item, _user, null, null, deviceId, filter);
}
provided++;
} }
else else
{ {
var childrenResult = GetUserItems(item, serverItem.StubType, _user, sortCriteria, start, requestedCount); _didlBuilder.WriteItemElement(dlnaOptions, writer, item, user, null, null, deviceId, filter);
totalCount = childrenResult.TotalRecordCount;
provided = childrenResult.Items.Count;
var dlnaOptions = _config.GetDlnaConfiguration();
foreach (var i in childrenResult.Items)
{
var childItem = i.Item;
var displayStubType = i.StubType;
if (childItem.IsDisplayedAsFolder || displayStubType.HasValue)
{
var childCount = GetUserItems(childItem, displayStubType, _user, sortCriteria, null, 0)
.TotalRecordCount;
_didlBuilder.WriteFolderElement(writer, childItem, displayStubType, item, childCount, filter);
}
else
{
_didlBuilder.WriteItemElement(writer, childItem, _user, item, serverItem.StubType, deviceId, filter);
}
}
} }
writer.WriteFullEndElement(); provided++;
} }
else
{
var childrenResult = (GetUserItems(item, serverItem.StubType, user, sortCriteria, start, requestedCount));
totalCount = childrenResult.TotalRecordCount;
xmlWriter.WriteElementString("Result", builder.ToString()); provided = childrenResult.Items.Length;
foreach (var i in childrenResult.Items)
{
var childItem = i.Item;
var displayStubType = i.StubType;
if (childItem.IsDisplayedAsFolder || displayStubType.HasValue)
{
var childCount = (GetUserItems(childItem, displayStubType, user, sortCriteria, null, 0))
.TotalRecordCount;
_didlBuilder.WriteFolderElement(writer, childItem, displayStubType, item, childCount, filter);
}
else
{
_didlBuilder.WriteItemElement(dlnaOptions, writer, childItem, user, item, serverItem.StubType, deviceId, filter);
}
}
}
writer.WriteFullEndElement();
//writer.WriteEndDocument();
} }
xmlWriter.WriteElementString("NumberReturned", provided.ToString(CultureInfo.InvariantCulture)); var resXML = builder.ToString();
xmlWriter.WriteElementString("TotalMatches", totalCount.ToString(CultureInfo.InvariantCulture));
xmlWriter.WriteElementString("UpdateID", _systemUpdateId.ToString(CultureInfo.InvariantCulture)); return new[]
{
new KeyValuePair<string,string>("Result", resXML),
new KeyValuePair<string,string>("NumberReturned", provided.ToString(_usCulture)),
new KeyValuePair<string,string>("TotalMatches", totalCount.ToString(_usCulture)),
new KeyValuePair<string,string>("UpdateID", _systemUpdateId.ToString(_usCulture))
};
} }
private void HandleXBrowseByLetter(XmlWriter xmlWriter, IDictionary<string, string> sparams, string deviceId) private IEnumerable<KeyValuePair<string, string>> HandleX_BrowseByLetter(IDictionary<string, string> sparams, User user, string deviceId)
{ {
// TODO: Implement this method // TODO: Implement this method
HandleSearch(xmlWriter, sparams, deviceId); return HandleSearch(sparams, user, deviceId);
} }
private void HandleSearch(XmlWriter xmlWriter, IDictionary<string, string> sparams, string deviceId) private IEnumerable<KeyValuePair<string, string>> HandleSearch(IDictionary<string, string> sparams, User user, string deviceId)
{ {
var searchCriteria = new SearchCriteria(GetValueOrDefault(sparams, "SearchCriteria", "")); var searchCriteria = new SearchCriteria(GetValueOrDefault(sparams, "SearchCriteria", ""));
var sortCriteria = new SortCriteria(GetValueOrDefault(sparams, "SortCriteria", "")); var sortCriteria = new SortCriteria(GetValueOrDefault(sparams, "SortCriteria", ""));
@@ -382,96 +340,109 @@ namespace Emby.Dlna.ContentDirectory
start = startVal; start = startVal;
} }
QueryResult<BaseItem> childrenResult; var settings = new XmlWriterSettings
using (StringWriter builder = new StringWriterWithEncoding(Encoding.UTF8))
{ {
var settings = new XmlWriterSettings() Encoding = Encoding.UTF8,
CloseOutput = false,
OmitXmlDeclaration = true,
ConformanceLevel = ConformanceLevel.Fragment
};
StringWriter builder = new StringWriterWithEncoding(Encoding.UTF8);
int totalCount = 0;
int provided = 0;
using (var writer = XmlWriter.Create(builder, settings))
{
//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);
DidlBuilder.WriteXmlRootAttributes(_profile, writer);
var serverItem = GetItemFromObjectId(sparams["ContainerID"], user);
var item = serverItem.Item;
var childrenResult = (GetChildrenSorted(item, user, searchCriteria, sortCriteria, start, requestedCount));
totalCount = childrenResult.TotalRecordCount;
provided = childrenResult.Items.Length;
var dlnaOptions = _config.GetDlnaConfiguration();
foreach (var i in childrenResult.Items)
{ {
Encoding = Encoding.UTF8, if (i.IsDisplayedAsFolder)
CloseOutput = false,
OmitXmlDeclaration = true,
ConformanceLevel = ConformanceLevel.Fragment
};
using (var writer = XmlWriter.Create(builder, settings))
{
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);
DidlBuilder.WriteXmlRootAttributes(_profile, writer);
var serverItem = GetItemFromObjectId(sparams["ContainerID"]);
var item = serverItem.Item;
childrenResult = GetChildrenSorted(item, _user, searchCriteria, sortCriteria, start, requestedCount);
var dlnaOptions = _config.GetDlnaConfiguration();
foreach (var i in childrenResult.Items)
{ {
if (i.IsDisplayedAsFolder) var childCount = (GetChildrenSorted(i, user, searchCriteria, sortCriteria, null, 0))
{ .TotalRecordCount;
var childCount = GetChildrenSorted(i, _user, searchCriteria, sortCriteria, null, 0)
.TotalRecordCount;
_didlBuilder.WriteFolderElement(writer, i, null, item, childCount, filter); _didlBuilder.WriteFolderElement(writer, i, null, item, childCount, filter);
} }
else else
{ {
_didlBuilder.WriteItemElement(writer, i, _user, item, serverItem.StubType, deviceId, filter); _didlBuilder.WriteItemElement(dlnaOptions, writer, i, user, item, serverItem.StubType, deviceId, filter);
}
} }
writer.WriteFullEndElement();
} }
xmlWriter.WriteElementString("Result", builder.ToString()); writer.WriteFullEndElement();
//writer.WriteEndDocument();
} }
xmlWriter.WriteElementString("NumberReturned", childrenResult.Items.Count.ToString(CultureInfo.InvariantCulture)); var resXML = builder.ToString();
xmlWriter.WriteElementString("TotalMatches", childrenResult.TotalRecordCount.ToString(CultureInfo.InvariantCulture));
xmlWriter.WriteElementString("UpdateID", _systemUpdateId.ToString(CultureInfo.InvariantCulture)); return new List<KeyValuePair<string, string>>
{
new KeyValuePair<string,string>("Result", resXML),
new KeyValuePair<string,string>("NumberReturned", provided.ToString(_usCulture)),
new KeyValuePair<string,string>("TotalMatches", totalCount.ToString(_usCulture)),
new KeyValuePair<string,string>("UpdateID", _systemUpdateId.ToString(_usCulture))
};
} }
private QueryResult<BaseItem> GetChildrenSorted(BaseItem item, User user, SearchCriteria search, SortCriteria sort, int? startIndex, int? limit) private QueryResult<BaseItem> GetChildrenSorted(BaseItem item, User user, SearchCriteria search, SortCriteria sort, int? startIndex, int? limit)
{ {
var folder = (Folder)item; var folder = (Folder)item;
var sortOrders = folder.IsPreSorted var sortOrders = new List<string>();
? Array.Empty<(string, SortOrder)>() if (!folder.IsPreSorted)
: new[] { (ItemSortBy.SortName, sort.SortOrder) }; {
sortOrders.Add(ItemSortBy.SortName);
}
string[] mediaTypes = Array.Empty<string>(); var mediaTypes = new List<string>();
bool? isFolder = null; bool? isFolder = null;
if (search.SearchType == SearchType.Audio) if (search.SearchType == SearchType.Audio)
{ {
mediaTypes = new[] { MediaType.Audio }; mediaTypes.Add(MediaType.Audio);
isFolder = false; isFolder = false;
} }
else if (search.SearchType == SearchType.Video) else if (search.SearchType == SearchType.Video)
{ {
mediaTypes = new[] { MediaType.Video }; mediaTypes.Add(MediaType.Video);
isFolder = false; isFolder = false;
} }
else if (search.SearchType == SearchType.Image) else if (search.SearchType == SearchType.Image)
{ {
mediaTypes = new[] { MediaType.Photo }; mediaTypes.Add(MediaType.Photo);
isFolder = false; isFolder = false;
} }
else if (search.SearchType == SearchType.Playlist) else if (search.SearchType == SearchType.Playlist)
{ {
// items = items.OfType<Playlist>(); //items = items.OfType<Playlist>();
isFolder = true; isFolder = true;
} }
else if (search.SearchType == SearchType.MusicAlbum) else if (search.SearchType == SearchType.MusicAlbum)
{ {
// items = items.OfType<MusicAlbum>(); //items = items.OfType<MusicAlbum>();
isFolder = true; isFolder = true;
} }
@@ -479,13 +450,13 @@ namespace Emby.Dlna.ContentDirectory
{ {
Limit = limit, Limit = limit,
StartIndex = startIndex, StartIndex = startIndex,
OrderBy = sortOrders, OrderBy = sortOrders.Select(i => new ValueTuple<string, SortOrder>(i, sort.SortOrder)).ToArray(),
User = user, User = user,
Recursive = true, Recursive = true,
IsMissing = false, IsMissing = false,
ExcludeItemTypes = new[] { typeof(Book).Name }, ExcludeItemTypes = new[] { typeof(Game).Name, typeof(Book).Name },
IsFolder = isFolder, IsFolder = isFolder,
MediaTypes = mediaTypes, MediaTypes = mediaTypes.ToArray(),
DtoOptions = GetDtoOptions() DtoOptions = GetDtoOptions()
}); });
} }
@@ -512,28 +483,29 @@ namespace Emby.Dlna.ContentDirectory
return GetGenreItems(item, Guid.Empty, user, sort, startIndex, limit); return GetGenreItems(item, Guid.Empty, user, sort, startIndex, limit);
} }
if ((!stubType.HasValue || stubType.Value != StubType.Folder) if (!stubType.HasValue || stubType.Value != StubType.Folder)
&& item is IHasCollectionType collectionFolder)
{ {
if (string.Equals(CollectionType.Music, collectionFolder.CollectionType, StringComparison.OrdinalIgnoreCase)) var collectionFolder = item as IHasCollectionType;
if (collectionFolder != null && string.Equals(CollectionType.Music, collectionFolder.CollectionType, StringComparison.OrdinalIgnoreCase))
{ {
return GetMusicFolders(item, user, stubType, sort, startIndex, limit); return GetMusicFolders(item, user, stubType, sort, startIndex, limit);
} }
else if (string.Equals(CollectionType.Movies, collectionFolder.CollectionType, StringComparison.OrdinalIgnoreCase)) if (collectionFolder != null && string.Equals(CollectionType.Movies, collectionFolder.CollectionType, StringComparison.OrdinalIgnoreCase))
{ {
return GetMovieFolders(item, user, stubType, sort, startIndex, limit); return GetMovieFolders(item, user, stubType, sort, startIndex, limit);
} }
else if (string.Equals(CollectionType.TvShows, collectionFolder.CollectionType, StringComparison.OrdinalIgnoreCase)) if (collectionFolder != null && string.Equals(CollectionType.TvShows, collectionFolder.CollectionType, StringComparison.OrdinalIgnoreCase))
{ {
return GetTvFolders(item, user, stubType, sort, startIndex, limit); return GetTvFolders(item, user, stubType, sort, startIndex, limit);
} }
else if (string.Equals(CollectionType.Folders, collectionFolder.CollectionType, StringComparison.OrdinalIgnoreCase))
if (collectionFolder != null && 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)) if (collectionFolder != null && string.Equals(CollectionType.LiveTv, collectionFolder.CollectionType, StringComparison.OrdinalIgnoreCase))
{ {
return GetLiveTvChannels(user, sort, startIndex, limit); return GetLiveTvChannels(item, user, stubType, sort, startIndex, limit);
} }
} }
@@ -552,7 +524,7 @@ namespace Emby.Dlna.ContentDirectory
Limit = limit, Limit = limit,
StartIndex = startIndex, StartIndex = startIndex,
IsVirtualItem = false, IsVirtualItem = false,
ExcludeItemTypes = new[] { typeof(Book).Name }, ExcludeItemTypes = new[] { typeof(Game).Name, typeof(Book).Name },
IsPlaceHolder = false, IsPlaceHolder = false,
DtoOptions = GetDtoOptions() DtoOptions = GetDtoOptions()
}; };
@@ -564,7 +536,7 @@ namespace Emby.Dlna.ContentDirectory
return ToResult(queryResult); 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) var query = new InternalItemsQuery(user)
{ {
@@ -596,7 +568,7 @@ namespace Emby.Dlna.ContentDirectory
if (stubType.HasValue && stubType.Value == StubType.Playlists) if (stubType.HasValue && stubType.Value == StubType.Playlists)
{ {
return GetMusicPlaylists(user, query); return GetMusicPlaylists(item, user, query);
} }
if (stubType.HasValue && stubType.Value == StubType.Albums) if (stubType.HasValue && stubType.Value == StubType.Albums)
@@ -693,7 +665,7 @@ namespace Emby.Dlna.ContentDirectory
return new QueryResult<ServerItem> return new QueryResult<ServerItem>
{ {
Items = list, Items = list.ToArray(),
TotalRecordCount = list.Count TotalRecordCount = list.Count
}; };
} }
@@ -724,7 +696,7 @@ namespace Emby.Dlna.ContentDirectory
if (stubType.HasValue && stubType.Value == StubType.Collections) if (stubType.HasValue && stubType.Value == StubType.Collections)
{ {
return GetMovieCollections(user, query); return GetMovieCollections(item, user, query);
} }
if (stubType.HasValue && stubType.Value == StubType.Favorites) if (stubType.HasValue && stubType.Value == StubType.Favorites)
@@ -737,42 +709,46 @@ namespace Emby.Dlna.ContentDirectory
return GetGenres(item, user, query); return GetGenres(item, user, query);
} }
var array = new[] var list = new List<ServerItem>();
list.Add(new ServerItem(item)
{ {
new ServerItem(item) StubType = StubType.ContinueWatching
{ });
StubType = StubType.ContinueWatching
}, list.Add(new ServerItem(item)
new ServerItem(item) {
{ StubType = StubType.Latest
StubType = StubType.Latest });
},
new ServerItem(item) list.Add(new ServerItem(item)
{ {
StubType = StubType.Movies StubType = StubType.Movies
}, });
new ServerItem(item)
{ list.Add(new ServerItem(item)
StubType = StubType.Collections {
}, StubType = StubType.Collections
new ServerItem(item) });
{
StubType = StubType.Favorites list.Add(new ServerItem(item)
}, {
new ServerItem(item) StubType = StubType.Favorites
{ });
StubType = StubType.Genres
} list.Add(new ServerItem(item)
}; {
StubType = StubType.Genres
});
return new QueryResult<ServerItem> return new QueryResult<ServerItem>
{ {
Items = array, Items = list.ToArray(),
TotalRecordCount = array.Length 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) var folders = _libraryManager.GetUserRootFolder().GetChildren(user, true)
.OrderBy(i => i.SortName) .OrderBy(i => i.SortName)
@@ -782,11 +758,11 @@ namespace Emby.Dlna.ContentDirectory
}) })
.ToArray(); .ToArray();
return ApplyPaging(new QueryResult<ServerItem> return new QueryResult<ServerItem>
{ {
Items = folders, Items = folders,
TotalRecordCount = folders.Length TotalRecordCount = folders.Length
}, startIndex, limit); };
} }
private QueryResult<ServerItem> GetTvFolders(BaseItem item, User user, StubType? stubType, SortCriteria sort, int? startIndex, int? limit) private QueryResult<ServerItem> GetTvFolders(BaseItem item, User user, StubType? stubType, SortCriteria sort, int? startIndex, int? limit)
@@ -805,7 +781,7 @@ namespace Emby.Dlna.ContentDirectory
if (stubType.HasValue && stubType.Value == StubType.NextUp) if (stubType.HasValue && stubType.Value == StubType.NextUp)
{ {
return GetNextUp(item, query); return GetNextUp(item, user, query);
} }
if (stubType.HasValue && stubType.Value == StubType.Latest) if (stubType.HasValue && stubType.Value == StubType.Latest)
@@ -872,7 +848,7 @@ namespace Emby.Dlna.ContentDirectory
return new QueryResult<ServerItem> return new QueryResult<ServerItem>
{ {
Items = list, Items = list.ToArray(),
TotalRecordCount = list.Count TotalRecordCount = list.Count
}; };
} }
@@ -883,10 +859,10 @@ namespace Emby.Dlna.ContentDirectory
query.Parent = parent; query.Parent = parent;
query.SetUser(user); query.SetUser(user);
query.OrderBy = new[] query.OrderBy = new ValueTuple<string, SortOrder>[]
{ {
(ItemSortBy.DatePlayed, SortOrder.Descending), new ValueTuple<string, SortOrder> (ItemSortBy.DatePlayed, SortOrder.Descending),
(ItemSortBy.SortName, SortOrder.Ascending) new ValueTuple<string, SortOrder> (ItemSortBy.SortName, SortOrder.Ascending)
}; };
query.IsResumable = true; query.IsResumable = true;
@@ -923,10 +899,10 @@ namespace Emby.Dlna.ContentDirectory
return ToResult(result); 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.Recursive = true;
// query.Parent = parent; //query.Parent = parent;
query.SetUser(user); query.SetUser(user);
query.IncludeItemTypes = new[] { typeof(BoxSet).Name }; query.IncludeItemTypes = new[] { typeof(BoxSet).Name };
@@ -1118,10 +1094,10 @@ namespace Emby.Dlna.ContentDirectory
return ToResult(result); 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.Parent = null;
query.IncludeItemTypes = new[] { nameof(Playlist) }; query.IncludeItemTypes = new[] { typeof(Playlist).Name };
query.SetUser(user); query.SetUser(user);
query.Recursive = true; query.Recursive = true;
@@ -1132,29 +1108,31 @@ namespace Emby.Dlna.ContentDirectory
private QueryResult<ServerItem> GetMusicLatest(BaseItem parent, User user, InternalItemsQuery query) private QueryResult<ServerItem> GetMusicLatest(BaseItem parent, User user, InternalItemsQuery query)
{ {
query.OrderBy = Array.Empty<(string, SortOrder)>(); query.OrderBy = new ValueTuple<string, SortOrder>[] { };
var items = _userViewManager.GetLatestItems(new LatestItemsQuery var items = _userViewManager.GetLatestItems(new LatestItemsQuery
{ {
UserId = user.Id, UserId = user.Id,
Limit = 50, Limit = 50,
IncludeItemTypes = new[] { nameof(Audio) }, IncludeItemTypes = new[] { typeof(Audio).Name },
ParentId = parent?.Id ?? Guid.Empty, ParentId = parent == null ? Guid.Empty : parent.Id,
GroupItems = true GroupItems = true
}, query.DtoOptions).Select(i => i.Item1 ?? i.Item2.FirstOrDefault()).Where(i => i != null).ToArray(); }, query.DtoOptions).Select(i => i.Item1 ?? i.Item2.FirstOrDefault()).Where(i => i != null).ToArray();
return ToResult(items); 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)>(); query.OrderBy = new ValueTuple<string, SortOrder>[] { };
var result = _tvSeriesManager.GetNextUp(new NextUpQuery var result = _tvSeriesManager.GetNextUp(new NextUpQuery
{ {
Limit = query.Limit, Limit = query.Limit,
StartIndex = query.StartIndex, StartIndex = query.StartIndex,
UserId = query.User.Id UserId = query.User.Id
}, new[] { parent }, query.DtoOptions); }, new[] { parent }, query.DtoOptions);
return ToResult(result); return ToResult(result);
@@ -1162,7 +1140,7 @@ namespace Emby.Dlna.ContentDirectory
private QueryResult<ServerItem> GetTvLatest(BaseItem parent, User user, InternalItemsQuery query) private QueryResult<ServerItem> GetTvLatest(BaseItem parent, User user, InternalItemsQuery query)
{ {
query.OrderBy = Array.Empty<(string, SortOrder)>(); query.OrderBy = new ValueTuple<string, SortOrder>[] { };
var items = _userViewManager.GetLatestItems(new LatestItemsQuery var items = _userViewManager.GetLatestItems(new LatestItemsQuery
{ {
@@ -1171,6 +1149,7 @@ namespace Emby.Dlna.ContentDirectory
IncludeItemTypes = new[] { typeof(Episode).Name }, IncludeItemTypes = new[] { typeof(Episode).Name },
ParentId = parent == null ? Guid.Empty : parent.Id, ParentId = parent == null ? Guid.Empty : parent.Id,
GroupItems = false GroupItems = false
}, query.DtoOptions).Select(i => i.Item1 ?? i.Item2.FirstOrDefault()).Where(i => i != null).ToArray(); }, query.DtoOptions).Select(i => i.Item1 ?? i.Item2.FirstOrDefault()).Where(i => i != null).ToArray();
return ToResult(items); return ToResult(items);
@@ -1178,16 +1157,16 @@ namespace Emby.Dlna.ContentDirectory
private QueryResult<ServerItem> GetMovieLatest(BaseItem parent, User user, InternalItemsQuery query) private QueryResult<ServerItem> GetMovieLatest(BaseItem parent, User user, InternalItemsQuery query)
{ {
query.OrderBy = Array.Empty<(string, SortOrder)>(); query.OrderBy = new ValueTuple<string, SortOrder>[] { };
var items = _userViewManager.GetLatestItems( var items = _userViewManager.GetLatestItems(new LatestItemsQuery
new LatestItemsQuery
{ {
UserId = user.Id, UserId = user.Id,
Limit = 50, Limit = 50,
IncludeItemTypes = new[] { nameof(Movie) }, IncludeItemTypes = new[] { typeof(Movie).Name },
ParentId = parent?.Id ?? Guid.Empty, ParentId = parent == null ? Guid.Empty : parent.Id,
GroupItems = true GroupItems = true
}, query.DtoOptions).Select(i => i.Item1 ?? i.Item2.FirstOrDefault()).Where(i => i != null).ToArray(); }, query.DtoOptions).Select(i => i.Item1 ?? i.Item2.FirstOrDefault()).Where(i => i != null).ToArray();
return ToResult(items); return ToResult(items);
@@ -1220,11 +1199,7 @@ namespace Emby.Dlna.ContentDirectory
Recursive = true, Recursive = true,
ParentId = parentId, ParentId = parentId,
GenreIds = new[] { item.Id }, GenreIds = new[] { item.Id },
IncludeItemTypes = new[] IncludeItemTypes = new[] { typeof(Movie).Name, typeof(Series).Name },
{
nameof(Movie),
nameof(Series)
},
Limit = limit, Limit = limit,
StartIndex = startIndex, StartIndex = startIndex,
DtoOptions = GetDtoOptions() DtoOptions = GetDtoOptions()
@@ -1286,14 +1261,13 @@ namespace Emby.Dlna.ContentDirectory
private void SetSorting(InternalItemsQuery query, SortCriteria sort, bool isPreSorted) private void SetSorting(InternalItemsQuery query, SortCriteria sort, bool isPreSorted)
{ {
if (isPreSorted) var sortOrders = new List<string>();
if (!isPreSorted)
{ {
query.OrderBy = Array.Empty<(string, SortOrder)>(); sortOrders.Add(ItemSortBy.SortName);
}
else
{
query.OrderBy = new[] { (ItemSortBy.SortName, sort.SortOrder) };
} }
query.OrderBy = sortOrders.Select(i => new ValueTuple<string, SortOrder>(i, sort.SortOrder)).ToArray();
} }
private QueryResult<ServerItem> ApplyPaging(QueryResult<ServerItem> result, int? startIndex, int? limit) private QueryResult<ServerItem> ApplyPaging(QueryResult<ServerItem> result, int? startIndex, int? limit)
@@ -1303,24 +1277,24 @@ namespace Emby.Dlna.ContentDirectory
return result; return result;
} }
private ServerItem GetItemFromObjectId(string id) private ServerItem GetItemFromObjectId(string id, User user)
{ {
return DidlBuilder.IsIdRoot(id) return DidlBuilder.IsIdRoot(id)
? new ServerItem(_libraryManager.GetUserRootFolder()) ? new ServerItem(_libraryManager.GetUserRootFolder())
: ParseItemId(id); : ParseItemId(id, user);
} }
private ServerItem ParseItemId(string id) private ServerItem ParseItemId(string id, User user)
{ {
StubType? stubType = null; StubType? stubType = null;
// After using PlayTo, MediaMonkey sends a request to the server trying to get item info // After using PlayTo, MediaMonkey sends a request to the server trying to get item info
const string ParamsSrch = "Params="; const string paramsSrch = "Params=";
var paramsIndex = id.IndexOf(ParamsSrch, StringComparison.OrdinalIgnoreCase); var paramsIndex = id.IndexOf(paramsSrch, StringComparison.OrdinalIgnoreCase);
if (paramsIndex != -1) if (paramsIndex != -1)
{ {
id = id.Substring(paramsIndex + ParamsSrch.Length); id = id.Substring(paramsIndex + paramsSrch.Length);
var parts = id.Split(';'); var parts = id.Split(';');
id = parts[23]; id = parts[23];
@@ -1348,7 +1322,7 @@ namespace Emby.Dlna.ContentDirectory
}; };
} }
Logger.LogError("Error parsing item Id: {id}. Returning user root folder.", id); _logger.LogError("Error parsing item Id: {id}. Returning user root folder.", id);
return new ServerItem(_libraryManager.GetUserRootFolder()); return new ServerItem(_libraryManager.GetUserRootFolder());
} }
@@ -1357,7 +1331,6 @@ namespace Emby.Dlna.ContentDirectory
internal class ServerItem internal class ServerItem
{ {
public BaseItem Item { get; set; } public BaseItem Item { get; set; }
public StubType? StubType { get; set; } public StubType? StubType { get; set; }
public ServerItem(BaseItem item) public ServerItem(BaseItem item)
@@ -1,5 +1,3 @@
#pragma warning disable CS1591
using System.Collections.Generic; using System.Collections.Generic;
using Emby.Dlna.Common; using Emby.Dlna.Common;
+3 -5
View File
@@ -1,13 +1,11 @@
#pragma warning disable CS1591 using System.Collections.Generic;
using System.IO; using System.IO;
using Microsoft.AspNetCore.Http;
namespace Emby.Dlna namespace Emby.Dlna
{ {
public class ControlRequest public class ControlRequest
{ {
public IHeaderDictionary Headers { get; set; } public IDictionary<string, string> Headers { get; set; }
public Stream InputXml { get; set; } public Stream InputXml { get; set; }
@@ -17,7 +15,7 @@ namespace Emby.Dlna
public ControlRequest() public ControlRequest()
{ {
Headers = new HeaderDictionary(); Headers = new Dictionary<string, string>();
} }
} }
} }
+5 -7
View File
@@ -1,20 +1,18 @@
#pragma warning disable CS1591
using System.Collections.Generic; using System.Collections.Generic;
namespace Emby.Dlna namespace Emby.Dlna
{ {
public class ControlResponse public class ControlResponse
{ {
public ControlResponse()
{
Headers = new Dictionary<string, string>();
}
public IDictionary<string, string> Headers { get; set; } public IDictionary<string, string> Headers { get; set; }
public string Xml { get; set; } public string Xml { get; set; }
public bool IsSuccessful { get; set; } public bool IsSuccessful { get; set; }
public ControlResponse()
{
Headers = new Dictionary<string, string>();
}
} }
} }
+254 -309
View File
@@ -1,34 +1,27 @@
#pragma warning disable CS1591
using System; using System;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Xml; using System.Xml;
using Emby.Dlna.Configuration;
using Emby.Dlna.ContentDirectory; using Emby.Dlna.ContentDirectory;
using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Controller.Playlists; using MediaBrowser.Controller.Playlists;
using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Drawing; using MediaBrowser.Model.Drawing;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Net; using MediaBrowser.Model.Net;
using Microsoft.Extensions.Logging; 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 namespace Emby.Dlna.Didl
{ {
@@ -50,33 +43,22 @@ namespace Emby.Dlna.Didl
private readonly ILocalizationManager _localization; private readonly ILocalizationManager _localization;
private readonly IMediaSourceManager _mediaSourceManager; private readonly IMediaSourceManager _mediaSourceManager;
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly IMediaEncoder _mediaEncoder;
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
private readonly IMediaEncoder _mediaEncoder;
public DidlBuilder( public DidlBuilder(DeviceProfile profile, User user, IImageProcessor imageProcessor, string serverAddress, string accessToken, IUserDataManager userDataManager, ILocalizationManager localization, IMediaSourceManager mediaSourceManager, ILogger logger, ILibraryManager libraryManager, IMediaEncoder mediaEncoder)
DeviceProfile profile,
User user,
IImageProcessor imageProcessor,
string serverAddress,
string accessToken,
IUserDataManager userDataManager,
ILocalizationManager localization,
IMediaSourceManager mediaSourceManager,
ILogger logger,
IMediaEncoder mediaEncoder,
ILibraryManager libraryManager)
{ {
_profile = profile; _profile = profile;
_user = user;
_imageProcessor = imageProcessor; _imageProcessor = imageProcessor;
_serverAddress = serverAddress; _serverAddress = serverAddress;
_accessToken = accessToken;
_userDataManager = userDataManager; _userDataManager = userDataManager;
_localization = localization; _localization = localization;
_mediaSourceManager = mediaSourceManager; _mediaSourceManager = mediaSourceManager;
_logger = logger; _logger = logger;
_mediaEncoder = mediaEncoder;
_libraryManager = libraryManager; _libraryManager = libraryManager;
_mediaEncoder = mediaEncoder;
_accessToken = accessToken;
_user = user;
} }
public static string NormalizeDlnaMediaUrl(string url) public static string NormalizeDlnaMediaUrl(string url)
@@ -84,7 +66,7 @@ namespace Emby.Dlna.Didl
return url + "&dlnaheaders=true"; 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 var settings = new XmlWriterSettings
{ {
@@ -98,21 +80,21 @@ namespace Emby.Dlna.Didl
{ {
using (var writer = XmlWriter.Create(builder, settings)) using (var writer = XmlWriter.Create(builder, settings))
{ {
// writer.WriteStartDocument(); //writer.WriteStartDocument();
writer.WriteStartElement(string.Empty, "DIDL-Lite", NS_DIDL); writer.WriteStartElement(string.Empty, "DIDL-Lite", NS_DIDL);
writer.WriteAttributeString("xmlns", "dc", null, NS_DC); writer.WriteAttributeString("xmlns", "dc", null, NS_DC);
writer.WriteAttributeString("xmlns", "dlna", null, NS_DLNA); writer.WriteAttributeString("xmlns", "dlna", null, NS_DLNA);
writer.WriteAttributeString("xmlns", "upnp", null, NS_UPNP); writer.WriteAttributeString("xmlns", "upnp", null, NS_UPNP);
// didl.SetAttribute("xmlns:sec", NS_SEC); //didl.SetAttribute("xmlns:sec", NS_SEC);
WriteXmlRootAttributes(_profile, writer); 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.WriteFullEndElement();
// writer.WriteEndDocument(); //writer.WriteEndDocument();
} }
return builder.ToString(); return builder.ToString();
@@ -135,7 +117,7 @@ namespace Emby.Dlna.Didl
} }
} }
public void WriteItemElement( public void WriteItemElement(DlnaOptions options,
XmlWriter writer, XmlWriter writer,
BaseItem item, BaseItem item,
User user, User user,
@@ -167,28 +149,43 @@ namespace Emby.Dlna.Didl
AddGeneralProperties(item, null, context, writer, filter); AddGeneralProperties(item, null, context, writer, filter);
AddSamsungBookmarkInfo(item, user, writer, streamInfo); AddSamsungBookmarkInfo(item, user, writer);
// refID? // refID?
// storeAttribute(itemNode, object, ClassProperties.REF_ID, false); // 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)) 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)) 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(); writer.WriteFullEndElement();
} }
private void AddVideoResource(XmlWriter writer, BaseItem video, string deviceId, Filter filter, StreamInfo streamInfo = null) private string GetMimeType(string input)
{
var mime = MimeTypes.GetMimeType(input);
// TODO: Instead of being hard-coded here, this should probably be moved into all of the existing profiles
if (string.Equals(mime, "video/mp2t", StringComparison.OrdinalIgnoreCase))
{
mime = "video/mpeg";
}
return mime;
}
private void AddVideoResource(DlnaOptions options, XmlWriter writer, BaseItem video, string deviceId, Filter filter, StreamInfo streamInfo = null)
{ {
if (streamInfo == null) if (streamInfo == null)
{ {
@@ -232,18 +229,15 @@ namespace Emby.Dlna.Didl
foreach (var contentFeature in contentFeatureList) 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); var subtitleProfiles = streamInfo.GetSubtitleProfiles(_mediaEncoder, false, _serverAddress, _accessToken)
.Where(subtitle => subtitle.DeliveryMethod == SubtitleDeliveryMethod.External)
.ToList();
foreach (var subtitle in subtitleProfiles) foreach (var subtitle in subtitleProfiles)
{ {
if (subtitle.DeliveryMethod != SubtitleDeliveryMethod.External)
{
continue;
}
var subtitleAdded = AddSubtitleElement(writer, subtitle); var subtitleAdded = AddSubtitleElement(writer, subtitle);
if (subtitleAdded && _profile.EnableSingleSubtitleLimit) if (subtitleAdded && _profile.EnableSingleSubtitleLimit)
@@ -256,8 +250,7 @@ namespace Emby.Dlna.Didl
private bool AddSubtitleElement(XmlWriter writer, SubtitleStreamInfo info) private bool AddSubtitleElement(XmlWriter writer, SubtitleStreamInfo info)
{ {
var subtitleProfile = _profile.SubtitleProfiles var subtitleProfile = _profile.SubtitleProfiles
.FirstOrDefault(i => string.Equals(info.Format, i.Format, StringComparison.OrdinalIgnoreCase) .FirstOrDefault(i => string.Equals(info.Format, i.Format, StringComparison.OrdinalIgnoreCase) && i.Method == SubtitleDeliveryMethod.External);
&& i.Method == SubtitleDeliveryMethod.External);
if (subtitleProfile == null) if (subtitleProfile == null)
{ {
@@ -272,7 +265,7 @@ namespace Emby.Dlna.Didl
// <sec:CaptionInfo sec:type="srt">http://192.168.1.3:9999/video.srt</sec:CaptionInfo> // <sec:CaptionInfo sec:type="srt">http://192.168.1.3:9999/video.srt</sec:CaptionInfo>
writer.WriteStartElement("sec", "CaptionInfoEx", null); writer.WriteStartElement("sec", "CaptionInfoEx", null);
writer.WriteAttributeString("sec", "type", null, info.Format.ToLowerInvariant()); writer.WriteAttributeString("sec", "type", null, info.Format.ToLower());
writer.WriteString(info.Url); writer.WriteString(info.Url);
writer.WriteFullEndElement(); writer.WriteFullEndElement();
@@ -289,10 +282,7 @@ namespace Emby.Dlna.Didl
else else
{ {
writer.WriteStartElement(string.Empty, "res", NS_DIDL); writer.WriteStartElement(string.Empty, "res", NS_DIDL);
var protocolInfo = string.Format( var protocolInfo = string.Format("http-get:*:text/{0}:*", info.Format.ToLower());
CultureInfo.InvariantCulture,
"http-get:*:text/{0}:*",
info.Format.ToLowerInvariant());
writer.WriteAttributeString("protocolInfo", protocolInfo); writer.WriteAttributeString("protocolInfo", protocolInfo);
writer.WriteString(info.Url); writer.WriteString(info.Url);
@@ -302,7 +292,7 @@ namespace Emby.Dlna.Didl
return true; 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); writer.WriteStartElement(string.Empty, "res", NS_DIDL);
@@ -344,13 +334,7 @@ namespace Emby.Dlna.Didl
{ {
if (targetWidth.HasValue && targetHeight.HasValue) if (targetWidth.HasValue && targetHeight.HasValue)
{ {
writer.WriteAttributeString( writer.WriteAttributeString("resolution", string.Format("{0}x{1}", targetWidth.Value, targetHeight.Value));
"resolution",
string.Format(
CultureInfo.InvariantCulture,
"{0}x{1}",
targetWidth.Value,
targetHeight.Value));
} }
} }
@@ -384,19 +368,17 @@ namespace Emby.Dlna.Didl
streamInfo.TargetVideoCodecTag, streamInfo.TargetVideoCodecTag,
streamInfo.IsTargetAVC); 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) var mimeType = mediaProfile == null || string.IsNullOrEmpty(mediaProfile.MimeType)
? MimeTypes.GetMimeType(filename) ? GetMimeType(filename)
: mediaProfile.MimeType; : mediaProfile.MimeType;
writer.WriteAttributeString( writer.WriteAttributeString("protocolInfo", string.Format(
"protocolInfo", "http-get:*:{0}:{1}",
string.Format( mimeType,
CultureInfo.InvariantCulture, contentFeatures
"http-get:*:{0}:{1}", ));
mimeType,
contentFeatures));
writer.WriteString(url); writer.WriteString(url);
@@ -405,125 +387,110 @@ namespace Emby.Dlna.Didl
private string GetDisplayName(BaseItem item, StubType? itemStubType, BaseItem context) private string GetDisplayName(BaseItem item, StubType? itemStubType, BaseItem context)
{ {
if (itemStubType.HasValue) if (itemStubType.HasValue && itemStubType.Value == StubType.Latest)
{ {
switch (itemStubType.Value) return _localization.GetLocalizedString("Latest");
{ }
case StubType.Latest: return _localization.GetLocalizedString("Latest"); if (itemStubType.HasValue && itemStubType.Value == StubType.Playlists)
case StubType.Playlists: return _localization.GetLocalizedString("Playlists"); {
case StubType.AlbumArtists: return _localization.GetLocalizedString("HeaderAlbumArtists"); return _localization.GetLocalizedString("Playlists");
case StubType.Albums: return _localization.GetLocalizedString("Albums"); }
case StubType.Artists: return _localization.GetLocalizedString("Artists"); if (itemStubType.HasValue && itemStubType.Value == StubType.AlbumArtists)
case StubType.Songs: return _localization.GetLocalizedString("Songs"); {
case StubType.Genres: return _localization.GetLocalizedString("Genres"); return _localization.GetLocalizedString("HeaderAlbumArtists");
case StubType.FavoriteAlbums: return _localization.GetLocalizedString("HeaderFavoriteAlbums"); }
case StubType.FavoriteArtists: return _localization.GetLocalizedString("HeaderFavoriteArtists"); if (itemStubType.HasValue && itemStubType.Value == StubType.Albums)
case StubType.FavoriteSongs: return _localization.GetLocalizedString("HeaderFavoriteSongs"); {
case StubType.ContinueWatching: return _localization.GetLocalizedString("HeaderContinueWatching"); return _localization.GetLocalizedString("Albums");
case StubType.Movies: return _localization.GetLocalizedString("Movies"); }
case StubType.Collections: return _localization.GetLocalizedString("Collections"); if (itemStubType.HasValue && itemStubType.Value == StubType.Artists)
case StubType.Favorites: return _localization.GetLocalizedString("Favorites"); {
case StubType.NextUp: return _localization.GetLocalizedString("HeaderNextUp"); return _localization.GetLocalizedString("Artists");
case StubType.FavoriteSeries: return _localization.GetLocalizedString("HeaderFavoriteShows"); }
case StubType.FavoriteEpisodes: return _localization.GetLocalizedString("HeaderFavoriteEpisodes"); if (itemStubType.HasValue && itemStubType.Value == StubType.Songs)
case StubType.Series: return _localization.GetLocalizedString("Shows"); {
} return _localization.GetLocalizedString("Songs");
}
if (itemStubType.HasValue && itemStubType.Value == StubType.Genres)
{
return _localization.GetLocalizedString("Genres");
}
if (itemStubType.HasValue && itemStubType.Value == StubType.FavoriteAlbums)
{
return _localization.GetLocalizedString("HeaderFavoriteAlbums");
}
if (itemStubType.HasValue && itemStubType.Value == StubType.FavoriteArtists)
{
return _localization.GetLocalizedString("HeaderFavoriteArtists");
}
if (itemStubType.HasValue && itemStubType.Value == StubType.FavoriteSongs)
{
return _localization.GetLocalizedString("HeaderFavoriteSongs");
}
if (itemStubType.HasValue && itemStubType.Value == StubType.ContinueWatching)
{
return _localization.GetLocalizedString("HeaderContinueWatching");
}
if (itemStubType.HasValue && itemStubType.Value == StubType.Movies)
{
return _localization.GetLocalizedString("Movies");
}
if (itemStubType.HasValue && itemStubType.Value == StubType.Collections)
{
return _localization.GetLocalizedString("Collections");
}
if (itemStubType.HasValue && itemStubType.Value == StubType.Favorites)
{
return _localization.GetLocalizedString("Favorites");
}
if (itemStubType.HasValue && itemStubType.Value == StubType.NextUp)
{
return _localization.GetLocalizedString("HeaderNextUp");
}
if (itemStubType.HasValue && itemStubType.Value == StubType.FavoriteSeries)
{
return _localization.GetLocalizedString("HeaderFavoriteShows");
}
if (itemStubType.HasValue && itemStubType.Value == StubType.FavoriteEpisodes)
{
return _localization.GetLocalizedString("HeaderFavoriteEpisodes");
}
if (itemStubType.HasValue && itemStubType.Value == StubType.Series)
{
return _localization.GetLocalizedString("Shows");
} }
return item is Episode episode var episode = item as Episode;
? GetEpisodeDisplayName(episode, context) var season = context as Season;
: item.Name;
}
/// <summary> if (episode != null && season != null)
/// Gets episode display name appropriate for the given context.
/// </summary>
/// <remarks>
/// If context is a season, this will return a string containing just episode number and name.
/// Otherwise the result will include series nams and season number.
/// </remarks>
/// <param name="episode">The episode.</param>
/// <param name="context">Current context.</param>
/// <returns>Formatted name of the episode.</returns>
private string GetEpisodeDisplayName(Episode episode, BaseItem context)
{
string[] components;
if (context is Season season)
{ {
// This is a special embedded within a season // This is a special embedded within a season
if (episode.ParentIndexNumber.HasValue && episode.ParentIndexNumber.Value == 0 if (item.ParentIndexNumber.HasValue && item.ParentIndexNumber.Value == 0)
&& season.IndexNumber.HasValue && season.IndexNumber.Value != 0)
{ {
return string.Format( if (season.IndexNumber.HasValue && season.IndexNumber.Value != 0)
CultureInfo.InvariantCulture, {
_localization.GetLocalizedString("ValueSpecialEpisodeName"), return string.Format(_localization.GetLocalizedString("ValueSpecialEpisodeName"), item.Name);
episode.Name); }
} }
// inside a season use simple format (ex. '12 - Episode Name') if (item.IndexNumber.HasValue)
var epNumberName = GetEpisodeIndexFullName(episode);
components = new[] { epNumberName, episode.Name };
}
else
{
// outside a season include series and season details (ex. 'TV Show - S05E11 - Episode Name')
var epNumberName = GetEpisodeNumberDisplayName(episode);
components = new[] { episode.SeriesName, epNumberName, episode.Name };
}
return string.Join(" - ", components.Where(NotNullOrWhiteSpace));
}
/// <summary>
/// Gets complete episode number.
/// </summary>
/// <param name="episode">The episode.</param>
/// <returns>For single episodes returns just the number. For double episodes - current and ending numbers.</returns>
private string GetEpisodeIndexFullName(Episode episode)
{
var name = string.Empty;
if (episode.IndexNumber.HasValue)
{
name += episode.IndexNumber.Value.ToString("00", CultureInfo.InvariantCulture);
if (episode.IndexNumberEnd.HasValue)
{ {
name += "-" + episode.IndexNumberEnd.Value.ToString("00", CultureInfo.InvariantCulture); var number = item.IndexNumber.Value.ToString("00", CultureInfo.InvariantCulture);
if (episode.IndexNumberEnd.HasValue)
{
number += "-" + episode.IndexNumberEnd.Value.ToString("00", CultureInfo.InvariantCulture);
}
return number + " - " + item.Name;
} }
} }
return name; return item.Name;
} }
/// <summary> private void AddAudioResource(DlnaOptions options, XmlWriter writer, BaseItem audio, string deviceId, Filter filter, StreamInfo streamInfo = null)
/// Gets episode number formatted as 'S##E##'.
/// </summary>
/// <param name="episode">The episode.</param>
/// <returns>Formatted episode number.</returns>
private string GetEpisodeNumberDisplayName(Episode episode)
{
var name = string.Empty;
var seasonNumber = episode.Season?.IndexNumber;
if (seasonNumber.HasValue)
{
name = "S" + seasonNumber.Value.ToString("00", CultureInfo.InvariantCulture);
}
var indexName = GetEpisodeIndexFullName(episode);
if (!string.IsNullOrWhiteSpace(indexName))
{
name += "E" + indexName;
}
return name;
}
private bool NotNullOrWhiteSpace(string s) => !string.IsNullOrWhiteSpace(s);
private void AddAudioResource(XmlWriter writer, BaseItem audio, string deviceId, Filter filter, StreamInfo streamInfo = null)
{ {
writer.WriteStartElement(string.Empty, "res", NS_DIDL); writer.WriteStartElement(string.Empty, "res", NS_DIDL);
@@ -589,10 +556,10 @@ namespace Emby.Dlna.Didl
targetSampleRate, targetSampleRate,
targetAudioBitDepth); 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) var mimeType = mediaProfile == null || string.IsNullOrEmpty(mediaProfile.MimeType)
? MimeTypes.GetMimeType(filename) ? GetMimeType(filename)
: mediaProfile.MimeType; : mediaProfile.MimeType;
var contentFeatures = new ContentFeatureBuilder(_profile).BuildAudioHeader(streamInfo.Container, var contentFeatures = new ContentFeatureBuilder(_profile).BuildAudioHeader(streamInfo.Container,
@@ -605,13 +572,11 @@ namespace Emby.Dlna.Didl
streamInfo.RunTimeTicks ?? 0, streamInfo.RunTimeTicks ?? 0,
streamInfo.TranscodeSeekInfo); streamInfo.TranscodeSeekInfo);
writer.WriteAttributeString( writer.WriteAttributeString("protocolInfo", string.Format(
"protocolInfo", "http-get:*:{0}:{1}",
string.Format( mimeType,
CultureInfo.InvariantCulture, contentFeatures
"http-get:*:{0}:{1}", ));
mimeType,
contentFeatures));
writer.WriteString(url); writer.WriteString(url);
@@ -619,10 +584,19 @@ namespace Emby.Dlna.Didl
} }
public static bool IsIdRoot(string id) public static bool IsIdRoot(string id)
=> string.IsNullOrWhiteSpace(id) {
|| string.Equals(id, "0", StringComparison.OrdinalIgnoreCase) if (string.IsNullOrWhiteSpace(id) ||
string.Equals(id, "0", StringComparison.OrdinalIgnoreCase)
// Samsung sometimes uses 1 as root // Samsung sometimes uses 1 as root
|| string.Equals(id, "1", StringComparison.OrdinalIgnoreCase); || string.Equals(id, "1", StringComparison.OrdinalIgnoreCase))
{
return true;
}
return false;
}
public void WriteFolderElement(XmlWriter writer, BaseItem folder, StubType? stubType, BaseItem context, int childCount, Filter filter, string requestedId = null) public void WriteFolderElement(XmlWriter writer, BaseItem folder, StubType? stubType, BaseItem context, int childCount, Filter filter, string requestedId = null)
{ {
@@ -634,7 +608,7 @@ namespace Emby.Dlna.Didl
var clientId = GetClientId(folder, stubType); var clientId = GetClientId(folder, stubType);
if (string.Equals(requestedId, "0", StringComparison.Ordinal)) if (string.Equals(requestedId, "0"))
{ {
writer.WriteAttributeString("id", "0"); writer.WriteAttributeString("id", "0");
writer.WriteAttributeString("parentID", "-1"); writer.WriteAttributeString("parentID", "-1");
@@ -663,19 +637,19 @@ namespace Emby.Dlna.Didl
AddGeneralProperties(folder, stubType, context, writer, filter); AddGeneralProperties(folder, stubType, context, writer, filter);
AddCover(folder, stubType, writer); AddCover(folder, context, stubType, writer);
writer.WriteFullEndElement(); writer.WriteFullEndElement();
} }
private void AddSamsungBookmarkInfo(BaseItem item, User user, XmlWriter writer, StreamInfo streamInfo) private void AddSamsungBookmarkInfo(BaseItem item, User user, XmlWriter writer)
{ {
if (!item.SupportsPositionTicksResume || item is Folder) if (!item.SupportsPositionTicksResume || item is Folder)
{ {
return; return;
} }
XmlAttribute secAttribute = null; MediaBrowser.Model.Dlna.XmlAttribute secAttribute = null;
foreach (var attribute in _profile.XmlRootAttributes) foreach (var attribute in _profile.XmlRootAttributes)
{ {
if (string.Equals(attribute.Name, "xmlns:sec", StringComparison.OrdinalIgnoreCase)) if (string.Equals(attribute.Name, "xmlns:sec", StringComparison.OrdinalIgnoreCase))
@@ -692,26 +666,22 @@ namespace Emby.Dlna.Didl
} }
var userdata = _userDataManager.GetUserData(user, item); var userdata = _userDataManager.GetUserData(user, item);
var playbackPositionTicks = (streamInfo != null && streamInfo.StartPositionTicks > 0) ? streamInfo.StartPositionTicks : userdata.PlaybackPositionTicks;
if (playbackPositionTicks > 0) if (userdata.PlaybackPositionTicks > 0)
{ {
var elementValue = string.Format( var elementValue = string.Format("BM={0}", Convert.ToInt32(TimeSpan.FromTicks(userdata.PlaybackPositionTicks).TotalSeconds).ToString(_usCulture));
CultureInfo.InvariantCulture,
"BM={0}",
Convert.ToInt32(TimeSpan.FromTicks(playbackPositionTicks).TotalSeconds));
AddValue(writer, "sec", "dcmInfo", elementValue, secAttribute.Value); AddValue(writer, "sec", "dcmInfo", elementValue, secAttribute.Value);
} }
} }
/// <summary> /// <summary>
/// Adds fields used by both items and folders. /// Adds fields used by both items and folders
/// </summary> /// </summary>
private void AddCommonFields(BaseItem item, StubType? itemStubType, BaseItem context, XmlWriter writer, Filter filter) 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 // 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 // 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); AddValue(writer, "dc", "title", GetDisplayName(item, itemStubType, context), NS_DC);
} }
@@ -722,7 +692,7 @@ namespace Emby.Dlna.Didl
{ {
if (item.PremiereDate.HasValue) if (item.PremiereDate.HasValue)
{ {
AddValue(writer, "dc", "date", item.PremiereDate.Value.ToString("o", CultureInfo.InvariantCulture), NS_DC); AddValue(writer, "dc", "date", item.PremiereDate.Value.ToString("o"), NS_DC);
} }
} }
@@ -750,7 +720,7 @@ namespace Emby.Dlna.Didl
AddValue(writer, "dc", "description", desc, NS_DC); AddValue(writer, "dc", "description", desc, NS_DC);
} }
} }
// if (filter.Contains("upnp:longDescription")) //if (filter.Contains("upnp:longDescription"))
//{ //{
// if (!string.IsNullOrWhiteSpace(item.Overview)) // if (!string.IsNullOrWhiteSpace(item.Overview))
// { // {
@@ -765,7 +735,6 @@ namespace Emby.Dlna.Didl
{ {
AddValue(writer, "dc", "rating", item.OfficialRating, NS_DC); AddValue(writer, "dc", "rating", item.OfficialRating, NS_DC);
} }
if (filter.Contains("upnp:rating")) if (filter.Contains("upnp:rating"))
{ {
AddValue(writer, "upnp", "rating", item.OfficialRating, NS_UPNP); AddValue(writer, "upnp", "rating", item.OfficialRating, NS_UPNP);
@@ -839,7 +808,7 @@ namespace Emby.Dlna.Didl
{ {
writer.WriteString(_profile.RequiresPlainFolders ? "object.container.storageFolder" : "object.container.genre.musicGenre"); writer.WriteString(_profile.RequiresPlainFolders ? "object.container.storageFolder" : "object.container.genre.musicGenre");
} }
else if (item is Genre) else if (item is Genre || item is GameGenre)
{ {
writer.WriteString(_profile.RequiresPlainFolders ? "object.container.storageFolder" : "object.container.genre"); writer.WriteString(_profile.RequiresPlainFolders ? "object.container.storageFolder" : "object.container.genre");
} }
@@ -853,45 +822,47 @@ namespace Emby.Dlna.Didl
private void AddPeople(BaseItem item, XmlWriter writer) private void AddPeople(BaseItem item, XmlWriter writer)
{ {
if (!item.SupportsPeople) //var types = new[]
{ //{
return; // PersonType.Director,
} // PersonType.Writer,
// PersonType.Producer,
// PersonType.Composer,
// "Creator"
//};
var types = new[] //var people = _libraryManager.GetPeople(item);
{
PersonType.Director,
PersonType.Writer,
PersonType.Producer,
PersonType.Composer,
"creator"
};
// Seeing some LG models locking up due content with large lists of people //var index = 0;
// 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
});
foreach (var actor in people) //// 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 type = types.FirstOrDefault(i => string.Equals(i, actor.Type, StringComparison.OrdinalIgnoreCase) || string.Equals(i, actor.Role, StringComparison.OrdinalIgnoreCase)) //var limit = 6;
?? PersonType.Actor;
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.ToLower(), actor.Name, NS_UPNP);
// index++;
// if (index >= limit)
// {
// break;
// }
//}
} }
private void AddGeneralProperties(BaseItem item, StubType? itemStubType, BaseItem context, XmlWriter writer, Filter filter) private void AddGeneralProperties(BaseItem item, StubType? itemStubType, BaseItem context, XmlWriter writer, Filter filter)
{ {
AddCommonFields(item, itemStubType, context, writer, filter); AddCommonFields(item, itemStubType, context, writer, filter);
var hasArtists = item as IHasArtist;
var hasAlbumArtists = item as IHasAlbumArtist; var hasAlbumArtists = item as IHasAlbumArtist;
if (item is IHasArtist hasArtists) if (hasArtists != null)
{ {
foreach (var artist in hasArtists.Artists) foreach (var artist in hasArtists.Artists)
{ {
@@ -959,9 +930,15 @@ 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); ImageDownloadInfo imageInfo = null;
// Finally, just use the image from the item
if (imageInfo == null)
{
imageInfo = GetImageInfo(item);
}
if (imageInfo == null) if (imageInfo == null)
{ {
@@ -991,6 +968,8 @@ namespace Emby.Dlna.Didl
} }
} }
AddImageResElement(item, writer, 160, 160, "jpg", "JPEG_TN");
if (!_profile.EnableSingleAlbumArtLimit || string.Equals(item.MediaType, MediaType.Photo, StringComparison.OrdinalIgnoreCase)) if (!_profile.EnableSingleAlbumArtLimit || string.Equals(item.MediaType, MediaType.Photo, StringComparison.OrdinalIgnoreCase))
{ {
AddImageResElement(item, writer, 4096, 4096, "jpg", "JPEG_LRG"); AddImageResElement(item, writer, 4096, 4096, "jpg", "JPEG_LRG");
@@ -999,12 +978,19 @@ namespace Emby.Dlna.Didl
AddImageResElement(item, writer, 4096, 4096, "png", "PNG_LRG"); AddImageResElement(item, writer, 4096, 4096, "png", "PNG_LRG");
AddImageResElement(item, writer, 160, 160, "png", "PNG_TN"); AddImageResElement(item, writer, 160, 160, "png", "PNG_TN");
} }
AddImageResElement(item, writer, 160, 160, "jpg", "JPEG_TN");
} }
private void AddImageResElement( private void AddEmbeddedImageAsCover(string name, XmlWriter writer)
BaseItem item, {
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, XmlWriter writer,
int maxWidth, int maxWidth,
int maxHeight, int maxHeight,
@@ -1030,17 +1016,13 @@ namespace Emby.Dlna.Didl
var contentFeatures = new ContentFeatureBuilder(_profile) var contentFeatures = new ContentFeatureBuilder(_profile)
.BuildImageHeader(format, width, height, imageInfo.IsDirectStream, org_Pn); .BuildImageHeader(format, width, height, imageInfo.IsDirectStream, org_Pn);
writer.WriteAttributeString( writer.WriteAttributeString("protocolInfo", string.Format(
"protocolInfo", "http-get:*:{0}:{1}",
string.Format( GetMimeType("file." + format),
CultureInfo.InvariantCulture, contentFeatures
"http-get:*:{0}:{1}", ));
MimeTypes.GetMimeType("file." + format),
contentFeatures));
writer.WriteAttributeString( writer.WriteAttributeString("resolution", string.Format("{0}x{1}", width, height));
"resolution",
string.Format(CultureInfo.InvariantCulture, "{0}x{1}", width, height));
writer.WriteString(albumartUrlInfo.Url); writer.WriteString(albumartUrlInfo.Url);
@@ -1053,12 +1035,10 @@ namespace Emby.Dlna.Didl
{ {
return GetImageInfo(item, ImageType.Primary); return GetImageInfo(item, ImageType.Primary);
} }
if (item.HasImage(ImageType.Thumb)) if (item.HasImage(ImageType.Thumb))
{ {
return GetImageInfo(item, ImageType.Thumb); return GetImageInfo(item, ImageType.Thumb);
} }
if (item.HasImage(ImageType.Backdrop)) if (item.HasImage(ImageType.Backdrop))
{ {
if (item is Channel) if (item is Channel)
@@ -1067,58 +1047,19 @@ namespace Emby.Dlna.Didl
} }
} }
// For audio tracks without art use album art if available. item = item.GetParents().FirstOrDefault(i => i.HasImage(ImageType.Primary));
if (item is Audio audioItem)
{
var album = audioItem.AlbumEntity;
return album != null && album.HasImage(ImageType.Primary)
? GetImageInfo(album, ImageType.Primary)
: null;
}
// Don't look beyond album/playlist level. Metadata service may assign an image from a different album/show to the parent folder. if (item != null)
if (item is MusicAlbum || item is Playlist)
{ {
return null; if (item.HasImage(ImageType.Primary))
} {
return GetImageInfo(item, ImageType.Primary);
// For other item types check parents, but be aware that image retrieved from a parent may be not suitable for this media item. }
var parentWithImage = GetFirstParentWithImageBelowUserRoot(item);
if (parentWithImage != null)
{
return GetImageInfo(parentWithImage, ImageType.Primary);
} }
return null; return null;
} }
private BaseItem GetFirstParentWithImageBelowUserRoot(BaseItem item)
{
if (item == null)
{
return null;
}
if (item.HasImage(ImageType.Primary))
{
return item;
}
var parent = item.GetParent();
if (parent is UserRootFolder)
{
return null;
}
// terminate in case we went past user root folder (unlikely?)
if (parent is Folder folder && folder.IsRoot)
{
return null;
}
return GetFirstParentWithImageBelowUserRoot(parent);
}
private ImageDownloadInfo GetImageInfo(BaseItem item, ImageType type) private ImageDownloadInfo GetImageInfo(BaseItem item, ImageType type)
{ {
var imageInfo = item.GetImageInfo(type, 0); var imageInfo = item.GetImageInfo(type, 0);
@@ -1138,24 +1079,25 @@ namespace Emby.Dlna.Didl
if (width == 0 || height == 0) if (width == 0 || height == 0)
{ {
// _imageProcessor.GetImageSize(item, imageInfo); //_imageProcessor.GetImageSize(item, imageInfo);
width = null; width = null;
height = null; height = null;
} }
else if (width == -1 || height == -1) else if (width == -1 || height == -1)
{ {
width = null; width = null;
height = null; height = null;
} }
// try //try
//{ //{
// var size = _imageProcessor.GetImageSize(imageInfo); // var size = _imageProcessor.GetImageSize(imageInfo);
// width = size.Width; // width = Convert.ToInt32(size.Width);
// height = size.Height; // height = Convert.ToInt32(size.Height);
//} //}
// catch //catch
//{ //{
//} //}
@@ -1176,7 +1118,7 @@ namespace Emby.Dlna.Didl
}; };
} }
private class ImageDownloadInfo class ImageDownloadInfo
{ {
internal Guid ItemId; internal Guid ItemId;
internal string ImageTag; internal string ImageTag;
@@ -1192,7 +1134,7 @@ namespace Emby.Dlna.Didl
internal ItemImageInfo ItemImageInfo; internal ItemImageInfo ItemImageInfo;
} }
private class ImageUrlInfo class ImageUrlInfo
{ {
internal string Url; internal string Url;
@@ -1207,11 +1149,11 @@ namespace Emby.Dlna.Didl
public static string GetClientId(Guid idValue, StubType? stubType) public static string GetClientId(Guid idValue, StubType? stubType)
{ {
var id = idValue.ToString("N", CultureInfo.InvariantCulture); var id = idValue.ToString("N");
if (stubType.HasValue) if (stubType.HasValue)
{ {
id = stubType.Value.ToString().ToLowerInvariant() + "_" + id; id = stubType.Value.ToString().ToLower() + "_" + id;
} }
return id; return id;
@@ -1219,16 +1161,15 @@ namespace Emby.Dlna.Didl
private ImageUrlInfo GetImageUrl(ImageDownloadInfo info, int maxWidth, int maxHeight, string format) private ImageUrlInfo GetImageUrl(ImageDownloadInfo info, int maxWidth, int maxHeight, string format)
{ {
var url = string.Format( var url = string.Format("{0}/Items/{1}/Images/{2}/0/{3}/{4}/{5}/{6}/0/0",
CultureInfo.InvariantCulture,
"{0}/Items/{1}/Images/{2}/0/{3}/{4}/{5}/{6}/0/0",
_serverAddress, _serverAddress,
info.ItemId.ToString("N", CultureInfo.InvariantCulture), info.ItemId.ToString("N"),
info.Type, info.Type,
info.ImageTag, info.ImageTag,
format, format,
maxWidth.ToString(CultureInfo.InvariantCulture), maxWidth.ToString(CultureInfo.InvariantCulture),
maxHeight.ToString(CultureInfo.InvariantCulture)); maxHeight.ToString(CultureInfo.InvariantCulture)
);
var width = info.Width; var width = info.Width;
var height = info.Height; var height = info.Height;
@@ -1237,11 +1178,15 @@ namespace Emby.Dlna.Didl
if (width.HasValue && height.HasValue) if (width.HasValue && height.HasValue)
{ {
var newSize = DrawingUtils.Resize( var newSize = DrawingUtils.Resize(new ImageSize
new ImageDimensions(width.Value, height.Value), 0, 0, maxWidth, maxHeight); {
Height = height.Value,
Width = width.Value
width = newSize.Width; }, 0, 0, maxWidth, maxHeight);
height = newSize.Height;
width = Convert.ToInt32(newSize.Width);
height = Convert.ToInt32(newSize.Height);
var normalizedFormat = format var normalizedFormat = format
.Replace("jpeg", "jpg", StringComparison.OrdinalIgnoreCase); .Replace("jpeg", "jpg", StringComparison.OrdinalIgnoreCase);
+4 -4
View File
@@ -1,6 +1,5 @@
#pragma warning disable CS1591
using System; using System;
using MediaBrowser.Model.Extensions;
namespace Emby.Dlna.Didl namespace Emby.Dlna.Didl
{ {
@@ -12,11 +11,12 @@ namespace Emby.Dlna.Didl
public Filter() public Filter()
: this("*") : this("*")
{ {
} }
public Filter(string filter) public Filter(string filter)
{ {
_all = string.Equals(filter, "*", StringComparison.OrdinalIgnoreCase); _all = StringHelper.EqualsIgnoreCase(filter, "*");
_fields = (filter ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); _fields = (filter ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
} }
@@ -25,7 +25,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. // 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 true;
// return _all || ListHelper.ContainsIgnoreCase(_fields, field); //return _all || ListHelper.ContainsIgnoreCase(_fields, field);
} }
} }
} }
+1 -3
View File
@@ -1,5 +1,3 @@
#pragma warning disable CS1591
using System; using System;
using System.IO; using System.IO;
using System.Text; using System.Text;
@@ -53,6 +51,6 @@ namespace Emby.Dlna.Didl
_encoding = encoding; _encoding = encoding;
} }
public override Encoding Encoding => _encoding ?? base.Encoding; public override Encoding Encoding => (null == _encoding) ? base.Encoding : _encoding;
} }
} }
+36 -62
View File
@@ -1,14 +1,9 @@
#pragma warning disable CS1591
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reflection;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Emby.Dlna.Profiles; using Emby.Dlna.Profiles;
using Emby.Dlna.Server; using Emby.Dlna.Server;
using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Configuration;
@@ -19,10 +14,9 @@ using MediaBrowser.Controller.Drawing;
using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Drawing; using MediaBrowser.Model.Drawing;
using MediaBrowser.Model.IO; using MediaBrowser.Model.IO;
using MediaBrowser.Model.Reflection;
using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Serialization;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Primitives;
namespace Emby.Dlna namespace Emby.Dlna
{ {
@@ -31,10 +25,10 @@ namespace Emby.Dlna
private readonly IApplicationPaths _appPaths; private readonly IApplicationPaths _appPaths;
private readonly IXmlSerializer _xmlSerializer; private readonly IXmlSerializer _xmlSerializer;
private readonly IFileSystem _fileSystem; private readonly IFileSystem _fileSystem;
private readonly ILogger<DlnaManager> _logger; private readonly ILogger _logger;
private readonly IJsonSerializer _jsonSerializer; private readonly IJsonSerializer _jsonSerializer;
private readonly IServerApplicationHost _appHost; private readonly IServerApplicationHost _appHost;
private static readonly Assembly _assembly = typeof(DlnaManager).Assembly; private readonly IAssemblyInfo _assemblyInfo;
private readonly Dictionary<string, Tuple<InternalProfileInfo, DeviceProfile>> _profiles = new Dictionary<string, Tuple<InternalProfileInfo, DeviceProfile>>(StringComparer.Ordinal); private readonly Dictionary<string, Tuple<InternalProfileInfo, DeviceProfile>> _profiles = new Dictionary<string, Tuple<InternalProfileInfo, DeviceProfile>>(StringComparer.Ordinal);
@@ -43,22 +37,22 @@ namespace Emby.Dlna
IFileSystem fileSystem, IFileSystem fileSystem,
IApplicationPaths appPaths, IApplicationPaths appPaths,
ILoggerFactory loggerFactory, ILoggerFactory loggerFactory,
IJsonSerializer jsonSerializer, IJsonSerializer jsonSerializer, IServerApplicationHost appHost, IAssemblyInfo assemblyInfo)
IServerApplicationHost appHost)
{ {
_xmlSerializer = xmlSerializer; _xmlSerializer = xmlSerializer;
_fileSystem = fileSystem; _fileSystem = fileSystem;
_appPaths = appPaths; _appPaths = appPaths;
_logger = loggerFactory.CreateLogger<DlnaManager>(); _logger = loggerFactory.CreateLogger("Dlna");
_jsonSerializer = jsonSerializer; _jsonSerializer = jsonSerializer;
_appHost = appHost; _appHost = appHost;
_assemblyInfo = assemblyInfo;
} }
public async Task InitProfilesAsync() public void InitProfiles()
{ {
try try
{ {
await ExtractSystemProfilesAsync(); ExtractSystemProfiles();
LoadProfiles(); LoadProfiles();
} }
catch (Exception ex) catch (Exception ex)
@@ -88,6 +82,7 @@ namespace Emby.Dlna
.Select(i => i.Item2) .Select(i => i.Item2)
.ToList(); .ToList();
} }
} }
public DeviceProfile GetDefaultProfile() public DeviceProfile GetDefaultProfile()
@@ -140,73 +135,55 @@ namespace Emby.Dlna
if (!string.IsNullOrEmpty(profileInfo.DeviceDescription)) if (!string.IsNullOrEmpty(profileInfo.DeviceDescription))
{ {
if (deviceInfo.DeviceDescription == null || !IsRegexMatch(deviceInfo.DeviceDescription, profileInfo.DeviceDescription)) if (deviceInfo.DeviceDescription == null || !IsRegexMatch(deviceInfo.DeviceDescription, profileInfo.DeviceDescription))
{
return false; return false;
}
} }
if (!string.IsNullOrEmpty(profileInfo.FriendlyName)) if (!string.IsNullOrEmpty(profileInfo.FriendlyName))
{ {
if (deviceInfo.FriendlyName == null || !IsRegexMatch(deviceInfo.FriendlyName, profileInfo.FriendlyName)) if (deviceInfo.FriendlyName == null || !IsRegexMatch(deviceInfo.FriendlyName, profileInfo.FriendlyName))
{
return false; return false;
}
} }
if (!string.IsNullOrEmpty(profileInfo.Manufacturer)) if (!string.IsNullOrEmpty(profileInfo.Manufacturer))
{ {
if (deviceInfo.Manufacturer == null || !IsRegexMatch(deviceInfo.Manufacturer, profileInfo.Manufacturer)) if (deviceInfo.Manufacturer == null || !IsRegexMatch(deviceInfo.Manufacturer, profileInfo.Manufacturer))
{
return false; return false;
}
} }
if (!string.IsNullOrEmpty(profileInfo.ManufacturerUrl)) if (!string.IsNullOrEmpty(profileInfo.ManufacturerUrl))
{ {
if (deviceInfo.ManufacturerUrl == null || !IsRegexMatch(deviceInfo.ManufacturerUrl, profileInfo.ManufacturerUrl)) if (deviceInfo.ManufacturerUrl == null || !IsRegexMatch(deviceInfo.ManufacturerUrl, profileInfo.ManufacturerUrl))
{
return false; return false;
}
} }
if (!string.IsNullOrEmpty(profileInfo.ModelDescription)) if (!string.IsNullOrEmpty(profileInfo.ModelDescription))
{ {
if (deviceInfo.ModelDescription == null || !IsRegexMatch(deviceInfo.ModelDescription, profileInfo.ModelDescription)) if (deviceInfo.ModelDescription == null || !IsRegexMatch(deviceInfo.ModelDescription, profileInfo.ModelDescription))
{
return false; return false;
}
} }
if (!string.IsNullOrEmpty(profileInfo.ModelName)) if (!string.IsNullOrEmpty(profileInfo.ModelName))
{ {
if (deviceInfo.ModelName == null || !IsRegexMatch(deviceInfo.ModelName, profileInfo.ModelName)) if (deviceInfo.ModelName == null || !IsRegexMatch(deviceInfo.ModelName, profileInfo.ModelName))
{
return false; return false;
}
} }
if (!string.IsNullOrEmpty(profileInfo.ModelNumber)) if (!string.IsNullOrEmpty(profileInfo.ModelNumber))
{ {
if (deviceInfo.ModelNumber == null || !IsRegexMatch(deviceInfo.ModelNumber, profileInfo.ModelNumber)) if (deviceInfo.ModelNumber == null || !IsRegexMatch(deviceInfo.ModelNumber, profileInfo.ModelNumber))
{
return false; return false;
}
} }
if (!string.IsNullOrEmpty(profileInfo.ModelUrl)) if (!string.IsNullOrEmpty(profileInfo.ModelUrl))
{ {
if (deviceInfo.ModelUrl == null || !IsRegexMatch(deviceInfo.ModelUrl, profileInfo.ModelUrl)) if (deviceInfo.ModelUrl == null || !IsRegexMatch(deviceInfo.ModelUrl, profileInfo.ModelUrl))
{
return false; return false;
}
} }
if (!string.IsNullOrEmpty(profileInfo.SerialNumber)) if (!string.IsNullOrEmpty(profileInfo.SerialNumber))
{ {
if (deviceInfo.SerialNumber == null || !IsRegexMatch(deviceInfo.SerialNumber, profileInfo.SerialNumber)) if (deviceInfo.SerialNumber == null || !IsRegexMatch(deviceInfo.SerialNumber, profileInfo.SerialNumber))
{
return false; return false;
}
} }
return true; return true;
@@ -225,13 +202,16 @@ namespace Emby.Dlna
} }
} }
public DeviceProfile GetProfile(IHeaderDictionary headers) public DeviceProfile GetProfile(IDictionary<string, string> headers)
{ {
if (headers == null) if (headers == null)
{ {
throw new ArgumentNullException(nameof(headers)); throw new ArgumentNullException(nameof(headers));
} }
// Convert to case insensitive
headers = new Dictionary<string, string>(headers, StringComparer.OrdinalIgnoreCase);
var profile = GetProfiles().FirstOrDefault(i => i.Identification != null && IsMatch(headers, i.Identification)); var profile = GetProfiles().FirstOrDefault(i => i.Identification != null && IsMatch(headers, i.Identification));
if (profile != null) if (profile != null)
@@ -247,12 +227,12 @@ namespace Emby.Dlna
return profile; return profile;
} }
private bool IsMatch(IHeaderDictionary headers, DeviceIdentification profileInfo) private bool IsMatch(IDictionary<string, string> headers, DeviceIdentification profileInfo)
{ {
return profileInfo.Headers.Any(i => IsMatch(headers, i)); return profileInfo.Headers.Any(i => IsMatch(headers, i));
} }
private bool IsMatch(IHeaderDictionary headers, HttpHeaderInfo header) private bool IsMatch(IDictionary<string, string> headers, HttpHeaderInfo header)
{ {
// Handle invalid user setup // Handle invalid user setup
if (string.IsNullOrEmpty(header.Name)) if (string.IsNullOrEmpty(header.Name))
@@ -260,15 +240,15 @@ namespace Emby.Dlna
return false; return false;
} }
if (headers.TryGetValue(header.Name, out StringValues value)) if (headers.TryGetValue(header.Name, out string value))
{ {
switch (header.Match) switch (header.Match)
{ {
case HeaderMatchType.Equals: case HeaderMatchType.Equals:
return string.Equals(value, header.Value, StringComparison.OrdinalIgnoreCase); return string.Equals(value, header.Value, StringComparison.OrdinalIgnoreCase);
case HeaderMatchType.Substring: case HeaderMatchType.Substring:
var isMatch = value.ToString().IndexOf(header.Value, StringComparison.OrdinalIgnoreCase) != -1; var isMatch = value.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; return isMatch;
case HeaderMatchType.Regex: case HeaderMatchType.Regex:
return Regex.IsMatch(value, header.Value, RegexOptions.IgnoreCase); return Regex.IsMatch(value, header.Value, RegexOptions.IgnoreCase);
@@ -320,7 +300,7 @@ namespace Emby.Dlna
profile = ReserializeProfile(tempProfile); profile = ReserializeProfile(tempProfile);
profile.Id = path.ToLowerInvariant().GetMD5().ToString("N", CultureInfo.InvariantCulture); profile.Id = path.ToLower().GetMD5().ToString("N");
_profiles[path] = new Tuple<InternalProfileInfo, DeviceProfile>(GetInternalProfileInfo(_fileSystem.GetFileInfo(path), type), profile); _profiles[path] = new Tuple<InternalProfileInfo, DeviceProfile>(GetInternalProfileInfo(_fileSystem.GetFileInfo(path), type), profile);
@@ -372,48 +352,45 @@ namespace Emby.Dlna
Info = new DeviceProfileInfo Info = new DeviceProfileInfo
{ {
Id = file.FullName.ToLowerInvariant().GetMD5().ToString("N", CultureInfo.InvariantCulture), Id = file.FullName.ToLower().GetMD5().ToString("N"),
Name = _fileSystem.GetFileNameWithoutExtension(file), Name = _fileSystem.GetFileNameWithoutExtension(file),
Type = type Type = type
} }
}; };
} }
private async Task ExtractSystemProfilesAsync() private void ExtractSystemProfiles()
{ {
var namespaceName = GetType().Namespace + ".Profiles.Xml."; var namespaceName = GetType().Namespace + ".Profiles.Xml.";
var systemProfilesPath = SystemProfilesPath; var systemProfilesPath = SystemProfilesPath;
foreach (var name in _assembly.GetManifestResourceNames()) foreach (var name in _assemblyInfo.GetManifestResourceNames(GetType())
.Where(i => i.StartsWith(namespaceName))
.ToList())
{ {
if (!name.StartsWith(namespaceName))
{
continue;
}
var filename = Path.GetFileName(name).Substring(namespaceName.Length); var filename = Path.GetFileName(name).Substring(namespaceName.Length);
var path = Path.Combine(systemProfilesPath, filename); var path = Path.Combine(systemProfilesPath, filename);
using (var stream = _assembly.GetManifestResourceStream(name)) using (var stream = _assemblyInfo.GetManifestResourceStream(GetType(), name))
{ {
var fileInfo = _fileSystem.GetFileInfo(path); var fileInfo = _fileSystem.GetFileInfo(path);
if (!fileInfo.Exists || fileInfo.Length != stream.Length) if (!fileInfo.Exists || fileInfo.Length != stream.Length)
{ {
Directory.CreateDirectory(systemProfilesPath); _fileSystem.CreateDirectory(systemProfilesPath);
using (var fileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read)) using (var fileStream = _fileSystem.GetFileStream(path, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read))
{ {
await stream.CopyToAsync(fileStream); stream.CopyTo(fileStream);
} }
} }
} }
} }
// Not necessary, but just to make it easy to find // Not necessary, but just to make it easy to find
Directory.CreateDirectory(UserProfilesPath); _fileSystem.CreateDirectory(UserProfilesPath);
} }
public void DeleteProfile(string id) public void DeleteProfile(string id)
@@ -456,7 +433,6 @@ namespace Emby.Dlna
{ {
throw new ArgumentException("Profile is missing Id"); throw new ArgumentException("Profile is missing Id");
} }
if (string.IsNullOrEmpty(profile.Name)) if (string.IsNullOrEmpty(profile.Name))
{ {
throw new ArgumentException("Profile is missing Name"); throw new ArgumentException("Profile is missing Name");
@@ -482,7 +458,6 @@ namespace Emby.Dlna
{ {
_profiles[path] = new Tuple<InternalProfileInfo, DeviceProfile>(GetInternalProfileInfo(_fileSystem.GetFileInfo(path), type), profile); _profiles[path] = new Tuple<InternalProfileInfo, DeviceProfile>(GetInternalProfileInfo(_fileSystem.GetFileInfo(path), type), profile);
} }
SerializeToXml(profile, path); SerializeToXml(profile, path);
} }
@@ -493,7 +468,7 @@ namespace Emby.Dlna
/// <summary> /// <summary>
/// Recreates the object using serialization, to ensure it's not a subclass. /// 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> /// </summary>
/// <param name="profile"></param> /// <param name="profile"></param>
/// <returns></returns> /// <returns></returns>
@@ -512,11 +487,10 @@ namespace Emby.Dlna
class InternalProfileInfo class InternalProfileInfo
{ {
internal DeviceProfileInfo Info { get; set; } internal DeviceProfileInfo Info { get; set; }
internal string Path { get; set; } internal string Path { get; set; }
} }
public string GetServerDescriptionXml(IHeaderDictionary headers, string serverUuId, string serverAddress) public string GetServerDescriptionXml(IDictionary<string, string> headers, string serverUuId, string serverAddress)
{ {
var profile = GetProfile(headers) ?? var profile = GetProfile(headers) ??
GetDefaultProfile(); GetDefaultProfile();
@@ -532,12 +506,12 @@ namespace Emby.Dlna
? ImageFormat.Png ? ImageFormat.Png
: ImageFormat.Jpg; : ImageFormat.Jpg;
var resource = GetType().Namespace + ".Images." + filename.ToLowerInvariant(); var resource = GetType().Namespace + ".Images." + filename.ToLower();
return new ImageStream return new ImageStream
{ {
Format = format, Format = format,
Stream = _assembly.GetManifestResourceStream(resource) Stream = _assemblyInfo.GetManifestResourceStream(GetType(), resource)
}; };
} }
} }
@@ -586,9 +560,9 @@ namespace Emby.Dlna
new Foobar2000Profile(), new Foobar2000Profile(),
new SharpSmartTvProfile(), new SharpSmartTvProfile(),
new MediaMonkeyProfile(), new MediaMonkeyProfile(),
// new Windows81Profile(), //new Windows81Profile(),
// new WindowsMediaCenterProfile(), //new WindowsMediaCenterProfile(),
// new WindowsPhoneProfile(), //new WindowsPhoneProfile(),
new DirectTvProfile(), new DirectTvProfile(),
new DishHopperJoeyProfile(), new DishHopperJoeyProfile(),
new DefaultProfile(), new DefaultProfile(),
+1 -25
View File
@@ -1,10 +1,5 @@
<Project Sdk="Microsoft.NET.Sdk"> <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> <ItemGroup>
<Compile Include="..\SharedVersion.cs" /> <Compile Include="..\SharedVersion.cs" />
</ItemGroup> </ItemGroup>
@@ -17,22 +12,8 @@
</ItemGroup> </ItemGroup>
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework> <TargetFramework>netstandard2.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<TreatWarningsAsErrors Condition=" '$(Configuration)' == 'Release'" >true</TreatWarningsAsErrors>
</PropertyGroup>
<!-- Code Analyzers-->
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.8" PrivateAssets="All" />
<PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="All" />
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" />
</ItemGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<CodeAnalysisRuleSet>../jellyfin.ruleset</CodeAnalysisRuleSet>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@@ -77,9 +58,4 @@
<EmbeddedResource Include="Profiles\Xml\Xbox One.xml" /> <EmbeddedResource Include="Profiles\Xml\Xbox One.xml" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Http" Version="2.2.2" />
<PackageReference Include="Microsoft.AspNetCore.WebUtilities" Version="2.2.0" />
</ItemGroup>
</Project> </Project>
+5 -8
View File
@@ -1,20 +1,17 @@
#pragma warning disable CS1591
using System.Collections.Generic; using System.Collections.Generic;
namespace Emby.Dlna namespace Emby.Dlna
{ {
public class EventSubscriptionResponse public class EventSubscriptionResponse
{ {
public string Content { get; set; }
public string ContentType { get; set; }
public Dictionary<string, string> Headers { get; set; }
public EventSubscriptionResponse() public EventSubscriptionResponse()
{ {
Headers = new Dictionary<string, string>(); Headers = new Dictionary<string, string>();
} }
public string Content { get; set; }
public string ContentType { get; set; }
public Dictionary<string, string> Headers { get; set; }
} }
} }
+25 -24
View File
@@ -1,11 +1,8 @@
#pragma warning disable CS1591
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
using System.Net.Http;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Extensions;
@@ -31,37 +28,41 @@ namespace Emby.Dlna.Eventing
public EventSubscriptionResponse RenewEventSubscription(string subscriptionId, string notificationType, string requestedTimeoutString, string callbackUrl) public EventSubscriptionResponse RenewEventSubscription(string subscriptionId, string notificationType, string requestedTimeoutString, string callbackUrl)
{ {
var subscription = GetSubscription(subscriptionId, false); var subscription = GetSubscription(subscriptionId, false);
int timeoutSeconds;
// Remove logging for now because some devices are sending this very frequently
// TODO re-enable with dlna debug logging setting
//_logger.LogDebug("Renewing event subscription for {0} with timeout of {1} to {2}",
// subscription.NotificationType,
// timeout,
// subscription.CallbackUrl);
if (subscription != null) if (subscription != null)
{ {
subscription.TimeoutSeconds = ParseTimeout(requestedTimeoutString) ?? 300; subscription.TimeoutSeconds = ParseTimeout(requestedTimeoutString) ?? 300;
int timeoutSeconds = subscription.TimeoutSeconds; timeoutSeconds = subscription.TimeoutSeconds;
subscription.SubscriptionTime = DateTime.UtcNow; subscription.SubscriptionTime = DateTime.UtcNow;
}
_logger.LogDebug( else
"Renewing event subscription for {0} with timeout of {1} to {2}", {
subscription.NotificationType, timeoutSeconds = 300;
timeoutSeconds,
subscription.CallbackUrl);
return GetEventSubscriptionResponse(subscriptionId, requestedTimeoutString, timeoutSeconds);
} }
return new EventSubscriptionResponse return GetEventSubscriptionResponse(subscriptionId, requestedTimeoutString, timeoutSeconds);
{
Content = string.Empty,
ContentType = "text/plain"
};
} }
public EventSubscriptionResponse CreateEventSubscription(string notificationType, string requestedTimeoutString, string callbackUrl) public EventSubscriptionResponse CreateEventSubscription(string notificationType, string requestedTimeoutString, string callbackUrl)
{ {
var timeout = ParseTimeout(requestedTimeoutString) ?? 300; var timeout = ParseTimeout(requestedTimeoutString) ?? 300;
var id = "uuid:" + Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture); var id = "uuid:" + Guid.NewGuid().ToString("N");
_logger.LogDebug("Creating event subscription for {0} with timeout of {1} to {2}", // Remove logging for now because some devices are sending this very frequently
notificationType, // TODO re-enable with dlna debug logging setting
timeout, //_logger.LogDebug("Creating event subscription for {0} with timeout of {1} to {2}",
callbackUrl); // notificationType,
// timeout,
// callbackUrl);
_subscriptions.TryAdd(id, new EventSubscription _subscriptions.TryAdd(id, new EventSubscription
{ {
@@ -158,7 +159,6 @@ namespace Emby.Dlna.Eventing
builder.Append("</" + key + ">"); builder.Append("</" + key + ">");
builder.Append("</e:property>"); builder.Append("</e:property>");
} }
builder.Append("</e:propertyset>"); builder.Append("</e:propertyset>");
var options = new HttpRequestOptions var options = new HttpRequestOptions
@@ -176,8 +176,9 @@ namespace Emby.Dlna.Eventing
try try
{ {
using (await _httpClient.SendAsync(options, new HttpMethod("NOTIFY")).ConfigureAwait(false)) using (await _httpClient.SendAsync(options, "NOTIFY").ConfigureAwait(false))
{ {
} }
} }
catch (OperationCanceledException) catch (OperationCanceledException)
+2 -7
View File
@@ -1,5 +1,3 @@
#pragma warning disable CS1591
using System; using System;
namespace Emby.Dlna.Eventing namespace Emby.Dlna.Eventing
@@ -7,19 +5,14 @@ namespace Emby.Dlna.Eventing
public class EventSubscription public class EventSubscription
{ {
public string Id { get; set; } public string Id { get; set; }
public string CallbackUrl { get; set; } public string CallbackUrl { get; set; }
public string NotificationType { get; set; } public string NotificationType { get; set; }
public DateTime SubscriptionTime { get; set; } public DateTime SubscriptionTime { get; set; }
public int TimeoutSeconds { get; set; } public int TimeoutSeconds { get; set; }
public long TriggerCount { get; set; } public long TriggerCount { get; set; }
public bool IsExpired => SubscriptionTime.AddSeconds(TimeoutSeconds) >= DateTime.UtcNow;
public void IncrementTriggerCount() public void IncrementTriggerCount()
{ {
if (TriggerCount == long.MaxValue) if (TriggerCount == long.MaxValue)
@@ -29,5 +22,7 @@ namespace Emby.Dlna.Eventing
TriggerCount++; TriggerCount++;
} }
public bool IsExpired => SubscriptionTime.AddSeconds(TimeoutSeconds) >= DateTime.UtcNow;
} }
} }
-1
View File
@@ -1,4 +1,3 @@
#pragma warning disable CS1591
namespace Emby.Dlna namespace Emby.Dlna
{ {
-1
View File
@@ -1,4 +1,3 @@
#pragma warning disable CS1591
namespace Emby.Dlna namespace Emby.Dlna
{ {
-1
View File
@@ -1,4 +1,3 @@
#pragma warning disable CS1591
namespace Emby.Dlna namespace Emby.Dlna
{ {
-1
View File
@@ -1,4 +1,3 @@
#pragma warning disable CS1591
namespace Emby.Dlna namespace Emby.Dlna
{ {
+4 -5
View File
@@ -1,6 +1,4 @@
#pragma warning disable CS1591 using System.Collections.Generic;
using System.Threading.Tasks;
namespace Emby.Dlna namespace Emby.Dlna
{ {
@@ -9,14 +7,15 @@ namespace Emby.Dlna
/// <summary> /// <summary>
/// Gets the content directory XML. /// Gets the content directory XML.
/// </summary> /// </summary>
/// <param name="headers">The headers.</param>
/// <returns>System.String.</returns> /// <returns>System.String.</returns>
string GetServiceXml(); string GetServiceXml(IDictionary<string, string> headers);
/// <summary> /// <summary>
/// Processes the control request. /// Processes the control request.
/// </summary> /// </summary>
/// <param name="request">The request.</param> /// <param name="request">The request.</param>
/// <returns>ControlResponse.</returns> /// <returns>ControlResponse.</returns>
Task<ControlResponse> ProcessControlRequestAsync(ControlRequest request); ControlResponse ProcessControlRequest(ControlRequest request);
} }
} }
Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Some files were not shown because too many files have changed in this diff Show More