Merge branch 'master' into namingtests

pull/2105/head
Bond_009 4 years ago
commit b50c4938e1

@ -19,9 +19,9 @@ jobs:
vmImage: ubuntu-latest
strategy:
matrix:
release:
Release:
BuildConfiguration: Release
debug:
Debug:
BuildConfiguration: Debug
maxParallel: 2
steps:
@ -31,32 +31,32 @@ jobs:
persistCredentials: true
- task: CmdLine@2
displayName: "Check out web"
displayName: "Clone Web Client (Master, Release, or Tag)"
condition: and(succeeded(), or(contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')) ,eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI', 'BuildCompletion'))
inputs:
script: 'git clone --single-branch --branch $(Build.SourceBranchName) --depth=1 https://github.com/jellyfin/jellyfin-web.git $(Agent.TempDirectory)/jellyfin-web'
- task: CmdLine@2
displayName: "Check out web (PR)"
displayName: "Clone Web Client (PR)"
condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master')) ,eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest'))
inputs:
script: 'git clone --single-branch --branch $(System.PullRequest.TargetBranch) --depth 1 https://github.com/jellyfin/jellyfin-web.git $(Agent.TempDirectory)/jellyfin-web'
- task: NodeTool@0
displayName: 'Install Node.js'
displayName: 'Install Node'
condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master'), contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')) ,eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI', 'BuildCompletion'))
inputs:
versionSpec: '10.x'
- task: CmdLine@2
displayName: "Build Web UI"
displayName: "Build Web Client"
condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master'), contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')) ,eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI', 'BuildCompletion'))
inputs:
script: yarn install
workingDirectory: $(Agent.TempDirectory)/jellyfin-web
- task: CopyFiles@2
displayName: Copy the web UI
displayName: 'Copy Web Client'
condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master'), contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')) ,eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI', 'BuildCompletion'))
inputs:
sourceFolder: $(Agent.TempDirectory)/jellyfin-web/dist # Optional
@ -66,8 +66,14 @@ jobs:
overWrite: true # Optional
flattenFolders: false # Optional
- task: UseDotNet@2
displayName: 'Update DotNet'
inputs:
packageType: sdk
version: 3.1.100
- task: DotNetCoreCLI@2
displayName: Publish
displayName: 'Publish Server'
inputs:
command: publish
publishWebProjects: false
@ -135,62 +141,20 @@ jobs:
!**\obj\**
!**\xunit.runner.visualstudio.testadapter.dll
!**\xunit.runner.visualstudio.dotnetcore.testadapter.dll
#testPlan: # Required when testSelector == TestPlan
#testSuite: # Required when testSelector == TestPlan
#testConfiguration: # Required when testSelector == TestPlan
#tcmTestRun: '$(test.RunId)' # Optional
searchFolder: '$(System.DefaultWorkingDirectory)'
#testFiltercriteria: # Optional
#runOnlyImpactedTests: False # Optional
#runAllTestsAfterXBuilds: '50' # Optional
#uiTests: false # Optional
#vstestLocationMethod: 'version' # Optional. Options: version, location
#vsTestVersion: 'latest' # Optional. Options: latest, 16.0, 15.0, 14.0, toolsInstaller
#vstestLocation: # Optional
#runSettingsFile: # Optional
#overrideTestrunParameters: # Optional
#pathtoCustomTestAdapters: # Optional
runInParallel: True # Optional
runTestsInIsolation: True # Optional
codeCoverageEnabled: True # Optional
#otherConsoleOptions: # Optional
#distributionBatchType: 'basedOnTestCases' # Optional. Options: basedOnTestCases, basedOnExecutionTime, basedOnAssembly
#batchingBasedOnAgentsOption: 'autoBatchSize' # Optional. Options: autoBatchSize, customBatchSize
#customBatchSizeValue: '10' # Required when distributionBatchType == BasedOnTestCases && BatchingBasedOnAgentsOption == CustomBatchSize
#batchingBasedOnExecutionTimeOption: 'autoBatchSize' # Optional. Options: autoBatchSize, customTimeBatchSize
#customRunTimePerBatchValue: '60' # Required when distributionBatchType == BasedOnExecutionTime && BatchingBasedOnExecutionTimeOption == CustomTimeBatchSize
#dontDistribute: False # Optional
#testRunTitle: # Optional
#platform: # Optional
configuration: 'Debug' # Optional
publishRunAttachments: true # Optional
#diagnosticsEnabled: false # Optional
#collectDumpOn: 'onAbortOnly' # Optional. Options: onAbortOnly, always, never
#rerunFailedTests: False # Optional
#rerunType: 'basedOnTestFailurePercentage' # Optional. Options: basedOnTestFailurePercentage, basedOnTestFailureCount
#rerunFailedThreshold: '30' # Optional
#rerunFailedTestCasesMaxLimit: '5' # Optional
#rerunMaxAttempts: '3' # Optional
# - task: PublishTestResults@2
# inputs:
# testResultsFormat: 'VSTest' # Options: JUnit, NUnit, VSTest, xUnit, cTest
# testResultsFiles: '**/*.trx'
# #searchFolder: '$(System.DefaultWorkingDirectory)' # Optional
# mergeTestResults: true # Optional
# #failTaskOnFailedTests: false # Optional
# #testRunTitle: # Optional
# #buildPlatform: # Optional
# #buildConfiguration: # Optional
# #publishRunAttachments: true # Optional
- job: main_build_win
displayName: Main Build Windows
displayName: Publish Windows
pool:
vmImage: windows-latest
strategy:
matrix:
release:
Release:
BuildConfiguration: Release
maxParallel: 2
steps:
@ -200,32 +164,32 @@ jobs:
persistCredentials: true
- task: CmdLine@2
displayName: "Check out web (master, release or tag)"
displayName: "Clone Web Client (Master, Release, or Tag)"
condition: and(succeeded(), or(contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master'), contains(variables['Build.SourceBranch'], 'tag')) ,eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI', 'BuildCompletion'))
inputs:
script: 'git clone --single-branch --branch $(Build.SourceBranchName) --depth=1 https://github.com/jellyfin/jellyfin-web.git $(Agent.TempDirectory)/jellyfin-web'
- task: CmdLine@2
displayName: "Check out web (PR)"
displayName: "Clone Web Client (PR)"
condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master')) ,eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest'))
inputs:
script: 'git clone --single-branch --branch $(System.PullRequest.TargetBranch) --depth 1 https://github.com/jellyfin/jellyfin-web.git $(Agent.TempDirectory)/jellyfin-web'
- task: NodeTool@0
displayName: 'Install Node.js'
displayName: 'Install Node'
condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master'), contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')) ,eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI', 'BuildCompletion'))
inputs:
versionSpec: '10.x'
- task: CmdLine@2
displayName: "Build Web UI"
displayName: "Build Web Client"
condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master'), contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')) ,eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI', 'BuildCompletion'))
inputs:
script: yarn install
workingDirectory: $(Agent.TempDirectory)/jellyfin-web
- task: CopyFiles@2
displayName: Copy the web UI
displayName: 'Copy Web Client'
condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master'), contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')) ,eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI', 'BuildCompletion'))
inputs:
sourceFolder: $(Agent.TempDirectory)/jellyfin-web/dist # Optional
@ -236,25 +200,21 @@ jobs:
flattenFolders: false # Optional
- task: CmdLine@2
displayName: Clone the UX repository
displayName: 'Clone UX Repository'
inputs:
script: git clone --depth=1 https://github.com/jellyfin/jellyfin-ux $(Agent.TempDirectory)\jellyfin-ux
- task: PowerShell@2
displayName: Build the NSIS Installer
displayName: 'Build NSIS Installer'
inputs:
targetType: 'filePath' # Optional. Options: filePath, inline
filePath: ./deployment/windows/build-jellyfin.ps1 # Required when targetType == FilePath
arguments: -InstallFFMPEG -InstallNSSM -MakeNSIS -InstallTrayApp -UXLocation $(Agent.TempDirectory)\jellyfin-ux -InstallLocation $(build.artifactstagingdirectory)
#script: '# Write your PowerShell commands here.Write-Host Hello World' # Required when targetType == Inline
errorActionPreference: 'stop' # Optional. Options: stop, continue, silentlyContinue
#failOnStderr: false # Optional
#ignoreLASTEXITCODE: false # Optional
#pwsh: false # Optional
workingDirectory: $(Build.SourcesDirectory) # Optional
- task: CopyFiles@2
displayName: Copy the NSIS Installer to the artifact directory
displayName: 'Copy NSIS Installer'
inputs:
sourceFolder: $(Build.SourcesDirectory)/deployment/windows/ # Optional
contents: 'jellyfin*.exe'
@ -264,7 +224,7 @@ jobs:
flattenFolders: true # Optional
- task: PublishPipelineArtifact@0
displayName: 'Publish Setup Artifact'
displayName: 'Publish Artifact Setup'
condition: and(eq(variables['BuildConfiguration'], 'Release'), succeeded())
inputs:
targetPath: '$(build.artifactstagingdirectory)/setup'
@ -275,7 +235,8 @@ jobs:
pool:
vmImage: ubuntu-latest
dependsOn: main_build
condition: and(succeeded(), variables['System.PullRequest.PullRequestNumber']) # Only execute if the pullrequest numer is defined. (So not for normal CI builds)
# only execute for pull requests
condition: and(succeeded(), variables['System.PullRequest.PullRequestNumber'])
strategy:
matrix:
Naming:
@ -293,24 +254,23 @@ jobs:
maxParallel: 2
steps:
- checkout: none
- task: UseDotNet@2
displayName: 'Update DotNet'
inputs:
packageType: sdk
version: 3.1.100
- task: DownloadPipelineArtifact@2
displayName: Download the New Assembly Build Artifact
displayName: 'Download New Assembly Build Artifact'
inputs:
source: 'current' # Options: current, specific
#preferTriggeringPipeline: false # Optional
#tags: # Optional
artifact: '$(NugetPackageName)' # Optional
#patterns: '**' # Optional
path: '$(System.ArtifactsDirectory)/new-artifacts'
#project: # Required when source == Specific
#pipeline: # Required when source == Specific
runVersion: 'latest' # Required when source == Specific. Options: latest, latestFromBranch, specific
#runBranch: 'refs/heads/master' # Required when source == Specific && runVersion == LatestFromBranch
#runId: # Required when source == Specific && runVersion == Specific
- task: CopyFiles@2
displayName: Copy New Assembly to new-release folder
displayName: 'Copy New Assembly Build Artifact'
inputs:
sourceFolder: $(System.ArtifactsDirectory)/new-artifacts # Optional
contents: '**/*.dll'
@ -320,22 +280,18 @@ jobs:
flattenFolders: true # Optional
- task: DownloadPipelineArtifact@2
displayName: Download the Reference Assembly Build Artifact
displayName: 'Download Reference Assembly Build Artifact'
inputs:
source: 'specific' # Options: current, specific
#preferTriggeringPipeline: false # Optional
#tags: # Optional
artifact: '$(NugetPackageName)' # Optional
#patterns: '**' # Optional
path: '$(System.ArtifactsDirectory)/current-artifacts'
project: '$(System.TeamProjectId)' # Required when source == Specific
pipeline: '$(System.DefinitionId)' # Required when source == Specific
runVersion: 'latestFromBranch' # Required when source == Specific. Options: latest, latestFromBranch, specific
runBranch: 'refs/heads/$(System.PullRequest.TargetBranch)' # Required when source == Specific && runVersion == LatestFromBranch
#runId: # Required when source == Specific && runVersion == Specific
- task: CopyFiles@2
displayName: Copy Reference Assembly to current-release folder
displayName: 'Copy Reference Assembly Build Artifact'
inputs:
sourceFolder: $(System.ArtifactsDirectory)/current-artifacts # Optional
contents: '**/*.dll'
@ -345,27 +301,24 @@ jobs:
flattenFolders: true # Optional
- task: DownloadGitHubRelease@0
displayName: Download ABI compatibility check tool from GitHub
displayName: 'Download ABI Compatibility Check Tool'
inputs:
connection: Jellyfin Release Download
userRepository: EraYaN/dotnet-compatibility
defaultVersionType: 'latest' # Options: latest, specificVersion, specificTag
#version: # Required when defaultVersionType != Latest
itemPattern: '**-ci.zip' # Optional
downloadPath: '$(System.ArtifactsDirectory)'
- task: ExtractFiles@1
displayName: Extract ABI compatibility check tool
displayName: 'Extract ABI Compatibility Check Tool'
inputs:
archiveFilePatterns: '$(System.ArtifactsDirectory)/*-ci.zip'
destinationFolder: $(System.ArtifactsDirectory)/tools
cleanDestinationFolder: true
# The `--warnings-only` switch will swallow the return code and not emit any errors.
- task: CmdLine@2
displayName: Execute ABI compatibility check tool
displayName: 'Execute ABI Compatibility Check Tool'
inputs:
script: 'dotnet tools/CompatibilityCheckerCoreCLI.dll current-release/$(AssemblyFileName) new-release/$(AssemblyFileName) --azure-pipelines'
script: 'dotnet tools/CompatibilityCheckerCLI.dll current-release/$(AssemblyFileName) new-release/$(AssemblyFileName) --azure-pipelines --warnings-only'
workingDirectory: $(System.ArtifactsDirectory) # Optional
#failOnStderr: false # Optional

