Merge branch 'master' into keyframe_extraction_v1
This commit is contained in:
commit
30f3be1da0
|
@ -7,7 +7,7 @@ parameters:
|
||||||
default: "ubuntu-latest"
|
default: "ubuntu-latest"
|
||||||
- name: DotNetSdkVersion
|
- name: DotNetSdkVersion
|
||||||
type: string
|
type: string
|
||||||
default: 5.0.302
|
default: 6.0.x
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
- job: CompatibilityCheck
|
- job: CompatibilityCheck
|
||||||
|
@ -34,6 +34,7 @@ jobs:
|
||||||
inputs:
|
inputs:
|
||||||
packageType: sdk
|
packageType: sdk
|
||||||
version: ${{ parameters.DotNetSdkVersion }}
|
version: ${{ parameters.DotNetSdkVersion }}
|
||||||
|
includePreviewVersions: true
|
||||||
|
|
||||||
- task: DotNetCoreCLI@2
|
- task: DotNetCoreCLI@2
|
||||||
displayName: 'Install ABI CompatibilityChecker Tool'
|
displayName: 'Install ABI CompatibilityChecker Tool'
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
parameters:
|
parameters:
|
||||||
LinuxImage: 'ubuntu-latest'
|
LinuxImage: 'ubuntu-latest'
|
||||||
RestoreBuildProjects: 'Jellyfin.Server/Jellyfin.Server.csproj'
|
RestoreBuildProjects: 'Jellyfin.Server/Jellyfin.Server.csproj'
|
||||||
DotNetSdkVersion: 5.0.302
|
DotNetSdkVersion: 6.0.x
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
- job: Build
|
- job: Build
|
||||||
|
@ -54,6 +54,7 @@ jobs:
|
||||||
inputs:
|
inputs:
|
||||||
packageType: sdk
|
packageType: sdk
|
||||||
version: ${{ parameters.DotNetSdkVersion }}
|
version: ${{ parameters.DotNetSdkVersion }}
|
||||||
|
includePreviewVersions: true
|
||||||
|
|
||||||
- task: DotNetCoreCLI@2
|
- task: DotNetCoreCLI@2
|
||||||
displayName: 'Publish Server'
|
displayName: 'Publish Server'
|
||||||
|
|
|
@ -195,10 +195,11 @@ jobs:
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- task: UseDotNet@2
|
- task: UseDotNet@2
|
||||||
displayName: 'Use .NET 5.0 sdk'
|
displayName: 'Use .NET 6.0 sdk'
|
||||||
inputs:
|
inputs:
|
||||||
packageType: 'sdk'
|
packageType: 'sdk'
|
||||||
version: '5.0.x'
|
version: '6.0.x'
|
||||||
|
includePreviewVersions: true
|
||||||
|
|
||||||
- task: DotNetCoreCLI@2
|
- task: DotNetCoreCLI@2
|
||||||
displayName: 'Build Stable Nuget packages'
|
displayName: 'Build Stable Nuget packages'
|
||||||
|
|
|
@ -10,7 +10,7 @@ parameters:
|
||||||
default: "tests/**/*Tests.csproj"
|
default: "tests/**/*Tests.csproj"
|
||||||
- name: DotNetSdkVersion
|
- name: DotNetSdkVersion
|
||||||
type: string
|
type: string
|
||||||
default: 5.0.302
|
default: 6.0.x
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
- job: Test
|
- job: Test
|
||||||
|
@ -41,6 +41,7 @@ jobs:
|
||||||
inputs:
|
inputs:
|
||||||
packageType: sdk
|
packageType: sdk
|
||||||
version: ${{ parameters.DotNetSdkVersion }}
|
version: ${{ parameters.DotNetSdkVersion }}
|
||||||
|
includePreviewVersions: true
|
||||||
|
|
||||||
- task: SonarCloudPrepare@1
|
- task: SonarCloudPrepare@1
|
||||||
displayName: 'Prepare analysis on SonarCloud'
|
displayName: 'Prepare analysis on SonarCloud'
|
||||||
|
@ -94,5 +95,5 @@ jobs:
|
||||||
displayName: 'Publish OpenAPI Artifact'
|
displayName: 'Publish OpenAPI Artifact'
|
||||||
condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux'))
|
condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux'))
|
||||||
inputs:
|
inputs:
|
||||||
targetPath: "tests/Jellyfin.Server.Integration.Tests/bin/Release/net5.0/openapi.json"
|
targetPath: "tests/Jellyfin.Server.Integration.Tests/bin/Release/net6.0/openapi.json"
|
||||||
artifactName: 'OpenAPI Spec'
|
artifactName: 'OpenAPI Spec'
|
||||||
|
|
|
@ -5,8 +5,6 @@ variables:
|
||||||
value: 'tests/**/*Tests.csproj'
|
value: 'tests/**/*Tests.csproj'
|
||||||
- name: RestoreBuildProjects
|
- name: RestoreBuildProjects
|
||||||
value: 'Jellyfin.Server/Jellyfin.Server.csproj'
|
value: 'Jellyfin.Server/Jellyfin.Server.csproj'
|
||||||
- name: DotNetSdkVersion
|
|
||||||
value: 5.0.302
|
|
||||||
|
|
||||||
pr:
|
pr:
|
||||||
autoCancel: true
|
autoCancel: true
|
||||||
|
|
4
.github/workflows/codeql-analysis.yml
vendored
4
.github/workflows/codeql-analysis.yml
vendored
|
@ -24,7 +24,9 @@ jobs:
|
||||||
- name: Setup .NET Core
|
- name: Setup .NET Core
|
||||||
uses: actions/setup-dotnet@v1
|
uses: actions/setup-dotnet@v1
|
||||||
with:
|
with:
|
||||||
dotnet-version: '5.0.x'
|
dotnet-version: '6.0.x'
|
||||||
|
include-prerelease: true
|
||||||
|
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@v1
|
uses: github/codeql-action/init@v1
|
||||||
with:
|
with:
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
#####################################
|
#####################################
|
||||||
# Requires binfm_misc registration
|
# Requires binfm_misc registration
|
||||||
# https://github.com/multiarch/qemu-user-static#binfmt_misc-register
|
# https://github.com/multiarch/qemu-user-static#binfmt_misc-register
|
||||||
ARG DOTNET_VERSION=5.0
|
ARG DOTNET_VERSION=6.0
|
||||||
|
|
||||||
FROM node:lts-alpine as web-builder
|
FROM node:lts-alpine as web-builder
|
||||||
ARG JELLYFIN_WEB_VERSION=master
|
ARG JELLYFIN_WEB_VERSION=master
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
#####################################
|
#####################################
|
||||||
# Requires binfm_misc registration
|
# Requires binfm_misc registration
|
||||||
# https://github.com/multiarch/qemu-user-static#binfmt_misc-register
|
# https://github.com/multiarch/qemu-user-static#binfmt_misc-register
|
||||||
ARG DOTNET_VERSION=5.0
|
ARG DOTNET_VERSION=6.0
|
||||||
|
|
||||||
|
|
||||||
FROM node:lts-alpine as web-builder
|
FROM node:lts-alpine as web-builder
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
#####################################
|
#####################################
|
||||||
# Requires binfm_misc registration
|
# Requires binfm_misc registration
|
||||||
# https://github.com/multiarch/qemu-user-static#binfmt_misc-register
|
# https://github.com/multiarch/qemu-user-static#binfmt_misc-register
|
||||||
ARG DOTNET_VERSION=5.0
|
ARG DOTNET_VERSION=6.0
|
||||||
|
|
||||||
|
|
||||||
FROM node:lts-alpine as web-builder
|
FROM node:lts-alpine as web-builder
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net5.0</TargetFramework>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
<AnalysisMode>AllDisabledByDefault</AnalysisMode>
|
<AnalysisMode>AllDisabledByDefault</AnalysisMode>
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
|
@ -76,7 +77,7 @@ namespace DvdLib.Ifo
|
||||||
|
|
||||||
private void ReadVTS(ushort vtsNum, IReadOnlyList<FileInfo> allFiles)
|
private void ReadVTS(ushort vtsNum, IReadOnlyList<FileInfo> allFiles)
|
||||||
{
|
{
|
||||||
var filename = string.Format("VTS_{0:00}_0.IFO", vtsNum);
|
var filename = string.Format(CultureInfo.InvariantCulture, "VTS_{0:00}_0.IFO", vtsNum);
|
||||||
|
|
||||||
var vtsPath = allFiles.FirstOrDefault(i => string.Equals(i.Name, filename, StringComparison.OrdinalIgnoreCase)) ??
|
var vtsPath = allFiles.FirstOrDefault(i => string.Equals(i.Name, filename, StringComparison.OrdinalIgnoreCase)) ??
|
||||||
allFiles.FirstOrDefault(i => string.Equals(i.Name, Path.ChangeExtension(filename, ".bup"), StringComparison.OrdinalIgnoreCase));
|
allFiles.FirstOrDefault(i => string.Equals(i.Name, Path.ChangeExtension(filename, ".bup"), StringComparison.OrdinalIgnoreCase));
|
||||||
|
|
|
@ -366,7 +366,7 @@ namespace Emby.Dlna
|
||||||
Directory.CreateDirectory(systemProfilesPath);
|
Directory.CreateDirectory(systemProfilesPath);
|
||||||
|
|
||||||
// use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 .
|
// use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 .
|
||||||
using (var fileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, AsyncFile.UseAsyncIO))
|
using (var fileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous))
|
||||||
{
|
{
|
||||||
await stream.CopyToAsync(fileStream).ConfigureAwait(false);
|
await stream.CopyToAsync(fileStream).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
@ -486,18 +486,22 @@ namespace Emby.Dlna
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public ImageStream GetIcon(string filename)
|
public ImageStream? GetIcon(string filename)
|
||||||
{
|
{
|
||||||
var format = filename.EndsWith(".png", StringComparison.OrdinalIgnoreCase)
|
var format = filename.EndsWith(".png", StringComparison.OrdinalIgnoreCase)
|
||||||
? ImageFormat.Png
|
? ImageFormat.Png
|
||||||
: ImageFormat.Jpg;
|
: ImageFormat.Jpg;
|
||||||
|
|
||||||
var resource = GetType().Namespace + ".Images." + filename.ToLowerInvariant();
|
var resource = GetType().Namespace + ".Images." + filename.ToLowerInvariant();
|
||||||
|
var stream = _assembly.GetManifestResourceStream(resource);
|
||||||
return new ImageStream
|
if (stream == null)
|
||||||
{
|
{
|
||||||
Format = format,
|
return null;
|
||||||
Stream = _assembly.GetManifestResourceStream(resource)
|
}
|
||||||
|
|
||||||
|
return new ImageStream(stream)
|
||||||
|
{
|
||||||
|
Format = format
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net5.0</TargetFramework>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
<AnalysisMode>AllDisabledByDefault</AnalysisMode>
|
<AnalysisMode>AllDisabledByDefault</AnalysisMode>
|
||||||
|
|
|
@ -250,8 +250,7 @@ namespace Emby.Dlna.Server
|
||||||
|
|
||||||
url = _serverAddress.TrimEnd('/') + "/dlna/" + _serverUdn + "/" + url.TrimStart('/');
|
url = _serverAddress.TrimEnd('/') + "/dlna/" + _serverUdn + "/" + url.TrimStart('/');
|
||||||
|
|
||||||
// TODO: @bond remove null-coalescing operator when https://github.com/dotnet/runtime/pull/52442 is merged/released
|
return SecurityElement.Escape(url);
|
||||||
return SecurityElement.Escape(url) ?? string.Empty;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private IEnumerable<DeviceIcon> GetIcons()
|
private IEnumerable<DeviceIcon> GetIcons()
|
||||||
|
|
|
@ -23,14 +23,14 @@ namespace Emby.Dlna.Service
|
||||||
return EventManager.CancelEventSubscription(subscriptionId);
|
return EventManager.CancelEventSubscription(subscriptionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public EventSubscriptionResponse RenewEventSubscription(string subscriptionId, string notificationType, string timeoutString, string callbackUrl)
|
public EventSubscriptionResponse RenewEventSubscription(string subscriptionId, string notificationType, string requestedTimeoutString, string callbackUrl)
|
||||||
{
|
{
|
||||||
return EventManager.RenewEventSubscription(subscriptionId, notificationType, timeoutString, callbackUrl);
|
return EventManager.RenewEventSubscription(subscriptionId, notificationType, requestedTimeoutString, callbackUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
public EventSubscriptionResponse CreateEventSubscription(string notificationType, string timeoutString, string callbackUrl)
|
public EventSubscriptionResponse CreateEventSubscription(string notificationType, string requestedTimeoutString, string callbackUrl)
|
||||||
{
|
{
|
||||||
return EventManager.CreateEventSubscription(notificationType, timeoutString, callbackUrl);
|
return EventManager.CreateEventSubscription(notificationType, requestedTimeoutString, callbackUrl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net5.0</TargetFramework>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
<AnalysisMode>AllDisabledByDefault</AnalysisMode>
|
<AnalysisMode>AllDisabledByDefault</AnalysisMode>
|
||||||
|
|
|
@ -102,7 +102,7 @@ namespace Emby.Drawing
|
||||||
{
|
{
|
||||||
var file = await ProcessImage(options).ConfigureAwait(false);
|
var file = await ProcessImage(options).ConfigureAwait(false);
|
||||||
|
|
||||||
using (var fileStream = new FileStream(file.Item1, FileMode.Open, FileAccess.Read, FileShare.Read, IODefaults.FileStreamBufferSize, AsyncFile.UseAsyncIO))
|
using (var fileStream = new FileStream(file.Item1, FileMode.Open, FileAccess.Read, FileShare.Read, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous))
|
||||||
{
|
{
|
||||||
await fileStream.CopyToAsync(toStream).ConfigureAwait(false);
|
await fileStream.CopyToAsync(toStream).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net5.0</TargetFramework>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net5.0</TargetFramework>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net5.0</TargetFramework>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
|
@ -1150,7 +1150,7 @@ namespace Emby.Server.Implementations.Data
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Enum.TryParse(imageType.ToString(), true, out ImageType type))
|
if (Enum.TryParse(imageType, true, out ImageType type))
|
||||||
{
|
{
|
||||||
image.Type = type;
|
image.Type = type;
|
||||||
}
|
}
|
||||||
|
@ -1571,7 +1571,6 @@ namespace Emby.Server.Implementations.Data
|
||||||
|
|
||||||
if (reader.TryGetString(index++, out var audioString))
|
if (reader.TryGetString(index++, out var audioString))
|
||||||
{
|
{
|
||||||
// TODO Span overload coming in the future https://github.com/dotnet/runtime/issues/1916
|
|
||||||
if (Enum.TryParse(audioString, true, out ProgramAudio audio))
|
if (Enum.TryParse(audioString, true, out ProgramAudio audio))
|
||||||
{
|
{
|
||||||
item.Audio = audio;
|
item.Audio = audio;
|
||||||
|
@ -1610,18 +1609,16 @@ namespace Emby.Server.Implementations.Data
|
||||||
{
|
{
|
||||||
if (reader.TryGetString(index++, out var lockedFields))
|
if (reader.TryGetString(index++, out var lockedFields))
|
||||||
{
|
{
|
||||||
IEnumerable<MetadataField> GetLockedFields(string s)
|
List<MetadataField> fields = null;
|
||||||
{
|
foreach (var i in lockedFields.AsSpan().Split('|'))
|
||||||
foreach (var i in s.Split('|', StringSplitOptions.RemoveEmptyEntries))
|
|
||||||
{
|
{
|
||||||
if (Enum.TryParse(i, true, out MetadataField parsedValue))
|
if (Enum.TryParse(i, true, out MetadataField parsedValue))
|
||||||
{
|
{
|
||||||
yield return parsedValue;
|
(fields ??= new List<MetadataField>()).Add(parsedValue);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
item.LockedFields = GetLockedFields(lockedFields).ToArray();
|
item.LockedFields = fields?.ToArray() ?? Array.Empty<MetadataField>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1647,18 +1644,16 @@ namespace Emby.Server.Implementations.Data
|
||||||
{
|
{
|
||||||
if (reader.TryGetString(index, out var trailerTypes))
|
if (reader.TryGetString(index, out var trailerTypes))
|
||||||
{
|
{
|
||||||
IEnumerable<TrailerType> GetTrailerTypes(string s)
|
List<TrailerType> types = null;
|
||||||
{
|
foreach (var i in trailerTypes.AsSpan().Split('|'))
|
||||||
foreach (var i in s.Split('|', StringSplitOptions.RemoveEmptyEntries))
|
|
||||||
{
|
{
|
||||||
if (Enum.TryParse(i, true, out TrailerType parsedValue))
|
if (Enum.TryParse(i, true, out TrailerType parsedValue))
|
||||||
{
|
{
|
||||||
yield return parsedValue;
|
(types ??= new List<TrailerType>()).Add(parsedValue);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
trailer.TrailerTypes = GetTrailerTypes(trailerTypes).ToArray();
|
trailer.TrailerTypes = types?.ToArray() ?? Array.Empty<TrailerType>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,7 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net5.0</TargetFramework>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
<!-- https://github.com/microsoft/ApplicationInsights-dotnet/issues/2047 -->
|
<!-- https://github.com/microsoft/ApplicationInsights-dotnet/issues/2047 -->
|
||||||
|
|
|
@ -246,9 +246,9 @@ namespace Emby.Server.Implementations.IO
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using (Stream thisFileStream = new FileStream(fileInfo.FullName, FileMode.Open, FileAccess.Read, FileShare.Read, 1))
|
using (var fileHandle = File.OpenHandle(fileInfo.FullName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
||||||
{
|
{
|
||||||
result.Length = thisFileStream.Length;
|
result.Length = RandomAccess.GetLength(fileHandle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (FileNotFoundException ex)
|
catch (FileNotFoundException ex)
|
||||||
|
|
|
@ -1250,10 +1250,8 @@ namespace Emby.Server.Implementations.Library
|
||||||
private CollectionTypeOptions? GetCollectionType(string path)
|
private CollectionTypeOptions? GetCollectionType(string path)
|
||||||
{
|
{
|
||||||
var files = _fileSystem.GetFilePaths(path, new[] { ".collection" }, true, false);
|
var files = _fileSystem.GetFilePaths(path, new[] { ".collection" }, true, false);
|
||||||
foreach (var file in files)
|
foreach (ReadOnlySpan<char> file in files)
|
||||||
{
|
{
|
||||||
// TODO: @bond use a ReadOnlySpan<char> here when Enum.TryParse supports it
|
|
||||||
// https://github.com/dotnet/runtime/issues/20008
|
|
||||||
if (Enum.TryParse<CollectionTypeOptions>(Path.GetFileNameWithoutExtension(file), true, out var res))
|
if (Enum.TryParse<CollectionTypeOptions>(Path.GetFileNameWithoutExtension(file), true, out var res))
|
||||||
{
|
{
|
||||||
return res;
|
return res;
|
||||||
|
|
|
@ -10,9 +10,9 @@ using System.Linq;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Jellyfin.Extensions.Json;
|
||||||
using MediaBrowser.Common.Configuration;
|
using MediaBrowser.Common.Configuration;
|
||||||
using MediaBrowser.Common.Extensions;
|
using MediaBrowser.Common.Extensions;
|
||||||
using Jellyfin.Extensions.Json;
|
|
||||||
using MediaBrowser.Controller.MediaEncoding;
|
using MediaBrowser.Controller.MediaEncoding;
|
||||||
using MediaBrowser.Model.Dlna;
|
using MediaBrowser.Model.Dlna;
|
||||||
using MediaBrowser.Model.Dto;
|
using MediaBrowser.Model.Dto;
|
||||||
|
|
|
@ -47,7 +47,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(targetFile) ?? throw new ArgumentException("Path can't be a root directory.", nameof(targetFile)));
|
Directory.CreateDirectory(Path.GetDirectoryName(targetFile) ?? throw new ArgumentException("Path can't be a root directory.", nameof(targetFile)));
|
||||||
|
|
||||||
// use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 .
|
// use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 .
|
||||||
using (var output = new FileStream(targetFile, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, AsyncFile.UseAsyncIO))
|
using (var output = new FileStream(targetFile, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous))
|
||||||
{
|
{
|
||||||
onStarted();
|
onStarted();
|
||||||
|
|
||||||
|
@ -80,7 +80,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(targetFile) ?? throw new ArgumentException("Path can't be a root directory.", nameof(targetFile)));
|
Directory.CreateDirectory(Path.GetDirectoryName(targetFile) ?? throw new ArgumentException("Path can't be a root directory.", nameof(targetFile)));
|
||||||
|
|
||||||
// use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 .
|
// use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 .
|
||||||
await using var output = new FileStream(targetFile, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.CopyToBufferSize, AsyncFile.UseAsyncIO);
|
await using var output = new FileStream(targetFile, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.CopyToBufferSize, FileOptions.Asynchronous);
|
||||||
|
|
||||||
onStarted();
|
onStarted();
|
||||||
|
|
||||||
|
|
|
@ -94,7 +94,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(logFilePath));
|
Directory.CreateDirectory(Path.GetDirectoryName(logFilePath));
|
||||||
|
|
||||||
// FFMpeg writes debug/error info to stderr. This is useful when debugging so let's put it in the log directory.
|
// FFMpeg writes debug/error info to stderr. This is useful when debugging so let's put it in the log directory.
|
||||||
_logFileStream = new FileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, AsyncFile.UseAsyncIO);
|
_logFileStream = new FileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous);
|
||||||
|
|
||||||
await JsonSerializer.SerializeAsync(_logFileStream, mediaSource, _jsonOptions, cancellationToken).ConfigureAwait(false);
|
await JsonSerializer.SerializeAsync(_logFileStream, mediaSource, _jsonOptions, cancellationToken).ConfigureAwait(false);
|
||||||
await _logFileStream.WriteAsync(Encoding.UTF8.GetBytes(Environment.NewLine + Environment.NewLine + commandLineLogMessage + Environment.NewLine + Environment.NewLine), cancellationToken).ConfigureAwait(false);
|
await _logFileStream.WriteAsync(Encoding.UTF8.GetBytes(Environment.NewLine + Environment.NewLine + commandLineLogMessage + Environment.NewLine + Environment.NewLine), cancellationToken).ConfigureAwait(false);
|
||||||
|
|
|
@ -82,7 +82,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
||||||
|
|
||||||
using var response = await _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(path, cancellationToken).ConfigureAwait(false);
|
using var response = await _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(path, cancellationToken).ConfigureAwait(false);
|
||||||
await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
|
await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
|
||||||
await using (var fileStream = new FileStream(cacheFile, FileMode.CreateNew, FileAccess.Write, FileShare.None, IODefaults.CopyToBufferSize, AsyncFile.UseAsyncIO))
|
await using (var fileStream = new FileStream(cacheFile, FileMode.CreateNew, FileAccess.Write, FileShare.None, IODefaults.CopyToBufferSize, FileOptions.Asynchronous))
|
||||||
{
|
{
|
||||||
await stream.CopyToAsync(fileStream, cancellationToken).ConfigureAwait(false);
|
await stream.CopyToAsync(fileStream, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,10 +23,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
|
||||||
{
|
{
|
||||||
public abstract class BaseTunerHost
|
public abstract class BaseTunerHost
|
||||||
{
|
{
|
||||||
protected readonly IServerConfigurationManager Config;
|
|
||||||
protected readonly ILogger<BaseTunerHost> Logger;
|
|
||||||
protected readonly IFileSystem FileSystem;
|
|
||||||
|
|
||||||
private readonly IMemoryCache _memoryCache;
|
private readonly IMemoryCache _memoryCache;
|
||||||
|
|
||||||
protected BaseTunerHost(IServerConfigurationManager config, ILogger<BaseTunerHost> logger, IFileSystem fileSystem, IMemoryCache memoryCache)
|
protected BaseTunerHost(IServerConfigurationManager config, ILogger<BaseTunerHost> logger, IFileSystem fileSystem, IMemoryCache memoryCache)
|
||||||
|
@ -37,12 +33,20 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
|
||||||
FileSystem = fileSystem;
|
FileSystem = fileSystem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected IServerConfigurationManager Config { get; }
|
||||||
|
|
||||||
|
protected ILogger<BaseTunerHost> Logger { get; }
|
||||||
|
|
||||||
|
protected IFileSystem FileSystem { get; }
|
||||||
|
|
||||||
public virtual bool IsSupported => true;
|
public virtual bool IsSupported => true;
|
||||||
|
|
||||||
protected abstract Task<List<ChannelInfo>> GetChannelsInternal(TunerHostInfo tuner, CancellationToken cancellationToken);
|
|
||||||
|
|
||||||
public abstract string Type { get; }
|
public abstract string Type { get; }
|
||||||
|
|
||||||
|
protected virtual string ChannelIdPrefix => Type + "_";
|
||||||
|
|
||||||
|
protected abstract Task<List<ChannelInfo>> GetChannelsInternal(TunerHostInfo tuner, CancellationToken cancellationToken);
|
||||||
|
|
||||||
public async Task<List<ChannelInfo>> GetChannels(TunerHostInfo tuner, bool enableCache, CancellationToken cancellationToken)
|
public async Task<List<ChannelInfo>> GetChannels(TunerHostInfo tuner, bool enableCache, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var key = tuner.Id;
|
var key = tuner.Id;
|
||||||
|
@ -217,8 +221,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
|
||||||
throw new LiveTvConflictException();
|
throw new LiveTvConflictException();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual string ChannelIdPrefix => Type + "_";
|
|
||||||
|
|
||||||
protected virtual bool IsValidChannelId(string channelId)
|
protected virtual bool IsValidChannelId(string channelId)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(channelId))
|
if (string.IsNullOrEmpty(channelId))
|
||||||
|
|
|
@ -101,7 +101,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (localAddress.IsIPv4MappedToIPv6) {
|
if (localAddress.IsIPv4MappedToIPv6)
|
||||||
|
{
|
||||||
localAddress = localAddress.MapToIPv4();
|
localAddress = localAddress.MapToIPv4();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -196,7 +197,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
||||||
cancellationToken,
|
cancellationToken,
|
||||||
timeOutSource.Token))
|
timeOutSource.Token))
|
||||||
{
|
{
|
||||||
var resTask = udpClient.ReceiveAsync();
|
var resTask = udpClient.ReceiveAsync(linkedSource.Token).AsTask();
|
||||||
if (await Task.WhenAny(resTask, Task.Delay(30000, linkedSource.Token)).ConfigureAwait(false) != resTask)
|
if (await Task.WhenAny(resTask, Task.Delay(30000, linkedSource.Token)).ConfigureAwait(false) != resTask)
|
||||||
{
|
{
|
||||||
resTask.Dispose();
|
resTask.Dispose();
|
||||||
|
|
|
@ -20,14 +20,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
|
||||||
{
|
{
|
||||||
private readonly IConfigurationManager _configurationManager;
|
private readonly IConfigurationManager _configurationManager;
|
||||||
|
|
||||||
protected readonly IFileSystem FileSystem;
|
|
||||||
|
|
||||||
protected readonly IStreamHelper StreamHelper;
|
|
||||||
|
|
||||||
protected string TempFilePath;
|
|
||||||
protected readonly ILogger Logger;
|
|
||||||
protected readonly CancellationTokenSource LiveStreamCancellationTokenSource = new CancellationTokenSource();
|
|
||||||
|
|
||||||
public LiveStream(
|
public LiveStream(
|
||||||
MediaSourceInfo mediaSource,
|
MediaSourceInfo mediaSource,
|
||||||
TunerHostInfo tuner,
|
TunerHostInfo tuner,
|
||||||
|
@ -55,7 +47,15 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
|
||||||
SetTempFilePath("ts");
|
SetTempFilePath("ts");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual int EmptyReadLimit => 1000;
|
protected IFileSystem FileSystem { get; }
|
||||||
|
|
||||||
|
protected IStreamHelper StreamHelper { get; }
|
||||||
|
|
||||||
|
protected ILogger Logger { get; }
|
||||||
|
|
||||||
|
protected CancellationTokenSource LiveStreamCancellationTokenSource { get; } = new CancellationTokenSource();
|
||||||
|
|
||||||
|
protected string TempFilePath { get; set; }
|
||||||
|
|
||||||
public MediaSourceInfo OriginalMediaSource { get; set; }
|
public MediaSourceInfo OriginalMediaSource { get; set; }
|
||||||
|
|
||||||
|
@ -97,7 +97,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
|
||||||
|
|
||||||
public Stream GetStream()
|
public Stream GetStream()
|
||||||
{
|
{
|
||||||
var stream = GetInputStream(TempFilePath, AsyncFile.UseAsyncIO);
|
var stream = GetInputStream(TempFilePath);
|
||||||
bool seekFile = (DateTime.UtcNow - DateOpened).TotalSeconds > 10;
|
bool seekFile = (DateTime.UtcNow - DateOpened).TotalSeconds > 10;
|
||||||
if (seekFile)
|
if (seekFile)
|
||||||
{
|
{
|
||||||
|
@ -107,14 +107,14 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
|
||||||
return stream;
|
return stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected FileStream GetInputStream(string path, bool allowAsyncFileRead)
|
protected FileStream GetInputStream(string path)
|
||||||
=> new FileStream(
|
=> new FileStream(
|
||||||
path,
|
path,
|
||||||
FileMode.Open,
|
FileMode.Open,
|
||||||
FileAccess.Read,
|
FileAccess.Read,
|
||||||
FileShare.ReadWrite,
|
FileShare.ReadWrite,
|
||||||
IODefaults.FileStreamBufferSize,
|
IODefaults.FileStreamBufferSize,
|
||||||
allowAsyncFileRead ? FileOptions.SequentialScan | FileOptions.Asynchronous : FileOptions.SequentialScan);
|
FileOptions.SequentialScan | FileOptions.Asynchronous);
|
||||||
|
|
||||||
protected async Task DeleteTempFiles(string path, int retryCount = 0)
|
protected async Task DeleteTempFiles(string path, int retryCount = 0)
|
||||||
{
|
{
|
||||||
|
|
|
@ -118,7 +118,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
|
||||||
Logger.LogInformation("Beginning {StreamType} stream to {FilePath}", GetType().Name, TempFilePath);
|
Logger.LogInformation("Beginning {StreamType} stream to {FilePath}", GetType().Name, TempFilePath);
|
||||||
using var message = response;
|
using var message = response;
|
||||||
await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
|
await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
|
||||||
await using var fileStream = new FileStream(TempFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, AsyncFile.UseAsyncIO);
|
await using var fileStream = new FileStream(TempFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous);
|
||||||
await StreamHelper.CopyToAsync(
|
await StreamHelper.CopyToAsync(
|
||||||
stream,
|
stream,
|
||||||
fileStream,
|
fileStream,
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"Albums": "البومات",
|
"Albums": "ألبومات",
|
||||||
"AppDeviceValues": "تطبيق: {0}, جهاز: {1}",
|
"AppDeviceValues": "تطبيق: {0}, جهاز: {1}",
|
||||||
"Application": "تطبيق",
|
"Application": "تطبيق",
|
||||||
"Artists": "الفنانين",
|
"Artists": "الفنانين",
|
||||||
|
@ -8,7 +8,7 @@
|
||||||
"CameraImageUploadedFrom": "صورة كاميرا جديدة تم رفعها من {0}",
|
"CameraImageUploadedFrom": "صورة كاميرا جديدة تم رفعها من {0}",
|
||||||
"Channels": "القنوات",
|
"Channels": "القنوات",
|
||||||
"ChapterNameValue": "الفصل {0}",
|
"ChapterNameValue": "الفصل {0}",
|
||||||
"Collections": "مجموعات",
|
"Collections": "التجميعات",
|
||||||
"DeviceOfflineWithName": "قُطِع الاتصال ب{0}",
|
"DeviceOfflineWithName": "قُطِع الاتصال ب{0}",
|
||||||
"DeviceOnlineWithName": "{0} متصل",
|
"DeviceOnlineWithName": "{0} متصل",
|
||||||
"FailedLoginAttemptWithUserName": "عملية تسجيل الدخول فشلت من {0}",
|
"FailedLoginAttemptWithUserName": "عملية تسجيل الدخول فشلت من {0}",
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
"Favorites": "Favoriti",
|
"Favorites": "Favoriti",
|
||||||
"Folders": "Mape",
|
"Folders": "Mape",
|
||||||
"Genres": "Žanrovi",
|
"Genres": "Žanrovi",
|
||||||
"HeaderAlbumArtists": "Izvođači na albumu",
|
"HeaderAlbumArtists": "Album od izvođača",
|
||||||
"HeaderContinueWatching": "Nastavi gledati",
|
"HeaderContinueWatching": "Nastavi gledati",
|
||||||
"HeaderFavoriteAlbums": "Omiljeni albumi",
|
"HeaderFavoriteAlbums": "Omiljeni albumi",
|
||||||
"HeaderFavoriteArtists": "Omiljeni izvođači",
|
"HeaderFavoriteArtists": "Omiljeni izvođači",
|
||||||
|
|
|
@ -17,6 +17,15 @@ namespace Emby.Server.Implementations.Playlists
|
||||||
Name = "Playlists";
|
Name = "Playlists";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public override bool IsHidden => true;
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public override bool SupportsInheritedParentImages => false;
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public override string CollectionType => MediaBrowser.Model.Entities.CollectionType.Playlists;
|
||||||
|
|
||||||
public override bool IsVisible(User user)
|
public override bool IsVisible(User user)
|
||||||
{
|
{
|
||||||
return base.IsVisible(user) && GetChildren(user, true).Any();
|
return base.IsVisible(user) && GetChildren(user, true).Any();
|
||||||
|
@ -27,15 +36,6 @@ namespace Emby.Server.Implementations.Playlists
|
||||||
return base.GetEligibleChildrenForRecursiveChildren(user).OfType<Playlist>();
|
return base.GetEligibleChildrenForRecursiveChildren(user).OfType<Playlist>();
|
||||||
}
|
}
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public override bool IsHidden => true;
|
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public override bool SupportsInheritedParentImages => false;
|
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public override string CollectionType => MediaBrowser.Model.Entities.CollectionType.Playlists;
|
|
||||||
|
|
||||||
protected override QueryResult<BaseItem> GetItemsInternal(InternalItemsQuery query)
|
protected override QueryResult<BaseItem> GetItemsInternal(InternalItemsQuery query)
|
||||||
{
|
{
|
||||||
if (query.User == null)
|
if (query.User == null)
|
||||||
|
|
|
@ -8,10 +8,10 @@ using System.Reflection;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MediaBrowser.Common;
|
|
||||||
using MediaBrowser.Common.Extensions;
|
|
||||||
using Jellyfin.Extensions.Json;
|
using Jellyfin.Extensions.Json;
|
||||||
using Jellyfin.Extensions.Json.Converters;
|
using Jellyfin.Extensions.Json.Converters;
|
||||||
|
using MediaBrowser.Common;
|
||||||
|
using MediaBrowser.Common.Extensions;
|
||||||
using MediaBrowser.Common.Net;
|
using MediaBrowser.Common.Net;
|
||||||
using MediaBrowser.Common.Plugins;
|
using MediaBrowser.Common.Plugins;
|
||||||
using MediaBrowser.Model.Configuration;
|
using MediaBrowser.Model.Configuration;
|
||||||
|
@ -39,14 +39,6 @@ namespace Emby.Server.Implementations.Plugins
|
||||||
|
|
||||||
private IHttpClientFactory? _httpClientFactory;
|
private IHttpClientFactory? _httpClientFactory;
|
||||||
|
|
||||||
private IHttpClientFactory HttpClientFactory
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return _httpClientFactory ?? (_httpClientFactory = _appHost.Resolve<IHttpClientFactory>());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="PluginManager"/> class.
|
/// Initializes a new instance of the <see cref="PluginManager"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -86,6 +78,14 @@ namespace Emby.Server.Implementations.Plugins
|
||||||
_plugins = Directory.Exists(_pluginsPath) ? DiscoverPlugins().ToList() : new List<LocalPlugin>();
|
_plugins = Directory.Exists(_pluginsPath) ? DiscoverPlugins().ToList() : new List<LocalPlugin>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private IHttpClientFactory HttpClientFactory
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _httpClientFactory ??= _appHost.Resolve<IHttpClientFactory>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the Plugins.
|
/// Gets the Plugins.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -18,7 +18,7 @@ namespace Emby.Server.Implementations.QuickConnect
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Quick connect implementation.
|
/// Quick connect implementation.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class QuickConnectManager : IQuickConnect, IDisposable
|
public class QuickConnectManager : IQuickConnect
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The length of user facing codes.
|
/// The length of user facing codes.
|
||||||
|
@ -30,7 +30,6 @@ namespace Emby.Server.Implementations.QuickConnect
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private const int Timeout = 10;
|
private const int Timeout = 10;
|
||||||
|
|
||||||
private readonly RNGCryptoServiceProvider _rng = new ();
|
|
||||||
private readonly ConcurrentDictionary<string, QuickConnectResult> _currentRequests = new ();
|
private readonly ConcurrentDictionary<string, QuickConnectResult> _currentRequests = new ();
|
||||||
private readonly ConcurrentDictionary<string, (DateTime Timestamp, AuthenticationResult AuthenticationResult)> _authorizedSecrets = new ();
|
private readonly ConcurrentDictionary<string, (DateTime Timestamp, AuthenticationResult AuthenticationResult)> _authorizedSecrets = new ();
|
||||||
|
|
||||||
|
@ -140,7 +139,7 @@ namespace Emby.Server.Implementations.QuickConnect
|
||||||
uint scale = uint.MaxValue;
|
uint scale = uint.MaxValue;
|
||||||
while (scale == uint.MaxValue)
|
while (scale == uint.MaxValue)
|
||||||
{
|
{
|
||||||
_rng.GetBytes(raw);
|
RandomNumberGenerator.Fill(raw);
|
||||||
scale = BitConverter.ToUInt32(raw);
|
scale = BitConverter.ToUInt32(raw);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -199,31 +198,10 @@ namespace Emby.Server.Implementations.QuickConnect
|
||||||
return result.AuthenticationResult;
|
return result.AuthenticationResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Dispose.
|
|
||||||
/// </summary>
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
Dispose(true);
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Dispose.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="disposing">Dispose unmanaged resources.</param>
|
|
||||||
protected virtual void Dispose(bool disposing)
|
|
||||||
{
|
|
||||||
if (disposing)
|
|
||||||
{
|
|
||||||
_rng.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private string GenerateSecureRandom(int length = 32)
|
private string GenerateSecureRandom(int length = 32)
|
||||||
{
|
{
|
||||||
Span<byte> bytes = stackalloc byte[length];
|
Span<byte> bytes = stackalloc byte[length];
|
||||||
_rng.GetBytes(bytes);
|
RandomNumberGenerator.Fill(bytes);
|
||||||
|
|
||||||
return Convert.ToHexString(bytes);
|
return Convert.ToHexString(bytes);
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,18 +32,18 @@ namespace Jellyfin.Api.Auth.FirstTimeSetupOrDefaultPolicy
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, FirstTimeSetupOrDefaultRequirement firstTimeSetupOrDefaultRequirement)
|
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, FirstTimeSetupOrDefaultRequirement requirement)
|
||||||
{
|
{
|
||||||
if (!_configurationManager.CommonConfiguration.IsStartupWizardCompleted)
|
if (!_configurationManager.CommonConfiguration.IsStartupWizardCompleted)
|
||||||
{
|
{
|
||||||
context.Succeed(firstTimeSetupOrDefaultRequirement);
|
context.Succeed(requirement);
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
var validated = ValidateClaims(context.User);
|
var validated = ValidateClaims(context.User);
|
||||||
if (validated)
|
if (validated)
|
||||||
{
|
{
|
||||||
context.Succeed(firstTimeSetupOrDefaultRequirement);
|
context.Succeed(requirement);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -33,18 +33,18 @@ namespace Jellyfin.Api.Auth.FirstTimeSetupOrElevatedPolicy
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, FirstTimeSetupOrElevatedRequirement firstTimeSetupOrElevatedRequirement)
|
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, FirstTimeSetupOrElevatedRequirement requirement)
|
||||||
{
|
{
|
||||||
if (!_configurationManager.CommonConfiguration.IsStartupWizardCompleted)
|
if (!_configurationManager.CommonConfiguration.IsStartupWizardCompleted)
|
||||||
{
|
{
|
||||||
context.Succeed(firstTimeSetupOrElevatedRequirement);
|
context.Succeed(requirement);
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
var validated = ValidateClaims(context.User);
|
var validated = ValidateClaims(context.User);
|
||||||
if (validated && context.User.IsInRole(UserRoles.Administrator))
|
if (validated && context.User.IsInRole(UserRoles.Administrator))
|
||||||
{
|
{
|
||||||
context.Succeed(firstTimeSetupOrElevatedRequirement);
|
context.Succeed(requirement);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -106,7 +106,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
await using var memoryStream = await GetMemoryStream(Request.Body).ConfigureAwait(false);
|
await using var memoryStream = await GetMemoryStream(Request.Body).ConfigureAwait(false);
|
||||||
|
|
||||||
// Handle image/png; charset=utf-8
|
// Handle image/png; charset=utf-8
|
||||||
var mimeType = Request.ContentType.Split(';').FirstOrDefault();
|
var mimeType = Request.ContentType?.Split(';').FirstOrDefault();
|
||||||
var userDataPath = Path.Combine(_serverConfigurationManager.ApplicationPaths.UserConfigurationDirectoryPath, user.Username);
|
var userDataPath = Path.Combine(_serverConfigurationManager.ApplicationPaths.UserConfigurationDirectoryPath, user.Username);
|
||||||
if (user.ProfileImage != null)
|
if (user.ProfileImage != null)
|
||||||
{
|
{
|
||||||
|
@ -153,7 +153,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
await using var memoryStream = await GetMemoryStream(Request.Body).ConfigureAwait(false);
|
await using var memoryStream = await GetMemoryStream(Request.Body).ConfigureAwait(false);
|
||||||
|
|
||||||
// Handle image/png; charset=utf-8
|
// Handle image/png; charset=utf-8
|
||||||
var mimeType = Request.ContentType.Split(';').FirstOrDefault();
|
var mimeType = Request.ContentType?.Split(';').FirstOrDefault();
|
||||||
var userDataPath = Path.Combine(_serverConfigurationManager.ApplicationPaths.UserConfigurationDirectoryPath, user.Username);
|
var userDataPath = Path.Combine(_serverConfigurationManager.ApplicationPaths.UserConfigurationDirectoryPath, user.Username);
|
||||||
if (user.ProfileImage != null)
|
if (user.ProfileImage != null)
|
||||||
{
|
{
|
||||||
|
@ -341,7 +341,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
await using var memoryStream = await GetMemoryStream(Request.Body).ConfigureAwait(false);
|
await using var memoryStream = await GetMemoryStream(Request.Body).ConfigureAwait(false);
|
||||||
|
|
||||||
// Handle image/png; charset=utf-8
|
// Handle image/png; charset=utf-8
|
||||||
var mimeType = Request.ContentType.Split(';').FirstOrDefault();
|
var mimeType = Request.ContentType?.Split(';').FirstOrDefault();
|
||||||
await _providerManager.SaveImage(item, memoryStream, mimeType, imageType, null, CancellationToken.None).ConfigureAwait(false);
|
await _providerManager.SaveImage(item, memoryStream, mimeType, imageType, null, CancellationToken.None).ConfigureAwait(false);
|
||||||
await item.UpdateToRepositoryAsync(ItemUpdateType.ImageUpdate, CancellationToken.None).ConfigureAwait(false);
|
await item.UpdateToRepositoryAsync(ItemUpdateType.ImageUpdate, CancellationToken.None).ConfigureAwait(false);
|
||||||
|
|
||||||
|
@ -377,7 +377,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
await using var memoryStream = await GetMemoryStream(Request.Body).ConfigureAwait(false);
|
await using var memoryStream = await GetMemoryStream(Request.Body).ConfigureAwait(false);
|
||||||
|
|
||||||
// Handle image/png; charset=utf-8
|
// Handle image/png; charset=utf-8
|
||||||
var mimeType = Request.ContentType.Split(';').FirstOrDefault();
|
var mimeType = Request.ContentType?.Split(';').FirstOrDefault();
|
||||||
await _providerManager.SaveImage(item, memoryStream, mimeType, imageType, null, CancellationToken.None).ConfigureAwait(false);
|
await _providerManager.SaveImage(item, memoryStream, mimeType, imageType, null, CancellationToken.None).ConfigureAwait(false);
|
||||||
await item.UpdateToRepositoryAsync(ItemUpdateType.ImageUpdate, CancellationToken.None).ConfigureAwait(false);
|
await item.UpdateToRepositoryAsync(ItemUpdateType.ImageUpdate, CancellationToken.None).ConfigureAwait(false);
|
||||||
|
|
||||||
|
@ -2026,7 +2026,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
return NoContent();
|
return NoContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
return PhysicalFile(imagePath, imageContentType);
|
return PhysicalFile(imagePath, imageContentType ?? MediaTypeNames.Text.Plain);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -206,7 +206,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
var fullCacheDirectory = Path.GetDirectoryName(fullCachePath) ?? throw new ResourceNotFoundException($"Provided path ({fullCachePath}) is not valid.");
|
var fullCacheDirectory = Path.GetDirectoryName(fullCachePath) ?? throw new ResourceNotFoundException($"Provided path ({fullCachePath}) is not valid.");
|
||||||
Directory.CreateDirectory(fullCacheDirectory);
|
Directory.CreateDirectory(fullCacheDirectory);
|
||||||
// use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 .
|
// use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 .
|
||||||
await using var fileStream = new FileStream(fullCachePath, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, AsyncFile.UseAsyncIO);
|
await using var fileStream = new FileStream(fullCachePath, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous);
|
||||||
await response.Content.CopyToAsync(fileStream).ConfigureAwait(false);
|
await response.Content.CopyToAsync(fileStream).ConfigureAwait(false);
|
||||||
|
|
||||||
var pointerCacheDirectory = Path.GetDirectoryName(pointerCachePath) ?? throw new ArgumentException($"Provided path ({pointerCachePath}) is not valid.", nameof(pointerCachePath));
|
var pointerCacheDirectory = Path.GetDirectoryName(pointerCachePath) ?? throw new ArgumentException($"Provided path ({pointerCachePath}) is not valid.", nameof(pointerCachePath));
|
||||||
|
|
|
@ -201,7 +201,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
|
|
||||||
// For older files, assume fully static
|
// For older files, assume fully static
|
||||||
var fileShare = file.LastWriteTimeUtc < DateTime.UtcNow.AddHours(-1) ? FileShare.Read : FileShare.ReadWrite;
|
var fileShare = file.LastWriteTimeUtc < DateTime.UtcNow.AddHours(-1) ? FileShare.Read : FileShare.ReadWrite;
|
||||||
FileStream stream = new FileStream(file.FullName, FileMode.Open, FileAccess.Read, fileShare, IODefaults.FileStreamBufferSize, AsyncFile.UseAsyncIO);
|
FileStream stream = new FileStream(file.FullName, FileMode.Open, FileAccess.Read, fileShare, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous);
|
||||||
return File(stream, "text/plain; charset=utf-8");
|
return File(stream, "text/plain; charset=utf-8");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
using System.Net.Mime;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Jellyfin.Api.Models.PlaybackDtos;
|
using Jellyfin.Api.Models.PlaybackDtos;
|
||||||
|
@ -40,7 +41,7 @@ namespace Jellyfin.Api.Helpers
|
||||||
|
|
||||||
// Can't dispose the response as it's required up the call chain.
|
// Can't dispose the response as it's required up the call chain.
|
||||||
var response = await httpClient.GetAsync(new Uri(state.MediaPath), cancellationToken).ConfigureAwait(false);
|
var response = await httpClient.GetAsync(new Uri(state.MediaPath), cancellationToken).ConfigureAwait(false);
|
||||||
var contentType = response.Content.Headers.ContentType?.ToString();
|
var contentType = response.Content.Headers.ContentType?.ToString() ?? MediaTypeNames.Text.Plain;
|
||||||
|
|
||||||
httpContext.Response.Headers[HeaderNames.AcceptRanges] = "none";
|
httpContext.Response.Headers[HeaderNames.AcceptRanges] = "none";
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,7 @@ namespace Jellyfin.Api.Helpers
|
||||||
FileAccess.Read,
|
FileAccess.Read,
|
||||||
FileShare.ReadWrite,
|
FileShare.ReadWrite,
|
||||||
IODefaults.FileStreamBufferSize,
|
IODefaults.FileStreamBufferSize,
|
||||||
(AsyncFile.UseAsyncIO ? FileOptions.Asynchronous : FileOptions.None) | FileOptions.SequentialScan);
|
FileOptions.Asynchronous | FileOptions.SequentialScan);
|
||||||
await using (fileStream.ConfigureAwait(false))
|
await using (fileStream.ConfigureAwait(false))
|
||||||
{
|
{
|
||||||
using var reader = new StreamReader(fileStream);
|
using var reader = new StreamReader(fileStream);
|
||||||
|
|
|
@ -17,7 +17,6 @@ namespace Jellyfin.Api.Helpers
|
||||||
private readonly TranscodingJobDto? _job;
|
private readonly TranscodingJobDto? _job;
|
||||||
private readonly TranscodingJobHelper? _transcodingJobHelper;
|
private readonly TranscodingJobHelper? _transcodingJobHelper;
|
||||||
private readonly int _timeoutMs;
|
private readonly int _timeoutMs;
|
||||||
private readonly bool _allowAsyncFileRead;
|
|
||||||
private int _bytesWritten;
|
private int _bytesWritten;
|
||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
|
|
||||||
|
@ -34,17 +33,7 @@ namespace Jellyfin.Api.Helpers
|
||||||
_transcodingJobHelper = transcodingJobHelper;
|
_transcodingJobHelper = transcodingJobHelper;
|
||||||
_timeoutMs = timeoutMs;
|
_timeoutMs = timeoutMs;
|
||||||
|
|
||||||
var fileOptions = FileOptions.SequentialScan;
|
_stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous | FileOptions.SequentialScan);
|
||||||
_allowAsyncFileRead = false;
|
|
||||||
|
|
||||||
// use non-async filestream along with read due to https://github.com/dotnet/corefx/issues/6039
|
|
||||||
if (AsyncFile.UseAsyncIO)
|
|
||||||
{
|
|
||||||
fileOptions |= FileOptions.Asynchronous;
|
|
||||||
_allowAsyncFileRead = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
_stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, IODefaults.FileStreamBufferSize, fileOptions);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -57,7 +46,6 @@ namespace Jellyfin.Api.Helpers
|
||||||
_job = null;
|
_job = null;
|
||||||
_transcodingJobHelper = null;
|
_transcodingJobHelper = null;
|
||||||
_timeoutMs = timeoutMs;
|
_timeoutMs = timeoutMs;
|
||||||
_allowAsyncFileRead = AsyncFile.UseAsyncIO;
|
|
||||||
_stream = stream;
|
_stream = stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,15 +91,7 @@ namespace Jellyfin.Api.Helpers
|
||||||
while (remainingBytesToRead > 0)
|
while (remainingBytesToRead > 0)
|
||||||
{
|
{
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
int bytesRead;
|
int bytesRead = await _stream.ReadAsync(buffer, newOffset, remainingBytesToRead, cancellationToken).ConfigureAwait(false);
|
||||||
if (_allowAsyncFileRead)
|
|
||||||
{
|
|
||||||
bytesRead = await _stream.ReadAsync(buffer, newOffset, remainingBytesToRead, cancellationToken).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
bytesRead = _stream.Read(buffer, newOffset, remainingBytesToRead);
|
|
||||||
}
|
|
||||||
|
|
||||||
remainingBytesToRead -= bytesRead;
|
remainingBytesToRead -= bytesRead;
|
||||||
newOffset += bytesRead;
|
newOffset += bytesRead;
|
||||||
|
|
|
@ -557,7 +557,7 @@ namespace Jellyfin.Api.Helpers
|
||||||
$"{logFilePrefix}{DateTime.Now:yyyy-MM-dd_HH-mm-ss}_{state.Request.MediaSourceId}_{Guid.NewGuid().ToString()[..8]}.log");
|
$"{logFilePrefix}{DateTime.Now:yyyy-MM-dd_HH-mm-ss}_{state.Request.MediaSourceId}_{Guid.NewGuid().ToString()[..8]}.log");
|
||||||
|
|
||||||
// FFmpeg writes debug/error info to stderr. This is useful when debugging so let's put it in the log directory.
|
// FFmpeg writes debug/error info to stderr. This is useful when debugging so let's put it in the log directory.
|
||||||
Stream logStream = new FileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, AsyncFile.UseAsyncIO);
|
Stream logStream = new FileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous);
|
||||||
|
|
||||||
var commandLineLogMessageBytes = Encoding.UTF8.GetBytes(request.Path + Environment.NewLine + Environment.NewLine + JsonSerializer.Serialize(state.MediaSource) + Environment.NewLine + Environment.NewLine + commandLineLogMessage + Environment.NewLine + Environment.NewLine);
|
var commandLineLogMessageBytes = Encoding.UTF8.GetBytes(request.Path + Environment.NewLine + Environment.NewLine + JsonSerializer.Serialize(state.MediaSource) + Environment.NewLine + Environment.NewLine + commandLineLogMessage + Environment.NewLine + Environment.NewLine);
|
||||||
await logStream.WriteAsync(commandLineLogMessageBytes, 0, commandLineLogMessageBytes.Length, cancellationTokenSource.Token).ConfigureAwait(false);
|
await logStream.WriteAsync(commandLineLogMessageBytes, 0, commandLineLogMessageBytes.Length, cancellationTokenSource.Token).ConfigureAwait(false);
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net5.0</TargetFramework>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
<!-- https://github.com/microsoft/ApplicationInsights-dotnet/issues/2047 -->
|
<!-- https://github.com/microsoft/ApplicationInsights-dotnet/issues/2047 -->
|
||||||
<NoWarn>AD0001</NoWarn>
|
<NoWarn>AD0001</NoWarn>
|
||||||
|
|
|
@ -32,7 +32,8 @@ namespace Jellyfin.Api.ModelBinders
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var convertedValue = converter.ConvertFromString(valueProviderResult.FirstValue);
|
// REVIEW: This shouldn't be null here
|
||||||
|
var convertedValue = converter.ConvertFromString(valueProviderResult.FirstValue!);
|
||||||
bindingContext.Result = ModelBindingResult.Success(convertedValue);
|
bindingContext.Result = ModelBindingResult.Success(convertedValue);
|
||||||
}
|
}
|
||||||
catch (FormatException e)
|
catch (FormatException e)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net5.0</TargetFramework>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net5.0</TargetFramework>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
@ -28,11 +28,6 @@
|
||||||
<ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj" />
|
<ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<!-- Needed for https://github.com/dotnet/roslyn-analyzers/issues/4382 which is in the SDK yet -->
|
|
||||||
<PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="5.0.3" PrivateAssets="All" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<!-- Code analysers-->
|
<!-- Code analysers-->
|
||||||
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
|
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||||
<PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" />
|
<PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" />
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net5.0</TargetFramework>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net5.0</TargetFramework>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
|
@ -23,7 +23,7 @@ namespace Jellyfin.Server.Configuration
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public Task<CorsPolicy> GetPolicyAsync(HttpContext context, string policyName)
|
public Task<CorsPolicy?> GetPolicyAsync(HttpContext context, string? policyName)
|
||||||
{
|
{
|
||||||
var corsHosts = _serverConfigurationManager.Configuration.CorsHosts;
|
var corsHosts = _serverConfigurationManager.Configuration.CorsHosts;
|
||||||
var builder = new CorsPolicyBuilder()
|
var builder = new CorsPolicyBuilder()
|
||||||
|
@ -43,7 +43,7 @@ namespace Jellyfin.Server.Configuration
|
||||||
.AllowCredentials();
|
.AllowCredentials();
|
||||||
}
|
}
|
||||||
|
|
||||||
return Task.FromResult(builder.Build());
|
return Task.FromResult<CorsPolicy?>(builder.Build());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,8 +55,8 @@ namespace Jellyfin.Server.Infrastructure
|
||||||
// This may or may not be fixed in .NET 6, but looks like it will not https://github.com/dotnet/aspnetcore/issues/34371
|
// This may or may not be fixed in .NET 6, but looks like it will not https://github.com/dotnet/aspnetcore/issues/34371
|
||||||
if ((fileInfo.Attributes & FileAttributes.ReparsePoint) == FileAttributes.ReparsePoint)
|
if ((fileInfo.Attributes & FileAttributes.ReparsePoint) == FileAttributes.ReparsePoint)
|
||||||
{
|
{
|
||||||
using Stream thisFileStream = File.OpenRead(path);
|
using var fileHandle = File.OpenHandle(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||||
length = thisFileStream.Length;
|
length = RandomAccess.GetLength(fileHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new FileMetadata
|
return new FileMetadata
|
||||||
|
@ -68,7 +68,7 @@ namespace Jellyfin.Server.Infrastructure
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override Task WriteFileAsync(ActionContext context, PhysicalFileResult result, RangeItemHeaderValue range, long rangeLength)
|
protected override Task WriteFileAsync(ActionContext context, PhysicalFileResult result, RangeItemHeaderValue? range, long rangeLength)
|
||||||
{
|
{
|
||||||
if (context == null)
|
if (context == null)
|
||||||
{
|
{
|
||||||
|
@ -132,7 +132,7 @@ namespace Jellyfin.Server.Infrastructure
|
||||||
FileAccess.Read,
|
FileAccess.Read,
|
||||||
FileShare.ReadWrite,
|
FileShare.ReadWrite,
|
||||||
bufferSize: BufferSize,
|
bufferSize: BufferSize,
|
||||||
options: (AsyncFile.UseAsyncIO ? FileOptions.Asynchronous : FileOptions.None) | FileOptions.SequentialScan);
|
options: FileOptions.Asynchronous | FileOptions.SequentialScan);
|
||||||
|
|
||||||
fileStream.Seek(offset, SeekOrigin.Begin);
|
fileStream.Seek(offset, SeekOrigin.Begin);
|
||||||
await StreamCopyOperation
|
await StreamCopyOperation
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<AssemblyName>jellyfin</AssemblyName>
|
<AssemblyName>jellyfin</AssemblyName>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net5.0</TargetFramework>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
<ServerGarbageCollection>false</ServerGarbageCollection>
|
<ServerGarbageCollection>false</ServerGarbageCollection>
|
||||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
|
|
|
@ -27,7 +27,11 @@ namespace Jellyfin.Server.Middleware
|
||||||
/// <returns>The async task.</returns>
|
/// <returns>The async task.</returns>
|
||||||
public async Task Invoke(HttpContext httpContext)
|
public async Task Invoke(HttpContext httpContext)
|
||||||
{
|
{
|
||||||
httpContext.Features.Set<IQueryFeature>(new UrlDecodeQueryFeature(httpContext.Features.Get<IQueryFeature>()));
|
var feature = httpContext.Features.Get<IQueryFeature>();
|
||||||
|
if (feature != null)
|
||||||
|
{
|
||||||
|
httpContext.Features.Set<IQueryFeature>(new UrlDecodeQueryFeature(feature));
|
||||||
|
}
|
||||||
|
|
||||||
await _next(httpContext).ConfigureAwait(false);
|
await _next(httpContext).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,20 +52,14 @@ namespace Jellyfin.Server.Middleware
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unencode and re-parse querystring.
|
if (!key.Contains('='))
|
||||||
var unencodedKey = HttpUtility.UrlDecode(key);
|
|
||||||
|
|
||||||
if (string.Equals(unencodedKey, key, StringComparison.Ordinal))
|
|
||||||
{
|
{
|
||||||
// Don't do anything if it's not encoded.
|
|
||||||
_store = value;
|
_store = value;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var pairs = new Dictionary<string, StringValues>();
|
var pairs = new Dictionary<string, StringValues>();
|
||||||
var queryString = unencodedKey.SpanSplit('&');
|
foreach (var pair in key.SpanSplit('&'))
|
||||||
|
|
||||||
foreach (var pair in queryString)
|
|
||||||
{
|
{
|
||||||
var i = pair.IndexOf('=');
|
var i = pair.IndexOf('=');
|
||||||
if (i == -1)
|
if (i == -1)
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using Emby.Server.Implementations.Data;
|
using Emby.Server.Implementations.Data;
|
||||||
using Emby.Server.Implementations.Serialization;
|
|
||||||
using Jellyfin.Data.Entities;
|
using Jellyfin.Data.Entities;
|
||||||
using Jellyfin.Data.Enums;
|
using Jellyfin.Data.Enums;
|
||||||
using Jellyfin.Extensions.Json;
|
using Jellyfin.Extensions.Json;
|
||||||
|
@ -10,6 +9,7 @@ using Jellyfin.Server.Implementations.Users;
|
||||||
using MediaBrowser.Controller;
|
using MediaBrowser.Controller;
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Model.Configuration;
|
using MediaBrowser.Model.Configuration;
|
||||||
|
using MediaBrowser.Model.Serialization;
|
||||||
using MediaBrowser.Model.Users;
|
using MediaBrowser.Model.Users;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using SQLitePCL.pretty;
|
using SQLitePCL.pretty;
|
||||||
|
@ -27,7 +27,7 @@ namespace Jellyfin.Server.Migrations.Routines
|
||||||
private readonly ILogger<MigrateUserDb> _logger;
|
private readonly ILogger<MigrateUserDb> _logger;
|
||||||
private readonly IServerApplicationPaths _paths;
|
private readonly IServerApplicationPaths _paths;
|
||||||
private readonly JellyfinDbProvider _provider;
|
private readonly JellyfinDbProvider _provider;
|
||||||
private readonly MyXmlSerializer _xmlSerializer;
|
private readonly IXmlSerializer _xmlSerializer;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="MigrateUserDb"/> class.
|
/// Initializes a new instance of the <see cref="MigrateUserDb"/> class.
|
||||||
|
@ -40,7 +40,7 @@ namespace Jellyfin.Server.Migrations.Routines
|
||||||
ILogger<MigrateUserDb> logger,
|
ILogger<MigrateUserDb> logger,
|
||||||
IServerApplicationPaths paths,
|
IServerApplicationPaths paths,
|
||||||
JellyfinDbProvider provider,
|
JellyfinDbProvider provider,
|
||||||
MyXmlSerializer xmlSerializer)
|
IXmlSerializer xmlSerializer)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_paths = paths;
|
_paths = paths;
|
||||||
|
|
|
@ -547,7 +547,7 @@ namespace Jellyfin.Server
|
||||||
?? throw new InvalidOperationException($"Invalid resource path: '{ResourcePath}'");
|
?? throw new InvalidOperationException($"Invalid resource path: '{ResourcePath}'");
|
||||||
|
|
||||||
// Copy the resource contents to the expected file path for the config file
|
// Copy the resource contents to the expected file path for the config file
|
||||||
await using Stream dst = new FileStream(configPath, FileMode.CreateNew, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, AsyncFile.UseAsyncIO);
|
await using Stream dst = new FileStream(configPath, FileMode.CreateNew, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous);
|
||||||
await resource.CopyToAsync(dst).ConfigureAwait(false);
|
await resource.CopyToAsync(dst).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net5.0</TargetFramework>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||||
|
|
|
@ -74,6 +74,6 @@ namespace MediaBrowser.Controller.Dlna
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="filename">The filename.</param>
|
/// <param name="filename">The filename.</param>
|
||||||
/// <returns>DlnaIconResponse.</returns>
|
/// <returns>DlnaIconResponse.</returns>
|
||||||
ImageStream GetIcon(string filename);
|
ImageStream? GetIcon(string filename);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,7 +58,7 @@ namespace MediaBrowser.Controller.Drawing
|
||||||
/// <returns>Guid.</returns>
|
/// <returns>Guid.</returns>
|
||||||
string GetImageCacheTag(BaseItem item, ItemImageInfo image);
|
string GetImageCacheTag(BaseItem item, ItemImageInfo image);
|
||||||
|
|
||||||
string GetImageCacheTag(BaseItem item, ChapterInfo info);
|
string GetImageCacheTag(BaseItem item, ChapterInfo chapter);
|
||||||
|
|
||||||
string? GetImageCacheTag(User user);
|
string? GetImageCacheTag(User user);
|
||||||
|
|
||||||
|
|
|
@ -8,11 +8,16 @@ namespace MediaBrowser.Controller.Drawing
|
||||||
{
|
{
|
||||||
public class ImageStream : IDisposable
|
public class ImageStream : IDisposable
|
||||||
{
|
{
|
||||||
|
public ImageStream(Stream stream)
|
||||||
|
{
|
||||||
|
Stream = stream;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the stream.
|
/// Gets the stream.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The stream.</value>
|
/// <value>The stream.</value>
|
||||||
public Stream? Stream { get; set; }
|
public Stream Stream { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the format.
|
/// Gets or sets the format.
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net5.0</TargetFramework>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||||
|
|
|
@ -541,7 +541,12 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||||
return MimeType;
|
return MimeType;
|
||||||
}
|
}
|
||||||
|
|
||||||
return MimeTypes.GetMimeType(outputPath, enableStreamDefault);
|
if (enableStreamDefault)
|
||||||
|
{
|
||||||
|
return MimeTypes.GetMimeType(outputPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
return MimeTypes.GetMimeType(outputPath, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool DeInterlace(string videoCodec, bool forceDeinterlaceIfSourceIsInterlaced)
|
public bool DeInterlace(string videoCodec, bool forceDeinterlaceIfSourceIsInterlaced)
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net5.0</TargetFramework>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net5.0</TargetFramework>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
|
@ -38,7 +38,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
|
||||||
subRip.LoadSubtitle(subtitle, lines, "untitled");
|
subRip.LoadSubtitle(subtitle, lines, "untitled");
|
||||||
if (subRip.ErrorCount > 0)
|
if (subRip.ErrorCount > 0)
|
||||||
{
|
{
|
||||||
_logger.LogError("{ErrorCount} errors encountered while parsing subtitle.");
|
_logger.LogError("{ErrorCount} errors encountered while parsing subtitle", subRip.ErrorCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
var trackInfo = new SubtitleTrackInfo();
|
var trackInfo = new SubtitleTrackInfo();
|
||||||
|
|
|
@ -680,7 +680,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
|
||||||
if (!string.Equals(text, newText, StringComparison.Ordinal))
|
if (!string.Equals(text, newText, StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
// use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 .
|
// use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 .
|
||||||
using (var fileStream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, AsyncFile.UseAsyncIO))
|
using (var fileStream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous))
|
||||||
using (var writer = new StreamWriter(fileStream, encoding))
|
using (var writer = new StreamWriter(fileStream, encoding))
|
||||||
{
|
{
|
||||||
await writer.WriteAsync(newText.AsMemory(), cancellationToken).ConfigureAwait(false);
|
await writer.WriteAsync(newText.AsMemory(), cancellationToken).ConfigureAwait(false);
|
||||||
|
|
|
@ -8,20 +8,13 @@ namespace MediaBrowser.Model.IO
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class AsyncFile
|
public static class AsyncFile
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Gets a value indicating whether we should use async IO on this platform.
|
|
||||||
/// <see href="https://github.com/dotnet/runtime/issues/16354" />.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>Returns <c>false</c> on Windows; otherwise <c>true</c>.</returns>
|
|
||||||
public static bool UseAsyncIO => !OperatingSystem.IsWindows();
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Opens an existing file for reading.
|
/// Opens an existing file for reading.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="path">The file to be opened for reading.</param>
|
/// <param name="path">The file to be opened for reading.</param>
|
||||||
/// <returns>A read-only <see cref="FileStream" /> on the specified path.</returns>
|
/// <returns>A read-only <see cref="FileStream" /> on the specified path.</returns>
|
||||||
public static FileStream OpenRead(string path)
|
public static FileStream OpenRead(string path)
|
||||||
=> new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, IODefaults.FileStreamBufferSize, UseAsyncIO);
|
=> new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Opens an existing file for writing.
|
/// Opens an existing file for writing.
|
||||||
|
@ -29,6 +22,6 @@ namespace MediaBrowser.Model.IO
|
||||||
/// <param name="path">The file to be opened for writing.</param>
|
/// <param name="path">The file to be opened for writing.</param>
|
||||||
/// <returns>An unshared <see cref="FileStream" /> object on the specified path with Write access.</returns>
|
/// <returns>An unshared <see cref="FileStream" /> object on the specified path with Write access.</returns>
|
||||||
public static FileStream OpenWrite(string path)
|
public static FileStream OpenWrite(string path)
|
||||||
=> new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, UseAsyncIO);
|
=> new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net5.0</TargetFramework>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Jellyfin.Extensions;
|
using Jellyfin.Extensions;
|
||||||
|
@ -164,15 +165,16 @@ namespace MediaBrowser.Model.Net
|
||||||
return dict;
|
return dict;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string? GetMimeType(string path) => GetMimeType(path, true);
|
public static string GetMimeType(string path) => GetMimeType(path, "application/octet-stream");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the type of the MIME.
|
/// Gets the type of the MIME.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="filename">The filename to find the MIME type of.</param>
|
/// <param name="filename">The filename to find the MIME type of.</param>
|
||||||
/// <param name="enableStreamDefault">Whether of not to return a default value if no fitting MIME type is found.</param>
|
/// <param name="defaultValue">The default value to return if no fitting MIME type is found.</param>
|
||||||
/// <returns>The worrect MIME type for the given filename, or `null` if it wasn't found and <paramref name="enableStreamDefault"/> is false.</returns>
|
/// <returns>The correct MIME type for the given filename, or <paramref name="defaultValue"/> if it wasn't found.</returns>
|
||||||
public static string? GetMimeType(string filename, bool enableStreamDefault)
|
[return: NotNullIfNotNullAttribute("defaultValue")]
|
||||||
|
public static string? GetMimeType(string filename, string? defaultValue = null)
|
||||||
{
|
{
|
||||||
if (filename.Length == 0)
|
if (filename.Length == 0)
|
||||||
{
|
{
|
||||||
|
@ -211,7 +213,7 @@ namespace MediaBrowser.Model.Net
|
||||||
return "application/octet-stream";
|
return "application/octet-stream";
|
||||||
}
|
}
|
||||||
|
|
||||||
return enableStreamDefault ? "application/octet-stream" : null;
|
return defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string? ToExtension(string mimeType)
|
public static string? ToExtension(string mimeType)
|
||||||
|
|
|
@ -264,7 +264,7 @@ namespace MediaBrowser.Providers.Manager
|
||||||
_fileSystem.SetAttributes(path, false, false);
|
_fileSystem.SetAttributes(path, false, false);
|
||||||
|
|
||||||
// use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 .
|
// use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 .
|
||||||
await using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, AsyncFile.UseAsyncIO))
|
await using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous))
|
||||||
{
|
{
|
||||||
await source.CopyToAsync(fs, cancellationToken).ConfigureAwait(false);
|
await source.CopyToAsync(fs, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -163,7 +163,7 @@ namespace MediaBrowser.Providers.Manager
|
||||||
{
|
{
|
||||||
var mimeType = MimeTypes.GetMimeType(response.Path);
|
var mimeType = MimeTypes.GetMimeType(response.Path);
|
||||||
|
|
||||||
var stream = new FileStream(response.Path, FileMode.Open, FileAccess.Read, FileShare.Read, IODefaults.FileStreamBufferSize, AsyncFile.UseAsyncIO);
|
var stream = new FileStream(response.Path, FileMode.Open, FileAccess.Read, FileShare.Read, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous);
|
||||||
|
|
||||||
await _providerManager.SaveImage(item, stream, mimeType, imageType, null, cancellationToken).ConfigureAwait(false);
|
await _providerManager.SaveImage(item, stream, mimeType, imageType, null, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net5.0</TargetFramework>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
<CodeAnalysisRuleSet>../jellyfin.ruleset</CodeAnalysisRuleSet>
|
<CodeAnalysisRuleSet>../jellyfin.ruleset</CodeAnalysisRuleSet>
|
||||||
|
|
|
@ -173,7 +173,7 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
|
||||||
using var response = await _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken).ConfigureAwait(false);
|
using var response = await _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken).ConfigureAwait(false);
|
||||||
await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
|
await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
|
||||||
// use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 .
|
// use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 .
|
||||||
await using var xmlFileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, AsyncFile.UseAsyncIO);
|
await using var xmlFileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous);
|
||||||
await stream.CopyToAsync(xmlFileStream, cancellationToken).ConfigureAwait(false);
|
await stream.CopyToAsync(xmlFileStream, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -155,7 +155,7 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(path));
|
Directory.CreateDirectory(Path.GetDirectoryName(path));
|
||||||
|
|
||||||
// use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 .
|
// use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 .
|
||||||
await using var xmlFileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, AsyncFile.UseAsyncIO);
|
await using var xmlFileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous);
|
||||||
await stream.CopyToAsync(xmlFileStream, cancellationToken).ConfigureAwait(false);
|
await stream.CopyToAsync(xmlFileStream, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
|
||||||
if (reader.TokenType == JsonTokenType.String)
|
if (reader.TokenType == JsonTokenType.String)
|
||||||
{
|
{
|
||||||
var str = reader.GetString();
|
var str = reader.GetString();
|
||||||
if (str != null && str.Equals("N/A", StringComparison.OrdinalIgnoreCase))
|
if (str == null || str.Equals("N/A", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -295,7 +295,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
|
||||||
imdbParam));
|
imdbParam));
|
||||||
|
|
||||||
var rootObject = await GetDeserializedOmdbResponse<RootObject>(_httpClientFactory.CreateClient(NamedClient.Default), url, cancellationToken).ConfigureAwait(false);
|
var rootObject = await GetDeserializedOmdbResponse<RootObject>(_httpClientFactory.CreateClient(NamedClient.Default), url, cancellationToken).ConfigureAwait(false);
|
||||||
await using FileStream jsonFileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, AsyncFile.UseAsyncIO);
|
await using FileStream jsonFileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous);
|
||||||
await JsonSerializer.SerializeAsync(jsonFileStream, rootObject, _jsonOptions, cancellationToken).ConfigureAwait(false);
|
await JsonSerializer.SerializeAsync(jsonFileStream, rootObject, _jsonOptions, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
return path;
|
return path;
|
||||||
|
@ -335,7 +335,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
|
||||||
seasonId));
|
seasonId));
|
||||||
|
|
||||||
var rootObject = await GetDeserializedOmdbResponse<SeasonRootObject>(_httpClientFactory.CreateClient(NamedClient.Default), url, cancellationToken).ConfigureAwait(false);
|
var rootObject = await GetDeserializedOmdbResponse<SeasonRootObject>(_httpClientFactory.CreateClient(NamedClient.Default), url, cancellationToken).ConfigureAwait(false);
|
||||||
await using FileStream jsonFileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, AsyncFile.UseAsyncIO);
|
await using FileStream jsonFileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous);
|
||||||
await JsonSerializer.SerializeAsync(jsonFileStream, rootObject, _jsonOptions, cancellationToken).ConfigureAwait(false);
|
await JsonSerializer.SerializeAsync(jsonFileStream, rootObject, _jsonOptions, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
return path;
|
return path;
|
||||||
|
|
|
@ -146,7 +146,7 @@ namespace MediaBrowser.Providers.Studios
|
||||||
|
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(file));
|
Directory.CreateDirectory(Path.GetDirectoryName(file));
|
||||||
await using var response = await httpClient.GetStreamAsync(url, cancellationToken).ConfigureAwait(false);
|
await using var response = await httpClient.GetStreamAsync(url, cancellationToken).ConfigureAwait(false);
|
||||||
await using var fileStream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, AsyncFile.UseAsyncIO);
|
await using var fileStream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous);
|
||||||
await response.CopyToAsync(fileStream, cancellationToken).ConfigureAwait(false);
|
await response.CopyToAsync(fileStream, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -245,7 +245,7 @@ namespace MediaBrowser.Providers.Subtitles
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(savePath));
|
Directory.CreateDirectory(Path.GetDirectoryName(savePath));
|
||||||
|
|
||||||
// use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 .
|
// use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 .
|
||||||
using var fs = new FileStream(savePath, FileMode.Create, FileAccess.Write, FileShare.None, FileStreamBufferSize, AsyncFile.UseAsyncIO);
|
using var fs = new FileStream(savePath, FileMode.Create, FileAccess.Write, FileShare.None, FileStreamBufferSize, FileOptions.Asynchronous);
|
||||||
await stream.CopyToAsync(fs).ConfigureAwait(false);
|
await stream.CopyToAsync(fs).ConfigureAwait(false);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net5.0</TargetFramework>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace Rssdp.Infrastructure
|
namespace Rssdp.Infrastructure
|
||||||
|
@ -45,11 +46,11 @@ namespace Rssdp.Infrastructure
|
||||||
|
|
||||||
const string ArgFormat = "{0}: {1}\r\n";
|
const string ArgFormat = "{0}: {1}\r\n";
|
||||||
|
|
||||||
builder.AppendFormat("{0}\r\n", header);
|
builder.AppendFormat(CultureInfo.InvariantCulture, "{0}\r\n", header);
|
||||||
|
|
||||||
foreach (var pair in values)
|
foreach (var pair in values)
|
||||||
{
|
{
|
||||||
builder.AppendFormat(ArgFormat, pair.Key, pair.Value);
|
builder.AppendFormat(CultureInfo.InvariantCulture, ArgFormat, pair.Key, pair.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.Append("\r\n");
|
builder.Append("\r\n");
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net5.0</TargetFramework>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||||
<AnalysisMode>AllDisabledByDefault</AnalysisMode>
|
<AnalysisMode>AllDisabledByDefault</AnalysisMode>
|
||||||
<Nullable>disable</Nullable>
|
<Nullable>disable</Nullable>
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Globalization;
|
||||||
using Rssdp.Infrastructure;
|
using Rssdp.Infrastructure;
|
||||||
|
|
||||||
namespace Rssdp
|
namespace Rssdp
|
||||||
|
@ -134,7 +135,9 @@ namespace Rssdp
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return String.Format("urn:{0}:{3}:{1}:{2}",
|
return String.Format(
|
||||||
|
CultureInfo.InvariantCulture,
|
||||||
|
"urn:{0}:{3}:{1}:{2}",
|
||||||
this.DeviceTypeNamespace ?? String.Empty,
|
this.DeviceTypeNamespace ?? String.Empty,
|
||||||
this.DeviceType ?? String.Empty,
|
this.DeviceType ?? String.Empty,
|
||||||
this.DeviceVersion,
|
this.DeviceVersion,
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
@ -233,7 +234,7 @@ namespace Rssdp.Infrastructure
|
||||||
{
|
{
|
||||||
if (String.IsNullOrEmpty(searchTarget))
|
if (String.IsNullOrEmpty(searchTarget))
|
||||||
{
|
{
|
||||||
WriteTrace(String.Format("Invalid search request received From {0}, Target is null/empty.", remoteEndPoint.ToString()));
|
WriteTrace(String.Format(CultureInfo.InvariantCulture, "Invalid search request received From {0}, Target is null/empty.", remoteEndPoint.ToString()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -340,7 +341,7 @@ namespace Rssdp.Infrastructure
|
||||||
|
|
||||||
private string GetUsn(string udn, string fullDeviceType)
|
private string GetUsn(string udn, string fullDeviceType)
|
||||||
{
|
{
|
||||||
return String.Format("{0}::{1}", udn, fullDeviceType);
|
return String.Format(CultureInfo.InvariantCulture, "{0}::{1}", udn, fullDeviceType);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void SendSearchResponse(
|
private async void SendSearchResponse(
|
||||||
|
@ -363,7 +364,7 @@ namespace Rssdp.Infrastructure
|
||||||
values["DATE"] = DateTime.UtcNow.ToString("r");
|
values["DATE"] = DateTime.UtcNow.ToString("r");
|
||||||
values["CACHE-CONTROL"] = "max-age = " + rootDevice.CacheLifetime.TotalSeconds;
|
values["CACHE-CONTROL"] = "max-age = " + rootDevice.CacheLifetime.TotalSeconds;
|
||||||
values["ST"] = searchTarget;
|
values["ST"] = searchTarget;
|
||||||
values["SERVER"] = string.Format("{0}/{1} UPnP/1.0 RSSDP/{2}", _OSName, _OSVersion, ServerVersion);
|
values["SERVER"] = string.Format(CultureInfo.InvariantCulture, "{0}/{1} UPnP/1.0 RSSDP/{2}", _OSName, _OSVersion, ServerVersion);
|
||||||
values["USN"] = uniqueServiceName;
|
values["USN"] = uniqueServiceName;
|
||||||
values["LOCATION"] = rootDevice.Location.ToString();
|
values["LOCATION"] = rootDevice.Location.ToString();
|
||||||
|
|
||||||
|
@ -497,7 +498,7 @@ namespace Rssdp.Infrastructure
|
||||||
values["DATE"] = DateTime.UtcNow.ToString("r");
|
values["DATE"] = DateTime.UtcNow.ToString("r");
|
||||||
values["CACHE-CONTROL"] = "max-age = " + rootDevice.CacheLifetime.TotalSeconds;
|
values["CACHE-CONTROL"] = "max-age = " + rootDevice.CacheLifetime.TotalSeconds;
|
||||||
values["LOCATION"] = rootDevice.Location.ToString();
|
values["LOCATION"] = rootDevice.Location.ToString();
|
||||||
values["SERVER"] = string.Format("{0}/{1} UPnP/1.0 RSSDP/{2}", _OSName, _OSVersion, ServerVersion);
|
values["SERVER"] = string.Format(CultureInfo.InvariantCulture, "{0}/{1} UPnP/1.0 RSSDP/{2}", _OSName, _OSVersion, ServerVersion);
|
||||||
values["NTS"] = "ssdp:alive";
|
values["NTS"] = "ssdp:alive";
|
||||||
values["NT"] = notificationType;
|
values["NT"] = notificationType;
|
||||||
values["USN"] = uniqueServiceName;
|
values["USN"] = uniqueServiceName;
|
||||||
|
@ -522,7 +523,7 @@ namespace Rssdp.Infrastructure
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.Add(SendByeByeNotification(device, device.Udn, device.Udn, cancellationToken));
|
tasks.Add(SendByeByeNotification(device, device.Udn, device.Udn, cancellationToken));
|
||||||
tasks.Add(SendByeByeNotification(device, String.Format("urn:{0}", device.FullDeviceType), GetUsn(device.Udn, device.FullDeviceType), cancellationToken));
|
tasks.Add(SendByeByeNotification(device, String.Format(CultureInfo.InvariantCulture, "urn:{0}", device.FullDeviceType), GetUsn(device.Udn, device.FullDeviceType), cancellationToken));
|
||||||
|
|
||||||
foreach (var childDevice in device.Devices)
|
foreach (var childDevice in device.Devices)
|
||||||
{
|
{
|
||||||
|
@ -542,7 +543,7 @@ namespace Rssdp.Infrastructure
|
||||||
// If needed later for non-server devices, these headers will need to be dynamic
|
// If needed later for non-server devices, these headers will need to be dynamic
|
||||||
values["HOST"] = "239.255.255.250:1900";
|
values["HOST"] = "239.255.255.250:1900";
|
||||||
values["DATE"] = DateTime.UtcNow.ToString("r");
|
values["DATE"] = DateTime.UtcNow.ToString("r");
|
||||||
values["SERVER"] = string.Format("{0}/{1} UPnP/1.0 RSSDP/{2}", _OSName, _OSVersion, ServerVersion);
|
values["SERVER"] = string.Format(CultureInfo.InvariantCulture, "{0}/{1} UPnP/1.0 RSSDP/{2}", _OSName, _OSVersion, ServerVersion);
|
||||||
values["NTS"] = "ssdp:byebye";
|
values["NTS"] = "ssdp:byebye";
|
||||||
values["NT"] = notificationType;
|
values["NT"] = notificationType;
|
||||||
values["USN"] = uniqueServiceName;
|
values["USN"] = uniqueServiceName;
|
||||||
|
@ -550,7 +551,7 @@ namespace Rssdp.Infrastructure
|
||||||
var message = BuildMessage(header, values);
|
var message = BuildMessage(header, values);
|
||||||
|
|
||||||
var sendCount = IsDisposed ? 1 : 3;
|
var sendCount = IsDisposed ? 1 : 3;
|
||||||
WriteTrace(String.Format("Sent byebye notification"), device);
|
WriteTrace(String.Format(CultureInfo.InvariantCulture, "Sent byebye notification"), device);
|
||||||
return _CommsServer.SendMulticastMessage(message, sendCount, _sendOnlyMatchedHost ? device.ToRootDevice().Address : null, cancellationToken);
|
return _CommsServer.SendMulticastMessage(message, sendCount, _sendOnlyMatchedHost ? device.ToRootDevice().Address : null, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
2
debian/control
vendored
2
debian/control
vendored
|
@ -3,7 +3,7 @@ Section: misc
|
||||||
Priority: optional
|
Priority: optional
|
||||||
Maintainer: Jellyfin Team <team@jellyfin.org>
|
Maintainer: Jellyfin Team <team@jellyfin.org>
|
||||||
Build-Depends: debhelper (>= 9),
|
Build-Depends: debhelper (>= 9),
|
||||||
dotnet-sdk-5.0,
|
dotnet-sdk-6.0,
|
||||||
libc6-dev,
|
libc6-dev,
|
||||||
libcurl4-openssl-dev,
|
libcurl4-openssl-dev,
|
||||||
libfontconfig1-dev,
|
libfontconfig1-dev,
|
||||||
|
|
|
@ -2,7 +2,6 @@ FROM centos:7
|
||||||
# Docker build arguments
|
# Docker build arguments
|
||||||
ARG SOURCE_DIR=/jellyfin
|
ARG SOURCE_DIR=/jellyfin
|
||||||
ARG ARTIFACT_DIR=/dist
|
ARG ARTIFACT_DIR=/dist
|
||||||
ARG SDK_VERSION=5.0
|
|
||||||
# Docker run environment
|
# Docker run environment
|
||||||
ENV SOURCE_DIR=/jellyfin
|
ENV SOURCE_DIR=/jellyfin
|
||||||
ENV ARTIFACT_DIR=/dist
|
ENV ARTIFACT_DIR=/dist
|
||||||
|
@ -11,12 +10,13 @@ ENV IS_DOCKER=YES
|
||||||
# Prepare CentOS environment
|
# Prepare CentOS environment
|
||||||
RUN yum update -yq \
|
RUN yum update -yq \
|
||||||
&& yum install -yq epel-release \
|
&& yum install -yq epel-release \
|
||||||
&& yum install -yq @buildsys-build rpmdevtools yum-plugins-core libcurl-devel fontconfig-devel freetype-devel openssl-devel glibc-devel libicu-devel git
|
&& yum install -yq @buildsys-build rpmdevtools yum-plugins-core libcurl-devel fontconfig-devel freetype-devel openssl-devel glibc-devel libicu-devel git wget
|
||||||
|
|
||||||
# Install DotNET SDK
|
# Install DotNET SDK
|
||||||
RUN rpm -Uvh https://packages.microsoft.com/config/rhel/7/packages-microsoft-prod.rpm \
|
RUN wget -q https://download.visualstudio.microsoft.com/download/pr/5fcb98bb-21e1-47a5-bb8e-bb25f41a3e52/04811d5d05b7e694f040d2a13c1aae4c/dotnet-sdk-6.0.100-rc.1.21463.6-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
|
||||||
&& rpmdev-setuptree \
|
&& mkdir -p dotnet-sdk \
|
||||||
&& yum install -yq dotnet-sdk-${SDK_VERSION}
|
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
|
||||||
|
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
|
||||||
|
|
||||||
# Create symlinks and directories
|
# Create symlinks and directories
|
||||||
RUN ln -sf ${SOURCE_DIR}/deployment/build.centos.amd64 /build.sh \
|
RUN ln -sf ${SOURCE_DIR}/deployment/build.centos.amd64 /build.sh \
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
FROM mcr.microsoft.com/dotnet/sdk:5.0-buster-slim
|
FROM mcr.microsoft.com/dotnet/sdk:6.0-bullseye-slim
|
||||||
# Docker build arguments
|
# Docker build arguments
|
||||||
ARG SOURCE_DIR=/jellyfin
|
ARG SOURCE_DIR=/jellyfin
|
||||||
ARG ARTIFACT_DIR=/dist
|
ARG ARTIFACT_DIR=/dist
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
FROM mcr.microsoft.com/dotnet/sdk:5.0-buster-slim
|
FROM mcr.microsoft.com/dotnet/sdk:6.0-bullseye-slim
|
||||||
# Docker build arguments
|
# Docker build arguments
|
||||||
ARG SOURCE_DIR=/jellyfin
|
ARG SOURCE_DIR=/jellyfin
|
||||||
ARG ARTIFACT_DIR=/dist
|
ARG ARTIFACT_DIR=/dist
|
||||||
|
@ -18,16 +18,16 @@ RUN apt-get update -yqq \
|
||||||
RUN dpkg --add-architecture arm64 \
|
RUN dpkg --add-architecture arm64 \
|
||||||
&& apt-get update -yqq \
|
&& apt-get update -yqq \
|
||||||
&& apt-get install -yqq --no-install-recommends cross-gcc-dev \
|
&& apt-get install -yqq --no-install-recommends cross-gcc-dev \
|
||||||
&& TARGET_LIST="arm64" cross-gcc-gensource 8 \
|
&& TARGET_LIST="arm64" cross-gcc-gensource 9 \
|
||||||
&& cd cross-gcc-packages-amd64/cross-gcc-8-arm64 \
|
&& cd cross-gcc-packages-amd64/cross-gcc-9-arm64 \
|
||||||
&& apt-get install -yqq --no-install-recommends \
|
&& apt-get install -yqq --no-install-recommends \
|
||||||
gcc-8-source libstdc++-8-dev-arm64-cross \
|
gcc-9-source libstdc++-9-dev-arm64-cross \
|
||||||
binutils-aarch64-linux-gnu bison flex libtool \
|
binutils-aarch64-linux-gnu bison flex libtool \
|
||||||
gdb sharutils netbase libmpc-dev libmpfr-dev libgmp-dev \
|
gdb sharutils netbase libmpc-dev libmpfr-dev libgmp-dev \
|
||||||
systemtap-sdt-dev autogen expect chrpath zlib1g-dev zip \
|
systemtap-sdt-dev autogen expect chrpath zlib1g-dev zip \
|
||||||
libc6-dev:arm64 linux-libc-dev:arm64 libgcc1:arm64 \
|
libc6-dev:arm64 linux-libc-dev:arm64 libgcc1:arm64 \
|
||||||
libcurl4-openssl-dev:arm64 libfontconfig1-dev:arm64 \
|
libcurl4-openssl-dev:arm64 libfontconfig1-dev:arm64 \
|
||||||
libfreetype6-dev:arm64 libssl-dev:arm64 liblttng-ust0:arm64 libstdc++-8-dev:arm64
|
libfreetype6-dev:arm64 libssl-dev:arm64 liblttng-ust0:arm64 libstdc++-9-dev:arm64
|
||||||
|
|
||||||
# Link to build script
|
# Link to build script
|
||||||
RUN ln -sf ${SOURCE_DIR}/deployment/build.debian.arm64 /build.sh
|
RUN ln -sf ${SOURCE_DIR}/deployment/build.debian.arm64 /build.sh
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
FROM mcr.microsoft.com/dotnet/sdk:5.0-buster-slim
|
FROM mcr.microsoft.com/dotnet/sdk:6.0-bullseye-slim
|
||||||
# Docker build arguments
|
# Docker build arguments
|
||||||
ARG SOURCE_DIR=/jellyfin
|
ARG SOURCE_DIR=/jellyfin
|
||||||
ARG ARTIFACT_DIR=/dist
|
ARG ARTIFACT_DIR=/dist
|
||||||
|
@ -18,17 +18,17 @@ RUN apt-get update -yqq \
|
||||||
RUN dpkg --add-architecture armhf \
|
RUN dpkg --add-architecture armhf \
|
||||||
&& apt-get update -yqq \
|
&& apt-get update -yqq \
|
||||||
&& apt-get install -yqq --no-install-recommends cross-gcc-dev \
|
&& apt-get install -yqq --no-install-recommends cross-gcc-dev \
|
||||||
&& TARGET_LIST="armhf" cross-gcc-gensource 8 \
|
&& TARGET_LIST="armhf" cross-gcc-gensource 9 \
|
||||||
&& cd cross-gcc-packages-amd64/cross-gcc-8-armhf \
|
&& cd cross-gcc-packages-amd64/cross-gcc-9-armhf \
|
||||||
&& apt-get install -yqq --no-install-recommends\
|
&& apt-get install -yqq --no-install-recommends\
|
||||||
gcc-8-source libstdc++-8-dev-armhf-cross \
|
gcc-9-source libstdc++-9-dev-armhf-cross \
|
||||||
binutils-aarch64-linux-gnu bison flex libtool gdb \
|
binutils-aarch64-linux-gnu bison flex libtool gdb \
|
||||||
sharutils netbase libmpc-dev libmpfr-dev libgmp-dev \
|
sharutils netbase libmpc-dev libmpfr-dev libgmp-dev \
|
||||||
systemtap-sdt-dev autogen expect chrpath zlib1g-dev \
|
systemtap-sdt-dev autogen expect chrpath zlib1g-dev \
|
||||||
zip binutils-arm-linux-gnueabihf libc6-dev:armhf \
|
zip binutils-arm-linux-gnueabihf libc6-dev:armhf \
|
||||||
linux-libc-dev:armhf libgcc1:armhf libcurl4-openssl-dev:armhf \
|
linux-libc-dev:armhf libgcc1:armhf libcurl4-openssl-dev:armhf \
|
||||||
libfontconfig1-dev:armhf libfreetype6-dev:armhf libssl-dev:armhf \
|
libfontconfig1-dev:armhf libfreetype6-dev:armhf libssl-dev:armhf \
|
||||||
liblttng-ust0:armhf libstdc++-8-dev:armhf
|
liblttng-ust0:armhf libstdc++-9-dev:armhf
|
||||||
|
|
||||||
# Link to build script
|
# Link to build script
|
||||||
RUN ln -sf ${SOURCE_DIR}/deployment/build.debian.armhf /build.sh
|
RUN ln -sf ${SOURCE_DIR}/deployment/build.debian.armhf /build.sh
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
ARG DOTNET_VERSION=5.0
|
FROM mcr.microsoft.com/dotnet/sdk:6.0-bullseye-slim
|
||||||
|
|
||||||
FROM mcr.microsoft.com/dotnet/sdk:${DOTNET_VERSION}-buster-slim
|
|
||||||
|
|
||||||
ARG SOURCE_DIR=/src
|
ARG SOURCE_DIR=/src
|
||||||
ARG ARTIFACT_DIR=/jellyfin
|
ARG ARTIFACT_DIR=/jellyfin
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
ARG DOTNET_VERSION=5.0
|
FROM mcr.microsoft.com/dotnet/sdk:6.0-bullseye-slim
|
||||||
|
|
||||||
FROM mcr.microsoft.com/dotnet/sdk:${DOTNET_VERSION}-buster-slim
|
|
||||||
|
|
||||||
ARG SOURCE_DIR=/src
|
ARG SOURCE_DIR=/src
|
||||||
ARG ARTIFACT_DIR=/jellyfin
|
ARG ARTIFACT_DIR=/jellyfin
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
ARG DOTNET_VERSION=5.0
|
FROM mcr.microsoft.com/dotnet/sdk:6.0-bullseye-slim
|
||||||
|
|
||||||
FROM mcr.microsoft.com/dotnet/sdk:${DOTNET_VERSION}-buster-slim
|
|
||||||
|
|
||||||
ARG SOURCE_DIR=/src
|
ARG SOURCE_DIR=/src
|
||||||
ARG ARTIFACT_DIR=/jellyfin
|
ARG ARTIFACT_DIR=/jellyfin
|
||||||
|
|
|
@ -2,7 +2,6 @@ FROM fedora:33
|
||||||
# Docker build arguments
|
# Docker build arguments
|
||||||
ARG SOURCE_DIR=/jellyfin
|
ARG SOURCE_DIR=/jellyfin
|
||||||
ARG ARTIFACT_DIR=/dist
|
ARG ARTIFACT_DIR=/dist
|
||||||
ARG SDK_VERSION=5.0
|
|
||||||
# Docker run environment
|
# Docker run environment
|
||||||
ENV SOURCE_DIR=/jellyfin
|
ENV SOURCE_DIR=/jellyfin
|
||||||
ENV ARTIFACT_DIR=/dist
|
ENV ARTIFACT_DIR=/dist
|
||||||
|
@ -10,10 +9,14 @@ ENV IS_DOCKER=YES
|
||||||
|
|
||||||
# Prepare Fedora environment
|
# Prepare Fedora environment
|
||||||
RUN dnf update -yq \
|
RUN dnf update -yq \
|
||||||
&& dnf install -yq @buildsys-build rpmdevtools git dnf-plugins-core libcurl-devel fontconfig-devel freetype-devel openssl-devel glibc-devel libicu-devel systemd
|
&& dnf install -yq @buildsys-build rpmdevtools git dnf-plugins-core libcurl-devel fontconfig-devel freetype-devel openssl-devel glibc-devel libicu-devel systemd wget
|
||||||
|
|
||||||
# Install DotNET SDK
|
# Install DotNET SDK
|
||||||
RUN dnf install -yq dotnet-sdk-${SDK_VERSION} dotnet-runtime-${SDK_VERSION}
|
RUN wget -q https://download.visualstudio.microsoft.com/download/pr/5fcb98bb-21e1-47a5-bb8e-bb25f41a3e52/04811d5d05b7e694f040d2a13c1aae4c/dotnet-sdk-6.0.100-rc.1.21463.6-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
|
||||||
|
&& mkdir -p dotnet-sdk \
|
||||||
|
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
|
||||||
|
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
|
||||||
|
|
||||||
|
|
||||||
# Create symlinks and directories
|
# Create symlinks and directories
|
||||||
RUN ln -sf ${SOURCE_DIR}/deployment/build.fedora.amd64 /build.sh \
|
RUN ln -sf ${SOURCE_DIR}/deployment/build.fedora.amd64 /build.sh \
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
FROM mcr.microsoft.com/dotnet/sdk:5.0-buster-slim
|
FROM mcr.microsoft.com/dotnet/sdk:6.0-bullseye-slim
|
||||||
# Docker build arguments
|
# Docker build arguments
|
||||||
ARG SOURCE_DIR=/jellyfin
|
ARG SOURCE_DIR=/jellyfin
|
||||||
ARG ARTIFACT_DIR=/dist
|
ARG ARTIFACT_DIR=/dist
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
FROM mcr.microsoft.com/dotnet/sdk:5.0-buster-slim
|
FROM mcr.microsoft.com/dotnet/sdk:6.0-bullseye-slim
|
||||||
# Docker build arguments
|
# Docker build arguments
|
||||||
ARG SOURCE_DIR=/jellyfin
|
ARG SOURCE_DIR=/jellyfin
|
||||||
ARG ARTIFACT_DIR=/dist
|
ARG ARTIFACT_DIR=/dist
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
FROM mcr.microsoft.com/dotnet/sdk:5.0-buster-slim
|
FROM mcr.microsoft.com/dotnet/sdk:6.0-bullseye-slim
|
||||||
# Docker build arguments
|
# Docker build arguments
|
||||||
ARG SOURCE_DIR=/jellyfin
|
ARG SOURCE_DIR=/jellyfin
|
||||||
ARG ARTIFACT_DIR=/dist
|
ARG ARTIFACT_DIR=/dist
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
FROM mcr.microsoft.com/dotnet/sdk:5.0-buster-slim
|
FROM mcr.microsoft.com/dotnet/sdk:6.0-bullseye-slim
|
||||||
# Docker build arguments
|
# Docker build arguments
|
||||||
ARG SOURCE_DIR=/jellyfin
|
ARG SOURCE_DIR=/jellyfin
|
||||||
ARG ARTIFACT_DIR=/dist
|
ARG ARTIFACT_DIR=/dist
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
FROM mcr.microsoft.com/dotnet/sdk:5.0-buster-slim
|
FROM mcr.microsoft.com/dotnet/sdk:6.0-bullseye-slim
|
||||||
# Docker build arguments
|
# Docker build arguments
|
||||||
ARG SOURCE_DIR=/jellyfin
|
ARG SOURCE_DIR=/jellyfin
|
||||||
ARG ARTIFACT_DIR=/dist
|
ARG ARTIFACT_DIR=/dist
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user