mirror of
https://github.com/jellyfin/jellyfin.git
synced 2025-07-09 03:04:24 -04:00
Merge branch 'master' into feature/ffmpeg-version-check
This commit is contained in:
commit
3588ee5229
131
.ci/azure-pipelines-package.yml
Normal file
131
.ci/azure-pipelines-package.yml
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
jobs:
|
||||||
|
- job: BuildPackage
|
||||||
|
displayName: 'Build Packages'
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
CentOS.amd64:
|
||||||
|
BuildConfiguration: centos.amd64
|
||||||
|
Fedora.amd64:
|
||||||
|
BuildConfiguration: fedora.amd64
|
||||||
|
Debian.amd64:
|
||||||
|
BuildConfiguration: debian.amd64
|
||||||
|
Debian.arm64:
|
||||||
|
BuildConfiguration: debian.arm64
|
||||||
|
Debian.armhf:
|
||||||
|
BuildConfiguration: debian.armhf
|
||||||
|
Ubuntu.amd64:
|
||||||
|
BuildConfiguration: ubuntu.amd64
|
||||||
|
Ubuntu.arm64:
|
||||||
|
BuildConfiguration: ubuntu.arm64
|
||||||
|
Ubuntu.armhf:
|
||||||
|
BuildConfiguration: ubuntu.armhf
|
||||||
|
Linux.amd64:
|
||||||
|
BuildConfiguration: linux.amd64
|
||||||
|
Windows.amd64:
|
||||||
|
BuildConfiguration: windows.amd64
|
||||||
|
MacOS:
|
||||||
|
BuildConfiguration: macos
|
||||||
|
Portable:
|
||||||
|
BuildConfiguration: portable
|
||||||
|
|
||||||
|
pool:
|
||||||
|
vmImage: 'ubuntu-latest'
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- script: 'docker build -f deployment/Dockerfile.$(BuildConfiguration) -t jellyfin-server-$(BuildConfiguration) deployment'
|
||||||
|
displayName: 'Build Dockerfile'
|
||||||
|
condition: or(startsWith(variables['Build.SourceBranch'], 'refs/tags'), startsWith(variables['Build.SourceBranch'], 'refs/heads/master'))
|
||||||
|
|
||||||
|
- script: 'docker image ls -a && docker run -v $(pwd)/deployment/dist:/dist -v $(pwd):/jellyfin -e IS_UNSTABLE="yes" -e BUILD_ID=$(Build.BuildNumber) jellyfin-server-$(BuildConfiguration)'
|
||||||
|
displayName: 'Run Dockerfile (unstable)'
|
||||||
|
condition: startsWith(variables['Build.SourceBranch'], 'refs/heads/master')
|
||||||
|
|
||||||
|
- script: 'docker image ls -a && docker run -v $(pwd)/deployment/dist:/dist -v $(pwd):/jellyfin -e IS_UNSTABLE="no" -e BUILD_ID=$(Build.BuildNumber) jellyfin-server-$(BuildConfiguration)'
|
||||||
|
displayName: 'Run Dockerfile (stable)'
|
||||||
|
condition: startsWith(variables['Build.SourceBranch'], 'refs/tags')
|
||||||
|
|
||||||
|
- task: PublishPipelineArtifact@1
|
||||||
|
displayName: 'Publish Release'
|
||||||
|
condition: or(startsWith(variables['Build.SourceBranch'], 'refs/tags'), startsWith(variables['Build.SourceBranch'], 'refs/heads/master'))
|
||||||
|
inputs:
|
||||||
|
targetPath: '$(Build.SourcesDirectory)/deployment/dist'
|
||||||
|
artifactName: 'jellyfin-server-$(BuildConfiguration)'
|
||||||
|
|
||||||
|
- task: CopyFilesOverSSH@0
|
||||||
|
displayName: 'Upload artifacts to repository server'
|
||||||
|
condition: or(startsWith(variables['Build.SourceBranch'], 'refs/tags'), startsWith(variables['Build.SourceBranch'], 'refs/heads/master'))
|
||||||
|
inputs:
|
||||||
|
sshEndpoint: repository
|
||||||
|
sourceFolder: '$(Build.SourcesDirectory)/deployment/dist'
|
||||||
|
contents: '**'
|
||||||
|
targetFolder: '/srv/repository/incoming/azure/$(Build.BuildNumber)/$(BuildConfiguration)'
|
||||||
|
|
||||||
|
- job: BuildDocker
|
||||||
|
displayName: 'Build Docker'
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
amd64:
|
||||||
|
BuildConfiguration: amd64
|
||||||
|
arm64:
|
||||||
|
BuildConfiguration: arm64
|
||||||
|
armhf:
|
||||||
|
BuildConfiguration: armhf
|
||||||
|
|
||||||
|
pool:
|
||||||
|
vmImage: 'ubuntu-latest'
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- task: Docker@2
|
||||||
|
displayName: 'Push Unstable Image'
|
||||||
|
condition: startsWith(variables['Build.SourceBranch'], 'refs/heads/master')
|
||||||
|
inputs:
|
||||||
|
repository: 'jellyfin/jellyfin-server'
|
||||||
|
command: buildAndPush
|
||||||
|
buildContext: '.'
|
||||||
|
Dockerfile: 'deployment/Dockerfile.docker.$(BuildConfiguration)'
|
||||||
|
containerRegistry: Docker Hub
|
||||||
|
tags: |
|
||||||
|
unstable-$(Build.BuildNumber)-$(BuildConfiguration)
|
||||||
|
unstable-$(BuildConfiguration)
|
||||||
|
|
||||||
|
- task: Docker@2
|
||||||
|
displayName: 'Push Stable Image'
|
||||||
|
condition: startsWith(variables['Build.SourceBranch'], 'refs/tags')
|
||||||
|
inputs:
|
||||||
|
repository: 'jellyfin/jellyfin-server'
|
||||||
|
command: buildAndPush
|
||||||
|
buildContext: '.'
|
||||||
|
Dockerfile: 'deployment/Dockerfile.docker.$(BuildConfiguration)'
|
||||||
|
containerRegistry: Docker Hub
|
||||||
|
tags: |
|
||||||
|
stable-$(Build.BuildNumber)-$(BuildConfiguration)
|
||||||
|
stable-$(BuildConfiguration)
|
||||||
|
|
||||||
|
- job: CollectArtifacts
|
||||||
|
displayName: 'Collect Artifacts'
|
||||||
|
dependsOn:
|
||||||
|
- BuildPackage
|
||||||
|
- BuildDocker
|
||||||
|
condition: and(succeeded('BuildPackage'), succeeded('BuildDocker'))
|
||||||
|
|
||||||
|
pool:
|
||||||
|
vmImage: 'ubuntu-latest'
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- task: SSH@0
|
||||||
|
displayName: 'Update Unstable Repository'
|
||||||
|
condition: startsWith(variables['Build.SourceBranch'], 'refs/heads/master')
|
||||||
|
inputs:
|
||||||
|
sshEndpoint: repository
|
||||||
|
runOptions: 'inline'
|
||||||
|
inline: 'sudo /srv/repository/collect-server.azure.sh /srv/repository/incoming/azure $(Build.BuildNumber) unstable'
|
||||||
|
|
||||||
|
- task: SSH@0
|
||||||
|
displayName: 'Update Stable Repository'
|
||||||
|
condition: startsWith(variables['Build.SourceBranch'], 'refs/tags')
|
||||||
|
inputs:
|
||||||
|
sshEndpoint: repository
|
||||||
|
runOptions: 'inline'
|
||||||
|
inline: 'sudo /srv/repository/collect-server.azure.sh /srv/repository/incoming/azure $(Build.BuildNumber)'
|
@ -43,3 +43,5 @@ jobs:
|
|||||||
NugetPackageName: Jellyfin.Common
|
NugetPackageName: Jellyfin.Common
|
||||||
AssemblyFileName: MediaBrowser.Common.dll
|
AssemblyFileName: MediaBrowser.Common.dll
|
||||||
LinuxImage: 'ubuntu-latest'
|
LinuxImage: 'ubuntu-latest'
|
||||||
|
|
||||||
|
- template: azure-pipelines-package.yml
|
||||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -39,7 +39,6 @@ ProgramData*/
|
|||||||
CorePlugins*/
|
CorePlugins*/
|
||||||
ProgramData-Server*/
|
ProgramData-Server*/
|
||||||
ProgramData-UI*/
|
ProgramData-UI*/
|
||||||
MediaBrowser.WebDashboard/jellyfin-web/**
|
|
||||||
|
|
||||||
#################
|
#################
|
||||||
## Visual Studio
|
## Visual Studio
|
||||||
@ -276,4 +275,4 @@ BenchmarkDotNet.Artifacts
|
|||||||
# Ignore web artifacts from native builds
|
# Ignore web artifacts from native builds
|
||||||
web/
|
web/
|
||||||
web-src.*
|
web-src.*
|
||||||
MediaBrowser.WebDashboard/jellyfin-web/
|
MediaBrowser.WebDashboard/jellyfin-web
|
||||||
|
12
.vscode/launch.json
vendored
12
.vscode/launch.json
vendored
@ -1,9 +1,6 @@
|
|||||||
{
|
{
|
||||||
// Use IntelliSense to find out which attributes exist for C# debugging
|
"version": "0.2.0",
|
||||||
// Use hover for the description of the existing attributes
|
"configurations": [
|
||||||
// For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
|
|
||||||
"version": "0.2.0",
|
|
||||||
"configurations": [
|
|
||||||
{
|
{
|
||||||
"name": ".NET Core Launch (console)",
|
"name": ".NET Core Launch (console)",
|
||||||
"type": "coreclr",
|
"type": "coreclr",
|
||||||
@ -24,5 +21,8 @@
|
|||||||
"request": "attach",
|
"request": "attach",
|
||||||
"processId": "${command:pickProcess}"
|
"processId": "${command:pickProcess}"
|
||||||
}
|
}
|
||||||
,]
|
],
|
||||||
|
"env": {
|
||||||
|
"DOTNET_CLI_TELEMETRY_OPTOUT": "1"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
7
.vscode/tasks.json
vendored
7
.vscode/tasks.json
vendored
@ -21,5 +21,10 @@
|
|||||||
],
|
],
|
||||||
"problemMatcher": "$msCompile"
|
"problemMatcher": "$msCompile"
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"options": {
|
||||||
|
"env": {
|
||||||
|
"DOTNET_CLI_TELEMETRY_OPTOUT": "1"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -566,10 +566,8 @@ namespace Emby.Server.Implementations
|
|||||||
serviceCollection.AddTransient(provider => new Lazy<IDtoService>(provider.GetRequiredService<IDtoService>));
|
serviceCollection.AddTransient(provider => new Lazy<IDtoService>(provider.GetRequiredService<IDtoService>));
|
||||||
|
|
||||||
// TODO: Refactor to eliminate the circular dependency here so that Lazy<T> isn't required
|
// TODO: Refactor to eliminate the circular dependency here so that Lazy<T> isn't required
|
||||||
// TODO: Add StartupOptions.FFmpegPath to IConfiguration and remove this custom activation
|
|
||||||
serviceCollection.AddTransient(provider => new Lazy<EncodingHelper>(provider.GetRequiredService<EncodingHelper>));
|
serviceCollection.AddTransient(provider => new Lazy<EncodingHelper>(provider.GetRequiredService<EncodingHelper>));
|
||||||
serviceCollection.AddSingleton<IMediaEncoder>(provider =>
|
serviceCollection.AddSingleton<IMediaEncoder, MediaBrowser.MediaEncoding.Encoder.MediaEncoder>();
|
||||||
ActivatorUtilities.CreateInstance<MediaBrowser.MediaEncoding.Encoder.MediaEncoder>(provider, _startupOptions.FFmpegPath ?? string.Empty));
|
|
||||||
|
|
||||||
// TODO: Refactor to eliminate the circular dependencies here so that Lazy<T> isn't required
|
// TODO: Refactor to eliminate the circular dependencies here so that Lazy<T> isn't required
|
||||||
serviceCollection.AddTransient(provider => new Lazy<ILibraryMonitor>(provider.GetRequiredService<ILibraryMonitor>));
|
serviceCollection.AddTransient(provider => new Lazy<ILibraryMonitor>(provider.GetRequiredService<ILibraryMonitor>));
|
||||||
|
@ -363,60 +363,4 @@ namespace Emby.Server.Implementations.Collections
|
|||||||
return results.Values;
|
return results.Values;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The collection manager entry point.
|
|
||||||
/// </summary>
|
|
||||||
public sealed class CollectionManagerEntryPoint : IServerEntryPoint
|
|
||||||
{
|
|
||||||
private readonly CollectionManager _collectionManager;
|
|
||||||
private readonly IServerConfigurationManager _config;
|
|
||||||
private readonly ILogger<CollectionManagerEntryPoint> _logger;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="CollectionManagerEntryPoint"/> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="collectionManager">The collection manager.</param>
|
|
||||||
/// <param name="config">The server configuration manager.</param>
|
|
||||||
/// <param name="logger">The logger.</param>
|
|
||||||
public CollectionManagerEntryPoint(
|
|
||||||
ICollectionManager collectionManager,
|
|
||||||
IServerConfigurationManager config,
|
|
||||||
ILogger<CollectionManagerEntryPoint> logger)
|
|
||||||
{
|
|
||||||
_collectionManager = (CollectionManager)collectionManager;
|
|
||||||
_config = config;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public async Task RunAsync()
|
|
||||||
{
|
|
||||||
if (!_config.Configuration.CollectionsUpgraded && _config.Configuration.IsStartupWizardCompleted)
|
|
||||||
{
|
|
||||||
var path = _collectionManager.GetCollectionsFolderPath();
|
|
||||||
|
|
||||||
if (Directory.Exists(path))
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await _collectionManager.EnsureLibraryFolder(path, true).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Error creating camera uploads library");
|
|
||||||
}
|
|
||||||
|
|
||||||
_config.Configuration.CollectionsUpgraded = true;
|
|
||||||
_config.SaveConfiguration();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
// Nothing to dispose
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -109,7 +109,6 @@ namespace Emby.Server.Implementations.Configuration
|
|||||||
if (!string.IsNullOrWhiteSpace(newPath)
|
if (!string.IsNullOrWhiteSpace(newPath)
|
||||||
&& !string.Equals(Configuration.CertificatePath, newPath, StringComparison.Ordinal))
|
&& !string.Equals(Configuration.CertificatePath, newPath, StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
// Validate
|
|
||||||
if (!File.Exists(newPath))
|
if (!File.Exists(newPath))
|
||||||
{
|
{
|
||||||
throw new FileNotFoundException(
|
throw new FileNotFoundException(
|
||||||
@ -133,7 +132,6 @@ namespace Emby.Server.Implementations.Configuration
|
|||||||
if (!string.IsNullOrWhiteSpace(newPath)
|
if (!string.IsNullOrWhiteSpace(newPath)
|
||||||
&& !string.Equals(Configuration.MetadataPath, newPath, StringComparison.Ordinal))
|
&& !string.Equals(Configuration.MetadataPath, newPath, StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
// Validate
|
|
||||||
if (!Directory.Exists(newPath))
|
if (!Directory.Exists(newPath))
|
||||||
{
|
{
|
||||||
throw new DirectoryNotFoundException(
|
throw new DirectoryNotFoundException(
|
||||||
@ -146,60 +144,5 @@ namespace Emby.Server.Implementations.Configuration
|
|||||||
EnsureWriteAccess(newPath);
|
EnsureWriteAccess(newPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sets all configuration values to their optimal values.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>If the configuration changed.</returns>
|
|
||||||
public bool SetOptimalValues()
|
|
||||||
{
|
|
||||||
var config = Configuration;
|
|
||||||
|
|
||||||
var changed = false;
|
|
||||||
|
|
||||||
if (!config.EnableCaseSensitiveItemIds)
|
|
||||||
{
|
|
||||||
config.EnableCaseSensitiveItemIds = true;
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!config.SkipDeserializationForBasicTypes)
|
|
||||||
{
|
|
||||||
config.SkipDeserializationForBasicTypes = true;
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!config.EnableSimpleArtistDetection)
|
|
||||||
{
|
|
||||||
config.EnableSimpleArtistDetection = true;
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!config.EnableNormalizedItemByNameIds)
|
|
||||||
{
|
|
||||||
config.EnableNormalizedItemByNameIds = true;
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!config.DisableLiveTvChannelUserDataName)
|
|
||||||
{
|
|
||||||
config.DisableLiveTvChannelUserDataName = true;
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!config.EnableNewOmdbSupport)
|
|
||||||
{
|
|
||||||
config.EnableNewOmdbSupport = true;
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!config.CollectionsUpgraded)
|
|
||||||
{
|
|
||||||
config.CollectionsUpgraded = true;
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return changed;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -585,7 +585,7 @@ namespace Emby.Server.Implementations.HttpServer
|
|||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(rangeHeader) && totalContentLength.HasValue)
|
if (!string.IsNullOrWhiteSpace(rangeHeader) && totalContentLength.HasValue)
|
||||||
{
|
{
|
||||||
var hasHeaders = new RangeRequestWriter(rangeHeader, totalContentLength.Value, stream, contentType, isHeadRequest, _logger)
|
var hasHeaders = new RangeRequestWriter(rangeHeader, totalContentLength.Value, stream, contentType, isHeadRequest)
|
||||||
{
|
{
|
||||||
OnComplete = options.OnComplete
|
OnComplete = options.OnComplete
|
||||||
};
|
};
|
||||||
@ -622,8 +622,11 @@ namespace Emby.Server.Implementations.HttpServer
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds the caching responseHeaders.
|
/// Adds the caching responseHeaders.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void AddCachingHeaders(IDictionary<string, string> responseHeaders, TimeSpan? cacheDuration,
|
private void AddCachingHeaders(
|
||||||
bool noCache, DateTime? lastModifiedDate)
|
IDictionary<string, string> responseHeaders,
|
||||||
|
TimeSpan? cacheDuration,
|
||||||
|
bool noCache,
|
||||||
|
DateTime? lastModifiedDate)
|
||||||
{
|
{
|
||||||
if (noCache)
|
if (noCache)
|
||||||
{
|
{
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Buffers;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
@ -8,52 +9,17 @@ using System.Net;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MediaBrowser.Model.Services;
|
using MediaBrowser.Model.Services;
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Microsoft.Net.Http.Headers;
|
using Microsoft.Net.Http.Headers;
|
||||||
|
|
||||||
namespace Emby.Server.Implementations.HttpServer
|
namespace Emby.Server.Implementations.HttpServer
|
||||||
{
|
{
|
||||||
public class RangeRequestWriter : IAsyncStreamWriter, IHttpResult
|
public class RangeRequestWriter : IAsyncStreamWriter, IHttpResult
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the source stream.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The source stream.</value>
|
|
||||||
private Stream SourceStream { get; set; }
|
|
||||||
|
|
||||||
private string RangeHeader { get; set; }
|
|
||||||
|
|
||||||
private bool IsHeadRequest { get; set; }
|
|
||||||
|
|
||||||
private long RangeStart { get; set; }
|
|
||||||
|
|
||||||
private long RangeEnd { get; set; }
|
|
||||||
|
|
||||||
private long RangeLength { get; set; }
|
|
||||||
|
|
||||||
private long TotalContentLength { get; set; }
|
|
||||||
|
|
||||||
public Action OnComplete { get; set; }
|
|
||||||
|
|
||||||
private readonly ILogger _logger;
|
|
||||||
|
|
||||||
private const int BufferSize = 81920;
|
private const int BufferSize = 81920;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The _options.
|
|
||||||
/// </summary>
|
|
||||||
private readonly Dictionary<string, string> _options = new Dictionary<string, string>();
|
private readonly Dictionary<string, string> _options = new Dictionary<string, string>();
|
||||||
|
|
||||||
/// <summary>
|
private List<KeyValuePair<long, long?>> _requestedRanges;
|
||||||
/// The us culture.
|
|
||||||
/// </summary>
|
|
||||||
private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Additional HTTP Headers.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The headers.</value>
|
|
||||||
public IDictionary<string, string> Headers => _options;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="RangeRequestWriter" /> class.
|
/// Initializes a new instance of the <see cref="RangeRequestWriter" /> class.
|
||||||
@ -63,8 +29,7 @@ namespace Emby.Server.Implementations.HttpServer
|
|||||||
/// <param name="source">The source.</param>
|
/// <param name="source">The source.</param>
|
||||||
/// <param name="contentType">Type of the content.</param>
|
/// <param name="contentType">Type of the content.</param>
|
||||||
/// <param name="isHeadRequest">if set to <c>true</c> [is head request].</param>
|
/// <param name="isHeadRequest">if set to <c>true</c> [is head request].</param>
|
||||||
/// <param name="logger">The logger instance.</param>
|
public RangeRequestWriter(string rangeHeader, long contentLength, Stream source, string contentType, bool isHeadRequest)
|
||||||
public RangeRequestWriter(string rangeHeader, long contentLength, Stream source, string contentType, bool isHeadRequest, ILogger logger)
|
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(contentType))
|
if (string.IsNullOrEmpty(contentType))
|
||||||
{
|
{
|
||||||
@ -74,7 +39,6 @@ namespace Emby.Server.Implementations.HttpServer
|
|||||||
RangeHeader = rangeHeader;
|
RangeHeader = rangeHeader;
|
||||||
SourceStream = source;
|
SourceStream = source;
|
||||||
IsHeadRequest = isHeadRequest;
|
IsHeadRequest = isHeadRequest;
|
||||||
this._logger = logger;
|
|
||||||
|
|
||||||
ContentType = contentType;
|
ContentType = contentType;
|
||||||
Headers[HeaderNames.ContentType] = contentType;
|
Headers[HeaderNames.ContentType] = contentType;
|
||||||
@ -84,6 +48,81 @@ namespace Emby.Server.Implementations.HttpServer
|
|||||||
SetRangeValues(contentLength);
|
SetRangeValues(contentLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the source stream.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The source stream.</value>
|
||||||
|
private Stream SourceStream { get; set; }
|
||||||
|
private string RangeHeader { get; set; }
|
||||||
|
private bool IsHeadRequest { get; set; }
|
||||||
|
|
||||||
|
private long RangeStart { get; set; }
|
||||||
|
private long RangeEnd { get; set; }
|
||||||
|
private long RangeLength { get; set; }
|
||||||
|
private long TotalContentLength { get; set; }
|
||||||
|
|
||||||
|
public Action OnComplete { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Additional HTTP Headers
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The headers.</value>
|
||||||
|
public IDictionary<string, string> Headers => _options;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the requested ranges.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The requested ranges.</value>
|
||||||
|
protected List<KeyValuePair<long, long?>> RequestedRanges
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_requestedRanges == null)
|
||||||
|
{
|
||||||
|
_requestedRanges = new List<KeyValuePair<long, long?>>();
|
||||||
|
|
||||||
|
// Example: bytes=0-,32-63
|
||||||
|
var ranges = RangeHeader.Split('=')[1].Split(',');
|
||||||
|
|
||||||
|
foreach (var range in ranges)
|
||||||
|
{
|
||||||
|
var vals = range.Split('-');
|
||||||
|
|
||||||
|
long start = 0;
|
||||||
|
long? end = null;
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(vals[0]))
|
||||||
|
{
|
||||||
|
start = long.Parse(vals[0], CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(vals[1]))
|
||||||
|
{
|
||||||
|
end = long.Parse(vals[1], CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
|
||||||
|
_requestedRanges.Add(new KeyValuePair<long, long?>(start, end));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return _requestedRanges;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ContentType { get; set; }
|
||||||
|
|
||||||
|
public IRequest RequestContext { get; set; }
|
||||||
|
|
||||||
|
public object Response { get; set; }
|
||||||
|
|
||||||
|
public int Status { get; set; }
|
||||||
|
|
||||||
|
public HttpStatusCode StatusCode
|
||||||
|
{
|
||||||
|
get => (HttpStatusCode)Status;
|
||||||
|
set => Status = (int)value;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets the range values.
|
/// Sets the range values.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -115,50 +154,6 @@ namespace Emby.Server.Implementations.HttpServer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The _requested ranges.
|
|
||||||
/// </summary>
|
|
||||||
private List<KeyValuePair<long, long?>> _requestedRanges;
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the requested ranges.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The requested ranges.</value>
|
|
||||||
protected List<KeyValuePair<long, long?>> RequestedRanges
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (_requestedRanges == null)
|
|
||||||
{
|
|
||||||
_requestedRanges = new List<KeyValuePair<long, long?>>();
|
|
||||||
|
|
||||||
// Example: bytes=0-,32-63
|
|
||||||
var ranges = RangeHeader.Split('=')[1].Split(',');
|
|
||||||
|
|
||||||
foreach (var range in ranges)
|
|
||||||
{
|
|
||||||
var vals = range.Split('-');
|
|
||||||
|
|
||||||
long start = 0;
|
|
||||||
long? end = null;
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(vals[0]))
|
|
||||||
{
|
|
||||||
start = long.Parse(vals[0], UsCulture);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(vals[1]))
|
|
||||||
{
|
|
||||||
end = long.Parse(vals[1], UsCulture);
|
|
||||||
}
|
|
||||||
|
|
||||||
_requestedRanges.Add(new KeyValuePair<long, long?>(start, end));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return _requestedRanges;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task WriteToAsync(Stream responseStream, CancellationToken cancellationToken)
|
public async Task WriteToAsync(Stream responseStream, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@ -174,59 +169,44 @@ namespace Emby.Server.Implementations.HttpServer
|
|||||||
// If the requested range is "0-", we can optimize by just doing a stream copy
|
// If the requested range is "0-", we can optimize by just doing a stream copy
|
||||||
if (RangeEnd >= TotalContentLength - 1)
|
if (RangeEnd >= TotalContentLength - 1)
|
||||||
{
|
{
|
||||||
await source.CopyToAsync(responseStream, BufferSize).ConfigureAwait(false);
|
await source.CopyToAsync(responseStream, BufferSize, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await CopyToInternalAsync(source, responseStream, RangeLength).ConfigureAwait(false);
|
await CopyToInternalAsync(source, responseStream, RangeLength, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
if (OnComplete != null)
|
OnComplete?.Invoke();
|
||||||
{
|
|
||||||
OnComplete();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task CopyToInternalAsync(Stream source, Stream destination, long copyLength)
|
private static async Task CopyToInternalAsync(Stream source, Stream destination, long copyLength, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var array = new byte[BufferSize];
|
var array = ArrayPool<byte>.Shared.Rent(BufferSize);
|
||||||
int bytesRead;
|
try
|
||||||
while ((bytesRead = await source.ReadAsync(array, 0, array.Length).ConfigureAwait(false)) != 0)
|
|
||||||
{
|
{
|
||||||
if (bytesRead == 0)
|
int bytesRead;
|
||||||
|
while ((bytesRead = await source.ReadAsync(array, 0, array.Length, cancellationToken).ConfigureAwait(false)) != 0)
|
||||||
{
|
{
|
||||||
break;
|
var bytesToCopy = Math.Min(bytesRead, copyLength);
|
||||||
}
|
|
||||||
|
|
||||||
var bytesToCopy = Math.Min(bytesRead, copyLength);
|
await destination.WriteAsync(array, 0, Convert.ToInt32(bytesToCopy), cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
await destination.WriteAsync(array, 0, Convert.ToInt32(bytesToCopy)).ConfigureAwait(false);
|
copyLength -= bytesToCopy;
|
||||||
|
|
||||||
copyLength -= bytesToCopy;
|
if (copyLength <= 0)
|
||||||
|
{
|
||||||
if (copyLength <= 0)
|
break;
|
||||||
{
|
}
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
finally
|
||||||
|
{
|
||||||
public string ContentType { get; set; }
|
ArrayPool<byte>.Shared.Return(array);
|
||||||
|
}
|
||||||
public IRequest RequestContext { get; set; }
|
|
||||||
|
|
||||||
public object Response { get; set; }
|
|
||||||
|
|
||||||
public int Status { get; set; }
|
|
||||||
|
|
||||||
public HttpStatusCode StatusCode
|
|
||||||
{
|
|
||||||
get => (HttpStatusCode)Status;
|
|
||||||
set => Status = (int)value;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using MediaBrowser.Controller;
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Controller.Library;
|
using MediaBrowser.Controller.Library;
|
||||||
using MediaBrowser.Controller.Resolvers;
|
using MediaBrowser.Controller.Resolvers;
|
||||||
@ -13,19 +14,28 @@ namespace Emby.Server.Implementations.Library
|
|||||||
public class CoreResolutionIgnoreRule : IResolverIgnoreRule
|
public class CoreResolutionIgnoreRule : IResolverIgnoreRule
|
||||||
{
|
{
|
||||||
private readonly ILibraryManager _libraryManager;
|
private readonly ILibraryManager _libraryManager;
|
||||||
|
private readonly IServerApplicationPaths _serverApplicationPaths;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="CoreResolutionIgnoreRule"/> class.
|
/// Initializes a new instance of the <see cref="CoreResolutionIgnoreRule"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="libraryManager">The library manager.</param>
|
/// <param name="libraryManager">The library manager.</param>
|
||||||
public CoreResolutionIgnoreRule(ILibraryManager libraryManager)
|
/// <param name="serverApplicationPaths">The server application paths.</param>
|
||||||
|
public CoreResolutionIgnoreRule(ILibraryManager libraryManager, IServerApplicationPaths serverApplicationPaths)
|
||||||
{
|
{
|
||||||
_libraryManager = libraryManager;
|
_libraryManager = libraryManager;
|
||||||
|
_serverApplicationPaths = serverApplicationPaths;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public bool ShouldIgnore(FileSystemMetadata fileInfo, BaseItem parent)
|
public bool ShouldIgnore(FileSystemMetadata fileInfo, BaseItem parent)
|
||||||
{
|
{
|
||||||
|
// Don't ignore application folders
|
||||||
|
if (fileInfo.FullName.Contains(_serverApplicationPaths.RootFolderPath, StringComparison.InvariantCulture))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Don't ignore top level folders
|
// Don't ignore top level folders
|
||||||
if (fileInfo.IsDirectory && parent is AggregateFolder)
|
if (fileInfo.IsDirectory && parent is AggregateFolder)
|
||||||
{
|
{
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
#nullable enable
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using DotNet.Globbing;
|
using DotNet.Globbing;
|
||||||
|
|
||||||
@ -11,7 +14,7 @@ namespace Emby.Server.Implementations.Library
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Files matching these glob patterns will be ignored.
|
/// Files matching these glob patterns will be ignored.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static readonly string[] Patterns = new string[]
|
private static readonly string[] _patterns =
|
||||||
{
|
{
|
||||||
"**/small.jpg",
|
"**/small.jpg",
|
||||||
"**/albumart.jpg",
|
"**/albumart.jpg",
|
||||||
@ -19,32 +22,51 @@ namespace Emby.Server.Implementations.Library
|
|||||||
|
|
||||||
// Directories
|
// Directories
|
||||||
"**/metadata/**",
|
"**/metadata/**",
|
||||||
|
"**/metadata",
|
||||||
"**/ps3_update/**",
|
"**/ps3_update/**",
|
||||||
|
"**/ps3_update",
|
||||||
"**/ps3_vprm/**",
|
"**/ps3_vprm/**",
|
||||||
|
"**/ps3_vprm",
|
||||||
"**/extrafanart/**",
|
"**/extrafanart/**",
|
||||||
|
"**/extrafanart",
|
||||||
"**/extrathumbs/**",
|
"**/extrathumbs/**",
|
||||||
|
"**/extrathumbs",
|
||||||
"**/.actors/**",
|
"**/.actors/**",
|
||||||
|
"**/.actors",
|
||||||
"**/.wd_tv/**",
|
"**/.wd_tv/**",
|
||||||
|
"**/.wd_tv",
|
||||||
"**/lost+found/**",
|
"**/lost+found/**",
|
||||||
|
"**/lost+found",
|
||||||
|
|
||||||
// WMC temp recording directories that will constantly be written to
|
// WMC temp recording directories that will constantly be written to
|
||||||
"**/TempRec/**",
|
"**/TempRec/**",
|
||||||
|
"**/TempRec",
|
||||||
"**/TempSBE/**",
|
"**/TempSBE/**",
|
||||||
|
"**/TempSBE",
|
||||||
|
|
||||||
// Synology
|
// Synology
|
||||||
"**/eaDir/**",
|
"**/eaDir/**",
|
||||||
|
"**/eaDir",
|
||||||
"**/@eaDir/**",
|
"**/@eaDir/**",
|
||||||
|
"**/@eaDir",
|
||||||
"**/#recycle/**",
|
"**/#recycle/**",
|
||||||
|
"**/#recycle",
|
||||||
|
|
||||||
// Qnap
|
// Qnap
|
||||||
"**/@Recycle/**",
|
"**/@Recycle/**",
|
||||||
|
"**/@Recycle",
|
||||||
"**/.@__thumb/**",
|
"**/.@__thumb/**",
|
||||||
|
"**/.@__thumb",
|
||||||
"**/$RECYCLE.BIN/**",
|
"**/$RECYCLE.BIN/**",
|
||||||
|
"**/$RECYCLE.BIN",
|
||||||
"**/System Volume Information/**",
|
"**/System Volume Information/**",
|
||||||
|
"**/System Volume Information",
|
||||||
"**/.grab/**",
|
"**/.grab/**",
|
||||||
|
"**/.grab",
|
||||||
|
|
||||||
// Unix hidden files and directories
|
// Unix hidden files and directories
|
||||||
"**/.*/**",
|
"**/.*/**",
|
||||||
|
"**/.*",
|
||||||
|
|
||||||
// thumbs.db
|
// thumbs.db
|
||||||
"**/thumbs.db",
|
"**/thumbs.db",
|
||||||
@ -56,19 +78,31 @@ namespace Emby.Server.Implementations.Library
|
|||||||
|
|
||||||
private static readonly GlobOptions _globOptions = new GlobOptions
|
private static readonly GlobOptions _globOptions = new GlobOptions
|
||||||
{
|
{
|
||||||
Evaluation = {
|
Evaluation =
|
||||||
|
{
|
||||||
CaseInsensitive = true
|
CaseInsensitive = true
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private static readonly Glob[] _globs = Patterns.Select(p => Glob.Parse(p, _globOptions)).ToArray();
|
private static readonly Glob[] _globs = _patterns.Select(p => Glob.Parse(p, _globOptions)).ToArray();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns true if the supplied path should be ignored.
|
/// Returns true if the supplied path should be ignored.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static bool ShouldIgnore(string path)
|
/// <param name="path">The path to test.</param>
|
||||||
|
/// <returns>Whether to ignore the path.</returns>
|
||||||
|
public static bool ShouldIgnore(ReadOnlySpan<char> path)
|
||||||
{
|
{
|
||||||
return _globs.Any(g => g.IsMatch(path));
|
int len = _globs.Length;
|
||||||
|
for (int i = 0; i < len; i++)
|
||||||
|
{
|
||||||
|
if (_globs[i].IsMatch(path))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -514,8 +514,8 @@ namespace Emby.Server.Implementations.Library
|
|||||||
return key.GetMD5();
|
return key.GetMD5();
|
||||||
}
|
}
|
||||||
|
|
||||||
public BaseItem ResolvePath(FileSystemMetadata fileInfo, Folder parent = null, bool allowIgnorePath = true)
|
public BaseItem ResolvePath(FileSystemMetadata fileInfo, Folder parent = null)
|
||||||
=> ResolvePath(fileInfo, new DirectoryService(_fileSystem), null, parent, allowIgnorePath: allowIgnorePath);
|
=> ResolvePath(fileInfo, new DirectoryService(_fileSystem), null, parent);
|
||||||
|
|
||||||
private BaseItem ResolvePath(
|
private BaseItem ResolvePath(
|
||||||
FileSystemMetadata fileInfo,
|
FileSystemMetadata fileInfo,
|
||||||
@ -523,8 +523,7 @@ namespace Emby.Server.Implementations.Library
|
|||||||
IItemResolver[] resolvers,
|
IItemResolver[] resolvers,
|
||||||
Folder parent = null,
|
Folder parent = null,
|
||||||
string collectionType = null,
|
string collectionType = null,
|
||||||
LibraryOptions libraryOptions = null,
|
LibraryOptions libraryOptions = null)
|
||||||
bool allowIgnorePath = true)
|
|
||||||
{
|
{
|
||||||
if (fileInfo == null)
|
if (fileInfo == null)
|
||||||
{
|
{
|
||||||
@ -548,7 +547,7 @@ namespace Emby.Server.Implementations.Library
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Return null if ignore rules deem that we should do so
|
// Return null if ignore rules deem that we should do so
|
||||||
if (allowIgnorePath && IgnoreFile(args.FileInfo, args.Parent))
|
if (IgnoreFile(args.FileInfo, args.Parent))
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -713,7 +712,7 @@ namespace Emby.Server.Implementations.Library
|
|||||||
Directory.CreateDirectory(rootFolderPath);
|
Directory.CreateDirectory(rootFolderPath);
|
||||||
|
|
||||||
var rootFolder = GetItemById(GetNewItemId(rootFolderPath, typeof(AggregateFolder))) as AggregateFolder ??
|
var rootFolder = GetItemById(GetNewItemId(rootFolderPath, typeof(AggregateFolder))) as AggregateFolder ??
|
||||||
((Folder) ResolvePath(_fileSystem.GetDirectoryInfo(rootFolderPath), allowIgnorePath: false))
|
((Folder) ResolvePath(_fileSystem.GetDirectoryInfo(rootFolderPath)))
|
||||||
.DeepCopy<Folder, AggregateFolder>();
|
.DeepCopy<Folder, AggregateFolder>();
|
||||||
|
|
||||||
// In case program data folder was moved
|
// In case program data folder was moved
|
||||||
@ -795,7 +794,7 @@ namespace Emby.Server.Implementations.Library
|
|||||||
if (tmpItem == null)
|
if (tmpItem == null)
|
||||||
{
|
{
|
||||||
_logger.LogDebug("Creating new userRootFolder with DeepCopy");
|
_logger.LogDebug("Creating new userRootFolder with DeepCopy");
|
||||||
tmpItem = ((Folder)ResolvePath(_fileSystem.GetDirectoryInfo(userRootPath), allowIgnorePath: false)).DeepCopy<Folder, UserRootFolder>();
|
tmpItem = ((Folder)ResolvePath(_fileSystem.GetDirectoryInfo(userRootPath))).DeepCopy<Folder, UserRootFolder>();
|
||||||
}
|
}
|
||||||
|
|
||||||
// In case program data folder was moved
|
// In case program data folder was moved
|
||||||
@ -1894,9 +1893,19 @@ namespace Emby.Server.Implementations.Library
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageDimensions size = _imageProcessor.GetImageDimensions(item, image);
|
try
|
||||||
image.Width = size.Width;
|
{
|
||||||
image.Height = size.Height;
|
ImageDimensions size = _imageProcessor.GetImageDimensions(item, image);
|
||||||
|
image.Width = size.Width;
|
||||||
|
image.Height = size.Height;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Cannnot get image dimensions for {0}", image.Path);
|
||||||
|
image.Width = 0;
|
||||||
|
image.Height = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"Albums": "ألبومات",
|
"Albums": "البومات",
|
||||||
"AppDeviceValues": "تطبيق: {0}, جهاز: {1}",
|
"AppDeviceValues": "تطبيق: {0}, جهاز: {1}",
|
||||||
"Application": "تطبيق",
|
"Application": "تطبيق",
|
||||||
"Artists": "الفنانين",
|
"Artists": "الفنانين",
|
||||||
@ -14,7 +14,7 @@
|
|||||||
"FailedLoginAttemptWithUserName": "عملية تسجيل الدخول فشلت من {0}",
|
"FailedLoginAttemptWithUserName": "عملية تسجيل الدخول فشلت من {0}",
|
||||||
"Favorites": "المفضلة",
|
"Favorites": "المفضلة",
|
||||||
"Folders": "المجلدات",
|
"Folders": "المجلدات",
|
||||||
"Genres": "الأنواع",
|
"Genres": "التضنيفات",
|
||||||
"HeaderAlbumArtists": "فناني الألبومات",
|
"HeaderAlbumArtists": "فناني الألبومات",
|
||||||
"HeaderCameraUploads": "تحميلات الكاميرا",
|
"HeaderCameraUploads": "تحميلات الكاميرا",
|
||||||
"HeaderContinueWatching": "استئناف",
|
"HeaderContinueWatching": "استئناف",
|
||||||
@ -50,7 +50,7 @@
|
|||||||
"NotificationOptionAudioPlayback": "بدأ تشغيل المقطع الصوتي",
|
"NotificationOptionAudioPlayback": "بدأ تشغيل المقطع الصوتي",
|
||||||
"NotificationOptionAudioPlaybackStopped": "تم إيقاف تشغيل المقطع الصوتي",
|
"NotificationOptionAudioPlaybackStopped": "تم إيقاف تشغيل المقطع الصوتي",
|
||||||
"NotificationOptionCameraImageUploaded": "تم رفع صورة الكاميرا",
|
"NotificationOptionCameraImageUploaded": "تم رفع صورة الكاميرا",
|
||||||
"NotificationOptionInstallationFailed": "فشل في التثبيت",
|
"NotificationOptionInstallationFailed": "فشل التثبيت",
|
||||||
"NotificationOptionNewLibraryContent": "تم إضافة محتوى جديد",
|
"NotificationOptionNewLibraryContent": "تم إضافة محتوى جديد",
|
||||||
"NotificationOptionPluginError": "فشل في البرنامج المضاف",
|
"NotificationOptionPluginError": "فشل في البرنامج المضاف",
|
||||||
"NotificationOptionPluginInstalled": "تم تثبيت الملحق",
|
"NotificationOptionPluginInstalled": "تم تثبيت الملحق",
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
"HeaderContinueWatching": "Seguir viendo",
|
"HeaderContinueWatching": "Seguir viendo",
|
||||||
"HeaderFavoriteAlbums": "Álbumes favoritos",
|
"HeaderFavoriteAlbums": "Álbumes favoritos",
|
||||||
"HeaderFavoriteArtists": "Artistas favoritos",
|
"HeaderFavoriteArtists": "Artistas favoritos",
|
||||||
"HeaderFavoriteEpisodes": "Episodios favoritos",
|
"HeaderFavoriteEpisodes": "Capítulos favoritos",
|
||||||
"HeaderFavoriteShows": "Programas favoritos",
|
"HeaderFavoriteShows": "Programas favoritos",
|
||||||
"HeaderFavoriteSongs": "Canciones favoritas",
|
"HeaderFavoriteSongs": "Canciones favoritas",
|
||||||
"HeaderLiveTV": "TV en vivo",
|
"HeaderLiveTV": "TV en vivo",
|
||||||
|
@ -31,7 +31,7 @@
|
|||||||
"ItemAddedWithName": "{0} fue agregado a la biblioteca",
|
"ItemAddedWithName": "{0} fue agregado a la biblioteca",
|
||||||
"ItemRemovedWithName": "{0} fue removido de la biblioteca",
|
"ItemRemovedWithName": "{0} fue removido de la biblioteca",
|
||||||
"LabelIpAddressValue": "Dirección IP: {0}",
|
"LabelIpAddressValue": "Dirección IP: {0}",
|
||||||
"LabelRunningTimeValue": "Duración: {0}",
|
"LabelRunningTimeValue": "Tiempo de reproducción: {0}",
|
||||||
"Latest": "Recientes",
|
"Latest": "Recientes",
|
||||||
"MessageApplicationUpdated": "El servidor Jellyfin ha sido actualizado",
|
"MessageApplicationUpdated": "El servidor Jellyfin ha sido actualizado",
|
||||||
"MessageApplicationUpdatedTo": "El servidor Jellyfin ha sido actualizado a {0}",
|
"MessageApplicationUpdatedTo": "El servidor Jellyfin ha sido actualizado a {0}",
|
||||||
|
@ -5,23 +5,23 @@
|
|||||||
"Artists": "Izvođači",
|
"Artists": "Izvođači",
|
||||||
"AuthenticationSucceededWithUserName": "{0} uspješno ovjerena",
|
"AuthenticationSucceededWithUserName": "{0} uspješno ovjerena",
|
||||||
"Books": "Knjige",
|
"Books": "Knjige",
|
||||||
"CameraImageUploadedFrom": "A new camera image has been uploaded from {0}",
|
"CameraImageUploadedFrom": "Nova fotografija sa kamere je uploadana iz {0}",
|
||||||
"Channels": "Kanali",
|
"Channels": "Kanali",
|
||||||
"ChapterNameValue": "Poglavlje {0}",
|
"ChapterNameValue": "Poglavlje {0}",
|
||||||
"Collections": "Kolekcije",
|
"Collections": "Kolekcije",
|
||||||
"DeviceOfflineWithName": "{0} se odspojilo",
|
"DeviceOfflineWithName": "{0} se odspojilo",
|
||||||
"DeviceOnlineWithName": "{0} je spojeno",
|
"DeviceOnlineWithName": "{0} je spojeno",
|
||||||
"FailedLoginAttemptWithUserName": "Neuspjeli pokušaj prijave za {0}",
|
"FailedLoginAttemptWithUserName": "Neuspjeli pokušaj prijave za {0}",
|
||||||
"Favorites": "Omiljeni",
|
"Favorites": "Favoriti",
|
||||||
"Folders": "Mape",
|
"Folders": "Mape",
|
||||||
"Genres": "Žanrovi",
|
"Genres": "Žanrovi",
|
||||||
"HeaderAlbumArtists": "Izvođači albuma",
|
"HeaderAlbumArtists": "Izvođači na albumu",
|
||||||
"HeaderCameraUploads": "Camera Uploads",
|
"HeaderCameraUploads": "Uvoz sa kamere",
|
||||||
"HeaderContinueWatching": "Continue Watching",
|
"HeaderContinueWatching": "Nastavi gledati",
|
||||||
"HeaderFavoriteAlbums": "Omiljeni albumi",
|
"HeaderFavoriteAlbums": "Omiljeni albumi",
|
||||||
"HeaderFavoriteArtists": "Omiljeni izvođači",
|
"HeaderFavoriteArtists": "Omiljeni izvođači",
|
||||||
"HeaderFavoriteEpisodes": "Omiljene epizode",
|
"HeaderFavoriteEpisodes": "Omiljene epizode",
|
||||||
"HeaderFavoriteShows": "Omiljene emisije",
|
"HeaderFavoriteShows": "Omiljene serije",
|
||||||
"HeaderFavoriteSongs": "Omiljene pjesme",
|
"HeaderFavoriteSongs": "Omiljene pjesme",
|
||||||
"HeaderLiveTV": "TV uživo",
|
"HeaderLiveTV": "TV uživo",
|
||||||
"HeaderNextUp": "Sljedeće je",
|
"HeaderNextUp": "Sljedeće je",
|
||||||
@ -34,23 +34,23 @@
|
|||||||
"LabelRunningTimeValue": "Vrijeme rada: {0}",
|
"LabelRunningTimeValue": "Vrijeme rada: {0}",
|
||||||
"Latest": "Najnovije",
|
"Latest": "Najnovije",
|
||||||
"MessageApplicationUpdated": "Jellyfin Server je ažuriran",
|
"MessageApplicationUpdated": "Jellyfin Server je ažuriran",
|
||||||
"MessageApplicationUpdatedTo": "Jellyfin Server has been updated to {0}",
|
"MessageApplicationUpdatedTo": "Jellyfin Server je ažuriran na {0}",
|
||||||
"MessageNamedServerConfigurationUpdatedWithValue": "Odjeljak postavka servera {0} je ažuriran",
|
"MessageNamedServerConfigurationUpdatedWithValue": "Odjeljak postavka servera {0} je ažuriran",
|
||||||
"MessageServerConfigurationUpdated": "Postavke servera su ažurirane",
|
"MessageServerConfigurationUpdated": "Postavke servera su ažurirane",
|
||||||
"MixedContent": "Miješani sadržaj",
|
"MixedContent": "Miješani sadržaj",
|
||||||
"Movies": "Filmovi",
|
"Movies": "Filmovi",
|
||||||
"Music": "Glazba",
|
"Music": "Glazba",
|
||||||
"MusicVideos": "Glazbeni spotovi",
|
"MusicVideos": "Glazbeni spotovi",
|
||||||
"NameInstallFailed": "{0} installation failed",
|
"NameInstallFailed": "{0} neuspješnih instalacija",
|
||||||
"NameSeasonNumber": "Sezona {0}",
|
"NameSeasonNumber": "Sezona {0}",
|
||||||
"NameSeasonUnknown": "Season Unknown",
|
"NameSeasonUnknown": "Nepoznata sezona",
|
||||||
"NewVersionIsAvailable": "A new version of Jellyfin Server is available for download.",
|
"NewVersionIsAvailable": "Nova verzija Jellyfin servera je dostupna za preuzimanje.",
|
||||||
"NotificationOptionApplicationUpdateAvailable": "Dostupno ažuriranje aplikacije",
|
"NotificationOptionApplicationUpdateAvailable": "Dostupno ažuriranje aplikacije",
|
||||||
"NotificationOptionApplicationUpdateInstalled": "Instalirano ažuriranje aplikacije",
|
"NotificationOptionApplicationUpdateInstalled": "Instalirano ažuriranje aplikacije",
|
||||||
"NotificationOptionAudioPlayback": "Reprodukcija glazbe započeta",
|
"NotificationOptionAudioPlayback": "Reprodukcija glazbe započeta",
|
||||||
"NotificationOptionAudioPlaybackStopped": "Reprodukcija audiozapisa je zaustavljena",
|
"NotificationOptionAudioPlaybackStopped": "Reprodukcija audiozapisa je zaustavljena",
|
||||||
"NotificationOptionCameraImageUploaded": "Slike kamere preuzete",
|
"NotificationOptionCameraImageUploaded": "Slike kamere preuzete",
|
||||||
"NotificationOptionInstallationFailed": "Instalacija nije izvršena",
|
"NotificationOptionInstallationFailed": "Instalacija neuspješna",
|
||||||
"NotificationOptionNewLibraryContent": "Novi sadržaj je dodan",
|
"NotificationOptionNewLibraryContent": "Novi sadržaj je dodan",
|
||||||
"NotificationOptionPluginError": "Dodatak otkazao",
|
"NotificationOptionPluginError": "Dodatak otkazao",
|
||||||
"NotificationOptionPluginInstalled": "Dodatak instaliran",
|
"NotificationOptionPluginInstalled": "Dodatak instaliran",
|
||||||
@ -62,7 +62,7 @@
|
|||||||
"NotificationOptionVideoPlayback": "Reprodukcija videa započeta",
|
"NotificationOptionVideoPlayback": "Reprodukcija videa započeta",
|
||||||
"NotificationOptionVideoPlaybackStopped": "Reprodukcija videozapisa je zaustavljena",
|
"NotificationOptionVideoPlaybackStopped": "Reprodukcija videozapisa je zaustavljena",
|
||||||
"Photos": "Slike",
|
"Photos": "Slike",
|
||||||
"Playlists": "Popisi",
|
"Playlists": "Popis za reprodukciju",
|
||||||
"Plugin": "Dodatak",
|
"Plugin": "Dodatak",
|
||||||
"PluginInstalledWithName": "{0} je instalirano",
|
"PluginInstalledWithName": "{0} je instalirano",
|
||||||
"PluginUninstalledWithName": "{0} je deinstalirano",
|
"PluginUninstalledWithName": "{0} je deinstalirano",
|
||||||
@ -70,15 +70,15 @@
|
|||||||
"ProviderValue": "Pružitelj: {0}",
|
"ProviderValue": "Pružitelj: {0}",
|
||||||
"ScheduledTaskFailedWithName": "{0} neuspjelo",
|
"ScheduledTaskFailedWithName": "{0} neuspjelo",
|
||||||
"ScheduledTaskStartedWithName": "{0} pokrenuto",
|
"ScheduledTaskStartedWithName": "{0} pokrenuto",
|
||||||
"ServerNameNeedsToBeRestarted": "{0} needs to be restarted",
|
"ServerNameNeedsToBeRestarted": "{0} treba biti ponovno pokrenuto",
|
||||||
"Shows": "Shows",
|
"Shows": "Serije",
|
||||||
"Songs": "Pjesme",
|
"Songs": "Pjesme",
|
||||||
"StartupEmbyServerIsLoading": "Jellyfin Server se učitava. Pokušajte ponovo kasnije.",
|
"StartupEmbyServerIsLoading": "Jellyfin Server se učitava. Pokušajte ponovo kasnije.",
|
||||||
"SubtitleDownloadFailureForItem": "Titlovi prijevoda nisu preuzeti za {0}",
|
"SubtitleDownloadFailureForItem": "Titlovi prijevoda nisu preuzeti za {0}",
|
||||||
"SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
|
"SubtitleDownloadFailureFromForItem": "Prijevodi nisu uspješno preuzeti {0} od {1}",
|
||||||
"Sync": "Sink.",
|
"Sync": "Sink.",
|
||||||
"System": "Sistem",
|
"System": "Sistem",
|
||||||
"TvShows": "TV Shows",
|
"TvShows": "Serije",
|
||||||
"User": "Korisnik",
|
"User": "Korisnik",
|
||||||
"UserCreatedWithName": "Korisnik {0} je stvoren",
|
"UserCreatedWithName": "Korisnik {0} je stvoren",
|
||||||
"UserDeletedWithName": "Korisnik {0} je obrisan",
|
"UserDeletedWithName": "Korisnik {0} je obrisan",
|
||||||
@ -87,10 +87,10 @@
|
|||||||
"UserOfflineFromDevice": "{0} se odspojilo od {1}",
|
"UserOfflineFromDevice": "{0} se odspojilo od {1}",
|
||||||
"UserOnlineFromDevice": "{0} je online od {1}",
|
"UserOnlineFromDevice": "{0} je online od {1}",
|
||||||
"UserPasswordChangedWithName": "Lozinka je promijenjena za korisnika {0}",
|
"UserPasswordChangedWithName": "Lozinka je promijenjena za korisnika {0}",
|
||||||
"UserPolicyUpdatedWithName": "User policy has been updated for {0}",
|
"UserPolicyUpdatedWithName": "Pravila za korisnika su ažurirana za {0}",
|
||||||
"UserStartedPlayingItemWithValues": "{0} je pokrenuo {1}",
|
"UserStartedPlayingItemWithValues": "{0} je pokrenuo {1}",
|
||||||
"UserStoppedPlayingItemWithValues": "{0} je zaustavio {1}",
|
"UserStoppedPlayingItemWithValues": "{0} je zaustavio {1}",
|
||||||
"ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
|
"ValueHasBeenAddedToLibrary": "{0} je dodano u medijsku biblioteku",
|
||||||
"ValueSpecialEpisodeName": "Specijal - {0}",
|
"ValueSpecialEpisodeName": "Specijal - {0}",
|
||||||
"VersionNumber": "Verzija {0}",
|
"VersionNumber": "Verzija {0}",
|
||||||
"TaskRefreshLibraryDescription": "Skenira vašu medijsku knjižnicu sa novim datotekama i osvježuje metapodatke.",
|
"TaskRefreshLibraryDescription": "Skenira vašu medijsku knjižnicu sa novim datotekama i osvježuje metapodatke.",
|
||||||
@ -100,5 +100,19 @@
|
|||||||
"TaskCleanCacheDescription": "Briše priručne datoteke nepotrebne za sistem.",
|
"TaskCleanCacheDescription": "Briše priručne datoteke nepotrebne za sistem.",
|
||||||
"TaskCleanCache": "Očisti priručnu memoriju",
|
"TaskCleanCache": "Očisti priručnu memoriju",
|
||||||
"TasksApplicationCategory": "Aplikacija",
|
"TasksApplicationCategory": "Aplikacija",
|
||||||
"TasksMaintenanceCategory": "Održavanje"
|
"TasksMaintenanceCategory": "Održavanje",
|
||||||
|
"TaskDownloadMissingSubtitlesDescription": "Pretraživanje interneta za prijevodima koji nedostaju bazirano na konfiguraciji meta podataka.",
|
||||||
|
"TaskDownloadMissingSubtitles": "Preuzimanje prijevoda koji nedostaju",
|
||||||
|
"TaskRefreshChannelsDescription": "Osvježava informacije o internet kanalima.",
|
||||||
|
"TaskRefreshChannels": "Osvježi kanale",
|
||||||
|
"TaskCleanTranscodeDescription": "Briše transkodirane fajlove starije od jednog dana.",
|
||||||
|
"TaskCleanTranscode": "Očisti direktorij za transkodiranje",
|
||||||
|
"TaskUpdatePluginsDescription": "Preuzima i instalira ažuriranja za dodatke koji su podešeni da se ažuriraju automatski.",
|
||||||
|
"TaskUpdatePlugins": "Ažuriraj dodatke",
|
||||||
|
"TaskRefreshPeopleDescription": "Ažurira meta podatke za glumce i redatelje u vašoj medijskoj biblioteci.",
|
||||||
|
"TaskRefreshPeople": "Osvježi ljude",
|
||||||
|
"TaskCleanLogsDescription": "Briši logove koji su stariji od {0} dana.",
|
||||||
|
"TaskCleanLogs": "Očisti direktorij sa logovima",
|
||||||
|
"TasksChannelsCategory": "Internet kanali",
|
||||||
|
"TasksLibraryCategory": "Biblioteka"
|
||||||
}
|
}
|
||||||
|
@ -58,5 +58,29 @@
|
|||||||
"Books": "पुस्तकहरु",
|
"Books": "पुस्तकहरु",
|
||||||
"Artists": "कलाकारहरू",
|
"Artists": "कलाकारहरू",
|
||||||
"Application": "अनुप्रयोगहरू",
|
"Application": "अनुप्रयोगहरू",
|
||||||
"Albums": "एल्बमहरू"
|
"Albums": "एल्बमहरू",
|
||||||
|
"TasksLibraryCategory": "पुस्तकालय",
|
||||||
|
"TasksApplicationCategory": "अनुप्रयोग",
|
||||||
|
"TasksMaintenanceCategory": "मर्मत",
|
||||||
|
"UserPolicyUpdatedWithName": "प्रयोगकर्ता नीति को लागी अद्यावधिक गरिएको छ {0}",
|
||||||
|
"UserPasswordChangedWithName": "पासवर्ड प्रयोगकर्ताका लागि परिवर्तन गरिएको छ {0}",
|
||||||
|
"UserOnlineFromDevice": "{0} बाट अनलाइन छ {1}",
|
||||||
|
"UserOfflineFromDevice": "{0} बाट विच्छेदन भएको छ {1}",
|
||||||
|
"UserLockedOutWithName": "प्रयोगकर्ता {0} लक गरिएको छ",
|
||||||
|
"UserDeletedWithName": "प्रयोगकर्ता {0} हटाइएको छ",
|
||||||
|
"UserCreatedWithName": "प्रयोगकर्ता {0} सिर्जना गरिएको छ",
|
||||||
|
"User": "प्रयोगकर्ता",
|
||||||
|
"PluginInstalledWithName": "",
|
||||||
|
"StartupEmbyServerIsLoading": "Jellyfin सर्भर लोड हुँदैछ। कृपया छिट्टै फेरि प्रयास गर्नुहोस्।",
|
||||||
|
"Songs": "गीतहरू",
|
||||||
|
"Shows": "शोहरू",
|
||||||
|
"ServerNameNeedsToBeRestarted": "{0} लाई पुन: सुरु गर्नु पर्छ",
|
||||||
|
"ScheduledTaskStartedWithName": "{0} सुरु भयो",
|
||||||
|
"ScheduledTaskFailedWithName": "{0} असफल",
|
||||||
|
"ProviderValue": "प्रदायक: {0}",
|
||||||
|
"Plugin": "प्लगइनहरू",
|
||||||
|
"Playlists": "प्लेलिस्टहरू",
|
||||||
|
"Photos": "तस्बिरहरु",
|
||||||
|
"NotificationOptionVideoPlaybackStopped": "भिडियो प्लेब्याक रोकियो",
|
||||||
|
"NotificationOptionVideoPlayback": "भिडियो प्लेब्याक सुरु भयो"
|
||||||
}
|
}
|
||||||
|
@ -101,7 +101,8 @@
|
|||||||
"TaskCleanLogsDescription": "Deletar arquivos de log que existe a mais de {0} dias.",
|
"TaskCleanLogsDescription": "Deletar arquivos de log que existe a mais de {0} dias.",
|
||||||
"TaskCleanLogs": "Limpar diretório de log",
|
"TaskCleanLogs": "Limpar diretório de log",
|
||||||
"TaskRefreshLibrary": "Escanear biblioteca de mídias",
|
"TaskRefreshLibrary": "Escanear biblioteca de mídias",
|
||||||
"TaskRefreshChapterImagesDescription": "Criar miniaturas para videos que tem capítulos.",
|
"TaskRefreshChapterImagesDescription": "Cria miniaturas para vídeos que têm capítulos.",
|
||||||
"TaskCleanCacheDescription": "Deletar arquivos de cache que não são mais usados pelo sistema.",
|
"TaskCleanCacheDescription": "Apaga ficheiros em cache que já não são usados pelo sistema.",
|
||||||
"TasksChannelsCategory": "Canais de Internet"
|
"TasksChannelsCategory": "Canais de Internet",
|
||||||
|
"TaskRefreshChapterImages": "Extrair Imagens do Capítulo"
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@ namespace Emby.Server.Implementations.Net
|
|||||||
var retVal = new Socket(AddressFamily.InterNetwork, System.Net.Sockets.SocketType.Dgram, System.Net.Sockets.ProtocolType.Udp);
|
var retVal = new Socket(AddressFamily.InterNetwork, System.Net.Sockets.SocketType.Dgram, System.Net.Sockets.ProtocolType.Udp);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
retVal.EnableBroadcast = true;
|
||||||
retVal.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
|
retVal.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
|
||||||
retVal.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1);
|
retVal.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1);
|
||||||
|
|
||||||
@ -46,6 +47,7 @@ namespace Emby.Server.Implementations.Net
|
|||||||
var retVal = new Socket(AddressFamily.InterNetwork, System.Net.Sockets.SocketType.Dgram, System.Net.Sockets.ProtocolType.Udp);
|
var retVal = new Socket(AddressFamily.InterNetwork, System.Net.Sockets.SocketType.Dgram, System.Net.Sockets.ProtocolType.Udp);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
retVal.EnableBroadcast = true;
|
||||||
retVal.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
|
retVal.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
|
||||||
retVal.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, 4);
|
retVal.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, 4);
|
||||||
|
|
||||||
@ -112,6 +114,7 @@ namespace Emby.Server.Implementations.Net
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
retVal.EnableBroadcast = true;
|
||||||
// retVal.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true);
|
// retVal.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true);
|
||||||
retVal.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, multicastTimeToLive);
|
retVal.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, multicastTimeToLive);
|
||||||
|
|
||||||
|
@ -502,7 +502,8 @@ namespace Emby.Server.Implementations.Session
|
|||||||
Client = appName,
|
Client = appName,
|
||||||
DeviceId = deviceId,
|
DeviceId = deviceId,
|
||||||
ApplicationVersion = appVersion,
|
ApplicationVersion = appVersion,
|
||||||
Id = key.GetMD5().ToString("N", CultureInfo.InvariantCulture)
|
Id = key.GetMD5().ToString("N", CultureInfo.InvariantCulture),
|
||||||
|
ServerId = _appHost.SystemId
|
||||||
};
|
};
|
||||||
|
|
||||||
var username = user?.Username;
|
var username = user?.Username;
|
||||||
|
@ -36,7 +36,6 @@ namespace Jellyfin.Api.Controllers
|
|||||||
public void CompleteWizard()
|
public void CompleteWizard()
|
||||||
{
|
{
|
||||||
_config.Configuration.IsStartupWizardCompleted = true;
|
_config.Configuration.IsStartupWizardCompleted = true;
|
||||||
_config.SetOptimalValues();
|
|
||||||
_config.SaveConfiguration();
|
_config.SaveConfiguration();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
<PackageReference Include="Microsoft.AspNetCore.Authentication" Version="2.2.0" />
|
<PackageReference Include="Microsoft.AspNetCore.Authentication" Version="2.2.0" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Authorization" Version="3.1.5" />
|
<PackageReference Include="Microsoft.AspNetCore.Authorization" Version="3.1.5" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.2.0" />
|
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.2.0" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="5.5.0" />
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="5.5.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -12,6 +12,7 @@ using Jellyfin.Data.Entities;
|
|||||||
using Jellyfin.Data.Enums;
|
using Jellyfin.Data.Enums;
|
||||||
using MediaBrowser.Common;
|
using MediaBrowser.Common;
|
||||||
using MediaBrowser.Common.Cryptography;
|
using MediaBrowser.Common.Cryptography;
|
||||||
|
using MediaBrowser.Common.Extensions;
|
||||||
using MediaBrowser.Common.Net;
|
using MediaBrowser.Common.Net;
|
||||||
using MediaBrowser.Controller.Authentication;
|
using MediaBrowser.Controller.Authentication;
|
||||||
using MediaBrowser.Controller.Drawing;
|
using MediaBrowser.Controller.Drawing;
|
||||||
@ -192,15 +193,15 @@ namespace Jellyfin.Server.Implementations.Users
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void DeleteUser(User user)
|
public void DeleteUser(Guid userId)
|
||||||
{
|
{
|
||||||
|
var dbContext = _dbProvider.CreateContext();
|
||||||
|
var user = dbContext.Users.Find(userId);
|
||||||
if (user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(user));
|
throw new ResourceNotFoundException(nameof(userId));
|
||||||
}
|
}
|
||||||
|
|
||||||
var dbContext = _dbProvider.CreateContext();
|
|
||||||
|
|
||||||
if (dbContext.Users.Find(user.Id) == null)
|
if (dbContext.Users.Find(user.Id) == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentException(string.Format(
|
throw new ArgumentException(string.Format(
|
||||||
@ -226,9 +227,18 @@ namespace Jellyfin.Server.Implementations.Users
|
|||||||
CultureInfo.InvariantCulture,
|
CultureInfo.InvariantCulture,
|
||||||
"The user '{0}' cannot be deleted because there must be at least one admin user in the system.",
|
"The user '{0}' cannot be deleted because there must be at least one admin user in the system.",
|
||||||
user.Username),
|
user.Username),
|
||||||
nameof(user));
|
nameof(userId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clear all entities related to the user from the database.
|
||||||
|
if (user.ProfileImage != null)
|
||||||
|
{
|
||||||
|
dbContext.Remove(user.ProfileImage);
|
||||||
|
}
|
||||||
|
|
||||||
|
dbContext.RemoveRange(user.Permissions);
|
||||||
|
dbContext.RemoveRange(user.Preferences);
|
||||||
|
dbContext.RemoveRange(user.AccessSchedules);
|
||||||
dbContext.Users.Remove(user);
|
dbContext.Users.Remove(user);
|
||||||
dbContext.SaveChanges();
|
dbContext.SaveChanges();
|
||||||
OnUserDeleted?.Invoke(this, new GenericEventArgs<User>(user));
|
OnUserDeleted?.Invoke(this, new GenericEventArgs<User>(user));
|
||||||
|
@ -101,6 +101,11 @@ namespace Jellyfin.Server
|
|||||||
config.Add(UdpServer.AddressOverrideConfigKey, PublishedServerUrl.ToString());
|
config.Add(UdpServer.AddressOverrideConfigKey, PublishedServerUrl.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (FFmpegPath != null)
|
||||||
|
{
|
||||||
|
config.Add(ConfigurationExtensions.FfmpegPathKey, FFmpegPath);
|
||||||
|
}
|
||||||
|
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,21 +11,16 @@ using Microsoft.Extensions.Logging;
|
|||||||
|
|
||||||
namespace MediaBrowser.Api.SyncPlay
|
namespace MediaBrowser.Api.SyncPlay
|
||||||
{
|
{
|
||||||
[Route("/SyncPlay/{SessionId}/NewGroup", "POST", Summary = "Create a new SyncPlay group")]
|
[Route("/SyncPlay/NewGroup", "POST", Summary = "Create a new SyncPlay group")]
|
||||||
[Authenticated]
|
[Authenticated]
|
||||||
public class SyncPlayNewGroup : IReturnVoid
|
public class SyncPlayNewGroup : IReturnVoid
|
||||||
{
|
{
|
||||||
[ApiMember(Name = "SessionId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
|
|
||||||
public string SessionId { get; set; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Route("/SyncPlay/{SessionId}/JoinGroup", "POST", Summary = "Join an existing SyncPlay group")]
|
[Route("/SyncPlay/JoinGroup", "POST", Summary = "Join an existing SyncPlay group")]
|
||||||
[Authenticated]
|
[Authenticated]
|
||||||
public class SyncPlayJoinGroup : IReturnVoid
|
public class SyncPlayJoinGroup : IReturnVoid
|
||||||
{
|
{
|
||||||
[ApiMember(Name = "SessionId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
|
|
||||||
public string SessionId { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the Group id.
|
/// Gets or sets the Group id.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -41,63 +36,48 @@ namespace MediaBrowser.Api.SyncPlay
|
|||||||
public string PlayingItemId { get; set; }
|
public string PlayingItemId { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
[Route("/SyncPlay/{SessionId}/LeaveGroup", "POST", Summary = "Leave joined SyncPlay group")]
|
[Route("/SyncPlay/LeaveGroup", "POST", Summary = "Leave joined SyncPlay group")]
|
||||||
[Authenticated]
|
[Authenticated]
|
||||||
public class SyncPlayLeaveGroup : IReturnVoid
|
public class SyncPlayLeaveGroup : IReturnVoid
|
||||||
{
|
{
|
||||||
[ApiMember(Name = "SessionId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
|
|
||||||
public string SessionId { get; set; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Route("/SyncPlay/{SessionId}/ListGroups", "POST", Summary = "List SyncPlay groups")]
|
[Route("/SyncPlay/ListGroups", "GET", Summary = "List SyncPlay groups")]
|
||||||
[Authenticated]
|
[Authenticated]
|
||||||
public class SyncPlayListGroups : IReturnVoid
|
public class SyncPlayListGroups : IReturnVoid
|
||||||
{
|
{
|
||||||
[ApiMember(Name = "SessionId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
|
|
||||||
public string SessionId { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the filter item id.
|
/// Gets or sets the filter item id.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The filter item id.</value>
|
/// <value>The filter item id.</value>
|
||||||
[ApiMember(Name = "FilterItemId", Description = "Filter by item id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
|
[ApiMember(Name = "FilterItemId", Description = "Filter by item id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
|
||||||
public string FilterItemId { get; set; }
|
public string FilterItemId { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
[Route("/SyncPlay/{SessionId}/PlayRequest", "POST", Summary = "Request play in SyncPlay group")]
|
[Route("/SyncPlay/PlayRequest", "POST", Summary = "Request play in SyncPlay group")]
|
||||||
[Authenticated]
|
[Authenticated]
|
||||||
public class SyncPlayPlayRequest : IReturnVoid
|
public class SyncPlayPlayRequest : IReturnVoid
|
||||||
{
|
{
|
||||||
[ApiMember(Name = "SessionId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
|
|
||||||
public string SessionId { get; set; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Route("/SyncPlay/{SessionId}/PauseRequest", "POST", Summary = "Request pause in SyncPlay group")]
|
[Route("/SyncPlay/PauseRequest", "POST", Summary = "Request pause in SyncPlay group")]
|
||||||
[Authenticated]
|
[Authenticated]
|
||||||
public class SyncPlayPauseRequest : IReturnVoid
|
public class SyncPlayPauseRequest : IReturnVoid
|
||||||
{
|
{
|
||||||
[ApiMember(Name = "SessionId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
|
|
||||||
public string SessionId { get; set; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Route("/SyncPlay/{SessionId}/SeekRequest", "POST", Summary = "Request seek in SyncPlay group")]
|
[Route("/SyncPlay/SeekRequest", "POST", Summary = "Request seek in SyncPlay group")]
|
||||||
[Authenticated]
|
[Authenticated]
|
||||||
public class SyncPlaySeekRequest : IReturnVoid
|
public class SyncPlaySeekRequest : IReturnVoid
|
||||||
{
|
{
|
||||||
[ApiMember(Name = "SessionId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
|
|
||||||
public string SessionId { get; set; }
|
|
||||||
|
|
||||||
[ApiMember(Name = "PositionTicks", IsRequired = true, DataType = "long", ParameterType = "query", Verb = "POST")]
|
[ApiMember(Name = "PositionTicks", IsRequired = true, DataType = "long", ParameterType = "query", Verb = "POST")]
|
||||||
public long PositionTicks { get; set; }
|
public long PositionTicks { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
[Route("/SyncPlay/{SessionId}/BufferingRequest", "POST", Summary = "Request group wait in SyncPlay group while buffering")]
|
[Route("/SyncPlay/BufferingRequest", "POST", Summary = "Request group wait in SyncPlay group while buffering")]
|
||||||
[Authenticated]
|
[Authenticated]
|
||||||
public class SyncPlayBufferingRequest : IReturnVoid
|
public class SyncPlayBufferingRequest : IReturnVoid
|
||||||
{
|
{
|
||||||
[ApiMember(Name = "SessionId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
|
|
||||||
public string SessionId { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the date used to pin PositionTicks in time.
|
/// Gets or sets the date used to pin PositionTicks in time.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -116,13 +96,10 @@ namespace MediaBrowser.Api.SyncPlay
|
|||||||
public bool BufferingDone { get; set; }
|
public bool BufferingDone { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
[Route("/SyncPlay/{SessionId}/UpdatePing", "POST", Summary = "Update session ping")]
|
[Route("/SyncPlay/UpdatePing", "POST", Summary = "Update session ping")]
|
||||||
[Authenticated]
|
[Authenticated]
|
||||||
public class SyncPlayUpdatePing : IReturnVoid
|
public class SyncPlayUpdatePing : IReturnVoid
|
||||||
{
|
{
|
||||||
[ApiMember(Name = "SessionId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
|
|
||||||
public string SessionId { get; set; }
|
|
||||||
|
|
||||||
[ApiMember(Name = "Ping", IsRequired = true, DataType = "double", ParameterType = "query", Verb = "POST")]
|
[ApiMember(Name = "Ping", IsRequired = true, DataType = "double", ParameterType = "query", Verb = "POST")]
|
||||||
public double Ping { get; set; }
|
public double Ping { get; set; }
|
||||||
}
|
}
|
||||||
|
@ -365,15 +365,8 @@ namespace MediaBrowser.Api
|
|||||||
|
|
||||||
public Task DeleteAsync(DeleteUser request)
|
public Task DeleteAsync(DeleteUser request)
|
||||||
{
|
{
|
||||||
var user = _userManager.GetUserById(request.Id);
|
_userManager.DeleteUser(request.Id);
|
||||||
|
_sessionMananger.RevokeUserTokens(request.Id, null);
|
||||||
if (user == null)
|
|
||||||
{
|
|
||||||
throw new ResourceNotFoundException("User not found");
|
|
||||||
}
|
|
||||||
|
|
||||||
_sessionMananger.RevokeUserTokens(user.Id, null);
|
|
||||||
_userManager.DeleteUser(user);
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,7 +19,5 @@ namespace MediaBrowser.Controller.Configuration
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The configuration.</value>
|
/// <value>The configuration.</value>
|
||||||
ServerConfiguration Configuration { get; }
|
ServerConfiguration Configuration { get; }
|
||||||
|
|
||||||
bool SetOptimalValues();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -560,8 +560,6 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The logger.
|
/// The logger.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static ILoggerFactory LoggerFactory { get; set; }
|
|
||||||
|
|
||||||
public static ILogger<BaseItem> Logger { get; set; }
|
public static ILogger<BaseItem> Logger { get; set; }
|
||||||
|
|
||||||
public static ILibraryManager LibraryManager { get; set; }
|
public static ILibraryManager LibraryManager { get; set; }
|
||||||
|
@ -68,7 +68,7 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
parent = LibraryManager.GetItemById(ParentId) as Folder ?? parent;
|
parent = LibraryManager.GetItemById(ParentId) as Folder ?? parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new UserViewBuilder(UserViewManager, LibraryManager, LoggerFactory.CreateLogger<UserViewBuilder>(), UserDataManager, TVSeriesManager, ConfigurationManager)
|
return new UserViewBuilder(UserViewManager, LibraryManager, Logger, UserDataManager, TVSeriesManager, ConfigurationManager)
|
||||||
.GetUserItems(parent, this, CollectionType, query);
|
.GetUserItems(parent, this, CollectionType, query);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ using MediaBrowser.Controller.Configuration;
|
|||||||
using MediaBrowser.Controller.Entities.Movies;
|
using MediaBrowser.Controller.Entities.Movies;
|
||||||
using MediaBrowser.Controller.Library;
|
using MediaBrowser.Controller.Library;
|
||||||
using MediaBrowser.Controller.TV;
|
using MediaBrowser.Controller.TV;
|
||||||
|
using MediaBrowser.Model.Dto;
|
||||||
using MediaBrowser.Model.Entities;
|
using MediaBrowser.Model.Entities;
|
||||||
using MediaBrowser.Model.Querying;
|
using MediaBrowser.Model.Querying;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
@ -22,7 +23,7 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
{
|
{
|
||||||
private readonly IUserViewManager _userViewManager;
|
private readonly IUserViewManager _userViewManager;
|
||||||
private readonly ILibraryManager _libraryManager;
|
private readonly ILibraryManager _libraryManager;
|
||||||
private readonly ILogger<UserViewBuilder> _logger;
|
private readonly ILogger<BaseItem> _logger;
|
||||||
private readonly IUserDataManager _userDataManager;
|
private readonly IUserDataManager _userDataManager;
|
||||||
private readonly ITVSeriesManager _tvSeriesManager;
|
private readonly ITVSeriesManager _tvSeriesManager;
|
||||||
private readonly IServerConfigurationManager _config;
|
private readonly IServerConfigurationManager _config;
|
||||||
@ -30,7 +31,7 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
public UserViewBuilder(
|
public UserViewBuilder(
|
||||||
IUserViewManager userViewManager,
|
IUserViewManager userViewManager,
|
||||||
ILibraryManager libraryManager,
|
ILibraryManager libraryManager,
|
||||||
ILogger<UserViewBuilder> logger,
|
ILogger<BaseItem> logger,
|
||||||
IUserDataManager userDataManager,
|
IUserDataManager userDataManager,
|
||||||
ITVSeriesManager tvSeriesManager,
|
ITVSeriesManager tvSeriesManager,
|
||||||
IServerConfigurationManager config)
|
IServerConfigurationManager config)
|
||||||
|
@ -23,6 +23,11 @@ namespace MediaBrowser.Controller.Extensions
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public const string FfmpegAnalyzeDurationKey = "FFmpeg:analyzeduration";
|
public const string FfmpegAnalyzeDurationKey = "FFmpeg:analyzeduration";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The key for the FFmpeg path option.
|
||||||
|
/// </summary>
|
||||||
|
public const string FfmpegPathKey = "ffmpeg";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The key for a setting that indicates whether playlists should allow duplicate entries.
|
/// The key for a setting that indicates whether playlists should allow duplicate entries.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -30,12 +30,10 @@ namespace MediaBrowser.Controller.Library
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="fileInfo">The file information.</param>
|
/// <param name="fileInfo">The file information.</param>
|
||||||
/// <param name="parent">The parent.</param>
|
/// <param name="parent">The parent.</param>
|
||||||
/// <param name="allowIgnorePath">Allow the path to be ignored.</param>
|
|
||||||
/// <returns>BaseItem.</returns>
|
/// <returns>BaseItem.</returns>
|
||||||
BaseItem ResolvePath(
|
BaseItem ResolvePath(
|
||||||
FileSystemMetadata fileInfo,
|
FileSystemMetadata fileInfo,
|
||||||
Folder parent = null,
|
Folder parent = null);
|
||||||
bool allowIgnorePath = true);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Resolves a set of files into a list of BaseItem.
|
/// Resolves a set of files into a list of BaseItem.
|
||||||
|
@ -111,8 +111,8 @@ namespace MediaBrowser.Controller.Library
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Deletes the specified user.
|
/// Deletes the specified user.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="user">The user to be deleted.</param>
|
/// <param name="userId">The id of the user to be deleted.</param>
|
||||||
void DeleteUser(User user);
|
void DeleteUser(Guid userId);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Resets the password.
|
/// Resets the password.
|
||||||
|
@ -1,15 +1,45 @@
|
|||||||
using MediaBrowser.Model.Entities;
|
using MediaBrowser.Model.Entities;
|
||||||
|
using MediaBrowser.Model.Providers;
|
||||||
|
|
||||||
namespace MediaBrowser.Controller.Providers
|
namespace MediaBrowser.Controller.Providers
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents an identifier for an external provider.
|
||||||
|
/// </summary>
|
||||||
public interface IExternalId
|
public interface IExternalId
|
||||||
{
|
{
|
||||||
string Name { get; }
|
/// <summary>
|
||||||
|
/// Gets the display name of the provider associated with this ID type.
|
||||||
|
/// </summary>
|
||||||
|
string ProviderName { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the unique key to distinguish this provider/type pair. This should be unique across providers.
|
||||||
|
/// </summary>
|
||||||
|
// TODO: This property is not actually unique across the concrete types at the moment. It should be updated to be unique.
|
||||||
string Key { get; }
|
string Key { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the specific media type for this id. This is used to distinguish between the different
|
||||||
|
/// external id types for providers with multiple ids.
|
||||||
|
/// A null value indicates there is no specific media type associated with the external id, or this is the
|
||||||
|
/// default id for the external provider so there is no need to specify a type.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This can be used along with the <see cref="ProviderName"/> to localize the external id on the client.
|
||||||
|
/// </remarks>
|
||||||
|
ExternalIdMediaType? Type { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the URL format string for this id.
|
||||||
|
/// </summary>
|
||||||
string UrlFormatString { get; }
|
string UrlFormatString { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether this id supports a given item type.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="item">The item.</param>
|
||||||
|
/// <returns>True if this item is supported, otherwise false.</returns>
|
||||||
bool Supports(IHasProviderIds item);
|
bool Supports(IHasProviderIds item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -108,6 +108,12 @@ namespace MediaBrowser.Controller.Session
|
|||||||
/// <value>The name of the device.</value>
|
/// <value>The name of the device.</value>
|
||||||
public string DeviceName { get; set; }
|
public string DeviceName { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the type of the device.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The type of the device.</value>
|
||||||
|
public string DeviceType { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the now playing item.
|
/// Gets or sets the now playing item.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -215,8 +221,17 @@ namespace MediaBrowser.Controller.Session
|
|||||||
|
|
||||||
public string PlaylistItemId { get; set; }
|
public string PlaylistItemId { get; set; }
|
||||||
|
|
||||||
|
public string ServerId { get; set; }
|
||||||
|
|
||||||
public string UserPrimaryImageTag { get; set; }
|
public string UserPrimaryImageTag { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the supported commands.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The supported commands.</value>
|
||||||
|
public string[] SupportedCommands
|
||||||
|
=> Capabilities == null ? Array.Empty<string>() : Capabilities.SupportedCommands;
|
||||||
|
|
||||||
public Tuple<ISessionController, bool> EnsureController<T>(Func<SessionInfo, ISessionController> factory)
|
public Tuple<ISessionController, bool> EnsureController<T>(Func<SessionInfo, ISessionController> factory)
|
||||||
{
|
{
|
||||||
var controllers = SessionControllers.ToList();
|
var controllers = SessionControllers.ToList();
|
||||||
|
@ -81,7 +81,7 @@ namespace MediaBrowser.LocalMetadata.Parsers
|
|||||||
var id = info.Key + "Id";
|
var id = info.Key + "Id";
|
||||||
if (!_validProviderIds.ContainsKey(id))
|
if (!_validProviderIds.ContainsKey(id))
|
||||||
{
|
{
|
||||||
_validProviderIds.Add(id, info.Key);
|
_validProviderIds.Add(id, info.Key!);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,6 +21,7 @@ using MediaBrowser.Model.MediaInfo;
|
|||||||
using MediaBrowser.Model.System;
|
using MediaBrowser.Model.System;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
|
||||||
namespace MediaBrowser.MediaEncoding.Encoder
|
namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
{
|
{
|
||||||
@ -46,7 +47,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
|||||||
private readonly object _runningProcessesLock = new object();
|
private readonly object _runningProcessesLock = new object();
|
||||||
private readonly List<ProcessWrapper> _runningProcesses = new List<ProcessWrapper>();
|
private readonly List<ProcessWrapper> _runningProcesses = new List<ProcessWrapper>();
|
||||||
|
|
||||||
private string _ffmpegPath;
|
private string _ffmpegPath = string.Empty;
|
||||||
private string _ffprobePath;
|
private string _ffprobePath;
|
||||||
|
|
||||||
public MediaEncoder(
|
public MediaEncoder(
|
||||||
@ -55,14 +56,14 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
|||||||
IFileSystem fileSystem,
|
IFileSystem fileSystem,
|
||||||
ILocalizationManager localization,
|
ILocalizationManager localization,
|
||||||
Lazy<EncodingHelper> encodingHelperFactory,
|
Lazy<EncodingHelper> encodingHelperFactory,
|
||||||
string startupOptionsFFmpegPath)
|
IConfiguration config)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_configurationManager = configurationManager;
|
_configurationManager = configurationManager;
|
||||||
_fileSystem = fileSystem;
|
_fileSystem = fileSystem;
|
||||||
_localization = localization;
|
_localization = localization;
|
||||||
_encodingHelperFactory = encodingHelperFactory;
|
_encodingHelperFactory = encodingHelperFactory;
|
||||||
_startupOptionFFmpegPath = startupOptionsFFmpegPath;
|
_startupOptionFFmpegPath = config.GetValue<string>(Controller.Extensions.ConfigurationExtensions.FfmpegPathKey) ?? string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
private EncodingHelper EncodingHelper => _encodingHelperFactory.Value;
|
private EncodingHelper EncodingHelper => _encodingHelperFactory.Value;
|
||||||
|
@ -82,8 +82,6 @@ namespace MediaBrowser.Model.Configuration
|
|||||||
|
|
||||||
public bool EnableRemoteAccess { get; set; }
|
public bool EnableRemoteAccess { get; set; }
|
||||||
|
|
||||||
public bool CollectionsUpgraded { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating whether [enable case sensitive item ids].
|
/// Gets or sets a value indicating whether [enable case sensitive item ids].
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -269,6 +267,9 @@ namespace MediaBrowser.Model.Configuration
|
|||||||
PathSubstitutions = Array.Empty<PathSubstitution>();
|
PathSubstitutions = Array.Empty<PathSubstitution>();
|
||||||
IgnoreVirtualInterfaces = false;
|
IgnoreVirtualInterfaces = false;
|
||||||
EnableSimpleArtistDetection = false;
|
EnableSimpleArtistDetection = false;
|
||||||
|
SkipDeserializationForBasicTypes = true;
|
||||||
|
|
||||||
|
PluginRepositories = new List<RepositoryInfo>();
|
||||||
|
|
||||||
DisplaySpecialsWithinSeasons = true;
|
DisplaySpecialsWithinSeasons = true;
|
||||||
EnableExternalContentInSuggestions = true;
|
EnableExternalContentInSuggestions = true;
|
||||||
@ -282,6 +283,9 @@ namespace MediaBrowser.Model.Configuration
|
|||||||
EnableHttps = false;
|
EnableHttps = false;
|
||||||
EnableDashboardResponseCaching = true;
|
EnableDashboardResponseCaching = true;
|
||||||
EnableCaseSensitiveItemIds = true;
|
EnableCaseSensitiveItemIds = true;
|
||||||
|
EnableNormalizedItemByNameIds = true;
|
||||||
|
DisableLiveTvChannelUserDataName = true;
|
||||||
|
EnableNewOmdbSupport = true;
|
||||||
|
|
||||||
AutoRunWebApp = true;
|
AutoRunWebApp = true;
|
||||||
EnableRemoteAccess = true;
|
EnableRemoteAccess = true;
|
||||||
|
@ -1,26 +1,36 @@
|
|||||||
#nullable disable
|
|
||||||
#pragma warning disable CS1591
|
|
||||||
|
|
||||||
namespace MediaBrowser.Model.Providers
|
namespace MediaBrowser.Model.Providers
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the external id information for serialization to the client.
|
||||||
|
/// </summary>
|
||||||
public class ExternalIdInfo
|
public class ExternalIdInfo
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the name.
|
/// Gets or sets the display name of the external id provider (IE: IMDB, MusicBrainz, etc).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The name.</value>
|
// TODO: This should be renamed to ProviderName
|
||||||
public string Name { get; set; }
|
public string? Name { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the key.
|
/// Gets or sets the unique key for this id. This key should be unique across all providers.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The key.</value>
|
// TODO: This property is not actually unique across the concrete types at the moment. It should be updated to be unique.
|
||||||
public string Key { get; set; }
|
public string? Key { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the specific media type for this id. This is used to distinguish between the different
|
||||||
|
/// external id types for providers with multiple ids.
|
||||||
|
/// A null value indicates there is no specific media type associated with the external id, or this is the
|
||||||
|
/// default id for the external provider so there is no need to specify a type.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This can be used along with the <see cref="Name"/> to localize the external id on the client.
|
||||||
|
/// </remarks>
|
||||||
|
public ExternalIdMediaType? Type { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the URL format string.
|
/// Gets or sets the URL format string.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The URL format string.</value>
|
public string? UrlFormatString { get; set; }
|
||||||
public string UrlFormatString { get; set; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
71
MediaBrowser.Model/Providers/ExternalIdMediaType.cs
Normal file
71
MediaBrowser.Model/Providers/ExternalIdMediaType.cs
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
namespace MediaBrowser.Model.Providers
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The specific media type of an <see cref="ExternalIdInfo"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Client applications may use this as a translation key.
|
||||||
|
/// </remarks>
|
||||||
|
public enum ExternalIdMediaType
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A music album.
|
||||||
|
/// </summary>
|
||||||
|
Album = 1,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The artist of a music album.
|
||||||
|
/// </summary>
|
||||||
|
AlbumArtist = 2,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The artist of a media item.
|
||||||
|
/// </summary>
|
||||||
|
Artist = 3,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A boxed set of media.
|
||||||
|
/// </summary>
|
||||||
|
BoxSet = 4,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A series episode.
|
||||||
|
/// </summary>
|
||||||
|
Episode = 5,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A movie.
|
||||||
|
/// </summary>
|
||||||
|
Movie = 6,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An alternative artist apart from the main artist.
|
||||||
|
/// </summary>
|
||||||
|
OtherArtist = 7,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A person.
|
||||||
|
/// </summary>
|
||||||
|
Person = 8,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A release group.
|
||||||
|
/// </summary>
|
||||||
|
ReleaseGroup = 9,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A single season of a series.
|
||||||
|
/// </summary>
|
||||||
|
Season = 10,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A series.
|
||||||
|
/// </summary>
|
||||||
|
Series = 11,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A music track.
|
||||||
|
/// </summary>
|
||||||
|
Track = 12
|
||||||
|
}
|
||||||
|
}
|
@ -102,7 +102,7 @@ namespace MediaBrowser.Providers.Manager
|
|||||||
|
|
||||||
_metadataServices = metadataServices.OrderBy(i => i.Order).ToArray();
|
_metadataServices = metadataServices.OrderBy(i => i.Order).ToArray();
|
||||||
_metadataProviders = metadataProviders.ToArray();
|
_metadataProviders = metadataProviders.ToArray();
|
||||||
_externalIds = externalIds.OrderBy(i => i.Name).ToArray();
|
_externalIds = externalIds.OrderBy(i => i.ProviderName).ToArray();
|
||||||
|
|
||||||
_savers = metadataSavers.Where(i =>
|
_savers = metadataSavers.Where(i =>
|
||||||
{
|
{
|
||||||
@ -900,7 +900,7 @@ namespace MediaBrowser.Providers.Manager
|
|||||||
|
|
||||||
return new ExternalUrl
|
return new ExternalUrl
|
||||||
{
|
{
|
||||||
Name = i.Name,
|
Name = i.ProviderName,
|
||||||
Url = string.Format(
|
Url = string.Format(
|
||||||
CultureInfo.InvariantCulture,
|
CultureInfo.InvariantCulture,
|
||||||
i.UrlFormatString,
|
i.UrlFormatString,
|
||||||
@ -914,8 +914,9 @@ namespace MediaBrowser.Providers.Manager
|
|||||||
return GetExternalIds(item)
|
return GetExternalIds(item)
|
||||||
.Select(i => new ExternalIdInfo
|
.Select(i => new ExternalIdInfo
|
||||||
{
|
{
|
||||||
Name = i.Name,
|
Name = i.ProviderName,
|
||||||
Key = i.Key,
|
Key = i.Key,
|
||||||
|
Type = i.Type,
|
||||||
UrlFormatString = i.UrlFormatString
|
UrlFormatString = i.UrlFormatString
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -6,17 +6,21 @@ using MediaBrowser.Controller.Entities.TV;
|
|||||||
using MediaBrowser.Controller.LiveTv;
|
using MediaBrowser.Controller.LiveTv;
|
||||||
using MediaBrowser.Controller.Providers;
|
using MediaBrowser.Controller.Providers;
|
||||||
using MediaBrowser.Model.Entities;
|
using MediaBrowser.Model.Entities;
|
||||||
|
using MediaBrowser.Model.Providers;
|
||||||
|
|
||||||
namespace MediaBrowser.Providers.Movies
|
namespace MediaBrowser.Providers.Movies
|
||||||
{
|
{
|
||||||
public class ImdbExternalId : IExternalId
|
public class ImdbExternalId : IExternalId
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string Name => "IMDb";
|
public string ProviderName => "IMDb";
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string Key => MetadataProvider.Imdb.ToString();
|
public string Key => MetadataProvider.Imdb.ToString();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ExternalIdMediaType? Type => null;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string UrlFormatString => "https://www.imdb.com/title/{0}";
|
public string UrlFormatString => "https://www.imdb.com/title/{0}";
|
||||||
|
|
||||||
@ -36,11 +40,14 @@ namespace MediaBrowser.Providers.Movies
|
|||||||
public class ImdbPersonExternalId : IExternalId
|
public class ImdbPersonExternalId : IExternalId
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string Name => "IMDb";
|
public string ProviderName => "IMDb";
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string Key => MetadataProvider.Imdb.ToString();
|
public string Key => MetadataProvider.Imdb.ToString();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ExternalIdMediaType? Type => ExternalIdMediaType.Person;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string UrlFormatString => "https://www.imdb.com/name/{0}";
|
public string UrlFormatString => "https://www.imdb.com/name/{0}";
|
||||||
|
|
||||||
|
@ -3,17 +3,21 @@
|
|||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Controller.Providers;
|
using MediaBrowser.Controller.Providers;
|
||||||
using MediaBrowser.Model.Entities;
|
using MediaBrowser.Model.Entities;
|
||||||
|
using MediaBrowser.Model.Providers;
|
||||||
|
|
||||||
namespace MediaBrowser.Providers.Music
|
namespace MediaBrowser.Providers.Music
|
||||||
{
|
{
|
||||||
public class ImvdbId : IExternalId
|
public class ImvdbId : IExternalId
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string Name => "IMVDb";
|
public string ProviderName => "IMVDb";
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string Key => "IMVDb";
|
public string Key => "IMVDb";
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ExternalIdMediaType? Type => null;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string UrlFormatString => null;
|
public string UrlFormatString => null;
|
||||||
|
|
||||||
|
@ -3,17 +3,21 @@
|
|||||||
using MediaBrowser.Controller.Entities.Audio;
|
using MediaBrowser.Controller.Entities.Audio;
|
||||||
using MediaBrowser.Controller.Providers;
|
using MediaBrowser.Controller.Providers;
|
||||||
using MediaBrowser.Model.Entities;
|
using MediaBrowser.Model.Entities;
|
||||||
|
using MediaBrowser.Model.Providers;
|
||||||
|
|
||||||
namespace MediaBrowser.Providers.Plugins.AudioDb
|
namespace MediaBrowser.Providers.Plugins.AudioDb
|
||||||
{
|
{
|
||||||
public class AudioDbAlbumExternalId : IExternalId
|
public class AudioDbAlbumExternalId : IExternalId
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string Name => "TheAudioDb";
|
public string ProviderName => "TheAudioDb";
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string Key => MetadataProvider.AudioDbAlbum.ToString();
|
public string Key => MetadataProvider.AudioDbAlbum.ToString();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ExternalIdMediaType? Type => null;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string UrlFormatString => "https://www.theaudiodb.com/album/{0}";
|
public string UrlFormatString => "https://www.theaudiodb.com/album/{0}";
|
||||||
|
|
||||||
@ -24,11 +28,14 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
|
|||||||
public class AudioDbOtherAlbumExternalId : IExternalId
|
public class AudioDbOtherAlbumExternalId : IExternalId
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string Name => "TheAudioDb Album";
|
public string ProviderName => "TheAudioDb";
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string Key => MetadataProvider.AudioDbAlbum.ToString();
|
public string Key => MetadataProvider.AudioDbAlbum.ToString();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ExternalIdMediaType? Type => ExternalIdMediaType.Album;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string UrlFormatString => "https://www.theaudiodb.com/album/{0}";
|
public string UrlFormatString => "https://www.theaudiodb.com/album/{0}";
|
||||||
|
|
||||||
@ -39,11 +46,14 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
|
|||||||
public class AudioDbArtistExternalId : IExternalId
|
public class AudioDbArtistExternalId : IExternalId
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string Name => "TheAudioDb";
|
public string ProviderName => "TheAudioDb";
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string Key => MetadataProvider.AudioDbArtist.ToString();
|
public string Key => MetadataProvider.AudioDbArtist.ToString();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ExternalIdMediaType? Type => ExternalIdMediaType.Artist;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string UrlFormatString => "https://www.theaudiodb.com/artist/{0}";
|
public string UrlFormatString => "https://www.theaudiodb.com/artist/{0}";
|
||||||
|
|
||||||
@ -54,11 +64,14 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
|
|||||||
public class AudioDbOtherArtistExternalId : IExternalId
|
public class AudioDbOtherArtistExternalId : IExternalId
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string Name => "TheAudioDb Artist";
|
public string ProviderName => "TheAudioDb";
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string Key => MetadataProvider.AudioDbArtist.ToString();
|
public string Key => MetadataProvider.AudioDbArtist.ToString();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ExternalIdMediaType? Type => ExternalIdMediaType.OtherArtist;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string UrlFormatString => "https://www.theaudiodb.com/artist/{0}";
|
public string UrlFormatString => "https://www.theaudiodb.com/artist/{0}";
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
using MediaBrowser.Controller.Entities.Audio;
|
using MediaBrowser.Controller.Entities.Audio;
|
||||||
using MediaBrowser.Controller.Providers;
|
using MediaBrowser.Controller.Providers;
|
||||||
using MediaBrowser.Model.Entities;
|
using MediaBrowser.Model.Entities;
|
||||||
|
using MediaBrowser.Model.Providers;
|
||||||
using MediaBrowser.Providers.Plugins.MusicBrainz;
|
using MediaBrowser.Providers.Plugins.MusicBrainz;
|
||||||
|
|
||||||
namespace MediaBrowser.Providers.Music
|
namespace MediaBrowser.Providers.Music
|
||||||
@ -10,11 +11,14 @@ namespace MediaBrowser.Providers.Music
|
|||||||
public class MusicBrainzReleaseGroupExternalId : IExternalId
|
public class MusicBrainzReleaseGroupExternalId : IExternalId
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string Name => "MusicBrainz Release Group";
|
public string ProviderName => "MusicBrainz";
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string Key => MetadataProvider.MusicBrainzReleaseGroup.ToString();
|
public string Key => MetadataProvider.MusicBrainzReleaseGroup.ToString();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ExternalIdMediaType? Type => ExternalIdMediaType.ReleaseGroup;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string UrlFormatString => Plugin.Instance.Configuration.Server + "/release-group/{0}";
|
public string UrlFormatString => Plugin.Instance.Configuration.Server + "/release-group/{0}";
|
||||||
|
|
||||||
@ -25,11 +29,14 @@ namespace MediaBrowser.Providers.Music
|
|||||||
public class MusicBrainzAlbumArtistExternalId : IExternalId
|
public class MusicBrainzAlbumArtistExternalId : IExternalId
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string Name => "MusicBrainz Album Artist";
|
public string ProviderName => "MusicBrainz";
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string Key => MetadataProvider.MusicBrainzAlbumArtist.ToString();
|
public string Key => MetadataProvider.MusicBrainzAlbumArtist.ToString();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ExternalIdMediaType? Type => ExternalIdMediaType.AlbumArtist;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string UrlFormatString => Plugin.Instance.Configuration.Server + "/artist/{0}";
|
public string UrlFormatString => Plugin.Instance.Configuration.Server + "/artist/{0}";
|
||||||
|
|
||||||
@ -40,11 +47,14 @@ namespace MediaBrowser.Providers.Music
|
|||||||
public class MusicBrainzAlbumExternalId : IExternalId
|
public class MusicBrainzAlbumExternalId : IExternalId
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string Name => "MusicBrainz Album";
|
public string ProviderName => "MusicBrainz";
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string Key => MetadataProvider.MusicBrainzAlbum.ToString();
|
public string Key => MetadataProvider.MusicBrainzAlbum.ToString();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ExternalIdMediaType? Type => ExternalIdMediaType.Album;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string UrlFormatString => Plugin.Instance.Configuration.Server + "/release/{0}";
|
public string UrlFormatString => Plugin.Instance.Configuration.Server + "/release/{0}";
|
||||||
|
|
||||||
@ -55,11 +65,14 @@ namespace MediaBrowser.Providers.Music
|
|||||||
public class MusicBrainzArtistExternalId : IExternalId
|
public class MusicBrainzArtistExternalId : IExternalId
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string Name => "MusicBrainz";
|
public string ProviderName => "MusicBrainz";
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string Key => MetadataProvider.MusicBrainzArtist.ToString();
|
public string Key => MetadataProvider.MusicBrainzArtist.ToString();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ExternalIdMediaType? Type => ExternalIdMediaType.Artist;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string UrlFormatString => Plugin.Instance.Configuration.Server + "/artist/{0}";
|
public string UrlFormatString => Plugin.Instance.Configuration.Server + "/artist/{0}";
|
||||||
|
|
||||||
@ -70,12 +83,15 @@ namespace MediaBrowser.Providers.Music
|
|||||||
public class MusicBrainzOtherArtistExternalId : IExternalId
|
public class MusicBrainzOtherArtistExternalId : IExternalId
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string Name => "MusicBrainz Artist";
|
public string ProviderName => "MusicBrainz";
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|
||||||
public string Key => MetadataProvider.MusicBrainzArtist.ToString();
|
public string Key => MetadataProvider.MusicBrainzArtist.ToString();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ExternalIdMediaType? Type => ExternalIdMediaType.OtherArtist;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string UrlFormatString => Plugin.Instance.Configuration.Server + "/artist/{0}";
|
public string UrlFormatString => Plugin.Instance.Configuration.Server + "/artist/{0}";
|
||||||
|
|
||||||
@ -86,11 +102,14 @@ namespace MediaBrowser.Providers.Music
|
|||||||
public class MusicBrainzTrackId : IExternalId
|
public class MusicBrainzTrackId : IExternalId
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string Name => "MusicBrainz Track";
|
public string ProviderName => "MusicBrainz";
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string Key => MetadataProvider.MusicBrainzTrack.ToString();
|
public string Key => MetadataProvider.MusicBrainzTrack.ToString();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ExternalIdMediaType? Type => ExternalIdMediaType.Track;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string UrlFormatString => Plugin.Instance.Configuration.Server + "/track/{0}";
|
public string UrlFormatString => Plugin.Instance.Configuration.Server + "/track/{0}";
|
||||||
|
|
||||||
|
@ -1,20 +1,25 @@
|
|||||||
#pragma warning disable CS1591
|
|
||||||
|
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Controller.Entities.Movies;
|
using MediaBrowser.Controller.Entities.Movies;
|
||||||
using MediaBrowser.Controller.Providers;
|
using MediaBrowser.Controller.Providers;
|
||||||
using MediaBrowser.Model.Entities;
|
using MediaBrowser.Model.Entities;
|
||||||
|
using MediaBrowser.Model.Providers;
|
||||||
|
|
||||||
namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets
|
namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// External ID for a TMDB box set.
|
||||||
|
/// </summary>
|
||||||
public class TmdbBoxSetExternalId : IExternalId
|
public class TmdbBoxSetExternalId : IExternalId
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string Name => TmdbUtils.ProviderName;
|
public string ProviderName => TmdbUtils.ProviderName;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string Key => MetadataProvider.TmdbCollection.ToString();
|
public string Key => MetadataProvider.TmdbCollection.ToString();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ExternalIdMediaType? Type => ExternalIdMediaType.BoxSet;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string UrlFormatString => TmdbUtils.BaseTmdbUrl + "collection/{0}";
|
public string UrlFormatString => TmdbUtils.BaseTmdbUrl + "collection/{0}";
|
||||||
|
|
||||||
|
@ -1,21 +1,26 @@
|
|||||||
#pragma warning disable CS1591
|
|
||||||
|
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Controller.Entities.Movies;
|
using MediaBrowser.Controller.Entities.Movies;
|
||||||
using MediaBrowser.Controller.LiveTv;
|
using MediaBrowser.Controller.LiveTv;
|
||||||
using MediaBrowser.Controller.Providers;
|
using MediaBrowser.Controller.Providers;
|
||||||
using MediaBrowser.Model.Entities;
|
using MediaBrowser.Model.Entities;
|
||||||
|
using MediaBrowser.Model.Providers;
|
||||||
|
|
||||||
namespace MediaBrowser.Providers.Plugins.Tmdb.Movies
|
namespace MediaBrowser.Providers.Plugins.Tmdb.Movies
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// External ID for a TMBD movie.
|
||||||
|
/// </summary>
|
||||||
public class TmdbMovieExternalId : IExternalId
|
public class TmdbMovieExternalId : IExternalId
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string Name => TmdbUtils.ProviderName;
|
public string ProviderName => TmdbUtils.ProviderName;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string Key => MetadataProvider.Tmdb.ToString();
|
public string Key => MetadataProvider.Tmdb.ToString();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ExternalIdMediaType? Type => ExternalIdMediaType.Movie;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string UrlFormatString => TmdbUtils.BaseTmdbUrl + "movie/{0}";
|
public string UrlFormatString => TmdbUtils.BaseTmdbUrl + "movie/{0}";
|
||||||
|
|
||||||
|
@ -1,19 +1,24 @@
|
|||||||
#pragma warning disable CS1591
|
|
||||||
|
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Controller.Providers;
|
using MediaBrowser.Controller.Providers;
|
||||||
using MediaBrowser.Model.Entities;
|
using MediaBrowser.Model.Entities;
|
||||||
|
using MediaBrowser.Model.Providers;
|
||||||
|
|
||||||
namespace MediaBrowser.Providers.Plugins.Tmdb.People
|
namespace MediaBrowser.Providers.Plugins.Tmdb.People
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// External ID for a TMDB person.
|
||||||
|
/// </summary>
|
||||||
public class TmdbPersonExternalId : IExternalId
|
public class TmdbPersonExternalId : IExternalId
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string Name => TmdbUtils.ProviderName;
|
public string ProviderName => TmdbUtils.ProviderName;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string Key => MetadataProvider.Tmdb.ToString();
|
public string Key => MetadataProvider.Tmdb.ToString();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ExternalIdMediaType? Type => ExternalIdMediaType.Person;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string UrlFormatString => TmdbUtils.BaseTmdbUrl + "person/{0}";
|
public string UrlFormatString => TmdbUtils.BaseTmdbUrl + "person/{0}";
|
||||||
|
|
||||||
|
@ -1,19 +1,24 @@
|
|||||||
#pragma warning disable CS1591
|
|
||||||
|
|
||||||
using MediaBrowser.Controller.Entities.TV;
|
using MediaBrowser.Controller.Entities.TV;
|
||||||
using MediaBrowser.Controller.Providers;
|
using MediaBrowser.Controller.Providers;
|
||||||
using MediaBrowser.Model.Entities;
|
using MediaBrowser.Model.Entities;
|
||||||
|
using MediaBrowser.Model.Providers;
|
||||||
|
|
||||||
namespace MediaBrowser.Providers.Plugins.Tmdb.TV
|
namespace MediaBrowser.Providers.Plugins.Tmdb.TV
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// External ID for a TMDB series.
|
||||||
|
/// </summary>
|
||||||
public class TmdbSeriesExternalId : IExternalId
|
public class TmdbSeriesExternalId : IExternalId
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string Name => TmdbUtils.ProviderName;
|
public string ProviderName => TmdbUtils.ProviderName;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string Key => MetadataProvider.Tmdb.ToString();
|
public string Key => MetadataProvider.Tmdb.ToString();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ExternalIdMediaType? Type => ExternalIdMediaType.Series;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string UrlFormatString => TmdbUtils.BaseTmdbUrl + "tv/{0}";
|
public string UrlFormatString => TmdbUtils.BaseTmdbUrl + "tv/{0}";
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
using MediaBrowser.Controller.Entities.TV;
|
using MediaBrowser.Controller.Entities.TV;
|
||||||
using MediaBrowser.Controller.Providers;
|
using MediaBrowser.Controller.Providers;
|
||||||
using MediaBrowser.Model.Entities;
|
using MediaBrowser.Model.Entities;
|
||||||
|
using MediaBrowser.Model.Providers;
|
||||||
using MediaBrowser.Providers.Plugins.TheTvdb;
|
using MediaBrowser.Providers.Plugins.TheTvdb;
|
||||||
|
|
||||||
namespace MediaBrowser.Providers.TV
|
namespace MediaBrowser.Providers.TV
|
||||||
@ -10,11 +11,14 @@ namespace MediaBrowser.Providers.TV
|
|||||||
public class Zap2ItExternalId : IExternalId
|
public class Zap2ItExternalId : IExternalId
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string Name => "Zap2It";
|
public string ProviderName => "Zap2It";
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string Key => MetadataProvider.Zap2It.ToString();
|
public string Key => MetadataProvider.Zap2It.ToString();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ExternalIdMediaType? Type => null;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string UrlFormatString => "http://tvlistings.zap2it.com/overview.html?programSeriesId={0}";
|
public string UrlFormatString => "http://tvlistings.zap2it.com/overview.html?programSeriesId={0}";
|
||||||
|
|
||||||
@ -25,11 +29,14 @@ namespace MediaBrowser.Providers.TV
|
|||||||
public class TvdbExternalId : IExternalId
|
public class TvdbExternalId : IExternalId
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string Name => "TheTVDB";
|
public string ProviderName => "TheTVDB";
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string Key => MetadataProvider.Tvdb.ToString();
|
public string Key => MetadataProvider.Tvdb.ToString();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ExternalIdMediaType? Type => null;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string UrlFormatString => TvdbUtils.TvdbBaseUrl + "?tab=series&id={0}";
|
public string UrlFormatString => TvdbUtils.TvdbBaseUrl + "?tab=series&id={0}";
|
||||||
|
|
||||||
@ -40,11 +47,14 @@ namespace MediaBrowser.Providers.TV
|
|||||||
public class TvdbSeasonExternalId : IExternalId
|
public class TvdbSeasonExternalId : IExternalId
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string Name => "TheTVDB";
|
public string ProviderName => "TheTVDB";
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string Key => MetadataProvider.Tvdb.ToString();
|
public string Key => MetadataProvider.Tvdb.ToString();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ExternalIdMediaType? Type => ExternalIdMediaType.Season;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string UrlFormatString => null;
|
public string UrlFormatString => null;
|
||||||
|
|
||||||
@ -55,11 +65,14 @@ namespace MediaBrowser.Providers.TV
|
|||||||
public class TvdbEpisodeExternalId : IExternalId
|
public class TvdbEpisodeExternalId : IExternalId
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string Name => "TheTVDB";
|
public string ProviderName => "TheTVDB";
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string Key => MetadataProvider.Tvdb.ToString();
|
public string Key => MetadataProvider.Tvdb.ToString();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ExternalIdMediaType? Type => ExternalIdMediaType.Episode;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string UrlFormatString => TvdbUtils.TvdbBaseUrl + "?tab=episode&id={0}";
|
public string UrlFormatString => TvdbUtils.TvdbBaseUrl + "?tab=episode&id={0}";
|
||||||
|
|
||||||
|
@ -16,8 +16,8 @@
|
|||||||
<a href="https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/?utm_source=widget">
|
<a href="https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/?utm_source=widget">
|
||||||
<img alt="Translation Status" src="https://translate.jellyfin.org/widgets/jellyfin/-/jellyfin-core/svg-badge.svg"/>
|
<img alt="Translation Status" src="https://translate.jellyfin.org/widgets/jellyfin/-/jellyfin-core/svg-badge.svg"/>
|
||||||
</a>
|
</a>
|
||||||
<a href="https://dev.azure.com/jellyfin-project/jellyfin/_build?definitionId=1">
|
<a href="https://dev.azure.com/jellyfin-project/jellyfin/_build?definitionId=29">
|
||||||
<img alt="Azure Builds" src="https://dev.azure.com/jellyfin-project/jellyfin/_apis/build/status/Jellyfin%20CI"/>
|
<img alt="Azure Builds" src="https://dev.azure.com/jellyfin-project/jellyfin/_apis/build/status/Jellyfin%20Server"/>
|
||||||
</a>
|
</a>
|
||||||
<a href="https://hub.docker.com/r/jellyfin/jellyfin">
|
<a href="https://hub.docker.com/r/jellyfin/jellyfin">
|
||||||
<img alt="Docker Pull Count" src="https://img.shields.io/docker/pulls/jellyfin/jellyfin.svg"/>
|
<img alt="Docker Pull Count" src="https://img.shields.io/docker/pulls/jellyfin/jellyfin.svg"/>
|
||||||
|
@ -339,7 +339,6 @@ namespace Rssdp.Infrastructure
|
|||||||
private ISocket ListenForBroadcastsAsync()
|
private ISocket ListenForBroadcastsAsync()
|
||||||
{
|
{
|
||||||
var socket = _SocketFactory.CreateUdpMulticastSocket(SsdpConstants.MulticastLocalAdminAddress, _MulticastTtl, SsdpConstants.MulticastPort);
|
var socket = _SocketFactory.CreateUdpMulticastSocket(SsdpConstants.MulticastLocalAdminAddress, _MulticastTtl, SsdpConstants.MulticastPort);
|
||||||
|
|
||||||
_ = ListenToSocketInternal(socket);
|
_ = ListenToSocketInternal(socket);
|
||||||
|
|
||||||
return socket;
|
return socket;
|
||||||
|
15
deployment/Dockerfile.docker.amd64
Normal file
15
deployment/Dockerfile.docker.amd64
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
ARG DOTNET_VERSION=3.1
|
||||||
|
|
||||||
|
FROM mcr.microsoft.com/dotnet/core/sdk:${DOTNET_VERSION}-buster
|
||||||
|
|
||||||
|
ARG SOURCE_DIR=/src
|
||||||
|
ARG ARTIFACT_DIR=/jellyfin
|
||||||
|
|
||||||
|
WORKDIR ${SOURCE_DIR}
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
|
||||||
|
|
||||||
|
# because of changes in docker and systemd we need to not build in parallel at the moment
|
||||||
|
# see https://success.docker.com/article/how-to-reserve-resource-temporarily-unavailable-errors-due-to-tasksmax-setting
|
||||||
|
RUN dotnet publish Jellyfin.Server --disable-parallel --configuration Release --output="${ARTIFACT_DIR}" --self-contained --runtime linux-x64 "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none"
|
15
deployment/Dockerfile.docker.arm64
Normal file
15
deployment/Dockerfile.docker.arm64
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
ARG DOTNET_VERSION=3.1
|
||||||
|
|
||||||
|
FROM mcr.microsoft.com/dotnet/core/sdk:${DOTNET_VERSION}-buster
|
||||||
|
|
||||||
|
ARG SOURCE_DIR=/src
|
||||||
|
ARG ARTIFACT_DIR=/jellyfin
|
||||||
|
|
||||||
|
WORKDIR ${SOURCE_DIR}
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
|
||||||
|
|
||||||
|
# because of changes in docker and systemd we need to not build in parallel at the moment
|
||||||
|
# see https://success.docker.com/article/how-to-reserve-resource-temporarily-unavailable-errors-due-to-tasksmax-setting
|
||||||
|
RUN dotnet publish Jellyfin.Server --disable-parallel --configuration Release --output="${ARTIFACT_DIR}" --self-contained --runtime linux-arm64 "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none"
|
15
deployment/Dockerfile.docker.armhf
Normal file
15
deployment/Dockerfile.docker.armhf
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
ARG DOTNET_VERSION=3.1
|
||||||
|
|
||||||
|
FROM mcr.microsoft.com/dotnet/core/sdk:${DOTNET_VERSION}-buster
|
||||||
|
|
||||||
|
ARG SOURCE_DIR=/src
|
||||||
|
ARG ARTIFACT_DIR=/jellyfin
|
||||||
|
|
||||||
|
WORKDIR ${SOURCE_DIR}
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
|
||||||
|
|
||||||
|
# because of changes in docker and systemd we need to not build in parallel at the moment
|
||||||
|
# see https://success.docker.com/article/how-to-reserve-resource-temporarily-unavailable-errors-due-to-tasksmax-setting
|
||||||
|
RUN dotnet publish Jellyfin.Server --disable-parallel --configuration Release --output="${ARTIFACT_DIR}" --self-contained --runtime linux-arm "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none"
|
@ -8,6 +8,22 @@ set -o xtrace
|
|||||||
# Move to source directory
|
# Move to source directory
|
||||||
pushd ${SOURCE_DIR}
|
pushd ${SOURCE_DIR}
|
||||||
|
|
||||||
|
# Modify changelog to unstable configuration if IS_UNSTABLE
|
||||||
|
if [[ ${IS_UNSTABLE} == 'yes' ]]; then
|
||||||
|
pushd fedora
|
||||||
|
|
||||||
|
PR_ID=$( git log --grep 'Merge pull request' --oneline --single-worktree --first-parent | head -1 | grep --color=none -Eo '#[0-9]+' | tr -d '#' )
|
||||||
|
|
||||||
|
sed -i "s/Version:.*/Version: ${BUILD_ID}/" jellyfin.spec
|
||||||
|
sed -i "/%changelog/q" jellyfin.spec
|
||||||
|
|
||||||
|
cat <<EOF >>jellyfin.spec
|
||||||
|
* $( LANG=C date '+%a %b %d %Y' ) Jellyfin Packaging Team <packaging@jellyfin.org>
|
||||||
|
- Jellyfin Server unstable build ${BUILD_ID} for merged PR #${PR_ID}
|
||||||
|
EOF
|
||||||
|
popd
|
||||||
|
fi
|
||||||
|
|
||||||
# Build RPM
|
# Build RPM
|
||||||
make -f fedora/Makefile srpm outdir=/root/rpmbuild/SRPMS
|
make -f fedora/Makefile srpm outdir=/root/rpmbuild/SRPMS
|
||||||
rpmbuild --rebuild -bb /root/rpmbuild/SRPMS/jellyfin-*.src.rpm
|
rpmbuild --rebuild -bb /root/rpmbuild/SRPMS/jellyfin-*.src.rpm
|
||||||
|
@ -14,6 +14,21 @@ if [[ ${IS_DOCKER} == YES ]]; then
|
|||||||
sed -i '/dotnet-sdk-3.1,/d' debian/control
|
sed -i '/dotnet-sdk-3.1,/d' debian/control
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Modify changelog to unstable configuration if IS_UNSTABLE
|
||||||
|
if [[ ${IS_UNSTABLE} == 'yes' ]]; then
|
||||||
|
pushd debian
|
||||||
|
PR_ID=$( git log --grep 'Merge pull request' --oneline --single-worktree --first-parent | head -1 | grep --color=none -Eo '#[0-9]+' | tr -d '#' )
|
||||||
|
|
||||||
|
cat <<EOF >changelog
|
||||||
|
jellyfin-server (${BUILD_ID}-unstable) unstable; urgency=medium
|
||||||
|
|
||||||
|
* Jellyfin Server unstable build ${BUILD_ID} for merged PR #${PR_ID}
|
||||||
|
|
||||||
|
-- Jellyfin Packaging Team <packaging@jellyfin.org> $( date --rfc-2822 )
|
||||||
|
EOF
|
||||||
|
popd
|
||||||
|
fi
|
||||||
|
|
||||||
# Build DEB
|
# Build DEB
|
||||||
dpkg-buildpackage -us -uc --pre-clean --post-clean
|
dpkg-buildpackage -us -uc --pre-clean --post-clean
|
||||||
|
|
||||||
|
@ -14,6 +14,21 @@ if [[ ${IS_DOCKER} == YES ]]; then
|
|||||||
sed -i '/dotnet-sdk-3.1,/d' debian/control
|
sed -i '/dotnet-sdk-3.1,/d' debian/control
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Modify changelog to unstable configuration if IS_UNSTABLE
|
||||||
|
if [[ ${IS_UNSTABLE} == 'yes' ]]; then
|
||||||
|
pushd debian
|
||||||
|
PR_ID=$( git log --grep 'Merge pull request' --oneline --single-worktree --first-parent | head -1 | grep --color=none -Eo '#[0-9]+' | tr -d '#' )
|
||||||
|
|
||||||
|
cat <<EOF >changelog
|
||||||
|
jellyfin-server (${BUILD_ID}-unstable) unstable; urgency=medium
|
||||||
|
|
||||||
|
* Jellyfin Server unstable build ${BUILD_ID} for merged PR #${PR_ID}
|
||||||
|
|
||||||
|
-- Jellyfin Packaging Team <packaging@jellyfin.org> $( date --rfc-2822 )
|
||||||
|
EOF
|
||||||
|
popd
|
||||||
|
fi
|
||||||
|
|
||||||
# Build DEB
|
# Build DEB
|
||||||
export CONFIG_SITE=/etc/dpkg-cross/cross-config.${ARCH}
|
export CONFIG_SITE=/etc/dpkg-cross/cross-config.${ARCH}
|
||||||
dpkg-buildpackage -us -uc -a arm64 --pre-clean --post-clean
|
dpkg-buildpackage -us -uc -a arm64 --pre-clean --post-clean
|
||||||
|
@ -14,6 +14,21 @@ if [[ ${IS_DOCKER} == YES ]]; then
|
|||||||
sed -i '/dotnet-sdk-3.1,/d' debian/control
|
sed -i '/dotnet-sdk-3.1,/d' debian/control
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Modify changelog to unstable configuration if IS_UNSTABLE
|
||||||
|
if [[ ${IS_UNSTABLE} == 'yes' ]]; then
|
||||||
|
pushd debian
|
||||||
|
PR_ID=$( git log --grep 'Merge pull request' --oneline --single-worktree --first-parent | head -1 | grep --color=none -Eo '#[0-9]+' | tr -d '#' )
|
||||||
|
|
||||||
|
cat <<EOF >changelog
|
||||||
|
jellyfin-server (${BUILD_ID}-unstable) unstable; urgency=medium
|
||||||
|
|
||||||
|
* Jellyfin Server unstable build ${BUILD_ID} for merged PR #${PR_ID}
|
||||||
|
|
||||||
|
-- Jellyfin Packaging Team <packaging@jellyfin.org> $( date --rfc-2822 )
|
||||||
|
EOF
|
||||||
|
popd
|
||||||
|
fi
|
||||||
|
|
||||||
# Build DEB
|
# Build DEB
|
||||||
export CONFIG_SITE=/etc/dpkg-cross/cross-config.${ARCH}
|
export CONFIG_SITE=/etc/dpkg-cross/cross-config.${ARCH}
|
||||||
dpkg-buildpackage -us -uc -a armhf --pre-clean --post-clean
|
dpkg-buildpackage -us -uc -a armhf --pre-clean --post-clean
|
||||||
|
@ -8,6 +8,22 @@ set -o xtrace
|
|||||||
# Move to source directory
|
# Move to source directory
|
||||||
pushd ${SOURCE_DIR}
|
pushd ${SOURCE_DIR}
|
||||||
|
|
||||||
|
# Modify changelog to unstable configuration if IS_UNSTABLE
|
||||||
|
if [[ ${IS_UNSTABLE} == 'yes' ]]; then
|
||||||
|
pushd fedora
|
||||||
|
|
||||||
|
PR_ID=$( git log --grep 'Merge pull request' --oneline --single-worktree --first-parent | head -1 | grep --color=none -Eo '#[0-9]+' | tr -d '#' )
|
||||||
|
|
||||||
|
sed -i "s/Version:.*/Version: ${BUILD_ID}/" jellyfin.spec
|
||||||
|
sed -i "/%changelog/q" jellyfin.spec
|
||||||
|
|
||||||
|
cat <<EOF >>jellyfin.spec
|
||||||
|
* $( LANG=C date '+%a %b %d %Y' ) Jellyfin Packaging Team <packaging@jellyfin.org>
|
||||||
|
- Jellyfin Server unstable build ${BUILD_ID} for merged PR #${PR_ID}
|
||||||
|
EOF
|
||||||
|
popd
|
||||||
|
fi
|
||||||
|
|
||||||
# Build RPM
|
# Build RPM
|
||||||
make -f fedora/Makefile srpm outdir=/root/rpmbuild/SRPMS
|
make -f fedora/Makefile srpm outdir=/root/rpmbuild/SRPMS
|
||||||
rpmbuild -rb /root/rpmbuild/SRPMS/jellyfin-*.src.rpm
|
rpmbuild -rb /root/rpmbuild/SRPMS/jellyfin-*.src.rpm
|
||||||
|
@ -9,7 +9,11 @@ set -o xtrace
|
|||||||
pushd ${SOURCE_DIR}
|
pushd ${SOURCE_DIR}
|
||||||
|
|
||||||
# Get version
|
# Get version
|
||||||
version="$( grep "version:" ./build.yaml | sed -E 's/version: "([0-9\.]+.*)"/\1/' )"
|
if [[ ${IS_UNSTABLE} == 'yes' ]]; then
|
||||||
|
version="${BUILD_ID}"
|
||||||
|
else
|
||||||
|
version="$( grep "version:" ./build.yaml | sed -E 's/version: "([0-9\.]+.*)"/\1/' )"
|
||||||
|
fi
|
||||||
|
|
||||||
# Build archives
|
# Build archives
|
||||||
dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime linux-x64 --output dist/jellyfin-server_${version}/ "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none;UseAppHost=true"
|
dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime linux-x64 --output dist/jellyfin-server_${version}/ "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none;UseAppHost=true"
|
||||||
|
@ -9,7 +9,11 @@ set -o xtrace
|
|||||||
pushd ${SOURCE_DIR}
|
pushd ${SOURCE_DIR}
|
||||||
|
|
||||||
# Get version
|
# Get version
|
||||||
version="$( grep "version:" ./build.yaml | sed -E 's/version: "([0-9\.]+.*)"/\1/' )"
|
if [[ ${IS_UNSTABLE} == 'yes' ]]; then
|
||||||
|
version="${BUILD_ID}"
|
||||||
|
else
|
||||||
|
version="$( grep "version:" ./build.yaml | sed -E 's/version: "([0-9\.]+.*)"/\1/' )"
|
||||||
|
fi
|
||||||
|
|
||||||
# Build archives
|
# Build archives
|
||||||
dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime osx-x64 --output dist/jellyfin-server_${version}/ "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none;UseAppHost=true"
|
dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime osx-x64 --output dist/jellyfin-server_${version}/ "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none;UseAppHost=true"
|
||||||
|
@ -9,7 +9,11 @@ set -o xtrace
|
|||||||
pushd ${SOURCE_DIR}
|
pushd ${SOURCE_DIR}
|
||||||
|
|
||||||
# Get version
|
# Get version
|
||||||
version="$( grep "version:" ./build.yaml | sed -E 's/version: "([0-9\.]+.*)"/\1/' )"
|
if [[ ${IS_UNSTABLE} == 'yes' ]]; then
|
||||||
|
version="${BUILD_ID}"
|
||||||
|
else
|
||||||
|
version="$( grep "version:" ./build.yaml | sed -E 's/version: "([0-9\.]+.*)"/\1/' )"
|
||||||
|
fi
|
||||||
|
|
||||||
# Build archives
|
# Build archives
|
||||||
dotnet publish Jellyfin.Server --configuration Release --output dist/jellyfin-server_${version}/ "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none;UseAppHost=true"
|
dotnet publish Jellyfin.Server --configuration Release --output dist/jellyfin-server_${version}/ "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none;UseAppHost=true"
|
||||||
|
@ -14,6 +14,21 @@ if [[ ${IS_DOCKER} == YES ]]; then
|
|||||||
sed -i '/dotnet-sdk-3.1,/d' debian/control
|
sed -i '/dotnet-sdk-3.1,/d' debian/control
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Modify changelog to unstable configuration if IS_UNSTABLE
|
||||||
|
if [[ ${IS_UNSTABLE} == 'yes' ]]; then
|
||||||
|
pushd debian
|
||||||
|
PR_ID=$( git log --grep 'Merge pull request' --oneline --single-worktree --first-parent | head -1 | grep --color=none -Eo '#[0-9]+' | tr -d '#' )
|
||||||
|
|
||||||
|
cat <<EOF >changelog
|
||||||
|
jellyfin-server (${BUILD_ID}-unstable) unstable; urgency=medium
|
||||||
|
|
||||||
|
* Jellyfin Server unstable build ${BUILD_ID} for merged PR #${PR_ID}
|
||||||
|
|
||||||
|
-- Jellyfin Packaging Team <packaging@jellyfin.org> $( date --rfc-2822 )
|
||||||
|
EOF
|
||||||
|
popd
|
||||||
|
fi
|
||||||
|
|
||||||
# Build DEB
|
# Build DEB
|
||||||
dpkg-buildpackage -us -uc --pre-clean --post-clean
|
dpkg-buildpackage -us -uc --pre-clean --post-clean
|
||||||
|
|
||||||
|
@ -14,6 +14,21 @@ if [[ ${IS_DOCKER} == YES ]]; then
|
|||||||
sed -i '/dotnet-sdk-3.1,/d' debian/control
|
sed -i '/dotnet-sdk-3.1,/d' debian/control
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Modify changelog to unstable configuration if IS_UNSTABLE
|
||||||
|
if [[ ${IS_UNSTABLE} == 'yes' ]]; then
|
||||||
|
pushd debian
|
||||||
|
PR_ID=$( git log --grep 'Merge pull request' --oneline --single-worktree --first-parent | head -1 | grep --color=none -Eo '#[0-9]+' | tr -d '#' )
|
||||||
|
|
||||||
|
cat <<EOF >changelog
|
||||||
|
jellyfin-server (${BUILD_ID}-unstable) unstable; urgency=medium
|
||||||
|
|
||||||
|
* Jellyfin Server unstable build ${BUILD_ID} for merged PR #${PR_ID}
|
||||||
|
|
||||||
|
-- Jellyfin Packaging Team <packaging@jellyfin.org> $( date --rfc-2822 )
|
||||||
|
EOF
|
||||||
|
popd
|
||||||
|
fi
|
||||||
|
|
||||||
# Build DEB
|
# Build DEB
|
||||||
export CONFIG_SITE=/etc/dpkg-cross/cross-config.${ARCH}
|
export CONFIG_SITE=/etc/dpkg-cross/cross-config.${ARCH}
|
||||||
dpkg-buildpackage -us -uc -a arm64 --pre-clean --post-clean
|
dpkg-buildpackage -us -uc -a arm64 --pre-clean --post-clean
|
||||||
|
@ -14,6 +14,21 @@ if [[ ${IS_DOCKER} == YES ]]; then
|
|||||||
sed -i '/dotnet-sdk-3.1,/d' debian/control
|
sed -i '/dotnet-sdk-3.1,/d' debian/control
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Modify changelog to unstable configuration if IS_UNSTABLE
|
||||||
|
if [[ ${IS_UNSTABLE} == 'yes' ]]; then
|
||||||
|
pushd debian
|
||||||
|
PR_ID=$( git log --grep 'Merge pull request' --oneline --single-worktree --first-parent | head -1 | grep --color=none -Eo '#[0-9]+' | tr -d '#' )
|
||||||
|
|
||||||
|
cat <<EOF >changelog
|
||||||
|
jellyfin-server (${BUILD_ID}-unstable) unstable; urgency=medium
|
||||||
|
|
||||||
|
* Jellyfin Server unstable build ${BUILD_ID} for merged PR #${PR_ID}
|
||||||
|
|
||||||
|
-- Jellyfin Packaging Team <packaging@jellyfin.org> $( date --rfc-2822 )
|
||||||
|
EOF
|
||||||
|
popd
|
||||||
|
fi
|
||||||
|
|
||||||
# Build DEB
|
# Build DEB
|
||||||
export CONFIG_SITE=/etc/dpkg-cross/cross-config.${ARCH}
|
export CONFIG_SITE=/etc/dpkg-cross/cross-config.${ARCH}
|
||||||
dpkg-buildpackage -us -uc -a armhf --pre-clean --post-clean
|
dpkg-buildpackage -us -uc -a armhf --pre-clean --post-clean
|
||||||
|
@ -15,7 +15,11 @@ FFMPEG_URL="https://ffmpeg.zeranoe.com/builds/win64/static/${FFMPEG_VERSION}.zip
|
|||||||
pushd ${SOURCE_DIR}
|
pushd ${SOURCE_DIR}
|
||||||
|
|
||||||
# Get version
|
# Get version
|
||||||
version="$( grep "version:" ./build.yaml | sed -E 's/version: "([0-9\.]+.*)"/\1/' )"
|
if [[ ${IS_UNSTABLE} == 'yes' ]]; then
|
||||||
|
version="${BUILD_ID}"
|
||||||
|
else
|
||||||
|
version="$( grep "version:" ./build.yaml | sed -E 's/version: "([0-9\.]+.*)"/\1/' )"
|
||||||
|
fi
|
||||||
|
|
||||||
output_dir="dist/jellyfin-server_${version}"
|
output_dir="dist/jellyfin-server_${version}"
|
||||||
|
|
||||||
|
@ -13,15 +13,15 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="AutoFixture" Version="4.11.0" />
|
<PackageReference Include="AutoFixture" Version="4.12.0" />
|
||||||
<PackageReference Include="AutoFixture.AutoMoq" Version="4.11.0" />
|
<PackageReference Include="AutoFixture.AutoMoq" Version="4.12.0" />
|
||||||
<PackageReference Include="AutoFixture.Xunit2" Version="4.11.0" />
|
<PackageReference Include="AutoFixture.Xunit2" Version="4.12.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Options" Version="3.1.5" />
|
<PackageReference Include="Microsoft.Extensions.Options" Version="3.1.5" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
|
||||||
<PackageReference Include="xunit" Version="2.4.1" />
|
<PackageReference Include="xunit" Version="2.4.1" />
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
|
||||||
<PackageReference Include="coverlet.collector" Version="1.3.0" />
|
<PackageReference Include="coverlet.collector" Version="1.3.0" />
|
||||||
<PackageReference Include="Moq" Version="4.14.3" />
|
<PackageReference Include="Moq" Version="4.14.4" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<!-- Code Analyzers -->
|
<!-- Code Analyzers -->
|
||||||
|
@ -14,9 +14,9 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="AutoFixture" Version="4.11.0" />
|
<PackageReference Include="AutoFixture" Version="4.12.0" />
|
||||||
<PackageReference Include="AutoFixture.AutoMoq" Version="4.11.0" />
|
<PackageReference Include="AutoFixture.AutoMoq" Version="4.12.0" />
|
||||||
<PackageReference Include="Moq" Version="4.14.3" />
|
<PackageReference Include="Moq" Version="4.14.4" />
|
||||||
<PackageReference Include="xunit" Version="2.4.1" />
|
<PackageReference Include="xunit" Version="2.4.1" />
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
|
||||||
<PackageReference Include="coverlet.collector" Version="1.3.0" />
|
<PackageReference Include="coverlet.collector" Version="1.3.0" />
|
||||||
|
@ -7,12 +7,19 @@ namespace Jellyfin.Server.Implementations.Tests.Library
|
|||||||
{
|
{
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData("/media/small.jpg", true)]
|
[InlineData("/media/small.jpg", true)]
|
||||||
|
[InlineData("/media/albumart.jpg", true)]
|
||||||
|
[InlineData("/media/movie.sample.mp4", true)]
|
||||||
[InlineData("/media/movies/#Recycle/test.txt", true)]
|
[InlineData("/media/movies/#Recycle/test.txt", true)]
|
||||||
[InlineData("/media/movies/#recycle/", true)]
|
[InlineData("/media/movies/#recycle/", true)]
|
||||||
|
[InlineData("/media/movies/#recycle", true)]
|
||||||
[InlineData("thumbs.db", true)]
|
[InlineData("thumbs.db", true)]
|
||||||
[InlineData(@"C:\media\movies\movie.avi", false)]
|
[InlineData(@"C:\media\movies\movie.avi", false)]
|
||||||
[InlineData("/media/.hiddendir/file.mp4", true)]
|
[InlineData("/media/.hiddendir/file.mp4", true)]
|
||||||
[InlineData("/media/dir/.hiddenfile.mp4", true)]
|
[InlineData("/media/dir/.hiddenfile.mp4", true)]
|
||||||
|
[InlineData("/volume1/video/Series/@eaDir", true)]
|
||||||
|
[InlineData("/volume1/video/Series/@eaDir/file.txt", true)]
|
||||||
|
[InlineData("/directory/@Recycle", true)]
|
||||||
|
[InlineData("/directory/@Recycle/file.mp3", true)]
|
||||||
public void PathIgnored(string path, bool expected)
|
public void PathIgnored(string path, bool expected)
|
||||||
{
|
{
|
||||||
Assert.Equal(expected, IgnorePatterns.ShouldIgnore(path));
|
Assert.Equal(expected, IgnorePatterns.ShouldIgnore(path));
|
||||||
|
Loading…
x
Reference in New Issue
Block a user