mirror of
https://github.com/jellyfin/jellyfin.git
synced 2025-12-09 18:43:05 +03:00
Compare commits
54 Commits
renovate/m
...
v10.8.0-be
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d45d228b36 | ||
|
|
a280ff603f | ||
|
|
9beb3aff4e | ||
|
|
066bdc1e72 | ||
|
|
5833c70725 | ||
|
|
cd93f49fa8 | ||
|
|
93009682b3 | ||
|
|
9001eaa67e | ||
|
|
76010e80dd | ||
|
|
5778541d2f | ||
|
|
cb0baddde3 | ||
|
|
0ff37413b0 | ||
|
|
d5434988d7 | ||
|
|
847518701d | ||
|
|
c5212a20a3 | ||
|
|
385a0b9437 | ||
|
|
884ba4f3ed | ||
|
|
cba6a4e3f3 | ||
|
|
d5f44f7a5c | ||
|
|
bf1ccf7493 | ||
|
|
d7c548f3db | ||
|
|
a7abdca47a | ||
|
|
7a8eaa8811 | ||
|
|
045dca49d0 | ||
|
|
e9ce82445e | ||
|
|
2133c6e348 | ||
|
|
5de2db9f52 | ||
|
|
620625c4c1 | ||
|
|
e0b035e34e | ||
|
|
1946414e14 | ||
|
|
132c85e554 | ||
|
|
9d1049e83f | ||
|
|
72aca15191 | ||
|
|
577325b788 | ||
|
|
fec2cf5060 | ||
|
|
53b06ce4e3 | ||
|
|
bdb85aeecf | ||
|
|
e299adc819 | ||
|
|
0674f84e9e | ||
|
|
b6086398d3 | ||
|
|
1d585146d6 | ||
|
|
aa1b1c6bbb | ||
|
|
bebe1808ce | ||
|
|
fb2e4c2e1a | ||
|
|
784ed796ce | ||
|
|
579155a571 | ||
|
|
ca16a55a47 | ||
|
|
e2ffd41141 | ||
|
|
ca67a48140 | ||
|
|
d2ce315c1d | ||
|
|
6a6874aa16 | ||
|
|
6f45848b51 | ||
|
|
23ba15ccfb | ||
|
|
5376c37d42 |
@@ -48,7 +48,6 @@ namespace Emby.Naming.Common
|
||||
".mkv",
|
||||
".mk3d",
|
||||
".mov",
|
||||
".mp2",
|
||||
".mp4",
|
||||
".mpe",
|
||||
".mpeg",
|
||||
|
||||
@@ -398,6 +398,12 @@ namespace Emby.Server.Implementations.AppBase
|
||||
});
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public ConfigurationStore[] GetConfigurationStores()
|
||||
{
|
||||
return _configurationStores;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Type GetConfigurationType(string key)
|
||||
{
|
||||
|
||||
@@ -24,12 +24,12 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="DiscUtils.Udf" Version="0.16.13" />
|
||||
<PackageReference Include="Jellyfin.XmlTv" Version="10.6.2" />
|
||||
<PackageReference Include="Jellyfin.XmlTv" Version="10.8.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="6.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="6.0.3" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="6.0.4" />
|
||||
<PackageReference Include="Mono.Nat" Version="3.0.2" />
|
||||
<PackageReference Include="prometheus-net.DotNetRuntime" Version="4.2.3" />
|
||||
<PackageReference Include="sharpcompress" Version="0.30.1" />
|
||||
|
||||
@@ -2840,10 +2840,12 @@ namespace Emby.Server.Implementations.Library
|
||||
|
||||
var existingNameCount = 1; // first numbered name will be 2
|
||||
var virtualFolderPath = Path.Combine(rootFolderPath, name);
|
||||
var originalName = name;
|
||||
while (Directory.Exists(virtualFolderPath))
|
||||
{
|
||||
existingNameCount++;
|
||||
virtualFolderPath = Path.Combine(rootFolderPath, name + " " + existingNameCount);
|
||||
name = originalName + existingNameCount;
|
||||
virtualFolderPath = Path.Combine(rootFolderPath, name);
|
||||
}
|
||||
|
||||
var mediaPathInfos = options.PathInfos;
|
||||
|
||||
@@ -65,7 +65,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
||||
|
||||
await RecordFromFile(mediaSource, mediaSource.Path, targetFile, onStarted, cancellationTokenSource.Token).ConfigureAwait(false);
|
||||
|
||||
_logger.LogInformation("Recording completed to file {0}", targetFile);
|
||||
_logger.LogInformation("Recording completed to file {Path}", targetFile);
|
||||
}
|
||||
|
||||
private async Task RecordFromFile(MediaSourceInfo mediaSource, string inputFile, string targetFile, Action onStarted, CancellationToken cancellationToken)
|
||||
@@ -115,7 +115,10 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
||||
// Important - don't await the log task or we won't be able to kill ffmpeg when the user stops playback
|
||||
_ = StartStreamingLog(_process.StandardError.BaseStream, _logFileStream);
|
||||
|
||||
_logger.LogInformation("ffmpeg recording process started for {0}", _targetPath);
|
||||
_logger.LogInformation("ffmpeg recording process started for {Path}", _targetPath);
|
||||
|
||||
// Block until ffmpeg exits
|
||||
await _taskCompletionSource.Task.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private string GetCommandLineArgs(MediaSourceInfo mediaSource, string inputTempFile, string targetFile)
|
||||
|
||||
@@ -8,6 +8,7 @@ using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Security.Cryptography;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Jellyfin.Extensions;
|
||||
@@ -26,6 +27,8 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
||||
{
|
||||
public class XmlTvListingsProvider : IListingsProvider
|
||||
{
|
||||
private static readonly TimeSpan _maxCacheAge = TimeSpan.FromHours(1);
|
||||
|
||||
private readonly IServerConfigurationManager _config;
|
||||
private readonly IHttpClientFactory _httpClientFactory;
|
||||
private readonly ILogger<XmlTvListingsProvider> _logger;
|
||||
@@ -69,13 +72,14 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
||||
return UnzipIfNeeded(info.Path, info.Path);
|
||||
}
|
||||
|
||||
string cacheFilename = DateTime.UtcNow.DayOfYear.ToString(CultureInfo.InvariantCulture) + "-" + DateTime.UtcNow.Hour.ToString(CultureInfo.InvariantCulture) + "-" + info.Id + ".xml";
|
||||
string cacheFilename = info.Id + ".xml";
|
||||
string cacheFile = Path.Combine(_config.ApplicationPaths.CachePath, "xmltv", cacheFilename);
|
||||
if (File.Exists(cacheFile))
|
||||
if (File.Exists(cacheFile) && File.GetLastWriteTimeUtc(cacheFile) >= DateTime.UtcNow.Subtract(_maxCacheAge))
|
||||
{
|
||||
return UnzipIfNeeded(info.Path, cacheFile);
|
||||
}
|
||||
|
||||
File.Delete(cacheFile);
|
||||
_logger.LogInformation("Downloading xmltv listings from {Path}", info.Path);
|
||||
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(cacheFile));
|
||||
@@ -124,7 +128,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
||||
{
|
||||
using (var stream = File.OpenRead(file))
|
||||
{
|
||||
string tempFolder = Path.Combine(_config.ApplicationPaths.TempDirectory, Guid.NewGuid().ToString());
|
||||
string tempFolder = GetTempFolderPath(stream);
|
||||
Directory.CreateDirectory(tempFolder);
|
||||
|
||||
_zipClient.ExtractFirstFileFromGz(stream, tempFolder, "data.xml");
|
||||
@@ -137,7 +141,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
||||
{
|
||||
using (var stream = File.OpenRead(file))
|
||||
{
|
||||
string tempFolder = Path.Combine(_config.ApplicationPaths.TempDirectory, Guid.NewGuid().ToString());
|
||||
string tempFolder = GetTempFolderPath(stream);
|
||||
Directory.CreateDirectory(tempFolder);
|
||||
|
||||
_zipClient.ExtractAllFromGz(stream, tempFolder, true);
|
||||
@@ -146,6 +150,16 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
||||
}
|
||||
}
|
||||
|
||||
private string GetTempFolderPath(Stream stream)
|
||||
{
|
||||
#pragma warning disable CA5351
|
||||
using var md5 = MD5.Create();
|
||||
#pragma warning restore CA5351
|
||||
var checksum = Convert.ToHexString(md5.ComputeHash(stream));
|
||||
stream.Position = 0;
|
||||
return Path.Combine(_config.ApplicationPaths.TempDirectory, checksum);
|
||||
}
|
||||
|
||||
private string FindXmlFile(string directory)
|
||||
{
|
||||
return _fileSystem.GetFiles(directory, true)
|
||||
|
||||
@@ -104,6 +104,10 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
|
||||
{
|
||||
_logger.LogError(ex, "Error updating {0}", package.Name);
|
||||
}
|
||||
catch (InvalidDataException ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error updating {0}", package.Name);
|
||||
}
|
||||
|
||||
// Update progress
|
||||
lock (progress)
|
||||
|
||||
@@ -699,7 +699,9 @@ namespace Emby.Server.Implementations.Session
|
||||
DeviceName = session.DeviceName,
|
||||
ClientName = session.Client,
|
||||
DeviceId = session.DeviceId,
|
||||
Session = session
|
||||
Session = session,
|
||||
PlaybackPositionTicks = info.PositionTicks,
|
||||
PlaySessionId = info.PlaySessionId
|
||||
};
|
||||
|
||||
await _eventManager.PublishAsync(eventArgs).ConfigureAwait(false);
|
||||
@@ -985,7 +987,8 @@ namespace Emby.Server.Implementations.Session
|
||||
DeviceName = session.DeviceName,
|
||||
ClientName = session.Client,
|
||||
DeviceId = session.DeviceId,
|
||||
Session = session
|
||||
Session = session,
|
||||
PlaySessionId = info.PlaySessionId
|
||||
};
|
||||
|
||||
await _eventManager.PublishAsync(eventArgs).ConfigureAwait(false);
|
||||
|
||||
@@ -34,6 +34,11 @@ namespace Emby.Server.Implementations.Sorting
|
||||
throw new ArgumentNullException(nameof(y));
|
||||
}
|
||||
|
||||
if (!x.IndexNumber.HasValue && !y.IndexNumber.HasValue)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!x.IndexNumber.HasValue)
|
||||
{
|
||||
return -1;
|
||||
|
||||
@@ -34,6 +34,11 @@ namespace Emby.Server.Implementations.Sorting
|
||||
throw new ArgumentNullException(nameof(y));
|
||||
}
|
||||
|
||||
if (!x.ParentIndexNumber.HasValue && !y.ParentIndexNumber.HasValue)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!x.ParentIndexNumber.HasValue)
|
||||
{
|
||||
return -1;
|
||||
|
||||
@@ -86,21 +86,23 @@ namespace Jellyfin.Api.Controllers
|
||||
/// Updates named configuration.
|
||||
/// </summary>
|
||||
/// <param name="key">Configuration key.</param>
|
||||
/// <param name="configuration">Configuration.</param>
|
||||
/// <response code="204">Named configuration updated.</response>
|
||||
/// <returns>Update status.</returns>
|
||||
[HttpPost("Configuration/{key}")]
|
||||
[Authorize(Policy = Policies.RequiresElevation)]
|
||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||
public async Task<ActionResult> UpdateNamedConfiguration([FromRoute, Required] string key)
|
||||
public ActionResult UpdateNamedConfiguration([FromRoute, Required] string key, [FromBody, Required] JsonDocument configuration)
|
||||
{
|
||||
var configurationType = _configurationManager.GetConfigurationType(key);
|
||||
var configuration = await JsonSerializer.DeserializeAsync(Request.Body, configurationType, _serializerOptions).ConfigureAwait(false);
|
||||
if (configuration == null)
|
||||
var deserializedConfiguration = configuration.Deserialize(configurationType, _serializerOptions);
|
||||
|
||||
if (deserializedConfiguration == null)
|
||||
{
|
||||
throw new ArgumentException("Body doesn't contain a valid configuration");
|
||||
}
|
||||
|
||||
_configurationManager.SaveConfiguration(key, configuration);
|
||||
_configurationManager.SaveConfiguration(key, deserializedConfiguration);
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
|
||||
@@ -155,7 +155,7 @@ namespace Jellyfin.Api.Controllers
|
||||
/// <response code="204">Package repositories saved.</response>
|
||||
/// <returns>A <see cref="NoContentResult"/>.</returns>
|
||||
[HttpPost("Repositories")]
|
||||
[Authorize(Policy = Policies.DefaultAuthorization)]
|
||||
[Authorize(Policy = Policies.RequiresElevation)]
|
||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||
public ActionResult SetRepositories([FromBody, Required] List<RepositoryInfo> repositoryInfos)
|
||||
{
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authorization" Version="6.0.3" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authorization" Version="6.0.4" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.2.3" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.ReDoc" Version="6.3.0" />
|
||||
|
||||
@@ -362,7 +362,7 @@ namespace Jellyfin.Data.Entities
|
||||
/// <returns><c>True</c> if the user has the specified permission.</returns>
|
||||
public bool HasPermission(PermissionKind kind)
|
||||
{
|
||||
return Permissions.First(p => p.Kind == kind).Value;
|
||||
return Permissions.FirstOrDefault(p => p.Kind == kind)?.Value ?? false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -372,7 +372,15 @@ namespace Jellyfin.Data.Entities
|
||||
/// <param name="value">The value to set.</param>
|
||||
public void SetPermission(PermissionKind kind, bool value)
|
||||
{
|
||||
Permissions.First(p => p.Kind == kind).Value = value;
|
||||
var currentPermission = Permissions.FirstOrDefault(p => p.Kind == kind);
|
||||
if (currentPermission == null)
|
||||
{
|
||||
Permissions.Add(new Permission(kind, value));
|
||||
}
|
||||
else
|
||||
{
|
||||
currentPermission.Value = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -382,9 +390,9 @@ namespace Jellyfin.Data.Entities
|
||||
/// <returns>A string array containing the user's preferences.</returns>
|
||||
public string[] GetPreference(PreferenceKind preference)
|
||||
{
|
||||
var val = Preferences.First(p => p.Kind == preference).Value;
|
||||
var val = Preferences.FirstOrDefault(p => p.Kind == preference)?.Value;
|
||||
|
||||
return Equals(val, string.Empty) ? Array.Empty<string>() : val.Split(Delimiter);
|
||||
return string.IsNullOrEmpty(val) ? Array.Empty<string>() : val.Split(Delimiter);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -395,7 +403,7 @@ namespace Jellyfin.Data.Entities
|
||||
/// <returns>A {T} array containing the user's preference.</returns>
|
||||
public T[] GetPreferenceValues<T>(PreferenceKind preference)
|
||||
{
|
||||
var val = Preferences.First(p => p.Kind == preference).Value;
|
||||
var val = Preferences.FirstOrDefault(p => p.Kind == preference)?.Value;
|
||||
if (string.IsNullOrEmpty(val))
|
||||
{
|
||||
return Array.Empty<T>();
|
||||
@@ -432,8 +440,16 @@ namespace Jellyfin.Data.Entities
|
||||
/// <param name="values">The values.</param>
|
||||
public void SetPreference(PreferenceKind preference, string[] values)
|
||||
{
|
||||
Preferences.First(p => p.Kind == preference).Value
|
||||
= string.Join(Delimiter, values);
|
||||
var value = string.Join(Delimiter, values);
|
||||
var currentPreference = Preferences.FirstOrDefault(p => p.Kind == preference);
|
||||
if (currentPreference == null)
|
||||
{
|
||||
Preferences.Add(new Preference(preference, value));
|
||||
}
|
||||
else
|
||||
{
|
||||
currentPreference.Value = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -444,8 +460,16 @@ namespace Jellyfin.Data.Entities
|
||||
/// <typeparam name="T">The type of value.</typeparam>
|
||||
public void SetPreference<T>(PreferenceKind preference, T[] values)
|
||||
{
|
||||
Preferences.First(p => p.Kind == preference).Value
|
||||
= string.Join(Delimiter, values);
|
||||
var value = string.Join(Delimiter, values);
|
||||
var currentPreference = Preferences.FirstOrDefault(p => p.Kind == preference);
|
||||
if (currentPreference == null)
|
||||
{
|
||||
Preferences.Add(new Preference(preference, value));
|
||||
}
|
||||
else
|
||||
{
|
||||
currentPreference.Value = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -463,6 +463,18 @@ namespace Jellyfin.Networking.Manager
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsInLocalNetwork(IPObject address)
|
||||
{
|
||||
return IsInLocalNetwork(address.Address);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsInLocalNetwork(string address)
|
||||
{
|
||||
return IPHost.TryParse(address, out IPHost ipHost) && IsInLocalNetwork(ipHost);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsInLocalNetwork(IPAddress address)
|
||||
{
|
||||
if (address == null)
|
||||
{
|
||||
@@ -481,36 +493,7 @@ namespace Jellyfin.Networking.Manager
|
||||
}
|
||||
|
||||
// As private addresses can be redefined by Configuration.LocalNetworkAddresses
|
||||
return address.IsLoopback() || (_lanSubnets.ContainsAddress(address) && !_excludedSubnets.ContainsAddress(address));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsInLocalNetwork(string address)
|
||||
{
|
||||
if (IPHost.TryParse(address, out IPHost ep))
|
||||
{
|
||||
return _lanSubnets.ContainsAddress(ep) && !_excludedSubnets.ContainsAddress(ep);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsInLocalNetwork(IPAddress address)
|
||||
{
|
||||
if (address == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(address));
|
||||
}
|
||||
|
||||
// See conversation at https://github.com/jellyfin/jellyfin/pull/3515.
|
||||
if (TrustAllIP6Interfaces && address.AddressFamily == AddressFamily.InterNetworkV6)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// As private addresses can be redefined by Configuration.LocalNetworkAddresses
|
||||
return _lanSubnets.ContainsAddress(address) && !_excludedSubnets.ContainsAddress(address);
|
||||
return IPAddress.IsLoopback(address) || (_lanSubnets.ContainsAddress(address) && !_excludedSubnets.ContainsAddress(address));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
||||
@@ -27,13 +27,13 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.Linq.Async" Version="6.0.1" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.3" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="6.0.3" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.3">
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.4" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="6.0.4" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.4">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.3">
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.4">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using MediaBrowser.Common.Plugins;
|
||||
using MediaBrowser.Common.Plugins;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.LiveTv;
|
||||
using MediaBrowser.Model.ApiClient;
|
||||
using MediaBrowser.Model.Entities;
|
||||
@@ -14,6 +15,17 @@ namespace Jellyfin.Server.Filters
|
||||
/// </summary>
|
||||
public class AdditionalModelFilter : IDocumentFilter
|
||||
{
|
||||
private readonly IServerConfigurationManager _serverConfigurationManager;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AdditionalModelFilter"/> class.
|
||||
/// </summary>
|
||||
/// <param name="serverConfigurationManager">Instance of the <see cref="IServerConfigurationManager"/> interface.</param>
|
||||
public AdditionalModelFilter(IServerConfigurationManager serverConfigurationManager)
|
||||
{
|
||||
_serverConfigurationManager = serverConfigurationManager;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
|
||||
{
|
||||
@@ -29,6 +41,11 @@ namespace Jellyfin.Server.Filters
|
||||
|
||||
context.SchemaGenerator.GenerateSchema(typeof(SessionMessageType), context.SchemaRepository);
|
||||
context.SchemaGenerator.GenerateSchema(typeof(ServerDiscoveryInfo), context.SchemaRepository);
|
||||
|
||||
foreach (var configuration in _serverConfigurationManager.GetConfigurationStores())
|
||||
{
|
||||
context.SchemaGenerator.GenerateSchema(configuration.ConfigurationType, context.SchemaRepository);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,8 +37,8 @@
|
||||
<PackageReference Include="CommandLineParser" Version="2.8.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="6.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="6.0.3" />
|
||||
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" Version="6.0.3" />
|
||||
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="6.0.4" />
|
||||
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" Version="6.0.4" />
|
||||
<PackageReference Include="prometheus-net" Version="6.0.0" />
|
||||
<PackageReference Include="prometheus-net.AspNetCore" Version="6.0.0" />
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="4.1.0" />
|
||||
|
||||
@@ -60,6 +60,12 @@ namespace MediaBrowser.Common.Configuration
|
||||
/// <returns>System.Object.</returns>
|
||||
object GetConfiguration(string key);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the array of coniguration stores.
|
||||
/// </summary>
|
||||
/// <returns>Array of ConfigurationStore.</returns>
|
||||
ConfigurationStore[] GetConfigurationStores();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of the configuration.
|
||||
/// </summary>
|
||||
|
||||
@@ -65,7 +65,7 @@ namespace MediaBrowser.Common.Net
|
||||
address = address.MapToIPv4();
|
||||
}
|
||||
|
||||
if (IsLoopback(address))
|
||||
if (IPAddress.IsLoopback(address))
|
||||
{
|
||||
return (address, prefixLength);
|
||||
}
|
||||
@@ -102,31 +102,6 @@ namespace MediaBrowser.Common.Net
|
||||
return (new IPAddress(addressBytes), prefixLength);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests to see if the ip address is a Loopback address.
|
||||
/// </summary>
|
||||
/// <param name="address">Value to test.</param>
|
||||
/// <returns>True if it is.</returns>
|
||||
public static bool IsLoopback(IPAddress address)
|
||||
{
|
||||
if (address == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(address));
|
||||
}
|
||||
|
||||
if (!address.Equals(IPAddress.None))
|
||||
{
|
||||
if (address.IsIPv4MappedToIPv6)
|
||||
{
|
||||
address = address.MapToIPv4();
|
||||
}
|
||||
|
||||
return address.Equals(IPAddress.Loopback) || address.Equals(IPAddress.IPv6Loopback);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests to see if the ip address is an IP6 address.
|
||||
/// </summary>
|
||||
@@ -295,7 +270,7 @@ namespace MediaBrowser.Common.Net
|
||||
/// <returns>True if it is.</returns>
|
||||
public virtual bool IsLoopback()
|
||||
{
|
||||
return IsLoopback(Address);
|
||||
return IPAddress.IsLoopback(Address);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -2244,7 +2244,8 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
{
|
||||
if (state.AudioStream.IsExternal)
|
||||
{
|
||||
int externalAudioMapIndex = state.SubtitleStream != null && state.SubtitleStream.IsExternal ? 2 : 1;
|
||||
bool hasExternalGraphicsSubs = state.SubtitleStream != null && state.SubtitleStream.IsExternal && !state.SubtitleStream.IsTextSubtitleStream;
|
||||
int externalAudioMapIndex = hasExternalGraphicsSubs ? 2 : 1;
|
||||
int externalAudioStream = state.MediaSource.MediaStreams.Where(i => i.Path == state.AudioStream.Path).ToList().IndexOf(state.AudioStream);
|
||||
|
||||
args += string.Format(
|
||||
@@ -3381,7 +3382,8 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
}
|
||||
|
||||
// qsv requires a fixed pool size.
|
||||
subFilters.Add("hwupload=extra_hw_frames=32");
|
||||
// default to 64 otherwise it will fail on certain iGPU.
|
||||
subFilters.Add("hwupload=extra_hw_frames=64");
|
||||
|
||||
var (overlayW, overlayH) = GetFixedOutputSize(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
|
||||
var overlaySize = (overlayW.HasValue && overlayH.HasValue)
|
||||
@@ -3589,7 +3591,8 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
}
|
||||
|
||||
// qsv requires a fixed pool size.
|
||||
subFilters.Add("hwupload=extra_hw_frames=32");
|
||||
// default to 64 otherwise it will fail on certain iGPU.
|
||||
subFilters.Add("hwupload=extra_hw_frames=64");
|
||||
|
||||
var (overlayW, overlayH) = GetFixedOutputSize(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
|
||||
var overlaySize = (overlayW.HasValue && overlayH.HasValue)
|
||||
|
||||
@@ -5,6 +5,11 @@ namespace MediaBrowser.Controller.Resolvers
|
||||
/// </summary>
|
||||
public enum ResolverPriority
|
||||
{
|
||||
/// <summary>
|
||||
/// The highest priority. Used by plugins to bypass the default server resolvers.
|
||||
/// </summary>
|
||||
Plugin = 0,
|
||||
|
||||
/// <summary>
|
||||
/// The first.
|
||||
/// </summary>
|
||||
|
||||
@@ -744,16 +744,19 @@ namespace MediaBrowser.Model.Dlna
|
||||
{
|
||||
var videoCodecs = ContainerProfile.SplitValue(transcodingProfile.VideoCodec);
|
||||
|
||||
if (ContainerProfile.ContainsContainer(videoCodecs, item.VideoStream.Codec))
|
||||
if (ContainerProfile.ContainsContainer(videoCodecs, item.VideoStream?.Codec))
|
||||
{
|
||||
var videoCodec = transcodingProfile.VideoCodec;
|
||||
var container = transcodingProfile.Container;
|
||||
var appliedVideoConditions = options.Profile.CodecProfiles
|
||||
.Where(i => i.Type == CodecType.Video &&
|
||||
i.ContainsAnyCodec(videoCodec, container))
|
||||
i.ContainsAnyCodec(videoCodec, container) &&
|
||||
i.ApplyConditions.All(applyCondition => ConditionProcessor.IsVideoConditionSatisfied(applyCondition, videoStream?.Width, videoStream?.Height, videoStream?.BitDepth, videoStream?.BitRate, videoStream?.Profile, videoStream?.Level, videoFramerate, videoStream?.PacketLength, timestamp, videoStream?.IsAnamorphic, videoStream?.IsInterlaced, videoStream?.RefFrames, numVideoStreams, numAudioStreams, videoStream?.CodecTag, videoStream?.IsAVC)))
|
||||
.Select(i =>
|
||||
i.ApplyConditions.Any(applyCondition => ConditionProcessor.IsVideoConditionSatisfied(applyCondition, videoStream?.Width, videoStream?.Height, videoStream?.BitDepth, videoStream?.BitRate, videoStream?.Profile, videoStream?.Level, videoFramerate, videoStream?.PacketLength, timestamp, videoStream?.IsAnamorphic, videoStream?.IsInterlaced, videoStream?.RefFrames, numVideoStreams, numAudioStreams, videoStream?.CodecTag, videoStream?.IsAVC)));
|
||||
var conditionsSatisfied = !appliedVideoConditions.Any() || !appliedVideoConditions.Any(satisfied => !satisfied);
|
||||
i.Conditions.All(condition => ConditionProcessor.IsVideoConditionSatisfied(condition, videoStream?.Width, videoStream?.Height, videoStream?.BitDepth, videoStream?.BitRate, videoStream?.Profile, videoStream?.Level, videoFramerate, videoStream?.PacketLength, timestamp, videoStream?.IsAnamorphic, videoStream?.IsInterlaced, videoStream?.RefFrames, numVideoStreams, numAudioStreams, videoStream?.CodecTag, videoStream?.IsAVC)));
|
||||
|
||||
// An empty appliedVideoConditions means that the codec has no conditions for the current video stream
|
||||
var conditionsSatisfied = appliedVideoConditions.All(satisfied => satisfied);
|
||||
return conditionsSatisfied ? 1 : 2;
|
||||
}
|
||||
|
||||
@@ -770,28 +773,28 @@ namespace MediaBrowser.Model.Dlna
|
||||
{
|
||||
// prefer matching video codecs
|
||||
var videoCodecs = ContainerProfile.SplitValue(videoCodec);
|
||||
var directVideoCodec = ContainerProfile.ContainsContainer(videoCodecs, videoStream.Codec) ? videoStream.Codec : null;
|
||||
var directVideoCodec = ContainerProfile.ContainsContainer(videoCodecs, videoStream?.Codec) ? videoStream?.Codec : null;
|
||||
playlistItem.VideoCodecs = directVideoCodec != null ? new[] { directVideoCodec } : videoCodecs;
|
||||
|
||||
// copy video codec options as a starting point, this applies to transcode and direct-stream
|
||||
playlistItem.MaxFramerate = videoStream.AverageFrameRate;
|
||||
var qualifier = videoStream.Codec;
|
||||
if (videoStream.Level.HasValue)
|
||||
playlistItem.MaxFramerate = videoStream?.AverageFrameRate;
|
||||
var qualifier = videoStream?.Codec;
|
||||
if (videoStream?.Level != null)
|
||||
{
|
||||
playlistItem.SetOption(qualifier, "level", videoStream.Level.Value.ToString(CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
if (videoStream.BitDepth.HasValue)
|
||||
if (videoStream?.BitDepth != null)
|
||||
{
|
||||
playlistItem.SetOption(qualifier, "videobitdepth", videoStream.BitDepth.Value.ToString(CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(videoStream.Profile))
|
||||
if (!string.IsNullOrEmpty(videoStream?.Profile))
|
||||
{
|
||||
playlistItem.SetOption(qualifier, "profile", videoStream.Profile.ToLowerInvariant());
|
||||
}
|
||||
|
||||
if (videoStream.Level != 0)
|
||||
if (videoStream != null && videoStream.Level != 0)
|
||||
{
|
||||
playlistItem.SetOption(qualifier, "level", videoStream.Level.ToString());
|
||||
}
|
||||
@@ -843,7 +846,7 @@ namespace MediaBrowser.Model.Dlna
|
||||
var appliedVideoConditions = options.Profile.CodecProfiles
|
||||
.Where(i => i.Type == CodecType.Video &&
|
||||
i.ContainsAnyCodec(videoCodec, container) &&
|
||||
i.ApplyConditions.Any(applyCondition => ConditionProcessor.IsVideoConditionSatisfied(applyCondition, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isInterlaced, refFrames, numVideoStreams, numAudioStreams, videoCodecTag, isAvc)));
|
||||
i.ApplyConditions.All(applyCondition => ConditionProcessor.IsVideoConditionSatisfied(applyCondition, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isInterlaced, refFrames, numVideoStreams, numAudioStreams, videoCodecTag, isAvc)));
|
||||
var isFirstAppliedCodecProfile = true;
|
||||
foreach (var i in appliedVideoConditions)
|
||||
{
|
||||
@@ -873,9 +876,9 @@ namespace MediaBrowser.Model.Dlna
|
||||
int? inputAudioBitDepth = audioStream == null ? null : audioStream.BitDepth;
|
||||
|
||||
var appliedAudioConditions = options.Profile.CodecProfiles
|
||||
.Where(i => i.Type == CodecType.Video &&
|
||||
.Where(i => i.Type == CodecType.VideoAudio &&
|
||||
i.ContainsAnyCodec(audioCodec, container) &&
|
||||
i.ApplyConditions.Any(applyCondition => ConditionProcessor.IsVideoAudioConditionSatisfied(applyCondition, audioChannels, inputAudioBitrate, inputAudioSampleRate, inputAudioBitDepth, audioProfile, isSecondaryAudio)));
|
||||
i.ApplyConditions.All(applyCondition => ConditionProcessor.IsVideoAudioConditionSatisfied(applyCondition, audioChannels, inputAudioBitrate, inputAudioSampleRate, inputAudioBitDepth, audioProfile, isSecondaryAudio)));
|
||||
isFirstAppliedCodecProfile = true;
|
||||
foreach (var i in appliedAudioConditions)
|
||||
{
|
||||
@@ -1108,18 +1111,9 @@ namespace MediaBrowser.Model.Dlna
|
||||
profile,
|
||||
"VideoCodecProfile",
|
||||
profile.CodecProfiles
|
||||
.Where(codecProfile => codecProfile.Type == CodecType.Video && codecProfile.ContainsAnyCodec(videoStream?.Codec, container))
|
||||
.SelectMany(codecProfile =>
|
||||
{
|
||||
var failedApplyConditions = checkVideoConditions(codecProfile.ApplyConditions);
|
||||
if (!failedApplyConditions.Any())
|
||||
{
|
||||
return Array.Empty<ProfileCondition>();
|
||||
}
|
||||
|
||||
var failedConditions = checkVideoConditions(codecProfile.Conditions);
|
||||
return failedApplyConditions.Concat(failedConditions);
|
||||
}));
|
||||
.Where(codecProfile => codecProfile.Type == CodecType.Video && codecProfile.ContainsAnyCodec(videoStream?.Codec, container) &&
|
||||
!checkVideoConditions(codecProfile.ApplyConditions).Any())
|
||||
.SelectMany(codecProfile => checkVideoConditions(codecProfile.Conditions)));
|
||||
|
||||
// Check audiocandidates profile conditions
|
||||
var audioStreamMatches = candidateAudioStreams.ToDictionary(s => s, audioStream => CheckVideoAudioStreamDirectPlay(options, mediaSource, container, audioStream, defaultLanguage, defaultMarked));
|
||||
@@ -1189,7 +1183,18 @@ namespace MediaBrowser.Model.Dlna
|
||||
audioCodecProfileReasons = audioStreamMatches.GetValueOrDefault(selectedAudioStream);
|
||||
}
|
||||
|
||||
var failureReasons = directPlayProfileReasons | containerProfileReasons | videoCodecProfileReasons | audioCodecProfileReasons | subtitleProfileReasons;
|
||||
var failureReasons = directPlayProfileReasons | containerProfileReasons | subtitleProfileReasons;
|
||||
|
||||
if ((failureReasons & TranscodeReason.VideoCodecNotSupported) == 0)
|
||||
{
|
||||
failureReasons |= videoCodecProfileReasons;
|
||||
}
|
||||
|
||||
if ((failureReasons & TranscodeReason.AudioCodecNotSupported) == 0)
|
||||
{
|
||||
failureReasons |= audioCodecProfileReasons;
|
||||
}
|
||||
|
||||
var directStreamFailureReasons = failureReasons & (~DirectStreamReasons);
|
||||
|
||||
PlayMethod? playMethod = null;
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="System.Globalization" Version="4.3.0" />
|
||||
<PackageReference Include="System.Text.Json" Version="6.0.2" />
|
||||
<PackageReference Include="System.Text.Json" Version="6.0.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -30,6 +30,7 @@ using MediaBrowser.Model.Configuration;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.Extensions;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.Net;
|
||||
using MediaBrowser.Model.Providers;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Priority_Queue;
|
||||
@@ -188,6 +189,12 @@ namespace MediaBrowser.Providers.Manager
|
||||
throw new HttpRequestException("Invalid image received.", null, HttpStatusCode.NotFound);
|
||||
}
|
||||
|
||||
// some iptv/epg providers don't correctly report media type, extract from url if no extension found
|
||||
if (string.IsNullOrWhiteSpace(MimeTypes.ToExtension(contentType)))
|
||||
{
|
||||
contentType = MimeTypes.GetMimeType(url);
|
||||
}
|
||||
|
||||
await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
|
||||
await SaveImage(
|
||||
item,
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
@@ -153,6 +152,7 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||
}
|
||||
|
||||
var files = directoryService.GetFilePaths(folder, clearCache).ToList();
|
||||
files.Remove(video.Path);
|
||||
var internalMetadataPath = video.GetInternalMetadataPath();
|
||||
if (_fileSystem.DirectoryExists(internalMetadataPath))
|
||||
{
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
using MediaBrowser.Model.Plugins;
|
||||
|
||||
namespace MediaBrowser.Providers.Plugins.StudioImages
|
||||
namespace MediaBrowser.Providers.Plugins.StudioImages.Configuration
|
||||
{
|
||||
public class PluginConfiguration : BasePluginConfiguration
|
||||
{
|
||||
@@ -12,12 +12,19 @@ namespace MediaBrowser.Providers.Plugins.StudioImages
|
||||
{
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrEmpty(_repository))
|
||||
{
|
||||
_repository = Plugin.DefaultServer;
|
||||
}
|
||||
|
||||
return _repository;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
_repository = value.TrimEnd('/');
|
||||
_repository = string.IsNullOrEmpty(value)
|
||||
? Plugin.DefaultServer
|
||||
: value.TrimEnd('/');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
<div class="content-primary">
|
||||
<form class="configForm">
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="repository" required label="Repository" />
|
||||
<div class="fieldDescription">This can be any Jellyfin-compatible artwork repository.</div>
|
||||
<input is="emby-input" type="text" id="repository" label="Repository" />
|
||||
<div class="fieldDescription">This can be any Jellyfin-compatible artwork repository. Leave blank to use default repository.</div>
|
||||
</div>
|
||||
<br />
|
||||
<div>
|
||||
@@ -44,7 +44,7 @@
|
||||
Dashboard.showLoadingMsg();
|
||||
|
||||
ApiClient.getPluginConfiguration(PluginConfig.pluginId).then(function (config) {
|
||||
config.RepositoryUrl = document.querySelector('#server').value;
|
||||
config.RepositoryUrl = document.querySelector('#repository').value;
|
||||
|
||||
ApiClient.updatePluginConfiguration(PluginConfig.pluginId, config).then(Dashboard.processPluginConfigurationUpdateResult);
|
||||
});
|
||||
|
||||
@@ -7,6 +7,7 @@ using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Common.Plugins;
|
||||
using MediaBrowser.Model.Plugins;
|
||||
using MediaBrowser.Model.Serialization;
|
||||
using MediaBrowser.Providers.Plugins.StudioImages.Configuration;
|
||||
|
||||
namespace MediaBrowser.Providers.Plugins.StudioImages
|
||||
{
|
||||
|
||||
@@ -18,29 +18,24 @@ using MediaBrowser.Controller.Providers;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.Providers;
|
||||
using MediaBrowser.Providers.Plugins.StudioImages;
|
||||
|
||||
namespace MediaBrowser.Providers.Studios
|
||||
namespace MediaBrowser.Providers.Plugins.StudioImages
|
||||
{
|
||||
public class StudiosImageProvider : IRemoteImageProvider
|
||||
{
|
||||
private readonly IServerConfigurationManager _config;
|
||||
private readonly IHttpClientFactory _httpClientFactory;
|
||||
private readonly IFileSystem _fileSystem;
|
||||
private readonly string repositoryUrl;
|
||||
|
||||
public StudiosImageProvider(IServerConfigurationManager config, IHttpClientFactory httpClientFactory, IFileSystem fileSystem)
|
||||
{
|
||||
_config = config;
|
||||
_httpClientFactory = httpClientFactory;
|
||||
_fileSystem = fileSystem;
|
||||
repositoryUrl = Plugin.Instance.Configuration.RepositoryUrl;
|
||||
}
|
||||
|
||||
public string Name => "Artwork Repository";
|
||||
|
||||
public int Order => 0;
|
||||
|
||||
public bool Supports(BaseItem item)
|
||||
{
|
||||
return item is Studio;
|
||||
@@ -98,12 +93,12 @@ namespace MediaBrowser.Providers.Studios
|
||||
|
||||
private string GetUrl(string image, string filename)
|
||||
{
|
||||
return string.Format(CultureInfo.InvariantCulture, "{0}/images/{1}/{2}.jpg", repositoryUrl, image, filename);
|
||||
return string.Format(CultureInfo.InvariantCulture, "{0}/images/{1}/{2}.jpg", GetRepositoryUrl(), image, filename);
|
||||
}
|
||||
|
||||
private Task<string> EnsureThumbsList(string file, CancellationToken cancellationToken)
|
||||
{
|
||||
string url = string.Format(CultureInfo.InvariantCulture, "{0}/thumbs.txt", repositoryUrl);
|
||||
string url = string.Format(CultureInfo.InvariantCulture, "{0}/thumbs.txt", GetRepositoryUrl());
|
||||
|
||||
return EnsureList(url, file, _fileSystem, cancellationToken);
|
||||
}
|
||||
@@ -169,5 +164,8 @@ namespace MediaBrowser.Providers.Studios
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string GetRepositoryUrl()
|
||||
=> Plugin.Instance.Configuration.RepositoryUrl;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,8 +84,8 @@ namespace MediaBrowser.Providers.Plugins.Tmdb
|
||||
public static bool IsTrailerType(Video video)
|
||||
{
|
||||
return video.Site.Equals("youtube", StringComparison.OrdinalIgnoreCase)
|
||||
&& (!video.Type.Equals("trailer", StringComparison.OrdinalIgnoreCase)
|
||||
|| !video.Type.Equals("teaser", StringComparison.OrdinalIgnoreCase));
|
||||
&& (video.Type.Equals("trailer", StringComparison.OrdinalIgnoreCase)
|
||||
|| video.Type.Equals("teaser", StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -58,7 +58,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
|
||||
{
|
||||
var formatString = ConfigurationManager.GetNfoConfiguration().ReleaseDateFormat;
|
||||
|
||||
writer.WriteElementString("disbanded", artist.EndDate.Value.ToLocalTime().ToString(formatString, CultureInfo.InvariantCulture));
|
||||
writer.WriteElementString("disbanded", artist.EndDate.Value.ToString(formatString, CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
var albums = artist
|
||||
|
||||
@@ -473,7 +473,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
|
||||
writer.WriteElementString("lockedfields", string.Join('|', item.LockedFields));
|
||||
}
|
||||
|
||||
writer.WriteElementString("dateadded", item.DateCreated.ToLocalTime().ToString(DateAddedFormat, CultureInfo.InvariantCulture));
|
||||
writer.WriteElementString("dateadded", item.DateCreated.ToString(DateAddedFormat, CultureInfo.InvariantCulture));
|
||||
|
||||
writer.WriteElementString("title", item.Name ?? string.Empty);
|
||||
|
||||
@@ -601,16 +601,16 @@ namespace MediaBrowser.XbmcMetadata.Savers
|
||||
{
|
||||
writer.WriteElementString(
|
||||
"formed",
|
||||
item.PremiereDate.Value.ToLocalTime().ToString(formatString, CultureInfo.InvariantCulture));
|
||||
item.PremiereDate.Value.ToString(formatString, CultureInfo.InvariantCulture));
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.WriteElementString(
|
||||
"premiered",
|
||||
item.PremiereDate.Value.ToLocalTime().ToString(formatString, CultureInfo.InvariantCulture));
|
||||
item.PremiereDate.Value.ToString(formatString, CultureInfo.InvariantCulture));
|
||||
writer.WriteElementString(
|
||||
"releasedate",
|
||||
item.PremiereDate.Value.ToLocalTime().ToString(formatString, CultureInfo.InvariantCulture));
|
||||
item.PremiereDate.Value.ToString(formatString, CultureInfo.InvariantCulture));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -622,7 +622,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
|
||||
|
||||
writer.WriteElementString(
|
||||
"enddate",
|
||||
item.EndDate.Value.ToLocalTime().ToString(formatString, CultureInfo.InvariantCulture));
|
||||
item.EndDate.Value.ToString(formatString, CultureInfo.InvariantCulture));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -891,7 +891,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
|
||||
{
|
||||
writer.WriteElementString(
|
||||
"lastplayed",
|
||||
userdata.LastPlayedDate.Value.ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture).ToLowerInvariant());
|
||||
userdata.LastPlayedDate.Value.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture).ToLowerInvariant());
|
||||
}
|
||||
|
||||
writer.WriteStartElement("resume");
|
||||
|
||||
@@ -75,7 +75,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
|
||||
{
|
||||
var formatString = ConfigurationManager.GetNfoConfiguration().ReleaseDateFormat;
|
||||
|
||||
writer.WriteElementString("aired", episode.PremiereDate.Value.ToLocalTime().ToString(formatString, CultureInfo.InvariantCulture));
|
||||
writer.WriteElementString("aired", episode.PremiereDate.Value.ToString(formatString, CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
if (!episode.ParentIndexNumber.HasValue || episode.ParentIndexNumber.Value == 0)
|
||||
|
||||
12
debian/changelog
vendored
12
debian/changelog
vendored
@@ -1,8 +1,14 @@
|
||||
jellyfin-server (10.8.0-1) unstable; urgency=medium
|
||||
jellyfin-server (10.8.0~beta2) unstable; urgency=medium
|
||||
|
||||
* Forthcoming stable release
|
||||
* New upstream version 10.8.0-beta2; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.8.0-beta2
|
||||
|
||||
-- Jellyfin Packaging Team <packaging@jellyfin.org> Fri, 04 Dec 2020 21:55:12 -0500
|
||||
-- Jellyfin Packaging Team <packaging@jellyfin.org> Sun, 17 Apr 2022 15:51:43 -0400
|
||||
|
||||
jellyfin-server (10.8.0~beta1) unstable; urgency=medium
|
||||
|
||||
* New upstream version 10.8.0-beta1; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.8.0-beta1
|
||||
|
||||
-- Jellyfin Packaging Team <packaging@jellyfin.org> Fri, 25 Mar 2022 22:22:51 -0400
|
||||
|
||||
jellyfin-server (10.7.0-1) unstable; urgency=medium
|
||||
|
||||
|
||||
2
debian/metapackage/jellyfin
vendored
2
debian/metapackage/jellyfin
vendored
@@ -5,7 +5,7 @@ Homepage: https://jellyfin.org
|
||||
Standards-Version: 3.9.2
|
||||
|
||||
Package: jellyfin
|
||||
Version: 10.8.0
|
||||
Version: 10.8.0~beta2
|
||||
Maintainer: Jellyfin Packaging Team <packaging@jellyfin.org>
|
||||
Depends: jellyfin-server, jellyfin-web
|
||||
Description: Provides the Jellyfin Free Software Media System
|
||||
|
||||
@@ -13,7 +13,7 @@ RUN yum update -yq \
|
||||
&& yum install -yq @buildsys-build rpmdevtools yum-plugins-core libcurl-devel fontconfig-devel freetype-devel openssl-devel glibc-devel libicu-devel git wget
|
||||
|
||||
# Install DotNET SDK
|
||||
RUN wget -q https://download.visualstudio.microsoft.com/download/pr/c505a449-9ecf-4352-8629-56216f521616/bd6807340faae05b61de340c8bf161e8/dotnet-sdk-6.0.201-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
|
||||
RUN wget -q https://download.visualstudio.microsoft.com/download/pr/9d8c7137-2091-4fc6-a419-60ba59c8b9de/db0c5cda94f31d2260d369123de32d59/dotnet-sdk-6.0.202-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
|
||||
&& mkdir -p dotnet-sdk \
|
||||
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
|
||||
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
|
||||
|
||||
@@ -12,7 +12,7 @@ RUN dnf update -yq \
|
||||
&& dnf install -yq @buildsys-build rpmdevtools git dnf-plugins-core libcurl-devel fontconfig-devel freetype-devel openssl-devel glibc-devel libicu-devel systemd wget
|
||||
|
||||
# Install DotNET SDK
|
||||
RUN wget -q https://download.visualstudio.microsoft.com/download/pr/c505a449-9ecf-4352-8629-56216f521616/bd6807340faae05b61de340c8bf161e8/dotnet-sdk-6.0.201-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
|
||||
RUN wget -q https://download.visualstudio.microsoft.com/download/pr/9d8c7137-2091-4fc6-a419-60ba59c8b9de/db0c5cda94f31d2260d369123de32d59/dotnet-sdk-6.0.202-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
|
||||
&& mkdir -p dotnet-sdk \
|
||||
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
|
||||
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
|
||||
|
||||
@@ -17,7 +17,7 @@ RUN apt-get update -yqq \
|
||||
libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0
|
||||
|
||||
# Install dotnet repository
|
||||
RUN wget -q https://download.visualstudio.microsoft.com/download/pr/c505a449-9ecf-4352-8629-56216f521616/bd6807340faae05b61de340c8bf161e8/dotnet-sdk-6.0.201-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
|
||||
RUN wget -q https://download.visualstudio.microsoft.com/download/pr/9d8c7137-2091-4fc6-a419-60ba59c8b9de/db0c5cda94f31d2260d369123de32d59/dotnet-sdk-6.0.202-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
|
||||
&& mkdir -p dotnet-sdk \
|
||||
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
|
||||
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
|
||||
|
||||
@@ -16,7 +16,7 @@ RUN apt-get update -yqq \
|
||||
mmv build-essential lsb-release
|
||||
|
||||
# Install dotnet repository
|
||||
RUN wget -q https://download.visualstudio.microsoft.com/download/pr/c505a449-9ecf-4352-8629-56216f521616/bd6807340faae05b61de340c8bf161e8/dotnet-sdk-6.0.201-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
|
||||
RUN wget -q https://download.visualstudio.microsoft.com/download/pr/9d8c7137-2091-4fc6-a419-60ba59c8b9de/db0c5cda94f31d2260d369123de32d59/dotnet-sdk-6.0.202-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
|
||||
&& mkdir -p dotnet-sdk \
|
||||
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
|
||||
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
|
||||
|
||||
@@ -16,7 +16,7 @@ RUN apt-get update -yqq \
|
||||
mmv build-essential lsb-release
|
||||
|
||||
# Install dotnet repository
|
||||
RUN wget -q https://download.visualstudio.microsoft.com/download/pr/c505a449-9ecf-4352-8629-56216f521616/bd6807340faae05b61de340c8bf161e8/dotnet-sdk-6.0.201-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
|
||||
RUN wget -q https://download.visualstudio.microsoft.com/download/pr/9d8c7137-2091-4fc6-a419-60ba59c8b9de/db0c5cda94f31d2260d369123de32d59/dotnet-sdk-6.0.202-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
|
||||
&& mkdir -p dotnet-sdk \
|
||||
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
|
||||
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
%endif
|
||||
|
||||
Name: jellyfin
|
||||
Version: 10.8.0
|
||||
Version: 10.8.0~beta2
|
||||
Release: 1%{?dist}
|
||||
Summary: The Free Software Media System
|
||||
License: GPLv3
|
||||
@@ -153,6 +153,10 @@ fi
|
||||
%systemd_postun_with_restart jellyfin.service
|
||||
|
||||
%changelog
|
||||
* Sun Apr 17 2022 Jellyfin Packaging Team <packaging@jellyfin.org>
|
||||
- New upstream version 10.8.0-beta2; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.8.0-beta2
|
||||
* Fri Mar 25 2022 Jellyfin Packaging Team <packaging@jellyfin.org>
|
||||
- New upstream version 10.8.0-beta1; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.8.0-beta1
|
||||
* Mon Nov 29 2021 Brian J. Murrell <brian@interlinx.bc.ca>
|
||||
- Add jellyfin-server-lowports.service drop-in in a server-lowports
|
||||
subpackage to allow binding to low ports
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<PackageReference Include="AutoFixture" Version="4.17.0" />
|
||||
<PackageReference Include="AutoFixture.AutoMoq" Version="4.17.0" />
|
||||
<PackageReference Include="AutoFixture.Xunit2" Version="4.17.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="6.0.3" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="6.0.4" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
|
||||
@@ -131,6 +131,37 @@ namespace Jellyfin.Model.Tests
|
||||
[InlineData("Null", "mkv-vp9-aac-srt-2600k", null, TranscodeReason.ContainerBitrateExceedsLimit)]
|
||||
[InlineData("Null", "mkv-vp9-ac3-srt-2600k", null, TranscodeReason.ContainerBitrateExceedsLimit)]
|
||||
[InlineData("Null", "mkv-vp9-vorbis-vtt-2600k", null, TranscodeReason.ContainerBitrateExceedsLimit)]
|
||||
// AndroidTV
|
||||
[InlineData("AndroidTVExoPlayer", "mp4-h264-aac-vtt-2600k", PlayMethod.DirectPlay)]
|
||||
[InlineData("AndroidTVExoPlayer", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.DirectPlay)]
|
||||
[InlineData("AndroidTVExoPlayer", "mp4-h264-ac3-srt-2600k", PlayMethod.DirectPlay)]
|
||||
[InlineData("AndroidTVExoPlayer", "mp4-hevc-aac-srt-15200k", PlayMethod.DirectPlay)]
|
||||
[InlineData("AndroidTVExoPlayer", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.DirectPlay)]
|
||||
[InlineData("AndroidTVExoPlayer", "mkv-vp9-aac-srt-2600k", PlayMethod.DirectPlay)]
|
||||
[InlineData("AndroidTVExoPlayer", "mkv-vp9-ac3-srt-2600k", PlayMethod.DirectPlay)]
|
||||
[InlineData("AndroidTVExoPlayer", "mkv-vp9-vorbis-vtt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)]
|
||||
// Tizen 3 Stereo
|
||||
[InlineData("Tizen3-stereo", "mp4-h264-aac-vtt-2600k", PlayMethod.DirectPlay)]
|
||||
[InlineData("Tizen3-stereo", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.DirectPlay)]
|
||||
[InlineData("Tizen3-stereo", "mp4-h264-ac3-srt-2600k", PlayMethod.DirectPlay)]
|
||||
[InlineData("Tizen3-stereo", "mp4-h264-dts-srt-2600k", PlayMethod.DirectPlay)]
|
||||
[InlineData("Tizen3-stereo", "mp4-hevc-aac-srt-15200k", PlayMethod.DirectPlay)]
|
||||
[InlineData("Tizen3-stereo", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.DirectPlay)]
|
||||
[InlineData("Tizen3-stereo", "mp4-hevc-truehd-srt-15200k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)]
|
||||
[InlineData("Tizen3-stereo", "mkv-vp9-aac-srt-2600k", PlayMethod.DirectPlay)]
|
||||
[InlineData("Tizen3-stereo", "mkv-vp9-ac3-srt-2600k", PlayMethod.DirectPlay)]
|
||||
[InlineData("Tizen3-stereo", "mkv-vp9-vorbis-vtt-2600k", PlayMethod.DirectPlay)]
|
||||
// Tizen 4 4K 5.1
|
||||
[InlineData("Tizen4-4K-5.1", "mp4-h264-aac-vtt-2600k", PlayMethod.DirectPlay)]
|
||||
[InlineData("Tizen4-4K-5.1", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.DirectPlay)]
|
||||
[InlineData("Tizen4-4K-5.1", "mp4-h264-ac3-srt-2600k", PlayMethod.DirectPlay)]
|
||||
[InlineData("Tizen4-4K-5.1", "mp4-h264-dts-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)]
|
||||
[InlineData("Tizen4-4K-5.1", "mp4-hevc-aac-srt-15200k", PlayMethod.DirectPlay)]
|
||||
[InlineData("Tizen4-4K-5.1", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.DirectPlay)]
|
||||
[InlineData("Tizen4-4K-5.1", "mp4-hevc-truehd-srt-15200k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)]
|
||||
[InlineData("Tizen4-4K-5.1", "mkv-vp9-aac-srt-2600k", PlayMethod.DirectPlay)]
|
||||
[InlineData("Tizen4-4K-5.1", "mkv-vp9-ac3-srt-2600k", PlayMethod.DirectPlay)]
|
||||
[InlineData("Tizen4-4K-5.1", "mkv-vp9-vorbis-vtt-2600k", PlayMethod.DirectPlay)]
|
||||
public async Task BuildVideoItemSimple(string deviceName, string mediaSource, PlayMethod? playMethod, TranscodeReason why = (TranscodeReason)0, string transcodeMode = "DirectStream", string transcodeProtocol = "")
|
||||
{
|
||||
var options = await GetVideoOptions(deviceName, mediaSource);
|
||||
@@ -198,6 +229,37 @@ namespace Jellyfin.Model.Tests
|
||||
[InlineData("JellyfinMediaPlayer", "mkv-vp9-aac-srt-2600k", PlayMethod.DirectPlay)] // #6450
|
||||
[InlineData("JellyfinMediaPlayer", "mkv-vp9-ac3-srt-2600k", PlayMethod.DirectPlay)] // #6450
|
||||
[InlineData("JellyfinMediaPlayer", "mkv-vp9-vorbis-vtt-2600k", PlayMethod.DirectPlay)] // #6450
|
||||
// AndroidTV
|
||||
[InlineData("AndroidTVExoPlayer", "mp4-h264-aac-vtt-2600k", PlayMethod.DirectPlay)]
|
||||
[InlineData("AndroidTVExoPlayer", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.DirectPlay)]
|
||||
[InlineData("AndroidTVExoPlayer", "mp4-h264-ac3-srt-2600k", PlayMethod.DirectPlay)]
|
||||
[InlineData("AndroidTVExoPlayer", "mp4-hevc-aac-srt-15200k", PlayMethod.DirectPlay)]
|
||||
[InlineData("AndroidTVExoPlayer", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.DirectPlay)]
|
||||
[InlineData("AndroidTVExoPlayer", "mkv-vp9-aac-srt-2600k", PlayMethod.DirectPlay)]
|
||||
[InlineData("AndroidTVExoPlayer", "mkv-vp9-ac3-srt-2600k", PlayMethod.DirectPlay)]
|
||||
[InlineData("AndroidTVExoPlayer", "mkv-vp9-vorbis-vtt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)]
|
||||
// Tizen 3 Stereo
|
||||
[InlineData("Tizen3-stereo", "mp4-h264-aac-vtt-2600k", PlayMethod.DirectPlay)]
|
||||
[InlineData("Tizen3-stereo", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.DirectPlay)]
|
||||
[InlineData("Tizen3-stereo", "mp4-h264-ac3-srt-2600k", PlayMethod.DirectPlay)]
|
||||
[InlineData("Tizen3-stereo", "mp4-h264-dts-srt-2600k", PlayMethod.DirectPlay)]
|
||||
[InlineData("Tizen3-stereo", "mp4-hevc-aac-srt-15200k", PlayMethod.DirectPlay)]
|
||||
[InlineData("Tizen3-stereo", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.DirectPlay)]
|
||||
[InlineData("Tizen3-stereo", "mp4-hevc-truehd-srt-15200k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)]
|
||||
[InlineData("Tizen3-stereo", "mkv-vp9-aac-srt-2600k", PlayMethod.DirectPlay)]
|
||||
[InlineData("Tizen3-stereo", "mkv-vp9-ac3-srt-2600k", PlayMethod.DirectPlay)]
|
||||
[InlineData("Tizen3-stereo", "mkv-vp9-vorbis-vtt-2600k", PlayMethod.DirectPlay)]
|
||||
// Tizen 4 4K 5.1
|
||||
[InlineData("Tizen4-4K-5.1", "mp4-h264-aac-vtt-2600k", PlayMethod.DirectPlay)]
|
||||
[InlineData("Tizen4-4K-5.1", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.DirectPlay)]
|
||||
[InlineData("Tizen4-4K-5.1", "mp4-h264-ac3-srt-2600k", PlayMethod.DirectPlay)]
|
||||
[InlineData("Tizen4-4K-5.1", "mp4-h264-dts-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)]
|
||||
[InlineData("Tizen4-4K-5.1", "mp4-hevc-aac-srt-15200k", PlayMethod.DirectPlay)]
|
||||
[InlineData("Tizen4-4K-5.1", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.DirectPlay)]
|
||||
[InlineData("Tizen4-4K-5.1", "mp4-hevc-truehd-srt-15200k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)]
|
||||
[InlineData("Tizen4-4K-5.1", "mkv-vp9-aac-srt-2600k", PlayMethod.DirectPlay)]
|
||||
[InlineData("Tizen4-4K-5.1", "mkv-vp9-ac3-srt-2600k", PlayMethod.DirectPlay)]
|
||||
[InlineData("Tizen4-4K-5.1", "mkv-vp9-vorbis-vtt-2600k", PlayMethod.DirectPlay)]
|
||||
public async Task BuildVideoItemWithFirstExplicitStream(string deviceName, string mediaSource, PlayMethod? playMethod, TranscodeReason why = (TranscodeReason)0, string transcodeMode = "DirectStream", string transcodeProtocol = "")
|
||||
{
|
||||
var options = await GetVideoOptions(deviceName, mediaSource);
|
||||
@@ -223,12 +285,26 @@ namespace Jellyfin.Model.Tests
|
||||
// RokuSSPlus
|
||||
[InlineData("RokuSSPlus", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.DirectPlay, (TranscodeReason)0, "Remux")] // #6450
|
||||
[InlineData("RokuSSPlus", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.DirectPlay, (TranscodeReason)0, "Remux")] // #6450
|
||||
// no streams
|
||||
[InlineData("Chrome", "no-streams", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported, "Transcode")] // #6450
|
||||
// AndroidTV
|
||||
[InlineData("AndroidTVExoPlayer", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.DirectPlay, (TranscodeReason)0, "Remux")]
|
||||
[InlineData("AndroidTVExoPlayer", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.DirectPlay, (TranscodeReason)0, "Remux")]
|
||||
// Tizen 3 Stereo
|
||||
[InlineData("Tizen3-stereo", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.DirectPlay, (TranscodeReason)0, "Remux")]
|
||||
[InlineData("Tizen3-stereo", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.DirectPlay, (TranscodeReason)0, "Remux")]
|
||||
// Tizen 4 4K 5.1
|
||||
[InlineData("Tizen4-4K-5.1", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.DirectPlay, (TranscodeReason)0, "Remux")]
|
||||
[InlineData("Tizen4-4K-5.1", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.DirectPlay, (TranscodeReason)0, "Remux")]
|
||||
public async Task BuildVideoItemWithDirectPlayExplicitStreams(string deviceName, string mediaSource, PlayMethod? playMethod, TranscodeReason why = (TranscodeReason)0, string transcodeMode = "DirectStream", string transcodeProtocol = "")
|
||||
{
|
||||
var options = await GetVideoOptions(deviceName, mediaSource);
|
||||
var streamCount = options.MediaSources[0].MediaStreams.Count;
|
||||
options.AudioStreamIndex = streamCount - 2;
|
||||
options.SubtitleStreamIndex = streamCount - 1;
|
||||
if (streamCount > 0)
|
||||
{
|
||||
options.AudioStreamIndex = streamCount - 2;
|
||||
options.SubtitleStreamIndex = streamCount - 1;
|
||||
}
|
||||
|
||||
var streamInfo = BuildVideoItemSimpleTest(options, playMethod, why, transcodeMode, transcodeProtocol);
|
||||
Assert.Equal(streamInfo?.AudioStreamIndex, options.AudioStreamIndex);
|
||||
|
||||
@@ -0,0 +1,216 @@
|
||||
{
|
||||
"Name": "Jellyfin AndroidTV-ExoPlayer",
|
||||
"EnableAlbumArtInDidl": false,
|
||||
"EnableSingleAlbumArtLimit": false,
|
||||
"EnableSingleSubtitleLimit": false,
|
||||
"SupportedMediaTypes": "Audio,Photo,Video",
|
||||
"MaxAlbumArtWidth": 0,
|
||||
"MaxAlbumArtHeight": 0,
|
||||
"MaxStreamingBitrate": 120000000,
|
||||
"MaxStaticBitrate": 100000000,
|
||||
"MusicStreamingTranscodingBitrate": 192000,
|
||||
"TimelineOffsetSeconds": 0,
|
||||
"RequiresPlainVideoItems": false,
|
||||
"RequiresPlainFolders": false,
|
||||
"EnableMSMediaReceiverRegistrar": false,
|
||||
"IgnoreTranscodeByteRangeRequests": false,
|
||||
"DirectPlayProfiles": [
|
||||
{
|
||||
"Container": "m4v,mov,xvid,vob,mkv,wmv,asf,ogm,ogv,mp4,webm",
|
||||
"AudioCodec": "aac,mp3,mp2,aac_latm,alac,ac3,eac3,dca,dts,mlp,truehd,pcm_alaw,pcm_mulaw",
|
||||
"VideoCodec": "h264,hevc,vp8,vp9,mpeg,mpeg2video",
|
||||
"Type": "Video",
|
||||
"$type": "DirectPlayProfile"
|
||||
},
|
||||
{
|
||||
"Container": "aac,mp3,mp2,aac_latm,alac,ac3,eac3,dca,dts,mlp,truehd,pcm_alaw,pcm_mulaw,,pa,flac,wav,wma,ogg,oga,webma,ape,opus",
|
||||
"Type": "Audio",
|
||||
"$type": "DirectPlayProfile"
|
||||
},
|
||||
{
|
||||
"Container": "jpg,jpeg,png,gif,web",
|
||||
"Type": "Photo",
|
||||
"$type": "DirectPlayProfile"
|
||||
}
|
||||
],
|
||||
"CodecProfiles": [
|
||||
{
|
||||
"Type": "Video",
|
||||
"Conditions": [
|
||||
{
|
||||
"Condition": "EqualsAny",
|
||||
"Property": "VideoProfile",
|
||||
"Value": "high|main|baseline|constrained baseline",
|
||||
"IsRequired": false,
|
||||
"$type": "ProfileCondition"
|
||||
},
|
||||
{
|
||||
"Condition": "LessThanEqual",
|
||||
"Property": "VideoLevel",
|
||||
"Value": "51",
|
||||
"IsRequired": false,
|
||||
"$type": "ProfileCondition"
|
||||
}
|
||||
],
|
||||
"Codec": "h264",
|
||||
"$type": "CodecProfile"
|
||||
},
|
||||
{
|
||||
"Type": "Video",
|
||||
"Conditions": [
|
||||
{
|
||||
"Condition": "LessThanEqual",
|
||||
"Property": "RefFrames",
|
||||
"Value": "12",
|
||||
"IsRequired": false,
|
||||
"$type": "ProfileCondition"
|
||||
}
|
||||
],
|
||||
"ApplyConditions": [
|
||||
{
|
||||
"Condition": "GreaterThanEqual",
|
||||
"Property": "Width",
|
||||
"Value": "1200",
|
||||
"IsRequired": false,
|
||||
"$type": "ProfileCondition"
|
||||
}
|
||||
],
|
||||
"Codec": "h264",
|
||||
"$type": "CodecProfile"
|
||||
},
|
||||
{
|
||||
"Type": "Video",
|
||||
"Conditions": [
|
||||
{
|
||||
"Condition": "LessThanEqual",
|
||||
"Property": "RefFrames",
|
||||
"Value": "4",
|
||||
"IsRequired": false,
|
||||
"$type": "ProfileCondition"
|
||||
}
|
||||
],
|
||||
"ApplyConditions": [
|
||||
{
|
||||
"Condition": "GreaterThanEqual",
|
||||
"Property": "Width",
|
||||
"Value": "1900",
|
||||
"IsRequired": false,
|
||||
"$type": "ProfileCondition"
|
||||
}
|
||||
],
|
||||
"Codec": "h264",
|
||||
"$type": "CodecProfile"
|
||||
},
|
||||
{
|
||||
"Type": "VideoAudio",
|
||||
"Conditions": [
|
||||
{
|
||||
"Condition": "LessThanEqual",
|
||||
"Property": "AudioChannels",
|
||||
"Value": "6",
|
||||
"IsRequired": false,
|
||||
"$type": "ProfileCondition"
|
||||
}
|
||||
],
|
||||
"$type": "CodecProfile"
|
||||
}
|
||||
],
|
||||
"TranscodingProfiles": [
|
||||
{
|
||||
"Container": "ts",
|
||||
"Type": "Video",
|
||||
"VideoCodec": "h264",
|
||||
"AudioCodec": "aac,mp3",
|
||||
"Protocol": "hls",
|
||||
"EstimateContentLength": false,
|
||||
"EnableMpegtsM2TsMode": false,
|
||||
"TranscodeSeekInfo": "Auto",
|
||||
"CopyTimestamps": false,
|
||||
"Context": "Streaming",
|
||||
"EnableSubtitlesInManifest": false,
|
||||
"MinSegments": 0,
|
||||
"SegmentLength": 0,
|
||||
"BreakOnNonKeyFrames": false,
|
||||
"$type": "TranscodingProfile"
|
||||
},
|
||||
{
|
||||
"Container": "mp3",
|
||||
"Type": "Audio",
|
||||
"AudioCodec": "mp3",
|
||||
"Protocol": "http",
|
||||
"EstimateContentLength": false,
|
||||
"EnableMpegtsM2TsMode": false,
|
||||
"TranscodeSeekInfo": "Auto",
|
||||
"CopyTimestamps": false,
|
||||
"Context": "Streaming",
|
||||
"EnableSubtitlesInManifest": false,
|
||||
"MinSegments": 0,
|
||||
"SegmentLength": 0,
|
||||
"BreakOnNonKeyFrames": false,
|
||||
"$type": "TranscodingProfile"
|
||||
}
|
||||
],
|
||||
"SubtitleProfiles": [
|
||||
{
|
||||
"Format": "srt",
|
||||
"Method": "Embed",
|
||||
"$type": "SubtitleProfile"
|
||||
},
|
||||
{
|
||||
"Format": "srt",
|
||||
"Method": "External",
|
||||
"$type": "SubtitleProfile"
|
||||
},
|
||||
{
|
||||
"Format": "subrip",
|
||||
"Method": "Embed",
|
||||
"$type": "SubtitleProfile"
|
||||
},
|
||||
{
|
||||
"Format": "subrip",
|
||||
"Method": "External",
|
||||
"$type": "SubtitleProfile"
|
||||
},
|
||||
{
|
||||
"Format": "ass",
|
||||
"Method": "Encode",
|
||||
"$type": "SubtitleProfile"
|
||||
},
|
||||
{
|
||||
"Format": "ssa",
|
||||
"Method": "Encode",
|
||||
"$type": "SubtitleProfile"
|
||||
},
|
||||
{
|
||||
"Format": "pgs",
|
||||
"Method": "Encode",
|
||||
"$type": "SubtitleProfile"
|
||||
},
|
||||
{
|
||||
"Format": "pgssub",
|
||||
"Method": "Encode",
|
||||
"$type": "SubtitleProfile"
|
||||
},
|
||||
{
|
||||
"Format": "dvdsub",
|
||||
"Method": "Encode",
|
||||
"$type": "SubtitleProfile"
|
||||
},
|
||||
{
|
||||
"Format": "vtt",
|
||||
"Method": "Embed",
|
||||
"$type": "SubtitleProfile"
|
||||
},
|
||||
{
|
||||
"Format": "sub",
|
||||
"Method": "Embed",
|
||||
"$type": "SubtitleProfile"
|
||||
},
|
||||
{
|
||||
"Format": "idx",
|
||||
"Method": "Embed",
|
||||
"$type": "SubtitleProfile"
|
||||
}
|
||||
],
|
||||
"$type": "DeviceProfile"
|
||||
}
|
||||
@@ -0,0 +1,526 @@
|
||||
{
|
||||
"Name": "Jellyfin Tizen 3 Stereo",
|
||||
"EnableAlbumArtInDidl": false,
|
||||
"EnableSingleAlbumArtLimit": false,
|
||||
"EnableSingleSubtitleLimit": false,
|
||||
"SupportedMediaTypes": "Audio,Photo,Video",
|
||||
"MaxAlbumArtWidth": 0,
|
||||
"MaxAlbumArtHeight": 0,
|
||||
"MaxStreamingBitrate": 120000000,
|
||||
"MaxStaticBitrate": 100000000,
|
||||
"MusicStreamingTranscodingBitrate": 384000,
|
||||
"TimelineOffsetSeconds": 0,
|
||||
"RequiresPlainVideoItems": false,
|
||||
"RequiresPlainFolders": false,
|
||||
"EnableMSMediaReceiverRegistrar": false,
|
||||
"IgnoreTranscodeByteRangeRequests": false,
|
||||
"DirectPlayProfiles": [
|
||||
{
|
||||
"Container": "webm",
|
||||
"AudioCodec": "vorbis,opus",
|
||||
"VideoCodec": "vp8,vp9",
|
||||
"Type": "Video",
|
||||
"$type": "DirectPlayProfile"
|
||||
},
|
||||
{
|
||||
"Container": "mp4,m4v",
|
||||
"AudioCodec": "aac,mp3,ac3,eac3,mp2,dca,dts,pcm_s16le,pcm_s24le,aac_latm,opus,flac,vorbis",
|
||||
"VideoCodec": "h264,hevc,mpeg2video,vc1,msmpeg4v2,vp8,vp9",
|
||||
"Type": "Video",
|
||||
"$type": "DirectPlayProfile"
|
||||
},
|
||||
{
|
||||
"Container": "mkv",
|
||||
"AudioCodec": "aac,mp3,ac3,eac3,mp2,dca,dts,pcm_s16le,pcm_s24le,aac_latm,opus,flac,vorbis",
|
||||
"VideoCodec": "h264,hevc,mpeg2video,vc1,msmpeg4v2,vp8,vp9",
|
||||
"Type": "Video",
|
||||
"$type": "DirectPlayProfile"
|
||||
},
|
||||
{
|
||||
"Container": "m2ts",
|
||||
"AudioCodec": "aac,mp3,ac3,eac3,mp2,dca,dts,pcm_s16le,pcm_s24le,aac_latm,opus,flac,vorbis",
|
||||
"VideoCodec": "h264,vc1,mpeg2video",
|
||||
"Type": "Video",
|
||||
"$type": "DirectPlayProfile"
|
||||
},
|
||||
{
|
||||
"Container": "wmv",
|
||||
"AudioCodec": "",
|
||||
"VideoCodec": "",
|
||||
"Type": "Video",
|
||||
"$type": "DirectPlayProfile"
|
||||
},
|
||||
{
|
||||
"Container": "ts,mpegts",
|
||||
"AudioCodec": "aac,mp3,ac3,eac3,mp2,dca,dts,pcm_s16le,pcm_s24le,aac_latm,opus,flac,vorbis",
|
||||
"VideoCodec": "h264,hevc,vc1,mpeg2video",
|
||||
"Type": "Video",
|
||||
"$type": "DirectPlayProfile"
|
||||
},
|
||||
{
|
||||
"Container": "asf",
|
||||
"AudioCodec": "",
|
||||
"VideoCodec": "",
|
||||
"Type": "Video",
|
||||
"$type": "DirectPlayProfile"
|
||||
},
|
||||
{
|
||||
"Container": "avi",
|
||||
"AudioCodec": "aac,mp3,ac3,eac3,mp2,dca,dts,pcm_s16le,pcm_s24le,aac_latm,opus,flac,vorbis",
|
||||
"VideoCodec": "",
|
||||
"Type": "Video",
|
||||
"$type": "DirectPlayProfile"
|
||||
},
|
||||
{
|
||||
"Container": "mpg,mpeg,flv,3gp,mts,trp,vob,vro",
|
||||
"AudioCodec": "aac,mp3,ac3,eac3,mp2,dca,dts,pcm_s16le,pcm_s24le,aac_latm,opus,flac,vorbis",
|
||||
"VideoCodec": "",
|
||||
"Type": "Video",
|
||||
"$type": "DirectPlayProfile"
|
||||
},
|
||||
{
|
||||
"Container": "mov",
|
||||
"AudioCodec": "aac,mp3,ac3,eac3,mp2,dca,dts,pcm_s16le,pcm_s24le,aac_latm,opus,flac,vorbis",
|
||||
"VideoCodec": "h264",
|
||||
"Type": "Video",
|
||||
"$type": "DirectPlayProfile"
|
||||
},
|
||||
{
|
||||
"Container": "opus",
|
||||
"Type": "Audio",
|
||||
"$type": "DirectPlayProfile"
|
||||
},
|
||||
{
|
||||
"Container": "webm",
|
||||
"AudioCodec": "opus",
|
||||
"Type": "Audio",
|
||||
"$type": "DirectPlayProfile"
|
||||
},
|
||||
{
|
||||
"Container": "mp3",
|
||||
"Type": "Audio",
|
||||
"$type": "DirectPlayProfile"
|
||||
},
|
||||
{
|
||||
"Container": "aac",
|
||||
"Type": "Audio",
|
||||
"$type": "DirectPlayProfile"
|
||||
},
|
||||
{
|
||||
"Container": "m4a",
|
||||
"AudioCodec": "aac",
|
||||
"Type": "Audio",
|
||||
"$type": "DirectPlayProfile"
|
||||
},
|
||||
{
|
||||
"Container": "m4b",
|
||||
"AudioCodec": "aac",
|
||||
"Type": "Audio",
|
||||
"$type": "DirectPlayProfile"
|
||||
},
|
||||
{
|
||||
"Container": "flac",
|
||||
"Type": "Audio",
|
||||
"$type": "DirectPlayProfile"
|
||||
},
|
||||
{
|
||||
"Container": "webma",
|
||||
"Type": "Audio",
|
||||
"$type": "DirectPlayProfile"
|
||||
},
|
||||
{
|
||||
"Container": "webm",
|
||||
"AudioCodec": "webma",
|
||||
"Type": "Audio",
|
||||
"$type": "DirectPlayProfile"
|
||||
},
|
||||
{
|
||||
"Container": "wma",
|
||||
"Type": "Audio",
|
||||
"$type": "DirectPlayProfile"
|
||||
},
|
||||
{
|
||||
"Container": "wav",
|
||||
"Type": "Audio",
|
||||
"$type": "DirectPlayProfile"
|
||||
},
|
||||
{
|
||||
"Container": "ogg",
|
||||
"Type": "Audio",
|
||||
"$type": "DirectPlayProfile"
|
||||
}
|
||||
],
|
||||
"TranscodingProfiles": [
|
||||
{
|
||||
"Container": "aac",
|
||||
"Type": "Audio",
|
||||
"AudioCodec": "aac",
|
||||
"Protocol": "hls",
|
||||
"EstimateContentLength": false,
|
||||
"EnableMpegtsM2TsMode": false,
|
||||
"TranscodeSeekInfo": "Auto",
|
||||
"CopyTimestamps": false,
|
||||
"Context": "Streaming",
|
||||
"EnableSubtitlesInManifest": false,
|
||||
"MaxAudioChannels": "2",
|
||||
"MinSegments": 1,
|
||||
"SegmentLength": 0,
|
||||
"BreakOnNonKeyFrames": true,
|
||||
"$type": "TranscodingProfile"
|
||||
},
|
||||
{
|
||||
"Container": "aac",
|
||||
"Type": "Audio",
|
||||
"AudioCodec": "aac",
|
||||
"Protocol": "http",
|
||||
"EstimateContentLength": false,
|
||||
"EnableMpegtsM2TsMode": false,
|
||||
"TranscodeSeekInfo": "Auto",
|
||||
"CopyTimestamps": false,
|
||||
"Context": "Streaming",
|
||||
"EnableSubtitlesInManifest": false,
|
||||
"MaxAudioChannels": "2",
|
||||
"MinSegments": 0,
|
||||
"SegmentLength": 0,
|
||||
"BreakOnNonKeyFrames": false,
|
||||
"$type": "TranscodingProfile"
|
||||
},
|
||||
{
|
||||
"Container": "mp3",
|
||||
"Type": "Audio",
|
||||
"AudioCodec": "mp3",
|
||||
"Protocol": "http",
|
||||
"EstimateContentLength": false,
|
||||
"EnableMpegtsM2TsMode": false,
|
||||
"TranscodeSeekInfo": "Auto",
|
||||
"CopyTimestamps": false,
|
||||
"Context": "Streaming",
|
||||
"EnableSubtitlesInManifest": false,
|
||||
"MaxAudioChannels": "2",
|
||||
"MinSegments": 0,
|
||||
"SegmentLength": 0,
|
||||
"BreakOnNonKeyFrames": false,
|
||||
"$type": "TranscodingProfile"
|
||||
},
|
||||
{
|
||||
"Container": "opus",
|
||||
"Type": "Audio",
|
||||
"AudioCodec": "opus",
|
||||
"Protocol": "http",
|
||||
"EstimateContentLength": false,
|
||||
"EnableMpegtsM2TsMode": false,
|
||||
"TranscodeSeekInfo": "Auto",
|
||||
"CopyTimestamps": false,
|
||||
"Context": "Streaming",
|
||||
"EnableSubtitlesInManifest": false,
|
||||
"MaxAudioChannels": "2",
|
||||
"MinSegments": 0,
|
||||
"SegmentLength": 0,
|
||||
"BreakOnNonKeyFrames": false,
|
||||
"$type": "TranscodingProfile"
|
||||
},
|
||||
{
|
||||
"Container": "wav",
|
||||
"Type": "Audio",
|
||||
"AudioCodec": "wav",
|
||||
"Protocol": "http",
|
||||
"EstimateContentLength": false,
|
||||
"EnableMpegtsM2TsMode": false,
|
||||
"TranscodeSeekInfo": "Auto",
|
||||
"CopyTimestamps": false,
|
||||
"Context": "Streaming",
|
||||
"EnableSubtitlesInManifest": false,
|
||||
"MaxAudioChannels": "2",
|
||||
"MinSegments": 0,
|
||||
"SegmentLength": 0,
|
||||
"BreakOnNonKeyFrames": false,
|
||||
"$type": "TranscodingProfile"
|
||||
},
|
||||
{
|
||||
"Container": "opus",
|
||||
"Type": "Audio",
|
||||
"AudioCodec": "opus",
|
||||
"Protocol": "http",
|
||||
"EstimateContentLength": false,
|
||||
"EnableMpegtsM2TsMode": false,
|
||||
"TranscodeSeekInfo": "Auto",
|
||||
"CopyTimestamps": false,
|
||||
"Context": "Static",
|
||||
"EnableSubtitlesInManifest": false,
|
||||
"MaxAudioChannels": "2",
|
||||
"MinSegments": 0,
|
||||
"SegmentLength": 0,
|
||||
"BreakOnNonKeyFrames": false,
|
||||
"$type": "TranscodingProfile"
|
||||
},
|
||||
{
|
||||
"Container": "mp3",
|
||||
"Type": "Audio",
|
||||
"AudioCodec": "mp3",
|
||||
"Protocol": "http",
|
||||
"EstimateContentLength": false,
|
||||
"EnableMpegtsM2TsMode": false,
|
||||
"TranscodeSeekInfo": "Auto",
|
||||
"CopyTimestamps": false,
|
||||
"Context": "Static",
|
||||
"EnableSubtitlesInManifest": false,
|
||||
"MaxAudioChannels": "2",
|
||||
"MinSegments": 0,
|
||||
"SegmentLength": 0,
|
||||
"BreakOnNonKeyFrames": false,
|
||||
"$type": "TranscodingProfile"
|
||||
},
|
||||
{
|
||||
"Container": "aac",
|
||||
"Type": "Audio",
|
||||
"AudioCodec": "aac",
|
||||
"Protocol": "http",
|
||||
"EstimateContentLength": false,
|
||||
"EnableMpegtsM2TsMode": false,
|
||||
"TranscodeSeekInfo": "Auto",
|
||||
"CopyTimestamps": false,
|
||||
"Context": "Static",
|
||||
"EnableSubtitlesInManifest": false,
|
||||
"MaxAudioChannels": "2",
|
||||
"MinSegments": 0,
|
||||
"SegmentLength": 0,
|
||||
"BreakOnNonKeyFrames": false,
|
||||
"$type": "TranscodingProfile"
|
||||
},
|
||||
{
|
||||
"Container": "wav",
|
||||
"Type": "Audio",
|
||||
"AudioCodec": "wav",
|
||||
"Protocol": "http",
|
||||
"EstimateContentLength": false,
|
||||
"EnableMpegtsM2TsMode": false,
|
||||
"TranscodeSeekInfo": "Auto",
|
||||
"CopyTimestamps": false,
|
||||
"Context": "Static",
|
||||
"EnableSubtitlesInManifest": false,
|
||||
"MaxAudioChannels": "2",
|
||||
"MinSegments": 0,
|
||||
"SegmentLength": 0,
|
||||
"BreakOnNonKeyFrames": false,
|
||||
"$type": "TranscodingProfile"
|
||||
},
|
||||
{
|
||||
"Container": "mkv",
|
||||
"Type": "Video",
|
||||
"VideoCodec": "h264,hevc,mpeg2video,vc1,msmpeg4v2,vp8,vp9",
|
||||
"AudioCodec": "aac,mp3,ac3,eac3,mp2,dca,dts,pcm_s16le,pcm_s24le,aac_latm,opus,flac,vorbis",
|
||||
"Protocol": "",
|
||||
"EstimateContentLength": false,
|
||||
"EnableMpegtsM2TsMode": false,
|
||||
"TranscodeSeekInfo": "Auto",
|
||||
"CopyTimestamps": true,
|
||||
"Context": "Static",
|
||||
"EnableSubtitlesInManifest": false,
|
||||
"MaxAudioChannels": "2",
|
||||
"MinSegments": 0,
|
||||
"SegmentLength": 0,
|
||||
"BreakOnNonKeyFrames": false,
|
||||
"Conditions": [
|
||||
{
|
||||
"Condition": "LessThanEqual",
|
||||
"Property": "Width",
|
||||
"Value": "1920",
|
||||
"IsRequired": false,
|
||||
"$type": "ProfileCondition"
|
||||
}
|
||||
],
|
||||
"$type": "TranscodingProfile"
|
||||
},
|
||||
{
|
||||
"Container": "ts",
|
||||
"Type": "Video",
|
||||
"VideoCodec": "h264,hevc",
|
||||
"AudioCodec": "aac,mp3,ac3,eac3,opus",
|
||||
"Protocol": "hls",
|
||||
"EstimateContentLength": false,
|
||||
"EnableMpegtsM2TsMode": false,
|
||||
"TranscodeSeekInfo": "Auto",
|
||||
"CopyTimestamps": false,
|
||||
"Context": "Streaming",
|
||||
"EnableSubtitlesInManifest": false,
|
||||
"MaxAudioChannels": "2",
|
||||
"MinSegments": 1,
|
||||
"SegmentLength": 0,
|
||||
"BreakOnNonKeyFrames": false,
|
||||
"Conditions": [
|
||||
{
|
||||
"Condition": "LessThanEqual",
|
||||
"Property": "Width",
|
||||
"Value": "1920",
|
||||
"IsRequired": false,
|
||||
"$type": "ProfileCondition"
|
||||
}
|
||||
],
|
||||
"$type": "TranscodingProfile"
|
||||
},
|
||||
{
|
||||
"Container": "webm",
|
||||
"Type": "Video",
|
||||
"VideoCodec": "vp8,vp9,vpx",
|
||||
"AudioCodec": "vorbis,opus",
|
||||
"Protocol": "http",
|
||||
"EstimateContentLength": false,
|
||||
"EnableMpegtsM2TsMode": false,
|
||||
"TranscodeSeekInfo": "Auto",
|
||||
"CopyTimestamps": false,
|
||||
"Context": "Streaming",
|
||||
"EnableSubtitlesInManifest": false,
|
||||
"MaxAudioChannels": "2",
|
||||
"MinSegments": 0,
|
||||
"SegmentLength": 0,
|
||||
"BreakOnNonKeyFrames": false,
|
||||
"Conditions": [
|
||||
{
|
||||
"Condition": "LessThanEqual",
|
||||
"Property": "Width",
|
||||
"Value": "1920",
|
||||
"IsRequired": false,
|
||||
"$type": "ProfileCondition"
|
||||
}
|
||||
],
|
||||
"$type": "TranscodingProfile"
|
||||
},
|
||||
{
|
||||
"Container": "mp4",
|
||||
"Type": "Video",
|
||||
"VideoCodec": "h264",
|
||||
"AudioCodec": "aac,mp3,ac3,eac3,mp2,dca,dts,pcm_s16le,pcm_s24le,aac_latm,opus,flac,vorbis",
|
||||
"Protocol": "http",
|
||||
"EstimateContentLength": false,
|
||||
"EnableMpegtsM2TsMode": false,
|
||||
"TranscodeSeekInfo": "Auto",
|
||||
"CopyTimestamps": false,
|
||||
"Context": "Static",
|
||||
"EnableSubtitlesInManifest": false,
|
||||
"MinSegments": 0,
|
||||
"SegmentLength": 0,
|
||||
"BreakOnNonKeyFrames": false,
|
||||
"Conditions": [
|
||||
{
|
||||
"Condition": "LessThanEqual",
|
||||
"Property": "Width",
|
||||
"Value": "1920",
|
||||
"IsRequired": false,
|
||||
"$type": "ProfileCondition"
|
||||
}
|
||||
],
|
||||
"$type": "TranscodingProfile"
|
||||
}
|
||||
],
|
||||
"CodecProfiles": [
|
||||
{
|
||||
"Type": "Video",
|
||||
"Conditions": [
|
||||
{
|
||||
"Condition": "NotEquals",
|
||||
"Property": "IsAnamorphic",
|
||||
"Value": "true",
|
||||
"IsRequired": false,
|
||||
"$type": "ProfileCondition"
|
||||
},
|
||||
{
|
||||
"Condition": "EqualsAny",
|
||||
"Property": "VideoProfile",
|
||||
"Value": "high|main|baseline|constrained baseline|high 10",
|
||||
"IsRequired": false,
|
||||
"$type": "ProfileCondition"
|
||||
},
|
||||
{
|
||||
"Condition": "LessThanEqual",
|
||||
"Property": "VideoLevel",
|
||||
"Value": "52",
|
||||
"IsRequired": false,
|
||||
"$type": "ProfileCondition"
|
||||
},
|
||||
{
|
||||
"Condition": "LessThanEqual",
|
||||
"Property": "VideoBitrate",
|
||||
"Value": "20000000",
|
||||
"IsRequired": false,
|
||||
"$type": "ProfileCondition"
|
||||
}
|
||||
],
|
||||
"Codec": "h264",
|
||||
"$type": "CodecProfile"
|
||||
},
|
||||
{
|
||||
"Type": "Video",
|
||||
"Conditions": [
|
||||
{
|
||||
"Condition": "NotEquals",
|
||||
"Property": "IsAnamorphic",
|
||||
"Value": "true",
|
||||
"IsRequired": false,
|
||||
"$type": "ProfileCondition"
|
||||
},
|
||||
{
|
||||
"Condition": "EqualsAny",
|
||||
"Property": "VideoProfile",
|
||||
"Value": "main|main 10",
|
||||
"IsRequired": false,
|
||||
"$type": "ProfileCondition"
|
||||
},
|
||||
{
|
||||
"Condition": "LessThanEqual",
|
||||
"Property": "VideoLevel",
|
||||
"Value": "183",
|
||||
"IsRequired": false,
|
||||
"$type": "ProfileCondition"
|
||||
},
|
||||
{
|
||||
"Condition": "LessThanEqual",
|
||||
"Property": "VideoBitrate",
|
||||
"Value": "20000000",
|
||||
"IsRequired": false,
|
||||
"$type": "ProfileCondition"
|
||||
}
|
||||
],
|
||||
"Codec": "hevc",
|
||||
"$type": "CodecProfile"
|
||||
},
|
||||
{
|
||||
"Type": "Video",
|
||||
"Conditions": [
|
||||
{
|
||||
"Condition": "LessThanEqual",
|
||||
"Property": "VideoBitrate",
|
||||
"Value": "20000000",
|
||||
"IsRequired": false,
|
||||
"$type": "ProfileCondition"
|
||||
}
|
||||
],
|
||||
"$type": "CodecProfile"
|
||||
}
|
||||
],
|
||||
"ResponseProfiles": [
|
||||
{
|
||||
"Container": "m4v",
|
||||
"Type": "Video",
|
||||
"MimeType": "video/mp4",
|
||||
"$type": "ResponseProfile"
|
||||
}
|
||||
],
|
||||
"SubtitleProfiles": [
|
||||
{
|
||||
"Format": "vtt",
|
||||
"Method": "External",
|
||||
"$type": "SubtitleProfile"
|
||||
},
|
||||
{
|
||||
"Format": "ass",
|
||||
"Method": "External",
|
||||
"$type": "SubtitleProfile"
|
||||
},
|
||||
{
|
||||
"Format": "ssa",
|
||||
"Method": "External",
|
||||
"$type": "SubtitleProfile"
|
||||
}
|
||||
],
|
||||
"$type": "DeviceProfile"
|
||||
}
|
||||
@@ -0,0 +1,499 @@
|
||||
{
|
||||
"Name": "Jellyfin Tizen 4 4K 5.1",
|
||||
"EnableAlbumArtInDidl": false,
|
||||
"EnableSingleAlbumArtLimit": false,
|
||||
"EnableSingleSubtitleLimit": false,
|
||||
"SupportedMediaTypes": "Audio,Photo,Video",
|
||||
"MaxAlbumArtWidth": 0,
|
||||
"MaxAlbumArtHeight": 0,
|
||||
"MaxStreamingBitrate": 120000000,
|
||||
"MaxStaticBitrate": 100000000,
|
||||
"MusicStreamingTranscodingBitrate": 384000,
|
||||
"TimelineOffsetSeconds": 0,
|
||||
"RequiresPlainVideoItems": false,
|
||||
"RequiresPlainFolders": false,
|
||||
"EnableMSMediaReceiverRegistrar": false,
|
||||
"IgnoreTranscodeByteRangeRequests": false,
|
||||
"DirectPlayProfiles": [
|
||||
{
|
||||
"Container": "webm",
|
||||
"AudioCodec": "vorbis,opus",
|
||||
"VideoCodec": "vp8,vp9",
|
||||
"Type": "Video",
|
||||
"$type": "DirectPlayProfile"
|
||||
},
|
||||
{
|
||||
"Container": "mp4,m4v",
|
||||
"AudioCodec": "aac,mp3,ac3,eac3,mp2,pcm_s16le,pcm_s24le,aac_latm,opus,flac,vorbis",
|
||||
"VideoCodec": "h264,hevc,mpeg2video,vc1,msmpeg4v2,vp8,vp9",
|
||||
"Type": "Video",
|
||||
"$type": "DirectPlayProfile"
|
||||
},
|
||||
{
|
||||
"Container": "mkv",
|
||||
"AudioCodec": "aac,mp3,ac3,eac3,mp2,pcm_s16le,pcm_s24le,aac_latm,opus,flac,vorbis",
|
||||
"VideoCodec": "h264,hevc,mpeg2video,vc1,msmpeg4v2,vp8,vp9",
|
||||
"Type": "Video",
|
||||
"$type": "DirectPlayProfile"
|
||||
},
|
||||
{
|
||||
"Container": "m2ts",
|
||||
"AudioCodec": "aac,mp3,ac3,eac3,mp2,pcm_s16le,pcm_s24le,aac_latm,opus,flac,vorbis",
|
||||
"VideoCodec": "h264,vc1,mpeg2video",
|
||||
"Type": "Video",
|
||||
"$type": "DirectPlayProfile"
|
||||
},
|
||||
{
|
||||
"Container": "wmv",
|
||||
"AudioCodec": "",
|
||||
"VideoCodec": "",
|
||||
"Type": "Video",
|
||||
"$type": "DirectPlayProfile"
|
||||
},
|
||||
{
|
||||
"Container": "ts,mpegts",
|
||||
"AudioCodec": "aac,mp3,ac3,eac3,mp2,pcm_s16le,pcm_s24le,aac_latm,opus,flac,vorbis",
|
||||
"VideoCodec": "h264,hevc,vc1,mpeg2video",
|
||||
"Type": "Video",
|
||||
"$type": "DirectPlayProfile"
|
||||
},
|
||||
{
|
||||
"Container": "asf",
|
||||
"AudioCodec": "",
|
||||
"VideoCodec": "",
|
||||
"Type": "Video",
|
||||
"$type": "DirectPlayProfile"
|
||||
},
|
||||
{
|
||||
"Container": "avi",
|
||||
"AudioCodec": "aac,mp3,ac3,eac3,mp2,pcm_s16le,pcm_s24le,aac_latm,opus,flac,vorbis",
|
||||
"VideoCodec": "h264,hevc",
|
||||
"Type": "Video",
|
||||
"$type": "DirectPlayProfile"
|
||||
},
|
||||
{
|
||||
"Container": "mpg,mpeg,flv,3gp,mts,trp,vob,vro",
|
||||
"AudioCodec": "aac,mp3,ac3,eac3,mp2,pcm_s16le,pcm_s24le,aac_latm,opus,flac,vorbis",
|
||||
"VideoCodec": "",
|
||||
"Type": "Video",
|
||||
"$type": "DirectPlayProfile"
|
||||
},
|
||||
{
|
||||
"Container": "mov",
|
||||
"AudioCodec": "aac,mp3,ac3,eac3,mp2,pcm_s16le,pcm_s24le,aac_latm,opus,flac,vorbis",
|
||||
"VideoCodec": "h264",
|
||||
"Type": "Video",
|
||||
"$type": "DirectPlayProfile"
|
||||
},
|
||||
{
|
||||
"Container": "opus",
|
||||
"Type": "Audio",
|
||||
"$type": "DirectPlayProfile"
|
||||
},
|
||||
{
|
||||
"Container": "webm",
|
||||
"AudioCodec": "opus",
|
||||
"Type": "Audio",
|
||||
"$type": "DirectPlayProfile"
|
||||
},
|
||||
{
|
||||
"Container": "mp3",
|
||||
"Type": "Audio",
|
||||
"$type": "DirectPlayProfile"
|
||||
},
|
||||
{
|
||||
"Container": "aac",
|
||||
"Type": "Audio",
|
||||
"$type": "DirectPlayProfile"
|
||||
},
|
||||
{
|
||||
"Container": "m4a",
|
||||
"AudioCodec": "aac",
|
||||
"Type": "Audio",
|
||||
"$type": "DirectPlayProfile"
|
||||
},
|
||||
{
|
||||
"Container": "m4b",
|
||||
"AudioCodec": "aac",
|
||||
"Type": "Audio",
|
||||
"$type": "DirectPlayProfile"
|
||||
},
|
||||
{
|
||||
"Container": "flac",
|
||||
"Type": "Audio",
|
||||
"$type": "DirectPlayProfile"
|
||||
},
|
||||
{
|
||||
"Container": "webma",
|
||||
"Type": "Audio",
|
||||
"$type": "DirectPlayProfile"
|
||||
},
|
||||
{
|
||||
"Container": "webm",
|
||||
"AudioCodec": "webma",
|
||||
"Type": "Audio",
|
||||
"$type": "DirectPlayProfile"
|
||||
},
|
||||
{
|
||||
"Container": "wma",
|
||||
"Type": "Audio",
|
||||
"$type": "DirectPlayProfile"
|
||||
},
|
||||
{
|
||||
"Container": "wav",
|
||||
"Type": "Audio",
|
||||
"$type": "DirectPlayProfile"
|
||||
},
|
||||
{
|
||||
"Container": "ogg",
|
||||
"Type": "Audio",
|
||||
"$type": "DirectPlayProfile"
|
||||
}
|
||||
],
|
||||
"TranscodingProfiles": [
|
||||
{
|
||||
"Container": "aac",
|
||||
"Type": "Audio",
|
||||
"AudioCodec": "aac",
|
||||
"Protocol": "hls",
|
||||
"EstimateContentLength": false,
|
||||
"EnableMpegtsM2TsMode": false,
|
||||
"TranscodeSeekInfo": "Auto",
|
||||
"CopyTimestamps": false,
|
||||
"Context": "Streaming",
|
||||
"EnableSubtitlesInManifest": false,
|
||||
"MaxAudioChannels": "6",
|
||||
"MinSegments": 1,
|
||||
"SegmentLength": 0,
|
||||
"BreakOnNonKeyFrames": true,
|
||||
"$type": "TranscodingProfile"
|
||||
},
|
||||
{
|
||||
"Container": "aac",
|
||||
"Type": "Audio",
|
||||
"AudioCodec": "aac",
|
||||
"Protocol": "http",
|
||||
"EstimateContentLength": false,
|
||||
"EnableMpegtsM2TsMode": false,
|
||||
"TranscodeSeekInfo": "Auto",
|
||||
"CopyTimestamps": false,
|
||||
"Context": "Streaming",
|
||||
"EnableSubtitlesInManifest": false,
|
||||
"MaxAudioChannels": "6",
|
||||
"MinSegments": 0,
|
||||
"SegmentLength": 0,
|
||||
"BreakOnNonKeyFrames": false,
|
||||
"$type": "TranscodingProfile"
|
||||
},
|
||||
{
|
||||
"Container": "mp3",
|
||||
"Type": "Audio",
|
||||
"AudioCodec": "mp3",
|
||||
"Protocol": "http",
|
||||
"EstimateContentLength": false,
|
||||
"EnableMpegtsM2TsMode": false,
|
||||
"TranscodeSeekInfo": "Auto",
|
||||
"CopyTimestamps": false,
|
||||
"Context": "Streaming",
|
||||
"EnableSubtitlesInManifest": false,
|
||||
"MaxAudioChannels": "6",
|
||||
"MinSegments": 0,
|
||||
"SegmentLength": 0,
|
||||
"BreakOnNonKeyFrames": false,
|
||||
"$type": "TranscodingProfile"
|
||||
},
|
||||
{
|
||||
"Container": "opus",
|
||||
"Type": "Audio",
|
||||
"AudioCodec": "opus",
|
||||
"Protocol": "http",
|
||||
"EstimateContentLength": false,
|
||||
"EnableMpegtsM2TsMode": false,
|
||||
"TranscodeSeekInfo": "Auto",
|
||||
"CopyTimestamps": false,
|
||||
"Context": "Streaming",
|
||||
"EnableSubtitlesInManifest": false,
|
||||
"MaxAudioChannels": "6",
|
||||
"MinSegments": 0,
|
||||
"SegmentLength": 0,
|
||||
"BreakOnNonKeyFrames": false,
|
||||
"$type": "TranscodingProfile"
|
||||
},
|
||||
{
|
||||
"Container": "wav",
|
||||
"Type": "Audio",
|
||||
"AudioCodec": "wav",
|
||||
"Protocol": "http",
|
||||
"EstimateContentLength": false,
|
||||
"EnableMpegtsM2TsMode": false,
|
||||
"TranscodeSeekInfo": "Auto",
|
||||
"CopyTimestamps": false,
|
||||
"Context": "Streaming",
|
||||
"EnableSubtitlesInManifest": false,
|
||||
"MaxAudioChannels": "6",
|
||||
"MinSegments": 0,
|
||||
"SegmentLength": 0,
|
||||
"BreakOnNonKeyFrames": false,
|
||||
"$type": "TranscodingProfile"
|
||||
},
|
||||
{
|
||||
"Container": "opus",
|
||||
"Type": "Audio",
|
||||
"AudioCodec": "opus",
|
||||
"Protocol": "http",
|
||||
"EstimateContentLength": false,
|
||||
"EnableMpegtsM2TsMode": false,
|
||||
"TranscodeSeekInfo": "Auto",
|
||||
"CopyTimestamps": false,
|
||||
"Context": "Static",
|
||||
"EnableSubtitlesInManifest": false,
|
||||
"MaxAudioChannels": "6",
|
||||
"MinSegments": 0,
|
||||
"SegmentLength": 0,
|
||||
"BreakOnNonKeyFrames": false,
|
||||
"$type": "TranscodingProfile"
|
||||
},
|
||||
{
|
||||
"Container": "mp3",
|
||||
"Type": "Audio",
|
||||
"AudioCodec": "mp3",
|
||||
"Protocol": "http",
|
||||
"EstimateContentLength": false,
|
||||
"EnableMpegtsM2TsMode": false,
|
||||
"TranscodeSeekInfo": "Auto",
|
||||
"CopyTimestamps": false,
|
||||
"Context": "Static",
|
||||
"EnableSubtitlesInManifest": false,
|
||||
"MaxAudioChannels": "6",
|
||||
"MinSegments": 0,
|
||||
"SegmentLength": 0,
|
||||
"BreakOnNonKeyFrames": false,
|
||||
"$type": "TranscodingProfile"
|
||||
},
|
||||
{
|
||||
"Container": "aac",
|
||||
"Type": "Audio",
|
||||
"AudioCodec": "aac",
|
||||
"Protocol": "http",
|
||||
"EstimateContentLength": false,
|
||||
"EnableMpegtsM2TsMode": false,
|
||||
"TranscodeSeekInfo": "Auto",
|
||||
"CopyTimestamps": false,
|
||||
"Context": "Static",
|
||||
"EnableSubtitlesInManifest": false,
|
||||
"MaxAudioChannels": "6",
|
||||
"MinSegments": 0,
|
||||
"SegmentLength": 0,
|
||||
"BreakOnNonKeyFrames": false,
|
||||
"$type": "TranscodingProfile"
|
||||
},
|
||||
{
|
||||
"Container": "wav",
|
||||
"Type": "Audio",
|
||||
"AudioCodec": "wav",
|
||||
"Protocol": "http",
|
||||
"EstimateContentLength": false,
|
||||
"EnableMpegtsM2TsMode": false,
|
||||
"TranscodeSeekInfo": "Auto",
|
||||
"CopyTimestamps": false,
|
||||
"Context": "Static",
|
||||
"EnableSubtitlesInManifest": false,
|
||||
"MaxAudioChannels": "6",
|
||||
"MinSegments": 0,
|
||||
"SegmentLength": 0,
|
||||
"BreakOnNonKeyFrames": false,
|
||||
"$type": "TranscodingProfile"
|
||||
},
|
||||
{
|
||||
"Container": "mkv",
|
||||
"Type": "Video",
|
||||
"VideoCodec": "h264,hevc,mpeg2video,vc1,msmpeg4v2,vp8,vp9",
|
||||
"AudioCodec": "aac,mp3,ac3,eac3,mp2,pcm_s16le,pcm_s24le,aac_latm,opus,flac,vorbis",
|
||||
"Protocol": "",
|
||||
"EstimateContentLength": false,
|
||||
"EnableMpegtsM2TsMode": false,
|
||||
"TranscodeSeekInfo": "Auto",
|
||||
"CopyTimestamps": true,
|
||||
"Context": "Static",
|
||||
"EnableSubtitlesInManifest": false,
|
||||
"MaxAudioChannels": "6",
|
||||
"MinSegments": 0,
|
||||
"SegmentLength": 0,
|
||||
"BreakOnNonKeyFrames": false,
|
||||
"Conditions": [
|
||||
{
|
||||
"Condition": "LessThanEqual",
|
||||
"Property": "Width",
|
||||
"Value": "3840",
|
||||
"IsRequired": false,
|
||||
"$type": "ProfileCondition"
|
||||
}
|
||||
],
|
||||
"$type": "TranscodingProfile"
|
||||
},
|
||||
{
|
||||
"Container": "ts",
|
||||
"Type": "Video",
|
||||
"VideoCodec": "h264,hevc",
|
||||
"AudioCodec": "aac,mp3,ac3,eac3,opus",
|
||||
"Protocol": "hls",
|
||||
"EstimateContentLength": false,
|
||||
"EnableMpegtsM2TsMode": false,
|
||||
"TranscodeSeekInfo": "Auto",
|
||||
"CopyTimestamps": false,
|
||||
"Context": "Streaming",
|
||||
"EnableSubtitlesInManifest": false,
|
||||
"MaxAudioChannels": "6",
|
||||
"MinSegments": 1,
|
||||
"SegmentLength": 0,
|
||||
"BreakOnNonKeyFrames": false,
|
||||
"Conditions": [
|
||||
{
|
||||
"Condition": "LessThanEqual",
|
||||
"Property": "Width",
|
||||
"Value": "3840",
|
||||
"IsRequired": false,
|
||||
"$type": "ProfileCondition"
|
||||
}
|
||||
],
|
||||
"$type": "TranscodingProfile"
|
||||
},
|
||||
{
|
||||
"Container": "webm",
|
||||
"Type": "Video",
|
||||
"VideoCodec": "vp8,vp9,vpx",
|
||||
"AudioCodec": "vorbis,opus",
|
||||
"Protocol": "http",
|
||||
"EstimateContentLength": false,
|
||||
"EnableMpegtsM2TsMode": false,
|
||||
"TranscodeSeekInfo": "Auto",
|
||||
"CopyTimestamps": false,
|
||||
"Context": "Streaming",
|
||||
"EnableSubtitlesInManifest": false,
|
||||
"MaxAudioChannels": "6",
|
||||
"MinSegments": 0,
|
||||
"SegmentLength": 0,
|
||||
"BreakOnNonKeyFrames": false,
|
||||
"Conditions": [
|
||||
{
|
||||
"Condition": "LessThanEqual",
|
||||
"Property": "Width",
|
||||
"Value": "3840",
|
||||
"IsRequired": false,
|
||||
"$type": "ProfileCondition"
|
||||
}
|
||||
],
|
||||
"$type": "TranscodingProfile"
|
||||
},
|
||||
{
|
||||
"Container": "mp4",
|
||||
"Type": "Video",
|
||||
"VideoCodec": "h264",
|
||||
"AudioCodec": "aac,mp3,ac3,eac3,mp2,pcm_s16le,pcm_s24le,aac_latm,opus,flac,vorbis",
|
||||
"Protocol": "http",
|
||||
"EstimateContentLength": false,
|
||||
"EnableMpegtsM2TsMode": false,
|
||||
"TranscodeSeekInfo": "Auto",
|
||||
"CopyTimestamps": false,
|
||||
"Context": "Static",
|
||||
"EnableSubtitlesInManifest": false,
|
||||
"MinSegments": 0,
|
||||
"SegmentLength": 0,
|
||||
"BreakOnNonKeyFrames": false,
|
||||
"Conditions": [
|
||||
{
|
||||
"Condition": "LessThanEqual",
|
||||
"Property": "Width",
|
||||
"Value": "3840",
|
||||
"IsRequired": false,
|
||||
"$type": "ProfileCondition"
|
||||
}
|
||||
],
|
||||
"$type": "TranscodingProfile"
|
||||
}
|
||||
],
|
||||
"CodecProfiles": [
|
||||
{
|
||||
"Type": "Video",
|
||||
"Conditions": [
|
||||
{
|
||||
"Condition": "NotEquals",
|
||||
"Property": "IsAnamorphic",
|
||||
"Value": "true",
|
||||
"IsRequired": false,
|
||||
"$type": "ProfileCondition"
|
||||
},
|
||||
{
|
||||
"Condition": "EqualsAny",
|
||||
"Property": "VideoProfile",
|
||||
"Value": "high|main|baseline|constrained baseline|high 10",
|
||||
"IsRequired": false,
|
||||
"$type": "ProfileCondition"
|
||||
},
|
||||
{
|
||||
"Condition": "LessThanEqual",
|
||||
"Property": "VideoLevel",
|
||||
"Value": "52",
|
||||
"IsRequired": false,
|
||||
"$type": "ProfileCondition"
|
||||
}
|
||||
],
|
||||
"Codec": "h264",
|
||||
"$type": "CodecProfile"
|
||||
},
|
||||
{
|
||||
"Type": "Video",
|
||||
"Conditions": [
|
||||
{
|
||||
"Condition": "NotEquals",
|
||||
"Property": "IsAnamorphic",
|
||||
"Value": "true",
|
||||
"IsRequired": false,
|
||||
"$type": "ProfileCondition"
|
||||
},
|
||||
{
|
||||
"Condition": "EqualsAny",
|
||||
"Property": "VideoProfile",
|
||||
"Value": "main|main 10",
|
||||
"IsRequired": false,
|
||||
"$type": "ProfileCondition"
|
||||
},
|
||||
{
|
||||
"Condition": "LessThanEqual",
|
||||
"Property": "VideoLevel",
|
||||
"Value": "183",
|
||||
"IsRequired": false,
|
||||
"$type": "ProfileCondition"
|
||||
}
|
||||
],
|
||||
"Codec": "hevc",
|
||||
"$type": "CodecProfile"
|
||||
}
|
||||
],
|
||||
"ResponseProfiles": [
|
||||
{
|
||||
"Container": "m4v",
|
||||
"Type": "Video",
|
||||
"MimeType": "video/mp4",
|
||||
"$type": "ResponseProfile"
|
||||
}
|
||||
],
|
||||
"SubtitleProfiles": [
|
||||
{
|
||||
"Format": "vtt",
|
||||
"Method": "External",
|
||||
"$type": "SubtitleProfile"
|
||||
},
|
||||
{
|
||||
"Format": "ass",
|
||||
"Method": "External",
|
||||
"$type": "SubtitleProfile"
|
||||
},
|
||||
{
|
||||
"Format": "ssa",
|
||||
"Method": "External",
|
||||
"$type": "SubtitleProfile"
|
||||
}
|
||||
],
|
||||
"$type": "DeviceProfile"
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
{
|
||||
"Id": "a766d122b58e45d9492d17af77748bf5",
|
||||
"Path": "/Media/MyVideo-720p.mp4",
|
||||
"Container": "mov,mp4,m4a,3gp,3g2,mj2",
|
||||
"Size": 835317696,
|
||||
"Name": "MyVideo-720p",
|
||||
"ETag": "579a34c6d5dfb21d81539a51220b6a23",
|
||||
"RunTimeTicks": 25801230336,
|
||||
"SupportsTranscoding": true,
|
||||
"SupportsDirectStream": true,
|
||||
"SupportsDirectPlay": true,
|
||||
"SupportsProbing": true,
|
||||
"MediaStreams": [
|
||||
{
|
||||
"Codec": "h264",
|
||||
"CodecTag": "avc1",
|
||||
"Language": "eng",
|
||||
"TimeBase": "1/11988",
|
||||
"VideoRange": "SDR",
|
||||
"DisplayTitle": "720p H264 SDR",
|
||||
"NalLengthSize": "0",
|
||||
"BitRate": 2032876,
|
||||
"BitDepth": 8,
|
||||
"RefFrames": 1,
|
||||
"IsDefault": true,
|
||||
"Height": 720,
|
||||
"Width": 1280,
|
||||
"AverageFrameRate": 23.976,
|
||||
"RealFrameRate": 23.976,
|
||||
"Profile": "High",
|
||||
"Type": 1,
|
||||
"AspectRatio": "16:9",
|
||||
"PixelFormat": "yuv420p",
|
||||
"Level": 41
|
||||
},
|
||||
{
|
||||
"Codec": "dts",
|
||||
"Language": "eng",
|
||||
"TimeBase": "1/48000",
|
||||
"DisplayTitle": "En - DTS - 5.1 - Default",
|
||||
"ChannelLayout": "5.1",
|
||||
"BitRate": 384000,
|
||||
"Channels": 6,
|
||||
"SampleRate": 48000,
|
||||
"IsDefault": true,
|
||||
"Index": 1,
|
||||
"Score": 202
|
||||
},
|
||||
{
|
||||
"Codec": "srt",
|
||||
"Language": "eng",
|
||||
"TimeBase": "1/1000000",
|
||||
"localizedUndefined": "Undefined",
|
||||
"localizedDefault": "Default",
|
||||
"localizedForced": "Forced",
|
||||
"DisplayTitle": "En - Default",
|
||||
"BitRate": 92,
|
||||
"IsDefault": true,
|
||||
"Type": 2,
|
||||
"Index": 2,
|
||||
"Score": 6421,
|
||||
"IsExternal": true,
|
||||
"IsTextSubtitleStream": true,
|
||||
"SupportsExternalStream": true
|
||||
}
|
||||
],
|
||||
"Bitrate": 2590008,
|
||||
"DefaultAudioStreamIndex": 1,
|
||||
"DefaultSubtitleStreamIndex": 2
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
{
|
||||
"Id": "f6eab7118618ab26e61e495a1853481a",
|
||||
"Path": "/Media/MyVideo-WEBDL-2160p.mp4",
|
||||
"Container": "mov,mp4,m4a,3gp,3g2,mj2",
|
||||
"Size": 6521110016,
|
||||
"Name": "MyVideo WEBDL-2160p",
|
||||
"ETag": "a2fb84b618ba2467fe377543f879e9bf",
|
||||
"RunTimeTicks": 34318510080,
|
||||
"SupportsTranscoding": true,
|
||||
"SupportsDirectStream": true,
|
||||
"SupportsDirectPlay": true,
|
||||
"SupportsProbing": true,
|
||||
"MediaStreams": [
|
||||
{
|
||||
"Codec": "hevc",
|
||||
"CodecTag": "hev1",
|
||||
"Language": "eng",
|
||||
"ColorSpace": "bt2020nc",
|
||||
"ColorTransfer": "smpte2084",
|
||||
"ColorPrimaries": "bt2020",
|
||||
"TimeBase": "1/16000",
|
||||
"VideoRange": "HDR",
|
||||
"DisplayTitle": "4K HEVC HDR",
|
||||
"BitRate": 14715079,
|
||||
"BitDepth": 8,
|
||||
"RefFrames": 1,
|
||||
"IsDefault": true,
|
||||
"Height": 2160,
|
||||
"Width": 3840,
|
||||
"AverageFrameRate": 23.976,
|
||||
"RealFrameRate": 23.976,
|
||||
"Profile": "Main 10",
|
||||
"Type": 1,
|
||||
"AspectRatio": "16:9",
|
||||
"PixelFormat": "yuv420p10le",
|
||||
"Level": 150
|
||||
},
|
||||
{
|
||||
"Codec": "truehd",
|
||||
"CodecTag": "AC-3",
|
||||
"Language": "eng",
|
||||
"TimeBase": "1/48000",
|
||||
"DisplayTitle": "TRUEHD - 7.1",
|
||||
"ChannelLayout": "7.1",
|
||||
"BitRate": 384000,
|
||||
"Channels": 8,
|
||||
"SampleRate": 48000,
|
||||
"IsDefault": true,
|
||||
"Index": 1,
|
||||
"Score": 203
|
||||
},
|
||||
{
|
||||
"Codec": "srt",
|
||||
"Language": "eng",
|
||||
"TimeBase": "1/1000000",
|
||||
"localizedUndefined": "Undefined",
|
||||
"localizedDefault": "Default",
|
||||
"localizedForced": "Forced",
|
||||
"DisplayTitle": "En - Default",
|
||||
"BitRate": 92,
|
||||
"IsDefault": true,
|
||||
"Type": 2,
|
||||
"Index": 2,
|
||||
"Score": 6421,
|
||||
"IsExternal": true,
|
||||
"IsTextSubtitleStream": true,
|
||||
"SupportsExternalStream": true,
|
||||
"Path": "/Media/MyVideo-WEBDL-2160p.default.eng.srt"
|
||||
}
|
||||
],
|
||||
"Bitrate": 15201382,
|
||||
"DefaultAudioStreamIndex": 1,
|
||||
"DefaultSubtitleStreamIndex": 2
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"Id": "f6eab7118618ab26e61e495a1853481a",
|
||||
"Path": "/Media/MyVideo-WEBDL-2160p.mp4",
|
||||
"Container": "mov,mp4,m4a,3gp,3g2,mj2",
|
||||
"Size": 6521110016,
|
||||
"Name": "MyVideo WEBDL-2160p",
|
||||
"ETag": "a2fb84b618ba2467fe377543f879e9bf",
|
||||
"RunTimeTicks": 34318510080,
|
||||
"SupportsTranscoding": true,
|
||||
"SupportsDirectStream": true,
|
||||
"SupportsDirectPlay": true,
|
||||
"SupportsProbing": true,
|
||||
"MediaStreams": [],
|
||||
"Bitrate": 15201382,
|
||||
"DefaultAudioStreamIndex": null,
|
||||
"DefaultSubtitleStreamIndex": null
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using Emby.Server.Implementations.Sorting;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.Audio;
|
||||
using MediaBrowser.Controller.Sorting;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Server.Implementations.Tests.Sorting;
|
||||
|
||||
public class IndexNumberComparerTests
|
||||
{
|
||||
private readonly IBaseItemComparer _cmp = new IndexNumberComparer();
|
||||
|
||||
private static TheoryData<BaseItem?, BaseItem?> Compare_GivenNull_ThrowsArgumentNullException_TestData()
|
||||
=> new()
|
||||
{
|
||||
{ null, new Audio() },
|
||||
{ new Audio(), null }
|
||||
};
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(Compare_GivenNull_ThrowsArgumentNullException_TestData))]
|
||||
public void Compare_GivenNull_ThrowsArgumentNullException(BaseItem? x, BaseItem? y)
|
||||
{
|
||||
Assert.Throws<ArgumentNullException>(() => _cmp.Compare(x, y));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(null, null, 0)]
|
||||
[InlineData(0, null, 1)]
|
||||
[InlineData(null, 0, -1)]
|
||||
[InlineData(1, 1, 0)]
|
||||
[InlineData(0, 1, -1)]
|
||||
[InlineData(1, 0, 1)]
|
||||
public void Compare_ValidIndices_SortsExpected(int? index1, int? index2, int expected)
|
||||
{
|
||||
BaseItem x = new Audio
|
||||
{
|
||||
IndexNumber = index1
|
||||
};
|
||||
BaseItem y = new Audio
|
||||
{
|
||||
IndexNumber = index2
|
||||
};
|
||||
|
||||
Assert.Equal(expected, _cmp.Compare(x, y));
|
||||
Assert.Equal(-expected, _cmp.Compare(y, x));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Emby.Server.Implementations.Sorting;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.Audio;
|
||||
using MediaBrowser.Controller.Sorting;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Server.Implementations.Tests.Sorting;
|
||||
|
||||
public class ParentIndexNumberComparerTests
|
||||
{
|
||||
private readonly IBaseItemComparer _cmp = new ParentIndexNumberComparer();
|
||||
|
||||
private static TheoryData<BaseItem?, BaseItem?> Compare_GivenNull_ThrowsArgumentNullException_TestData()
|
||||
=> new()
|
||||
{
|
||||
{ null, new Audio() },
|
||||
{ new Audio(), null }
|
||||
};
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(Compare_GivenNull_ThrowsArgumentNullException_TestData))]
|
||||
public void Compare_GivenNull_ThrowsArgumentNullException(BaseItem? x, BaseItem? y)
|
||||
{
|
||||
Assert.Throws<ArgumentNullException>(() => _cmp.Compare(x, y));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(null, null, 0)]
|
||||
[InlineData(0, null, 1)]
|
||||
[InlineData(null, 0, -1)]
|
||||
[InlineData(1, 1, 0)]
|
||||
[InlineData(0, 1, -1)]
|
||||
[InlineData(1, 0, 1)]
|
||||
public void Compare_ValidIndices_SortsExpected(int? parentIndex1, int? parentIndex2, int expected)
|
||||
{
|
||||
BaseItem x = new Audio
|
||||
{
|
||||
ParentIndexNumber = parentIndex1
|
||||
};
|
||||
BaseItem y = new Audio
|
||||
{
|
||||
ParentIndexNumber = parentIndex2
|
||||
};
|
||||
|
||||
Assert.Equal(expected, _cmp.Compare(x, y));
|
||||
Assert.Equal(-expected, _cmp.Compare(y, x));
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@
|
||||
<PackageReference Include="AutoFixture" Version="4.17.0" />
|
||||
<PackageReference Include="AutoFixture.AutoMoq" Version="4.17.0" />
|
||||
<PackageReference Include="AutoFixture.Xunit2" Version="4.17.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="6.0.3" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="6.0.4" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<PackageReference Include="AutoFixture" Version="4.17.0" />
|
||||
<PackageReference Include="AutoFixture.AutoMoq" Version="4.17.0" />
|
||||
<PackageReference Include="AutoFixture.Xunit2" Version="4.17.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="6.0.3" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="6.0.4" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
|
||||
Reference in New Issue
Block a user