@ -10,7 +10,7 @@
"request": "launch",
"preLaunchTask": "build",
// If you have changed target frameworks, make sure to update the program path.
"program": "${workspaceFolder}/Jellyfin.Server/bin/Debug/netcoreapp3.0/jellyfin.dll",
"program": "${workspaceFolder}/Jellyfin.Server/bin/Debug/netcoreapp3.1/jellyfin.dll",
"args": [],
"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

@ -1,17 +0,0 @@
<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>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
</Project>

@ -1,33 +0,0 @@
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;
}
}

@ -1,449 +0,0 @@
//============================================================================
// 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(Path.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 (File.Exists(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));
}
}
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));
}
}
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()
{
foreach (var streamClipFile in StreamClipFiles.Values)
{
try
{
streamClipFile.Scan();
}
catch (Exception ex)
{
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);
foreach (var playlistFile in PlaylistFiles.Values)
{
try
{
playlistFile.Scan(StreamFiles, StreamClipFiles);
}
catch (Exception ex)
{
if (PlaylistFileScanError != null)
{
if (PlaylistFileScanError(playlistFile, ex))
{
continue;
}
else
{
break;
}
}
else throw;
}
}
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)
{
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 = Path.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;
}
}
}
}
}

@ -1,493 +0,0 @@
//============================================================================
// 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;
}
}
}
}

@ -1,21 +0,0 @@
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 Server")]
[assembly: AssemblyCopyright("Copyright © 2016 CinemaSquid. Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License")]
[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)]

@ -1,5 +0,0 @@
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.

@ -1,309 +0,0 @@
//============================================================================
// 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;
}
}
}

@ -1,148 +0,0 @@
//============================================================================
// 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;
}
}
}

@ -1,159 +0,0 @@
//============================================================================
// 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;
}
}
}
}

@ -1,246 +0,0 @@
//============================================================================
// 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);
}
}
}
}

@ -1,123 +0,0 @@
//============================================================================
// 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;
}
}
}

@ -1,208 +0,0 @@
//============================================================================
// 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
}
}
}
}
}

@ -1,36 +0,0 @@
//============================================================================
// 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;
}
}
}

@ -1,186 +0,0 @@
//============================================================================
// 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;
}
}
}

@ -1,131 +0,0 @@
//============================================================================
// 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;
}
}
}
}
}

@ -1,37 +0,0 @@
//============================================================================
// 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

@ -1,780 +0,0 @@
//============================================================================
// 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;
}
}
}

@ -1,130 +0,0 @@
//============================================================================
// 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;
}
}
}

@ -1,107 +0,0 @@
//============================================================================
// 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;
}
}
}

@ -1,244 +0,0 @@
//============================================================================
// 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
{
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)
{
FileInfo = fileInfo;
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 = File.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

@ -31,6 +31,7 @@
- [fhriley](https://github.com/fhriley)
- [nevado](https://github.com/nevado)
- [mark-monteiro](https://github.com/mark-monteiro)
- [ullmie02](https://github.com/ullmie02)
# Emby Contributors

@ -1,4 +1,4 @@
ARG DOTNET_VERSION=3.0
ARG DOTNET_VERSION=3.1
ARG FFMPEG_VERSION=latest
FROM node:alpine as web-builder

@ -1,6 +1,6 @@
# Requires binfm_misc registration
# https://github.com/multiarch/qemu-user-static#binfmt_misc-register
ARG DOTNET_VERSION=3.0
ARG DOTNET_VERSION=3.1
FROM node:alpine as web-builder

@ -1,6 +1,6 @@
# Requires binfm_misc registration
# https://github.com/multiarch/qemu-user-static#binfmt_misc-register
ARG DOTNET_VERSION=3.0
ARG DOTNET_VERSION=3.1
FROM node:alpine as web-builder

@ -29,25 +29,15 @@ namespace Emby.Dlna.Eventing
{
var subscription = GetSubscription(subscriptionId, false);
int timeoutSeconds;
subscription.TimeoutSeconds = ParseTimeout(requestedTimeoutString) ?? 300;
int timeoutSeconds = subscription.TimeoutSeconds;
subscription.SubscriptionTime = DateTime.UtcNow;
// 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)
{
subscription.TimeoutSeconds = ParseTimeout(requestedTimeoutString) ?? 300;
timeoutSeconds = subscription.TimeoutSeconds;
subscription.SubscriptionTime = DateTime.UtcNow;
}
else
{
timeoutSeconds = 300;
}
_logger.LogDebug(
"Renewing event subscription for {0} with timeout of {1} to {2}",
subscription.NotificationType,
timeoutSeconds,
subscription.CallbackUrl);
return GetEventSubscriptionResponse(subscriptionId, requestedTimeoutString, timeoutSeconds);
}
@ -57,12 +47,10 @@ namespace Emby.Dlna.Eventing
var timeout = ParseTimeout(requestedTimeoutString) ?? 300;
var id = "uuid:" + Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture);
// Remove logging for now because some devices are sending this very frequently
// TODO re-enable with dlna debug logging setting
//_logger.LogDebug("Creating event subscription for {0} with timeout of {1} to {2}",
// notificationType,
// timeout,
// callbackUrl);
_logger.LogDebug("Creating event subscription for {0} with timeout of {1} to {2}",
notificationType,
timeout,
callbackUrl);
_subscriptions.TryAdd(id, new EventSubscription
{

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System;
using System.Globalization;
using System.IO;

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

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
namespace Emby.Naming.Audio
{
public class MultiPartResult

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System;
using System.Globalization;
using System.IO;

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
namespace Emby.Naming.AudioBook
{
public class AudioBookFilePathParserResult

@ -7,6 +7,9 @@ namespace Emby.Naming.AudioBook
/// </summary>
public class AudioBookInfo
{
/// <summary>
/// Initializes a new instance of the <see cref="AudioBookInfo" /> class.
/// </summary>
public AudioBookInfo()
{
Files = new List<AudioBookFileInfo>();

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System.Collections.Generic;
using System.Linq;
using Emby.Naming.Common;

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

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System;
using System.Text.RegularExpressions;

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
namespace Emby.Naming.Common
{
public enum MediaType

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System;
using System.Linq;
using System.Text.RegularExpressions;

@ -6,6 +6,10 @@
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<Compile Include="..\SharedVersion.cs" />
</ItemGroup>
@ -21,7 +25,7 @@
<RepositoryUrl>https://github.com/jellyfin/jellyfin</RepositoryUrl>
</PropertyGroup>
<!-- Code analysers-->
<!-- Code Analyzers-->
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.7" PrivateAssets="All" />
<PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" />

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
namespace Emby.Naming.Subtitles
{
public class SubtitleInfo

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

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
namespace Emby.Naming.TV
{
public class EpisodeInfo

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System;
using System.Collections.Generic;
using System.Globalization;

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
namespace Emby.Naming.TV
{
public class EpisodePathParserResult

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

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System;
using System.Globalization;
using System.IO;

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
namespace Emby.Naming.TV
{
public class SeasonPathParserResult

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System;
using System.Globalization;
using System.IO;

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
namespace Emby.Naming.Video
{
public class CleanDateTimeResult
@ -7,11 +10,13 @@ namespace Emby.Naming.Video
/// </summary>
/// <value>The name.</value>
public string Name { get; set; }
/// <summary>
/// Gets or sets the year.
/// </summary>
/// <value>The year.</value>
public int? Year { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance has changed.
/// </summary>

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System.Collections.Generic;
using System.Text.RegularExpressions;

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
namespace Emby.Naming.Video
{
public class CleanStringResult
@ -7,6 +10,7 @@ namespace Emby.Naming.Video
/// </summary>
/// <value>The name.</value>
public string Name { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance has changed.
/// </summary>

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

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using MediaBrowser.Model.Entities;
namespace Emby.Naming.Video

@ -1,4 +1,8 @@
using Emby.Naming.Common;
#pragma warning disable CS1591
#pragma warning disable SA1600
using MediaBrowser.Model.Entities;
using MediaType = Emby.Naming.Common.MediaType;
namespace Emby.Naming.Video
{
@ -14,7 +18,7 @@ namespace Emby.Naming.Video
/// Gets or sets the type of the extra.
/// </summary>
/// <value>The type of the extra.</value>
public MediaBrowser.Model.Entities.ExtraType ExtraType { get; set; }
public ExtraType ExtraType { get; set; }
/// <summary>
/// Gets or sets the type of the rule.

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
namespace Emby.Naming.Video
{
public enum ExtraRuleType
@ -6,10 +9,12 @@ namespace Emby.Naming.Video
/// The suffix
/// </summary>
Suffix = 0,
/// <summary>
/// The filename
/// </summary>
Filename = 1,
/// <summary>
/// The regex
/// </summary>

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System;
using System.Collections.Generic;
using System.Linq;
@ -6,15 +9,17 @@ namespace Emby.Naming.Video
{
public class FileStack
{
public string Name { get; set; }
public List<string> Files { get; set; }
public bool IsDirectoryStack { get; set; }
public FileStack()
{
Files = new List<string>();
}
public string Name { get; set; }
public List<string> Files { get; set; }
public bool IsDirectoryStack { get; set; }
public bool ContainsFile(string file, bool isDirectory)
{
if (IsDirectoryStack == isDirectory)

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System;
using System.IO;
using Emby.Naming.Common;

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System;
using System.Linq;
using Emby.Naming.Common;

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System.Collections.Generic;
namespace Emby.Naming.Video

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
namespace Emby.Naming.Video
{
public class Format3DRule
@ -7,6 +10,7 @@ namespace Emby.Naming.Video
/// </summary>
/// <value>The token.</value>
public string Token { get; set; }
/// <summary>
/// Gets or sets the preceeding token.
/// </summary>

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System;
using System.Collections.Generic;
using System.IO;

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System.Collections.Generic;
namespace Emby.Naming.Video

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

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
namespace Emby.Naming.Video
{
public struct StubResult

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
namespace Emby.Naming.Video
{
public class StubTypeRule

@ -79,6 +79,7 @@ namespace Emby.Naming.Video
/// <value>The file name without extension.</value>
public string FileNameWithoutExtension => !IsDirectory ? System.IO.Path.GetFileNameWithoutExtension(Path) : System.IO.Path.GetFileName(Path);
/// <inheritdoc />
public override string ToString()
{
// Makes debugging easier

@ -7,6 +7,16 @@ namespace Emby.Naming.Video
/// </summary>
public class VideoInfo
{
/// <summary>
/// Initializes a new instance of the <see cref="VideoInfo" /> class.
/// </summary>
public VideoInfo()
{
Files = new List<VideoFileInfo>();
Extras = new List<VideoFileInfo>();
AlternateVersions = new List<VideoFileInfo>();
}
/// <summary>
/// Gets or sets the name.
/// </summary>
@ -36,12 +46,5 @@ namespace Emby.Naming.Video
/// </summary>
/// <value>The alternate versions.</value>
public List<VideoFileInfo> AlternateVersions { get; set; }
public VideoInfo()
{
Files = new List<VideoFileInfo>();
Extras = new List<VideoFileInfo>();
AlternateVersions = new List<VideoFileInfo>();
}
}
}

@ -1,3 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System;
using System.Collections.Generic;
using System.IO;

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

@ -1,5 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" />
<ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" />
@ -20,7 +24,7 @@
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<!-- Code analysers-->
<!-- Code Analyzers-->
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.7" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" />

@ -103,14 +103,11 @@ using MediaBrowser.Providers.Subtitles;
using MediaBrowser.Providers.TV.TheTVDB;
using MediaBrowser.WebDashboard.Api;
using MediaBrowser.XbmcMetadata.Providers;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.OpenApi.Models;
using OperatingSystem = MediaBrowser.Common.System.OperatingSystem;
namespace Emby.Server.Implementations
@ -764,9 +761,8 @@ namespace Emby.Server.Implementations
LibraryManager = new LibraryManager(this, LoggerFactory, TaskManager, UserManager, ServerConfigurationManager, UserDataManager, () => LibraryMonitor, FileSystemManager, () => ProviderManager, () => UserViewManager);
serviceCollection.AddSingleton(LibraryManager);
// TODO wtaylor: investigate use of second music manager
var musicManager = new MusicManager(LibraryManager);
serviceCollection.AddSingleton<IMusicManager>(new MusicManager(LibraryManager));
serviceCollection.AddSingleton<IMusicManager>(musicManager);
LibraryMonitor = new LibraryMonitor(LoggerFactory, LibraryManager, ServerConfigurationManager, FileSystemManager);
serviceCollection.AddSingleton(LibraryMonitor);
@ -841,16 +837,14 @@ namespace Emby.Server.Implementations
serviceCollection.AddSingleton(ChapterManager);
MediaEncoder = new MediaBrowser.MediaEncoding.Encoder.MediaEncoder(
LoggerFactory,
JsonSerializer,
StartupOptions.FFmpegPath,
LoggerFactory.CreateLogger<MediaBrowser.MediaEncoding.Encoder.MediaEncoder>(),
ServerConfigurationManager,
FileSystemManager,
() => SubtitleEncoder,
() => MediaSourceManager,
ProcessFactory,
5000,
LocalizationManager);
LocalizationManager,
() => SubtitleEncoder,
_configuration,
StartupOptions.FFmpegPath);
serviceCollection.AddSingleton(MediaEncoder);
EncodingManager = new MediaEncoder.EncodingManager(FileSystemManager, LoggerFactory, MediaEncoder, ChapterManager, LibraryManager);
@ -867,10 +861,21 @@ namespace Emby.Server.Implementations
AuthService = new AuthService(LoggerFactory.CreateLogger<AuthService>(), authContext, ServerConfigurationManager, SessionManager, NetworkManager);
serviceCollection.AddSingleton(AuthService);
SubtitleEncoder = new MediaBrowser.MediaEncoding.Subtitles.SubtitleEncoder(LibraryManager, LoggerFactory, ApplicationPaths, FileSystemManager, MediaEncoder, JsonSerializer, HttpClient, MediaSourceManager, ProcessFactory);
SubtitleEncoder = new MediaBrowser.MediaEncoding.Subtitles.SubtitleEncoder(
LibraryManager,
LoggerFactory.CreateLogger<MediaBrowser.MediaEncoding.Subtitles.SubtitleEncoder>(),
ApplicationPaths,
FileSystemManager,
MediaEncoder,
HttpClient,
MediaSourceManager,
ProcessFactory);
serviceCollection.AddSingleton(SubtitleEncoder);
serviceCollection.AddSingleton(typeof(IResourceFileManager), typeof(ResourceFileManager));
serviceCollection.AddSingleton<EncodingHelper>();
serviceCollection.AddSingleton(typeof(IAttachmentExtractor), typeof(MediaBrowser.MediaEncoding.Attachments.AttachmentExtractor));
_displayPreferencesRepository.Initialize();
@ -1472,7 +1477,7 @@ namespace Emby.Server.Implementations
/// </summary>
/// <param name="address">The IPv6 address.</param>
/// <returns>The IPv6 address without the scope id.</returns>
private string RemoveScopeId(string address)
private ReadOnlySpan<char> RemoveScopeId(ReadOnlySpan<char> address)
{
var index = address.IndexOf('%');
if (index == -1)
@ -1480,33 +1485,50 @@ namespace Emby.Server.Implementations
return address;
}
return address.Substring(0, index);
return address.Slice(0, index);
}
/// <inheritdoc />
public string GetLocalApiUrl(IPAddress ipAddress)
{
if (ipAddress.AddressFamily == AddressFamily.InterNetworkV6)
{
var str = RemoveScopeId(ipAddress.ToString());
Span<char> span = new char[str.Length + 2];
span[0] = '[';
str.CopyTo(span.Slice(1));
span[^1] = ']';
return GetLocalApiUrl("[" + str + "]");
return GetLocalApiUrl(span);
}
return GetLocalApiUrl(ipAddress.ToString());
}
public string GetLocalApiUrl(string host)
/// <inheritdoc />
public string GetLocalApiUrl(ReadOnlySpan<char> host)
{
var url = new StringBuilder(64);
if (EnableHttps)
{
return string.Format("https://{0}:{1}",
host,
HttpsPort.ToString(CultureInfo.InvariantCulture));
url.Append("https://");
}
else
{
url.Append("http://");
}
url.Append(host)
.Append(':')
.Append(HttpPort);
string baseUrl = ServerConfigurationManager.Configuration.BaseUrl;
if (baseUrl.Length != 0)
{
url.Append('/').Append(baseUrl);
}
return string.Format("http://{0}:{1}",
host,
HttpPort.ToString(CultureInfo.InvariantCulture));
return url.ToString();
}
public Task<List<IPAddress>> GetLocalIpAddresses(CancellationToken cancellationToken)

@ -31,11 +31,7 @@ namespace Emby.Server.Implementations.Collections
private readonly ILogger _logger;
private readonly IProviderManager _providerManager;
private readonly ILocalizationManager _localizationManager;
private IApplicationPaths _appPaths;
public event EventHandler<CollectionCreatedEventArgs> CollectionCreated;
public event EventHandler<CollectionModifiedEventArgs> ItemsAddedToCollection;
public event EventHandler<CollectionModifiedEventArgs> ItemsRemovedFromCollection;
private readonly IApplicationPaths _appPaths;
public CollectionManager(
ILibraryManager libraryManager,
@ -55,6 +51,10 @@ namespace Emby.Server.Implementations.Collections
_appPaths = appPaths;
}
public event EventHandler<CollectionCreatedEventArgs> CollectionCreated;
public event EventHandler<CollectionModifiedEventArgs> ItemsAddedToCollection;
public event EventHandler<CollectionModifiedEventArgs> ItemsRemovedFromCollection;
private IEnumerable<Folder> FindFolders(string path)
{
return _libraryManager
@ -341,11 +341,11 @@ namespace Emby.Server.Implementations.Collections
}
}
public class CollectionManagerEntryPoint : IServerEntryPoint
public sealed class CollectionManagerEntryPoint : IServerEntryPoint
{
private readonly CollectionManager _collectionManager;
private readonly IServerConfigurationManager _config;
private ILogger _logger;
private readonly ILogger _logger;
public CollectionManagerEntryPoint(ICollectionManager collectionManager, IServerConfigurationManager config, ILogger logger)
{
@ -354,6 +354,7 @@ namespace Emby.Server.Implementations.Collections
_logger = logger;
}
/// <inheritdoc />
public async Task RunAsync()
{
if (!_config.Configuration.CollectionsUpgraded && _config.Configuration.IsStartupWizardCompleted)
@ -377,39 +378,10 @@ namespace Emby.Server.Implementations.Collections
}
}
#region IDisposable Support
private bool disposedValue = false; // To detect redundant calls
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
// TODO: dispose managed state (managed objects).
}
// TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
// TODO: set large fields to null.
disposedValue = true;
}
}
// TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources.
// ~CollectionManagerEntryPoint() {
// // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
// Dispose(false);
// }
// This code added to correctly implement the disposable pattern.
/// <inheritdoc />
public void Dispose()
{
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.
Dispose(true);
// TODO: uncomment the following line if the finalizer is overridden above.
// GC.SuppressFinalize(this);
// Nothing to dispose
}
#endregion
}
}

@ -1,13 +1,16 @@
using System.Collections.Generic;
using static MediaBrowser.Controller.Extensions.ConfigurationExtensions;
namespace Emby.Server.Implementations
{
public static class ConfigurationOptions
{
public static readonly Dictionary<string, string> Configuration = new Dictionary<string, string>
public static Dictionary<string, string> Configuration => new Dictionary<string, string>
{
{ "HttpListenerHost:DefaultRedirectPath", "web/index.html" },
{ "MusicBrainz:BaseUrl", "https://www.musicbrainz.org" }
{ "MusicBrainz:BaseUrl", "https://www.musicbrainz.org" },
{ FfmpegProbeSizeKey, "1G" },
{ FfmpegAnalyzeDurationKey, "200M" }
};
}
}

@ -49,6 +49,21 @@ namespace Emby.Server.Implementations.Data
private readonly TypeMapper _typeMapper;
private readonly JsonSerializerOptions _jsonOptions;
static SqliteItemRepository()
{
var queryPrefixText = new StringBuilder();
queryPrefixText.Append("insert into mediaattachments (");
foreach (var column in _mediaAttachmentSaveColumns)
{
queryPrefixText.Append(column)
.Append(',');
}
queryPrefixText.Length -= 1;
queryPrefixText.Append(") values ");
_mediaAttachmentInsertPrefix = queryPrefixText.ToString();
}
/// <summary>
/// Initializes a new instance of the <see cref="SqliteItemRepository"/> class.
/// </summary>
@ -92,6 +107,8 @@ namespace Emby.Server.Implementations.Data
{
const string CreateMediaStreamsTableCommand
= "create table if not exists mediastreams (ItemId GUID, StreamIndex INT, StreamType TEXT, Codec TEXT, Language TEXT, ChannelLayout TEXT, Profile TEXT, AspectRatio TEXT, Path TEXT, IsInterlaced BIT, BitRate INT NULL, Channels INT NULL, SampleRate INT NULL, IsDefault BIT, IsForced BIT, IsExternal BIT, Height INT NULL, Width INT NULL, AverageFrameRate FLOAT NULL, RealFrameRate FLOAT NULL, Level FLOAT NULL, PixelFormat TEXT, BitDepth INT NULL, IsAnamorphic BIT NULL, RefFrames INT NULL, CodecTag TEXT NULL, Comment TEXT NULL, NalLengthSize TEXT NULL, IsAvc BIT NULL, Title TEXT NULL, TimeBase TEXT NULL, CodecTimeBase TEXT NULL, ColorPrimaries TEXT NULL, ColorSpace TEXT NULL, ColorTransfer TEXT NULL, PRIMARY KEY (ItemId, StreamIndex))";
const string CreateMediaAttachmentsTableCommand
= "create table if not exists mediaattachments (ItemId GUID, AttachmentIndex INT, Codec TEXT, CodecTag TEXT NULL, Comment TEXT NULL, Filename TEXT NULL, MIMEType TEXT NULL, PRIMARY KEY (ItemId, AttachmentIndex))";
string[] queries =
{
@ -114,6 +131,7 @@ namespace Emby.Server.Implementations.Data
"create table if not exists " + ChaptersTableName + " (ItemId GUID, ChapterIndex INT NOT NULL, StartPositionTicks BIGINT NOT NULL, Name TEXT, ImagePath TEXT, PRIMARY KEY (ItemId, ChapterIndex))",
CreateMediaStreamsTableCommand,
CreateMediaAttachmentsTableCommand,
"pragma shrink_memory"
};
@ -421,6 +439,19 @@ namespace Emby.Server.Implementations.Data
"ColorTransfer"
};
private static readonly string[] _mediaAttachmentSaveColumns =
{
"ItemId",
"AttachmentIndex",
"Codec",
"CodecTag",
"Comment",
"Filename",
"MIMEType"
};
private static readonly string _mediaAttachmentInsertPrefix;
private static string GetSaveItemCommandText()
{
var saveColumns = new []
@ -4593,10 +4624,20 @@ namespace Emby.Server.Implementations.Data
if (query.ExcludeInheritedTags.Length > 0)
{
var tagValues = query.ExcludeInheritedTags.Select(i => "'" + GetCleanValue(i) + "'");
var tagValuesList = string.Join(",", tagValues);
whereClauses.Add("((select CleanValue from itemvalues where ItemId=Guid and Type=6 and cleanvalue in (" + tagValuesList + ")) is null)");
var paramName = "@ExcludeInheritedTags";
if (statement == null)
{
int index = 0;
string excludedTags = string.Join(",", query.ExcludeInheritedTags.Select(t => paramName + index++));
whereClauses.Add("((select CleanValue from itemvalues where ItemId=Guid and Type=6 and cleanvalue in (" + excludedTags + ")) is null)");
}
else
{
for (int index = 0; index < query.ExcludeInheritedTags.Length; index++)
{
statement.TryBind(paramName + index, GetCleanValue(query.ExcludeInheritedTags[index]));
}
}
}
if (query.SeriesStatuses.Length > 0)
@ -6126,5 +6167,175 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
return item;
}
public List<MediaAttachment> GetMediaAttachments(MediaAttachmentQuery query)
{
CheckDisposed();
if (query == null)
{
throw new ArgumentNullException(nameof(query));
}
var cmdText = "select "
+ string.Join(",", _mediaAttachmentSaveColumns)
+ " from mediaattachments where"
+ " ItemId=@ItemId";
if (query.Index.HasValue)
{
cmdText += " AND AttachmentIndex=@AttachmentIndex";
}
cmdText += " order by AttachmentIndex ASC";
var list = new List<MediaAttachment>();
using (var connection = GetConnection(true))
using (var statement = PrepareStatement(connection, cmdText))
{
statement.TryBind("@ItemId", query.ItemId.ToByteArray());
if (query.Index.HasValue)
{
statement.TryBind("@AttachmentIndex", query.Index.Value);
}
foreach (var row in statement.ExecuteQuery())
{
list.Add(GetMediaAttachment(row));
}
}
return list;
}
public void SaveMediaAttachments(
Guid id,
IReadOnlyList<MediaAttachment> attachments,
CancellationToken cancellationToken)
{
CheckDisposed();
if (id == Guid.Empty)
{
throw new ArgumentException(nameof(id));
}
if (attachments == null)
{
throw new ArgumentNullException(nameof(attachments));
}
cancellationToken.ThrowIfCancellationRequested();
using (var connection = GetConnection())
{
connection.RunInTransaction(db =>
{
var itemIdBlob = id.ToByteArray();
db.Execute("delete from mediaattachments where ItemId=@ItemId", itemIdBlob);
InsertMediaAttachments(itemIdBlob, attachments, db, cancellationToken);
}, TransactionMode);
}
}
private void InsertMediaAttachments(
byte[] idBlob,
IReadOnlyList<MediaAttachment> attachments,
IDatabaseConnection db,
CancellationToken cancellationToken)
{
const int InsertAtOnce = 10;
for (var startIndex = 0; startIndex < attachments.Count; startIndex += InsertAtOnce)
{
var insertText = new StringBuilder(_mediaAttachmentInsertPrefix);
var endIndex = Math.Min(attachments.Count, startIndex + InsertAtOnce);
for (var i = startIndex; i < endIndex; i++)
{
var index = i.ToString(CultureInfo.InvariantCulture);
insertText.Append("(@ItemId, ");
foreach (var column in _mediaAttachmentSaveColumns.Skip(1))
{
insertText.Append("@" + column + index + ",");
}
insertText.Length -= 1;
insertText.Append("),");
}
insertText.Length--;
cancellationToken.ThrowIfCancellationRequested();
using (var statement = PrepareStatement(db, insertText.ToString()))
{
statement.TryBind("@ItemId", idBlob);
for (var i = startIndex; i < endIndex; i++)
{
var index = i.ToString(CultureInfo.InvariantCulture);
var attachment = attachments[i];
statement.TryBind("@AttachmentIndex" + index, attachment.Index);
statement.TryBind("@Codec" + index, attachment.Codec);
statement.TryBind("@CodecTag" + index, attachment.CodecTag);
statement.TryBind("@Comment" + index, attachment.Comment);
statement.TryBind("@FileName" + index, attachment.FileName);
statement.TryBind("@MimeType" + index, attachment.MimeType);
}
statement.Reset();
statement.MoveNext();
}
}
}
/// <summary>
/// Gets the attachment.
/// </summary>
/// <param name="reader">The reader.</param>
/// <returns>MediaAttachment</returns>
private MediaAttachment GetMediaAttachment(IReadOnlyList<IResultSetValue> reader)
{
var item = new MediaAttachment
{
Index = reader[1].ToInt()
};
if (reader[2].SQLiteType != SQLiteType.Null)
{
item.Codec = reader[2].ToString();
}
if (reader[2].SQLiteType != SQLiteType.Null)
{
item.CodecTag = reader[3].ToString();
}
if (reader[4].SQLiteType != SQLiteType.Null)
{
item.Comment = reader[4].ToString();
}
if (reader[6].SQLiteType != SQLiteType.Null)
{
item.FileName = reader[5].ToString();
}
if (reader[6].SQLiteType != SQLiteType.Null)
{
item.MimeType = reader[6].ToString();
}
return item;
}
}
}

@ -29,7 +29,6 @@
<PackageReference Include="Microsoft.AspNetCore.ResponseCompression" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.WebSockets" Version="2.2.1" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="3.0.1" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.0.1" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="3.0.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.0.1" />
@ -50,7 +49,7 @@
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
<!-- Code analysers-->
<!-- Code Analyzers-->
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.7" />
<PackageReference Include="SerilogAnalyzer" Version="0.15.0" />

@ -109,7 +109,7 @@ namespace Emby.Server.Implementations.IO
}
try
{
return Path.Combine(Path.GetFullPath(folderPath), filePath);
return Path.GetFullPath(Path.Combine(folderPath, filePath));
}
catch (ArgumentException)
{

@ -392,9 +392,9 @@ namespace Emby.Server.Implementations.Library
// Add this flag to GetDeletePaths if required in the future
var isRequiredForDelete = true;
foreach (var fileSystemInfo in item.GetDeletePaths().ToList())
foreach (var fileSystemInfo in item.GetDeletePaths())
{
if (File.Exists(fileSystemInfo.FullName))
if (Directory.Exists(fileSystemInfo.FullName) || File.Exists(fileSystemInfo.FullName))
{
try
{

@ -130,6 +130,21 @@ namespace Emby.Server.Implementations.Library
return streams;
}
/// <inheritdoc />
public List<MediaAttachment> GetMediaAttachments(MediaAttachmentQuery query)
{
return _itemRepo.GetMediaAttachments(query);
}
/// <inheritdoc />
public List<MediaAttachment> GetMediaAttachments(Guid itemId)
{
return GetMediaAttachments(new MediaAttachmentQuery
{
ItemId = itemId
});
}
public async Task<List<MediaSourceInfo>> GetPlayackMediaSources(BaseItem item, User user, bool allowMediaProbe, bool enablePathSubstitution, CancellationToken cancellationToken)
{
var mediaSources = GetStaticMediaSources(item, enablePathSubstitution, user);

@ -42,13 +42,13 @@ namespace Emby.Server.Implementations.Library
/// </summary>
public class UserManager : IUserManager
{
private readonly object _policySyncLock = new object();
private readonly object _configSyncLock = new object();
/// <summary>
/// The logger.
/// </summary>
private readonly ILogger _logger;
private readonly object _policySyncLock = new object();
/// <summary>
/// Gets the active user repository.
/// </summary>
@ -255,7 +255,12 @@ namespace Emby.Server.Implementations.Library
return builder.ToString();
}
public async Task<User> AuthenticateUser(string username, string password, string hashedPassword, string remoteEndPoint, bool isUserSession)
public async Task<User> AuthenticateUser(
string username,
string password,
string hashedPassword,
string remoteEndPoint,
bool isUserSession)
{
if (string.IsNullOrWhiteSpace(username))
{
@ -392,7 +397,7 @@ namespace Emby.Server.Implementations.Library
if (providers.Length == 0)
{
// Assign the user to the InvalidAuthProvider since no configured auth provider was valid/found
_logger.LogWarning("User {UserName} was found with invalid/missing Authentication Provider {AuthenticationProviderId}. Assigning user to InvalidAuthProvider until this is corrected", user.Name, user.Policy.AuthenticationProviderId);
_logger.LogWarning("User {UserName} was found with invalid/missing Authentication Provider {AuthenticationProviderId}. Assigning user to InvalidAuthProvider until this is corrected", user?.Name, user?.Policy.AuthenticationProviderId);
providers = new IAuthenticationProvider[] { _invalidAuthProvider };
}
@ -472,7 +477,7 @@ namespace Emby.Server.Implementations.Library
if (!success
&& _networkManager.IsInLocalNetwork(remoteEndPoint)
&& user.Configuration.EnableLocalPassword
&& user?.Configuration.EnableLocalPassword == true
&& !string.IsNullOrEmpty(user.EasyPassword))
{
// Check easy password
@ -754,13 +759,10 @@ namespace Emby.Server.Implementations.Library
return user;
}
/// <summary>
/// Deletes the user.
/// </summary>
/// <param name="user">The user.</param>
/// <returns>Task.</returns>
/// <exception cref="ArgumentNullException">user</exception>
/// <exception cref="ArgumentException"></exception>
/// <inheritdoc />
/// <exception cref="ArgumentNullException">The <c>user</c> is <c>null</c>.</exception>
/// <exception cref="ArgumentException">The <c>user</c> doesn't exist, or is the last administrator.</exception>
/// <exception cref="InvalidOperationException">The <c>user</c> can't be deleted; there are no other users.</exception>
public void DeleteUser(User user)
{
if (user == null)
@ -779,7 +781,7 @@ namespace Emby.Server.Implementations.Library
if (_users.Count == 1)
{
throw new ArgumentException(string.Format(
throw new InvalidOperationException(string.Format(
CultureInfo.InvariantCulture,
"The user '{0}' cannot be deleted because there must be at least one user in the system.",
user.Name));
@ -800,17 +802,20 @@ namespace Emby.Server.Implementations.Library
_userRepository.DeleteUser(user);
try
{
_fileSystem.DeleteFile(configPath);
}
catch (IOException ex)
// Delete user config dir
lock (_configSyncLock)
lock (_policySyncLock)
{
_logger.LogError(ex, "Error deleting file {path}", configPath);
try
{
Directory.Delete(user.ConfigurationDirectoryPath, true);
}
catch (IOException ex)
{
_logger.LogError(ex, "Error deleting user config dir: {Path}", user.ConfigurationDirectoryPath);
}
}
DeleteUserPolicy(user);
_users.TryRemove(user.Id, out _);
OnUserDeleted(user);
@ -918,10 +923,9 @@ namespace Emby.Server.Implementations.Library
public UserPolicy GetUserPolicy(User user)
{
var path = GetPolicyFilePath(user);
if (!File.Exists(path))
{
return GetDefaultPolicy(user);
return GetDefaultPolicy();
}
try
@ -931,19 +935,15 @@ namespace Emby.Server.Implementations.Library
return (UserPolicy)_xmlSerializer.DeserializeFromFile(typeof(UserPolicy), path);
}
}
catch (IOException)
{
return GetDefaultPolicy(user);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error reading policy file: {path}", path);
_logger.LogError(ex, "Error reading policy file: {Path}", path);
return GetDefaultPolicy(user);
return GetDefaultPolicy();
}
}
private static UserPolicy GetDefaultPolicy(User user)
private static UserPolicy GetDefaultPolicy()
{
return new UserPolicy
{
@ -983,27 +983,6 @@ namespace Emby.Server.Implementations.Library
}
}
private void DeleteUserPolicy(User user)
{
var path = GetPolicyFilePath(user);
try
{
lock (_policySyncLock)
{
_fileSystem.DeleteFile(path);
}
}
catch (IOException)
{
}
catch (Exception ex)
{
_logger.LogError(ex, "Error deleting policy file");
}
}
private static string GetPolicyFilePath(User user)
{
return Path.Combine(user.ConfigurationDirectoryPath, "policy.xml");
@ -1030,19 +1009,14 @@ namespace Emby.Server.Implementations.Library
return (UserConfiguration)_xmlSerializer.DeserializeFromFile(typeof(UserConfiguration), path);
}
}
catch (IOException)
{
return new UserConfiguration();
}
catch (Exception ex)
{
_logger.LogError(ex, "Error reading policy file: {path}", path);
_logger.LogError(ex, "Error reading policy file: {Path}", path);
return new UserConfiguration();
}
}
private readonly object _configSyncLock = new object();
public void UpdateConfiguration(Guid userId, UserConfiguration config)
{
var user = GetUserById(userId);

@ -1,5 +1,6 @@
using System;
using System.IO;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Net;
@ -74,7 +75,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
DecompressionMethod = CompressionMethod.None
};
using (var response = await _httpClient.SendAsync(httpRequestOptions, "GET").ConfigureAwait(false))
using (var response = await _httpClient.SendAsync(httpRequestOptions, HttpMethod.Get).ConfigureAwait(false))
{
_logger.LogInformation("Opened recording stream from tuner provider");

@ -5,6 +5,7 @@ using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common;
@ -12,7 +13,6 @@ using MediaBrowser.Common.Net;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.LiveTv;
using MediaBrowser.Model.Net;
using MediaBrowser.Model.Serialization;
@ -663,7 +663,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
try
{
return await _httpClient.SendAsync(options, "GET").ConfigureAwait(false);
return await _httpClient.SendAsync(options, HttpMethod.Get).ConfigureAwait(false);
}
catch (HttpException ex)
{
@ -738,7 +738,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
httpOptions.RequestHeaders["token"] = token;
using (await _httpClient.SendAsync(httpOptions, "PUT").ConfigureAwait(false))
using (await _httpClient.SendAsync(httpOptions, HttpMethod.Put).ConfigureAwait(false))
{
}
}

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Configuration;
@ -64,7 +65,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
httpRequestOptions.RequestHeaders[header.Key] = header.Value;
}
var response = await _httpClient.SendAsync(httpRequestOptions, "GET").ConfigureAwait(false);
var response = await _httpClient.SendAsync(httpRequestOptions, HttpMethod.Get).ConfigureAwait(false);
var extension = "ts";
var requiresRemux = false;

@ -1,11 +1,11 @@
{
"Albums": "Àlbums",
"AppDeviceValues": "App: {0}, Dispositiu: {1}",
"Application": "Application",
"AppDeviceValues": "Aplicació: {0}, Dispositiu: {1}",
"Application": "Aplicació",
"Artists": "Artistes",
"AuthenticationSucceededWithUserName": "{0} s'ha autenticat correctament",
"Books": "Llibres",
"CameraImageUploadedFrom": "A new camera image has been uploaded from {0}",
"CameraImageUploadedFrom": "Una nova imatge de càmera ha sigut pujada des de {0}",
"Channels": "Canals",
"ChapterNameValue": "Episodi {0}",
"Collections": "Col·leccions",
@ -15,8 +15,8 @@
"Favorites": "Preferits",
"Folders": "Directoris",
"Genres": "Gèneres",
"HeaderAlbumArtists": "Album Artists",
"HeaderCameraUploads": "Camera Uploads",
"HeaderAlbumArtists": "Artistes dels Àlbums",
"HeaderCameraUploads": "Pujades de Càmera",
"HeaderContinueWatching": "Continua Veient",
"HeaderFavoriteAlbums": "Àlbums Preferits",
"HeaderFavoriteArtists": "Artistes Preferits",
@ -27,71 +27,71 @@
"HeaderNextUp": "A continuació",
"HeaderRecordingGroups": "Grups d'Enregistrament",
"HomeVideos": "Vídeos domèstics",
"Inherit": "Heretat",
"ItemAddedWithName": "{0} afegit a la biblioteca",
"ItemRemovedWithName": "{0} eliminat de la biblioteca",
"Inherit": "Hereta",
"ItemAddedWithName": "{0} ha estat afegit a la biblioteca",
"ItemRemovedWithName": "{0} ha estat eliminat de la biblioteca",
"LabelIpAddressValue": "Adreça IP: {0}",
"LabelRunningTimeValue": "Temps en marxa: {0}",
"LabelRunningTimeValue": "Temps en funcionament: {0}",
"Latest": "Darreres",
"MessageApplicationUpdated": "El Servidor d'Jellyfin ha estat actualitzat",
"MessageApplicationUpdatedTo": "Jellyfin Server has been updated to {0}",
"MessageNamedServerConfigurationUpdatedWithValue": "La secció de configuració {0} ha estat actualitzada",
"MessageApplicationUpdated": "El Servidor de Jellyfin ha estat actualitzat",
"MessageApplicationUpdatedTo": "El Servidor de Jellyfin ha estat actualitzat a {0}",
"MessageNamedServerConfigurationUpdatedWithValue": "La secció {0} de la configuració del servidor ha estat actualitzada",
"MessageServerConfigurationUpdated": "S'ha actualitzat la configuració del servidor",
"MixedContent": "Contingut mesclat",
"Movies": "Pel·lícules",
"Music": "Música",
"MusicVideos": "Vídeos musicals",
"NameInstallFailed": "{0} installation failed",
"NameInstallFailed": "Instalació de {0} fallida",
"NameSeasonNumber": "Temporada {0}",
"NameSeasonUnknown": "Season Unknown",
"NewVersionIsAvailable": "A new version of Jellyfin Server is available for download.",
"NameSeasonUnknown": "Temporada Desconeguda",
"NewVersionIsAvailable": "Una nova versió del Servidor Jellyfin està disponible per descarregar.",
"NotificationOptionApplicationUpdateAvailable": "Actualització d'aplicació disponible",
"NotificationOptionApplicationUpdateInstalled": "Actualització d'aplicació instal·lada",
"NotificationOptionAudioPlayback": "Audio playback started",
"NotificationOptionAudioPlaybackStopped": "Audio playback stopped",
"NotificationOptionCameraImageUploaded": "Camera image uploaded",
"NotificationOptionInstallationFailed": "Installation failure",
"NotificationOptionNewLibraryContent": "New content added",
"NotificationOptionPluginError": "Un component ha fallat",
"NotificationOptionPluginInstalled": "Complement instal·lat",
"NotificationOptionPluginUninstalled": "Complement desinstal·lat",
"NotificationOptionPluginUpdateInstalled": "Actualització de complement instal·lada",
"NotificationOptionServerRestartRequired": "Server restart required",
"NotificationOptionTaskFailed": "Scheduled task failure",
"NotificationOptionUserLockedOut": "User locked out",
"NotificationOptionVideoPlayback": "Video playback started",
"NotificationOptionVideoPlaybackStopped": "Video playback stopped",
"NotificationOptionAudioPlayback": "Reproducció d'audio iniciada",
"NotificationOptionAudioPlaybackStopped": "Reproducció d'audio aturada",
"NotificationOptionCameraImageUploaded": "Imatge de càmera pujada",
"NotificationOptionInstallationFailed": "Instalació fallida",
"NotificationOptionNewLibraryContent": "Nou contingut afegit",
"NotificationOptionPluginError": "Un connector ha fallat",
"NotificationOptionPluginInstalled": "Connector instal·lat",
"NotificationOptionPluginUninstalled": "Connector desinstal·lat",
"NotificationOptionPluginUpdateInstalled": "Actualització de connector instal·lada",
"NotificationOptionServerRestartRequired": "Reinici del servidor requerit",
"NotificationOptionTaskFailed": "Tasca programada fallida",
"NotificationOptionUserLockedOut": "Usuari tancat",
"NotificationOptionVideoPlayback": "Reproducció de video iniciada",
"NotificationOptionVideoPlaybackStopped": "Reproducció de video aturada",
"Photos": "Fotos",
"Playlists": "Llistes de reproducció",
"Plugin": "Plugin",
"Plugin": "Connector",
"PluginInstalledWithName": "{0} ha estat instal·lat",
"PluginUninstalledWithName": "{0} ha estat desinstal·lat",
"PluginUpdatedWithName": "{0} ha estat actualitzat",
"ProviderValue": "Proveïdor: {0}",
"ScheduledTaskFailedWithName": "{0} ha fallat",
"ScheduledTaskStartedWithName": "{0} iniciat",
"ServerNameNeedsToBeRestarted": "{0} needs to be restarted",
"Shows": "Espectacles",
"ServerNameNeedsToBeRestarted": "{0} necessita ser reiniciat",
"Shows": "Programes",
"Songs": "Cançons",
"StartupEmbyServerIsLoading": "El Servidor d'Jellyfin est&agrave; carregant. Si et plau, prova de nou en breus.",
"SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}",
"SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
"SubtitleDownloadFailureFromForItem": "Els subtítols no s'han pogut baixar de {0} per {1}",
"SubtitlesDownloadedForItem": "Subtítols descarregats per a {0}",
"Sync": "Sync",
"Sync": "Sincronitzar",
"System": "System",
"TvShows": "Espectacles de TV",
"User": "User",
"UserCreatedWithName": "S'ha creat l'usuari {0}",
"UserDeletedWithName": "L'usuari {0} ha estat eliminat",
"UserDownloadingItemWithValues": "{0} està descarregant {1}",
"UserLockedOutWithName": "User {0} has been locked out",
"UserLockedOutWithName": "L'usuari {0} ha sigut tancat",
"UserOfflineFromDevice": "{0} s'ha desconnectat de {1}",
"UserOnlineFromDevice": "{0} està connectat des de {1}",
"UserPasswordChangedWithName": "La contrasenya ha estat canviada per a l'usuari {0}",
"UserPolicyUpdatedWithName": "User policy has been updated for {0}",
"UserPolicyUpdatedWithName": "La política d'usuari s'ha actualitzat per {0}",
"UserStartedPlayingItemWithValues": "{0} ha començat a reproduir {1}",
"UserStoppedPlayingItemWithValues": "{0} ha parat de reproduir {1}",
"ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
"ValueHasBeenAddedToLibrary": "{0} ha sigut afegit a la teva llibreria",
"ValueSpecialEpisodeName": "Especial - {0}",
"VersionNumber": "Versió {0}"
}

@ -19,10 +19,10 @@
"HeaderCameraUploads": "Photos transférées",
"HeaderContinueWatching": "Continuer à regarder",
"HeaderFavoriteAlbums": "Albums favoris",
"HeaderFavoriteArtists": "Artistes favoris",
"HeaderFavoriteArtists": "Artistes préférés",
"HeaderFavoriteEpisodes": "Épisodes favoris",
"HeaderFavoriteShows": "Séries favorites",
"HeaderFavoriteSongs": "Chansons favorites",
"HeaderFavoriteSongs": "Chansons préférées",
"HeaderLiveTV": "TV en direct",
"HeaderNextUp": "À suivre",
"HeaderRecordingGroups": "Groupes d'enregistrements",

@ -0,0 +1,32 @@
{
"Albums": "Album",
"AuthenticationSucceededWithUserName": "{0} berhasil diautentikasi",
"AppDeviceValues": "Aplikasi: {0}, Alat: {1}",
"LabelRunningTimeValue": "Waktu berjalan: {0}",
"MessageApplicationUpdatedTo": "Jellyfin Server sudah diperbarui ke {0}",
"MessageApplicationUpdated": "Jellyfin Server sudah diperbarui",
"Latest": "Terbaru",
"LabelIpAddressValue": "IP address: {0}",
"ItemRemovedWithName": "{0} sudah dikeluarkan dari perpustakaan",
"ItemAddedWithName": "{0} sudah dimasukkan ke dalam perpustakaan",
"Inherit": "Warisan",
"HomeVideos": "Video Rumah",
"HeaderRecordingGroups": "Grup Rekaman",
"HeaderNextUp": "Selanjutnya",
"HeaderLiveTV": "TV Live",
"HeaderFavoriteSongs": "Lagu Favorit",
"HeaderFavoriteShows": "Tayangan Favorit",
"HeaderFavoriteEpisodes": "Episode Favorit",
"HeaderFavoriteArtists": "Artis Favorit",
"HeaderFavoriteAlbums": "Album Favorit",
"HeaderContinueWatching": "Masih Melihat",
"HeaderCameraUploads": "Uplod Kamera",
"HeaderAlbumArtists": "Album Artis",
"Genres": "Genre",
"Folders": "Folder",
"Favorites": "Favorit",
"Collections": "Koleksi",
"Books": "Buku",
"Artists": "Artis",
"Application": "Aplikasi"
}

@ -1,97 +1,97 @@
{
"Albums": "Albumai",
"AppDeviceValues": "App: {0}, Device: {1}",
"Application": "Application",
"AppDeviceValues": "Programa: {0}, Įrenginys: {1}",
"Application": "Programa",
"Artists": "Atlikėjai",
"AuthenticationSucceededWithUserName": "{0} successfully authenticated",
"AuthenticationSucceededWithUserName": "{0} sėkmingai autentifikuota",
"Books": "Knygos",
"CameraImageUploadedFrom": "A new camera image has been uploaded from {0}",
"CameraImageUploadedFrom": "Nauja nuotrauka įkelta iš kameros {0}",
"Channels": "Kanalai",
"ChapterNameValue": "Chapter {0}",
"ChapterNameValue": "Scena{0}",
"Collections": "Kolekcijos",
"DeviceOfflineWithName": "{0} has disconnected",
"DeviceOnlineWithName": "{0} is connected",
"FailedLoginAttemptWithUserName": "Failed login attempt from {0}",
"DeviceOfflineWithName": "{0} buvo atjungtas",
"DeviceOnlineWithName": "{0} prisijungęs",
"FailedLoginAttemptWithUserName": "{0} - nesėkmingas bandymas prisijungti",
"Favorites": "Mėgstami",
"Folders": "Katalogai",
"Genres": "Žanrai",
"HeaderAlbumArtists": "Albumo atlikėjai",
"HeaderCameraUploads": "Camera Uploads",
"HeaderCameraUploads": "Kameros",
"HeaderContinueWatching": "Žiūrėti toliau",
"HeaderFavoriteAlbums": "Favorite Albums",
"HeaderFavoriteArtists": "Favorite Artists",
"HeaderFavoriteEpisodes": "Favorite Episodes",
"HeaderFavoriteShows": "Favorite Shows",
"HeaderFavoriteSongs": "Favorite Songs",
"HeaderLiveTV": "Live TV",
"HeaderNextUp": "Next Up",
"HeaderRecordingGroups": "Recording Groups",
"HomeVideos": "Home videos",
"Inherit": "Inherit",
"ItemAddedWithName": "{0} was added to the library",
"ItemRemovedWithName": "{0} was removed from the library",
"LabelIpAddressValue": "Ip address: {0}",
"LabelRunningTimeValue": "Running time: {0}",
"Latest": "Latest",
"MessageApplicationUpdated": "Jellyfin Server has been updated",
"MessageApplicationUpdatedTo": "Jellyfin Server has been updated to {0}",
"MessageNamedServerConfigurationUpdatedWithValue": "Server configuration section {0} has been updated",
"MessageServerConfigurationUpdated": "Server configuration has been updated",
"HeaderFavoriteAlbums": "Mėgstami Albumai",
"HeaderFavoriteArtists": "Mėgstami Atlikėjai",
"HeaderFavoriteEpisodes": "Mėgstamiausios serijos",
"HeaderFavoriteShows": "Mėgstamiausi serialai",
"HeaderFavoriteSongs": "Mėgstamos dainos",
"HeaderLiveTV": "TV gyvai",
"HeaderNextUp": "Toliau eilėje",
"HeaderRecordingGroups": "Įrašų grupės",
"HomeVideos": "Namų vaizdo įrašai",
"Inherit": "Paveldėti",
"ItemAddedWithName": "{0} - buvo įkeltas į mediateką",
"ItemRemovedWithName": "{0} - buvo pašalinta iš mediatekos",
"LabelIpAddressValue": "IP adresas: {0}",
"LabelRunningTimeValue": "Trukmė: {0}",
"Latest": "Naujausi",
"MessageApplicationUpdated": "\"Jellyfin Server\" atnaujintas",
"MessageApplicationUpdatedTo": "\"Jellyfin Server\" buvo atnaujinta iki {0}",
"MessageNamedServerConfigurationUpdatedWithValue": "Serverio nustatymai (skyrius {0}) buvo atnaujinti",
"MessageServerConfigurationUpdated": "Serverio nustatymai buvo atnaujinti",
"MixedContent": "Mixed content",
"Movies": "Filmai",
"Music": "Music",
"MusicVideos": "Music videos",
"NameInstallFailed": "{0} installation failed",
"NameSeasonNumber": "Season {0}",
"NameSeasonUnknown": "Season Unknown",
"NewVersionIsAvailable": "A new version of Jellyfin Server is available for download.",
"NotificationOptionApplicationUpdateAvailable": "Application update available",
"NotificationOptionApplicationUpdateInstalled": "Application update installed",
"NotificationOptionAudioPlayback": "Audio playback started",
"NotificationOptionAudioPlaybackStopped": "Audio playback stopped",
"NotificationOptionCameraImageUploaded": "Camera image uploaded",
"NotificationOptionInstallationFailed": "Installation failure",
"NotificationOptionNewLibraryContent": "New content added",
"NotificationOptionPluginError": "Plugin failure",
"NotificationOptionPluginInstalled": "Plugin installed",
"NotificationOptionPluginUninstalled": "Plugin uninstalled",
"NotificationOptionPluginUpdateInstalled": "Plugin update installed",
"NotificationOptionServerRestartRequired": "Server restart required",
"NotificationOptionTaskFailed": "Scheduled task failure",
"NotificationOptionUserLockedOut": "User locked out",
"NotificationOptionVideoPlayback": "Video playback started",
"NotificationOptionVideoPlaybackStopped": "Video playback stopped",
"Photos": "Photos",
"Playlists": "Playlists",
"Music": "Muzika",
"MusicVideos": "Muzikiniai klipai",
"NameInstallFailed": "{0} diegimo klaida",
"NameSeasonNumber": "Sezonas {0}",
"NameSeasonUnknown": "Sezonas neatpažintas",
"NewVersionIsAvailable": "Nauja \"Jellyfin Server\" versija yra prieinama atsisiuntimui.",
"NotificationOptionApplicationUpdateAvailable": "Galimi programos atnaujinimai",
"NotificationOptionApplicationUpdateInstalled": "Programos atnaujinimai įdiegti",
"NotificationOptionAudioPlayback": "Garso atkūrimas pradėtas",
"NotificationOptionAudioPlaybackStopped": "Garso atkūrimas sustabdytas",
"NotificationOptionCameraImageUploaded": "Kameros vaizdai įkelti",
"NotificationOptionInstallationFailed": "Diegimo klaida",
"NotificationOptionNewLibraryContent": "Naujas turinys įkeltas",
"NotificationOptionPluginError": "Įskiepio klaida",
"NotificationOptionPluginInstalled": "Įskiepis įdiegtas",
"NotificationOptionPluginUninstalled": "Įskiepis pašalintas",
"NotificationOptionPluginUpdateInstalled": "Įskiepio atnaujinimas įdiegtas",
"NotificationOptionServerRestartRequired": "Reikalingas serverio perleidimas",
"NotificationOptionTaskFailed": "Suplanuotos užduoties klaida",
"NotificationOptionUserLockedOut": "Vartotojas užblokuotas",
"NotificationOptionVideoPlayback": "Vaizdo įrašo atkūrimas pradėtas",
"NotificationOptionVideoPlaybackStopped": "Vaizdo įrašo atkūrimas sustabdytas",
"Photos": "Nuotraukos",
"Playlists": "Grojaraštis",
"Plugin": "Plugin",
"PluginInstalledWithName": "{0} was installed",
"PluginUninstalledWithName": "{0} was uninstalled",
"PluginUpdatedWithName": "{0} was updated",
"PluginInstalledWithName": "{0} buvo įdiegtas",
"PluginUninstalledWithName": "{0} buvo pašalintas",
"PluginUpdatedWithName": "{0} buvo atnaujintas",
"ProviderValue": "Provider: {0}",
"ScheduledTaskFailedWithName": "{0} failed",
"ScheduledTaskStartedWithName": "{0} started",
"ServerNameNeedsToBeRestarted": "{0} needs to be restarted",
"Shows": "Shows",
"Songs": "Songs",
"StartupEmbyServerIsLoading": "Jellyfin Server is loading. Please try again shortly.",
"ScheduledTaskFailedWithName": "{0} klaida",
"ScheduledTaskStartedWithName": "{0} paleista",
"ServerNameNeedsToBeRestarted": "{0} reikia iš naujo paleisti",
"Shows": "Laidos",
"Songs": "Kūriniai",
"StartupEmbyServerIsLoading": "Jellyfin Server kraunasi. Netrukus pabandykite dar kartą.",
"SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}",
"SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
"SubtitlesDownloadedForItem": "Subtitles downloaded for {0}",
"SubtitleDownloadFailureFromForItem": "{1} subtitrai buvo nesėkmingai parsiųsti iš {0}",
"SubtitlesDownloadedForItem": "{0} subtitrai parsiųsti",
"Sync": "Sinchronizuoti",
"System": "System",
"TvShows": "TV Shows",
"TvShows": "TV Serialai",
"User": "User",
"UserCreatedWithName": "User {0} has been created",
"UserDeletedWithName": "User {0} has been deleted",
"UserDownloadingItemWithValues": "{0} is downloading {1}",
"UserLockedOutWithName": "User {0} has been locked out",
"UserOfflineFromDevice": "{0} has disconnected from {1}",
"UserOnlineFromDevice": "{0} is online from {1}",
"UserPasswordChangedWithName": "Password has been changed for user {0}",
"UserPolicyUpdatedWithName": "User policy has been updated for {0}",
"UserStartedPlayingItemWithValues": "{0} is playing {1} on {2}",
"UserStoppedPlayingItemWithValues": "{0} has finished playing {1} on {2}",
"ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
"UserCreatedWithName": "Vartotojas {0} buvo sukurtas",
"UserDeletedWithName": "Vartotojas {0} ištrintas",
"UserDownloadingItemWithValues": "{0} siunčiasi {1}",
"UserLockedOutWithName": "Vartotojas {0} užblokuotas",
"UserOfflineFromDevice": "{0} buvo atjungtas nuo {1}",
"UserOnlineFromDevice": "{0} prisijungęs iš {1}",
"UserPasswordChangedWithName": "Slaptažodis pakeistas vartotojui {0}",
"UserPolicyUpdatedWithName": "Vartotojo {0} teisės buvo pakeistos",
"UserStartedPlayingItemWithValues": "{0} leidžia {1} į {2}",
"UserStoppedPlayingItemWithValues": "{0} baigė leisti {1} į {2}",
"ValueHasBeenAddedToLibrary": "{0} pridėtas į mediateką",
"ValueSpecialEpisodeName": "Ypatinga - {0}",
"VersionNumber": "Version {0}"
}

@ -17,7 +17,7 @@
"Genres": "Sjangre",
"HeaderAlbumArtists": "Albumartister",
"HeaderCameraUploads": "Kameraopplastinger",
"HeaderContinueWatching": "Forsett å se",
"HeaderContinueWatching": "Fortsett å se",
"HeaderFavoriteAlbums": "Favorittalbum",
"HeaderFavoriteArtists": "Favorittartister",
"HeaderFavoriteEpisodes": "Favorittepisoder",
@ -25,18 +25,18 @@
"HeaderFavoriteSongs": "Favorittsanger",
"HeaderLiveTV": "Direkte-TV",
"HeaderNextUp": "Neste",
"HeaderRecordingGroups": "Opptak Grupper",
"HomeVideos": "Hjemmelaget filmer",
"HeaderRecordingGroups": "Opptaksgrupper",
"HomeVideos": "Hjemmelagde filmer",
"Inherit": "Arve",
"ItemAddedWithName": "{0} ble lagt til i biblioteket",
"ItemRemovedWithName": "{0} ble fjernet fra biblioteket",
"LabelIpAddressValue": "IP adresse: {0}",
"LabelRunningTimeValue": "Løpetid {0}",
"LabelIpAddressValue": "IP-adresse: {0}",
"LabelRunningTimeValue": "Kjøretid {0}",
"Latest": "Siste",
"MessageApplicationUpdated": "Jellyfin server har blitt oppdatert",
"MessageApplicationUpdatedTo": "Jellyfin-serveren ble oppdatert til {0}",
"MessageNamedServerConfigurationUpdatedWithValue": "Server konfigurasjon seksjon {0} har blitt oppdatert",
"MessageServerConfigurationUpdated": "Server konfigurasjon er oppdatert",
"MessageApplicationUpdated": "Jellyfin Server har blitt oppdatert",
"MessageApplicationUpdatedTo": "Jellyfin Server ble oppdatert til {0}",
"MessageNamedServerConfigurationUpdatedWithValue": "Serverkonfigurasjon seksjon {0} har blitt oppdatert",
"MessageServerConfigurationUpdated": "Serverkonfigurasjon er oppdatert",
"MixedContent": "Blandet innhold",
"Movies": "Filmer",
"Music": "Musikk",
@ -44,38 +44,38 @@
"NameInstallFailed": "{0}-installasjonen mislyktes",
"NameSeasonNumber": "Sesong {0}",
"NameSeasonUnknown": "Sesong ukjent",
"NewVersionIsAvailable": "En ny versjon av Jellyfin-serveren er tilgjengelig for nedlastning.",
"NotificationOptionApplicationUpdateAvailable": "Applikasjon oppdatering tilgjengelig",
"NewVersionIsAvailable": "En ny versjon av Jellyfin Server er tilgjengelig for nedlasting.",
"NotificationOptionApplicationUpdateAvailable": "Programvareoppdatering er tilgjengelig",
"NotificationOptionApplicationUpdateInstalled": "Applikasjonsoppdatering installert",
"NotificationOptionAudioPlayback": "Lyd tilbakespilling startet",
"NotificationOptionAudioPlaybackStopped": "Lyd avspilling stoppet",
"NotificationOptionCameraImageUploaded": "Kamera bilde lastet opp",
"NotificationOptionAudioPlayback": "Lydavspilling startet",
"NotificationOptionAudioPlaybackStopped": "Lydavspilling stoppet",
"NotificationOptionCameraImageUploaded": "Kamerabilde lastet opp",
"NotificationOptionInstallationFailed": "Installasjonsfeil",
"NotificationOptionNewLibraryContent": "Ny innhold er lagt til",
"NotificationOptionPluginError": "Plugin feil",
"NotificationOptionNewLibraryContent": "Nytt innhold lagt til",
"NotificationOptionPluginError": "Pluginfeil",
"NotificationOptionPluginInstalled": "Plugin installert",
"NotificationOptionPluginUninstalled": "Plugin avinstallert",
"NotificationOptionPluginUpdateInstalled": "Plugin oppdatering installert",
"NotificationOptionServerRestartRequired": "Server omstart er nødvendig",
"NotificationOptionTaskFailed": "Feil under utføring av planlagt oppgaver",
"NotificationOptionPluginUpdateInstalled": "Pluginoppdatering installert",
"NotificationOptionServerRestartRequired": "Serveromstart er nødvendig",
"NotificationOptionTaskFailed": "Feil under utføring av planlagt oppgave",
"NotificationOptionUserLockedOut": "Bruker er utestengt",
"NotificationOptionVideoPlayback": "Video tilbakespilling startet",
"NotificationOptionVideoPlaybackStopped": "Video avspilling stoppet",
"NotificationOptionVideoPlayback": "Videoavspilling startet",
"NotificationOptionVideoPlaybackStopped": "Videoavspilling stoppet",
"Photos": "Bilder",
"Playlists": "Spillelister",
"Playlists": "Spliielister",
"Plugin": "Plugin",
"PluginInstalledWithName": "{0} ble installert",
"PluginUninstalledWithName": "{0} ble avinstallert",
"PluginUpdatedWithName": "{0} ble oppdatert",
"ProviderValue": "Leverandører: {0}",
"ScheduledTaskFailedWithName": "{0} Mislykkes",
"ScheduledTaskStartedWithName": "{0} Startet",
"ProviderValue": "Leverandør: {0}",
"ScheduledTaskFailedWithName": "{0} mislykkes",
"ScheduledTaskStartedWithName": "{0} startet",
"ServerNameNeedsToBeRestarted": "{0} må startes på nytt",
"Shows": "Programmer",
"Songs": "Sanger",
"StartupEmbyServerIsLoading": "Jellyfin server laster. Prøv igjen snart.",
"StartupEmbyServerIsLoading": "Jellyfin Server laster. Prøv igjen snart.",
"SubtitleDownloadFailureForItem": "En feil oppstå under nedlasting av undertekster for {0}",
"SubtitleDownloadFailureFromForItem": "Kunne ikke laste ned teksting fra {0} for {1}",
"SubtitleDownloadFailureFromForItem": "Kunne ikke laste ned undertekster fra {0} for {1}",
"SubtitlesDownloadedForItem": "Undertekster lastet ned for {0}",
"Sync": "Synkroniser",
"System": "System",

@ -0,0 +1,96 @@
{
"HeaderLiveTV": "TV ao Vivo",
"Collections": "Colecções",
"Books": "Livros",
"Artists": "Artistas",
"Albums": "Álbuns",
"HeaderNextUp": "A Seguir",
"HeaderFavoriteSongs": "Músicas Favoritas",
"HeaderFavoriteArtists": "Artistas Favoritos",
"HeaderFavoriteAlbums": "Álbuns Favoritos",
"HeaderFavoriteEpisodes": "Episódios Favoritos",
"HeaderFavoriteShows": "Séries Favoritas",
"HeaderContinueWatching": "Continuar a Ver",
"HeaderAlbumArtists": "Artistas do Álbum",
"Genres": "Géneros",
"Folders": "Pastas",
"Favorites": "Favoritos",
"Channels": "Canais",
"UserDownloadingItemWithValues": "{0} está a transferir {1}",
"VersionNumber": "Versão {0}",
"ValueHasBeenAddedToLibrary": "{0} foi adicionado à sua biblioteca multimédia",
"UserStoppedPlayingItemWithValues": "{0} terminou a reprodução de {1} em {2}",
"UserStartedPlayingItemWithValues": "{0} está a reproduzir {1} em {2}",
"UserPolicyUpdatedWithName": "A política do utilizador {0} foi alterada",
"UserPasswordChangedWithName": "A palavra-passe do utilizador {0} foi alterada",
"UserOnlineFromDevice": "{0} ligou-se a partir de {1}",
"UserOfflineFromDevice": "{0} desligou-se a partir de {1}",
"UserLockedOutWithName": "Utilizador {0} bloqueado",
"UserDeletedWithName": "Utilizador {0} removido",
"UserCreatedWithName": "Utilizador {0} criado",
"User": "Utilizador",
"TvShows": "Programas",
"System": "Sistema",
"SubtitlesDownloadedForItem": "Legendas transferidas para {0}",
"SubtitleDownloadFailureFromForItem": "Falha na transferência de legendas de {0} para {1}",
"StartupEmbyServerIsLoading": "O servidor Jellyfin está a iniciar. Tente novamente dentro de momentos.",
"ServerNameNeedsToBeRestarted": "{0} necessita ser reiniciado",
"ScheduledTaskStartedWithName": "{0} iniciou",
"ScheduledTaskFailedWithName": "{0} falhou",
"ProviderValue": "Fornecedor: {0}",
"PluginUpdatedWithName": "{0} foi actualizado",
"PluginUninstalledWithName": "{0} foi desinstalado",
"PluginInstalledWithName": "{0} foi instalado",
"Plugin": "Extensão",
"NotificationOptionVideoPlaybackStopped": "Reprodução de vídeo parada",
"NotificationOptionVideoPlayback": "Reprodução de vídeo iniciada",
"NotificationOptionUserLockedOut": "Utilizador bloqueado",
"NotificationOptionTaskFailed": "Falha em tarefa agendada",
"NotificationOptionServerRestartRequired": "É necessário reiniciar o servidor",
"NotificationOptionPluginUpdateInstalled": "Extensão actualizada",
"NotificationOptionPluginUninstalled": "Extensão desinstalada",
"NotificationOptionPluginInstalled": "Extensão instalada",
"NotificationOptionPluginError": "Falha na extensão",
"NotificationOptionNewLibraryContent": "Novo conteúdo adicionado",
"NotificationOptionInstallationFailed": "Falha de instalação",
"NotificationOptionCameraImageUploaded": "Imagem da câmara enviada",
"NotificationOptionAudioPlaybackStopped": "Reprodução Parada",
"NotificationOptionAudioPlayback": "Reprodução Iniciada",
"NotificationOptionApplicationUpdateInstalled": "A actualização da aplicação foi instalada",
"NotificationOptionApplicationUpdateAvailable": "Uma actualização da aplicação está disponível",
"NewVersionIsAvailable": "Uma nova versão do servidor Jellyfin está disponível para transferência.",
"NameSeasonUnknown": "Temporada Desconhecida",
"NameSeasonNumber": "Temporada {0}",
"NameInstallFailed": "Falha na instalação de {0}",
"MusicVideos": "Videoclips",
"Music": "Música",
"MixedContent": "Conteúdo Misto",
"MessageServerConfigurationUpdated": "A configuração do servidor foi actualizada",
"MessageNamedServerConfigurationUpdatedWithValue": "Configurações do servidor na secção {0} foram atualizadas",
"MessageApplicationUpdatedTo": "O servidor Jellyfin foi actualizado para a versão {0}",
"MessageApplicationUpdated": "O servidor Jellyfin foi actualizado",
"Latest": "Mais Recente",
"LabelRunningTimeValue": "Duração: {0}",
"LabelIpAddressValue": "Endereço IP: {0}",
"ItemRemovedWithName": "{0} foi removido da biblioteca",
"ItemAddedWithName": "{0} foi adicionado à biblioteca",
"Inherit": "Herdar",
"HomeVideos": "Vídeos Caseiros",
"HeaderRecordingGroups": "Grupos de Gravação",
"ValueSpecialEpisodeName": "Especial - {0}",
"Sync": "Sincronização",
"Songs": "Músicas",
"Shows": "Séries",
"Playlists": "Listas de Reprodução",
"Photos": "Fotografias",
"Movies": "Filmes",
"HeaderCameraUploads": "Envios a partir da câmara",
"FailedLoginAttemptWithUserName": "Tentativa de ligação a partir de {0} falhou",
"DeviceOnlineWithName": "{0} ligou-se",
"DeviceOfflineWithName": "{0} desligou-se",
"ChapterNameValue": "Capítulo {0}",
"CameraImageUploadedFrom": "Uma nova imagem de câmara foi enviada a partir de {0}",
"AuthenticationSucceededWithUserName": "{0} autenticado com sucesso",
"Application": "Aplicação",
"AppDeviceValues": "Aplicação {0}, Dispositivo: {1}"
}

@ -0,0 +1,96 @@
{
"HeaderNextUp": "Urmează",
"VersionNumber": "Versiunea {0}",
"ValueSpecialEpisodeName": "Special - {0}",
"ValueHasBeenAddedToLibrary": "{0} a fost adăugat la biblioteca multimedia",
"UserStoppedPlayingItemWithValues": "{0} a terminat rularea {1} pe {2}",
"UserStartedPlayingItemWithValues": "{0} ruleaza {1} pe {2}",
"UserPolicyUpdatedWithName": "Politica utilizatorului {0} a fost actualizată",
"UserPasswordChangedWithName": "Parola utilizatorului {0} a fost schimbată",
"UserOnlineFromDevice": "{0} este conectat de la {1}",
"UserOfflineFromDevice": "{0} s-a deconectat de la {1}",
"UserLockedOutWithName": "Utilizatorul {0} a fost blocat",
"UserDownloadingItemWithValues": "{0} descarcă {1}",
"UserDeletedWithName": "Utilizatorul {0} a fost eliminat",
"UserCreatedWithName": "Utilizatorul {0} a fost creat",
"User": "Utilizator",
"TvShows": "Spectacole TV",
"System": "Sistem",
"Sync": "Sincronizare",
"SubtitlesDownloadedForItem": "Subtitrari descarcate pentru {0}",
"SubtitleDownloadFailureFromForItem": "Subtitrările nu au putut fi descărcate de la {0} pentru {1}",
"StartupEmbyServerIsLoading": "Se încarcă serverul Jellyfin. Încercați din nou în scurt timp.",
"Songs": "Melodii",
"Shows": "Spectacole",
"ServerNameNeedsToBeRestarted": "{0} trebuie repornit",
"ScheduledTaskStartedWithName": "{0} pornit/ă",
"ScheduledTaskFailedWithName": "{0} eșuat/ă",
"ProviderValue": "Furnizor: {0}",
"PluginUpdatedWithName": "{0} a fost actualizat/ă",
"PluginUninstalledWithName": "{0} a fost dezinstalat",
"PluginInstalledWithName": "{0} a fost instalat",
"Plugin": "Complement",
"Playlists": "Liste redare",
"Photos": "Fotografii",
"NotificationOptionVideoPlaybackStopped": "Redarea video oprită",
"NotificationOptionVideoPlayback": "Începută redarea video",
"NotificationOptionUserLockedOut": "Utilizatorul a fost blocat",
"NotificationOptionTaskFailed": "Activitate programata eșuată",
"NotificationOptionServerRestartRequired": "Este necesară repornirea Serverului",
"NotificationOptionPluginUpdateInstalled": "Actualizare plugin instalată",
"NotificationOptionPluginUninstalled": "Plugin dezinstalat",
"NotificationOptionPluginInstalled": "Plugin instalat",
"NotificationOptionPluginError": "Plugin-ul a eșuat",
"NotificationOptionNewLibraryContent": "Adăugat conținut nou",
"NotificationOptionInstallationFailed": "Eșec la instalare",
"NotificationOptionCameraImageUploaded": "Încarcată imagine cameră",
"NotificationOptionAudioPlaybackStopped": "Redare audio oprită",
"NotificationOptionAudioPlayback": "A inceput redarea audio",
"NotificationOptionApplicationUpdateInstalled": "Actualizarea aplicației a fost instalată",
"NotificationOptionApplicationUpdateAvailable": "Disponibilă o actualizare a aplicației",
"NewVersionIsAvailable": "O nouă versiune a Jellyfin Server este disponibilă pentru descărcare.",
"NameSeasonUnknown": "Sezon Necunoscut",
"NameSeasonNumber": "Sezonul {0}",
"NameInstallFailed": "{0} instalare eșuată",
"MusicVideos": "Videoclipuri muzicale",
"Music": "Muzică",
"Movies": "Filme",
"MixedContent": "Conținut mixt",
"MessageServerConfigurationUpdated": "Configurația serverului a fost actualizată",
"MessageNamedServerConfigurationUpdatedWithValue": "Secțiunea de configurare a serverului {0} a fost acualizata",
"MessageApplicationUpdatedTo": "Jellyfin Server a fost actualizat la {0}",
"MessageApplicationUpdated": "Jellyfin Server a fost actualizat",
"Latest": "Cele mai recente",
"LabelRunningTimeValue": "Durată: {0}",
"LabelIpAddressValue": "Adresa IP: {0}",
"ItemRemovedWithName": "{0} a fost eliminat din bibliotecă",
"ItemAddedWithName": "{0} a fost adăugat în bibliotecă",
"Inherit": "moștenit",
"HomeVideos": "Videoclipuri personale",
"HeaderRecordingGroups": "Grupuri de înregistrare",
"HeaderLiveTV": "TV în Direct",
"HeaderFavoriteSongs": "Melodii Favorite",
"HeaderFavoriteShows": "Spectacole Favorite",
"HeaderFavoriteEpisodes": "Episoade Favorite",
"HeaderFavoriteArtists": "Artiști Favoriți",
"HeaderFavoriteAlbums": "Albume Favorite",
"HeaderContinueWatching": "Vizionează în continuare",
"HeaderCameraUploads": "Incărcări Cameră Foto",
"HeaderAlbumArtists": "Album Artiști",
"Genres": "Genuri",
"Folders": "Dosare",
"Favorites": "Favorite",
"FailedLoginAttemptWithUserName": "Încercare de conectare nereușită de la {0}",
"DeviceOnlineWithName": "{0} este conectat",
"DeviceOfflineWithName": "{0} s-a deconectat",
"Collections": "Colecții",
"ChapterNameValue": "Capitol {0}",
"Channels": "Canale",
"CameraImageUploadedFrom": "O nouă fotografie a fost încărcată din {0}",
"Books": "Cărți",
"AuthenticationSucceededWithUserName": "{0} autentificare reușită",
"Artists": "Artiști",
"Application": "Aplicație",
"AppDeviceValues": "Aplicație: {0}, Dispozitiv: {1}",
"Albums": "Albume"
}

@ -24,7 +24,7 @@
"HeaderFavoriteShows": "最爱的节目",
"HeaderFavoriteSongs": "最爱的歌曲",
"HeaderLiveTV": "电视直播",
"HeaderNextUp": "接下来",
"HeaderNextUp": "下一步",
"HeaderRecordingGroups": "录制组",
"HomeVideos": "家庭视频",
"Inherit": "继承",
@ -34,7 +34,7 @@
"LabelRunningTimeValue": "运行时间:{0}",
"Latest": "最新",
"MessageApplicationUpdated": "Jellyfin 服务器已更新",
"MessageApplicationUpdatedTo": "Jellyfin Server 版本已更新为 {0}",
"MessageApplicationUpdatedTo": "Jellyfin Server 版本已更新为 {0}",
"MessageNamedServerConfigurationUpdatedWithValue": "服务器配置 {0} 部分已更新",
"MessageServerConfigurationUpdated": "服务器配置已更新",
"MixedContent": "混合内容",
@ -42,7 +42,7 @@
"Music": "音乐",
"MusicVideos": "音乐视频",
"NameInstallFailed": "{0} 安装失败",
"NameSeasonNumber": "季 {0}",
"NameSeasonNumber": "第 {0} 季",
"NameSeasonUnknown": "未知季",
"NewVersionIsAvailable": "Jellyfin Server 有新版本可以下载。",
"NotificationOptionApplicationUpdateAvailable": "有可用的应用程序更新",

@ -56,10 +56,8 @@ namespace Emby.Server.Implementations.Playlists
{
var name = options.Name;
var folderName = _fileSystem.GetValidFilename(name) + " [playlist]";
var folderName = _fileSystem.GetValidFilename(name);
var parentFolder = GetPlaylistsFolder(Guid.Empty);
if (parentFolder == null)
{
throw new ArgumentException();
@ -253,11 +251,13 @@ namespace Emby.Server.Implementations.Playlists
SavePlaylistFile(playlist);
}
_providerManager.QueueRefresh(playlist.Id, new MetadataRefreshOptions(new DirectoryService(_fileSystem))
{
ForceSave = true
}, RefreshPriority.High);
_providerManager.QueueRefresh(
playlist.Id,
new MetadataRefreshOptions(new DirectoryService(_fileSystem))
{
ForceSave = true
},
RefreshPriority.High);
}
public void MoveItem(string playlistId, string entryId, int newIndex)
@ -303,7 +303,8 @@ namespace Emby.Server.Implementations.Playlists
private void SavePlaylistFile(Playlist item)
{
// This is probably best done as a metatata provider, but saving a file over itself will first require some core work to prevent this from happening when not needed
// this is probably best done as a metadata provider
// saving a file over itself will require some work to prevent this from happening when not needed
var playlistPath = item.Path;
var extension = Path.GetExtension(playlistPath);
@ -335,12 +336,14 @@ namespace Emby.Server.Implementations.Playlists
{
entry.Duration = TimeSpan.FromTicks(child.RunTimeTicks.Value);
}
playlist.PlaylistEntries.Add(entry);
}
string text = new WplContent().ToText(playlist);
File.WriteAllText(playlistPath, text);
}
if (string.Equals(".zpl", extension, StringComparison.OrdinalIgnoreCase))
{
var playlist = new ZplPlaylist();
@ -375,6 +378,7 @@ namespace Emby.Server.Implementations.Playlists
string text = new ZplContent().ToText(playlist);
File.WriteAllText(playlistPath, text);
}
if (string.Equals(".m3u", extension, StringComparison.OrdinalIgnoreCase))
{
var playlist = new M3uPlaylist();
@ -398,12 +402,14 @@ namespace Emby.Server.Implementations.Playlists
{
entry.Duration = TimeSpan.FromTicks(child.RunTimeTicks.Value);
}
playlist.PlaylistEntries.Add(entry);
}
string text = new M3uContent().ToText(playlist);
File.WriteAllText(playlistPath, text);
}
if (string.Equals(".m3u8", extension, StringComparison.OrdinalIgnoreCase))
{
var playlist = new M3uPlaylist();
@ -427,12 +433,14 @@ namespace Emby.Server.Implementations.Playlists
{
entry.Duration = TimeSpan.FromTicks(child.RunTimeTicks.Value);
}
playlist.PlaylistEntries.Add(entry);
}
string text = new M3u8Content().ToText(playlist);
File.WriteAllText(playlistPath, text);
}
if (string.Equals(".pls", extension, StringComparison.OrdinalIgnoreCase))
{
var playlist = new PlsPlaylist();
@ -448,6 +456,7 @@ namespace Emby.Server.Implementations.Playlists
{
entry.Length = TimeSpan.FromTicks(child.RunTimeTicks.Value);
}
playlist.PlaylistEntries.Add(entry);
}
@ -473,7 +482,7 @@ namespace Emby.Server.Implementations.Playlists
throw new ArgumentException("File absolute path was null or empty.", nameof(fileAbsolutePath));
}
if (!folderPath.EndsWith(Path.DirectorySeparatorChar.ToString()))
if (!folderPath.EndsWith(Path.DirectorySeparatorChar))
{
folderPath = folderPath + Path.DirectorySeparatorChar;
}
@ -481,7 +490,11 @@ namespace Emby.Server.Implementations.Playlists
var folderUri = new Uri(folderPath);
var fileAbsoluteUri = new Uri(fileAbsolutePath);
if (folderUri.Scheme != fileAbsoluteUri.Scheme) { return fileAbsolutePath; } // path can't be made relative.
// path can't be made relative
if (folderUri.Scheme != fileAbsoluteUri.Scheme)
{
return fileAbsolutePath;
}
var relativeUri = folderUri.MakeRelativeUri(fileAbsoluteUri);
string relativePath = Uri.UnescapeDataString(relativeUri.ToString());

@ -1,10 +1,10 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Runtime.CompilerServices;
using System.Security.Cryptography;
using System.Threading;
using System.Threading.Tasks;
@ -189,7 +189,7 @@ namespace Emby.Server.Implementations.Updates
}
/// <inheritdoc />
public async IAsyncEnumerable<PackageVersionInfo> GetAvailablePluginUpdates(CancellationToken cancellationToken = default)
public async IAsyncEnumerable<PackageVersionInfo> GetAvailablePluginUpdates([EnumeratorCancellation] CancellationToken cancellationToken = default)
{
var catalog = await GetAvailablePackages(cancellationToken).ConfigureAwait(false);

@ -96,7 +96,6 @@ namespace Jellyfin.Api.Controllers
public StartupUserDto GetFirstUser()
{
var user = _userManager.Users.First();
return new StartupUserDto
{
Name = user.Name,

@ -17,7 +17,7 @@
<ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" />
</ItemGroup>
<!-- Code analysers-->
<!-- Code Analyzers-->
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.7" PrivateAssets="All" />
<PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" />

@ -4,6 +4,7 @@
<TargetFramework>netstandard2.1</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
@ -22,4 +23,16 @@
<ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj" />
</ItemGroup>
<!-- Code analysers-->
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.7" 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>
</Project>

@ -4,10 +4,19 @@ using SkiaSharp;
namespace Jellyfin.Drawing.Skia
{
/// <summary>
/// Static helper class used to draw percentage-played indicators on images.
/// </summary>
public static class PercentPlayedDrawer
{
private const int IndicatorHeight = 8;
/// <summary>
/// Draw a percentage played indicator on a canvas.
/// </summary>
/// <param name="canvas">The canvas to draw the indicator on.</param>
/// <param name="imageSize">The size of the image being drawn on.</param>
/// <param name="percent">The percentage played to display with the indicator.</param>
public static void Process(SKCanvas canvas, ImageDimensions imageSize, double percent)
{
using (var paint = new SKPaint())

@ -3,10 +3,21 @@ using SkiaSharp;
namespace Jellyfin.Drawing.Skia
{
/// <summary>
/// Static helper class for drawing 'played' indicators.
/// </summary>
public static class PlayedIndicatorDrawer
{
private const int OffsetFromTopRightCorner = 38;
/// <summary>
/// Draw a 'played' indicator in the top right corner of a canvas.
/// </summary>
/// <param name="canvas">The canvas to draw the indicator on.</param>
/// <param name="imageSize">
/// The dimensions of the image to draw the indicator on. The width is used to determine the x-position of the
/// indicator.
/// </param>
public static void DrawPlayedIndicator(SKCanvas canvas, ImageDimensions imageSize)
{
var x = imageSize.Width - OffsetFromTopRightCorner;
@ -26,10 +37,10 @@ namespace Jellyfin.Drawing.Skia
paint.TextSize = 30;
paint.IsAntialias = true;
// or:
// var emojiChar = 0x1F680;
var text = "✔️";
var emojiChar = StringUtilities.GetUnicodeCharacterCode(text, SKTextEncoding.Utf32);
// or:
//var emojiChar = 0x1F680;
// ask the font manager for a font with that character
var fontManager = SKFontManager.Default;

@ -1,3 +1,4 @@
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using SkiaSharp;
@ -8,16 +9,10 @@ namespace Jellyfin.Drawing.Skia
/// </summary>
public class SkiaCodecException : SkiaException
{
/// <summary>
/// Returns the non-successfull codec result returned by Skia.
/// </summary>
/// <value>The non-successfull codec result returned by Skia.</value>
public SKCodecResult CodecResult { get; }
/// <summary>
/// Initializes a new instance of the <see cref="SkiaCodecException" /> class.
/// </summary>
/// <param name="result">The non-successfull codec result returned by Skia.</param>
/// <param name="result">The non-successful codec result returned by Skia.</param>
public SkiaCodecException(SKCodecResult result) : base()
{
CodecResult = result;
@ -27,7 +22,7 @@ namespace Jellyfin.Drawing.Skia
/// Initializes a new instance of the <see cref="SkiaCodecException" /> class
/// with a specified error message.
/// </summary>
/// <param name="result">The non-successfull codec result returned by Skia.</param>
/// <param name="result">The non-successful codec result returned by Skia.</param>
/// <param name="message">The message that describes the error.</param>
public SkiaCodecException(SKCodecResult result, string message)
: base(message)
@ -35,6 +30,11 @@ namespace Jellyfin.Drawing.Skia
CodecResult = result;
}
/// <summary>
/// Gets the non-successful codec result returned by Skia.
/// </summary>
public SKCodecResult CodecResult { get; }
/// <inheritdoc />
public override string ToString()
=> string.Format(

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

Loading…
Cancel
Save